SWIG C# Passing binary data with directors="1" - c#

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.
}
}

Related

Swig Pass Std::vector by ref / out from C# to C++

Given the C++ function
void Foo(unsigned int _x, unsigned int _y, std::vector< unsigned int > &_results)
And the Swig interface file to map std::vector to type VectorUInt32 in C#
%include "std_vector.i"
namespace std {
%template(VectorUInt32) vector<unsigned int>;
};
I get the following result in C# code:
public static void Foo(uint _x, uint _y, VectorUInt32 _results)
Which is great, but what I was really hoping for was this:
public static void Foo(uint _x, uint _y, out VectorUInt32 _results)
Does anyone know how to map the std::vector from C++ to C# as a ref or out param?
Shame on you Stackoverflow for not having an answer!
Anyway, the answer is, if anyone else is interested... If you create the VectorUInt32 type in C# and pass it to the C++ function it is passed by reference, so can be modified inside C++ without a ref or out param.
The interface becomes:
C++
void Foo(unsigned int _x, unsigned int _y, std::vector< unsigned int > &_results)
Interface file
%include "std_vector.i"
namespace std {
%template(VectorUInt32) vector<unsigned int>;
};
C#
public static void Foo(uint _x, uint _y, VectorUInt32 _results)
Usage
// Create and pass vector to C++
var vec = new VectorUInt32();
Foo(x, y, vec);
// do something with vec, its been operated on!

Create SWIG C# wrapper for function that contains void* parameter

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.

C++/CLI wrapper memleak when calling managed lib from unmanaged

I have this error when calling managed code library from unmanaged code:
Run-Time Check Failure #2 - Stack around the variable 'BridgeObj' was corrupted.
The code I created is:
Managed lib:
using System;
namespace My.Name.Space
{
public class Sample
{
public int Request(string xml_input, out string xml_output)
{
xml_output = "Retun string.";
return 0;
}
}
}
Wrapper C++/CLI:
#include "stdafx.h"
#include <msclr\auto_gcroot.h>
#using "..\link\ManagedLib.dll"
using namespace System::Runtime::InteropServices; // Marshal
struct ManagedModul
{
public:
msclr::auto_gcroot<My::Name::Space::Sample^> SampleModul;
};
class __declspec(dllexport) Bridge
{
private:
ManagedModul _private;
public:
Bridge()
{
_private.SampleModul = gcnew My::Name::Space::Sample();
};
~Bridge()
{
}
int Request ( const char * xmlin, char ** xmlout )
{
System::String ^ps;
_private.SampleModul->Request(gcnew System::String(xmlin), ps);
* xmlout = (char*) (Marshal::StringToHGlobalAnsi(ps)).ToPointer();
return 0;
}
};
Sample usage:
#include "stdafx.h"
#include <Windows.h>
#pragma comment ( lib, "..\\link\\Wrapper.lib" )
class Bridge
{
public:
Bridge();
~Bridge();
int Request ( const char * xmlin, char ** xmlout );
};
int _tmain(int argc, _TCHAR* argv[])
{
Bridge BridgeObj;
char * buffer = NULL;
BridgeObj.Request("aaaaa", & buffer );
LocalFree ( buffer );
return 0;
}
class Bridge
{
public:
Bridge();
~Bridge();
int Request ( const char * xmlin, char ** xmlout );
};
This is a very, very bad practice. Instead of using a .h file that is used in both projects, you redeclared the Bridge class. And got it wrong, you missed the _private member. This always goes wrong. If not immediately then in a year from now when you modify the real Bridge class.
What happens next is pretty inevitable. The real class object is larger than what the compiler thinks when it compiles your redeclared class. So it doesn't reserve enough space on the stack to store the object. Inevitable, that will cause another variable on the stack to get whacked, overwritten whenever the real class assigns the _private member.
Buy Microsoft a cigar for building in this diagnostic, stack corruption like this is normally extraordinarily hard to diagnose. And use a .h file to declare Bridge.

Issues import C DLL into C# program

I've been reading on this problem a lot before i decided to post it here. I have C dll that i need to access inside of a C# program. The DLL's code is pretty simple. As it's nothing more then a hook into biometrics driver (it's C because it's has to include lib and header files from driver for the code to work). Here is the code for the dll:
#include <stdio.h>
#include <windows.h>
#include <DPCN_TM.h>
__declspec(dllexport) unsigned char * ConvertPlatinumTemplateToDpTemplate(const unsigned char* inputData);
HRESULT Convert(
const unsigned char* inputData,
const size_t size,
DPCN_DATA_TYPE inputDataType,
DPCN_DATA_TYPE outputDataType,
DPCN_PURPOSE purpose,
unsigned char** ppbOutputData,
const void * outputParameters,
size_t * pcbData)
{
HRESULT hr = 0;
size_t cbData = 0;
if (FAILED(hr = DPCNConvertFingerprintData(inputData, size, inputDataType, purpose, NULL, outputDataType, outputParameters, NULL, &cbData))) {
return hr;
}
if (!(*ppbOutputData = (unsigned char *)malloc(cbData))) {
return DPCN_ERR_NO_MEMORY;
}
hr = DPCNConvertFingerprintData(inputData, size, inputDataType, purpose, NULL, outputDataType, outputParameters, *ppbOutputData, &cbData);
*pcbData = cbData;
return hr;
}
unsigned char * ConvertPlatinumTemplateToDpTemplate(const unsigned char* inputData) {
HRESULT hr = 0;
const size_t inputSize = sizeof(inputData);
DPCN_DATA_TYPE inputDataType = DPCN_DT_DP_TEMPLATE;
DPCN_DATA_TYPE outputDataType = DPCN_DT_DP_PLATINUM_TEMPLATE;
unsigned char *pbOutputData = NULL;
size_t cbData = 0;
hr = Convert(inputData, inputSize, inputDataType, outputDataType, DPCN_PURPOSE_IDENTIFICATION, &pbOutputData, NULL, &cbData);
return pbOutputData;
}
As you can see the contents of the DLL is pretty straight forward. From the code you can see I'm needing to access this function inside of the C# program.
unsigned char * ConvertPlatinumTemplateToDpTemplate(const unsigned char* inputData);
Now in my C# code I have done this:
[DllImport(#"path_to_dll\DPFPTemplateConvert.dll")]
public extern byte[] ConvertPlatinumTemplateToDpTemplate(byte[] inputData);
When I call the function I end up getting this error:
A first chance exception of type 'System.Runtime.InteropServices.MarshalDirectiveException' occurred in DLLImportTest.exe
Additional information: Cannot marshal 'return value': Invalid managed/unmanaged type combination.
What am I doing wrong?
An unsigned char * cannot be converted to a .NET byte array, for one simple reason: what should be the length of that array?
And even then, it's a bad idea to pass a pointer out of your function, if that pointer is pointing to memory allocated by that function. Who will release this memory?
You should have the .NET side allocate the byte[] for the result, and pass it into the function.
If the .NET side does not know beforehand how big the allocated array needs to be, use a callback as explained here: http://blog.getpaint.net/2012/04/30/marshaling-native-arrays-back-as-managed-arrays-without-copying/

Reverse PInvoke from native C++

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();
}

Categories

Resources