I wrote a C# application that uses an unmanaged c++ dll via managed c++ dll.
In the unmanaged dll, there's a callback that one of its params is std::string&.
I can't seem to find the right way to wrap this with the managed dll.
When I use String^, the callback works, but the C# application does not get anything in the string.
When I used String^%, things started to crash in other places that does not seem to be related (maybe memory corruption).
So my question is, what's the right way to do this?
Thanks
I can't copy-paste code here, but I'll try to explain again.
I can't use marshaling in the managed c++ section because I'm not calling a function, but passing a c# delegate for a callback.
In the unmanaged dll, I have a callback that requires a function like this: void Func(unsigned int, int, std:string &).
My goal is to pass a c# delegate from my program to that callback, so in the unmanaged code,
I made a delegate like this: delegate void DEL(unsigned int a, int b, String ^ c) and a function like: void mFunc(DEL ^ del), and that function marshal's the delegate into a cb that the unmanaged callback subscribe function accepts. The unsigned int and int works fine, but the string is always "" when the C# function is triggered.
Posting some code would help me understand better and give a better answer; but there is no automatic conversion or marshalling of String^ to std::string. You would need to do to marshalling yourself to get the string back to the C# code. A quick search can provide with details on how to do this.
http://msdn.microsoft.com/en-us/library/42zy2z41.aspx
I don't believe that the marshalling can deal with std::string. I think you need to make your own callback that passes char * and then write the glue code between the two.
Also, once the delegate is marshalled into a callback, that callback does not count as a reference to the object that the delegate might have been made from. So if the delegate is not a static method, you need to stuff it somewhere for the lifetime of the unmanaged callback.
Related
In C, there is a function that accepts a pointer to a function to perform comparison:
[DllImport("mylibrary.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int set_compare(IntPtr id, MarshalAs(UnmanagedType.FunctionPtr)]CompareFunction cmp);
In C#, a delegate is passed to the C function:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int CompareFunction(ref IntPtr left, ref IntPtr right);
Currently, I accept Func<T,T,int> comparer in a constructor of a generic class and convert it to the delegate. "mylibrary.dll" owns data, managed C# library knows how to convert pointers to T and then compare Ts.
//.in ctor
CompareFunction cmpFunc = (ref IntPtr left, ref IntPtr right) => {
var l = GenericFromPointer<T>(left);
var r = GenericFromPointer<T>(right);
return comparer(l, r);
};
I also have an option to write a CompareFunction in C for most important data types that are used in 90%+ cases, but I hope to avoid modifications to the native library.
The question is, when setting the compare function with P/Invoke, does every subsequent call to that function from C code incurs marshaling overheads, or the delegate is called from C as if it was initially written in C?
I imagine that, when compiled, the delegate is a sequence of machine instructions in memory, but do not understand if/why C code would need to ask .NET to make the actual comparison, instead of just executing these instructions in place?
I am mostly interested in better understanding how interop works. However, this delegate is used for binary search on big data sets, and if every subsequent call has some overheads as a single P/Invoke, rewriting comparers in native C could be a good option.
I imagine that, when compiled, the delegate is a sequence of machine instructions in memory, but do not understand if/why C code would need to ask .NET to make the actual comparison, instead of just executing these instructions in place?
I guess you're a bit confused about how .NET works. C doesn't ask .NET to execute code.
First, your lambda is turned into a compiler-generated class instance (because you're closing over the comparer variable), and then a delegate to a method of this class is used. And it's an instance method since your lambda is a closure.
A delegate is similar to a function pointer. So, like you say, it points to executable code. Whether this code is generated from a C source or a .NET source is irrelevant at this point.
It's in the interop case when this starts to matter. P/Invoke won't pass your delegate as-is as a function pointer to C code. It will pass a function pointer to a thunk which calls the delegate. Visual Studio will display this as a [Native to Managed Transition] stack frame. This is needed for different reasons such as marshaling or passing additional parameters (like the instance of the class backing your lambda for instance).
As to the performance considerations of this, here's what MSDN says, quite obviously:
Thunking. Regardless of the interoperability technique used, special transition sequences, which are known as thunks, are required each time a managed function calls an native function, and vice-versa. Because thunking contributes to the overall time that it takes to interoperate between managed code and native code, the accumulation of these transitions can negatively affect performance.
So, if your code requires a lot of transitions between managed and native code, you should get better performance by doing your comparisons on the C side if possible, so that you avoid the transitions.
I am passing a C# object (PSObject) to managed C++ in the following way. But it crashes in the commented statement. Am I missing something?
Am I passing PSObject correctly to managed C++?
or am I accessing it wrongly?
I am using clr:oldsyntax.
Actually C# will pass a PSObject to managed C++,
in managed C++ I want to examine the properties present in PSObject. (An object returned by running PowerShell commands)
In C#, using delegate concept I am calling managed C++ method to pass PSObject.
The delegate is declared as below:
delegate bool CFuncDelegate(PSObject Arg);
funcObj (a IntPtr) is a pointer to addData function in C++ (I didn't write that code here, since it is not relevant) and I am calling addData in csharp as:
CFuncDelegate func = (CFuncDelegate)Marshal.GetDelegateForFunctionPointer(funcObj, typeof(CFuncDelegate));
bool isCompleted = func(psoObject); //psoObject is a PSObject
and in managed C++,
static bool __clrcall addData(System::Management::Automation::PSObject* curPsObj)
{
log(Marshal::StringToHGlobalUni(curPsObj->AdaptedMemberSetName));
//prints psadapted
System::Object* value = curPsObj->Properties->get_Item("DisplayName");
//crashes
}
It would be better if someone just post two lines of code to pass object from C# and accessing it in managed C++. Thanks in advance.
I think it's really time that you abandoned oldsyntax and moved to C++/CLI. Anyway, even doing that is not going to solve your problem. You define a delegate like this:
delegate bool CFuncDelegate(PSObject Arg);
And then you assign a delegate variable like this:
CFuncDelegate func = (CFuncDelegate)Marshal.GetDelegateForFunctionPointer(
funcObj, typeof(CFuncDelegate));
The documentation for Marshal.GetDelegateForFunctionPointer says:
Converts an unmanaged function pointer to a delegate.
So, your code can only work if funcObj is an unmanaged function. And your addData method is certainly not that. You need to stop using GetDelegateForFunctionPointer here. It is simply not compatible with calling the managed function addData.
I've no idea where funcObj comes from. You said:
funcObj (a IntPtr) is a pointer to addData function in cpp (i didnt write that code here, since it is not relevant)
In fact it is not only relevant to the problem, it is the root cause of the problem. What I would expect to see here would be for you to add the C++ assembly to your C# project as a reference, at which point addData could be referenced directly.
Of course, none of this even mentions the fact that your calling conventions are mis-matched. The managed code uses clrcall and your unmanaged function pointer is taken to be stdcall.
Update
There's some more information in the comments. Pass addData as a delegate. You'll need to declare the delegate type in the C# code which I believe you reference from your C++ assembly.
I'm currently working on a little Midi Project and just now I'm writing a little "SharpMidi" Library in C++/Cli to make it easier for me to use native Midi functions from Managed code.
I'm now stuck on the midiInOpen Function:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd798458%28v=vs.85%29.aspx
As you can see from the documentation, this function requires an unmanaged function as Callback and optional instance data.
I managed to convert a Managed Function Pointer to an unmanaged Function Pointer using
System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate
but I now have problems converting the this Pointer from my managed class to an unmanaged Pointer, I don't even know if its possibly they way I want it to work.
HMIDIIN _midiInDevice;
void MidiInProcNative(HMIDIIN midiInDevice, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
delegate void MidiInProc(HMIDIIN midiInDevice, UINT msg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
MidiInProc^ _midiInProc;
...
_midiInProc = gcnew MidiInDevice::MidiInProc(this, &MidiInDevice::MidiInProcNative);
cli::pin_ptr<HMIDIIN> midiInDevPtr = &_midiInDevice;
pin_ptr<???> thisPtr = this; // This is the line I'm struggling with
MMRESULT result = midiInOpen(midiInDevPtr, devId, (DWORD_PTR)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(_midiInProc).ToPointer(), thisPtr, CALLBACK_FUNCTION);
As you can see I have experimented with pin_ptrs but I don't think this will work as the this pointer can't be used to initialize any pin_ptr.
I hope I could make clear what I want to try to achieve and maybe somebody can help me out.
Regards,
Xaser
I now have problems converting the this Pointer from my managed class to an unmanaged Pointer
Just don't, there's no need for it. The dwCallbackInstance argument of miniInOpen() is a convenience argument for native C++. You get it back in the static callback function, let's you call an instance method of a C++ class.
But delegates are far more powerful than member function pointers, they already encapsulate this. A delegate constructor takes two arguments, the target object and the target method. So that it can automatically call an instance method. You already took advantage of that, you indeed passed this as the first argument. So you can be sure that your MidiInProcNative() instance method uses the proper object reference.
Just pass nullptr for the argument.
I am writing a C# library to be used by native C++ application. I am using C++/CLI as the Interoperability mechanisim.
I require to pass a callback function from C++ to C# (using C++/CLI as the intermediate layer). C# library needs to call the C++ function with a zero terminated string of wide characters; i.e. the prototype of the callback function is
Func(LPCWSTR pszString);
There are other parameters but they are immaterial for this discussion.
I searched net and found Marshal.GetDelegateForFunctionPointer Method wich I can use. The problem with this is that it converts System.String from C# to char* and not wchar_t* which I am looking for.
Also, what is the best method of achieving this- code example including the C++/CLI portion, if possible.
C++/CLI dll is dependent on C# dll. Method needs to be called synchronously.
GetDelegateForFunctionPointer will work, but you need to add a [MarshalAs(UnmanagedType.LPWStr)] attribute to the parameter in your delegate declaration in order for String to get converted into wchar_t*:
delegate void MyDelegate([MarshalAs(UnmanagedType.LPWStr)] string foo)
IntPtr func = ...;
MyDelegate del = (MyDelegate)Marshal.GetDelegateForFunctionPointer(func,
typeof(MyDelegate));
To pass a modifiable string, give a StringBuilder. You need to explicitly reserve space for the unmanaged function to work with:
delegate void MyDelegate([MarshalAs(UnmanagedType.LPWStr)] StringBuilder foo)
StringBuilder sb = new StringBuilder(64); // reserve 64 characters.
del(sb);
See the little-known UnmanagedFunctionPointer attribute, which is like DllImport for delegates, if you'd like to use CharSet or whatnot.
I´m trying to call a method that is in a C++ dll declarated as __declspec(dllexport) to use in C#, but I don´t know how to return a string value from C++ and how to declare the signature using DllImport in C#.
C++ code "VNVAPI.dll"
__declspec(dllexport) char * GetGpuName(int phyGPUid)
{
CNvidia * pInstance = CNvidia::GetInstance();
char szName[512]={0};
pInstance->GetGpuName(phyGPUid,szName,512);
return szName;
}
C# method signature:
[DllImport("VNVAPI.dll")]
public static extern char GetGpuName(int phyGPUid);
Error generated:
A call to PInvoke function
'Core!Core.Hardware.IO.NVAPI::GetGpuName'
has unbalanced the stack. This is
likely because the managed PInvoke
signature does not match the unmanaged
target signature. Check that the
calling convention and parameters of
the PInvoke signature match the target
unmanaged signature.
Thanks.
As has been pointed out by others you need to specify the C calling convention in your P/Invoke and also use string on the managed side to marshal the null terminated char*.
However you should rejig the C++ routine to take a char* as an input parameter, together with a buffer length parameter. You then write into this buffer in the native code. This avoids the current problem that the data, as you presently have the code, is returned from the stack which, of course, is unwound as the function returns.
The suggestion to use static will make this memory global and so avoid stack unwind problems, at the expense of thread safety. Yes it will likely work for this use case but its a bad habit to get into.
The error message might be confusing.
Without going into detail, try this:
static char szName[512]={0};
If you still get the error, the you need to specify the calling convention in the DllImport attribute.
Edit:
Also make the return type string for C# method declaration.
The error message suggests checking the calling convention. I suspect that the function is using C calling convention, which would explain the unbalanced stack. I would suggest that you specify the calling convention (as #leppi suggested) in your DllImport attribute. Like this:
[DllImport("VNVAPI.dll", CallingConvention=CallingConvention.Cdecl)]
According to the Strings samples, the return value should be string:
static extern string GetGpuName(int phyGPUid);