I am getting error as "'Cannot marshal 'parameter #1': Non-blittable generic types cannot be marshaled.'"
in C code I have
#include <stdio.h>
#include "pch.h"
typedef void(*RECV_CALLBACK)();
RECV_CALLBACK pfRecvCallBack;
typedef void(*RECV_CALLBACKINT_PARA)(int);
RECV_CALLBACKINT_PARA pfRecvCallBackIntPara;
extern "C"
{
__declspec(dllexport) void MethodWith_INT_Para(RECV_CALLBACKINT_PARA pfIntPara)
{
if (pfIntPara)
{
pfRecvCallBackIntPara = pfIntPara;
}
}
__declspec(dllexport) void MethodWithNo_Int_Para(RECV_CALLBACK pfRecv)
{
if (pfRecv)
{
pfRecvCallBack = pfRecv;
}
}
__declspec(dllexport) void Calling_INT_PARA(int a, int b)
{
pfRecvCallBackIntPara(a + b);
}
__declspec(dllexport) void Calling_without_Para(int a, int b)
{
pfRecvCallBack();
}
}
When i try to call this in C# call back without int parameter is working fine, but the callback with int parameter(line ->MethodWith_INT_Para(testWithIntPara);) is giving error as mention above.
C# code
using System.Runtime.InteropServices;
[DllImport("Sample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "MethodWithNo_Int_Para")]
static extern void MethodWithNo_Int_Para(Action ptr);
[DllImport("Sample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "MethodWith_INT_Para")]
static extern void MethodWith_INT_Para(Action<Int32> ptr);
[DllImport("Sample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "Calling_INT_PARA")]
static extern void Calling_INT_PARA(int x, int y);
[DllImport("Sample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "Calling_without_Para")]
static extern void Calling_without_Para();
Console.WriteLine("After calling Declare");
MethodWithNo_Int_Para(testWithoutIntPara);
Calling_without_Para();
void testWithoutIntPara()
{
Console.WriteLine("Without int para : called by Calling_without_Para");
}
MethodWith_INT_Para(testWithIntPara);
Calling_INT_PARA(1, 3);
void testWithIntPara(int x)
{
Console.WriteLine("With Int Para {x} : called by Calling_INT_PARA");
}
Console.ReadLine();
I am using Action to define callback function, please let me know if there is any other way to do this ?
Action<int> is generic, which cannot be marshalled. You need to create a custom delegate. Another problem is that C is expecting a function with the CDecl calling convention, bu the default is StdCall.
You need to use the following types, instead of Action and Action<int>
[UnmanagedFunctionPointer(CallingConvention.CDecl)]
public delegate void ActionCDecl();
[UnmanagedFunctionPointer(CallingConvention.CDecl)]
public delegate void ActionIntCDecl(int p1);
Related
I'm trying to Marshal this C++ test DLL with C interface which works when calling from Python using CFFI:
#define AS_TYPE(Type, Obj) reinterpret_cast<Type *>(Obj)
#define AS_CTYPE(Type, Obj) reinterpret_cast<const Type *>(Obj)
struct mytype_s;
typedef struct mytype_s mytype_t;
namespace TestDLL {
class Test {
public:
Test() { return; }
~Test() { return; }
int test_function() const { return 314159; }
};
}
mytype_t *test_new() {
return AS_TYPE(mytype_t, new TestDLL::Test());
}
int test_function(const mytype_t *mytype) {
return AS_CTYPE(TestDLL::Test, mytype)->test_function();
}
From C# I tried using an IntPtr for mytype_t which had worked for me with structures in the past. The C# code:
[DllImport(dllPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr test_new();
[DllImport(dllPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int test_function(IntPtr mytype);
IntPtr mytype;
mytype = test_new();
Console.WriteLine("Alloc complete");
int result;
result = test_function(mytype);
Console.WriteLine("test: {0}", result);
But I get the error:
Alloc complete
Unhandled Exception: System.EntryPointNotFoundException: Unable to find an entry point named 'test_function' in DLL
Is this related to how I marshalled the mytype_t or a more complex issue with passing the object from C++ to C#?
Thanks
I'm building a C# application that loads a C++ library.
I call functions from that C++ DLL. I use below function to display input string.
c++ dll:
wchar_t* Test_EchoString( wchar_t *InputStr )
{
String HWStr = String( InputStr );
return HWStr.c_str();
}
c# code:
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int _Test_EchoString([MarshalAs(UnmanagedType.LPWStr)] string s);
private void echo_string_Click(object sender, RoutedEventArgs e)
{
string upn = "aaaaa";
_Test_EchoString(upn);
MessageBox.Show(_Test_EchoString(upn).ToString());
}
I get in messagebox number 18666252 but i want to get a string from _Test_EchoString().
You have a pair of problems in your code:
in your C# you defined _Test_EchoString as public static extern int _Test_EchoString, so when you execute it, the returned value will be the address of the first character of the string HWStr.c_str().
And here it shows another problem, as anderas said, you are returning an invalid pointer, because HWStr.c_str() returns the pointer to the current value of the std::wstring object, so it is valid as long that the wstring is valid, so when the method Test_EchoString ends its execution it is no more valid(because HWStr is destroyed).
There are different ways to fix this problems, I'm going to show you two of these:
1) The first is to allocate the memory you want to return in the Heap and free it later with another call:
static wchar_t *Test_EchoStringResult;
extern "C" __declspec(dllexport) const wchar_t * Test_EchoStringNew(const wchar_t *InputStr)
{
std::wstring HWStr(InputStr);
HWStr += L" something";
Test_EchoStringResult = new wchar_t[HWStr.length() + sizeof(wchar_t)];
HWStr.copy(Test_EchoStringResult, HWStr.length());
Test_EchoStringResult[HWStr.length()] = L'\0';
return Test_EchoStringResult;
}
extern "C" __declspec(dllexport) void Test_EchoStringDelete()
{
delete[] Test_EchoStringResult;
}
And this is the usage in C#:
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern IntPtr Test_EchoStringNew(string foo);
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void Test_EchoStringDelete();
public void foo()
{
string result = Marshal.PtrToStringAuto(Test_EchoStringNew("test"));
MessageBox.Show(result.ToString());
Test_EchoStringDelete();
}
To me, this looks pretty ugly, so I'd prefer to use another pattern
2) Passing a callback to the C method and pass to this method HWStr.c_str() when HWStr is still valid:
extern "C" __declspec(dllexport) void Test_EchoString(const wchar_t *InputStr, void (*callback)(const wchar_t*))
{
std::wstring HWStr(InputStr);
HWStr += L" something";
callback(HWStr.c_str());
}
And here is the C# usage:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public delegate void myCallback(string toShow);
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void Test_EchoString(string foo, myCallback callback);
public void foo()
{
Test_EchoString("test", callback);
}
void callback(string toShow)
{
MessageBox.Show(toShow);
}
Imagine I have a function called
Myfunction(const void * x);
My C# declaration could be
MyFunction(IntPtr x);
Is this functionally and technically equivalent to
struct MyStruct { IntPtr P; }
MyFunction(MyStruct x);
Or will there be a difference in how they are marshalled.
I'm asking this because the library I'm calling is all void *, typedef'd to other names, and in C# I'd like to get type safety, for what it's worth.
If your StructLayout is Sequential, then it is indeed identical.
Easiest way to verify this for yourself is to try it out, of course:
Make a C++ Win32 DLL project:
extern "C"
{
__declspec(dllexport) void MyFunction(const void* ptr)
{
// put a breakpoint and inspect
}
}
Make a C# project:
public struct Foo
{
public IntPtr x;
}
[DllImport(#"Win32Project1.dll", EntryPoint = "MyFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern void MyFunctionWithIntPtr(IntPtr x);
[DllImport(#"Win32Project1.dll", EntryPoint = "MyFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern void MyFunctionWithStruct(Foo x);
static void Main(string[] args)
{
IntPtr j = new IntPtr(10);
var s = new Foo();
s.x = new IntPtr(10);
MyFunctionWithIntPtr(j);
MyFunctionWithStruct(s);
}
In your debug settings, make sure you select Native debugging is enabled.
You'll see both values to be 0xA.
Note, however, if you use out/ref parameters for your IntPtr vs Struct, they will be different values.
//------------------------------------- C# Code ------------------------------------
[DllImport("MarshallStringsWin32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void Test([MarshalAs(UnmanagedType.AnsiBStr)] out String str);
[DllImport("MarshallStringsWin32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void FreeString([MarshalAs(UnmanagedType.AnsiBStr)] String str);
static void Main(string[] args)
{
String str;
Test(out str);
FreeString(str);
}
//------------------------------------- C++ Code ------------------------------------
void Test(__out BSTR* str)
{
const std::string stdStr = "The quick brown fox jumps over the lazy dog";
_bstr_t bstrStr = stdStr.c_str();
*str = bstrStr.copy();
}
void FreeString(BSTR str)
{
SysFreeString(str);
}
I am getting a System.EntryPointNotFoundException when calling Test() . Would anybody know what I am doing wrong? Is this the correct way to marshall strings?
Maybe you need to add the code for c++ code in the header file:
extern "C" void __declspec(dllexport) FreeString(BSTR str);
extern "C" void __declspec(dllexport) Test(BSTR* str);
This is almost certainly because C# cant map your name of the method Test to the Test method in the native code. Try specifying the EntryPoint="Test" attribute for the method as follows:
[DllImport("MarshallStringsWin32.dll", EntryPoint="Test", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void Test([MarshalAs(UnmanagedType.AnsiBStr)] out String str);
I have a C++ assembly that I am importing using DLLImport.
I am attempting to call its method:
namespace Testing
{
class Test{
int Run(char* filePath, bool bEntry, double duration){//code}
};
}
by
[DllImport(dllName, CharSet = CharSet.Auto)]
public static extern int Run(string filePath, bool bEntry, double duration)
);
When I call its method, I get the error message:
Unable to find an entry point named Run in dll
The "Run" looks to be a non-static class method. Although, it's possible to call such methods from C# this is not the primary use-case. It would be way easier to consume it from .NET if you expose it via COM, or at-least as a plain C interface:
extern "C" __declspec(dllexport) void* Testing_Test_Create();
extern "C" __declspec(dllexport) void Testing_Test_Destroy(void* self);
extern "C" __declspec(dllexport) int Testing_Test_Run(void* self, char* filePath, bool bEntry, double duration);
And here is a sample how to call C++ class methods from C#:
// Test.cpp in NativeLib.dll
namespace Testing
{
class __declspec(dllexport) Test
{
public:
explicit Test(int data)
: data(data)
{
}
int Run(char const * path)
{
return this->data + strlen(path);
}
private:
int data;
};
}
// Program.cs in CSharpClient.exe
class Program
{
[DllImport(
"NativeLib.dll",
EntryPoint = "??0Test#Testing##QAE#H#Z",
CallingConvention = CallingConvention.ThisCall,
CharSet = CharSet.Ansi)]
public static extern void TestingTestCtor(IntPtr self, int data);
[DllImport(
"NativeLib.dll",
EntryPoint = "?Run#Test#Testing##QAEHPBD#Z",
CallingConvention = CallingConvention.ThisCall,
CharSet = CharSet.Ansi)]
public static extern int TestingTestRun(IntPtr self, string path);
static void Main(string[] args)
{
var test = Marshal.AllocCoTaskMem(4);
TestingTestCtor(test, 10);
var result = TestingTestRun(test, "path");
Console.WriteLine(result);
Marshal.FreeCoTaskMem(test);
}
}
Entry point names might be different for your build configuration/compiler, so use dumpbin utility to obtain them. Again, this is just a proof of concept, in real code it would be better to use COM.
See here: http://dotnetperls.com/dllimport
I'm not sure this will help if the function is a member of a class, but to locate the entry point by name, not ordinal, you'll need a .def file in your dll..
LIBRARY mylib
Run #1