Calling C++ DLL from C++ and C# - c#

I have a C++ application that I have to convert to a DLL. I have all the source.
my function is
extern "C"
__declspec(dllexport) int mymain(int i, std::wstring myArgs)
I need to be able to pass in the arguments from a c++ or c# wrapper. I am able to call this from a c++ console application without error. I am now trying to call it from C#.
This is my c# code:
public static class DllHelper
{
[DllImport("rep.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mymain(int iArgs, string aArgs);
}
class Program
{
static void Main(string[] args)
{
string s = "my string data";
DllHelper.mymain(0, s);
}
}
}
When I run it I get
System.Runtime.InteropServices.SEHException: 'External component has thrown an exception.'
I am out of ideas.
TIA

Specify Unicode but also, in your C or C++ function, use printf with "%S" (upper-case 'S' means wide-character string).. OR std::wcout.
Without that, it might print weird or terminate at the first null char it finds. Also, you might want to actually pass the length of the string, but that's entirely up to you.
Note the signature of the C++ function uses LPCWSTR (const wchar_t*) for the myArgs parameter..
public static class DllHelper
{
[DllImport("rep.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int mymain(int iArgs, string aArgs);
}
class Program
{
static void Main(string[] args)
{
string s = "my string data";
DllHelper.mymain(0, s);
}
}
#ifdef __cplusplus
extern "C" {
#endif
int __declspec(dllexport) mymain(int i, const wchar_t* myArgs)
{
#ifdef __cplusplus
std::wcout<<std::wstring(myArgs)<<L"\n";
#else
printf(L"%S\n", myArgs);
#endif
}
#ifdef __cplusplus
}
#endif

Based on yr last comment u might need to:
[DllImport("rep.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
anyway since I dont have rep.dll it's hard to guess

The naming used in your code:
mymain(int iArgs, string aArgs);
makes me think that what you are trying to do is probably passing an array of strings (similar to wmain(int argc, wchar_t** argv)).
If this is what you want, then on the native DLL side your function prototype would look like this:
extern "C" int __declspec(dllexport) mymain(int iArgs, wchar_t** aArgs)
And on the C# side, you would write a PInvoke declaration like this:
[DllImport("rep.dll",
CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode)]
public static extern int mymain(int iArgs, [In] string[] aArgs);
that you can invoke in C# like this:
string[] test = { "C64", "Bravo", "Charlie" };
int returnCode = mymain(test.Length, test);

Related

how to display in messagebox a C function from C# with a WCHAR*

I'm building a C# application that loads a C++ library.
I call functions from that C++ DLL. I use below function to display input string.
c++ dll:
wchar_t* Test_EchoString( wchar_t *InputStr )
{
String HWStr = String( InputStr );
return HWStr.c_str();
}
c# code:
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int _Test_EchoString([MarshalAs(UnmanagedType.LPWStr)] string s);
private void echo_string_Click(object sender, RoutedEventArgs e)
{
string upn = "aaaaa";
_Test_EchoString(upn);
MessageBox.Show(_Test_EchoString(upn).ToString());
}
I get in messagebox number 18666252 but i want to get a string from _Test_EchoString().
You have a pair of problems in your code:
in your C# you defined _Test_EchoString as public static extern int _Test_EchoString, so when you execute it, the returned value will be the address of the first character of the string HWStr.c_str().
And here it shows another problem, as anderas said, you are returning an invalid pointer, because HWStr.c_str() returns the pointer to the current value of the std::wstring object, so it is valid as long that the wstring is valid, so when the method Test_EchoString ends its execution it is no more valid(because HWStr is destroyed).
There are different ways to fix this problems, I'm going to show you two of these:
1) The first is to allocate the memory you want to return in the Heap and free it later with another call:
static wchar_t *Test_EchoStringResult;
extern "C" __declspec(dllexport) const wchar_t * Test_EchoStringNew(const wchar_t *InputStr)
{
std::wstring HWStr(InputStr);
HWStr += L" something";
Test_EchoStringResult = new wchar_t[HWStr.length() + sizeof(wchar_t)];
HWStr.copy(Test_EchoStringResult, HWStr.length());
Test_EchoStringResult[HWStr.length()] = L'\0';
return Test_EchoStringResult;
}
extern "C" __declspec(dllexport) void Test_EchoStringDelete()
{
delete[] Test_EchoStringResult;
}
And this is the usage in C#:
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern IntPtr Test_EchoStringNew(string foo);
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void Test_EchoStringDelete();
public void foo()
{
string result = Marshal.PtrToStringAuto(Test_EchoStringNew("test"));
MessageBox.Show(result.ToString());
Test_EchoStringDelete();
}
To me, this looks pretty ugly, so I'd prefer to use another pattern
2) Passing a callback to the C method and pass to this method HWStr.c_str() when HWStr is still valid:
extern "C" __declspec(dllexport) void Test_EchoString(const wchar_t *InputStr, void (*callback)(const wchar_t*))
{
std::wstring HWStr(InputStr);
HWStr += L" something";
callback(HWStr.c_str());
}
And here is the C# usage:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public delegate void myCallback(string toShow);
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void Test_EchoString(string foo, myCallback callback);
public void foo()
{
Test_EchoString("test", callback);
}
void callback(string toShow)
{
MessageBox.Show(toShow);
}

Data values in the parameters are beeing copied to the other parameters c++, c#

In c++ the method that I am exporting is:
__declspec(dllexport) int __thiscall A::check(char *x,char *y,char *z)
{
temp=new B(x,y,z);
}
In c# I am importing this method like this:
[DllImport("IDLL.dll", CallingConvention=CallingConvention.ThisCall, ExactSpelling = true, EntryPoint = "check")]
public static extern int check(string x, string y, string z);
I am calling this method in c# like this and passing the values:
public int temp()
{
string x="sdf";
string y="dfggh";
string z="vbnfg";
int t;
t=Class1.check(x,y,z);
return t;
}
The problem is that when I debug in to the native code I see that the parameters x,y,z having values sdf,dfggh,vbnfg and being altered when they reach c++ dll like this even before it is entering the native c++ dll method.
x=dfggh,y=vbnfg,z=null value
and is giving me the error saying that null pointer value is passed to the function. Can any one help me out fixing this weird problem.
Looks like your native method is an instance(vs static) method. I guess your first parameter gets mapped to 'this' somehow.
Here is an example:
#include <fstream>
using namespace std;
class A
{
public:
__declspec(dllexport) static int __stdcall check(char *x,char *y,char *z)
{
ofstream f;
f.open("c:\\temp\\test.txt");
f<<x<<endl;
f<<y<<endl;
f<<z<<endl;
return 0;
}
__declspec(dllexport) int __thiscall checkInst(char *x,char *y,char *z)
{
ofstream f;
f.open("c:\\temp\\testInst.txt");
f<<x<<endl;
f<<y<<endl;
f<<z<<endl;
return 0;
}
};
see the static keyword on the first one?
Imports(i used the mangled names because I'm lazy):
[DllImport("TestDLL.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true, EntryPoint = "?check#A##SGHPAD00#Z")]
public static extern int check(string x, string y, string z);
[DllImport("TestDLL.dll", CallingConvention = CallingConvention.ThisCall, ExactSpelling = true, EntryPoint = "?checkInst#A##QAEHPAD00#Z")]
public static extern int checkInst(IntPtr theObject, string x, string y, string z);
That makes it work just like that:
check("x", "yy", "zzz");
the instance method requires an IntPtr
IntPtr obj = IntPtr.Zero;
checkInst(obj, "1", "12", "123");
and the contents of my test.txt are:
x
yy
zzz
and testInst.txt
1
12
123

Encoding from C++ to C#

I am sending a series of char arrays from C++ to my C# program.
The c++ functions look something like this:
ReturnChar.cpp
extern "C" RETURNCHAR_API char* testString()
{
return test;
}
ReturnChar.h
extern "C" RETURNCHAR_API char* __cdecl testString();
ReturnChar Import C#
public static class ImportTest
{
[DllImport("ReturnChar.dll", EntryPoint = "testString", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern char* testString();
}
public partial class MainWindow : Window
{
public unsafe MainWindow()
{
InitializeComponent();
try
{
StringBuilder sb = new StringBuilder(new string(ImportTest.testString()));
textBox1.Text = sb.ToString();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
I get the result actually, but looks like there is a problem of encoding between C++ and C#. How can I resolve this?
I don't intend to use wchar_t in my C++ program, because I have the operations being done on char arrays. Can I somehow encode this in C#?
You should be using wchar_t throughout in your C++ program, and perform your operations on wchar_t arrays instead.
That said, you need to add CharSet = CharSet.Ansi to the DllImport parameters.

Pass string from C# to C DLL

I am trying to pass a string from C# to a C DLL. From what I have read .NET should do the conversion from string to char* for me, however I get "error CS1503: Argument '1': cannot convert from 'string' to 'char*'" Can someone advise me of where I have went wrong? Thanks.
C# code
[DllImport("Source.dll", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static unsafe extern bool StreamReceiveInitialise(char* filepath);
const string test = "test";
// This method that will be called when the thread is started
public void Stream()
{
if (StreamReceiveInitialise(test))
{
}
}
C DLL
extern "C"
{
__declspec(dllexport) bool __cdecl StreamReceiveInitialise(char* filepath);
}
Declare your external method as:
public static extern bool StreamReceiveInitialise(string filepath);
Use a StringBuilder in place of char*. See this
[DllImport("Source.dll")]
public static extern bool StreamReceiveInitialise(StringBuilder filepath);
Do it like this:
[DllImport("Source.dll", CallingConvention = CallingConvention.Cdecl, CharSet=CharSet.ANSI)]
static extern bool StreamReceiveInitialise([MarshalAs(UnmanagedType.LPStr)] string filepath);
(Marshalling as UnmanagedType.LPStr is the default, but I like being explicit).

C# Error Finding Method in DLLImport

I have a C++ assembly that I am importing using DLLImport.
I am attempting to call its method:
namespace Testing
{
class Test{
int Run(char* filePath, bool bEntry, double duration){//code}
};
}
by
[DllImport(dllName, CharSet = CharSet.Auto)]
public static extern int Run(string filePath, bool bEntry, double duration)
);
When I call its method, I get the error message:
Unable to find an entry point named Run in dll
The "Run" looks to be a non-static class method. Although, it's possible to call such methods from C# this is not the primary use-case. It would be way easier to consume it from .NET if you expose it via COM, or at-least as a plain C interface:
extern "C" __declspec(dllexport) void* Testing_Test_Create();
extern "C" __declspec(dllexport) void Testing_Test_Destroy(void* self);
extern "C" __declspec(dllexport) int Testing_Test_Run(void* self, char* filePath, bool bEntry, double duration);
And here is a sample how to call C++ class methods from C#:
// Test.cpp in NativeLib.dll
namespace Testing
{
class __declspec(dllexport) Test
{
public:
explicit Test(int data)
: data(data)
{
}
int Run(char const * path)
{
return this->data + strlen(path);
}
private:
int data;
};
}
// Program.cs in CSharpClient.exe
class Program
{
[DllImport(
"NativeLib.dll",
EntryPoint = "??0Test#Testing##QAE#H#Z",
CallingConvention = CallingConvention.ThisCall,
CharSet = CharSet.Ansi)]
public static extern void TestingTestCtor(IntPtr self, int data);
[DllImport(
"NativeLib.dll",
EntryPoint = "?Run#Test#Testing##QAEHPBD#Z",
CallingConvention = CallingConvention.ThisCall,
CharSet = CharSet.Ansi)]
public static extern int TestingTestRun(IntPtr self, string path);
static void Main(string[] args)
{
var test = Marshal.AllocCoTaskMem(4);
TestingTestCtor(test, 10);
var result = TestingTestRun(test, "path");
Console.WriteLine(result);
Marshal.FreeCoTaskMem(test);
}
}
Entry point names might be different for your build configuration/compiler, so use dumpbin utility to obtain them. Again, this is just a proof of concept, in real code it would be better to use COM.
See here: http://dotnetperls.com/dllimport
I'm not sure this will help if the function is a member of a class, but to locate the entry point by name, not ordinal, you'll need a .def file in your dll..
LIBRARY mylib
Run #1

Categories