I have a dll which accepts a struct that contains a pointer to a function to do a callback.
How can I get an IntPtr to a function of my application to build the struct?
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class OPERATION {
public uint OperationID;
public IntPtr Context;
public IntPtr Callback; -> How to pass this?
}
Here is the delegate accepting the OPERATION struct
public delegate void MY_CALLBACK([In] OPERATION operation, [In] uint msgId, [In] IntPtr msgDataPtr);
use Marshal.GetFunctionPointerForDelegate
Maybe the Marshal.GetFunctionPointerForDelegate method may help you.
Related
In C++, calling this function is just as simple as:
CertEnumSystemStore(CERT_SYSTEM_STORE_CURRENT_USER, NULL, NULL, (PFN_CERT_ENUM_SYSTEM_STORE)addr);
Addr would just be the base address of where the function resides
In C#, you can't just pass in an address, and will have to pass in a function, which is not what I want in this case. Below is a snippet of my code
public delegate bool CertEnumSystemStoreCallback([In, MarshalAs(UnmanagedType.LPWStr)] String pvSystemStore, uint dwFlags, ref CERT_SYSTEM_STORE_INFO pStoreInfo, uint pvReserved, [In, MarshalAs(UnmanagedType.LPWStr)] String pvArg);
[DllImport("crypt32.dll", CharSet = CharSet.Unicode)]
public static extern uint CertEnumSystemStore(uint dwFlags, uint pvSystemStoreLocationPara, String pvArg, CertEnumSystemStoreCallback pfnEnum);
[StructLayout(LayoutKind.Sequential)]
public struct CERT_SYSTEM_STORE_INFO
{
uint cbSize;
}
CertEnumSystemStore(CERT_SYSTEM_STORE_CURRENT_USER, 0, null, (CertEnumSystemStoreCallback)addr);
I get a type conversion error that tells me it can't cast type InPtr to type CertEnumSystemStoreCallback.
What can I do to just pass an address containing the function instead of having to supply the function name itself?
First: I'm sorry if the title is wrong. I'm not sure how to name my problem.
In my C API I have a function:
MYAPI_STATUS SetParam(void *hInst, unsigned long param, void *value);
This function accepts different types of pointers depending on param type. Like this:
SetParam(hInst, 1, (void*)"somevalue");
int x = 55;
SetParam(hInst, 2, &x);
I'm just writing a wrapper/binding in C# and I have a problem.
[DllImport("myapi", CallingConvention = CallingConvention.Cdecl]
public static extern uint SetParam(IntPtr hInst, uint paramCode, IntPtr paramValue);
What's the best way to replicate behaviour from C? So the function would look like:
public static uint SetParam(IntPtr hInst, uint paramCode, ref object paramValue);
or possibly:
public static uint SetParam(IntPtr hInst, uint paramCode, object paramValue);
I solved it by marshalling manually first checking type of object if the object is string then I use Marshal.StringToHGlobalAnsi if it's something else then I marshall differently based on what I need.
If someone has any better solution feel free to write :)
The * sign in C programming means give parameter by reference, so this code is not match:
public static uint SetParam(IntPtr hInst, uint paramCode, object paramValue);
Because it gives parameter by value.
This code is very similar to what you want:
public static uint SetParam(IntPtr hInst, uint paramCode, ref object paramValue);
But there is a bit difference. When you using ref before a parameter you have to initialize it before sending to the method, but by using out you don't have this limitation for passing it. So I think the best possible match will be this code:
public static uint SetParam(IntPtr hInst, uint paramCode, out object paramValue);
I have a C structure used for callback that I need to marshall to C# .NET:
struct CTMDeviceInfo {
enum CTMDeviceType eDeviceType;
char * szDeviceModel;
char * szDeviceSubModel;
int32_t * piDeviceID;
};
This is my C# version:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceInfo
{
public CTMDeviceType deviceType;
[MarshalAs(UnmanagedType.LPStr)]
public string deviceModel;
[MarshalAs(UnmanagedType.LPStr)]
public string deviceSubModel;
public IntPtr deviceId;
};
Which is used inside another structure:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceError
{
public CTMDeviceInfo deviceInfo;
[MarshalAs(UnmanagedType.I4)]
public Int32 resultCode;
[MarshalAs(UnmanagedType.I4)]
public Int32 extendedResultCode;
public IntPtr denomination;
public IntPtr changeDue;
};
My problem is that the "IntPtr deviceId" does not consistently return the correct value every time a callback was made.
I was expecting an integer value of 5, 15 or 16 but it keeps returning random values like 106, 865412, 652272, etc.
I don't know what I did wrong. What I did though is to prevent the callback in my managed code to be garbage collected using GCHandle.
Here is the sequence on how I did it:
From my unmanaged code I have this CDECL callback method:
void ctm_add_device_error_event_handler(CTMDeviceErrorCallback);
typedef void (CTMDeviceErrorCallback) (struct CTMEventInfo, struct CTMDeviceError );
This is my managed code:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void OnDeviceErrorCallBack(CTMEventInfo evtInfo, CTMDeviceError deviceError);
[DllImport("libctmclient-0.dll", EntryPoint = "ctm_add_device_error_event_handler", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddDeviceErrorEventHandler([MarshalAs(UnmanagedType.FunctionPtr)] OnDeviceErrorCallBack deviceErrorCallBack);
OnDeviceErrorCallBack deviceErrorCallback;
GCHandle deviceErrorCallbackGCHandle;
deviceErrorCallback = new OnDeviceErrorCallBack(OnDeviceError);
deviceErrorCallbackGCHandle = GCHandle.Alloc(deviceErrorCallback);
AddDeviceErrorEventHandler(deviceErrorCallback);
And this is where the callback is handled:
public void OnDeviceError(CTMEventInfo evtInfo, CTMDeviceError deviceError)
{
int nDeviceId = Marshal.ReadInt32(deviceError.deviceInfo.deviceId);
}
I tried to use unsafe to use pointers directly but the issue is still the same.
public unsafe int *deviceId; //instead of IntPtr
int nDeviceId = 0;
unsafe
{
nDeviceId = *(deviceError.deviceInfo.deviceId);
}
I'm sure that my unmanaged code returned the correct value because I have logs but when it reached in my managed code, somehow another value was returned.
It's like it is reading on a different reference or something.
Hope somewhat could help me because I am stuck for a while now.
Thanks!
Use following c# structure. You can get the two string from the pointer later in the code. :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceInfo
{
public CTMDeviceType deviceType;
public IntPtr deviceModel;
public IntPtr deviceSubModel;
public IntPtr deviceId;
};
This structure is define at http://msdn.microsoft.com/en-us/library/windows/hardware/ff541621%28v=vs.85%29.aspx
typedef struct _FILTER_MESSAGE_HEADER {
ULONG ReplyLength;
ULONGLONG MessageId;
} FILTER_MESSAGE_HEADER, *PFILTER_MESSAGE_HEADER;
I defined it in C# code as below:
[StructLayout(LayoutKind.Sequential)]
public struct FILTER_MESSAGE_HEADER {
public uint replyLength;
public ulong messageId;
};
I only define FILTER_MESSAGE_HEADER in C# code, PFILTER_MESSAGE_HEADER isn't.
How should I do to define PFILTER_MESSAGE_HEADER??
P/S: I want to define PFILTER_MESSAGE_HEADER to use this struct in a function.
You don't have to (can't) define PFILTER_MESSAGE_HEADER. Just specify it as either out or ref as appropriate.
[DllImport("foo")]
void SomeMethod(ref FILTER_MESSAGE_HEADER lpMessageBuffer);
If you are specifically interested in FilterGetMessage, I'm not sure what if any dll it is exported from, but one possible signature would be as below:
[DllImport(fltmgr, CharSet=CharSet.Unicode, ExactSpelling=true, PreserveSig=false)]
void FilterGetMessage(
CommunicationPortSafeHandle hPort,
ref FILTER_MESSAGE_HEADER lpMessageBuffer,
uint dwMessageBufferSize,
IntPtr lpOverlapped);
I used PreserveSig to automatically translate the HRESULT to an exception in the event of failure, the CharSet specification is defensive and results in the need for ExactSpelling. CommunicationPortSafeHandle would be a class which inherits from SafeHandleMinusOneIsInvalid based off of the documentation on FilterConnectCommunicationPort.
You would use this signature as:
FILTER_MESSAGE_HEADER header;
FilterGetMessage(hFilter, ref header,
Marshal.SizeOf(typeof(FILTER_MESSAGE_HEADER)), IntPtr.Zero);
When I want get total value of memory in C# I found a kernel32 function in MSDN to invoke data from system. MSDN declare function this way:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
but this don't work correctly. I change "ref" to "[In, Out]" then it work correctly.
How can tell me what is [In, Out] parameters in C#?
In: http://msdn.microsoft.com/de-de/library/system.runtime.interopservices.inattribute.aspx
Out: http://msdn.microsoft.com/de-de/library/system.runtime.interopservices.outattribute.aspx
Short: They control the way data is marshalled. In this case, where you specify both of them, it means that data is marshalled to both sides (caller and callee).
The out and the ref parameters are used to return values in the same variables, ref is enough if you don't know you will use it in or out.
Out if you just want to use the variable to receive data from the function, In if you just want to send data to the function.
ref if you want to send and receive data from a function, if you put nothing so it will be In by default
Note: ref and out parameters are very useful when your method needs to return more than one values.
The following definition works (define the MEMORYSTATUSEX as a class):
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GlobalMemoryStatusEx(MEMORYSTATUSEX lpBuffer);
[StructLayout(LayoutKind.Sequential)]
public sealed class MEMORYSTATUSEX {
public uint dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
public uint dwMemoryLoad;
public ulong ullTotalPhys;
public ulong ullAvailPhys;
public ulong ullTotalPageFile;
public ulong ullAvailPageFile;
public ulong ullTotalVirtual;
public ulong ullAvailVirtual;
public ulong ullAvailExtendedVirtual;
}
Usage
var status = new MEMORYSTATUSEX();
GlobalMemoryStatusEx(status);
If you look at the function definition on MSDN it will tell you whether the parameters are In/Out:
BOOL WINAPI GlobalMemoryStatusEx(
__inout LPMEMORYSTATUSEX lpBuffer
);
In general if it says out, you should use a ref parameter, it makes is easier on any future developers trying to figure out how the code is working. When looking at the function call, you know the developer meant for the argument to be affected.