how to use cdecl callback with pinvoke - c#

I have a c library that has cdecl callbacks. How can I use these from c#.
Everything seems to say that they must be stdcall callbacks
to be clear:
delegate int del();
[dllimport("mylib.dll",CallingConvention=CallingConvention.Cdecl)]
public static extern int funcwithcallback(del foo);
where del must be called cdecl-wise

Take a look at this. The functionality has been around since 1.1 so it should cover whatever .NET version you are using. You just have to specify the CallingConvention.
CallingConvention Documenation at MSDN
You can also look at this article on Code Project:
Using the _CDECL calling convention in C#
EDIT: Also, Here is a example from FreeImage.NET.
static FreeImage_OutputMessageFunction freeimage_outputmessage_proc = NULL;
DLL_API void DLL_CALLCONV
FreeImage_SetOutputMessage(FreeImage_OutputMessageFunction omf);
Then on the C# side, simply:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FreeImage_OutputMessageFunction(FREE_IMAGE_FORMAT
format, string msg);
[DllImport(dllName, EntryPoint="FreeImage_SetOutputMessage")]
public static extern void SetOutputMessage(FreeImage_OutputMessageFunction
omf);

Compile with .NET 2.0, use 2005 compiler!!
Reverse the argument direction.
It works due to some safeguard code added by the 2005 compiler.
EDIT: Don't try this if you can possibly make a shim in native code.

Related

.NET signature to p/invoke GetSystemDEPPolicy function

I can't find any example signatures for .NET to use this function (GetSystemDEPPolicy).
http://msdn.microsoft.com/en-us/library/windows/desktop/bb736298(v=vs.85).aspx
It's a fairly simple function but I don't know how to create the signature to call it. Can someone please help?
GetSystemDEPPolicy is defined as
DEP_SYSTEM_POLICY_TYPE WINAPI GetSystemDEPPolicy(void);
and DEP_SYSTEM_POLICY_TYPE is an enum (see winbase.h assuming you have the C++ components installed in your development environment - if not try winbase.h) and enums in C default to int, as such I would go with
[DllImport("kernel32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
public static extern int GetSystemDEPPolicy();
May I recommend you follow this tutorial on PInvoke

How to marshal collection in c# to pass to native (C++) code

I am working on an enterprise application development. the entire application is developed in c++, except the UI, which is developed in c#, now its time to hookup the UI with c++ code. After detailed study i choose PInvoke in order to do so.
All is successful, the only case where i stuck is that how can i pass collection to C++ code.
e.g:
C# Side Code
List<string> lst = new List<string>();
lst.Add("1");
lst.Add("2");
lst.Add("3");
lst.Add("4");
C++ Side Code
std::vector<std::string> vStr;
Now how do i pass lst to native C++ code
As mzabsky mentioned, you cannot marshal these types. You can, however, marshal an array:
The theoretical C++ export:
extern "C" __declspec(dllexport) void __stdcall Foo(wchar_t const* const* values, int length)
{
// Check argument validity...
// If you really need a vector
std::vector<std::wstring> vStr(values, values + length);
//...
}
The P/Invoke signature:
[DllImport("foo.dll")]
static extern void Foo([MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPWStr)] string[] values, int length);
The call from C#:
Foo(lst.ToArray(), lst.Count);
Note that I'm using std::wstring here; you could instead use char instead of wchar_t, LPStr instead of LPWStr, and std::string instead of std::wstring.
Note that this will allocate an array from the list and then the vector will copy the array's contents. If the original list is small in size, this should be of negligible concern.
Edit: fixing markup (< and >).
You can't do this, only C types can be marshalled. You will have to write a C++/CLI wrapper (or a C wrapper around the C++ vector).
See this answer.
Yes. You can. Actually, not just 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.
The basic idea of instantiating a C++ object from .NET world is to allocate exact size of the C++ object from .NET, then call the constructor which is exported from the C++ DLL to initialize the object, then you will be able to call any of the functions to access that C++ object, if any of the method involves other C++ classes, you will need to wrap them in a C# class as well, for methods with primitive types, you can simply P/Invoke them. If you have only a few methods to call, it would be simple, manual coding won't take long. When you are done with the C++ object, you call the destructor method of the C++ object, which is a export function as well. if it does not have one, then you just need to free your memory from .NET.
Here is an example.
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="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()
{
Marshal.FreeHGlobal(this.ptr);
}
}
For the detail, please see the below link,
C#/.NET PInvoke Interop SDK
(I am the author of the SDK tool)

Win32 api call via C# fails!

I have a C++ function exported as api like this:
#define WIN322_API __declspec(dllexport)
WIN322_API char* Test(LPSTR str);
WIN322_API char* Test(LPSTR str)
{
return "hello";
}
the function is exported as API correctly by the .DEF file, cause i can see it in Dependency Walker tool.
Now i have a C# tester program:
[DllImport("c:\\win322.dll")]
public static extern string Test([MarshalAs(UnmanagedType.LPStr)] String str);
private void Form1_Load(object sender, EventArgs e)
{
string _str = "0221";
Test(_str); // runtime error here!
}
on calling the Test() method i get the error:
"A call to PInvoke function 'MyClient!MyClient.Form1::Test' 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."
i tried many other data types and marshalings, but got nothing!
plz help me!
It is caused by a mismatch on the calling convention, the default for [DllImport] is Stdcall but the C compiler's default is Cdecl. Use the CallingConvention property in the declaration.
That's not the only problem though, this code will crash on Vista and Win7. Returning a string from a C function is quite troublesome, there's a memory management problem. It isn't clear who is responsible for freeing the string buffer. You are returning a literal now but that's going to stop being useful pretty soon. Next stop is using malloc() for the return string with the intent for the caller to call free(). That's not going to work, the pinvoke marshaller cannot call it since it doesn't know what heap the C code is using.
It will call Marshal.FreeCoTaskMem(). That's wrong, the string wasn't allocated by CoTaskMemAlloc(). That goes unnoticed on XP and earlier, other than the very hard to diagnose memory leak this causes. And goes kaboom on Vista and Win7, they have a much more stricter memory manager.
You need to rewrite the C function like this:
extern "C" __declspec(dllexport)
void __stdcall Test(const char* input, char* output, int outLen);
Now the caller supplies the buffer, through the output argument, there's no longer a guess who owns the memory. You use StringBuilder in the C# declaration.
[DllImport("foo.dll")]
private static extern void Test(string input, StringBuilder output, int outLen);
...
var sb = new StringBuilder(666);
test("bar", sb, sb.Capacity);
string result = sb.ToString();
Be careful to use the outLen argument in your C code so that you can be sure not to overflow the buffer. That corrupts the garbage collected heap, crashing the app with a Fatal Execution Engine Error.
Change your macro definition to
#define WIN322_API __declspec(dllexport) __stdcall
As an alternative, use CallingConvention.Cdecl when importing.
Read, for example, here for more info on calling conventions.
Make sure that you have the correct calling convention. Your DLL may use Cdecl, while C# defaults to StdCall. It's better to always explicitly define the calling convention.
Especially when using Windows functions which exist in an ANSI and wide char version (those with an A or W prefix), explicitly specify the CharSet so the correct version is used.
When the function returns a value, explictly marshal the return value. Otherwise, the compiler chooses a default, which may be wrong. I suspect this is (also) the problem here.
So change to this, for example:
[DllImport("c:\\win322.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string Test([MarshalAs(UnmanagedType.LPStr)] String str);
Try returning LPSTR, not char*. You might also need to specify stdcall calling convention, which is default in .NET, but I'm not sure about your unmanaged project.
Use Unmanagedtype.LPTStr for the input. Notice the additional T
[MarshalAs(UnmanagedType.LPStr)] String str // your current code
[MarshalAs(UnmanagedType.LPTStr)] String str // try this code
Pass a StringBuilder from .Net as a parameter rather than returning a string (in this case it will be like an out parameter)

using C function in C#

i have a dll, built with mingw
one of the header files contains this:
extern "C" {
int get_mac_address(char * mac); //the function returns a mac address in the char * mac
}
I use this dll in another c++ app, built using Visual C++ (2008SP1), not managed, but plain c++
(simply include the header, and call the function)
But now I have to use it in a C# application
The problem is that i can't figure out how exactly (i'm new in .net programming)
this is what i've tried
public class Hwdinfo {
[DllImport("mydll.dll")]
public static extern void get_mac_address(string s);
}
When i call the function, nothing happens
(the mydll.dll file is located in the bin folder of the c# app, and it gives me no errors or warnings whatsoever)
I think you need to define the extern as:
public class Hwdinfo {
[DllImport("mydll.dll")]
public static extern int get_mac_address(out string s);
}
You should match both the return argument type on the function (int) as well as mark the string parameter as an out parameter so that your C# code is generated to expect to receive a value from the called function, rather than just passing one in.
Remember, strings in C# are treated as immutable, this behavior extends to external calls as well.
To use string output parameters with DllImport, the type should be StringBuilder.
public class Hwdinfo {
[DllImport("mydll.dll")]
public static extern int get_mac_address(StringBuilder s);
}
Here's an MSDN Article about using Win32 dlls and C#:
http://msdn.microsoft.com/en-us/magazine/cc164123.aspx
If you expect your MAC address to come through your string parameter, I guess you had better to make it a reference.
public class Hwdinfo {
[DllImport("mydll.dll")]
public static extern int get_mac_address(out string s);
}
Or something like so.
You can find lots of examples here: http://pinvoke.net/
I suspect that you your best hints would come from something like: http://pinvoke.net/default.aspx/shell32.SHGetSpecialFolderPath
Strings in .NET are immutable so try:
public class Hwdinfo {
[DllImport("mydll.dll")]
public static extern int get_mac_address(char[] s);
}
C# PInvoke out strings declaration
This suggests you might try using a StringBuilder as your parameter instead of a string. If that doesn't work then an out parameter would be my next choice.

Can anyone find equivalent c# code

i need equivalent c# code
VssSdkClientId VSS_SDK_SPEC startVssSdkClientEcho(const VssSdkXChar *host,
const VssSdkPort port,
NotifyFunc &notifyFunc,
const eProtocolType protocolType,
bool doIcmpEchoRequest = true );
this is my c++ SDK code .... here i need to import this interface and need to perform some operation
[DllImport("VssSdkd")]
public static extern void startVssSdkClientEcho(StringBuilder IpAddress, long port, ? ,eProtocolType proType, bool Req);
NotifyFunc &notifyFunc -> here i need to implemet the some callback function it should call the function like
myNotifyFunc( enumType notificationType, void *data)
{
}
in c# "void *data" is not avalable how can achive this... hepl me guys... thanks in advance
For a call back function you can pass in a reference to a delegate and P/Invoke will marshal the callback to your delegate. See the this example.
For the void* parameter that would be best translated to an IntPtr which is always the same size as a pointer. If you need to retrieve the data from that pointer you can use one of the methods in System.Runtime.InteropServices.Marshal.
So for your example:
public delegate void NotifyFunc(enumType notificationType, IntPtr data);
[DllImport("VssSdkd")]
public static extern void startVssSdkClientEcho(string IpAddress,
long port, NotifyFunc notifyFunc, eProtocolType proType, bool Req);
Note that IpAddress can be a string. It would only have to be a StringBuilder if the calling code modified it and passed it back. Since the C++ prototype lists it as a const char*, it won't be changing the string.
I'm no expert when it comes to C++ interop, so I can't answer your question directly. However, I suggest you have a look at pinvoke.net. There are lots of examples of the correct way to use the Windows API from managed code. That may be enough information to help you figure out what would be appropriate for your function.

Categories