Can one have Python receive a variable-length string array from C#? - c#

This may be a red herring, but my non-array version looks like this:
C#
using RGiesecke.DllExport;
using System.Runtime.InteropServices;
namespace Blah
{
public static class Program
{
[DllExport("printstring", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.AnsiBStr)]
public static string PrintString()
{
return "Hello world";
}
}
}
Python
import ctypes
dll = ctypes.cdll.LoadLibrary(“test.dll")
dll.printstring.restype = ctypes.c_char_p
dll.printstring()
I am looking for a printstrings, which would fetch a List<string> of variable size. If that's not possible, I will settle for a fixed-length string[].

.NET is capable of transforming the object type into COM Automation's VARIANT and the reverse, when going through the p/invoke layer.
VARIANT is declared in python's automation.py that comes with comtypes.
What's cool with the VARIANT is it's a wrapper that can hold many things, including arrays of many things.
With that in mind, you can declare you .NET C# code like this:
[DllExport("printstrings", CallingConvention = CallingConvention.Cdecl)]
public static void PrintStrings(ref object obj)
{
obj = new string[] { "hello", "world" };
}
And use it like this in python:
import ctypes
from ctypes import *
from comtypes.automation import VARIANT
dll = ctypes.cdll.LoadLibrary("test")
dll.printstrings.argtypes = [POINTER(VARIANT)]
v = VARIANT()
dll.printstrings(v)
for x in v.value:
print(x)

Related

Pass an array to a C# DLL from C++ DLL

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

Passing string back from Golang to C#

I am trying to use a Golang library that is compiled as a shared C library in my .NET application. It's compiled with the following command: go build --buildmode=c-shared -o main.dll
My Golang code looks like this:
func DoRequest(request *C.char) *C.char {
// ...
// b = []byte
return (*C.char)(unsafe.Pointer(&b[0]))
}
How can I get this string back into a usable form in the .NET world? I've tried this but I get a panic from Go:
[DllImport("C:\\Users\\GolandProjects\\awesomeProject\\main.dll", EntryPoint = "DoRequest", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.LPStr)]
extern static string DoRequest(byte[] requestJsonString);
static void Main(string[] args)
{
var result = DoRequest(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new Request
{
// ...
})));
Console.ReadLine();
}
Please start with thoroughly reading the doc on cgo.
Strings in Go are not NUL-terminated, so there's thre ways to gateway a Go string across a C-compatible shared library barrier:
Copy it to a C-style NUL-terminated string—yielding a pointer to the first character of a copy, and passing it.
Pass the pointer to the 1st character of a Go string, and then also explicitly pass the string length along with it.
Teach "the other side" about the native Go's string representation and use it. A string in Go is a (packed) struct containing a pointer and a pointer-sized integer.

How I can get map<string, int> from C++ to C#

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

How to send a struct by ref containing a string from C# to unmanaged C++ library

I need to send a struct from C# managed code to a C library. The C library will populate the values in the struct. I have been trying to pass the struct as a reference so that the C# code will get the updated data values.
This is an example C function in libshlib.so:
void sharedStruct(struct data* d)
{
d->number = calcSomething();
d->message = doSomething();
}
I can send individual parameters (int and StringBuilder) and the library code runs and returns new values to the C# code that called it.
But how can I create a struct in C# that contains both int and string and send it to the unmanaged code (C library) which will populate the values to be used back in the C# code?
The C struct might be like this:
struct data
{
int number;
char* message;
};
Right now I'm trying to establish the best way to manipulate data in the C library for use in C#. I am writing both pieces of code so I am flexible but right now I haven't been able to get it working.
If you want the struct to be populated by the C code, then you are probably looking for an out variable. Note that the mono runtime by default will use free() to deallocate any strings you pass in and you might have to take extra care with padding in structures (see the StructLayout stuff in msdn). For strings, further problems can be had with character sets.
Sample code:
Managed.cs:
using System;
using System.Runtime.InteropServices;
struct Data
{
public int number;
public string message;
}
class Managed
{
[DllImport("unmanaged")]
extern static void Foo(out Data data);
static void Main()
{
Data data;
Foo(out data);
Console.WriteLine("number = {0}, message = {1}", data.number, data.message);
}
}
unmanaged.c:
#include <string.h>
struct data
{
int number;
char* message;
};
void Foo(struct data* data)
{
data->number = 42;
data->message = strdup("Hello from unmanaged code!");
}
Test run:
$ mcs Managed.cs
$ gcc -shared -fPIC -o libunmanaged.so unmanaged.c
$ LD_LIBRARY_PATH=$PWD mono Managed.exe
number = 42, message = Hello from unmanaged code!

Convert managed String (C#) to LPCOLESTR (C++)

I wave a method in C++ that receives a parameter of the LPCOLESTR type. I'm accessing this method through C#, but I can't make the correct conversion between String and this type.
Let's say the method signinature in C++ is:
void Something(LPCOLESTR str)
In C#, I'm trying to call it (all reference issues to access the method through a DLL have been solved already):
String test = "Hello world";
Something(test);
But with no luck, of course. If anyone can help me, I'd be very glad. Thank you!
Code snippet:
As an example, here's my C++ portion of code, defined in the file MixedCodeCpp.h (CLR Class Library)
#include "windows.h"
#pragma once
using namespace System;
namespace MixedCodeCpp
{
public ref class MyClass
{
public:
HRESULT Something(LPCOLESTR str)
{
return S_OK;
}
};
}
And here's my code in C# (I've added a reference to the C++ project in the C# project, through Visual Studio):
StringBuilder sb = new StringBuilder();
sb.Append("Hello world");
MixedCodeCpp.MyClass cls = new MixedCodeCpp.MyClass();
cls.Something(sb);
The argument will appear as Char* on the C# side. That requires unsafe code, like this:
unsafe static void CallSomething(MyClass obj, string arg) {
IntPtr mem = Marshal.StringToCoTaskMemUni(arg);
try {
obj.Something((Char*)mem);
}
finally {
Marshal.FreeCoTaskMem(mem);
}
}
It makes very little sense to expose the LPCOLESTR to other managed code. This method really should accept a String^ and convert to wchar_t* internally.
Try StringBuilder instead of String thusly:
System.Text.StringBuilder test = new System.Text.StringBuilder ();
test.Append("Hello world");
Something(test);
I've used it that way in pinvoke to Win32 functions that required various string pointers as parameters. Not sure it will work with your API but it's worth a shot. Here's some MSDN info about the process. And here is another.
Here's an arbitrary sample of what your import statement and declaration ought to look like. (To be taken with a grain of salt.)
[DllImport(SomeLib.SomeName, CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Something(StringBuilder pMyString);
StringBuilder str = new StringBuilder(MAX_PATH);
DWORD uSize;
bool b = Something(str);

Categories

Resources