I'm trying to use a native C++ dll in C# and am getting the "External component has thrown an exception" with an error code of -2147467259.
C#:
[DllImport(#"MyDLL.dll", CharSet = CharSet.Auto)]
public static extern string MyFunction([MarshalAs(UnmanagedType.LPStr)] StringBuilder input);
C++:
__declspec(dllexport) char * __stdcall MyFunction(const char* inputPtr);
The function works just fine in C++. How can I track this error down?
I have tried using string and string builder for the parameter.
Update
I found this article http://tom-shelton.net/index.php/2008/12/11/creating-a-managed-wrapper-for-a-lib-file/
It details a way to use managed C++ to wrap an unmanaged static library in C++ which can then be used in a managed language. Would this be a good way of going about tackling this problem? Can the lib be an unmanaged dll?
try the technique from http://blog.rednael.com/2008/08/29/MarshallingUsingNativeDLLsInNET.aspx - this has saved the day several times :-)
If the parameter is in-only (and not out), stringbuilder is not required. If it's an out parameter (or ref) you should use stringbuilder and pre-allocate the buffer using stringbuilder constructor.
I can guess the problem is that you are returning an Ansi string instead of the expected unicode string. This cause the default pinvoke marshaler to read too much memory.
Try this:
[DllImport(#"MyDLL.dll", CharSet = CharSet.Auto)]
[return : MarshalAs(UnmanagedType.LPStr)]
public static extern string MyFunction([MarshalAs(UnmanagedType.LPStr)] string input);
In any case, in the vast majority of times, it does not make any sense to write Ansi C++ code. I would suggest to convert the C++ code to unicode only (not tchar but wchar_t).
CharSet = CharSet.Auto?? use CharSet.Ansi
You cannot use a char array as return, a block of memory allocated with c++ cannot be used by C#! In your case you probably need to copy it into C# managed memory.
Use StringBuilder for that or MarshalAs attribute that will copy for you the buffer into C# managed memory.
You have to change your C++ function, your C++ function must write into the destination buffer that must be allocate so it contains at least the number of characters you need plus one (for null termination character).
[DllImport(#"MyDLL.dll", CharSet = CharSet.Ansi)]
private static extern void MyFunction([MarshalAs(UnmanagedType.LPStr)] string input, StringBuilder result);
public static string MyFunctionPublic(string input)
{
StringBuilder sb = new StringBuilder(input.Length + 1);
MyFunction(input, sb);
return sb.ToString();
}
And i expect your C function to do something like this:
void __stdcall MyFunction(const char* input, char* result)
{
strcpy(result, input); // this is a dummy stupid code to show how it works.
}
Probably you will need a function that gives you idea of how much bytes you have to allocate in C#. This function can be something like this:
int __stdcall ComputeMyFunctionBytes(const char* input)
{
return strlen(input); // this is a dummy stupid code to show how it works.
}
Never return a piece of allocated memory to C#, it cannot do anything with that, nor deallocating, nor reading and nor writing, until you use unsafe code, but this is not what you are trying to do.
A good article seems to be here: http://www.devx.com/dotnet/Article/6990/1954
Also note that C# uses unicode, 16 bit per character, so it will be converted from ansi to unicode.
Related
My c++ function is given below
# define MyFunction _declspec(dllexport)
extern "C" {
MyFunction int SubtractNumbers(int a, int b)
{
return a - b;
}
MyFunction const char* GetStringKey()
{
return "My String";
}
}
Calling c++ function from windows form is given below,
[DllImport(cppFunctionsDll, CallingConvention = CallingConvention.Cdecl)]
private const string DllFilePath = #"D:\\Projects\\December\\17-12-2020\\project-device-
setup\\Debug\\AccurynCPP.dll";
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern int SubtractNumbers(int a, int b);
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern string GetStringKey();
public void GetSubtract()
{
lblSubtract.Text= Convert.ToString(SubtractNumbers(1, 2));
}
public void GetStringFunction()
{
lblstringKey.Text= GetStringKey();
}
From the above function, GetSubtract() worked perfectly. But GetStringKey() not worked. when it reach on it's function while debugging, it automatically cancelled running mode in visual studio. How can i fix this issue.
The calling convention describes to the compiler and linker how parameters, the stack and registers are to be handled. So if these don't match things won't work or memory can be corrupted. For instance, if the caller thinks the first parameter should be passed in the register eax, but the callee thinks it's on the stack. Things will obviously not work. For more info on calling conventions Wikipedia has a good description here. The calling convention you choose isn't very important, unless the compiler forces one on you. For instance for 64 bit programs Microsoft only uses one to my knowledge. So, I suggest adding __cdecl in front of the functions you have written. And using CallingConvention.Cdecl on the DLLImport lines.
Also, all the data types must exactly match. For instance:
[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern string GetStringKey();
This says it is returning a string, however despite our using the term string to represent a series of characters followed by a null. In this case string is a formal type. Which is they type std::string. Which is not what is being returned by the C code. The C code is returning a pointer to a char.
Generally passing complex types based on a library is kinda iffy. That's because it requires both languages to use the exact data structure to represent the type. So when crossing language boundaries it's often useful to stay with your own custom structures and very primitive types.
So I recommend your C function be this instead:
__cdecl bool GetStringKey(char *buffer, int maximumLength)
{
// Write code here to copy all the characters from your "My String"
// to the buffer or use a standard library function for copying char * strings (often called C strings) when copying make sure to copy the null character at the end. It's definitely needed.
return true; // Yes it fit
}
You will need to change your DllImport to match. Once that is working you can add code to never go over the maximum buffer length (to prevent memory corruption) and maybe change the bool to the length of the string or -1 for failure.
I suggested this particular function prototype because it doesn't pass memory between languages. Instead the C code fills in a buffer from the C++ code. Which is the safest way to do something. Passing memory between unrelated modules (in this case C and C++ doesn't always work). Though I suspect in this case your C and C++ compiler and libraries are enough alike. That you could just switch to returning a std::string. As long as both sides match perfectly.
I have the following exposed DLL functions in C++.
// center_of_rotation_api.h
// CENTER_OF_ROTATION_API is a macro like __cdecl(dllexport) on Windows
CENTER_OF_ROTATION_API void SerializeMesh(Mesh * mesh, const char * path);
CENTER_OF_ROTATION_API const char * SerializationError(Mesh * mesh);
From C#, I use the following.
// dll is the name of the compiled dll
[DllImport(dll)]
public static extern void SerializeMesh(IntPtr mesh, string path);
[DllImport(dll)]
public static extern IntPtr SerializationError(IntPtr mesh);
The IntPtr returned by SerializationError is allocated using new in C++. So I have another DLL exported function that frees the pointer when called by C#.
My question is, do I need to free the memory of the const char * path in the argument of SerializeMesh in C++?
No, the C# marshaler will take care of this for you. The char* will be valid during the lifetime of the PInvoke call. If your C++ code expects to own the char*, you should make a copy in SerializeMesh.
Here is a primer on string marshaling. If you really need to use a const char*, you might need to set the MarshalAsAttribute to UnmanagedType.LPStr. See here for more information on string marshaling behavior.
Here is another useful reference if you want to dig even deeper.
In C:
extern "C" __declspec(dllexport) int CfgGetVariableString(const char *Name, char **Value)
{
char StrValue[STR_MAX];
int RetValue = GetVariableToStrValue(Name, StrValue, STR_MAX);
if (RetValue == 0)
*Value = StrValue;
return RetValue;
}
C#
[DllImport(DllName, CallingConvention = DllCallingConvention)]
private static extern int CfgGetVariableString([MarshalAs(UnmanagedType.LPStr)]string name, [MarshalAs(UnmanagedType.LPStr)]out string value);
This does not work. I can make it work by calling CoTaskMemAlloc, but then I guess I should free it with separate managed to native call?
So what is the cleanest way to do it?
I can make it work by calling CoTaskMemAlloc.
That is the only way that it can work. Because the marshaller will call CoTaskMemFree to deallocate the memory returned from the native function.
I guess I should free it with separate managed to native call?
As mentioned above, that's not necessary because the framework already does so.
What is the cleanest way to do it?
In my opinion the cleanest approach here, since you are already using a string length determined at compile time, is to have the caller allocate the memory. Change the type from char** to char*. Add an extra argument containing the length of the allocated string. Use StringBuilder on the C# side. There are countless examples of this pattern available here and indeed in other places so I don't feel any need to add to the canon.
I am trying to import a function from a c dll into C#. The c function looks like this
unsigned short write_buffer( unsigned short device_number, unsigned short word_count, unsigned long buffer_link, unsigned short* buffer)
my attempt at a C# import looks like this
[DllImport("sslib32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern ushort write_buffer(ushort deviceNumber, ushort wordCount, UInt32 bufferLink, IntPtr buffer)
In C# i have a Dictionary of messages that i would like to pass to this function. The Dictionary looks like this:
Dictionary<string, List<ushort>> msgs
I am a bit confused how to make a make a proper call to pass msgs as the buffer. deviceNumber is 2, wordCount is 32, and buffLink is 0. So i know the call should look something like this
write_buffer(2,32,0, msgs[key]);
Obviously i am getting an invalid argument for the IntPtr. What is the proper way to make this call?
It is quite unclear what buffer should contain and in which direction its data flows. Your dictionary suggests it should be an array. Just declare it that way:
private static extern ushort write_buffer(.., ushort[] buffer);
And use msgs[key].ToArray() in the call.
Using constants in the write_buffer() call does not make that a likely scenario though, there ought to be msgs[key].Count in there somewhere.
You can generate P/Invoke signatures using the P/Invoke Interop Assistant tool that is referenced here.
In the January 2008 issue of the MSDN
Magazine, Yi Zhang and Xiaoying Guo
have published a CLR Inside Out column
on marshaling between managed and
unmanaged code. In that column, they
introduce the P/Invoke Interop
Assistant, an automatic GUI and
command-line utility for converting
between managed and unmanaged
signatures (in both directions). This
conversion, of course, is not limited
just to Windows signatures; give the
tool a snippet of your own C header
files and it will dutifully convert
them to pretty-printed C#
[DllImport]'s.
If you don't mind marking the code unsafe, you can simply do:
fixed(ushort* pData = msgs[key])
{
write_buffer(2,32,0, pData);
}
And declare your DllImport to take ushort* as the last argument.
The alternative is to use Marshal.StructureToPtr to get the array marshalled into fixed memory. This requires you to allocate memory first using AllocHGlobal, and freeing using FreeHGlobal.
I'm attempting to call a 3rd-party DLL from C#, and I'm having trouble marshalling some string data. The DLL was written with Clarion, and I'm not very familiar with the data types being used. Specifically, the spec has this signature for a function that I can't get to work:
Bool QuerySoftwareVersion( cstring* version) // edited documentation typo
//Returns Software version (20 character cstring).
I was assuming that the cstring was just a null-terminated string, but I wasn't able to get it to work with out char[] version in my definition. Anyone know the right way to handle this?
EDIT: Actually, what I've found thus far suggests that a cstring in Clarion is indeed just a null-terminated string.
UPDATE: I've updated the title of the question and the details because it turns out that there was a typo in the DLL documentation. The version parameter in question is declared as type cstring* not cstringt*. And in Clarion,cstring` is apparently just a c-style, null-terminated string. So the marshalling shouldn't be that complicated, since they claim it was written with C calling conventions. Has anyone successfully p/invoked a Clarion DLL that passes strings via a reference param?
I had some success with a CString reference in a DLL API:
SCODE WINAPI Test( const CString& str );
I used the following C# code to import:
[DllImport("CBData.Dll")]
public static extern int Test( [MarshalAs(UnmanagedType.LPStr)] ref String str );
And this C# code to call:
String b = "Some text";
int x = Test(ref b);
This worked for me - I am not sure if this is safe though. I hope this helps you.
I've never called into Clarion, but I've got a DLL (in C) that is called from both Clarion and C#, and I'd interop that as:
[DllImport("clarionlib.dll", CharSet=CharSet.Ansi,
CallingConvention = CallingConvention.Cdecl,
ExactSpelling=true, EntryPoint="QuerySoftwareVersion")] static extern
bool QuerySoftwareVersion(StringBuilder sName);
Note also that the StringBuilder parameter you pass has to be sized up to the maximum expected return size (pardon my C#, I'm sure there's a shorter equivalent):
System.Text.StringBuilder buffer;
buffer = new System.Text.StringBuilder();
buffer.EnsureCapacity(21);
QuerySoftwareVersion(buffer);
string myString = buffer.ToString();
Try with a StringBuilder:
[DllImport("mylibrary.dll")]
static extern bool QuerySoftwareVersion([Out] StringBuilder version);