I'm trying to pinvoke a function that receives a couple of WCHAR <paramName>[1] parameters.
From what I've read in multiple places, in C/C++ you can't actually pass arrays to functions, instead they get converted to a pointer holding the first element of the array, which means that the array length becomes irrelevant and therefore WCHAR <paramName>[1] is the same as WCHAR* <paramName>.
So normally I'd declare this as a StringBuilder in C# and marshal it as LPWStr, but in this particular case that throws all sorts of errors.
Basically, the function above receives a GET_VIRTUAL_DISK_INFO, which in turn holds a union of some structs and other loose fields. And it is a couple of the structs in the union that receive a WCHAR <paramName>[1].
When I try to marshal it as LPWStr and declare my field as StringBuilder or string I get the error [...] contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.
I've also tried declaring it as an IntPtr and then using Marshal.PtrToStringAuto(<IntPtr>) but I get an empty string.
My C# code with all the structs, enums etc:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Permissions;
namespace ConsoleApplication1
{
class Program
{
public static Guid VirtualStorageTypeVendorMicrosoft = new Guid("EC984AEC-A0F9-47e9-901F-71415A66345B");
static void Main(string[] args)
{
var handle = new VirtualDiskSafeHandle();
var storageType = new VirtualStorageType
{
DeviceId = VirtualStorageDeviceType.Vhdx,
VendorId = VirtualStorageTypeVendorMicrosoft
};
var parameters = new OpenVirtualDiskParameters
{
Version = OpenVirtualDiskVersion.Version2
};
var result = OpenVirtualDisk(ref storageType, "D:\\Test2.vhdx", VirtualDiskAccessMask.None, OpenVirtualDiskFlag.None,
ref parameters, ref handle);
if (result != 0)
{
throw new Win32Exception((int) result);
}
var info = new GetVirtualDiskInfo {Version = GetVirtualDiskInfoVersion.PhysicalDisk};
var infoSize = (uint) Marshal.SizeOf(info);
uint sizeUsed = 0;
result = GetVirtualDiskInformation(handle, ref infoSize, ref info, ref sizeUsed);
if (result != 0)
{
throw new Win32Exception((int) result);
}
Console.WriteLine($"LogicalSizeSector = {info.Union.PhysicalDisk.LogicalSectorSize}");
Console.WriteLine($"PhysicalSizeSector = {info.Union.PhysicalDisk.PhysicalSectorSize}");
Console.ReadLine();
}
[DllImport("virtdisk.dll", CharSet = CharSet.Unicode)]
public static extern uint OpenVirtualDisk(
[In] ref VirtualStorageType virtualStorageType,
[In] string path,
[In] VirtualDiskAccessMask virtualDiskAccessMask,
[In] OpenVirtualDiskFlag flags,
[In] ref OpenVirtualDiskParameters parameters,
[In, Out] ref VirtualDiskSafeHandle handle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(
[In] IntPtr hObject);
[DllImport("virtdisk.dll", CharSet = CharSet.Unicode)]
public static extern uint GetVirtualDiskInformation(
[In] VirtualDiskSafeHandle virtualDiskHandle,
[In, Out] ref uint virtualDiskInfoSize,
[In, Out] ref GetVirtualDiskInfo virtualDiskInfo,
[In, Out] ref uint sizeUsed);
[SecurityPermission(SecurityAction.Demand)]
public class VirtualDiskSafeHandle : SafeHandle
{
public VirtualDiskSafeHandle() : base(IntPtr.Zero, true) { }
public override bool IsInvalid => IsClosed || (handle == IntPtr.Zero);
public bool IsOpen => !IsInvalid;
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
public override string ToString()
{
return handle.ToString();
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct OpenVirtualDiskParameters
{
public OpenVirtualDiskVersion Version; //OPEN_VIRTUAL_DISK_VERSION
public OpenVirtualDiskParametersUnion Union;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct OpenVirtualDiskParametersUnion
{
[FieldOffset(0)]
public OpenVirtualDiskParametersVersion1 Version1;
[FieldOffset(0)]
public OpenVirtualDiskParametersVersion2 Version2;
[FieldOffset(0)]
public OpenVirtualDiskParametersVersion3 Version3;
}
/// <summary>
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct OpenVirtualDiskParametersVersion1
{
public uint RWDepth; //ULONG
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct OpenVirtualDiskParametersVersion2
{
public bool GetInfoOnly; //BOOL
public bool ReadOnly; //BOOL
public Guid ResiliencyGuid; //GUID
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct OpenVirtualDiskParametersVersion3
{
public bool GetInfoOnly; //BOOL
public bool ReadOnly; //BOOL
public Guid ResiliencyGuid; //GUID
public Guid SnapshotId; //GUID
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfo
{
public GetVirtualDiskInfoVersion Version; //GET_VIRTUAL_DISK_INFO_VERSION
public GetVirtualDiskInfoUnion Union;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoUnion
{
[FieldOffset(0)] public GetVirtualDiskInfoSize Size;
[FieldOffset(0)] public Guid Identifier; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoParentLocation ParentLocation;
[FieldOffset(0)] public Guid ParentIdentifier; //GUID
[FieldOffset(0)] public uint ParentTimestamp; //ULONG
[FieldOffset(0)] public VirtualStorageType VirtualStorageType; //VIRTUAL_STORAGE_TYPE
[FieldOffset(0)] public uint ProviderSubtype; //ULONG
[FieldOffset(0)] public bool Is4kAligned; //BOOL
[FieldOffset(0)] public bool IsLoaded; //BOOL
[FieldOffset(0)] public GetVirtualDiskInfoPhysicalDisk PhysicalDisk;
[FieldOffset(0)] public uint VhdPhysicalSectorSize; //ULONG
[FieldOffset(0)] public ulong SmallestSafeVirtualSize; //ULONGLONG
[FieldOffset(0)] public uint FragmentationPercentage; //ULONG
[FieldOffset(0)] public Guid VirtualDiskId; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoChangeTrackingState ChangeTrackingState;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoSize
{
public ulong VirtualSize; //ULONGLONG
public ulong PhysicalSize; //ULONGLONG
public uint BlockSize; //ULONG
public uint SectorSize; //ULONG
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoParentLocation
{
public bool ParentResolved; //BOOL
public IntPtr ParentLocationBuffer; //WCHAR[1]
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoPhysicalDisk
{
public uint LogicalSectorSize; //ULONG
public uint PhysicalSectorSize; //ULONG
public bool IsRemote; //BOOL
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoChangeTrackingState
{
public bool Enabled; //BOOL
public bool NewerChanges; //BOOL
public IntPtr MostRecentId; //WCHAR[1]
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct VirtualStorageType
{
public VirtualStorageDeviceType DeviceId; //ULONG
public Guid VendorId; //GUID
}
public enum GetVirtualDiskInfoVersion
{
Unspecified = 0,
Size = 1,
Identifier = 2,
ParentLocation = 3,
ParentIdentifier = 4,
ParentTimestamp = 5,
VirtualStorageType = 6,
ProviderSubtype = 7,
Is4KAligned = 8,
PhysicalDisk = 9,
VhdPhysicalSectorSize = 10,
SmallestSafeVirtualSize = 11,
Fragmentation = 12,
IsLoaded = 13,
VirtualDiskId = 14,
ChangeTrackingState = 15
}
public enum VirtualStorageDeviceType
{
Unknown = 0,
Iso = 1,
Vhd = 2,
Vhdx = 3,
Vhdset = 4
}
public enum VirtualDiskAccessMask
{
None = 0x00000000,
AttachRo = 0x00010000,
AttachRw = 0x00020000,
Detach = 0x00040000,
GetInfo = 0x00080000,
Create = 0x00100000,
Metaops = 0x00200000,
Read = 0x000d0000,
All = 0x003f0000,
Writable = 0x00320000
}
[Flags]
public enum OpenVirtualDiskFlag
{
None = 0x00000000,
NoParents = 0x00000001,
BlankFile = 0x00000002,
BootDrive = 0x00000004,
CachedIo = 0x00000008,
CustomDiffChain = 0x00000010,
ParentCachedIo = 0x00000020,
VhdsetFileOnly = 0x00000040
}
public enum OpenVirtualDiskVersion
{
Unspecified = 0,
Version1 = 1,
Version2 = 2,
Version3 = 3,
}
}
}
Please comment out the line:
[FieldOffset(0)] public GetVirtualDiskInfoChangeTrackingState ChangeTrackingState;
Without that the struct results will be completely screwed up, still trying to figure out why.
Marshal the struct from the function call as an IntPtr. You will need to use Marshal.AllocHGlobal or another similar technique to get a block of unmanaged memory, since the Marshal isn't going to do it for you. You can then load the size member manually, or using Marshal.StructureToPtr.
From there use Marshal.OffsetOf to get the offset to the Union member. Once that's done use Marshal.Read and Marshal.PtrToStringUni to get at the data. For example with the Parent Location information:
IntPtr raw = Marshal.AllocHGlobal(1024);
// This is the GetVirtualDiskInfo from your provided code.
GetVirtualDiskInfo info = new GetVirtualDiskInfo();
info.Version = GetVirtualDiskInfoVersion.ParentLocation;
Marshal.StructureToPtr(info, raw, true);
Class1.Test(raw); // Replace this with your call to the function,
// This is a call to a C++/CLI method I wrote to stuff data
// into the structure.
IntPtr offsetToUnion = Marshal.OffsetOf(typeof(GetVirtualDiskInfo), "Union");
IntPtr data = raw + offsetToUnion.ToInt32();
bool parentResolved = Marshal.ReadInt32(data) != 0;
string parentLocationBuffer = Marshal.PtrToStringUni(data + 4);
Marshal.FreeHGlobal(raw); // Don't forget this!
Here's the method in the C++/CLI that loads the data for testing:
static void Test(IntPtr ptr)
{
GET_VIRTUAL_DISK_INFO* info = (GET_VIRTUAL_DISK_INFO*)ptr.ToPointer();
info->ParentLocation.ParentResolved = TRUE;
memcpy(info->ParentLocation.ParentLocationBuffer, L"123456789", 20);
}
Related
How would you write this type of struct in c#?
struct _JOBOBJECT_BASIC_PROCESS_ID_LIST {
DWORD NumberOfAssignedProcesses;
DWORD NumberOfProcessIdsInList;
ULONG_PTR ProcessIdList[1];
}
sins there is no set size for the ProcessIdList array, what do you do? Do you just write it like this:
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
int NumberOfAssignedProcesses;
int NumberOfProcessIdsInList;
IntPtr ProcessIdList; //Must point to a allocated array, thanks jdweng for letting me know.
}
or do you just assign a size which is big enough, e.g.:
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
int NumberOfAssignedProcesses;
int NumberOfProcessIdsInList;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_PATH)]
UIntPtr[] ProcessIdList; //Works just fine, but is limited to the SizeConst.
}
This sort of structure is usually declared (there are others like this one in WLan APIs for example) :
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
public int NumberOfAssignedProcesses;
public int NumberOfProcessIdsInList;
public IntPtr[] ProcessIdList;
public JOBOBJECT_BASIC_PROCESS_ID_LIST(IntPtr pList)
{
int nIntSize = Marshal.SizeOf<int>(); // 4
NumberOfAssignedProcesses = Marshal.ReadInt32(pList, 0);
NumberOfProcessIdsInList = Marshal.ReadInt32(pList, nIntSize);
ProcessIdList = new IntPtr[NumberOfProcessIdsInList];
for (int i = 0; i < NumberOfProcessIdsInList; i++)
{
IntPtr pItemList = IntPtr.Zero;
if (Marshal.SizeOf<IntPtr>() == 4)
pItemList = new IntPtr(pList.ToInt32() + (i * Marshal.SizeOf<IntPtr>()) + (nIntSize * 2));
else
pItemList = new IntPtr(pList.ToInt64() + (i * Marshal.SizeOf<IntPtr>()) + (nIntSize * 2));
IntPtr nPID = new IntPtr();
nPID = Marshal.ReadIntPtr(pItemList, 0);
ProcessIdList[i] = nPID;
}
}
}
A test with 5 Notepad launched and assigned to a job with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
then QueryInformationJobObject to enumerate the PIDs by using this structure =>
private IntPtr hJob = IntPtr.Zero;
bool bRet = false;
hJob = CreateJobObject(IntPtr.Zero, "Test Job Object");
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jbeli = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
jbeli.BasicLimitInformation.LimitFlags |= (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_BREAKAWAY_OK);
int nLength = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
IntPtr pJobInfo = Marshal.AllocHGlobal(nLength);
Marshal.StructureToPtr(jbeli, pJobInfo, false);
SetInformationJobObject(hJob, JOBOBJECTINFOCLASS.JobObjectExtendedLimitInformation, pJobInfo, (uint)nLength);
Marshal.FreeHGlobal(pJobInfo);
int nNbProcesses = 5;
for (int i = 0; i < nNbProcesses; i++)
{
using (Process exeProcess = new Process())
{
exeProcess.StartInfo.FileName = "notepad";
exeProcess.Start();
exeProcess.WaitForInputIdle();
IntPtr hProcess = exeProcess.Handle;
bRet = AssignProcessToJobObject(hJob, hProcess);
}
}
JOBOBJECT_BASIC_PROCESS_ID_LIST jobpil = new JOBOBJECT_BASIC_PROCESS_ID_LIST();
jobpil.NumberOfAssignedProcesses = nNbProcesses;
int nSize = Marshal.SizeOf<JOBOBJECT_BASIC_PROCESS_ID_LIST>() + (nNbProcesses - 1) * Marshal.SizeOf<IntPtr>();
IntPtr pJobpil = Marshal.AllocHGlobal(nSize);
Marshal.StructureToPtr(jobpil, pJobpil, false);
int nReturnLength = 0;
bRet = QueryInformationJobObject(hJob, JOBOBJECTINFOCLASS.JobObjectBasicProcessIdList, pJobpil, nSize, out nReturnLength);
if (bRet)
{
var processidlist = new JOBOBJECT_BASIC_PROCESS_ID_LIST(pJobpil);
foreach (var pid in processidlist.ProcessIdList)
{
Console.WriteLine("PID: {0}", pid.ToString());
}
}
else
{
int nErr = Marshal.GetLastWin32Error();
Win32Exception win32Exception = new Win32Exception(nErr);
this.Activate();
MessageBox.Show("Error: " + win32Exception.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Marshal.FreeHGlobal(pJobpil);
// CloseHandle can be added in Form1_FormClosed :
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
CloseHandle(hJob);
}
Declarations =>
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string lpName);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool SetInformationJobObject(IntPtr hJob, JOBOBJECTINFOCLASS JobObjectInfoClass, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool QueryInformationJobObject(IntPtr hJob, JOBOBJECTINFOCLASS JobObjectInformationClass, [Out, MarshalAs(UnmanagedType.SysUInt)] IntPtr lpJobObjectInformation, int cbJobObjectInformationLength, out int lpReturnLength);
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public ulong PerProcessUserTimeLimit;
public ulong PerJobUserTimeLimit;
public int LimitFlags;
public IntPtr MinimumWorkingSetSize;
public IntPtr MaximumWorkingSetSize;
public int ActiveProcessLimit;
public IntPtr Affinity;
public int PriorityClass;
public int SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
struct IO_COUNTERS
{
public ulong ReadOperationCount;
public ulong WriteOperationCount;
public ulong OtherOperationCount;
public ulong ReadTransferCount;
public ulong WriteTransferCount;
public ulong OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public IntPtr ProcessMemoryLimit;
public IntPtr JobMemoryLimit;
public IntPtr PeakProcessMemoryUsed;
public IntPtr PeakJobMemoryUsed;
}
//
// Basic Limits
//
public const int JOB_OBJECT_LIMIT_WORKINGSET = 0x00000001;
public const int JOB_OBJECT_LIMIT_PROCESS_TIME = 0x00000002;
public const int JOB_OBJECT_LIMIT_JOB_TIME = 0x00000004;
public const int JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 0x00000008;
public const int JOB_OBJECT_LIMIT_AFFINITY = 0x00000010;
public const int JOB_OBJECT_LIMIT_PRIORITY_CLASS = 0x00000020;
public const int JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME = 0x00000040;
public const int JOB_OBJECT_LIMIT_SCHEDULING_CLASS = 0x00000080;
//
// Extended Limits
//
public const int JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x00000100;
public const int JOB_OBJECT_LIMIT_JOB_MEMORY = 0x00000200;
public const int JOB_OBJECT_LIMIT_JOB_MEMORY_HIGH = JOB_OBJECT_LIMIT_JOB_MEMORY;
public const int JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x00000400;
public const int JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800;
public const int JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000;
public const int JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000;
public const int JOB_OBJECT_LIMIT_SUBSET_AFFINITY = 0x00004000;
public const int JOB_OBJECT_LIMIT_JOB_MEMORY_LOW = 0x00008000;
public enum JOBOBJECTINFOCLASS
{
JobObjectBasicAccountingInformation = 1,
JobObjectBasicLimitInformation,
JobObjectBasicProcessIdList,
JobObjectBasicUIRestrictions,
JobObjectSecurityLimitInformation, // deprecated
JobObjectEndOfJobTimeInformation,
JobObjectAssociateCompletionPortInformation,
JobObjectBasicAndIoAccountingInformation,
JobObjectExtendedLimitInformation,
JobObjectJobSetInformation,
JobObjectGroupInformation,
JobObjectNotificationLimitInformation,
JobObjectLimitViolationInformation,
JobObjectGroupInformationEx,
JobObjectCpuRateControlInformation,
JobObjectCompletionFilter,
JobObjectCompletionCounter,
JobObjectReserved1Information = 18,
JobObjectReserved2Information,
JobObjectReserved3Information,
JobObjectReserved4Information,
JobObjectReserved5Information,
JobObjectReserved6Information,
JobObjectReserved7Information,
JobObjectReserved8Information,
JobObjectReserved9Information,
JobObjectReserved10Information,
JobObjectReserved11Information,
JobObjectReserved12Information,
JobObjectReserved13Information,
JobObjectReserved14Information = 31,
JobObjectNetRateControlInformation,
JobObjectNotificationLimitInformation2,
JobObjectLimitViolationInformation2,
JobObjectCreateSilo,
JobObjectSiloBasicInformation,
JobObjectReserved15Information = 37,
JobObjectReserved16Information,
JobObjectReserved17Information,
JobObjectReserved18Information,
JobObjectReserved19Information = 41,
JobObjectReserved20Information,
MaxJobObjectInfoClass
}
I think any of the ways you mentioned should work.
In addition, there is a matching feature in c#: Define an array with the fixed keyword:
struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
int NumberOfAssignedProcesses;
int NumberOfProcessIdsInList;
fixed IntPtr ProcessIdList[1];
}
See documentation:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/unsafe-code-pointers/fixed-size-buffers
Also no bounds check, so you should be able to read behind the end of the struckt easily:
Note
Except for memory created by using stackalloc, the C# compiler and the common language runtime (CLR) do not perform any security buffer overrun checks. As with all unsafe code, use caution.
I'm trying to Marshal the following structures from c++ to c# on a Windows CE program and compact framework 2.0. I'm having a lot of difficulties with the marshalling of strings.
I have this c++ code:
#define Console_Parameters_MAX 50
struct AllParameters {
Parameter Parameters[Console_Parameters_MAX];
} ;
struct Parameter {
int Index;
int Id;
char Value[20];
};
extern "C" {
BOOL GetAllConsoleParameters(AllParameters *pItem);
}
and this are the corresponding c# code:
[StructLayout(LayoutKind.Sequential)]
public struct AllParameters {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
public Parameter[] Parameters
}
[StructLayout(LayoutKind.Sequential)]
public struct Parameter {
public int Index;
public int Id;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public byte[] Value;
}
[DllImport("exemple.dll", SetLastError = true)]
public static extern bool GetAllConsoleParameters([MarshalAs(UnmanagedType.Struct)] ref AllParameters pItem);
and this is how I invoke it:
AllParameters item = new AllParameters();
if (AppAPI.GetAllConsoleParameters(ref item)) {
var array = item.Parameters;
}
When I call the GetAllConsoleParameters I get exception NotSupportedException. I've tried many configurations but with no success.
Can anyone advise on how to achieve it?
Thanks in advance
This works for me on a Windows Desktop. You might have to change the calling convention to Cdecl in the C DLL and the C# DllImport attribute, because I read here that Cdecl is standard on Windows CE: https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention(v=vs.110).aspx
C code:
extern "C" {
__declspec(dllexport) BOOL __stdcall GetAllConsoleParameters(AllParameters *pItem)
{
pItem->Parameters[0].Index = 0;
pItem->Parameters[0].Id = 42;
CopyMemory(&pItem->Parameters[0].Value[0], "Hello World", 12);
pItem->Parameters[1].Index = 1;
pItem->Parameters[1].Id = 43;
CopyMemory(&pItem->Parameters[1].Value[0], "Hello World 43", 15);
return TRUE;
}
}
C# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct Parameter
{
int Index;
int Id;
//char Value[20];
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
string Value;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct AllParameters
{
//Parameter Parameters[Console_Parameters_MAX];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
Parameter[] Parameters;
};
class Program
{
[DllImport("MarshalC.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetAllConsoleParameters(ref AllParameters pItem);
static void Main(string[] args)
{
var size = Marshal.SizeOf<AllParameters>();
AllParameters all = new AllParameters();
bool result = GetAllConsoleParameters(ref all);
}
}
I would do it like this
[StructLayout(LayoutKind.Sequential)]
public struct AllParameters {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
public Parameter[] Parameters;
}
[StructLayout(LayoutKind.Sequential)]
public struct Parameter {
public int Index;
public int Id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] Value;
}
[DllImport("exemple.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public static extern bool GetAllConsoleParameters(ref IntPtr pItem);
static void Main(string[] args)
{
AllParameters allParameters = new AllParameters();
allParameters.Parameters = new Parameter[50];
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(allParameters));
int z = Marshal.SizeOf(allParameters);
if (GetAllConsoleParameters(ref ptr))
{
Marshal.PtrToStructure(ptr, allParameters);
Parameter[] parameters = allParameters.Parameters;
}
}
following my solution, c++ code:
/* - not used
#define Console_Parameters_MAX 50
struct AllParameters {
Parameter Parameters[Console_Parameters_MAX];
} ;
*/
struct Parameter {
int Index;
int Id;
char Value[20];
};
extern "C" {
BOOL GetAllConsoleParameters(Parameter pItem[], int size);
}
and corresponding c# code:
/* - not used
[StructLayout(LayoutKind.Sequential)]
public struct AllParameters {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
public Parameter[] Parameters
}
*/
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Parameter {
public int Index;
public int Id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] Value;
}
[DllImport("exemple.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool GetAllConsoleParameters([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] ConsoleParameter[] myStruct, int size);
and code invoke:
ConsoleParameter[] item = new ConsoleParameter[50];
if (AppAPI.GetAllConsoleParameters(item, 50)) {
var array = item;
}
thanks a lot for help
Full minimalist (non-working) code: http://pastebin.com/GPdSxyrt
I'm trying to PInvoke GetVirtualDiskInformation (https://msdn.microsoft.com/en-us/library/windows/desktop/dd323670(v=vs.85).aspx) and have my code as such:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfo
{
public GetVirtualDiskInfoVersion Version; //GET_VIRTUAL_DISK_INFO_VERSION
public GetVirtualDiskInfoUnion Union;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoUnion
{
[FieldOffset(0)] public GetVirtualDiskInfoSize Size;
[FieldOffset(0)] public Guid Identifier; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoParentLocation ParentLocation;
[FieldOffset(0)] public Guid ParentIdentifier; //GUID
[FieldOffset(0)] public uint ParentTimestamp; //ULONG
[FieldOffset(0)] public VirtualStorageType VirtualStorageType; //VIRTUAL_STORAGE_TYPE
[FieldOffset(0)] public uint ProviderSubtype; //ULONG
[FieldOffset(0)] public bool Is4kAligned; //BOOL
[FieldOffset(0)] public bool IsLoaded; //BOOL
[FieldOffset(0)] public GetVirtualDiskInfoPhysicalDisk PhysicalDisk;
[FieldOffset(0)] public uint VhdPhysicalSectorSize; //ULONG
[FieldOffset(0)] public ulong SmallestSafeVirtualSize; //ULONGLONG
[FieldOffset(0)] public uint FragmentationPercentage; //ULONG
[FieldOffset(0)] public Guid VirtualDiskId; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoChangeTrackingState ChangeTrackingState;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoSize
{
public ulong VirtualSize; //ULONGLONG
public ulong PhysicalSize; //ULONGLONG
public uint BlockSize; //ULONG
public uint SectorSize; //ULONG
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoParentLocation
{
public bool ParentResolved; //BOOL
public char ParentLocationBuffer; //WCHAR[1] //TODO
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoPhysicalDisk
{
public uint LogicalSectorSize; //ULONG
public uint PhysicalSectorSize; //ULONG
public bool IsRemote; //BOOL
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct GetVirtualDiskInfoChangeTrackingState
{
public bool Enabled; //BOOL
public bool NewerChanges; //BOOL
public char MostRecentId; //WCHAR[1] //TODO
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct VirtualStorageType
{
public VirtualStorageDeviceType DeviceId; //ULONG
public Guid VendorId; //GUID
}
public enum GetVirtualDiskInfoVersion
{
Unspecified = 0,
Size = 1,
Identifier = 2,
ParentLocation = 3,
ParentIdentifier = 4,
ParentTimestamp = 5,
VirtualStorageType = 6,
ProviderSubtype = 7,
Is4KAligned = 8,
PhysicalDisk = 9,
VhdPhysicalSectorSize = 10,
SmallestSafeVirtualSize = 11,
Fragmentation = 12,
IsLoaded = 13,
VirtualDiskId = 14,
ChangeTrackingState = 15
}
public enum VirtualStorageDeviceType
{
Unknown = 0,
Iso = 1,
Vhd = 2,
Vhdx = 3,
Vhdset = 4
}
[DllImport("virtdisk.dll", CharSet = CharSet.Unicode)]
public static extern uint GetVirtualDiskInformation
(
[In] VirtualDiskSafeHandle virtualDiskHandle,
[In, Out] ref uint virtualDiskInfoSize,
[In, Out] ref GetVirtualDiskInfo virtualDiskInfo,
[In, Out] ref uint sizeUsed
);
And I'm calling GetVirtualDiskInformation like so:
var info = new GetVirtualDiskInfo {Version = infoVersion};
infoSize = (uint) Marshal.SizeOf(info);
var result = NativeMethods.GetVirtualDiskInformation(handle, ref infoSize, ref info, ref sizeUsed);
Obviously handle does contain a valid VirtualDiskSafeHandle here.
The problem with this is that my out struct is all screwed up. The data is all over the place. According to it the VHD's LogicalSectorSize is 257 for example, and it has a negative VirtualSize.
What am I doing wrong and how can I get this to work properly?
EDIT:
A concrete example of what is going wrong:
I created a brand new VHDX as differencing (no parent, no source), set its max size to 50MB, LogicalSectorSize to 512, PhysicalSectorSize to 4096, BlockSize to 2MB and it's VendorId (GUID) to new Guid("EC984AEC-A0F9-47e9-901F-71415A66345B"). It has never been attached, it's size on disk stands at exactly 4096KB.
I would expect that when performing:
var info = new GetVirtualDiskInfo {Version = GetVirtualDiskInfoVersion.Size};
infoSize = (uint) Marshal.SizeOf(info);
var result = NativeMethods.GetVirtualDiskInformation(handle, ref infoSize, ref info, ref sizeUsed);
That info.Union.Size would return:
VirtualSize = 52428800
PhysicalSize = 4194304
BlockSize = 2097152
SectorSize = 512
What I get is "close enough". All values are correct with the exception of VirtualSize, which returns 52428801. Now the extra one byte could be something that is expected, but I doubt it. Regardless, the next couple of examples produces far worse results:
var info = new GetVirtualDiskInfo {Version = GetVirtualDiskInfoVersion.VirtualStorageType};
infoSize = (uint) Marshal.SizeOf(info);
var result = NativeMethods.GetVirtualDiskInformation(handle, ref infoSize, ref info, ref sizeUsed);
Expected results for info.Union.VirtualStorageType:
DeviceId = 2 //2 Symbolizes VHDX, which is how I created it
VendorId = "EC984AEC-A0F9-47e9-901F-71415A66345B" //As a GUID
Actual results:
DeviceId = 257
VendorId = "EC984AEC-A0F9-47e9-901F-71415A66345B" //As a GUID
In other words, the GUID is fine, the DeviceId is not. 257 is not even a valid value.
Last example:
var info = new GetVirtualDiskInfo {Version = GetVirtualDiskInfoVersion.PhysicalDisk};
infoSize = (uint) Marshal.SizeOf(info);
var result = NativeMethods.GetVirtualDiskInformation(handle, ref infoSize, ref info, ref sizeUsed);
Expected info.Union.PhysicalDisk to return:
LogicalSectorSize = 512
PhysicalSectorSize = 4096
IsRemote = false
Actual results:
LogicalSectorSize = 257
PhysicalSectorSize = 512
IsRemote = false
So again the very first value is completely wrong. 257 is not an accepted value, second value is also wrong, I'd expect 4096.
EDIT2 ADded VirtualStorageDeviceType to initial code.
EDIT3 Full example here: http://pastebin.com/GPdSxyrt
EDIT4
I worked out that the problem is with the fields that in C are WCHAR[1]. If I comment out the ChangeTrackingState in the struct GetVirtualDiskInfoUnion, then things work just fine.
Still, I'm not sure what to set them as in C#, they don't appear to be char nor IntPtr from what I tried, so what does that translate to exactly?
i do not have solution but noticed that if you add any uint field as last in structure it is also working correctly. My definition is:
public struct GetVirtualDiskInfoUnion
{
[FieldOffset(0)] public GetVirtualDiskInfoSize Size;
[FieldOffset(0)] public Guid Identifier; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoParentLocation ParentLocation;
[FieldOffset(0)] public Guid ParentIdentifier; //GUID
[FieldOffset(0)] public uint ParentTimestamp; //ULONG
[FieldOffset(0)] public VirtualStorageType VirtualStorageType; //VIRTUAL_STORAGE_TYPE
[FieldOffset(0)] public uint ProviderSubtype; //ULONG
[FieldOffset(0)] public bool Is4kAligned; //BOOL
[FieldOffset(0)] public bool IsLoaded; //BOOL
[FieldOffset(0)] public GetVirtualDiskInfoPhysicalDisk PhysicalDisk;
[FieldOffset(0)] public uint VhdPhysicalSectorSize; //ULONG
[FieldOffset(0)] public ulong SmallestSafeVirtualSize; //ULONGLONG
[FieldOffset(0)] public uint FragmentationPercentage; //ULONG
[FieldOffset(0)] public Guid VirtualDiskId; //GUID
[FieldOffset(0)] public GetVirtualDiskInfoChangeTrackingState ChangeTrackingState;
[FieldOffset(0)] public uint Reserved; //ULONG
}
I'm trying to import and use CryptCATCDFEnumMembersByCDFTagEx in order to create .cat files using C#. The function is returning null and the parameters do not seem to be initialized. I wrote the corresponding code in C++ and that works fine. I'm guessing my structures are off.
Any help would be appreciated. The code should create a Good.cat file in the working directory of the program, which should contain the hashes of the files specified below [CatalogFiles].
Catalog.cdf
[CatalogHeader]
Name=Good.cat
ResultDir=.\
PublicVersion=0x0000001
EncodingType=0x00010001
CATATTR1=0x10010001:OSAttr:2:6.0
[CatalogFiles]
Notepad.exe=C:\Windows\Notepad.exe
C# sample program.
class Program
{
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb427419%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct CRYPTCATCDF
{
uint cbStruct;
IntPtr hFile;
uint dwCurFilePos;
uint dwLastMemberOffset;
bool fEOF;
[MarshalAs(UnmanagedType.LPWStr)]
string pwszResultDir;
IntPtr hCATStore;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa381414%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct CRYPTOAPI_BLOB
{
public uint cbData; // UInt32 cbData;
//[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
public IntPtr pbData; //public byte[] pbData
} // CRYPT_INTEGER_BLOB, CRYPT_ATTR_BLOB, CRYPT_OBJID_BLOB, CRYPT_HASH_BLOB
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb736433%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct SIP_INDIRECT_DATA
{
public CRYPT_ATTRIBUTE_TYPE_VALUE Data;
public CRYPT_ALGORITHM_IDENTIFIER DigestAlgorithm;
public CRYPTOAPI_BLOB Digest;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa381151%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_ATTRIBUTE_TYPE_VALUE {
[MarshalAs(UnmanagedType.LPStr)]
public string pszObjId;
public CRYPTOAPI_BLOB Value;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa381133%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_ALGORITHM_IDENTIFIER
{
[MarshalAs(UnmanagedType.LPStr)]
public string pszObjId;
public CRYPTOAPI_BLOB Parameters;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa373931%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct GUID
{
int a;
short b;
short c;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
byte[] d;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa379905%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public class CRYPTCATMEMBER
{
public uint cbStruct;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszReferenceTag;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszFileName;
public GUID gSubjectType;
public uint fdwMemberFlags;
public IntPtr pIndirectData; //struct SIP_INDIRECT_DATA_ *pIndirectData;
public uint dwCertVersion;
public uint dwReserved;
public IntPtr hReserved;
public CRYPTOAPI_BLOB sEncodedIndirectData;
public CRYPTOAPI_BLOB sEncodedMemberInfo;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb410248%28v=vs.85%29.aspx
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void PFN_CDF_PARSE_ERROR_CALLBACK(
[In] uint dwErrorArea,
[In] uint dwLocalError,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszLine
);
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb427424%28v=vs.85%29.aspx
[DllImport("wintrust.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr CryptCATCDFOpen(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilePath,
[In, Optional] IntPtr pfnParseError
);
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb427423%28v=vs.85%29.aspx
[DllImport("wintrust.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)]
static extern string CryptCATCDFEnumMembersByCDFTagEx(
[In] IntPtr pCDF,
[In, Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszPrevCDFTag,
[In] IntPtr pfnParseError,
[In] CRYPTCATMEMBER ppMember,
[In] bool fContinueOnError,
[In] IntPtr pvReserved
);
private static void ParseErrorCallback(uint u1, uint u2, string s)
{
Console.WriteLine(u1 + " " + u2 + " " + s);
}
static void Main(string[] args)
{
PFN_CDF_PARSE_ERROR_CALLBACK pfn = new PFN_CDF_PARSE_ERROR_CALLBACK(ParseErrorCallback);
StringBuilder sb = new StringBuilder(256);
string s = string.Empty;
IntPtr cdfPtr = CryptCATCDFOpen("catalog.cdf", Marshal.GetFunctionPointerForDelegate(pfn));
CRYPTCATCDF cdf = (CRYPTCATCDF)Marshal.PtrToStructure(cdfPtr, typeof(CRYPTCATCDF));
CRYPTCATMEMBER ccm = new CRYPTCATMEMBER();
ccm.pIndirectData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIP_INDIRECT_DATA)));
do
{
s = CryptCATCDFEnumMembersByCDFTagEx(cdfPtr, sb, Marshal.GetFunctionPointerForDelegate(pfn), ccm, true, IntPtr.Zero);
Console.WriteLine(s ?? "N/A");
} while (s != null);
}
}
Working C++ example
void callback(DWORD u1, DWORD u2, LPWSTR s)
{
printf("%d %d %s", u1, u2, s);
}
typedef LPWSTR(WINAPI *CryptCATCDFEnumMembersByCDFTagEx)(
CRYPTCATCDF *pCDF,
LPWSTR pwszPrevCDFTag,
PFN_CDF_PARSE_ERROR_CALLBACK pfnParseError,
CRYPTCATMEMBER **ppMember,
BOOL fContinueOnError,
LPVOID pvReserved);
int _tmain(int argc, _TCHAR* argv[])
{
CRYPTCATCDF *cdf;
CRYPTCATMEMBER *pMember = NULL;
LPWSTR pwszMemberTag = NULL;
HINSTANCE dllHandle = LoadLibrary(L"wintrust.dll");
cdf = CryptCATCDFOpen(L"catalog.cdf", (PFN_CDF_PARSE_ERROR_CALLBACK)callback);
CryptCATCDFEnumMembersByCDFTagEx fptr = (CryptCATCDFEnumMembersByCDFTagEx)GetProcAddress(dllHandle, "CryptCATCDFEnumMembersByCDFTagEx");
while (pwszMemberTag = (*fptr)(cdf, pwszMemberTag, NULL, &pMember, TRUE, NULL))
{
wprintf(L"%s", pwszMemberTag);
}
CryptCATCDFClose(cdf);
}
With minor changes to your program, I'm able to create the catalog file. Did not modify any structs or its members. Still using "CRYPTCATMEMBER" as class
Changes:
Assign null to string variable, in this case "s".
Calling CryptCATCDFClose function after enumerating the files in catalog definition file(.cdf).
Working Console program.
static void Main(string[] args)
{
CRYPTCATMEMBER ccm = null;
try
{
PFN_CDF_PARSE_ERROR_CALLBACK pfn = ParseErrorCallback;
string s = null; //This null assignment is deliberately done.
IntPtr cdfPtr = CryptCATCDFOpen("catalog.cdf", Marshal.GetFunctionPointerForDelegate(pfn));
CRYPTCATCDF cdf = (CRYPTCATCDF) Marshal.PtrToStructure(cdfPtr, typeof(CRYPTCATCDF)); //This call is required else the catlog file creation fails
ccm = new CRYPTCATMEMBER
{
pIndirectData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SIP_INDIRECT_DATA)))
};
do
{
s = CryptCATCDFEnumMembersByCDFTagEx(cdfPtr, s, Marshal.GetFunctionPointerForDelegate(pfn), ccm, true, IntPtr.Zero);
Console.WriteLine(s ?? "N/A");
} while (s != null);
CryptCATCDFClose(cdfPtr); //This is required to update the .cat with the files details specified in .cdf file.
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
finally
{
// Free the unmanaged memory.
if (ccm != null)
{
Marshal.FreeHGlobal(ccm.pIndirectData);
}
}
}
I try to programmatically enumerate the DHCP filters on my Windows 2012 R2 DHCP server. Using P/Invoke, the code looks like:
public const uint ERROR_SUCCESS = 0;
public const uint ERROR_MORE_DATA = 234;
public const uint ERROR_NO_MORE_ITEMS = 259;
public const int MAX_PATTERN_LENGTH = 255;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DHCP_ADDR_PATTERN {
public bool MatchHWType;
public byte HWType;
public bool IsWildCard;
public byte Length;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_PATTERN_LENGTH)]
public byte[] Pattern;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DHCP_FILTER_ENUM_INFO {
public uint NumElements;
public IntPtr pEnumRecords;
}
public enum DHCP_FILTER_LIST_TYPE : uint {
Deny = 0x1,
Allow = 0x2
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DHCP_FILTER_RECORD {
public DHCP_ADDR_PATTERN AddrPatt;
public string Comment;
}
[DllImport("dhcpsapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint DhcpEnumFilterV4(string ServerIpAddress,
ref DHCP_ADDR_PATTERN ResumeHandle, uint PreferredMaximum,
DHCP_FILTER_LIST_TYPE ListType, out IntPtr EnumFilterInfo,
out uint ElementsRead, out uint ElementsTotal);
public static IEnumerable<DHCP_FILTER_RECORD> DhcpEnumFilterV4(
string serverIpAddress, DHCP_FILTER_LIST_TYPE listType,
uint preferredMaximum = 1024) {
uint cntRead = 0;
uint cntTotal = 0;
uint error = ERROR_SUCCESS;
var hResume = new DHCP_ADDR_PATTERN();
var data = IntPtr.Zero;
var size = Marshal.SizeOf(typeof(DHCP_FILTER_RECORD));
do {
error = DhcpEnumFilterV4(serverIpAddress, ref hResume,
preferredMaximum, listType, out data, out cntRead,
out cntTotal);
//
// PROBLEM OCCURS HERE: 'error' is always 259
//
if ((error == ERROR_SUCCESS) || (error == ERROR_MORE_DATA)) {
var array = data.ToStructure<DHCP_FILTER_ENUM_INFO>();
for (uint i = 0; i < array.NumElements; ++i) {
var ptr = new IntPtr((long) array.pEnumRecords + i * size);
var obj = (DHCP_FILTER_RECORD) Marshal.PtrToStructure(ptr, typeof(DHCP_FILTER_RECORD));
yield return obj;
}
DhcpRpcFreeMemory(array.pEnumRecords);
DhcpRpcFreeMemory(data);
data = IntPtr.Zero;
} else if (error != ERROR_NO_MORE_ITEMS) {
Debug.Assert(data == IntPtr.Zero);
throw new Win32Exception((int) error);
}
} while (error == ERROR_MORE_DATA);
}
[DllImport("dhcpsapi.dll", SetLastError = true)]
public static extern void DhcpRpcFreeMemory(IntPtr BufferPointer);
The documentation (http://msdn.microsoft.com/en-us/library/windows/desktop/dd897526(v=vs.85).aspx) of the whole DHCP APIs is imho a bit sketchy, so I am not completely sure whether I am doing the right thing.
The problem is: I never get any results, DhcpEnumFilterV4 always returns ERROR_NO_MORE_ITEMS. Any suggestions?
I just stumbled over an important user comment regarding DHCP_FILTER_LIST_TYPE in MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/dd897586(v=vs.85).aspx). It seems that the definition of the enumeration in MSDN is wrong. The following
typedef enum {
Deny = 0x1, // This is wrong!
Allow = 0x2 // This is wrong!
} DHCP_FILTER_LIST_TYPE;
should be
typedef enum {
Deny = 0x0, // This is correct!
Allow = 0x1 // This is correct!
} DHCP_FILTER_LIST_TYPE;
Using the updated constants, my code works.