How to define unmanaged dll dependency in C# - c#

I want to link an unmanaged C++ library to a C# app. I am using the PInvoke process because the unmanaged C++ dll has multiple dependencies that won't compile with CLR. When I compile the example code below, I am getting the following errors. I did find a reference that I need to add the dll reference, but MSVS tells me it can't add it. I also read about registering it with the regsvr32, but that appears to be specific to CLR libraries, right? So my question is, how do I get clear this error for a unmanaged dll?
ServerTerminal.cs(62,48): error CS1031: Type expected
ServerTerminal.cs(62,48): error CS1519: Invalid token ';' in class, struct, or interface member declaration
ServerTerminal.cs(64,48): error CS1031: Type expected
ServerTerminal.cs(64,48): error CS1519: Invalid token ';' in class, struct, or interface member declaration
ServerTerminal.cs:
class ServerTerminal
{
private delegate int Callback(string text);
private Callback mInstance;
public ServerTerminal()
{
mInstance = new Callback(Handler);
SetCallback(mInstance);
}
public void Test()
{
TestCallback();
}
private int Handler(string text)
{
return 0;
}
[DllImport(#"..\\lib\\DDS_Service.dll", EntryPoint="SetCallback")];
private static extern void SetCallback(Callback fn);
[DllImport(#"..\\lib\\DDS_Service.dll", EntryPoint="TestCallback")];
private static extern void TestCallback();
}
and the C++ DLL's Component.h:
typedef int (__stdcall * Callback)(const char* text);
Callback Handler=0;
class COM_Component : public CM_Component
{
// Contents not pasted
}
and the C++ DLL's Component.cpp:
extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler)
{
Handler = handler;
}
extern "C" __declspec(dllexport)
void __stdcall TestCallback()
{
int retval = Handler("hello world");
}
COM_Component::COM_Component( void ) : CM_Component( TDstring( "COM_Component" ) )
{
// register the observer callback methods
}
// Remainder of file not pasted

Your "invalid token" compiler errors are due to the semicolon immediately after the DllImport attributes. Additionally, you're specifiying a verbatim #"..." string with double backslashes in it. I think your declaration should look like:
[DllImport(#"..\lib\DDS_Service.dll", EntryPoint="SetCallback")]
private static extern void SetCallback(Callback fn);
If your DLL is a COM dll, then you can run regsvr32 to register it, and add a reference to it in your project. If that's the case, then you don't need to use P/Invoke: you would be able to reference it like any other library.

[DllImport(#"..\\lib\\DDS_Service.dll", EntryPoint="SetCallback")];
private static extern void SetCallback(Callback fn);
[DllImport(#"..\\lib\\DDS_Service.dll", EntryPoint="TestCallback")];
private static extern void TestCallback();
Remove the ; in the lines after the DllImport attribute.

Related

C# delegate callback with params cause AccessViolation when calling from C++ DLL

I have an unmanaged C++ DLL and a .net application which communicate using P/Invoke .
I need to update a dictionary within the .net app from a C++ DLL.
I tried to do that in the following way:
public delegate void MyDelegate(string address, string username);
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SaveMyDelegate(MyDelegate callback);
private static SortedDictionary<string, string> dict = new SortedDictionary<string, string>();
public void save_delegate() {
SaveMyDelegate(delegate(string address, string username) {
if (username != "") {
dict.Add(address, username);
}
});
}
On C++ side I have:
typedef void (*MANAGED_CALLBACK)(string user_address, string username);
extern "C" __declspec(dllexport) void SaveMyDelegate(MANAGED_CALLBACK callback);
MANAGED_CALLBACK my_callback;
void SaveMyDelegate(MANAGED_CALLBACK callback) {
my_callback = callback;
}
extern "C" __declspec(dllexport) VOID start_collection();
VOID start_collection() {
my_callback("test", "User");
}
On C# I do the following calls:
[DllImport("MyDLL.dll")]
public static extern void start_collection();
private void Button1_Click(object sender, EventArgs e) {
save_delegate();
start_collection();
}
When I call start_collection() I get an AccessViolation exception.
I tried to declare the delegate in the following way:
public delegate void MyDelegate(
[MarshalAs(UnmanagedType.LPStr)]string address,
[MarshalAs(UnmanagedType.LPStr)]string username);
I have also added this statement:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void MyDelegate(...)
I've not looked at every detail of your code, but some commonly seen p/invoke issues just out immediately:
You cannot use C++ strings as interop types. You should use null terminated character arrays on the C++ side. Obtain these using the c_str() method of std::string.
The delegate likely has the wrong calling convention. The unmanaged code (probably) uses cdecl and so you need to use the [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute when declaring the delegate type in C#.
Your C# delegate is susceptible to being collected early (while your unmanaged code still holds a reference to it) because there are no managed references to it. Typically that would be solved by holding a reference to the delegate in a static variable in your C# code.

What's wrong with this interop C#/managed/unmanaged C++ parameter passing?

I'm trying to make a non-static vendor C++ DLL accessible via C#. In order to do this, I'm writing a managed C++ wrapper DLL which basically creates static variables for the vendor DLL and makes those accessible to the C# application.
Here's an example:
typedef void(__stdcall *LPLISTENER_FUNC)(VENDORHANDLE hModule, VENDORWPARAM wParam, VENDORLPARAM lParam);
public delegate void VENDOR_Delegate(VENDORHANDLE hModule,
VENDORWPARAM wParam, VENDORLPARAM lParam);
public class VENDORWrapper
{
private:
static VENDORHSTORAGE _hStorage;
static VENDOR_Delegate^ _hOpenCallback;
void static Initialize()
{
_hStorage=storage_initialize();
}
void static registerCallback(unsigned int type, VENDOR_Delegate^ callback)
{
if (type == 2)
{
_hOpenCallback = callback;
::storage_register_callback(_hStorage, type, (LPLISTENER_FUNC)&_hOpenCallback);
}
}
bool static Open(String^ file)
{
bool retval=false;
filePath = file;
IntPtr ip = Marshal::StringToHGlobalAuto(filePath);
LPCWSTR str = static_cast<LPCWSTR>(ip.ToPointer());
//ERROR OCCURS HERE
retval = storage_open(_hStorage, str);
Marshal::FreeHGlobal( ip );
return retval;
}
void static Close()
{
storage_close(_hStorage);
}
}
The C# is skeletal:
public static VENDORStorageWrapper.VENDOR_Delegate openCallback
= new VENDORStorageWrapper.VENDOR_Delegate(fileOpened);
static void Main(string[] args)
{
VENDORStorageWrapper.VENDORStorageWrapper.Initialize();
Debug.WriteLine("DLL initalized");
VENDORStorageWrapper.VENDORStorageWrapper.registerCallback(2,
openCallback);
Debug.WriteLine("Callback registered");
VENDORStorageWrapper.VENDORStorageWrapper.Open("blah_file");
Debug.WriteLine("File opened");
}
public static void fileOpened(System.Int32 hstorage, System.UInt32 wParam, System.Int32 lParam)
{
Debug.WriteLine("file opened");
}
The vendor DLL's functions are specified as __stdcall, so I think I'm compliant on that front. The vendor's initialize call (_storage_initialize above) seems to be properly setting the handle, which is statically scoped. The storage_open call that's leading into the exception accepts a VENDORHANDLE (really a long) and an LPCWSTR, which I'm trying to convert the string passed from C# to. I think that's where the problem is...
When run, the app throws an unhandled exception "System.Runtime.InteropServices.SEHException" at the commented line above. The exception's coming from inside the vendor DLL, which I have no source code for. The vendor library works perfectly when called in an unmanaged C++ context and the file is known to be good. I think I'm missing something obvious in how I'm handling the parameters, but I can't see what it is.
I also don't think I have the callback set up properly, but I'm not the point where I can test that yet. Any ideas?
I'm not sure of the true big picture, but my experience using native DLLs with .net c# or vb, create a simple c# wrapper (just declarations) to the native DLL, instead of a c++ wrapper. Maybe this will help if the vendor DLL is really a vanilla DLL, as most are, like Windows api calls.
namespace MyNameSpace
{
public class MyWrapper
{
// passing an int to the native DLL
[DllImport("Vendor.DLL")]
public static extern int DllFunc1(int hModule, int nData);
// passing a string to a native DLL expecting null terminated raw wide characters //
[DllImport("Vendor.DLL", CharSet=CharSet.Unicode )]
public static extern int Dllszset(int hModule, string text);
}
}
Then .net will handle it for you and you call your vendor function as...
MyNameSpace.MyWrapper.Dllszset(h, "hello");
Hope this helps you or someone.

Attempting to read/write protected memory error while pinvoking a c++ function

I have a C++ project X with calling convention set as __stdcall (/Gz) and a C# project Y.
I have defined a class myClass in BOTH these projects.
class myClass
{
private:int mem1;
};
In the C# definition of the class, I have prefixed it with
[StructLayout(LayoutKind.Sequential)]
The C++ function is
_declspec (dllexport) void getLen(myClass* str)
{
printf("%s",sizeof(int));
}
In Y, I have defined the function as follows
[DllImport("X.dll")]
private static extern void getLen(ref myClass str);
And I am calling it like this:
getLen(ref str);
where str is an object of type myClass.
Why is this error coming up when I run this solution?
Your problem is in the printf() call: "%s" expects a pointer to a null-terminated string of characters, but you are providing a size_t.

p/invoke calling C dll from c#

this is my C code
extern "C"
{
__declspec(dllexport) void DisplayHelloFromDLL(string a)
{
printf ("%s\n",a)
}
}
this is my C# code
class HelloWorld
{
[DllImport("TestLib.dll")]
public static extern void DisplayHelloFromDLL(string a);
static void Main ()
{
string a = "Hello";
DisplayHelloFromDLL(a);
}
}
It built successfully but crash like this:
SO,how to use P/invoke to call my own C dll from C#?
Please help,thanx in advance.
First of all your code is C++ rather than C. Your function receives a parameter of type std::string and the use of std::string means that your code is actually C++.
Now this parameter type is the root of your problem. You cannot create a std::string in .net and instead will need to use a char* to pass the string data. The following code is what you need:
C++
__declspec(dllexport) void DisplayHelloFromDLL(char* a)
{
printf("%s\n", a);
}
C#
[DllImport("TestLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void DisplayHelloFromDLL(string a);
static void Main ()
{
string a = "Hello";
DisplayHelloFromDLL(a);
}
The default p/invoke marshalling for a .net string is to pass a char* as an [In] parameter. There is no need for the complexity of IntPtr, StringToHGlobalAnsi, FreeHGlobal as suggested by one of the other answers. If you can let the p/invoke marshaller do the work then it is preferable to do so.
Note that you also need to make sure that your calling conventions match. Under the assumption that you have not used any special compiler options when building your C++ code, that code will default to used cdecl calling convention. You can make that match with the CallingConvention parameter to the DllImport attribute.
Please take a look at marshalling string at MSDN
In a nut shell, a C# string doesn't get marshalled as std::string but a char* by default
For one thing the return type is not matching. In C it is void and in C# int.
Change your C++ param type to char* and update your C# code as following
class HelloWorld
{
[DllImport("TestLib.dll")]
public static extern void DisplayHelloFromDLL(IntPtr a);
static void Main ()
{
string a = "Hello";
var ptr = System.Runtime.Marshal.StringToHGlobalAnsi(a);
DisplayHelloFromDLL(ptr);
System.Runtime.Marshal.FreeHGlobal(ptr);
}
}

Correct way to call a C DLL method from C#

I'm trying to execute some methods (in this particular case, rdOnAllDone) from a third party DLL, written in C, and looking trough the headers files, I found this:
#ifndef TDECLSDONE
#ifdef STDCALL
#define CCON __stdcall
#else
#define CCON __cdecl
#endif
#define TDECLSDONE
#endif
#define DLLIMP __declspec (dllimport)
DLLIMP int CCON rdOnAllDone (void(CCON *)(int));
After goggling for a way to call this method, I made this:
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(Delegate d);
public delegate void rdOnAllDoneCallbackDelegate();
private static void rdOnAllDoneCallback()
{
Console.WriteLine("rdOnAllDoneCallback invoked");
}
The method was called correctly except that I couldn't get the int parameter. So I tried adding the input parameter int like this
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(Delegate d);
public delegate void rdOnAllDoneCallbackDelegate(int number);
private static void rdOnAllDoneCallback(int number)
{
Console.WriteLine("rdOnAllDoneCallback invoked " + number);
}
But now delegate is called twice and and it crashes the program with the following error " vshosts32.exe has stopped working"
What's the correct way to call this DLL method?
EDIT: Forgot to add the Main method:
public static void Main()
{
rdOnAllDoneCallbackDelegate del3 = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback);
rdOnAllDone(del3);
while (true)
{
Thread.Sleep(1000);
}
}
Three things you need to do to make this work right:
you need to tell the pinvoke marshaller about the actual delegate type, using Delegate isn't good enough. That will create the wrong thunk that won't properly marshal the argument. Which is what you saw happening.
you need to tell the marshaller about the calling convention if it isn't __stdcall with the [UnmanagedFunctionPointer] attribute. Getting this wrong imbalances the stack with good odds for a hard crash.
you need to store a reference to the delegate object so that the garbage collector won't collect it. It cannot see references held by native code. Getting this wrong makes the native code fail with a hard crash after the next garbage collection.
So this ought to work better, tweak as necessary:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void rdOnAllDoneCallbackDelegate(int parameter);
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(rdOnAllDoneCallbackDelegate d);
class Foo {
private static rdOnAllDoneCallbackDelegate callback; // Keeps it referenced
public static void SetupCallback() {
callback = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback);
rdOnAllDone(callback);
}
private static void rdOnAllDoneCallback(int parameter) {
Console.WriteLine("rdOnAllDoneCallback invoked, parameter={0}", parameter);
}
}
Your delegates signature has to match that of the native callback, also it has to have the UnmanagedFunctionPointerAttribute set appropriately.
In your case like so:
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
public delegate void rdOnAllDoneCallbackDelegate(int parameter);
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(rdOnAllDoneCallbackDelegate callback);
Usage:
{
rdOnAllDone(rdOnAllDoneCallback);
}
private static void rdOnAllDoneCallback(int parameter)
{
Console.WriteLine("rdOnAllDoneCallback invoked, parameter={0}", parameter);
}

Categories

Resources