I have a dll which contains this function:
int __stdcall PrnText(char *printtext);
In Windows Forms i have this code to invoke the dll:
[DllImport("Printing.dll", EntryPoint = "PrnText", CharSet = CharSet.Ansi)]
public static extern int PrnText(char *printtext);
When i call the function in C# code i get an error like this : " cannot cast string to char*
PrnText("Hello World");
What parameter should i give to PrnText() to make it work?
Later edit:
Parameter: printtext
pointer to string containing text to be printed
The CLR knows how to convert a string to an unmanaged char* at runtime. You should use a signature which accepts a string, as such:
public static extern int PrnText(string printtext);
Note that this will work only if the parameter is input only.
Related
I want to call in C# a function from unmanaged library with following signature:
DLL_EXPORT int xli_open(char *, int , struct t_info *);
In legacy code on Windows 7 the function is improrted as:
[DllImport(DRIVER_FILENAME, EntryPoint = "xli_open", CallingConvention = CallingConvention.Cdecl)]
public static extern int xli_open(string device, int hndl, ref t_info tInfo);
On Windows 10 I get an AccessViolationException for calling the function and I import the function as:
[DllImport(DRIVER_FILENAME, EntryPoint = "xli_open", CallingConvention = CallingConvention.Cdecl)]
public static extern int xli_open(ref string device, int hndl, ref t_info tInfo);
I don't get AccessViolationException anymore, but it seems that the function gets an empty string. Is the declaration right? And why does the pass of the ref parameter work (would string not be passed by reference anyway?)?
Assuming that you are passing the text to the function, then plain by value string is correct. The access violation is likely because of some other error. Perhaps the structure definition does not match, perhaps the calling convention is wrong. Or perhaps some other mistake, but the string argument appears to be correct.
I have been provided with a DLL which is to be called by C#. The DLL contains two methods as follows
extern "C" {
__declspec(dllexport) BSTR GroupInit(LPCTSTR bstrIniFile, bool bDiagErr, bool bProcErr);
}
BSTR GroupInit(LPCTSTR bstrIniFile, bool bDiagErr, bool bProcErr) {
CString strResult = "";
char* sz;
::SetVars(bDiagErr, bProcErr);
if (sz = ::GroupInit((char*)bstrIniFile, 1))
strResult = sz;
return strResult.AllocSysString();
}
I am attempting to call these DLLs from C# by first defining the class:
[DllImport("GrouperServer.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GroupInit(
string strCmdFile,
bool bAllowBadDiagCodes,
bool bAllowBadProcCodes
);
and doing
this.strCommandFilePath = "C:\\MyDir\\MyCommandFile.txt";
string s = Grouper.GrouperServer.GroupInit(this.strCommandFilePath, true, true);
But the DLL is returning the error: 'Cannot find command file: "C"' (the first character of the path only, which I have checked in the C++ DLL). For some reason the string this.strCommandFilePath is not being passed into the C++ method correctly.
What is wrong with the above call?
Edit to address comments.
The method being called in the if (sz = ::GroupInit((char*)bstrIniFile, 1)) statement is defined in a .c file and has the signature
char *GroupInit(char *szCmd, int iType)
{
...
}
It is a mistake to use TCHAR and related types here. The use case of TCHAR is for code that needs to compile for both Windows 9x which has no Unicode support, and Windows NT which does. Those days are long gone and TCHAR is obscuring the problem. What's more, the underlying code uses char* so it makes little sense to pretend that your wrapper code can do anything else. So switch to char.
On top of that you are casting away const. I guess because the function you call accepts a modifiable buffer for a parameter that it does not modify. Best solution is to fix the original library code that erroneously accepts char* and make it accept const char*. If you cannot do that then you'll need to cast away the const. But do that the C++ way with const_cast<>.
So, I'd have the C++ code like this:
BSTR GroupInit(const char* szIniFile, bool bDiagErr, bool bProcErr) {
CString strResult = "";
char* sz;
::SetVars(bDiagErr, bProcErr);
if (sz = ::GroupInit(const_cast<char*>(szIniFile), 1))
strResult = sz;
return strResult.AllocSysString();
}
And the C# code should be:
[DllImport("GrouperServer.dll", CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GroupInit(
string strCmdFile,
bool bAllowBadDiagCodes,
bool bAllowBadProcCodes
);
Now, one wonders what happens to sz. Who is expected to deallocate that? Does it even need to be deallocated? Only you can answer those questions.
I have a dynamic library (.dll) written in C++ exporting a function I'd like to use in my C# applicaiton:
int SendText(void* pControl, char* sText);
How can I, given it takes a pointer to void?
for void* you can just use IntPtr ,
strings will work with the MarshalAs attribute:
[DllImport("MyDll.dll", CharSet = CharSet.Ansi)]
public static extern int SendText(IntPtr pControl, [MarshalAs(UnmanagedType.LPStr)] string sText);
I have written a C++ wrapper DLL for C# to call. The DLL was tested and worked fine with my C++ test program.
now integrated with C#, I got runtime error and crashed. Cannot use debugger to see more details.
The C++ side has only one method:
#ifdef DLLWRAPPERWIN32_EXPORTS
#define DLLWRAPPERWIN32_API __declspec(dllexport)
#else
#define DLLWRAPPERWIN32_API __declspec(dllimport)
#endif
#include "NB_DPSM.h"
extern "C" {
DLLWRAPPERWIN32_API int WriteGenbenchDataWrapper(string fileNameToAnalyze,
string parameterFileName,
string baseNameToSaveData,
string logFileName,
string& message) ;
}
in the C# side, there is a definition,
[DllImport("..\\..\\thirdParty\\cogs\\DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze,
string parameterFileName,
string baseNameToSaveData,
string logFileName,
ref string message);
and a call:
string msg = "";
int returnVal = WriteGenbenchDataWrapper(rawDataFileName,
parameterFileName, outputBaseName, logFileName, ref msg);
I guess there must be something wrong with the last parameter of the function. string& in C++ should be ref string in C#?
EDIT:
Do we really need the extern "C"?
EDIT 2:
after I remove the extern "C from the dll, I got the EntryPointNotFoundException. When I look at the dll by using DLL Export Viewer, I found the function name is "int __cdecl WriteGenbenchDataWrapper(class std:: ..." Do I need to include the " __cdecl"?
There are a bunch of rules for marsheling with PInvoke.
For reference Marsheling between managaed & unmanaged
Focusing on the C# side first.
If you knew a reasonable size of the message up front you could use StringBuilder type and define that size, something like.
[DllImport("DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze,
string parameterFileName,
string baseNameToSaveData,
string logFileName,
StringBuilder message
int messageLength );
Impression from the name message (and other posts) indiciates you don't know the size up front, and you won't be passing a partial message to the function so maybe
[DllImport("DLLWrapperWin32.dll")]
public static extern int WriteGenbenchDataWrapper(in string fileNameToAnalyze,
in string parameterFileName,
in string baseNameToSaveData,
in string logFileName,
out string message );
Now on the C/C++ side - to match the second definition
extern "C" // if this is a C++ file to turn off name mangling for this function only
int WriteGenbenchDataWrapper( char * fileNameToAnalyze,
char * parameterFileName,
char * baseNameToSaveData,
char * logFileName,
char ** message ) {
string internalMessage;
SomeFunc( internalMessage ); // these functions won't have extern "C" applied
* message = (char *)::CoTaskMemAlloc(internalMessage.length()+1);
strcpy(* message, internalMessage.c_str());
}
Consideration of unicode/ansi strings is also important, refer to [MarshalAsAttribute(UnmanagedType.LPWSTR)]
For release mode you will want to remove your development path settings "..\..\thirdParty\cogs"
In your C++ code:
I've always needed the extern "C". C++ mangles function names if you don't (the mangling is needed to support function overloading). The extern "C" tells it not to do this.
I also will declare the functions as __stdcall. I believe you can tell C# which type of calling convention to use, but I think __stdcall is the default.
As far as passing a string object, I'm not sure about that, I stick to only using primitives for parameter passing, so I would use const char * and adjust accordingly in my C++ code.
Also, I try to avoid passing by reference. Rather, if I need to return several values, I'll set up a series of getters to handle this (a const char * returns as an IntPtr).
In your C# code:
I use String for the const char *, int for int, and so on. I believe Microsoft has a chart somewhere to tell you what should sub in for what.
When dealing with a returned string, you need to convert it to ANSI. This can be done with a call to Marshal.PtrToStringAnsi().
For Example:
In my C++ code:
extern "C" __declspec(dllexport) const char* __stdcall GetCompany(const char *In) {
return MyGetCompany(In); // Calls the real implementation
}
In my C# code:
[DllImport("TheDLL.dll", EntryPoint = "GetCompany")]
private static extern IntPtr privGetCompany(String In);
// Call this one, not the one above:
public String GetProvince(String In)
{
return Marshal.PtrToStringAnsi(privGetCompany(In));
}
One final note, if you're running on a 64-bit machine, the 'Any CPU' configuration will make a 64-bit C# executable, which will need a 64-bit DLL. If you only have a 32-bit DLL, you'll need to add a configuration (x86).
The error message you got indicates that your C# program is probably finding the DLL correctly and the function as well, so name mangling is not likely the problem. It sounds like calling convention issue or a problem with the parameter passing.
How to marshal the type of "Cstring" in .NET Compact Framework(C#)?
DLLname:Test_Cstring.dll(OS is WinCE 5.0),source code:
extern "C" __declspec(dllexport) int GetStringLen(CString str)
{
return str.GetLength();
}
I marshal that in .NET Compact Framework(C#),for example:
[DllImport("Test_Cstring.dll", EntryPoint = "GetStringLen", SetLastError = true)]
public extern static int GetStringLen(string s);
private void Test_Cstring()
{
int len=-1;
len=GetStringLen("abcd");
MessageBox.Show("Length:"+len.ToString()); //result is -1,so PInvoke is unsuccessful!
}
The Method of "GetStringLen" in .NET CF is unsuccessful!
How to marshal this type of "Cstring"?
Any information about it would be very appreciated!
You can't marshal CString as it's not a native type - it's a C++ class that wraps up a char array.
You can marshal string to char[] as char[] is a native type. You need to have the parameters to the function you want to P/Invoke into as basic types like int, bool, char or struct, but not classes. Read more here:
http://msdn.microsoft.com/en-us/library/aa446536.aspx
In order to call functions that take CString as an argument you can do something like this:
//Compile with /UNICODE
extern "C" MFCINTEROP_API int GetStringLen(const TCHAR* str) {
CString s(str);
return s.GetLength();
//Or call some other function taking CString as an argument
//return CallOtherFunction(s);
}
[DllImport("YourDLL.dll", CharSet=CharSet.Unicode)]
public extern static int GetStringLen(string param);
In the above P/Invoke function we pass in a System.String which can marshal to char*/wchar_t*. The unmanaged function then creates a instance of CString and works with that.
By default System.String is marshalled to char*, so be careful with what kind of string the unmanaged version takes. This version uses TCHAR, which becomes wchar_t when compiled with /UNICODE. That's why you need to specify CharSet=CharSet.Unicode in the DllImport attribute.
you should do the following:
extern "C" __declspec(dllexport) int GetStringLen(LPCTSTR str)
{
CString s(str);
return s.GetLength();
}
The CString is actually an MFC type not a native type. Just grab the string and turn it into a CString in native method.