i am having trouble importing c++ unmanaged dll into C# [winform]. Can someone help?
Basically i am just trying to create a safearray of strings in c++ and trying to send it to C#.
Here is my c++ code.
extern "C" __declspec(dllexport) BOOL GetStringArr(SAFEARRAY* arr)
{
SAFEARRAY* myArray;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = 5;
myArray = SafeArrayCreate(VT_BSTR, 1, rgsabound);
VARIANT* pvData = (VARIANT*)(myArray->pvData);
pvData[0].vt = VT_BSTR;
pvData[0].bstrVal = SysAllocString(L"FirstString");
pvData[1].vt = VT_BSTR;
pvData[1].bstrVal = SysAllocString(L"SecondString");
pvData[2].vt = VT_BSTR;
pvData[2].bstrVal = SysAllocString(L"ThirdString");
pvData[3].vt = VT_BSTR;
pvData[3].bstrVal = SysAllocString(L"FourthString");
pvData[4].vt = VT_BSTR;
pvData[4].bstrVal = SysAllocString(L"FifthString");
arr = myArray;
return true;
}
Here is my c# code.
[DllImport("MyData.dll", EntryPoint = "GetStringArr")]
public static extern bool GetStringArr([MarshalAs(UnmanagedType.SafeArray)] out Array strServerList);
i am getting exception when i call GetStringArr from C#. i am sure there is something silly i am doing. Can someone please help?
Thanks in advance.
Several problems in your C++ code. You are returning an array, that requires the argument to be SAFEARRAY**. You also are stuffing the array with the wrong data, you created an array of strings but you are writing VARIANTs. Not sure what the intention was, I'll keep variants in the code fix:
extern "C" __declspec(dllexport) BOOL GetStringArr(SAFEARRAY** arr)
{
SAFEARRAY* myArray;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = 5;
myArray = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
VARIANT* pvData = 0;
SafeArrayAccessData(myArray, (void**)&pvData);
pvData[0].vt = VT_BSTR;
pvData[0].bstrVal = SysAllocString(L"FirstString");
// etc..
SafeArrayUnaccessData(myArray);
*arr = myArray;
return true;
}
C# code:
object[] array;
bool ok = GetStringArr(out array);
[DllImport(#"blah.dll", EntryPoint = "GetStringArr")]
[return: MarshalAs(UnmanagedType.U1)]
public static extern bool GetStringArr([MarshalAs(UnmanagedType.SafeArray)] out object[] strServerList);
Some problems on both the C and .NET side of things
On the C side
Incorrect argument indirection. Since you are allocating the SAFEARRAY descriptor in the function you need a SAFEARRAY**.
The SAFEARRAY is not being filled correctly. You created the SAFEARRAY descriptor with a base type of VT_BSTR, this means that the data elements should be BSTRs.
C Code
extern "C" __declspec(dllexport)
BOOL GetStringArr(SAFEARRAY** arr)
{
SAFEARRAY* myArray;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = 5;
myArray = SafeArrayCreate(VT_BSTR, 1, rgsabound);
BSTR* pvData = (BSTR*)(myArray->pvData);
pvData[0] = SysAllocString(L"FirstString");
pvData[1] = SysAllocString(L"SecondString");
pvData[2] = SysAllocString(L"ThirdString");
pvData[3] = SysAllocString(L"FourthString");
pvData[4] = SysAllocString(L"FifthString");
*arr = myArray;
return true;
}
On the .NET side
The Calling convention needs to be specified otherwise you will have stack issues
You should set the SafeArraySubType
You can use out string[] to get the pointer to the SAFEARRAY
.NET Code
class Program
{
static void Main(string[] args)
{
string[] data;
bool b = GetStringArr(out data);
}
[DllImport("MyData.dll",
CallingConvention = CallingConvention.Cdecl)]
public static extern bool GetStringArr(
[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)]
out string[] strServerList);
}
Do you have access to the native DLL's source? If so you can enable unmanaged debugging in your Managed projects options, and step thru the unmanaged code (preferably Debug build) to see what's going on. If nothing else you can enable Exceptions in the debugger options, and debug to see where the native exception gets thrown.
I recommend you add a C++/CLI project (assembly) to your C# solution. That will enable you to write code that live in both managed and unmanaged land simultaneously. That means that your C++/CLI code can create a List<string^> instead and add managed strings to it before you return it to C#. :-)
Related
I am trying to handle a C++ DLL which in turn connects to a C# DLL.
My problem is when I want to send or receive arrays between C++ and C# DLLs.
I have created the C# DLL and C++ DLL, so I can modified all the files. Within the C# DLL, I must necessarily have an array of string an another of double because that is where I make use of other functions unrelated to this question. (The declaration of the function that I have to use in C# DLL is Error SetMultipleChannelValues(string[] names,double[] values).
I am using CLI in Visual Studio 2015 to compile and generate all DLLs and projects.
Here it is the C# code that I have:
public static Error SetMultipleSignals(string[] signals, double[] values)
{
Error error = null;
error = workspace.SetMultipleChannelValues(signals, values);
if (error.IsError)
Console.WriteLine("[DLL] Error in SetMultipleChannelValues(). Code: " + error.Code + ". Message: " + error.Message);
return error;
}
Here it is the C++ code that I have:
bool setMultipleSignals(double* setSignal_values)
{
array<double, DEFAULT_SETSIGNAL_SIZE> values;
for (int index = 0 ; index < DEFAULT_SETSIGNAL_SIZE ;index++)
{
values[index] = *(setSignal_values + index);
}
if (!veristand_wrapper_cs::VeriStand_dll::SetMultipleSignals(setSignals_path, values)) // This is the call to C# function
return true;
else
return false;
}
Here it is the C++ header that I have:
#pragma once
#define VERISTAND_WRAPPER_CPP __declspec(dllexport)
#include <string>
#include <iostream>
#include <windows.h>
#include <array>
using namespace std;
using namespace System;
using System::Runtime::InteropServices::Marshal;
#using "veristand_wrapper_cs.dll" // This is the C# DLL
#define DEFAULT_SETSIGNAL_SIZE 100
extern "C" VERISTAND_WRAPPER_CPP bool setMultipleSignals(double* setSignal_values); // This function is called from API C++
I pass the double array with a pointer to double as a parameter from my C++ application, but I have to pass to C# DLL an array, so I have tried to build a whole array of double before pass it.
The array setSignals_path is created as a global array in this C++ DLL as array<String^, DEFAULT_SETSIGNAL_SIZE> setSignals_path;.
The problem is that the call of C# function provides me an error that says that I can not call the function with these arguments. It expects array<System::String^> ^signals, array<double> ^values and I am passing std::array<System::String^, 100Ui64>, std::array<double, 100Ui64>
The concept of my idea is simple. From the C++ API, pass a pointer to the array of doubles, so that the function of my DLL in C++ passes the array of doubles and strings to the DLL of C#, and it returns the array of modified doubles to be able to indicate to the API from C++ that the values have been modified from that memory address.
Would anyone know how to approach this problem or how to do it in some other way to make it work?
It looks like you are using c++/cli. So you can actually use .Net types in native code.
In C++/cli you need to explicitly declare if a variable is a reference, that is what the ^ is for. Your arrays is declared without this, but your method needs a reference. You will also need to allocate your array on the managed heap. So your array should probably be declared as
array<double>^ values = gcnew array< double>(DEFAULT_SETSIGNAL_SIZE);
But it was a while ago I coded c++/cli, so I may have missed something. See How to use arrays in c++/cli. It also looks like you may be able to use ref new instead of gcnew
Use following. The size of array must be 100, or add another parameter before the string array indicating length. The string in code is a byte array terminating with '\0'. c++ must use a method that will read the null terminated string. A double is 8 bytes so the 100 double can be consecutive memory locations (800 bytes).
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StringArray
{
public IntPtr[] unmanagedArray;
}
public struct DoubleArray
{
public double[] values;
}
public static Error SetMultipleSignals(IntPtr signals, IntPtr values)
{
const int DEFAULT_SETSIGNAL_SIZE = 100;
StringArray stringStruct = new StringArray();
stringStruct.unmanagedArray = new IntPtr[DEFAULT_SETSIGNAL_SIZE];
Marshal.PtrToStructure(signals, stringStruct);
string[] strArray = new string[DEFAULT_SETSIGNAL_SIZE];
for(int i = 0; i < DEFAULT_SETSIGNAL_SIZE; i++)
{
strArray[i] = Marshal.PtrToStringAnsi(stringStruct.unmanagedArray[i]);
}
DoubleArray dArray = new DoubleArray();
dArray.values = new double[DEFAULT_SETSIGNAL_SIZE];
dArray = Marshal.PtrToStructure<DoubleArray>(values);
return null;
Here is going the other way.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StringArray
{
public IntPtr[] unmanagedArray;
}
static void Main(string[] args)
{
string[] strArray = { "Message 1", "Message 2", "Message 3"};
StringArray stringStruct = new StringArray();
stringStruct.unmanagedArray = new IntPtr[strArray.Length];
for (int i = 0; i < strArray.Length; i++)
{
stringStruct.unmanagedArray[i] = Marshal.StringToBSTR(strArray[i]);
}
IntPtr unmanagedArray = Marshal.AllocHGlobal(Marshal.SizeOf(stringStruct));
Marshal.StructureToPtr(stringStruct, unmanagedArray, false);
}
Goal: Retrieve the array of structs from c++, from the c++ side it's finalized to return pointer to struct pointer (Double pointer)(Not in my control).
Sample c++ code :
struct Output
{
char* Name;
};
extern "C"
{
__declspec(dllexport) Output** getoutput()
{
Output* items = (Output*)malloc(sizeof(Output) * 4);
items->Name = "Hello World";
return &items;
}
}
c# Side code :
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Output
{
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string Name;
};
[DllImport(#"CPPInvokeExposed.dll",
CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr getoutput();
static void Main(string[] args)
{
var output = Program.getoutput();
Output[] outputs = new Output[1];
MarshalUnmananagedArray2Struct<Output>(output, 1, out outputs);
**outputs[0]// this has junk chars**
}
public static void MarshalUnmananagedArray2Struct<T>(IntPtr unmanagedArray, int length, out T[] mangagedArray)
{
var size = Marshal.SizeOf(typeof(T));
mangagedArray = new T[length];
for (int i = 0; i < length; i++)
{
IntPtr ins = new IntPtr(unmanagedArray.ToInt64() + i * size);
mangagedArray[i] = Marshal.PtrToStructure<T>(ins);
}
}
Is not clear whether the problem is in c++ or c# code. What should be the correct way get the Char* from c++ which exists in struct.
One strange thing is, for the same code if c++ code return single pointer(Output*) instead double pointer(Output**), there is no junk character, getting the correct assigned value. looks like something wrong while returning double pointer from c++.
You C++ code causes undefined behaviour:
return &items;
This returns the address of an automatic variable.
That variable will not exist anymore when the function returns. Accessing that memory location is illegal and causes undefined behaviour.
A clean solution would be to return the pointer itself, not the address. But in your question you state that the return type is not under your control.
In that case you must create the second level of indirection by yourself:
__declspec(dllexport) Output** getoutput()
{
Output* items = (Output*)malloc(sizeof(Output) * 4);
items->Name = "Hello World";
Output **retval = (Output**)malloc(sizeof(Output*))
*retval = items;
return retval;
}
Of course you also need to take care about freeing both levels of memory allocation afterwards.
BTW:
You allocate memory for 4 structs but only assign a value to member of the first element.
I am trying to get map from dll c++
So I have to get the map and parsing it as dictionary in C# side.
I have tried to do below steps and it is not worked.
C++ code:
extern "C" __declspec(dllexport) map<string, int> createMap(string &fileName) {
ifstream infile(fileName);
vector<string> bitsLine;
bool headerEnded = false;
string line;
int i = 0;
int length = 0;
while (getline(infile, line)) {
if (headerEnded) {
bitsLine = split(line, ',');
signalsMap.insert({ bitsLine.at(0), length });
}
else {
if (line.find("HEADER_END") != std::string::npos) {
headerEnded = true;
}
}
length = infile.tellg();
i++;
}
return signalsMap;
}
C# code:
Dictionary<string, int> x = createMap("C:/users/asalah/source/repos/WindowsFormsApp3/WindowsFormsApp3/RR_Test2_3.csv");
The simple answer to this question is unfortunately "you shouldn't". You shouldn't export STL types from a dll in the first place, much less try to marshal them in C#. An STL type may vary in memory layout from compiler to compiler, C++ runtime to C++ runtime. It could cause very fragile code. So if you export a C function it should take a const char* instead of std::string for example.
What you could do could be to just marshal each key and value as they are made available. The advantage of this is that you don't have to do any work with memory management and it's fairly simple to integrate in what you already have, though I'm making no statement about performance.
Here is a short C++ and C# example to get you going on such a solution if it is of any help to you:
extern "C" __declspec(dllexport) void doFoo(void(*adder)(const char*, int32_t))
{
adder("Test", 346);
}
Below is the C# code for consuming this API. It should simply just add "Test" with the value 346 to the dictionary and nothing more. It does this by invoking a callback function which is a native shim around Dictionary.Add for the specified instance of the dictionary.
namespace Eff3
{
using System.Collections.Generic;
using System.Runtime.InteropServices;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void DictionaryAdd(string key, int value);
class Program
{
[DllImport("TestDll", CallingConvention = CallingConvention.Cdecl)]
static extern void doFoo(DictionaryAdd callback);
static void Main()
{
var result = new Dictionary<string, int>();
doFoo(result.Add);
}
}
}
I've tested this on my machine, and I built the DLL in Visual C++ 2017 in x64 and disabled "Prefer 32-bit" in C#.
The following code snippet is from Unities Bonjour client example, which demonstrates how to interact with native code from C#. It's a simple C function that returns a string back to C# (Unity):
char* MakeStringCopy (const char* string)
{
if (string == NULL)
return NULL;
char* res = (char*)malloc(strlen(string) + 1);
strcpy(res, string);
return res;
}
const char* _GetLookupStatus ()
{
// By default mono string marshaler creates .Net string for returned UTF-8 C string
// and calls free for returned value, thus returned strings should be allocated on heap
return MakeStringCopy([[delegateObject getStatus] UTF8String]);
}
The C# declaration of the function looks like:
[DllImport ("__Internal")]
private static extern string _GetLookupStatus ();
There are a few things that puzzle me here:
Is this the right way to return a string from iOS native code to C#?
How does the returned string ever get freed?
Is there a better way to do it?
Any insights in this matter are appreciated.
Thank you.
1.No.
2.You have to do that yourself.
3.Yes
If you allocate memory inside a function on the C or C++ side, you must free it. I don't see any code allocating memory on the side but I assume you left that part. Also, do not return a variable declared on the stack to C#. You will end up with undefined behavior including crashes.
Here is a C++ solution for this.
For the C solution:
char* getByteArray()
{
//Create your array(Allocate memory)
char * arrayTest = malloc( 2 * sizeof(char) );
//Do something to the Array
arrayTest[0]=3;
arrayTest[1]=5;
//Return it
return arrayTest;
}
int freeMem(char* arrayPtr){
free(arrayPtr);
return 0;
}
The only difference is that the C version uses malloc and free function to allocate and de-allocate memory.
C#:
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr getByteArray();
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int freeMem(IntPtr ptr);
//Test
void Start()
{
//Call and return the pointer
IntPtr returnedPtr = getIntArray();
//Create new Variable to Store the result
byte[] returnedResult = new byte[2];
//Copy from result pointer to the C# variable
Marshal.Copy(returnedPtr, returnedResult, 0, 2);
//Free native memory
freeMem(returnedPtr);
//The returned value is saved in the returnedResult variable
byte val1 = returnedResult[0];
byte val2 = returnedResult[1];
}
Note that this is only a test that uses char with 2 characters only. You can make the size of the string dynamic by adding a out int outValue parameter to the C# function then adding int* outValue parameter to the C function. You can then write to this parameter on the C side the size of the character is and access that size from the C# side.
This size can then be passed to the last argument of the Marshal.Copy function and remove the current hard-coded 2 value limit. I will leave this for you to do but if confused, see this post for example of that.
The better solution is to pass StringBuilder to the native side then write to it. The bad side is that you have to declare the size of the StringBuilder on time.
C++:
void __cdecl _GetLookupStatus (char* data, size_t size)
{
strcpy_s(data, size, "Test");
}
C#:
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int _GetLookupStatus(StringBuilder data, int size);
//Test
void Start()
{
StringBuilder buffer = new StringBuilder(500);
_GetLookupStatus (buffer, buffer.Capacity);
string result = buffer.ToString();
}
If you are looking for the fastest way then you should use char array on the C# side, pin it on C# side then send it to C as IntPtr. On the C side, you can use strcpy_s to modify the char array. That way, no memory is allocated on the C side. You are just re-using the memory of the char array from C#. You can see the float[] example at the end of the answer here.
I defined a function in C DLL library.
__declspec(dllexport) void* GetText();
It will return a string which is dynamically allocated from heap memory (And GlobalAlloc is used here for allocating memory). Note that the returned string is not null-terminated.
Then at C# side I tried two methods to declare the function
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern String GetText();
When calling above method, the application will crash without any exception thrown.
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr GetText();
ptr = GetText();
string text = Marshal.PtrToStringAuto(ptr, 1000);
And calling this method will return incorrect string. Checked the real bytes by using Marshal.Copy, I found the bytes value is not same as the value in DLL library. (I think it's caused by Virtual Memory, C# process cannot access memory space of the DLL directly)
(Don't mind the string length, I hard coded it to 1000 for ease)
This is the C++ code and the memory value of the string when debugging (It's a Console Application but not the original DLL, because Console Application is easy to debug. But the DLL code is same as this one except the logging part).
Following is the original DLL code
__declspec(dllexport) char* GetText(){
VTHDOC hDoc = NULL;
VTHTEXT hText = VTHDOC_INVALID;
DAERR da_err = NULL;
DAERR ta_err = NULL;
DAERR read_err = NULL;
char *buf = (char*)GlobalAlloc(GMEM_FIXED, 1000);
DWORD real_size;
DAInitEx(SCCOPT_INIT_NOTHREADS, OI_INIT_DEFAULT);
da_err = DAOpenDocument(&hDoc, 2, "D:\\1TB.doc", 0);
ta_err = TAOpenText(hDoc, &hText);
read_err = TAReadFirst(hText, (VTLPBYTE)buf, 1000, &real_size);
return buf;
}
But at C# side the bytes are not same as C++ side
You can see the first byte in C++ is 0, but it's 200 for C# (decimal)
Another thing to note: if I return a const string(e.g. "AASSDD") directly in DLL code, C# side will get the correct string
You can't do it that way. Marshaling of string works only for null-terminated strings (or for BSTR, if you specify some options). You can:
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr GetText();
But from there, it isn't clear how the C# program should know the length of the string.
The various Marshal methods of C# handle BSTR (that have internally their length) or NUL terminated strings.
As already stated, it works for null-terminated strings only, in the following way:
C# part, declaration:
[DllImport("myDll.dll", EntryPoint = "myString", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
extern private static string myString(out int size);
C# part, usage:
int size;
string s = myString(out size);
C++ part:
char* myString(int* size)
{
*size = 20;
char* strg = (char*)::GlobalAlloc(GMEM_FIXED, *size);
memset(strg, 0x3f, *size); //preset with a questionmark
for (int i=0; i < 9; i++)
strg[i] = 0x40 + i;
strg[*size -1] = 0; //limit the maximum string length
return strg;
}
And the obtained C# string:
"#ABCDEFGH??????????", value of size: 20
A treatment of the issue may be found here