Pass string from C# to C DLL - c#

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

Related

Calling C++ DLL from C++ and 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);

Passing string from Fortran dll to C#

I am trying to load a Fortran dll dynamically and pass a string back from fortran to C#. Everything looks fine when inside the fortran code, but when returning to C# the value of the string is lost. Instead the initial value, set in C#, is back. I have tried to use the 'ref' keyword to get the string to be passed by reference, but then I get error like below. What am I doing wrong?
The runtime has encountered a fatal error. The address of the error was at 0x709ce248, on thread 0x2ac4. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
Fortran code:
module FortLibInterface
implicit none
integer, parameter :: STR_LENGTH = 256
contains
subroutine GetString(Str)
!DIR$ ATTRIBUTES DLLEXPORT::GetString
!DIR$ ATTRIBUTES ALIAS: 'GetString' :: GetString
!DIR$ ATTRIBUTES REFERENCE:: Str
character(len=STR_LENGTH), intent(inout) :: Str
Str = 'bcdef...'
end subroutine
end module
C# code:
using System;
using System.Runtime.InteropServices;
namespace FortranCSTest
{
class Program
{
static void Main(string[] args)
{
string dllPath = "C:\\Temp\\FortLib.dll";
FortLibTest lib = new FortLibTest(dllPath);
lib.MakeTestCall();
}
}
public class FortLibTest
{
public const int STR_LENGTH = 256;
public const string FortranFuncName = "GetString";
private string pathToDll = null;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibrary(String DllName);
[DllImport("kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
private static extern bool FreeLibrary(IntPtr hModule);
public FortLibTest(string FullPathToDll)
{
pathToDll = FullPathToDll;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void TypeGetStrInfo(char[] str);
void GetStrInfo(char[] str)
{
IntPtr pDll = LoadLibrary(pathToDll);
if (pDll != IntPtr.Zero)
{
IntPtr pFunc = GetProcAddress(pDll, FortranFuncName);
if (pFunc != IntPtr.Zero)
{
TypeGetStrInfo func = (TypeGetStrInfo)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(TypeGetStrInfo));
func(str);
}
else
{
//Something
}
FreeLibrary(pDll);
}
else
{
//Something
}
}
public void MakeTestCall()
{
char[] str = new char[STR_LENGTH];
str[0] = 'a';
GetStrInfo(str);
}
}
}
For future reference. I added [In, Out] and everything works.
[DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void GetString([In, Out] char[] str);

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

System.EntryPointNotFoundException when calling C++ code from C#

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

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