I am trying to call a WinAPI function DeviceIoControl in C# with code IOCTL_DISK_SET_DISK_ATTRIBUTES and pass struct SET_DISK_ATTRIBUTES. I am trying do it with this code:
const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000;
const int FILE_SHARE_READ = 0x1;
const int FILE_SHARE_WRITE = 0x2;
const uint IOCTL_DISK_SET_DISK_ATTRIBUTES = 0x0007c0f4;
const ulong DISK_ATTRIBUTE_READ_ONLY = 0x0000000000000002;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped
);
struct SET_DISK_ATTRIBUTES
{
public uint Version;
public bool Persist;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] Reserved1;
public ulong Attributes;
public ulong AttributesMask;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public uint[] Reserved2;
};
private bool SetReadonly(IntPtr handle)
{
var sda = new SET_DISK_ATTRIBUTES();
sda.AttributesMask = DISK_ATTRIBUTE_READ_ONLY;
sda.Attributes = DISK_ATTRIBUTE_READ_ONLY;
int nPtrQryBytes = Marshal.SizeOf(sda);
sda.Version = (uint)nPtrQryBytes;
IntPtr ptrQuery = Marshal.AllocHGlobal(nPtrQryBytes);
Marshal.StructureToPtr(sda, ptrQuery, false);
uint byteReturned;
var res = DeviceIoControl(handle, IOCTL_DISK_SET_DISK_ATTRIBUTES, ptrQuery, (uint)nPtrQryBytes, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
var ex = new Win32Exception(Marshal.GetLastWin32Error());
MessageBox.Show(ex.Message);
return res;
}
I receive error "Parameter incorrect". What is the right way to call DeviceIoControl function passing structure SET_DISK_ATTRIBUTES?
The original definition of SET_DISK_ATTRIBUTES:
typedef struct _SET_DISK_ATTRIBUTES {
DWORD Version;
BOOLEAN Persist;
BYTE Reserved1[3];
DWORDLONG Attributes;
DWORDLONG AttributesMask;
DWORD Reserved2[4];
} SET_DISK_ATTRIBUTES, *PSET_DISK_ATTRIBUTES;
makes use of BOOLEAN data type, which is defined as a synonym of unsigned char (1 byte), as opposed to BOOL that is a synonym of int (4 bytes).
C#'s bool is marshaled as BOOL by default.
You need to force it into one byte:
{
...
[MarshalAs(UnmanagedType.I1)]
public bool Persist;
...
}
Finally code to set disk readonly
const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000;
const int FILE_SHARE_READ = 0x1;
const int FILE_SHARE_WRITE = 0x2;
const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
const uint IOCTL_DISK_SET_DISK_ATTRIBUTES = 0x0007c0f4;
const ulong DISK_ATTRIBUTE_READ_ONLY = 0x0000000000000002;
const uint IOCTL_DISK_UPDATE_PROPERTIES = 459072;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped
);
struct SET_DISK_ATTRIBUTES
{
public uint Version;
[MarshalAs(UnmanagedType.I1)]
public bool Persist;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] Reserved1;
public ulong Attributes;
public ulong AttributesMask;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public uint[] Reserved2;
};
public IntPtr CreateHandle(string driveLetter)
{
string filename = #"\\.\" + driveLetter[0] + ":";
return CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, 0x3, FILE_FLAG_WRITE_THROUGH, IntPtr.Zero);
}
private void SetReadonly(IntPtr handle)
{
var sda = new SET_DISK_ATTRIBUTES();
sda.Persist = true;
sda.AttributesMask = DISK_ATTRIBUTE_READ_ONLY;
sda.Attributes = DISK_ATTRIBUTE_READ_ONLY;
sda.Reserved1 = new byte[3] { 0, 0, 0 };
sda.Reserved2 = new uint[4] { 0, 0, 0, 0 };
int nPtrQryBytes = Marshal.SizeOf(sda);
sda.Version = (uint)nPtrQryBytes;
IntPtr ptrQuery = Marshal.AllocHGlobal(nPtrQryBytes);
Marshal.StructureToPtr(sda, ptrQuery, false);
uint byteReturned;
bool res = DeviceIoControl(handle, IOCTL_DISK_SET_DISK_ATTRIBUTES, ptrQuery, (uint)nPtrQryBytes, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
bool res2 = DeviceIoControl(handle, IOCTL_DISK_UPDATE_PROPERTIES, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
var mess = new List<string>();
mess.Add(new Win32Exception(Marshal.GetLastWin32Error()).Message);
mess.Add(new Win32Exception(Marshal.GetLastWin32Error()).Message);
MessageBox.Show(string.Join(" ", mess));
}
private void button1_Click(object sender, EventArgs e)
{
SetReadonly(CreateHandle(textBox1.Text));
}
Related
I wrote code that can write and read data from physical disk.
I open the physical disk using pInvoke CreateFile and use FileStream to perform read and write data.
When physical disk is online, every thing works great.
In case the physical disk is offline and I try to write to disk, I get an error
System.IO.IOException: 'The media is write protected.'
How can I detect if disk is offline without trying to write to disk.
Here is the code that create the FileStream and perform writes to disk
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const uint OPEN_EXISTING = 3;
private const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
private const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
public void OpenFile()
{
m_handleValue = CreateFile(DevicePath, GENERIC_WRITE | GENERIC_READ,
0x3, IntPtr.Zero, OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH
, IntPtr.Zero);
m_fileStream = new FileStream(m_handleValue, FileAccess.ReadWrite, 512);
}
public void WriteData(int offset, byte[] data)
{
m_fileStream.Seek(offset, SeekOrigin.Begin);
m_fileStream.Write(data, 0, data.Length);
}
Using DeviceIoControl pInvoke we can review attribute DISK_ATTRIBUTE_OFFLINE
public static class RawDeviceInfoProvider
{
private const int IOCTL_DISK_GET_DISK_ATTRIBUTES = 0x000700F0;
private const uint DISK_ATTRIBUTE_OFFLINE = 0x1;
[StructLayout(LayoutKind.Sequential)]
public struct GetDiskAttributes
{
public uint Version;
public uint Reserved;
public ulong Attributes;
}
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool DeviceIoControl(SafeHandle hDevice, uint dwIoControlCode, IntPtr lpInBuffer,
uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);
public static bool GetOnlineStatus(SafeFileHandle hDevice)
{
uint dummy;
GetDiskAttributes attributes = new GetDiskAttributes();
IntPtr lpOutBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(attributes));
bool success = DeviceIoControl(hDevice, IOCTL_DISK_GET_DISK_ATTRIBUTES, IntPtr.Zero, 0,
lpOutBuffer, (uint)Marshal.SizeOf(typeof(GetDiskAttributes)), out dummy, IntPtr.Zero);
attributes = (GetDiskAttributes)Marshal.PtrToStructure(lpOutBuffer, typeof(GetDiskAttributes));
Marshal.FreeHGlobal(lpOutBuffer);
if (!success)
{
int errorCode = Marshal.GetLastWin32Error();
throw new IOException("Unable to retrieve disk attributes, Error: " + errorCode);
}
bool isOffline = (attributes.Attributes & DISK_ATTRIBUTE_OFFLINE) > 0;
return !isOffline;
}
}
I have implemented all required code to get file type name from file path and it is working successfully but it returns subtracted type name e.g. if file path of .pdb or .pdf file then it will return "obe Acrobat Document" instead of "Adobe Acrobat Document"
I have used shell32.dll. I am not getting what happens please, help me to get rid out of it.
Source code:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
internal struct SHFILEINFO
{
public IntPtr hIcon;
public IntPtr iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
internal class Win32
{
public const uint FILE_ATTRIBUTE_NORMAL = 0x80;
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x10;
public const uint SHGFI_TYPENAME = 0x000000400;
public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
internal const uint SHGFI_SYSICONINDEX = 0x000004000;
internal const int ILD_TRANSPARENT = 0x1;
internal const uint SHGFI_ICON = 0x100;
internal const uint SHGFI_LARGEICON = 0x0;
internal const uint SHGFI_SMALLICON = 0x1;
[DllImport("shell32.dll", CharSet=CharSet.Unicode)]
internal static extern IntPtr SHGetFileInfo
(
string pszPath,
uint dwFileAttributes,
ref SHFILEINFO psfi,
uint cbSizeFileInfo,
uint uFlags
);
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
internal static extern int ExtractIconEx
(
string stExeFileName,
int nIconIndex,
ref IntPtr phiconLarge,
ref IntPtr phiconSmall,
int nIcons
);
[DllImport("comctl32.dll", SetLastError = true)]
internal static extern IntPtr ImageList_GetIcon
(
IntPtr himl,
int i,
int flags
);
[DllImport("user32.dll")]
internal static extern bool DestroyIcon(IntPtr hIcon);
}
internal static string GetFileType(string filename)
{
SHFILEINFO shinfo = new SHFILEINFO();
Win32.SHGetFileInfo
(
filename,
Win32.FILE_ATTRIBUTE_NORMAL,
ref shinfo, (uint)Marshal.SizeOf(shinfo),
Win32.SHGFI_TYPENAME |
Win32.SHGFI_USEFILEATTRIBUTES
);
return shinfo.szTypeName; //It return "obe Acrobat Document" Instead of "Adobe Acrobat Document"
}
The iIcon field in the C++ struct has type int.So, I just have to set the type of iIcon int only, not IntPtr. IntPtr works as per system platform so. I just set int type to iIcon like,
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
internal struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
And It's working fine...
For my program I need to get detailed information about the current displays. In my research I came across this post with talks about linking the System.Windows.Forms.Screen class and its EDID information. At first I tried copying and pasting the code found with using p/invoke to supply all the required native methods and structs, but it did not work and only gave me a string of ? for the InstanceID. So instead I tried to use the MSDN resources and again p/invoke to create the code myself. This is what I came up with:
private static void Foo()
{
Guid DisplayGUID = new Guid(Bar.GUID_DEVINTERFACE_MONITOR);
IntPtr DisplaysHandle = Bar.SetupDiGetClassDevs(ref DisplayGUID, null, IntPtr.Zero, (uint)(Win32.DIGCF_PRESENT | Win32.DIGCF_DEVICEINTERFACE));
Bar.SP_DEVICE_INTERFACE_DATA Data = new Bar.SP_DEVICE_INTERFACE_DATA();
Data.cbSize = Marshal.SizeOf(Data);
for (uint id = 0; Bar.SetupDiEnumDeviceInterfaces(DisplaysHandle, IntPtr.Zero, ref DisplayGUID, id, ref Data); id++)
{
Bar.SP_DEVINFO_DATA SPDID = new Bar.SP_DEVINFO_DATA();
SPDID.cbSize = (uint)Marshal.SizeOf(SPDID);
Bar.SP_DEVICE_INTERFACE_DETAIL_DATA NDIDD = new Bar.SP_DEVICE_INTERFACE_DETAIL_DATA();
if (IntPtr.Size == 8) //64 bit
NDIDD.cbSize = 8;
else //32 bit
NDIDD.cbSize = 4 + Marshal.SystemDefaultCharSize;
uint requiredsize = 0;
uint buffer = Bar.BUFFER_SIZE;
if (Bar.SetupDiGetDeviceInterfaceDetail(DisplaysHandle, ref Data, ref NDIDD, buffer, ref requiredsize, ref SPDID))
{
uint size = 0;
Bar.CM_Get_Device_ID_Size(out size, SPDID.DevInst);
IntPtr ptrInstanceBuf = Marshal.AllocHGlobal((int)size);
Bar.CM_Get_Device_ID(SPDID.DevInst, ref ptrInstanceBuf, size);
string InstanceID = Marshal.PtrToStringAuto(ptrInstanceBuf);
Console.WriteLine("InstanceID: {0}", InstanceID);
Marshal.FreeHGlobal(ptrInstanceBuf);
Console.WriteLine("DevicePath: {0}\n", NDIDD.DevicePath);
}
}
Bar.SetupDiDestroyDeviceInfoList(DisplaysHandle);
}
private class Bar
{
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, [MarshalAs(UnmanagedType.LPTStr)] string Enumerator, IntPtr hwndParent, uint Flags);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
[DllImport(#"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, UInt32 memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);
[DllImport(#"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, UInt32 deviceInterfaceDetailDataSize, ref UInt32 requiredSize, ref SP_DEVINFO_DATA deviceInfoData);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern int CM_Get_Device_ID_Size(out uint pulLen, UInt32 dnDevInst, int flags = 0);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern int CM_Get_Device_ID(uint dnDevInst, ref IntPtr Buffer, uint BufferLen, int ulFlags = 0);
public const int BUFFER_SIZE = 168; //guess
public const string GUID_DEVINTERFACE_MONITOR = "{E6F07B5F-EE97-4a90-B076-33F57BF4EAA7}";
[Flags]
public enum DiGetClassFlags : uint
{
DIGCF_DEFAULT = 0x00000001, // only valid with DIGCF_DEVICEINTERFACE
DIGCF_PRESENT = 0x00000002,
DIGCF_ALLCLASSES = 0x00000004,
DIGCF_PROFILE = 0x00000008,
DIGCF_DEVICEINTERFACE = 0x00000010,
}
[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVICE_INTERFACE_DATA
{
public Int32 cbSize;
public Guid interfaceClassGuid;
public Int32 flags;
private UIntPtr reserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVINFO_DATA
{
public uint cbSize;
public Guid classGuid;
public uint DevInst;
public IntPtr reserved;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
public int cbSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)]
public string DevicePath;
}
}
My code compiles and runs, but it does not give me the output I am looking for.
The output that I am looking for is:
InstanceID: DISPLAY\DELA00B\5&786e6ca&0&UID1048832
DevicePath: \\?\display#dela00b#5&786e6ca&0&uid1048832#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
But this is the output I am receiving:
InstanceID: l
DevicePath: \\?\display#dela00b#5&786e6ca&0&uid1048832#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
My question comes in the form of what is the problem causing the InstanceID to not output correctly.
Turns out I was using the wrong p/invoke signature. CM_Get_Device_ID should look like this:
[DllImport("setupapi.dll", SetLastError = true)]
public static extern int CM_Get_Device_ID(uint dnDevInst, StringBuilder Buffer, int BufferLen, int ulFlags = 0);
Also Updated Usage Code:
StringBuilder IDBuffer = new StringBuilder((int)buffer);
Bar.CM_Get_Device_ID(SPDID.DevInst, IDBuffer, (int)buffer);
Console.WriteLine("InstanceID: {0}", IDBuffer.ToString());
Console.WriteLine("DevicePath: {0}\n", NDIDD.DevicePath);
I got a problem when using the following code to get the icon from an application with Mono.
The background color is black, which is really not my expect.
Is there any suggestion?
[StructLayout(LayoutKind.Sequential)]
private struct SHFILEINFO
{
public IntPtr hIcon;
public IntPtr iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
private const uint SHGFI_ICON = 0x100;
private const uint SHGFI_LARGEICON = 0x0; // 'Large icon
private const uint SHGFI_SMALLICON = 0x1; // 'Small icon
[DllImport("shell32.dll")]
private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
[DllImport("shell32.dll")]
private static extern uint ExtractIconEx(string lpszFile, int nIconIndex, int[] phiconLarge, int[] phiconSmall, uint nIcons);
[DllImport("User32.dll")]
public static extern int DestroyIcon(IntPtr hIcon);
public static Bitmap GetExeIcon(string filename, bool smallIcon)
{
SHFILEINFO shinfo = new SHFILEINFO();
uint index = 0;
uint largeIcom = SHGFI_LARGEICON;
if (smallIcon)
{
largeIcom = SHGFI_SMALLICON;
}
SHGetFileInfo(filename, (uint)index, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | largeIcom);
Icon icon = (Icon)System.Drawing.Icon.FromHandle(shinfo.hIcon).Clone();
DestroyIcon(shinfo.hIcon);
return icon.ToBitmap();
}
BTW, I can just use Bitmap.FromHicon(shinfo.hIcon) to get the bitmap object, but the image shown is so rough, which i don't think the client will like it.
I have a problem retrieving the information of printer ports. I already use XcvData function to add, configure and delete ports, but I can not get the following to run as I expect it to:
public PrinterNativeMethods.PORT_DATA_1 GetPortData1FromPort(string serverName, string portName)
{
IntPtr printerHandle;
PrinterNativeMethods.PRINTER_DEFAULTS defaults = new PrinterNativeMethods.PRINTER_DEFAULTS
{
DesiredAccess = PrinterNativeMethods.PrinterAccess.ServerAdmin
};
string connection = string.Format(#"{0},XcvPort {1}", serverName, portName);
PrinterNativeMethods.OpenPrinter(connection, out printerHandle, ref defaults);
PrinterNativeMethods.CONFIG_INFO_DATA_1 configData = new PrinterNativeMethods.CONFIG_INFO_DATA_1
{
dwVersion = 1,
};
uint size = (uint)Marshal.SizeOf(configData);
IntPtr pointer = Marshal.AllocHGlobal((int)size);
Marshal.StructureToPtr(configData, pointer, true);
PrinterNativeMethods.PORT_DATA_1 portData = new PrinterNativeMethods.PORT_DATA_1();
uint portDataSize = (uint)Marshal.SizeOf(portData);
IntPtr portDataHandle = Marshal.AllocHGlobal((int)portDataSize);
try
{
uint outputNeeded;
uint status;
var retVal = PrinterNativeMethods.XcvData(printerHandle, "GetConfigInfo", pointer, size, out portDataHandle, portDataSize, out outputNeeded, out status);
//portDataHandle now points to a different location!? Unmarshalling will fail:
portData = (PrinterNativeMethods.PORT_DATA_1)Marshal.PtrToStructure(portDataHandle, typeof(PrinterNativeMethods.PORT_DATA_1));
}
finally
{
PrinterNativeMethods.ClosePrinter(printerHandle);
Marshal.FreeHGlobal(pointer);
Marshal.FreeHGlobal(portDataHandle);
}
return portData;
}
from PrinterNativeMethods:
[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int XcvData(
IntPtr handle,
string dataName,
IntPtr inputData,
uint inputDataSize,
out IntPtr outputData,
uint outputDataSize,
out uint outputNeededSize,
out uint status);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct PORT_DATA_1
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string sztPortName;
public uint dwVersion;
public uint dwProtocol;
public uint cbSize;
public uint dwReserved;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 49)]
public string sztHostAddress;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33h)]
public string sztSNMPCommunity;
public uint dwDoubleSpool;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)]
public string sztQueue;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string sztIPAddress;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 540)]
public byte[] Reserved;
public uint dwPortNumber;
public uint dwSNMPEnabled;
public uint dwSNMPDevIndex;
}
Additional comment: I cannot use WMI or prnadmin.dll as an alternative.
The problem you are having is in the definition of your XcvData definition. The outputData parameter by MS's definition is simply wanting a pointer to write data to, an IntPtr is a pointer, however by setting the parameter to out IntPtr you are making it a pointer to a pointer, this is why the address of your parameter appears to be changing. Changing the signature to the below will fix your problem.
[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int XcvData(
IntPtr handle,
string dataName,
IntPtr inputData,
uint inputDataSize,
IntPtr outputData,
uint outputDataSize,
out uint outputNeededSize,
out uint status);
You can also avoid some unnecessary allocation/deallocation by changing things around a bit.
Change your method signature for XcvData to
[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int XcvData(
IntPtr handle,
string dataName,
IntPtr inputData,
uint inputDataSize,
ref PORT_DATA_1 outputData,
uint outputDataSize,
out uint outputNeededSize,
out uint status);
Assuming that you will be using XcvData for more than just this single call you can make multiple references to it with slightly different signatures by setting the EntryPoint property on the DllImport Attribute.
[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint="XcvData")]
internal static extern int XcvDataGetPortData1(
IntPtr handle,
string dataName,
IntPtr inputData,
uint inputDataSize,
ref PORT_DATA_1 outputData,
uint outputDataSize,
out uint outputNeededSize,
out uint status);
I have given this a quick test on my machine and can confirm that this will fix your problem.