Marshal va_list in C# delegate - c#

I'm trying to make this work from c#:
C header:
typedef void (LogFunc) (const char *format, va_list args);
bool Init(uint32 version, LogFunc *log)
C# implementation:
static class NativeMethods
{
[DllImport("My.dll", SetLastError = true)]
internal static extern bool Init(uint version, LogFunc log);
[UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = true)]
internal delegate void LogFunc(string format, string[] args);
}
class Program
{
public static void Main(string[] args)
{
NativeMethods.Init(5, LogMessage);
Console.ReadLine();
}
private static void LogMessage(string format, string[] args)
{
Console.WriteLine("Format: {0}, args: {1}", format, DisplayArgs(args));
}
}
What happens here is that the call to NativeMethods.Init calls back LogMessage and passes data from unmanaged code as parameters. This works for most cases in which the arguments are strings. However, there is a call on which the format is:
Loaded plugin %s for version %d.
and the args contains only a string (the plugin name). They do not contain the version value, which makes sense since I used string[] in the delegate declaration. Question is, how should I write the delegate to get both the string and the int?
I tried using object[] args and got this exception:
An invalid VARIANT was detected during a conversion from an unmanaged VARIANT to a managed object. Passing invalid VARIANTs to the CLR can cause unexpected exceptions, corruption or data loss.
EDIT:
I could change the delegate signature to this:
internal delegate void LogFunc(string format, IntPtr args);
I could parse the format and find out how many arguments to expect and of what type. E.g. for Loaded plugin %s for version %d. I would expect a string and an int. Is there a way to get these 2 out of that IntPtr?

Just in case it helps someone, here's a solution for marshaling the arguments. The delegate is declared as:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = true)] // Cdecl is a must
internal delegate void LogFunc(string format, IntPtr argsAddress);
The argsAddress is the unmanaged memory address where the array starts (I think). The format gives the size of the array. Knowing this I can create the managed array and fill it. Pseuso-code:
size <- get size from format
if size = 0 then return
array <- new IntPtr[size]
Marshal.Copy(argsAddress, array, 0, size);
args <- new string[size]
for i = 0 to size-1 do
placeholder <- get the i-th placeholder from format // e.g. "%s"
switch (placeholder)
case "%s": args[i] <- Marshal.PtrToStringAnsi(array[i])
case "%d": args[i] <- array[i].ToString() // i can't explain why the array contains the value, but it does
default: throw exception("todo: handle {placeholder}")
To tell the truth, I'm not sure how this works. It just seems to get the right data. I'm not claiming it is correct though.

Another approach is to pass the va_list back to native code, something like calling vprintf in .net.
I had the same issue, and I wanted it cross platform. So I wrote a sample project to demonstrate how it could work on several platforms.
See https://github.com/jeremyVignelles/va-list-interop-demo
The basic idea is :
You declare your callback delegate:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void LogFunc(string format, IntPtr args);
You pass your callback as you did:
NativeMethods.Init(5, LogMessage);
In the callback, you handle the specific cases of the different platforms. You need to understand how it works on each platform. From my testing and understanding, you can pass the IntPtr as-is to the vprintf* family of functions on Windows (x86,x64) and Linux x86, but on Linux x64, you will need to copy a structure for that to work.
See my demo for more explanations.
EDIT : We posted an issue on .net runtime's repository a while back, you can see it here https://github.com/dotnet/runtime/issues/9316 . Unfortunately, it didn't went far because we lacked a formal proposal.

I understand there's also an "__arglist" keyword available in C#:
http://www.dotnetinterop.com/faq/?q=Vararg
http://bartdesmet.net/blogs/bart/archive/2006/09/28/4473.aspx

.NET can (to some extent) marshal between va_list and ArgIterator. You can try this:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = true)]
internal delegate void LogFunc(string format, ArgIterator args);
I am not sure how the arguments are going to be passed (strings as pointers, probably). You may have some luck with ArgIterator.GetNextArgType. Eventually, you will probably have to parse the placeholders in the format string to get the argument types.

Related

C# app crashes on exit after using C++ function with delegates

My C# app needs to talk with a DLL written in C++. I don't have the code of this DLL, but I have the code for a demo app (also in C++) that uses the DLL and works.
Here's the interesting code from the C++ demo app. It has two functions, one of which accepts a callback set of 4 delegates.
typedef BOOL (CBAPI* OPENSCANNERSDK)(HWND hwnd, TCallBackSet* callbackSet, wchar_t* configPath);
typedef void (CBAPI* CLOSESCANNERSDK)();
typedef struct TCallBackSet
{
TOnScannerStatusEvent scannerStatusEvent;
TOnScannerNotifyEvent scannerNotifyEvent;
TOnRFIDStatusEvent rfidStatusEvent;
TOnRFIDNotifyEvent rfidNotifyEvent;
} TCallBackSet;
typedef void (cdecl* TOnScannerStatusEvent ) (int scannerStatus);
typedef void (cdecl* TOnScannerNotifyEvent) (int scannerNotify, int lParam);
typedef void (cdecl* TOnRFIDStatusEvent ) (int rfidStatus);
typedef void (cdecl* TOnRFIDNotifyEvent ) (int rfidnotify);
So, I have to call OpenScannerSDK, pass a callback set with pointers to some functions, do some stuff, and finally call CloseScannerSDK.
I have declared this in my C# app like so:
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "_OpenScannerSDK")]
extern public static bool OpenScannerSDK(IntPtr hwnd, TCallBackSet callbackSet,
[MarshalAs(UnmanagedType.LPWStr)]string configPath);
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "_CloseScannerSDK")]
extern public static void CloseScannerSDK();
[StructLayout(LayoutKind.Sequential)]
public class TCallBackSet
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TOnScannerStatusEvent(int scannerStatus);
[MarshalAs(UnmanagedType.FunctionPtr)]
public TOnScannerStatusEvent ScannerStatusEvent;
(I have removed the other 3 callbacks for brevity)
}
Finally I use the library like so:
var callback = new TCallBackSet() { ... set the delegates ... }
OpenScannerSDK(IntPtr.Zero, callback, ".");
... do other stuff...
CloseScannerSDK();
All this seems to work - both OpenScannerSDK and CloseScannerSDK and all the others I use between them work correctly. The problem is that as soon as the application tries to exit, I get an APPCRASH in KERNELBASE.dll. I don't see any relevant information in the crash report file. I have noticed that if I do not call OpenScannerSDK, but just some other functions of the DLL that are not related to delegates, the APPCRASH doesn't happen.
I also tried GC.KeepAlive for the delegates, no effect.
I had a similar issue porting other C DLL to C# some years ago.
Function pointers in C# are represented as delegates. Internally, delegates are class instances and they're collected by GC the same way other objects are collected.
Probably in your DLL there's some method that "stops" the API, making it stopping invoking the C++ function pointers. You must call this before you close the application.
Probably your app collects the delegate objects and when the C++ DLL tries to invoke from unmanaged code, finds an invalid object reference.
It's interesting to keep the references in C# of these delegates in private fields, to avoid them to be collected when the application is running. The sympton of this issue is that the application crashes intermitently.
Hope this helps.
To anyone interested, here is the final working solution.
First, CallBackSet is defined like so:
[StructLayout(LayoutKind.Sequential)]
public class CallBackSet
{
public IntPtr ScannerStatusEvent;
public IntPtr ScannerNotifyEvent;
public IntPtr RfidStatusEvent;
public IntPtr RfidNotifyEvent;
}
And then:
OnScannerStatusEvent onScannerStatus = (ScannerStatus status) => {...};
OnScannerNotifyEvent onScannerNotify = (ScannerNotify notify, short lparam) => {...};
OnRfidStatusEvent onRfidStatus = (RfidStatus status) => {...};
OnRfidNotifyEvent onRfidNotify = (RfidNotify notify) => {...};
GCHandle.Alloc(onScannerStatus);
GCHandle.Alloc(onScannerNotify);
GCHandle.Alloc(onRfidStatus);
GCHandle.Alloc(onRfidNotify);
callbackSet = new CallBackSet()
{
RfidNotifyEvent = Marshal.GetFunctionPointerForDelegate(onRfidNotify),
RfidStatusEvent = Marshal.GetFunctionPointerForDelegate(onRfidStatus),
ScannerNotifyEvent = Marshal.GetFunctionPointerForDelegate(onScannerNotify),
ScannerStatusEvent = Marshal.GetFunctionPointerForDelegate(onScannerStatus)
};
callbackPtr = Marshal.AllocHGlobal(Marshal.SizeOf(callbackSet));
Marshal.StructureToPtr(callbackSet, callbackPtr, false);
OpenScannerSDK(IntPtr.Zero, callbackPtr, ".");
Upon further testing I found that actually OpenScannerSDK always returns True, which made me believe it was working correctly, but it may not be the case. I did several tests, including passing a TCallBackSet struct that does not have any members and OpenScannerSDK still returned True.
So it seems the problem is with TCallBackSet and these delegates. Something's wrong there. I tried passing the CallBackSet w and w/o ref, no difference. Noticed int in C++ is not the same as int in C#, so changed functions signatures to use short instead of int. None of these made any difference.

calling C++ functions containing callbacks in C#

hey all im trying to get my head around calling this c++ function in c#:
BOOL __stdcall CodecStart(int hRadio,void __stdcall (*CallbackFunc)(void *),void *CallbackTarget);
this is from a WinRadio api found here http://www.winradio.com/home/g305_sdk.htm.
i did find that other people asked about calling this specific function on the net and they had:
public delegate void CallbackFunc( IntPtr p);
[DllImport("WRG305API.dll")]
public static extern bool CodecStart(int hRadio, CallbackFunc func, IntPtr CallbackTarget);
but i cant figure out how to implement this further.
any thoughts or guidance as to how to call this?
many thanks
Here's a simple implementation that will put it all together.
class WinRadioWrapper
{
public delegate void CallbackFunc( IntPtr pData );
[DllImport( "WRG305API.dll" )]
public static extern bool CodecStart( int hRadio, CallbackFunc func, IntPtr CallbackTarget );
public bool CodecStartTest(int hRadio)
{
bool bStarted = CodecStart( hRadio, MyCallbackFunc, IntPtr.Zero );
return bStarted;
}
// Note: this method will be called from a different thread!
static void MyCallbackFunc( IntPtr pData )
{
// Sophisticated work goes here...
}
}
Note that because MyCallbackFunc will be executed on a different thread, I chose to make is static. This way you won't be tempted to access WinRadioWrapper data members.
For simplicity I passed an IntPtr.Zero parameter to the callback, but this can point to any data that you'd like to use in the callback.[Please ignore this paragraph] Look into Marshal.StructureToPtr if you'd like to pass data to the callback, but make sure to also pin the data that you're passing in order to make sure it's not garbage-collected (see GCHandle for more details).
EDIT:
With the interesting words by svick (thanks!), I realize I was mixing a copied object with a pinned one.
So, to sort things out:
Marshal.StructureToPtr should be used if you want to copy an existing data structure and then pass it to the callback function.
If, on the other hand, you'd like to use and existing data structure (e.g. for modifying its content), the you should use GCHandle in order to pin it in memory and prevent it from being garbage-collected.This, however, will add some maintenance overhead for the GCHandle.
A callback function is a code that is called by a dll (you're importing in this case) that performs some functions. You also need to learn how to work with delegates in c#. You can implement the code like this:
public void MyCallback(IntPtr p)
{
//do something
}
and then your dll call will be like this:
[DllImport("WRG305API.dll")]
public static extern bool CodecStart(int hRadio, func, IntPtr CallbackTarget);
If you need more guidance, post the C++ version of the code you want to convert and we can help you with the C# version.
All you need to do is to create a c# function that matches the signature of the delegate you declared. Create a delegate, hold on to a reference to this delegate so it doesn't get garbage collected, and call the dll import with the delegate as the callback.
so you would having something like this:
public void MyCallback(IntPtr P)
{
//do something
}
// somewhere else in your code
var cb = new CallbackFunc(MyCallback);
CodecStart(..., cb, ...);

How can I marshall a vector<int> from a C++ dll to a C# application?

I have a C++ function that produces a list of rectangles that are interesting. I want to be able to get that list out of the C++ library and back into the C# application that is calling it.
So far, I'm encoding the rectangles like so:
struct ImagePatch{
int xmin, xmax, ymin, ymax;
}
and then encoding some vectors:
void MyFunc(..., std::vector<int>& rectanglePoints){
std::vector<ImagePatch> patches; //this is filled with rectangles
for(i = 0; i < patches.size(); i++){
rectanglePoints.push_back(patches[i].xmin);
rectanglePoints.push_back(patches[i].xmax);
rectanglePoints.push_back(patches[i].ymin);
rectanglePoints.push_back(patches[i].ymax);
}
}
The header for interacting with C# looks like (and works for a bunch of other functions):
extern "C" {
__declspec(dllexport) void __cdecl MyFunc(..., std::vector<int>& rectanglePoints);
}
Are there some keywords or other things I can do to get that set of rectangles out? I found this article for marshalling objects in C#, but it seems way too complicated and way too underexplained. Is a vector of integers the right way to do this, or is there some other trick or approach?
The STL is a C++ specific library, so you cant directly get it across as one object to C#.
The one thing that is guaranteed about std::vector is that &v[0] points to the first element and all the elements lie linearly in memory (in other words, its just like a C array in terms of memory layout)
So marshal as array of int... which shouldn't be hard - There are lot of examples on the web.
Added
Assuming you only pass the data from C++ to C# :
C# cannot handle a C++ vector object, so do not try passing it by reference : Instead your C++ code must return a pointer to an array of ints...
If you are not going to be using this function from multiple threads, you can use static storage :
int *getRects(bool bClear)
{
static vector<int> v; // This variable persists across invocations
if(bClear)
{
v.swap(vector<int>());
}
else
{
v.clear();
// Fill v with data as you wish
}
return v.size() ? &v[0] : NULL;
}
call getRects(true) if the returned data is significant in size, so you release the memory in v.
For simplicity, instead of passing out the size of the vector data too, just put a sentinel value at the end (like say -1) so the C# code can detect where the data ends.
Yes. You can. Actually, not just std::vector, std::string, std::wstring, any standard C++ class or your own classes can be marshaled or instantiated and called from C#/.NET.
Wrapping a std::vector<any_type> in C# is indeed possible with just regular P/Invoke Interop, it is complicated though. even a std::map of any type can be done in C#/.NET.
public class SampleClass : IDisposable
{
[DllImport("YourDll.dll", EntryPoint="ConstructorOfYourClass", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void SampleClassConstructor(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DestructorOfYourClass", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void SampleClassDestructor(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomething", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomethingElse", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject, int x);
IntPtr ptr;
public SampleClass(int sizeOfYourCppClass)
{
this.ptr = Marshal.AllocHGlobal(sizeOfYourCppClass);
SampleClassConstructor(this.ptr);
}
public void DoSomething()
{
DoSomething(this.ptr);
}
public void DoSomethingElse(int x)
{
DoSomethingElse(this.ptr, x);
}
public void Dispose()
{
if (this.ptr != IntPtr.Zero)
{
// The following 2 calls equals to "delete object" in C++
// Calling the destructor of the C++ class will free the memory allocated by the native c++ class.
SampleClassDestructor(this.ptr);
// Free the memory allocated from .NET.
Marshal.FreeHGlobal(this.ptr);
this.ptr = IntPtr.Zero;
}
}
}
Please see the below link,
C#/.NET PInvoke Interop SDK
(I am the author of the SDK tool)
Once you have the C# wrapper class for your C++ class ready, it is easy to implement ICustomMarshaler so that you can marshal the C++ object from .NET.
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.icustommarshaler.aspx
I'm pretty sure you can't do this. You have to be able to translate the C++ code directly to a C# class, so you would at least have to replicate the internals of the vector class to marshall it correctly. I'm also pretty sure you won't be able to move references across the boundary, you'll have to use IntPtr (raw pointers). The approach that i know works is to marshall a raw array of the structs.

C#. How to pass message from unsafe callback to managed code?

Is there a simple example of how to pass messages from unsafe callback to managed code?
I have a proprietary dll which receives some messages packed in structs and all is coming to a callback function.
The example of usage is as follows but it calls unsafe code too. I want to pass the messages into my application which is all managed code.
*P.S. I have no experience in interop or unsafe code. I used to develop in C++ 8 yrs ago but remember very little from that nightmarish times :)
P.P.S. The application is loaded as hell, the original devs claim it processes 2mil messages per sec.. I need a most efficient solution.*
static unsafe int OnCoreCallback(IntPtr pSys, IntPtr pMsg)
{
// Alias structure pointers to the pointers passed in.
CoreSystem* pCoreSys = (CoreSystem*)pSys;
CoreMessage* pCoreMsg = (CoreMessage*)pMsg;
// message handler function.
if (pCoreMsg->MessageType == Core.MSG_STATUS)
OnCoreStatus(pCoreSys, pCoreMsg);
// Continue running
return (int)Core.CALLBACKRETURN_CONTINUE;
}
Thank you.
You can use Marshal class to deal with interop code.
example:
C:
void someFunction(int msgId, void* funcCallback)
{
//do something
funcCallback(msgId); //assuming that function signature is "void func(int)"
}
C#
[DllImport("yourDllname.dll")]
static extern someFunction(int msgId, IntPtr funcCallbackPtr);
public delegate FunctionCallback(int msgId);
public FunctionCallback functionCallback;
public void SomeFunction(int msgId, out FunctionCallback functionCallback)
{
IntPtr callbackPtr;
someFunction(msgId, callbackPtr);
functionCallback = Marshal.DelegateToPointer(callbackPtr);
}
you can call as:
SomeFunction(0, (msgIdx) => Console.WriteLine("messageProcessed"));
I hope I did it right. I did not try to compile it :)

Invalid Variant crash

I have a situation where I've wrapped a Native C++ DLL with C++/CLI for eventual use in C#.
There are a few callback functions that are causing some issues at run time. Particularly, I get the following exception:
An unhandled exception of type
'System.Runtime.InteropServices.InvalidOleVariantTypeException'
occurred in ToadWrapTest.dll
Additional information: Specified OLE
variant is invalid.
On this line of code (C++/CLI):
public delegate int ManagedCallbackFunction (Object^ inst, const Object^ data);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
ManagedCallbackFunction^ m_callbackFn;
int intermidiaryCallback(void * pInstance, const void * pData)
{
void* temp = (void*)pData;
System::IntPtr ip1 = IntPtr(pInstance);
System::IntPtr ip2 = IntPtr(temp);
Object^ oInst = Marshal::GetObjectForNativeVariant(ip1);
Object^ oData = Marshal::GetObjectForNativeVariant(ip2);
//invoke the callback to c#
//return m_callbackFn::Invoke(oInst, oData);
return 0;
};
The reason I've made this "intermediary callback" was an attempt to circumvent the Invalid variant exception being thrown when I tried to directly map the delegate from C# to the native C++ code. As an attempted work-around, I declare a delegate on the C# side and pass that funcptr to the C++/CLI wrapper. I then pass the intermediary funcptr to the native C++ and just daisy chain the calls together.
What I know is that it all works in native C++ world. The problem is mapping the void* to the managed world. The following code shows the native C++ version of the callback:
int (*CallbackFunction) (void *inst, const void *data);
If anyone can help here, I'd really appreciate it.
Are pInstance and pData really VARIANT? If they are, I would expect your callback function to be more strongly typed:
int (*CallbackFunction)(VARIANT *inst, VARIANT *data);
If that's the case, in your code you should be able to look at the actual VARIANT to hand check it. If you are not really getting VARIANTs (ie, you are really just getting void * pointers), you shouldn't try to turn them into C# objects since there is no inherent meaning to them. They should get passed through as IntPtr. If you know that they should have some other type of inherent meaning, you need to marshal them as appropriate types.
Big Thanks to plinth on this one! I am posting the final solution below to anyone else who has to deal with 3rd party fun like this one! Please feel free to critique, as I am not done optimizing the code. This may still be to roundabout a solution.
First, the callback functions became:
public delegate int ManagedCallbackFunction (IntPtr oInst, IntPtr oData);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
ManagedCallbackFunction^ m_callbackFn;
Big props on this one. It just plain won't work if you try to cast from void* directly to Object^. Using the IntPtr and my intermediary callback:
int intermidiaryCallback(void * pInstance, const void * pData)
{
void* temp = (void*)pData;
return m_callbackFn->Invoke(IntPtr(pInstance), IntPtr(temp));
};
We finally get a working model on the C# side with some massaging of the objects:
public static int hReceiveTestMessage(IntPtr pInstance, IntPtr pData)
{
// provide object context for static member function
helloworld2 hw = (helloworld2)GCHandle.FromIntPtr(pInstance).Target;
if (hw == null || pData == null)
{
Console.WriteLine("hReceiveTestMessage received NULL data or instance pointer\n");
return 0;
}
// populate message with received data
IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataPacketWrap(pData)));
DataPacketWrap dpw = (DataPacketWrap)GCHandle.FromIntPtr(ip2).Target;
uint retval = hw.m_testData.load_dataSets(ref dpw);
// display message contents
hw.displayTestData();
return 1;
}
I mention "massaging" the objects because the delegate is not specific to this callback function and I don't know what object pData will be until run time(from the delegates POV). Because of this issue, I have to do some extra work with the pData object. I basically had to overload the constructor in my wrapper to accept an IntPtr. Code is provided for full "clarity":
DataPacketWrap (IntPtr dp)
{
DataPacket* pdp = (DataPacket*)(dp.ToPointer());
m_NativeDataPacket = pdp;
};

Categories

Resources