I would like to call this C++ function from my C# code:
void GetArrayOfNames(char** names, int nbOfNames);
To call it in C++, I just define an array of char*:
char* aNames[20];
And allocate each name in a loop:
for(int i-0; i<20; i++)
{
aNames[i] = new char[50];
}
Then call:
GetArrayOfNames(aNames, 20);
In my C# code, I have:
[DllImport("MyDLL.dll")]
unsafe static extern void GetArrayOfNames(char** ppNames, int nbOfNames);
Now, how do I do the memory allocation and call GetArrayOfNames? Also, any way of not having to declare my function as "unsafe"?
Probably:
static extern void GetArrayOfNames([MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr] StringBuilder[] args, int count);
// Call using
StringBuilder[] arr = new StringBuilder[20];
for (int i = 0; i < arr.Length; i++)
{
arr[i] = new StringBuilder(50);
}
GetArrayOfNames(arr, arr.Length);
As I understand char** is just a reference to string. I created a small test for myself and it worked fine:
class Program
{
[DllImport("TestCppLib.dll", CharSet = CharSet.Ansi, EntryPoint = "?fnTestCppLib##YAHPAPAD#Z", CallingConvention=CallingConvention.Cdecl)]
extern static int fnTestCppLib(ref string s);
static void Main(string[] args)
{
var s = "some";
var t = fnTestCppLib(ref s);
Debug.Assert(s == "test");
}
}
The implementation of function in C++ is:
TESTCPPLIB_API int fnTestCppLib(char ** str)
{
*str = "test";
return 42;
}
This ended up working:
static extern int GetArrayOfNames(IntPtr[] astr, int iLength);
And called/setup like this:
IntPtr[] intArr = new IntPtr[200];
for (int i = 0; i < intArr.Length; i++)
{
intArr[i] = Marshal.AllocHGlobal(256);
}
int nbOfNames = 2;
GetArrayOfNames(intArr, nbOfNames);
And to put back in a string:
string tmp;
tmp = Marshal.PtrToStringAnsi(intArr[i]);
Can i help you? my sample code in C++:
//CamMethods.cpp:
#include <iostream>
using namespace std;
#include "CamPS.h"
namespace Export {
char * CameraPS::CamStart(char *s){
return "teste";
};
}
//CamPs.h
namespace Export{
class CameraPS
{
public:
_declspec(dllexport) char * _stdcall CamStart(char *s);
};
}
and in C# i call:
using UnityEngine;
using System;
using System.Collections;
using System.Runtime.InteropServices;
public class DllTeste : MonoBehaviour
{
public int x;
[DllImport ("PS_Cam.dll", EntryPoint="CamStart", CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CamStart (int s);//i can put <string> in same place of <int>
void Start ()
{
}
void Update ()
{
// IntPtr a = CamStart(x);
// string b = Marshal.PtrToStringAnsi(a);
// Debug.Log(b);
Debug.Log(Marshal.PtrToStringAnsi(CamStart(x)));
}
}
Sorry for my english.
Related
I have a simple C++ win32 DLL developed in visual studio 2017 and compiled in 64 bit environment having the following code:
typedef struct sum {
struct {
int num1;
int num2;
} nums;
} sum1;
extern "C" {
__declspec(dllexport) int initialize(sum1 *summing)
{
int res;
res = summing->nums.num1 + summing->nums.num2;
return res;
}
}
The above code contains a method which returns the sum of two integers by taking a typedef struct as an argument.
I have a C# client application which consumes this Win32 C++ DLL using PInvoke. Following is the code of my C# client application:
[StructLayout(LayoutKind.Sequential)]
public struct nums
{
public int a;
public int b;
}
[StructLayout(LayoutKind.Sequential)]
public struct mydef
{
public IntPtr sum;
}
public class LibWrap
{
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref mydef mydef);
}
class Program
{
static void Main(string[] args)
{
mydef mydef = new mydef();
nums nums;
nums.a = 6;
nums.b = 6;
IntPtr buffer1 = Marshal.AllocCoTaskMem(Marshal.SizeOf(nums));
Marshal.StructureToPtr(nums, buffer1, false);
mydef.sum = buffer1;
int res = LibWrap.Initialize(ref mydef);
Console.WriteLine(res);
}
}
With the above code, I am expecting '12' as output, but instead I am getting '-1504178328' as output.
I am a C# developer with no experience in C++ at all. Please help me to solve this problem.
Use a simpler P/Invoke wrapper:
public static class LibWrap
{
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref Nums nums);
[StructLayout(LayoutKind.Sequential)]
public struct Nums
{
public int a;
public int b;
}
}
and use it like this:
void CSharpExample()
{
LibWrap.Nums nums;
nums.a = 6;
nums.b = 7;
int res = LibWrap.Initialize(ref nums);
Console.WriteLine(res);
}
In your example, you don't need any memory allocation and marshaling, because:
LibWrap.Nums is a struct, thus local variable nums in CSharpExample() is allocated completely on stack.
passing managed struct LibWrap.Nums by ref to LibWrap.Initialize will pass the pointer to local variable nums on stack.
LibWrap.Initialize is called synchronously, so that the pointer you pass to it isn't used anywhere after LibWrap.Initialize function exits. This is important because the pointer becomes invalid as soon as CSharpExample() exits.
On the C# side, you are not handling the nested struct correctly. Try this instead:
[StructLayout(LayoutKind.Sequential)]
public struct mynums {
public int num1;
public int num2;
}
[StructLayout(LayoutKind.Sequential)]
public struct sum1 {
public mynums nums;
}
public class LibWrap {
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref sum1 summing);
}
class Program {
static void Main(string[] args) {
sum1 mysum;
mysum.nums.num1 = 6;
mysum.nums.num2 = 6;
int res = LibWrap.Initialize(ref mysum);
Console.WriteLine(res);
}
}
That being said, having a struct whose sole data member is another struct is redundant and unnecessary. You should remove the outer struct altogether, eg:
struct nums {
int num1;
int num2;
};
extern "C" {
__declspec(dllexport) int initialize(nums *summing) {
return summing->num1 + summing->num2;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct nums {
public int num1;
public int num2;
}
public class LibWrap {
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref nums summing);
}
class Program {
static void Main(string[] args) {
nums mynums;
mynums.num1 = 6;
mynums.num2 = 6;
int res = LibWrap.Initialize(ref mynums);
Console.WriteLine(res);
}
}
I have a huge C++ structure with a lot of validation code in C++, that I want to import in a C# project. I'm able to transfer all value except the CHAR* and CHAR[].
With a CHAR*, my string is full of chiness caracter BUT, if i go look in memory, my string is there, i can see "This is a test #1".
With a CHAR[x], I can see only the 1rst char, same in memory.
In the following test, i can extract the integer : value1 = data.Value1;
and value 1 is 123, but the the CHAR.
Question: What i miss, why i can't get the value with a char array.
Thank you
The C++ DLL
//This is the main DLL file.
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
extern "C"
{
public struct Data
{
int Value1;
char* pValue2;
char Value3[1024];
};
typedef int( *FN_CCP_INVOKE_NEW_BOARD_OPTIMIZER) (struct Data* data);
FN_CCP_INVOKE_NEW_BOARD_OPTIMIZER _pInvokeCallback;
int __declspec(dllexport) DLLTestCPlusPlus_Initialize()
{
return 0;
}
int __declspec(dllexport) DLLTestCPlusPlus_RegisterDllInvokeProcessCallback(void* fnInvokeCaller)
{
_pInvokeCallback = (FN_CCP_INVOKE_NEW_BOARD_OPTIMIZER)fnInvokeCaller;
struct Data data;
// INT
data.Value1 = 123;
// CHAR*
data.pValue2 = new char[1024];
sprintf(data.pValue2, "This is a test #1");
// CHAR [1024]
sprintf(data.Value3, "This is a test #2");
if (_pInvokeCallback)
{
_pInvokeCallback(&data);
}
return 0;
}
}
And here's the C# program that import the DLL.
using System;
using System.Runtime.InteropServices;
public unsafe struct Data
{
public int Value1;
public char* pValue2;
public fixed char Value3[1024];
}
public static class Interop
{
public delegate Int32 Callback([MarshalAs(UnmanagedType.Struct)] Data data);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetDllDirectory(string lpPathName);
[DllImport("C:\\DATA\\CODE\\ApplicationTestCSharp\\x64\\Debug\\DLLTestCPlusPlus.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 DLLTestCPlusPlus_Initialize();
[DllImport("C:\\DATA\\CODE\\ApplicationTestCSharp\\x64\\Debug\\DLLTestCPlusPlus.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 DLLTestCPlusPlus_RegisterDllInvokeProcessCallback([MarshalAs(UnmanagedType.FunctionPtr)] Callback handler);
}
public class MyTest
{
private Interop.Callback _callback = null;
public MyTest()
{
int returnCode = 0;
returnCode = Interop.DLLTestCPlusPlus_Initialize();
_callback = new Interop.Callback(CallbackHandler);
returnCode = Interop.DLLTestCPlusPlus_RegisterDllInvokeProcessCallback(_callback);
}
private Int32 CallbackHandler(Data data)
{
int value1 = 0;
string value2 = "";
string value3 = "";
unsafe
{
// INT
value1 = data.Value1;
// CHAR* - MUST BE "This is a test #1"
value2 = new string(data.pValue2);
// CHAR [1024] - "This is a test #2"
value3 = new string(data.Value3);
}
return 1;
}
}
class Program
{
static void Main(string[] args)
{
MyTest myTest = new MyTest();
}
}
Ok i found it, i made that change in the structure declaration
/*
public char* pValue2;
public fixed char Value3[1024];
*/
[MarshalAs(UnmanagedType.LPStr)] public String pValue2;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)] public String pValue3;
And pull the data like that!
// CHAR* - "This is a test #1";
// value2 = new string(data.pValue2);
value2 = data.pValue2
// CHAR [1024] - "This is a test #2"
//value3 = new string(data.Value3);
value3 = data.pValue3;
I'm making messaging system with C library.
To send messages from C library (DLL), I made this DLL file and tried to callback from C to C#
here is C DLL Code
logging.h
#pragma once
#include <stdio.h>
struct loggingMessage {
void (*fp)(char *, int, int);
};
struct loggingMessage messageConfig;
__declspec(dllexport) void loggingInitialize();
__declspec(dllexport) void print();
__declspec(dllexport) void setEventCallBack(void(*fp)(char *, int, int));
logging.c
void loggingInitialize()
{
messageConfig.fp = NULL;
}
void print(char *str, int length)
{
char buf[1024];
memset(buf, 0, sizeof(char) * 1024);
sprintf(buf, "%s", str);
if (messageConfig.fp != NULL)
{
messageConfig.fp(buf, length, 0);
}
}
void setEventCallBack(void (*fp)(char *buf, int length, int debugLevel))
{
messageConfig.fp = fp;
char str[512] = "stringTmp";
fp(str, 1, 0);
fp(str, 1, 1);
fp(str, 1, 2);
fp(str, 1, 3);
}
Program.cs in C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
delegate void EventCallback(string _input, int length, int level);
namespace console
{
class Program
{
[DllImport("logging.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void print();
[DllImport("logging.dll")]
public static extern void setEventCallBack(EventCallback fp);
[DllImport("logging.dll")]
public static extern void loggingInitialize();
public static void update(string _input, int length, int level)
{
Console.WriteLine("Message : {0} Length {1} Level {2}", _input, length , level);
}
static void Main(string[] args)
{
loggingInitialize();
setEventCallBack(update);
}
}
}
And in console I could see the message, but there was a error about function pointer.
I didn't understand what error was and I wonder how to debug and how to set parameters, pointers between C# and C.
You have declared your DllImport statements as CallingConvention = CallingConvention.StdCall however in your C code you declared them as __declspec, you need to use CallingConvention = CallingConvention.Cdecl instead on all of the imports (the default if you don't specify is CallingConvention.Winapi which maps to StdCall on most platforms).
I'm trying to pass a string from C# to C++, using platform invoke.
C++ code:
#include<string>
using namespace std;
extern "C"
{
double __declspec(dllexport) Add(double a, double b)
{
return a + b;
}
string __declspec(dllexport) ToUpper(string s)
{
string tmp = s;
for(string::iterator it = tmp.begin();it != tmp.end();it++)
(*it)-=32;
return tmp;
}
}
C# code:
[DllImport("TestDll.dll", CharSet = CharSet.Ansi, CallingConvention =CallingConvention.Cdecl)]
public static extern string ToUpper(string s);
static void Main(string[] args)
{
string s = "hello";
Console.WriteLine(Add(a,b));
Console.WriteLine(ToUpper(s));
}
I receive a SEHException. Is it impossible to use std::string like this? Should I use char* instead ?
Сorrect decision
C# side:
[DllImport("CppDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetString(string s);
public string GetString_(string s)
{
var ptr = GetString(s);
var answerStr = Marshal.PtrToStringAnsi(ptr);
return answerStr;
}
C++ side:
extern "C" __declspec(dllexport) const char* GetString(char* s)
{
string workStr(s);
int lenStr = workStr.length() + 1;
char* answer = new char[lenStr];
const char * constAnswer = new char[lenStr];
strcpy(answer, workStr.c_str());
constAnswer = answer;
return constAnswer;
}
And disable /sdl- in the settings of the cpp project.
One way of doing it without causing a memory leak is using a callback.
C# side:
private delegate bool DLLCallback(IntPtr message);
[DllImport(#"YourLibrary.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
private static extern void Receive(DLLCallback callback);
private static bool callback(IntPtr ptr)
{
string result = Marshal.PtrToStringAnsi(ptr);
Console.WriteLine(result);
// If the Heap is used
// Marshal.FreeHGlobal(ptr);
return true;
}
private static void Main(string[] args) {
Receive(callback);
}
C++ side:
extern "C" {
typedef BOOL(__stdcall* OutputCallback)(const char* str);
__declspec(dllexport) void Receive(OutputCallback callback)
{
char buffer[BUFFER_SIZE];
ZeroMemory(buffer, BUFFER_SIZE);
BOOL callbackResult = callback(buffer);
}
}
There are other options. This is a good article about passing strings between managed and unmanaged code: article
I suggest to use char*. Here a possible solution.
If you create another C# function ToUpper_2 as follows
C# side:
[DllImport("TestDll.dll"), CallingConvention = CallingConvention.Cdecl]
private static extern IntPtr ToUpper(string s);
public static string ToUpper_2(string s)
{
return Marshal.PtrToStringAnsi(ToUpper(string s));
}
C++ side:
#include <algorithm>
#include <string>
extern "C" __declspec(dllexport) const char* ToUpper(char* s)
{
string tmp(s);
// your code for a string applied to tmp
return tmp.c_str();
}
you are done!
I am trying to do 2 things: get a return value from a C dll function, and have that same function modify 1 of the members of a structure that is passed to it. After much experimentation I was able to get the function to return a value, but I am still unable to get it to return a modified value to the C# code; the value remains (unmodified) as 0.
I've tried lots of variations (ref, [In,Out], etc) to no avail
using System;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
namespace Vexing.Problem{
public class myIdx : VexingObject {
public myIdx(object _ctx) : base(_ctx) { }
private IPlotObject plot1;
[StructLayout(LayoutKind.Sequential)]
public class PLEX { public int yowser; }
[DllImport("my.dll", CharSet = CharSet.Unicode)]
public static extern int cFunction(
[MarshalAs(UnmanagedType.LPStruct)] PLEX mPlex);
PLEX a;
protected override void Create() { a = new PLEX(); }
protected override void CalcBar() {
int mf = cFunction(a);
plot1.Set(a.yowser); }
}}
// pertinent c dll code
typedef struct s_plex { int yowser;} cplex;
extern "C" __declspec( dllexport )
int cFunction(cplex *Cplex){ Cplex->yowser = 44; return 1;}
Your import declaration is wrong.
Setting the CharSet in your case doesn't make any sense (there are no string parameters in the native function declaration).
If you want to pass class instance, ref/out also must be thrown away (classes being passed by reference).
And the main point: extern "C" __declspec( dllexport ) means CallingConvention.Cdecl.
UPDATE. Here's complete working code sample:
C++ (header):
struct CStruct
{
int myField;
};
extern "C" __declspec( dllexport ) int MyFunction(CStruct *pStruct);
C++ (code):
int MyFunction(CStruct *pStruct)
{
pStruct->myField = 100;
return 1;
}
C#:
[StructLayout(LayoutKind.Sequential)]
class MyStruct
{
public int myField;
};
class Program
{
MyStruct myStruct = new MyStruct();
[DllImport("MyLib.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int MyFunction(MyStruct pStruct);
static void Main(string[] args)
{
var p = new Program();
var result = MyFunction(p.myStruct);
Console.WriteLine("Result: {0}, MyStruct.myField = {1}", result, p.myStruct.myField);
}
}
Prints:
Result: 1, MyStruct.myField = 100