From the Microsoft Documentation:
DWORD WINAPI WlanHostedNetworkSetProperty(
_In_ HANDLE hClientHandle,
_In_ WLAN_HOSTED_NETWORK_OPCODE OpCode,
_In_ DWORD dwDataSize,
_In_ PVOID pvData,
_Out_opt_ PWLAN_HOSTED_NETWORK_REASON pFailReason,
_Reserved_ PVOID pvReserved
);
My implementation
[DllImport("Wlanapi.dll", EntryPoint = "WlanHostedNetworkSetProperty")]
public static extern uint WlanHostedNetworkSetProperty(
IntPtr hClientHandle,
WLAN_HOSTED_NETWORK_OPCODE OpCode,
uint dwDataSize,
IntPtr pvData,
[Out] out WLAN_HOSTED_NETWORK_REASON pFailReason,
IntPtr pReserved
);
This works fine if I am only trying to enable or disable Wlan. However when I try to set the ssid and the numbers of peers I am running into an error because pvData needs to be a pointer to the following struct:
From Microsoft Documentation:
typedef struct _WLAN_HOSTED_NETWORK_CONNECTION_SETTINGS {
DOT11_SSID hostedNetworkSSID;
DWORD dwMaxNumberOfPeers;
} WLAN_HOSTED_NETWORK_CONNECTION_SETTINGS, *PWLAN_HOSTED_NETWORK_CONNECTION_SETTINGS;
and
typedef struct _DOT11_SSID {
ULONG uSSIDLength;
UCHAR ucSSID[DOT11_SSID_MAX_LENGTH];
} DOT11_SSID, *PDOT11_SSID;
This is my implementation of both structures:
public struct WLAN_HOSTED_NETWORK_CONNECTION_SETTINGS
{
public DOT11_SSID hostedNetworkSSID;
public UInt32 dwMaxNumberOfPeers;
}
public struct DOT11_SSID
{
public UInt32 uSSIDLength;
public byte[] ucSSID;
}
Finally, this is how I am making the call:
APCreator.WLAN_HOSTED_NETWORK_CONNECTION_SETTINGS networkSetting = new APCreator.WLAN_HOSTED_NETWORK_CONNECTION_SETTINGS();
networkSetting.dwMaxNumberOfPeers = 5;
networkSetting.hostedNetworkSSID = new APCreator.DOT11_SSID();
networkSetting.hostedNetworkSSID.ucSSID = new byte[10];
//Populate ucSSID with network name
networkSetting.hostedNetworkSSID.uSSIDLength = 10;
int size = Marshal.SizeOf(networkSetting);
IntPtr pNetworkSetting = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(networkSetting, pNetworkSetting, true);
APCreator.WlanHostedNetworkSetProperty(HANDLE, APCreator.WLAN_HOSTED_NETWORK_OPCODE.wlan_hosted_network_opcode_connection_settings, (uint)size, pNetworkSetting, out ReasonEnum, Nill).ToString();
Error reason returns with: wlan_hosted_network_reason_isufficient_resources
The question is: How to set up those structures?
Your DOT11_SSID struct is incorrect. Try this:
public struct DOT11_SSID
{
public UInt32 uSSIDLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = DOT11_SSID_MAX_LENGTH)]
public byte[] ucSSID;
}
Without the MarshalAs attribute, it's trying to marshal the ucSSID field as a pointer to a byte array rather than including the value with the struct.
Replace DOT11_SSID_MAX_LENGTH with whatever value is defined in the header.
Related
So I am trying to use C# and the ObRegisterCallbacks function to get notified about any calls to OpenProcess.
This is the code I have so far:
internal static class Win32SelfProtection
{
[DllImport("NtosKrnl.exe", SetLastError = true, PreserveSig = false)]
private static extern uint ObRegisterCallbacks(IntPtr callbackRegistration, out IntPtr registrationHandle);
[DllImport("NtosKrnl.exe", SetLastError = true, PreserveSig = false)]
private static extern void ObUnRegisterCallbacks(IntPtr registrationHandle);
[DllImport("kernel32.dll")]
internal static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
private const uint OB_OPERATION_HANDLE_CREATE = 0x00000001;
private const uint OB_OPERATION_HANDLE_DUPLICATE = 0x00000002;
private const uint PAGE_READWRITE = 0x04;
[StructLayout(LayoutKind.Sequential)]
internal struct OB_CALLBACK_REGISTRATION
{
internal ushort Version;
internal ushort OperationRegistrationCount; // 1
internal IntPtr Altitude;
internal IntPtr RegistrationContext; // NULL, probably
internal IntPtr OperationRegistration; // OB_OPERATION_REGISTRATION*
}
[StructLayout(LayoutKind.Sequential)]
internal struct OB_OPERATION_REGISTRATION
{
internal IntPtr ObjectType; // PsProcessType
internal uint Operations; // OB_OPERATION_HANDLE_CREATE
internal IntPtr PreOperation; // POB_PRE_OPERATION_CALLBACK
internal IntPtr PostOperation; // POB_POST_OPERATION_CALLBACK
}
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct UNICODE_STRING
{
internal ushort Length;
internal ushort MaximumLength;
internal IntPtr Buffer;
}
internal static unsafe void Protect()
{
PobPreOperationCallback preOperationCallback = PreOperationCallback;
IntPtr pPreOperationCallback = Marshal.GetFunctionPointerForDelegate(preOperationCallback);
PobPostOperationCallback postOperationCallback = PostOperationCallback;
IntPtr pPostOperationCallback = Marshal.GetFunctionPointerForDelegate(postOperationCallback);
OB_OPERATION_REGISTRATION operationRegistration = new OB_OPERATION_REGISTRATION
{
ObjectType = IntPtr.Zero, // I have no idea ... <-- Need pointer to PsProcessType
Operations = OB_OPERATION_HANDLE_CREATE,
PreOperation = pPreOperationCallback,
PostOperation = pPostOperationCallback
};
IntPtr pOperationRegistration = Marshal.AllocHGlobal(sizeof(OB_OPERATION_REGISTRATION));
Marshal.StructureToPtr(operationRegistration, pOperationRegistration, false);
const ushort buffersize = sizeof(ushort) * 64;
IntPtr buffer = Marshal.AllocHGlobal(buffersize);
// No idea what kind of string I should put in here :C just zero it for now ...
Marshal.Copy(new byte[buffersize], 0, buffer, buffersize);
UNICODE_STRING unicodeString = new UNICODE_STRING
{
Length = buffersize,
MaximumLength = buffersize,
Buffer = buffer
};
IntPtr pUnicodeString = Marshal.AllocHGlobal(sizeof(UNICODE_STRING));
Marshal.StructureToPtr(unicodeString, pUnicodeString, false);
OB_CALLBACK_REGISTRATION callbackRegistration = new OB_CALLBACK_REGISTRATION
{
Version = 0x0100,
OperationRegistrationCount = 1,
Altitude = pUnicodeString,
RegistrationContext = IntPtr.Zero,
OperationRegistration = pOperationRegistration
};
IntPtr pCallbackRegistration = Marshal.AllocHGlobal(sizeof(OB_CALLBACK_REGISTRATION));
Marshal.StructureToPtr(callbackRegistration, pCallbackRegistration, false);
uint status = ObRegisterCallbacks(pCallbackRegistration, out IntPtr hRegistration); // FAILS WITH: AccessViolationException
// yeah, yeah I'll remember to call Marshal.FreeHGlobal() later ... :D
}
public delegate uint PobPreOperationCallback(IntPtr registrationContext, IntPtr operationInformation);
// dummy method for now
internal static uint PreOperationCallback(IntPtr registrationContext, IntPtr operationInformation)
{
Console.WriteLine("PreOperationCallback!");
return 0x0;
}
public delegate void PobPostOperationCallback(IntPtr registrationContext, IntPtr operationInformation);
// dummy method for now
internal static void PostOperationCallback(IntPtr registrationContext, IntPtr operationInformation)
{
Console.WriteLine("PostOperationCallback!");
}
}
The ObRegisterCallbacks function takes an OB_CALLBACK_REGISTRATION struct (docs here) as parameter that itself consists of an array of OB_OPERATION_REGISTRATION structs (docs here).
This is where I'm stuck:
The OB_OPERATION_REGISTRATION's first member, "ObjectType", is documented as follows
ObjectType
A pointer to the object type that triggers the callback routine. Specify one of the following values:
PsProcessType for process handle operations
PsThreadType for thread handle operations
ExDesktopObjectType for desktop handle operations. This value is supported in Windows 10 and not in the earlier versions of the operating system.
After a few hour of searching I still have no clue how I'm supposed to specify PsProcessType and initialize my struct with it. PsProcessType seems to be defined in process.c in line 20.
However it literally just says
POBJECT_TYPE PsProcessType = NULL;
which isn't especially helpful, since when setting the ObjectType field to IntPtr.Zero when initializing the OB_OPERATION_REGISTRATION struct a System.AccessViolationException is triggered when calling ObRegisterCallbacks. (There's also a UNICODE_STRING in the OB_OPERATION_REGISTRATION struct called Altitude that has to be set to some value (but currently isn't lol :D), but that string is initialized and allocated, so it shouldn't be responsible for the access violation... right?)
This is my first time diving this deep into Windows kernel stuff, so it would be nice if someone could help me out with this or point me to some hidden resources I didn't manage to dig up :)
There's an article that uses ObRegisterCallbacks for similar things (in C++ though), however they don't really specify where they're getting PsProcessType from, or how it's defined. So there has to be documentation somewhere out there, if they can successfully use that "ObjectType" field, right?
PsProcessType is exported at ntoskrnl.exe and is the same as ObRegisterCallbacks, the difference between them is that one is an exported global variable and the other is an exported function.
In C, these global variables are declared in wdm.h:
extern POBJECT_TYPE *CmKeyObjectType;
extern POBJECT_TYPE *IoFileObjectType;
extern POBJECT_TYPE *ExEventObjectType;
extern POBJECT_TYPE *ExSemaphoreObjectType;
extern POBJECT_TYPE *TmTransactionManagerObjectType;
extern POBJECT_TYPE *TmResourceManagerObjectType;
extern POBJECT_TYPE *TmEnlistmentObjectType;
extern POBJECT_TYPE *TmTransactionObjectType;
extern POBJECT_TYPE *PsProcessType;
extern POBJECT_TYPE *PsThreadType;
extern POBJECT_TYPE *PsJobType;
extern POBJECT_TYPE *SeTokenObjectType;
#if (NTDDI_VERSION >= NTDDI_THRESHOLD)
extern POBJECT_TYPE *ExDesktopObjectType;
#endif
So you can just use these variables without having to do anything else.
But I'm not good at C#, and I'm not even sure if C# modules can be loaded into the kernel.
The most recommended approach is to use C/C++ for kernel programming, and I've never heard of anyone doing this with C#.
I need to use the following C++ function in my WPF application:
/****************************************
* BOOL WINAPI SetWindowFeedbackSetting(
* _In_ HWND hwnd,
* _In_ FEEDBACK_TYPE feedback,
* _In_ DWORD dwFlags,
* _In_ UINT32 size,
* _In_opt_ const VOID *configuration
*);
*****************************************/
But I'm not sure how to Marshal it and how to PInvoke it. This is what I have so far:
public enum FEEDBACK_TYPE : uint {
FEEDBACK_TOUCH_CONTACTVISUALIZATION = 1,
FEEDBACK_PEN_BARRELVISUALIZATION = 2,
FEEDBACK_PEN_TAP = 3,
FEEDBACK_PEN_DOUBLETAP = 4,
FEEDBACK_PEN_PRESSANDHOLD = 5,
FEEDBACK_PEN_RIGHTTAP = 6,
FEEDBACK_TOUCH_TAP = 7,
FEEDBACK_TOUCH_DOUBLETAP = 8,
FEEDBACK_TOUCH_PRESSANDHOLD = 9,
FEEDBACK_TOUCH_RIGHTTAP = 10,
FEEDBACK_GESTURE_PRESSANDTAP = 11,
FEEDBACK_MAX = 0xFFFFFFFF
}
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)] // (1)
public static extern bool SetWindowFeedbackSetting(
[In] IntPtr hwnd,
[In] FEEDBACK_TYPE feedback,
[In] uint dwFlags,
[In] uint size, // (2)
[In, Optional] IntPtr configuration // (3)
);
So first, is (1),(2),(3) correct?
Second, if I want to PInvoke this function according to this C++ function call:
BOOL fEnabled = FALSE;
BOOL res = SetWindowFeedbackSetting(
hwnd,
FEEDBACK_TOUCH_CONTACTVISUALIZATION,
0,
sizeof(fEnabled),
&fEnabled
);
What it the way to send the HWND and the boolean?
I'm asking this question to better understand the pinvoke\Marshal mechanism and also since the way I tried is not working.
Thanks
Your translation is accurate. But personally, for convenience, I'd make one overload for each type you need to use. For a boolean:
[DllImport("user32.dll"])
public static extern bool SetWindowFeedbackSetting(
IntPtr hwnd,
FEEDBACK_TYPE feedback,
uint dwFlags,
uint size,
[In] ref bool configuration
);
I removed the attributes that were not needed because they restate default values.
Remember that the Win32 BOOL type, the default marshalling option for C# bool is a 4 byte type. So you must pass 4 as the size.
See this question to learn how to obtain a window handle for your WPF window: How to get the hWnd of Window instance?
From what I remember, P/Invoke mechanism has problems with passing/returning .NET's bool. I'd do:
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)] // (1)
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowFeedbackSetting(
[In] IntPtr hwnd,
[In] FEEDBACK_TYPE feedback,
[In] uint dwFlags,
[In] uint size, // (2)
[In, Optional] IntPtr configuration // (3)
);
Then, you have to get HWND for current window in WPF. This question: How to get the hWnd of Window instance? should help in this matter.
For your convenience:
Window window = Window.GetWindow(this);
var wih = new WindowInteropHelper(window);
IntPtr hWnd = wih.Handle;
(credit goes to Drahcir)
Finally, to get address of the boolean, I'd do the following:
int myBool; // Win32's BOOL is actually an 4-byte integral number, 0 or non-zero
GCHandle handle = GCHandle.Alloc(myBool, GCHandleType.Pinned);
try
{
IntPtr ptr = handle.AddrOfPinnedObject();
// use ptr
}
finally
{
handle.Free();
}
This is the signature of my function in DLL:
int __stdcall myFun( void * const context, const char * const pszFileName, const unsigned int buffSize, void * const pWaveFormatex );
All parameters are [in]. The user should pass a pointer to a WAVEFORMATEX struct through the last parameter. Upon return, it will be filled. All that works very well in C++.
Now, I'm trying for days to use the same DLL from C# and it simply doesn't work. The problem is in the last parameter. Since I do not know C# at all, I would like to ask somebody if this is doable at all. If it is, I would appreciate an example.
One of my last attempts was this:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct WAVEFORMATEX
{
public ushort wFormatTag;
public ushort nChannels;
public uint nSamplesPerSec;
public uint nAvgBytesPerSec;
public ushort nBlockAlign;
public ushort wBitsPerSample;
public ushort cbSize;
}
Note: I also built my DLL written in C++ with the Struct Member Alignment = 1. Maybe I'm stupid, but I thought that Pack = 1 above is related with that in C++, but I have no idea if it is...
[DllImport("myLib.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern int myFun( IntPtr context,
[MarshalAs( UnmanagedType.LPStr )]
string pszFileName,
int bufferSize,
ref IntPtr pWfx );
IntPtr unmanaged_pWfx = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WAVEFORMATEX)));
result = DLLWrapper.myFun(context,
"C:\\video.wmv",
176400,
ref unmanaged_pWfx );
WAVEFORMATEX wfxFormat = (WAVEFORMATEX)Marshal.PtrToStructure( unmanaged_pWfx, typeof(WAVEFORMATEX));
The behavior is undefined. Sometimes it hangs, sometimes it terminates... Something is very wrong. However, the problem is not in the DLL, the problem is on the C# side. Is C# capable of working with pointers at all?
Thanks for any feedback you provide.
EDIT (working C++ code):
void * context;
WAVEFORMATEX wfx;
int success = getContext( &context );
success = myFun( context, "C:\\video.wmv", 176400, &wfx );
The equivalent in C# is:
IntPtr context;
WAVEFORMATEX wfx;
int success = getContext( out context );
success = myFun( context, "C:\\video.wmv", 176400, out wfx );
extern "C" __stdcall int getContext( void ** pContext );
[DllImport("myLib.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int getContext(out IntPtr context);
Well, based on the information you have provided I would say that you need to declare the C# function like this:
[DllImport("myLib.dll")]
public static extern int myFun(
IntPtr context,
string fileName,
uint bufferSize,
out WAVEFORMATEX wfx
);
And call the function like this:
WAVEFORMATEX wfx;
int result = DLLWrapper.myFun(context, #"C:\video.wmv", 176400, out wfx);
There's really no need for manual marshalling of this struct. It's a very simple blittable struct and it is much cleaner to let the framework handle the marshalling.
I am assuming that you are accurate when you state that the final struct parameter does not need to be initialised and its members are filled out by the function.
Packing looks reasonable. I don't think you need to build your DLL in any special way. I hope that you are picking up WAVEFORMATEX from the Windows header files and they already specify packing for that struct.
If you are still stuck then you should show the successful C++ calling code.
Judging from the comments, you still have a bug somewhere in your code. In such a situation, especially when you doubt the interop, it pays to make a simple reproduction to determine whether the interop is the problem, or not. Here is mine:
C++ DLL
#include <Windows.h>
#include <mmsystem.h>
#include <iostream>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
__declspec(dllexport) int __stdcall myFun(void * const context,
const char * const pszFileName, const unsigned int buffSize,
void * const pWaveFormatex)
{
std::cout << context << std::endl
<< pszFileName << std::endl
<< buffSize << std::endl;
WAVEFORMATEX wfx;
wfx.cbSize = 1;
wfx.nAvgBytesPerSec = 2;
wfx.nBlockAlign = 3;
wfx.nChannels = 4;
wfx.nSamplesPerSec = 5;
wfx.wBitsPerSample = 6;
wfx.wFormatTag = 7;
CopyMemory(pWaveFormatex, &wfx, sizeof(wfx));
return 666;
}
C# console app
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication13
{
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct WAVEFORMATEX
{
public ushort wFormatTag;
public ushort nChannels;
public uint nSamplesPerSec;
public uint nAvgBytesPerSec;
public ushort nBlockAlign;
public ushort wBitsPerSample;
public ushort cbSize;
}
[DllImport(#"Win32Project1.dll", EntryPoint = "?myFun##YGHQAXQBDI0#Z")]
public static extern int myFun(
IntPtr context,
string fileName,
uint bufferSize,
out WAVEFORMATEX wfx
);
static void Main(string[] args)
{
WAVEFORMATEX wfx;
int result = myFun((IntPtr)42, #"C:\video.wmv", 176400, out wfx);
Console.WriteLine(result);
Console.WriteLine(wfx.cbSize);
Console.WriteLine(wfx.nAvgBytesPerSec);
Console.WriteLine(wfx.nBlockAlign);
Console.WriteLine(wfx.nChannels);
Console.WriteLine(wfx.nSamplesPerSec);
Console.WriteLine(wfx.wBitsPerSample);
Console.WriteLine(wfx.wFormatTag);
Console.ReadLine();
}
}
}
Output
0000002A
C:\video.wmv
176400
666
1
2
3
4
5
6
7
The problem is passing the IntPtr. You're passing stack allocated variable(the one that holds actual pointer) to your code, but you want to pass the pointer. Just remove the "ref" keyword from your code.
My C declarations are as follows:
int myData(uint myHandle, tchar *dataName, long *Time, uint *maxData, DATASTRUCT **data);
typedef struct {
byte Rel;
__int64 Time;
char Validated;
unsigned char Data[1];
} DATASTRUCT;
My C# declarations are as follows:
[DllImport("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref DATASTRUCT[] data);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DATASTRUCT
{
public sbyte Rel;
public long Time;
public byte Validated;
public double Data;
}
I then call the managed function as follows:
string dataToShow = "description";
long Time;
uint maxData; // How many structs will be returned, i.e., how much data is available?
uint myHandle = 1;
DATASTRUCT[] dataInformation = new DATASTRUCT[3]; // Doesn't it matter what I specify as the array size?
myData(myHandle, dataToShow, out Time, out maxData, ref dataInformation);
Upon execution the above function will return successfully with only one structure even though there are 3 to return. Why is this so?
Additional information; I have tried passing the pointer to a pointer of an array of structs the following ways:
ref DATASTRUCT[] data; // It works, but it only returns one struct
[Out, MarshalAs(UnmanagedType.LPArray)] DATASTRUCT[] data; // returns the number of defined structs with garbage
As I understand it I might need to do some manual marshalling using IntPtr, I do not know how to implement this however, so any advice would be appreciated.
Okay, it seems as though your native library does the allocation, so really all you need to do is provide a pointer through which you can access the allocated data.
Change your API definition to (note, I changed the maxData param to uint, long is 64 bits in .NET and 32 bits in native.
[DllImportAttribute("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out uint Time, out uint maxData, out IntPtr pData);
Off the top of my head I can't quite remember if you need the out keyword for the final parameter, but I think so.
Then, call myData:
uint nAllocs = 0, time = 0;
IntPtr pAllocs = IntPtr.Zero;
myData(1, "description", out time, out nAllocs, out pAllocs);
Now, pAllocs should point to unmanaged memory, to marshal these into managed memory isn't too difficult:
[StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)]
public struct DATASTRUCT
{
public byte Rel;
public long Time;
public byte Validated;
public IntPtr Data; //pointer to unmanaged string.
}
int szStruct = Marshal.SizeOf(typeof(DATASTRUCT));
DATASTRUCT[] localStructs = new DATASTRUCT[nAllocs];
for(uint i = 0; i < nallocs; i++)
localStructs[i] = (DATASTRUCT)Marshal.PtrToStructure(new IntPtr(pAllocs.ToInt32() + (szStruct * i)), typeof(DATASTRUCT));
And now you should have an array of local structs.
A point to note
You may need to set your project to compile as x86, to standardize the size of an IntPtr to 4 bytes (DWORD) instead of AnyCPU's default 8.
A pointer to a pointer could be represented in your dllimport declaration as ref IntPtr data, so your declaration would become:
[DllImportAttribute("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref IntPtr data);
(As an aside, I think a long in C is just equivalent to an int in C#. Long in C# is an Int64, which would be a long long in C)
Marshalling your DATASTRUCT[] to an IntPtr can be done using the GCHandle class
DATASTRUCT [] dataInformation = new DATASTRUCT[3];
GCHandle gch = GCHandle.Alloc(dataInformation , GCHandleType.Pinned);
IntPtr ptr = gch.AddrOfPinnedObject();
myData(myHandle, dataToShow, out Time, out maxData, ref ptr);
//It's absolutely essential you do this next bit so the object can be garbage collected again,
//but it should only be done once the unmanaged code is definitely done with the reference.
gch.Free();
Using the Marshal class and the StructureToPtr or Copy methods of it would also be an option but for the purposes of proving the concept at least the GCHandle should do the trick, it's just not ideal for scenarios where the unmanaged code does long running operations because you've pinned this object in place and the GC can't move it until you free it.
I want to use the MiniDumpWriteDump function to create some custom dump files (mainly, i want to export a dump file that contains the minimum amount of information for the thread callstacks), but i am having difficulties defining the structures that need to be passed as a parameter to the callback function
[StructLayout(LayoutKind.Explicit)]
internal struct MINIDUMP_CALLBACK_OUTPUT
{
[FieldOffset(0)]
public ulong ModuleWriteFlags;
[FieldOffset(0)]
public ulong ThreadWriteFlags;
}
public struct MINIDUMP_CALLBACK_INFORMATION
{
public IntPtr CallbackRoutine;
public IntPtr CallbackParam;
}
public delegate bool MINIDUMP_CALLBACK_ROUTINE(
IntPtr CallBackParam,
MINIDUMP_CALLBACK_INPUT input,
MINIDUMP_CALLBACK_OUTPUT output);
[DllImport("dbghelp.dll")]
public static extern bool MiniDumpWriteDump(IntPtr hProcess, Int32 ProcessId, IntPtr hFile, int DumpType,
IntPtr ExceptionParam, IntPtr UserStreamParam, IntPtr CallStackParam);
And the call looks like this:
MINIDUMP_CALLBACK_INFORMATION mci;
MINIDUMP_CALLBACK_ROUTINE r = new MINIDUMP_CALLBACK_ROUTINE(MyCallback);
GC.KeepAlive(r);
mci.CallbackRoutine = Marshal.GetFunctionPointerForDelegate(r);
mci.CallbackParam = IntPtr.Zero;
IntPtr structPointer = Marshal.AllocHGlobal(Marshal.SizeOf(mci));
Marshal.StructureToPtr(mci, structPointer, true);
MiniDumpWriteDump(process[0].Handle, process[0].Id,
fs.SafeFileHandle.DangerousGetHandle(),
(int)MINIDUMP_TYPE.MiniDumpNormal,
Marshal.GetExceptionPointers(),
IntPtr.Zero,
structPointer);
Marshal.FreeHGlobal(structPointer);
So my question is how to define MINIDUMP_CALLBACK_INPUT:
The definition of the structures in C that pose problems are:
typedef struct _MINIDUMP_CALLBACK_INPUT
{
ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
union
{
MINIDUMP_THREAD_CALLBACK Thread;
MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
MINIDUMP_MODULE_CALLBACK Module;
MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
} u;
} MINIDUMP_CALLBACK_INPUT
typedef struct _MINIDUMP_MODULE_CALLBACK
{
PWCHAR FullPath;
ULONGLONG BaseOfImage;
ULONG SizeOfImage;
ULONG CheckSum;
ULONG TimeDateStamp;
VS_FIXEDFILEINFO VersionInfo;
PVOID CvRecord;
ULONG SizeOfCvRecord;
PVOID MiscRecord;
ULONG SizeOfMiscRecord;
} MINIDUMP_MODULE_CALLBACK
It's not a direct answer of your question but a workaround...
Do you know the ClrDump lib which does what you need ? I've used it for a project several years ago and it works fine for me.
Answer to author comment:
Read on the site:
ClrDump can produce small minidumps that contain enough information to recover the call stacks of all threads in the application.
I wrapped the API in the following class:
internal class ClrDump
{
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("clrdump.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern bool CreateDump(uint ProcessId, string FileName, MINIDUMP_TYPE DumpType, uint ExcThreadId, IntPtr ExtPtrs);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("clrdump.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern bool RegisterFilter(string FileName, MINIDUMP_TYPE DumpType);
[DllImport("clrdump.dll")]
public static extern FILTER_OPTIONS SetFilterOptions(FILTER_OPTIONS Options);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("clrdump.dll", SetLastError=true)]
public static extern bool UnregisterFilter();
[Flags]
public enum FILTER_OPTIONS
{
CLRDMP_OPT_CALLDEFAULTHANDLER = 1
}
[Flags]
public enum MINIDUMP_TYPE
{
MiniDumpFilterMemory = 8,
MiniDumpFilterModulePaths = 0x80,
MiniDumpNormal = 0,
MiniDumpScanMemory = 0x10,
MiniDumpWithCodeSegs = 0x2000,
MiniDumpWithDataSegs = 1,
MiniDumpWithFullMemory = 2,
MiniDumpWithFullMemoryInfo = 0x800,
MiniDumpWithHandleData = 4,
MiniDumpWithIndirectlyReferencedMemory = 0x40,
MiniDumpWithoutManagedState = 0x4000,
MiniDumpWithoutOptionalData = 0x400,
MiniDumpWithPrivateReadWriteMemory = 0x200,
MiniDumpWithProcessThreadData = 0x100,
MiniDumpWithThreadInfo = 0x1000,
MiniDumpWithUnloadedModules = 0x20
}
}
and then, somewhere in my initialization code, I called the method RegisterFilter which registers an internal filter for unhandled exceptions in the current process. If the process crashes with unhandled exception (it can be native or managed exception), the filter catches it and creates a minidump (with the specified file name). Here is a sample code for this:
StringBuilder sb = new StringBuilder();
sb.Append(Path.GetFileNameWithoutExtension(Application.ExecutablePath));
sb.Append("_");
sb.Append(DateTime.Now.ToString("yyyyMMddHHmmssFF"));
sb.Append(".dmp");
string dmpFilePath = Path.Combine(Path.GetTempPath(), sb.ToString());
ClrDump.RegisterFilter(_dmpFilePath, ClrDump.MINIDUMP_TYPE.MiniDumpNormal);
You can read this article to understand the different MINIDUMP_TYPE options, but I think the basic one (MiniDumpNormal) could fit your needs.
Use this code to define a structure with union in 32 bits:
[StructLayout(LayoutKind.Explicit)]
internal struct MINIDUMP_CALLBACK_INPUT
{
[FieldOffset(0)]
UInt32 ProcessId;
[FieldOffset(4)]
IntPtr ProcessHandle;
[FieldOffset(8)]
UInt32 CallbackType;
[FieldOffset(12)]
MINIDUMP_THREAD_CALLBACK Thread;
[FieldOffset(12)]
MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
[FieldOffset(12)]
MINIDUMP_MODULE_CALLBACK Module;
[FieldOffset(12)]
MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
[FieldOffset(12)]
MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
};
I think that on a 64 bits platform, the offsets should be respectively { 0, 8, 16, 24 } or { 0, 4, 12, 16 } whether ULONG is 64 or 32 bits.
Edit
I think you can use the MarshalAsAttribute to specifically convert fields:
[StructLayout(LayoutKind.Sequential)]
struct VS_FIXEDFILEINFO
{
UInt32 dwSignature;
UInt32 dwStrucVersion;
UInt32 dwFileVersionMS;
UInt32 dwFileVersionLS;
UInt32 dwProductVersionMS;
UInt32 dwProductVersionLS;
UInt32 dwFileFlagsMask;
UInt32 dwFileFlags;
UInt32 dwFileOS;
UInt32 dwFileType;
UInt32 dwFileSubtype;
UInt32 dwFileDateMS;
UInt32 dwFileDateLS;
}
[StructLayout (LayoutKind.Sequential)]
struct MINIDUMP_MODULE_CALLBACK
{
[MarshalAs(UnmanagedType.LPWStr)]
String FullPath;
UInt64 BaseOfImage;
UInt32 SizeOfImage;
UInt32 CheckSum;
UInt32 TimeDateStamp;
VS_FIXEDFILEINFO VersionInfo;
IntPtr CvRecord;
UInt32 SizeOfCvRecord;
IntPtr MiscRecord;
UInt32 SizeOfMiscRecord;
}
I take it that it is the union that is giving you trouble?
If CallbackType==KernelMinidumpStatusCallback, then the CALLBACK_INPUT structure is defined as:
ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
HRESULT Status;
If CallbackType==ThreadCallback, then it is:
ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
MINIDUMP_THREAD_CALLBACK Thread;
If CallbackType==ThreadExCallback, then it is:
ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
And so on (this is from MSDN) -- it looks like that 4th member can be one of 8 different types, depending on what CallbackType is equal to. Internally, Windows will use the same chunk of memory for all of these structures (padding the smaller ones to the largest one's size). In C++, it's an easy typecast to get the type you need.
I'm not sure how to do this in C#. I have used MiniDumpWriteDump in C++, but never used the callback function. If you could be sure that CallbackType was always one value, you could just code that one structure, but I don't know if this is the case.
Sorry I can't provide more information, but maybe this helps describe the problem.