I have some trouble using a c dll in a c# application. The function which gives me an error is defined in the header file of the dll like this:
int __stdcall DDC_CreateFilePropertyString (DDCFileHandle file,
const char *property,
const char *value);
I added the following code in my class where I access the dll.
[DllImport("nilibddc.dll", CallingConvention = CallingConvention.Cdecl, CharSet=CharSet.Ansi)]
private static extern int DDC_CreateFilePropertyString(IntPtr file,
[MarshalAs(UnmanagedType.LPStr)]string property,
[MarshalAs(UnmanagedType.LPStr)]string value);
The Type DDCFileHandle is defined in the header file like this:
typedef struct _DDCFile DDCFile;
typedef DDCFile* DDCFileHandle;
There is are no additional information about the _DDCFile struct in the header file (i don't have any other files from the library im using).
Before I'm calling the function DDC_CreateFilePropertyString() I call the following function to create a file and get the file handle.
[DllImport("nilibddc.dll", CallingConvention = CallingConvention.Cdecl, CharSet=CharSet.Ansi]
private static extern int DDC_CreateFile(char[] filePath,
char[] fileType,
char[] name,
char[] description,
char[] title,
char[] author,
ref IntPtr file);
The definition in the header file looks like this.
int __stdcall DDC_CreateFile (const char *filePath,
const char *fileType,
const char *name,
const char *description,
const char *title,
const char *author,
DDCFileHandle *file);
Now always when i call the function DDC_CreateFilePropertyString it returns me an error telling me that i have some wrong parameters passed. What am i doing wrong? The library I'm using is the TDMS C API from National instruments.
Thanks for your help.
Your p/invokes are a little off. You need to use CallingConvention.Stdcall, which is the default. And for the const char* parameters you should simply declare them to be string at the C# end.
The correct C# p/invoke for DDC_CreateFile is:
[DllImport("nilibddc.dll", CharSet=CharSet.Ansi]
private static extern int DDC_CreateFile(
string filePath,
string fileType,
string name,
string description,
string title,
string author,
ref IntPtr file
);
And for DDC_CreateFilePropertyString you need this:
[DllImport("nilibddc.dll", CharSet=CharSet.Ansi)]
private static extern int DDC_CreateFilePropertyString(
IntPtr file,
string property,
string value
);
If, after fixing your code, you still receive errors when calling these functions, then you are clearly using the library incorrectly. And that's beyond the scope of this question. Consult the documentation, and/or seek support from the library vendor.
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 this define in C++ header file
extern "C" __declspec(dllexport) const char* __stdcall GetId(const My_Polyhedron *obj);
and has this in C#
[DllImport("polyhedra.dll", CallingConvention = CallingConvention.Cdecl)]
static private extern string GetId(IntPtr obj);
It returns me gobbledygook
îþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþ2§{€ûW#_‹p
in .net 4.0, but it works well in .net 3.5.
Note:
I have tried charset=...(all possible settings, e.g. Charset.Unicode) in DllImport.
I have tried to let it returns IntPtr instead of string, then use Marshal.PtrToStringAnsi convert it back to string.
I have tried StringBuilder instead of string.
All the solutions above do not work.
If you are the owner of the C++ code, you can modify the function so that it returns the string as a out parameter:
extern "C" __declspec(dllexport) void __stdcall GetId(
const My_Polyhedron *obj, char* result);
Then use this import in C#:
[DllImport("polyhedra.dll", CallingConvention = CallingConvention.Cdecl)]
static private extern void GetId(IntPtr obj, StringBuilder result);
Please note that result is a StringBuilder instead of a string.
You have to allocate enough space first by making the StringBuilder big enough.
Call it like this:
StringBuilder result = new StringBuilder(1000);
GetId(obj, result);
Use result.ToString() to get the string.
There's the return attribute (it isn't really a attribute) that specifies how marshal the returned value:
[return: MarshalAs(UnmanagedType.Bool)]
But I didn't tried it on your code.
Maybe this helps...
Using C#.net, how I declare and use this function inside a .dll file:
DEMO_API Boolean DEMO_CALL GetVersion(Char* versionBuffer,
UInt16* versionLengthBuffer);
Thanks in advance!
C# doesn't have the notion of string pointers as such, you can just use a string / int.
Basically to define your own you'd have to do somethign slightly different, but it wouldn't be compatible.
public bool GetVersion (string versionBuffer, int versionLengthBuffer)
If your using an existing item have a look at pInvoke for a list of possible signatures.
This is the solution I found:
[DllImport("DEMO.dll",
SetLastError = true,
CallingConvention = CallingConvention.Cdecl)]
public extern static byte GetVersion(
[MarshalAs(UnmanagedType.LPStr)] StringBuilder versionBuffer,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder versionLengthBuffer);
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.
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.