//------------------------------------- C# Code ------------------------------------
[DllImport("MarshallStringsWin32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void Test([MarshalAs(UnmanagedType.AnsiBStr)] out String str);
[DllImport("MarshallStringsWin32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void FreeString([MarshalAs(UnmanagedType.AnsiBStr)] String str);
static void Main(string[] args)
{
String str;
Test(out str);
FreeString(str);
}
//------------------------------------- C++ Code ------------------------------------
void Test(__out BSTR* str)
{
const std::string stdStr = "The quick brown fox jumps over the lazy dog";
_bstr_t bstrStr = stdStr.c_str();
*str = bstrStr.copy();
}
void FreeString(BSTR str)
{
SysFreeString(str);
}
I am getting a System.EntryPointNotFoundException when calling Test() . Would anybody know what I am doing wrong? Is this the correct way to marshall strings?
Maybe you need to add the code for c++ code in the header file:
extern "C" void __declspec(dllexport) FreeString(BSTR str);
extern "C" void __declspec(dllexport) Test(BSTR* str);
This is almost certainly because C# cant map your name of the method Test to the Test method in the native code. Try specifying the EntryPoint="Test" attribute for the method as follows:
[DllImport("MarshallStringsWin32.dll", EntryPoint="Test", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void Test([MarshalAs(UnmanagedType.AnsiBStr)] out String str);
Related
I am getting error as "'Cannot marshal 'parameter #1': Non-blittable generic types cannot be marshaled.'"
in C code I have
#include <stdio.h>
#include "pch.h"
typedef void(*RECV_CALLBACK)();
RECV_CALLBACK pfRecvCallBack;
typedef void(*RECV_CALLBACKINT_PARA)(int);
RECV_CALLBACKINT_PARA pfRecvCallBackIntPara;
extern "C"
{
__declspec(dllexport) void MethodWith_INT_Para(RECV_CALLBACKINT_PARA pfIntPara)
{
if (pfIntPara)
{
pfRecvCallBackIntPara = pfIntPara;
}
}
__declspec(dllexport) void MethodWithNo_Int_Para(RECV_CALLBACK pfRecv)
{
if (pfRecv)
{
pfRecvCallBack = pfRecv;
}
}
__declspec(dllexport) void Calling_INT_PARA(int a, int b)
{
pfRecvCallBackIntPara(a + b);
}
__declspec(dllexport) void Calling_without_Para(int a, int b)
{
pfRecvCallBack();
}
}
When i try to call this in C# call back without int parameter is working fine, but the callback with int parameter(line ->MethodWith_INT_Para(testWithIntPara);) is giving error as mention above.
C# code
using System.Runtime.InteropServices;
[DllImport("Sample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "MethodWithNo_Int_Para")]
static extern void MethodWithNo_Int_Para(Action ptr);
[DllImport("Sample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "MethodWith_INT_Para")]
static extern void MethodWith_INT_Para(Action<Int32> ptr);
[DllImport("Sample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "Calling_INT_PARA")]
static extern void Calling_INT_PARA(int x, int y);
[DllImport("Sample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "Calling_without_Para")]
static extern void Calling_without_Para();
Console.WriteLine("After calling Declare");
MethodWithNo_Int_Para(testWithoutIntPara);
Calling_without_Para();
void testWithoutIntPara()
{
Console.WriteLine("Without int para : called by Calling_without_Para");
}
MethodWith_INT_Para(testWithIntPara);
Calling_INT_PARA(1, 3);
void testWithIntPara(int x)
{
Console.WriteLine("With Int Para {x} : called by Calling_INT_PARA");
}
Console.ReadLine();
I am using Action to define callback function, please let me know if there is any other way to do this ?
Action<int> is generic, which cannot be marshalled. You need to create a custom delegate. Another problem is that C is expecting a function with the CDecl calling convention, bu the default is StdCall.
You need to use the following types, instead of Action and Action<int>
[UnmanagedFunctionPointer(CallingConvention.CDecl)]
public delegate void ActionCDecl();
[UnmanagedFunctionPointer(CallingConvention.CDecl)]
public delegate void ActionIntCDecl(int p1);
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 have C++ DLL.
When call method from this DLL in C# AccessViolation was been throwing.
What in my code is wrong? Can someone help me?
C++ Header part:
typedef PVOID X_HANDLE;
XREADER_API BOOL ReaderOpen(X_HANDLE *pxHandle);
XREADER_API BOOL ReaderReceiveW26(X_HANDLE xHandle, LPVOID pBuffer, DWORD nBufferSize);
Working Example part C++:
X_HANDLE hReader;
unsigned char xKeyBuffer[3];
ReaderOpen(&hReader);
ReaderReceiveW26(hReader,xKeyBuffer,sizeof(xKeyBuffer));
My C# Code:
[DllImport("reader.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool ReaderOpen(IntPtr reference);
[DllImport("reader.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool ReaderReceiveW26(IntPtr hReader, IntPtr pBuffer, uint xKeyBuffer);
static void Main(string[] args)
{
byte[] received = new byte[3];
IntPtr unmanagedPointer = Marshal.AllocHGlobal(received.Length);
Marshal.Copy(received, 0, unmanagedPointer, received.Length);
IntPtr hReader = Marshal.AllocHGlobal(sizeof(uint));
var qqq = uint.Parse((Marshal.SizeOf(typeof(byte)) * received.Length).ToString());
ReaderOpen(hReader);
while (true)
{
if (ReaderReceiveW26(hReader, unmanagedPointer, qqq))
{
Console.WriteLine("!");
}
}
}
AccessViolation throwing at ReaderReceiveW26(hReader, unmanagedPointer, qqq)
Thanks for your patience!
Thanks to Hast Passant comment!
If we want use IntPtr or smth which edited by unmanaged code we need use
out paramName
For PVOID we can pass real c# type.
Working example:
[DllImport("reader.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool ReaderOpen(out IntPtr reference);
[DllImport("reader.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool ReaderReceiveW26(IntPtr hReader, byte[] pBuffer, uint xKeyBuffer);
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);
}
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).