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.
Related
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);
I have a C++ application that I have to convert to a DLL. I have all the source.
my function is
extern "C"
__declspec(dllexport) int mymain(int i, std::wstring myArgs)
I need to be able to pass in the arguments from a c++ or c# wrapper. I am able to call this from a c++ console application without error. I am now trying to call it from C#.
This is my c# code:
public static class DllHelper
{
[DllImport("rep.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mymain(int iArgs, string aArgs);
}
class Program
{
static void Main(string[] args)
{
string s = "my string data";
DllHelper.mymain(0, s);
}
}
}
When I run it I get
System.Runtime.InteropServices.SEHException: 'External component has thrown an exception.'
I am out of ideas.
TIA
Specify Unicode but also, in your C or C++ function, use printf with "%S" (upper-case 'S' means wide-character string).. OR std::wcout.
Without that, it might print weird or terminate at the first null char it finds. Also, you might want to actually pass the length of the string, but that's entirely up to you.
Note the signature of the C++ function uses LPCWSTR (const wchar_t*) for the myArgs parameter..
public static class DllHelper
{
[DllImport("rep.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int mymain(int iArgs, string aArgs);
}
class Program
{
static void Main(string[] args)
{
string s = "my string data";
DllHelper.mymain(0, s);
}
}
#ifdef __cplusplus
extern "C" {
#endif
int __declspec(dllexport) mymain(int i, const wchar_t* myArgs)
{
#ifdef __cplusplus
std::wcout<<std::wstring(myArgs)<<L"\n";
#else
printf(L"%S\n", myArgs);
#endif
}
#ifdef __cplusplus
}
#endif
Based on yr last comment u might need to:
[DllImport("rep.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
anyway since I dont have rep.dll it's hard to guess
The naming used in your code:
mymain(int iArgs, string aArgs);
makes me think that what you are trying to do is probably passing an array of strings (similar to wmain(int argc, wchar_t** argv)).
If this is what you want, then on the native DLL side your function prototype would look like this:
extern "C" int __declspec(dllexport) mymain(int iArgs, wchar_t** aArgs)
And on the C# side, you would write a PInvoke declaration like this:
[DllImport("rep.dll",
CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode)]
public static extern int mymain(int iArgs, [In] string[] aArgs);
that you can invoke in C# like this:
string[] test = { "C64", "Bravo", "Charlie" };
int returnCode = mymain(test.Length, test);
Ok I have a DLL function declared as:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, ref IntPtr param_value);
This function takes many different params i.e in C++ this is how you would use it.
int i=2;
setParam("width", &i);
float k=2.5f;
setParam("factor", &f);
So I am trying to declare a C# functions to call this DLL api, I have got one working for the pointer to integer case:
public static void setWidth(int width)
{
IntPtr w = new IntPtr(width);
setParam("width", ref w);
}
But I cannot figure out how to do the second one where I pass a pointer to a float as IntPtr. Any ideas?
public static void setFactor(float f)
{
IntPtr w = new IntPtr(f); // WHAT GOES HERE??
setParam("factor", ref w);
}
Unless there's too many combinations, I'd say the best way is to simply have multiple DllImports for the various argument types. For example:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParamInt32(string param_name, ref int param_value);
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParamSingle(string param_name, ref float param_value);
You can then call them properly as
var intVal = 42;
setParamInt32("param", ref intVal);
var floatVal = 42.0f;
setParamSingle("param", ref floatVal);
Using ref IntPtr is wrong in either case - the only reason it works at all is that in 32-bit applications, IntPtr is a 32-bit integer internally. However, it's supposed to be a pointer. A proper use would be something like this:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, IntPtr param_value);
Note that the ref isn't there - IntPtr is already an indirection.
To call this, you'll need to allocate some memory, and get a pointer to that - or, use a GCHandle to refer directly to a managed object:
var intValue = 42;
var handle = GCHandle.Alloc(intValue, GCHandleType.Pinned);
setParam("param", handle.AddrOfPinnedObject());
Make sure to dispose of the managed handle properly, of course - pinned handles are a huge pain for the GC.
Manually copying the data to unmanaged memory and back also isn't exactly hard:
var ptr = Marshal.AllocCoTaskMem(sizeof(int));
try
{
Marshal.WriteInt32(ptr, 42);
setParam("param", ptr);
// If you need to read the value back:
var result = Marshal.ReadInt32(ptr);
}
finally
{
Marshal.FreeCoTaskMem(ptr);
}
But I'd simply stick with automatic marshalling unless you have a very good reason not to.
If you absolutely have to use IntPtr and can't just pass a floatparameter then you can use unsafe coding to pass a float pointer as IntPtr takes a void pointer as a parameter in one of its constructor. However the corresponding unmanaged function must also take a void pointer.
There is two ways you can deal with this, passing void* or passing IntPtr*. I would say passing void* is probably better, since you in general will be doing the same with IntPtr except for that IntPtr will be passed with the pointer instead of the function.
Option 1 - IntPtr
You first have to correct the p/invoke declaration by removing the erroneous ref
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, IntPtr param_value);
You do not pass IntPtr as ref either, just simply pass the instance of your IntPtr
public static unsafe void setFactor(float f)
{
IntPtr w = new IntPtr(&f);
setParam("factor", w);
}
Or if you want the unsafe declaration in the body
public static void setFactor(float f)
{
unsafe
{
IntPtr w = new IntPtr(&f);
setParam("factor", w);
}
}
Option 2 - void*
[DllImport(mDllName, EntryPoint = "SetParam")]
public unsafe static extern bool setParam(string param_name, void* param_value);
Then you can set it like
public static unsafe void setWidth(int width)
{
int* w = &width;
setParam("width", w);
}
And for the float
public static unsafe void setFactor(float f)
{
float* fptr = &f;
setParam("factor", fptr);
}
You can theoretically declare different DllImport entries for "safe" automatic marshalling:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, ref int param_value);
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, ref float param_value);
And use it like:
public static void setFactor(float f)
{
setParam("factor", ref f);
}
I have done this for other functions using void * as a signature and it works just fine, the ref <value_type> gets correctly passed in as a pointer.
Also, ref IntPtr should be for void ** (you'd use IntPtr for void *, not ref IntPtr)
If you are ok with unsafe, then you can use #Bauss solution
Furthermore, using the constructor IntPtr(int) is giving a location for the pointer, not the value at the location
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);
}
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