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.
Related
I'm trying, for the first time, to use an unmanaged C++ DLL ("res_lib") in a C# application. I've used cppsharp to generate the PInvoke code: for example, one of the functions/methods I'm trying to call is get_system_snapshot. From the .h file, this is defined as
SYS_INT SYS_ERR get_system_snapshot(SNAPSHOT_PARMS* snapshotp);
SYS_INT and SYS_ERR equate to a int32_t. SNAPSHOT_PARMS is
typedef struct SNAPSHOT_PARMS
{
SYS_ULONG size;
SYS_UINT count;
SYS_CHAR serial_no[600];
} SYS_PACK_DIRECTIVE SYS_SNAPSHOT_PARMS;
cppsharp has turned this into the following code snippets:
DllImport
[SuppressUnmanagedCodeSecurity]
[DllImport("res_lib", CallingConvention = CallingConvention.StdCall,
EntryPoint="get_system_snapshot")]
internal static extern int GetSystemSnapshot(IntPtr snapshotp);
Object
public unsafe partial class SNAPSHOT_PARMS : IDisposable
{
[StructLayout(LayoutKind.Explicit, Size = 608)]
public partial struct __Internal
{
[FieldOffset(0)]
internal uint size;
[FieldOffset(4)]
internal uint count;
[FieldOffset(8)]
internal fixed sbyte serial_no[600];
[SuppressUnmanagedCodeSecurity]
[DllImport("res_lib", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.ThisCall,
EntryPoint="??0SNAPSHOT_PARMS##QAE#ABU0##Z")]
internal static extern global::System.IntPtr cctor(global::System.IntPtr instance, global::System.IntPtr _0);
}
}
public SNAPSHOT_PARMS()
{
__Instance = Marshal.AllocHGlobal(sizeof(global::res_lib.SNAPSHOT_PARMS.__Internal));
__ownsNativeInstance = true;
NativeToManagedMap[__Instance] = this;
}
Main code
static void Main(string[] args)
{
SNAPSHOT_PARMS p = new SNAPSHOT_PARMS();
var result = res_lib.res_lib.GetSystemSnapshot(p);
}
public static unsafe int GetSystemSnapshot(global::res_lib.SNAPSHOT_PARMS snapshotp)
{
var __arg0 = ReferenceEquals(snapshotp, null) ? global::System.IntPtr.Zero : snapshotp.__Instance;
var __ret = __Internal.GetSystemSnapshot(out __arg0);
return __ret;
}
When calling the function, I get the infamous:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I've tried changing the CallingConvention from StdCall to Cdecl, introducing [In] and [Out] to the DllImport etc, but all to no avail. Can anyone see anything obviously wrong with the code - as may be apparent, this is all new to me, and perhaps I'm asking a bit much for cppsharp to generate code that won't need tweaked.
EDIT The original C++ documentation has an example, where the struct is initialised by
#define INIT_STRUCT(struct_p) { memset(struct_p, 0, sizeof(*(struct_p))); (struct_p)->size = sizeof(*(struct_p)); }
and is used
SNAPSHOT_PARMS snapshot_parms;
SYS_ERR result;
INIT_STRUCT(&snapshot_parms);
result = get_system_snapshot(&snapshot_parms);
From the C++ declarations, this should suffice:
[StructLayout(LayoutKind.Sequential)]
unsafe struct SNAPSHOT_PARMS {
public int size;
public int count;
public fixed byte serial_no[600];
}
[DllImport("res_lib", EntryPoint = "get_system_snapshot")]
static extern int GetSystemSnapshot(ref SNAPSHOT_PARMS snapshot);
Use as
var s = new SNAPSHOT_PARMS { size = Marshal.SizeOf<SNAPSHOT_PARMS>() };
int result = GetSystemSnapshot(ref s);
// check result, use s
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);
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);
}
Imagine I have a function called
Myfunction(const void * x);
My C# declaration could be
MyFunction(IntPtr x);
Is this functionally and technically equivalent to
struct MyStruct { IntPtr P; }
MyFunction(MyStruct x);
Or will there be a difference in how they are marshalled.
I'm asking this because the library I'm calling is all void *, typedef'd to other names, and in C# I'd like to get type safety, for what it's worth.
If your StructLayout is Sequential, then it is indeed identical.
Easiest way to verify this for yourself is to try it out, of course:
Make a C++ Win32 DLL project:
extern "C"
{
__declspec(dllexport) void MyFunction(const void* ptr)
{
// put a breakpoint and inspect
}
}
Make a C# project:
public struct Foo
{
public IntPtr x;
}
[DllImport(#"Win32Project1.dll", EntryPoint = "MyFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern void MyFunctionWithIntPtr(IntPtr x);
[DllImport(#"Win32Project1.dll", EntryPoint = "MyFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern void MyFunctionWithStruct(Foo x);
static void Main(string[] args)
{
IntPtr j = new IntPtr(10);
var s = new Foo();
s.x = new IntPtr(10);
MyFunctionWithIntPtr(j);
MyFunctionWithStruct(s);
}
In your debug settings, make sure you select Native debugging is enabled.
You'll see both values to be 0xA.
Note, however, if you use out/ref parameters for your IntPtr vs Struct, they will be different values.
I am trying to use a function from a c dll, in a c# application, I am facing this error each time i try to run the application and call the function in question.
At first i thought maybe this is because of the wrong signature i am using, but i tried to make it as simple as possible yet no luck.!
To cut a long story short:
This is my actual source code for c dll :
#include <stdio.h>
#include <string.h>
extern "C"
{
struct teststruct
{
char acharacter;
int anumber;
char* astring;
char anarray[10];
const char* conststring;
};
__declspec(dllexport) teststruct TestDLL()
{
teststruct stc;
stc.acharacter = 'A';
stc.anumber = 10;
strcpy(stc.anarray, "Test");
stc.astring = "astring!";
stc.conststring = "Crash?";
return stc;
}
}
And this is the c# counter part:
[StructLayout(LayoutKind.Sequential)]
public struct teststruct
{
public char acharacter;
public int anumber;
[MarshalAs(UnmanagedType.LPStr)]
public string astring;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public char[] anarray;
[MarshalAs(UnmanagedType.LPStr)]
public string conststring;
}
namespace Tcp_connections_list
{
public partial class Form1 : Form
{
[DllImport("simple c dll.dll",CallingConvention= CallingConvention.Cdecl)]
public static extern teststruct TestDLL();
public Form1()
{
InitializeComponent();
}
private void btnTestDll_Click(object sender, EventArgs e)
{
teststruct test = TestDLL(); //Crash at the very begining!
textBox1.Text = test.acharacter.ToString();
textBox1.Text = test.anarray.ToString();
textBox1.Text = test.anumber.ToString();
textBox1.Text = test.astring.ToString();
textBox1.Text = test.conststring.ToString();
}
}
}
The following code snippet gives me the same exact error, I changed the structure to
struct teststruct
{
char acharacter;
int anumber;
};
and its C# equivalent to
[StructLayout(LayoutKind.Sequential)]
public struct teststruct
{
public char acharacter;
public int anumber;
}
to make it as simple as possible, Yet again i get the same exact error!
What am i missing here?
The problem is the marshalling of the null-terminated C strings. You cannot expect the p/invoke marshaller to deal with those in a function return value. The struct needs to be declared like this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct teststruct
{
public byte acharacter; // don't use C# char which is 2 bytes wide
public int anumber;
public IntPtr astring;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public char[] anarray;
public IntPtr conststring;
}
You'll need to use Marshal.PtrToStringAnsi to extract the contents of the C strings.
As a more general piece of advice, passing large structs as function return values is a bad idea. There is no single generally accepted ABI for doing that. Different compilers handle this in different ways. Better is to allocate the struct in the calling code, and pass its address. Like this:
__declspec(dllexport) void TestDLL(teststruct *ts)
{
ts->... = ...;
...
}
And on the C# side:
[DllImport(...)]
public static extern void TestDLL(out teststruct ts);
In fact, it would not surprise me if the struct that you are trying to work with cannot be marshalled as a return value. You really should pass it as an out parameter.