I'm trying to create a C# wrapper for a C .lib that contains functions take take a void pointer using SWIG.
int inputPointExample(void* input);
int outputPointerExample(void* output);
By default SWIG doesn't handle void pointer conversions, you have to use typemaps somehow. I found this page -> http://www.nickdarnell.com/2011/05/swig-and-a-miss/
Number 9 says to use the following typemaps to handle void pointers...
%typemap(ctype) void * "void *"
%typemap(imtype) void * "IntPtr"
%typemap(cstype) void * "IntPtr"
%typemap(csin) void * "$csinput"
%typemap(in) void * %{ $1 = $input; %}
%typemap(out) void * %{ $result = $1; %}
%typemap(csout) void * { return $imcall; }
When I try that I get a compile error in my exampleVectorType.cs in this function...
public IntPtr pData {
set {
examplePINVOKE.ExampleVectorType_pData_set(swigCPtr, value);
}
get {
global::System.IntPtr cPtr = examplePINVOKE.ExampleVectorType_pData_get(swigCPtr);
SWIGTYPE_p_void ret = (cPtr == global::System.IntPtr.Zero) ? null : new SWIGTYPE_p_void(cPtr, false);
return ret; //Compile error occurs here
}
}
I got-
Cannot implicitly convert type 'SWIGTYPE_p_void' to 'System.IntPtr'
From what I was able to find, many others are having problems with this as well and there are only a few poor examples on how to fix this. Can someone help me out here?
I've tried this with
// c++
void* GetRawPtr()
{
return (void*)_data;
}
which swigs to
// swig generated c#
// Example.cs
IntPtr GetRawPtr()
{
return examplePINVOKE.Example_GetRawPtr(swigCPtr);
}
// examplePINVOKE.cs
[global::System.Runtime.InteropServices.DllImport("example", EntryPoint="CSharp_Example_GetRawPtr")]
public static extern IntPtr Example_GetRawPtr(global::System.Runtime.InteropServices.HandleRef jarg1);
if the following line is removed, then i get the code you had:
%typemap(csout) void * { return $imcall; }
perhaps SWIG doesn't support typemapping well with properties? If you don't use get/set properties for your pData, it should work (as I've gotten it to work for me)
I had a similar issue, but my case included struct fields:
struct foo {
void* bar;
};
I was able to get Swig to work by using this:
%typemap(ctype) void* "void *"
%typemap(imtype) void* "System.IntPtr"
%typemap(cstype) void* "System.IntPtr"
%typemap(csin) void* "$csinput"
%typemap(in) void* %{ $1 = $input; %}
%typemap(out) void* %{ $result = $1; %}
%typemap(csout, excode=SWIGEXCODE) void* {
System.IntPtr cPtr = $imcall;$excode
return cPtr;
}
%typemap(csvarout, excode=SWIGEXCODE2) void* %{
get {
System.IntPtr cPtr = $imcall;$excode
return cPtr;
}
%}
I don't totally understand this stuff, but I believe the excode portion only matters if you're using exceptions. Overriding the property's get accessor via csvarout seems to be the key.
Related
I see a java example about how to pass binary data in director callback from C++ to java , https://github.com/swig/swig/blob/90cdbee6a69d13b39d734083b9f91069533b0d7b/Examples/test-suite/director_binary_string.i , I'd like to do the same thing on C# with the same swig directive,
%apply (char *STRING, size_t LENGTH) { (char *dataBufferAA, int sizeAA) };
but it does not take effect, and shows
Warning 453: Can't apply (char const *STRING,size_t LENGTH). No typemaps are defined
Basically my code's structure is like below, I'd expect a callback function like void run(byte[] aData) can be invoked in C#.
%module(directors="1") director_binary_string;
%feature("director") Callback;
%apply (char *STRING, size_t LENGTH) { (char* aData, size_t aLen) };
%inline %{
#include <stdlib.h>
class Callback {
public:
virtual ~Callback() {}
virtual void run(char* aData, size_t aLen) {}
};
%}
And I tries a typemap like below, but it does not work as expected.
%typemap(imtype) (const char *STRING, size_t LENGTH) "byte[]"
%typemap(cstype) (const char *STRING, size_t LENGTH) "byte[]"
%typemap(directorin, excode=SWIGEXCODE) (const char *STRING, size_t LENGTH) %{
{
$input = new byte[(int)$2];
IntPtr data = $imcall;
System.Runtime.InteropServices.Marshal.Copy(data, $input, 0, (int)$2);
}
%}
Anyone can show how to write a proper typemap or any other better solution.
Finally I got an alternative solution. Instead of passing binary data to C# side from C++, within the callback, the caller from C# proactively retrieves binary data. For example
Swig Interface
%module(directors="1") director_binary_string;
%feature("director") Callback;
%include "arrays_csharp.i"
%inline %{
#include <stdlib.h>
class CallbackData
{
public:
%apply unsigned char INOUT[] { unsigned char* aBlockData}
bool GetBlockData(unsigned char* aBlockData);
size_t GetSize();
};
class Callback {
public:
virtual ~Callback() {}
virtual void run(CallbackData aCB) {}
};
C#
class csharp_callback : Callback
{
public override void run(CallbackData aCB)
{
byte[] aData = new byte[aCB.GetSize()];
aCB.GetBlockData(aData);
//aData is the expected binary data.
}
}
I'm currently working on an C# (.NET Framework 4.7.2) application using some business logic from an unmanaged C++ library. I try to pass data (interop) back and forth from C# to C++. I may not use C++/CLI, no common language runtime allowed in my project.
It works fine for int. Unfortunately as soon as I try to send another datatype I'm getting an conversion error e.g. float 4.2f becomes 1 and string "fourtytwo" turns into -1529101360.
My C# code looks like this:
// works fine, creates an instance of TestClass
var test = TestProxy.Wrapper_Create("test");
// int, works fine, a = 42
var a = TestProxy.TryInt(test, 42);
// float, problem, b = 1
var b = TestProxy.TryFloat(test, 4.2f);
// string, problem, c = -159101360
var c = TestProxy.TryString(test, "fourtytwo");
My C# Interop Proxy class to call the native (unmanaged) C++ code looks like this:
public static class TestProxy
{
private const string coreDLL = "test.core.dll";
[DllImport(coreDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Wrapper_Create(string name);
[DllImport(coreDLL, EntryPoint = "?TryInt#TestClass##XXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int TryInt(IntPtr instance, int n);
[DllImport(coreDLL, EntryPoint = "?TryFloat#TestClass##XXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int TryFloat(IntPtr instance, float n);
[DllImport(coreDLL, EntryPoint = "?TryString#TestClass##XXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int TryString(IntPtr instance, string n);
}
My native (unmanaged) C++ looks like that:
the header file:
#ifdef TESTCORE_EXPORTS
#define TESTCORE_API __declspec(dllexport)
#endif
#pragma once
extern "C"
{
class TESTCORE_API TestClass
{
private:
char* name;
public:
TestClass(char*);
int TryInt(int);
float TryFloat(float);
char* TryString(char*);
};
TESTCORE_API TestClass* Wrapper_Create(char* name);
}
the implementation file:
#include "stdafx.h"
#include "TESTCore.h"
TestClass::TestClass(char* n)
{
name = n;
}
int TestClass::TryInt(int n)
{
return n; // works fine
}
float TestClass::TryFloat(float n)
{
return n; // something goes wrong here
}
char* TestClass::TryString(char* n)
{
return n; // something goes wrong here
}
extern "C"
{
TESTCORE_API TestClass * Wrapper_Create(char* name)
{
return new TestClass(name);
}
TESTCORE_API int TryInt(TestClass * instance, int n)
{
if (instance != NULL)
{
return instance->TryInt(n);
}
}
TESTCORE_API float TryFloat(TestClass * instance, float n)
{
if (instance != NULL)
{
return instance->TryFloat(n);
}
}
TESTCORE_API char* TryString(TestClass * instance, char* n)
{
if (instance != NULL)
{
return instance->TryString(n);
}
}
}
Do you know how to correctly marshal float, string from C# to C++ and back?
Thank you!
C++ doesn't have standard ABI. It's rarely a good idea to use C++ classes across DLLs, even when you have same language on both sides.
There're better ways.
Replace your __thiscall class methods with global functions, cdecl or stdcall whichever you like (but note C# and C++ have different defaults, if you'll do nothing C++ will use cdecl, C# will import as stdcall). You can pass "this" pointer of the class in the first argument, IntPtr in C#, just like you're doing now. Also if you'll write extern "C" or use a module definition file, they will have human-readable names.
If you want objects, use COM. Declare an interface that inherits from IUnknown, implement it in C++ (I usually use ATL), and export a global function to create an instance of that object (2 lines in ATL, CComObject<T>::CreateInstance followed by AddRef). No need to register, type libraries, you just need to implement IUnknown (but see this if you want to use them from multiple threads)
Update: strings are indeed harder. Apply [MarshalAs(UnmanagedType.LPTStr)] to the argument. Apply [return: MarshalAs(UnmanagedType.LPTStr)] to the function. Specify PreserveSig=true in your DllImport. Finally, modify the C++ code to return a copy of the string, i.e. call strlen then CoTaskMemAlloc (don't forget about the '\0') then strcpy.
Easier way to deal with strings is like this:
HRESULT TryString( TestClass *instance, BSTR i, BSTR *o )
At least there're CComBSTR and _bstr_t built-in classes to deal with memory management.
There are a few people asking around to know how to use the dl library to dynamically load and invoke a plugin from C# on non-windows systems.
The advice appears to be, generally speaking, 'use GetDelegateForFunctionPointer'. However, no one can supply an actually working example of both loading the external library AND invoking it.
The following code:
class Helper
{
const int RTLD_NOW = 2;
[DllImport("dl")]
static extern IntPtr dlopen([MarshalAs(UnmanagedType.LPTStr)] string filename, int flags);
[DllImport("dl")]
static extern IntPtr dlsym(IntPtr handle, [MarshalAs(UnmanagedType.LPTStr)] string symbol);
public static void Test() {
IntPtr libraryHandle = dlopen("libextern.dylib", RTLD_NOW);
IntPtr action = dlsym(libraryHandle, "rs_trigger");
var caller = (rs_trigger) Marshal.GetDelegateForFunctionPointer(action, typeof(rs_trigger));
var r = caller(1);
log ("Success! " + libraryHandle + " .. " + action + " --> " + r);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate int rs_trigger(int value);
}
Prints:
Success! 105553117399168 .. 6120303264 --> 1339432
However, the rs_trigger in the c code invoked does not return 1339432. Invoking the library using python and ctypes shows:
Python 2.7.9 (default, Jan 29 2015, 06:27:40)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> lib = ctypes.CDLL("libextern.dylib")
>>> lib
<CDLL 'libextern.dylib', handle 7f7f83601e60 at 10b389550>
>>> lib.rs_trigger
<_FuncPtr object at 0x10b367940>
>>> lib.rs_trigger(1)
Trigger called
Invoking callback
No callback bound
101
>>> lib.rs_trigger(100)
Trigger called
Nope
200
>>> output = lib.rs_trigger(100)
Trigger called
Nope
>>> output
200
Clearly the C# call caller(1) is passing malformed data over to the c function call... but I've read a lot of example in the past hour, and all of them seem to say to do exactly this.
What am I doing wrong?
What exactly is the syntax to use for the delegate you define to bind the the IntPtr to to invoke it as an external function call?
(NB. everything is x86_64, that's categorically not the issue)
Here's the c code:
#include <stdio.h>
#ifdef _WIN64
#define __EXT __declspec(dllexport)
#elif _WIN32
#define __EXT __declspec(dllexport)
#else
#define __EXT extern
#endif
static void (*callback)(int value) = NULL;
__EXT int rs_trigger(int val) {
printf("Trigger called\n");
if (val == 1) {
printf("Invoking callback\n");
if (callback != NULL) {
(*callback)(100);
}
else {
printf("No callback bound\n");
}
}
else {
printf("Nope\n");
}
return val + 100;
}
__EXT void rs_register(void *cb) {
printf("Register called\n");
callback = cb;
}
...and finally, if you're going to down vote the question, please at the very least leave feedback on why this is poor question to ask.
I am currently trying to call a function from a C# DLL from an unmanaged C++ app.
After searching for hours on the web and SO, I found I have a few options.
I can use COM, DllExport, or use reverse PInvoke with delegates. The last sounded most appealing to me, so after searching SO I ended up here.
It states that the article shows how to use reverse PInvoke, but it looks like the C# code has to first import the C++ Dll, before it can be used.
I need to be able to use C++ to call my C# Dll functions, without running a C# app first.
Maybe reverse PInvoke isn't the way to do it, but I am quite inexperienced when it comes to low level stuff, so any pointers or tips on how to do this would be great.
The code in the link is
C#
using System.Runtime.InteropServices;
public class foo
{
public delegate void callback(string str);
public static void callee(string str)
{
System.Console.WriteLine("Managed: " +str);
}
public static int Main()
{
caller("Hello World!", 10, new callback(foo.callee));
return 0;
}
[DllImport("nat.dll",CallingConvention=CallingConvention.StdCall)]
public static extern void caller(string str, int count, callback call);
}
C++
#include <stdio.h>
#include <string.h>
typedef void (__stdcall *callback)(wchar_t * str);
extern "C" __declspec(dllexport) void __stdcall caller(wchar_t * input, int count, callback call)
{
for(int i = 0; i < count; i++)
{
call(input);
}
}
Meh, just spin up your own CLR host and run what you need to:
#include <mscoree.h>
#include <stdio.h>
#pragma comment(lib, "mscoree.lib")
void Bootstrap()
{
ICLRRuntimeHost *pHost = NULL;
HRESULT hr = CorBindToRuntimeEx(L"v4.0.30319", L"wks", 0, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (PVOID*)&pHost);
pHost->Start();
printf("HRESULT:%x\n", hr);
// target method MUST be static int method(string arg)
DWORD dwRet = 0;
hr = pHost->ExecuteInDefaultAppDomain(L"c:\\temp\\test.dll", L"Test.Hello", L"SayHello", L"Person!", &dwRet);
printf("HRESULT:%x\n", hr);
hr = pHost->Stop();
printf("HRESULT:%x\n", hr);
pHost->Release();
}
int main()
{
Bootstrap();
}
I need to take a C++ program, load CLR and call a function in a C# library. The function I need to call takes in a COM interface as the parameter.
My problem is, the CLR hosting interface only seems to let you call a method with this signature:
int Foo(String arg)
Example, this C++ code loads CLR and runs the P.Test function in "test.exe":
ICLRRuntimeHost *pClrHost = NULL;
HRESULT hrCorBind = CorBindToRuntimeEx(NULL, L"wks", 0, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (PVOID*)&pClrHost);
HRESULT hrStart = pClrHost->Start();
DWORD retVal;
HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(L"C:\\Test.exe", L"P", L"Test", L"", &retVal);
What I need to do is call a function with this method signature (note I own the C# code, so I can change it):
void SomeFunction(IFoo interface)
Where IFoo is a com interface. I could even do what I need to if I could call a function like this:
IntPtr SomeFunction();
I could have SomeFunction construct a correct delegate then use Marshal.GetFunctionPointerForDelegate. However, I can't figure out how to make the hosting interfaces do anything other than call a function with an int func(string) signature.
Does anyone know how to call a C# function from C++ code with a different signature?
(Note I cannot use C++/CLI for this. I need to use the hosting APIs.)
Edit: I promised to update my answer to include the code for passing 64-bit values, so here's goes..
I left the original answer if someone's interested in a less complicated solution for a 32-bit system.
Note: Since you're using CorBindToRuntimeEx, which is obsolete in .net 4.0, I'll assume a .net 2.0 compliant solution using good old Win32 API.
So, in order to pass data between C# and C++ (in our case - the IntPtr of a delegate), we'll create a small Win32 DLL project, named SharedMem, with two straight-forward methods.
SharedMem.h
#pragma once
#ifdef SHAREDMEM_EXPORTS
#define SHAREDMEM_API __declspec(dllexport)
#else
#define SHAREDMEM_API __declspec(dllimport)
#endif
#define SHAREDMEM_CALLING_CONV __cdecl
extern "C" {
SHAREDMEM_API BOOL SHAREDMEM_CALLING_CONV SetSharedMem(ULONGLONG _64bitValue);
SHAREDMEM_API BOOL SHAREDMEM_CALLING_CONV GetSharedMem(ULONGLONG* p64bitValue);
}
Now for the implementation file:
SharedMem.cpp
#include "stdafx.h"
#include "SharedMem.h"
HANDLE hMappedFileObject = NULL; // handle to mapped file
LPVOID lpvSharedMem = NULL; // pointer to shared memory
const int SHARED_MEM_SIZE = sizeof(ULONGLONG);
BOOL CreateSharedMem()
{
// Create a named file mapping object
hMappedFileObject = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
SHARED_MEM_SIZE,
TEXT("shmemfile") // Name of shared mem file
);
if (hMappedFileObject == NULL)
{
return FALSE;
}
BOOL bFirstInit = (ERROR_ALREADY_EXISTS != GetLastError());
// Get a ptr to the shared memory
lpvSharedMem = MapViewOfFile( hMappedFileObject, FILE_MAP_WRITE, 0, 0, 0);
if (lpvSharedMem == NULL)
{
return FALSE;
}
if (bFirstInit) // First time the shared memory is accessed?
{
ZeroMemory(lpvSharedMem, SHARED_MEM_SIZE);
}
return TRUE;
}
BOOL SetSharedMem(ULONGLONG _64bitValue)
{
BOOL bOK = CreateSharedMem();
if ( bOK )
{
ULONGLONG* pSharedMem = (ULONGLONG*)lpvSharedMem;
*pSharedMem = _64bitValue;
}
return bOK;
}
BOOL GetSharedMem(ULONGLONG* p64bitValue)
{
if ( p64bitValue == NULL ) return FALSE;
BOOL bOK = CreateSharedMem();
if ( bOK )
{
ULONGLONG* pSharedMem = (ULONGLONG*)lpvSharedMem;
*p64bitValue = *pSharedMem;
}
return bOK;
}
Note that for simplicity I'm just sharing a 64-bit value, but this is a general way of sharing memory between C# and C++. Feel free to enlarge SHARED_MEM_SIZE and/or add functions in order to share other data types as you see fit.
This is how we'll consume the above methods: we'll use SetSharedMem() on the C# side in order to set the delegate's IntPtr as a 64-bit value (regardless if the code runs on a 32 or a 64 bit system).
C# Code
namespace CSharpCode
{
delegate void VoidDelegate();
static public class COMInterfaceClass
{
[DllImport( "SharedMem.dll" )]
static extern bool SetSharedMem( Int64 value );
static GCHandle gcDelegateHandle;
public static int EntryPoint(string ignored)
{
IntPtr pFunc = IntPtr.Zero;
Delegate myFuncDelegate = new VoidDelegate( SomeMethod );
gcDelegateHandle = GCHandle.Alloc( myFuncDelegate );
pFunc = Marshal.GetFunctionPointerForDelegate( myFuncDelegate );
bool bSetOK = SetSharedMem( pFunc.ToInt64() );
return bSetOK ? 1 : 0;
}
public static void SomeMethod()
{
MessageBox.Show( "Hello from C# SomeMethod!" );
gcDelegateHandle.Free();
}
}
}
Note the use of GCHandle in order to prevent the delegate from being garbage-collected.
For good measures, we'll use the return value as a success/failure flag.
On the C++ side we'll extract the 64-bit value using GetSharedMem(), convert it to a function pointer and invoke the C# delegate.
C++ Code
#include "SharedMem.h"
typedef void (*VOID_FUNC_PTR)();
void ExecCSharpCode()
{
ICLRRuntimeHost *pClrHost = NULL;
HRESULT hrCorBind = CorBindToRuntimeEx(
NULL,
L"wks",
0,
CLSID_CLRRuntimeHost,
IID_ICLRRuntimeHost,
(PVOID*)&pClrHost
);
HRESULT hrStart = pClrHost->Start();
DWORD retVal;
HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(
szPathToAssembly,
L"CSharpCode.COMInterfaceClass",
L"EntryPoint",
L"",
&retVal // 1 for success, 0 is a failure
);
if ( hrExecute == S_OK && retVal == 1 )
{
ULONGLONG nSharedMemValue = 0;
BOOL bGotValue = GetSharedMem(&nSharedMemValue);
if ( bGotValue )
{
VOID_FUNC_PTR CSharpFunc = (VOID_FUNC_PTR)nSharedMemValue;
CSharpFunc();
}
}
}
The Original Answer - Good for 32-bit Systems
Here's a solution that is based on using IntPtr.ToInt32() in order to convert the delegate func. ptr. to the int which is returned from the static C# EntryPoint method.
(*) Note the use of GCHandle in order to prevent the delegate from being garbage-collected.
C# Code
namespace CSharpCode
{
delegate void VoidDelegate();
public class COMInterfaceClass
{
static GCHandle gcDelegateHandle;
public static int EntryPoint(string ignored)
{
IntPtr pFunc = IntPtr.Zero;
Delegate myFuncDelegate = new VoidDelegate( SomeMethod );
gcDelegateHandle = GCHandle.Alloc( myFuncDelegate );
pFunc = Marshal.GetFunctionPointerForDelegate( myFuncDelegate );
return (int)pFunc.ToInt32();
}
public static void SomeMethod()
{
MessageBox.Show( "Hello from C# SomeMethod!" );
gcDelegateHandle.Free();
}
}
}
C++ Code
We'll need to convert the returned int value to a function pointer, so we'll start off by defining a void function ptr. type:
typedef void (*VOID_FUNC_PTR)();
And the rest of the code looks pretty much like your original code, with the addition of converting and executing the function ptr.
ICLRRuntimeHost *pClrHost = NULL;
HRESULT hrCorBind = CorBindToRuntimeEx(
NULL,
L"wks",
0,
CLSID_CLRRuntimeHost,
IID_ICLRRuntimeHost,
(PVOID*)&pClrHost
);
HRESULT hrStart = pClrHost->Start();
DWORD retVal;
HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(
szPathToAssembly,
L"CSharpCode.COMInterfaceClass",
L"EntryPoint",
L"",
&retVal
);
if ( hrExecute == S_OK )
{
VOID_FUNC_PTR func = (VOID_FUNC_PTR)retVal;
func();
}
A Little Bit of Extra
You can also make use of the string input in order to choose which method to execute:
public static int EntryPoint( string interfaceName )
{
IntPtr pFunc = IntPtr.Zero;
if ( interfaceName == "SomeMethod" )
{
Delegate myFuncDelegate = new VoidDelegate( SomeMethod );
gcDelegateHandle = GCHandle.Alloc( myFuncDelegate );
pFunc = Marshal.GetFunctionPointerForDelegate( myFuncDelegate );
}
return (int)pFunc.ToInt32();
}
You can get even more generic by using reflection in order to find the correct method according to the given string input.