pass C# string to C++ dll fails in Release build - c#

I have a C++ dll with simple function like
#ifdef BUILDING_THE_DLL
#define EXPORTED __declspec(dllexport)
#else
#define EXPORTED __declspec(dllimport)
#endif
...
EXPORTED void AddSetting(char *key, char *value)
And a C# project with function declaration:
[DllImport("pers.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddSetting(string key, string value);
Everything works perfectly well while C# project is build in Debug mode. In Release build an exception is fired: "An attempt was made to load a program with an incorrect format."
Any ideas?
UPD: In C# project Platform target was set to x86 in Debug mode and Any CPU in Release. I've changed to x86 in Release and it was the solution. Thanks a lot to Matt.

You need specify to use the ansi format of the string, by default. it is unicode. It should be :
[DllImport("pers.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet::Ansi)]
public static extern void AddSetting(string key, string value);
Also, if the function "AddSetting" modifies the strings, you need to use StringBuilder in C#. Please refer this MSDN article for details.

Related

C# EXE w/ Unmanaged C++ Unicode DLL linking to unmanaged C++ ANSI DLL crash

I have a C# executable which loads in a DLL which is a unicode unmanaged C++ DLL. This unmanaged C++ DLL also links to another DLL, an unmanaged C++ DLL that happens to be ANSI.
When I run my C# executable, the program ends up crashing in the ANSI portion of the DLL calls (I haven't been able to pull the exception yet). However, by simply switching the ANSI DLL to Unicode, everything works except for the fact that there is a third DLL, which is from a SDK from another company, which has an apparent sensitivity to unicode/ANSI so it works best if the calling DLL is in ANSI.
So we have one executable calling functions in only one unmanaged unicode C++ DLL which serves as a wrapper for an unmanaged ANSI C++ DLL which is a wrapper for the final unmanaged DLL which we have no information about.
Switching the two intermediary DLL's to unicode corrects the crashing only to have it fail with the third separate vendor DLL (but not fail catastrophically with an exception, they just output incorrectly). We can't switch the first DLL to ANSI because we use Unicode in our C# application and that's our standard across the board.
I don't understand the sensitivity to a second-order DLL. Can someone shed some light on this for me?
I use this class to dynamically link to the DLL's:
static class NativeMethods
{
[DllImport("kernel32", SetLastError = true)]
public static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllToLoad);
}
with delegates similar to:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Auto)]
private delegate int ExampleFunction();
and switching the CharSet.Auto to .Ansi or .Unicode has no effect.
with function calls and such:
m_pDll = NativeMethods.LoadLibrary(#strDLLName);
if (m_pDll == IntPtr.Zero) this.Close();
IntPtr pAddressForExampleFunction = NativeMethods.GetProcAddress(m_pDll, "ExampleFunction");
if (pAddressForExampleFunction == IntPtr.Zero) this.Close();
m_ExampleFunction = (ExampleFunction)Marshal.GetDelegateForFunctionPointer(pAddressForExampleFunction, typeof(ExampleFunction));
with function call:
m_ExampleFunction();
elsewhere in code.
Edit:
As requested, the C++ EXE Counterpart:
In the .h file, defined as a member:
ExampleFunction pExampleFunction;
with
typedef BOOL __declspec(dllimport) (*ExampleFunction)();
The pExampleFunction being defined as:
pExampleFunction= (ExampleFunction) ::GetProcAddress(m_hDll,"ExampleFunction");
using this call, prior:
m_hDll = AfxLoadLibrary(m_DllName);
Most probably the problem happens between two unmanaged dlls because string data transfer between them is inconsistent.
ANSI/Unicode dll flag is a compile-time property. Compiler selects types and functions depending on this flag. TCHAR for Unicode compiled as wchar_t and for ANSI it's char. E.g. such difference could cause out of bound problem if one dll expects to get wchar_t* with length in symbols, but actual received value is char*. This is Undefined Behavior and could cause application crash.
Also many Win API functions have two versions xxxW for Unicode and xxxA for ANSI. E.g:
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif.
On C# side CharSet attribute controls string marshaling and determines how platform invoke finds function names in a DLL. It doesn't affect further string manipulations inside unmanaged C++ dll. Method
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Auto)]
private delegate int ExampleFunction();
has no strings to marshal, so CharSet doesn't affect it. There can be a difference if you have two implementations of this method on your unmanaged C++ side: ExampleFunctionA for ANSI and ExampleFunctionW for Unicode.

embedding c++ native dll in set up

I want to embed c++ native dll in set up file created with install shield limited edition.
Hint :- My application created by using c# and c++ native dll.
Here is my example :-
My c++ dll_code
extern "c" __declspec(dllexport) int function_c ()
{
int a=10;
return a;
}
My .net code
public partial class Form1 : Form
{
[DllImport(#"C:\Users\bajwa\Documents\Visual Studio 2012\Projects\c++dll\c++_dll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int function_c();
void csharp_function()
{
int result= function_c(); // calling c++ native function
MessageBox.Show(result);
}
private void button1_Click(object sender, EventArgs e)
{
csharp_function(); // calling c# function.
}
}
When I installed this setup on my computer it runs perfectly. Because C++ native dll is placed on my computer at "C:\Users\bajwa\Documents\Visual Studio 2012\Projects\c++dll\c++_dll.dll".
But when I delete the c++ native dll from that location then it shows the error.
dll not fount at this location
Please help and solve my problem.
I think you will just need to use a relative path for the imported DLL. The easiest thing to do would be to copy the DLL to a known folder relative to the executable (maybe even the same folder) and then use that path. So if they share a folder, your code can become this:
[DllImport("c++_dll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int function_c();
Then just modify the installer to place the dll in the correct location. To test, just change your code and move the dll over, but it should work.

Passing string from .NET to native (64-bit)

I am having an issue passing a simple string from my .NET app, compiled as 64-bit, to my native DLL, also compiled as 64-bit.
C++ signature:
DllExport void SetFoo(LPWSTR foo);
C# signature:
[DllImport(Library, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
internal static extern void SetFoo(
[In][MarshalAs(UnmanagedType.LPWStr)] string foo);
Then I run the following code in C#:
X.SetFoo("some string");
When it reaches the debugger in the DLL, the value is swearing at me in Chinese: Ⴐ虘翺
When I change both the .NET and native code to 32-bit, the value I get in the debugger is the correct string. Where am I being stupid?
Minimal reproduction as a Visual Studio 2015 Solution: download here
To reproduce:
Create a new Visual Studio solution with a WinForms project.
Add a Win32 Project, of type DLL to the solution
Add the following files:
Foo.h:
extern "C" {
__declspec( dllexport ) void SetFoo(LPWSTR);
}
Foo.cpp:
#include "stdafx.h"
#include "Foo.h"
DllExport void SetFoo(LPWSTR foo)
{
}
Set a breakpoint on the opening brace in SetFoo
Add a button to the winforms form
Double click it, and call SetFoo("abc123").
Implement SetFoo:
Form1.cs:
[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void SetFoo(string text);
Change the apps to build in 64-bit mode by opening Configuration Manager.
Set Win32Project1 to build as x64
Set WindowsFormApplication1 to build as x64, by picking platform new, pick x64, OK.
Change the output directory of WindowsFormsApplication1 to match the output directory of the other app.
Start without debugging.
Attach debugger (Ctrl+Alt+P) by setting Attach to to Managed (v4.5, v4.0) code, Native code and finding the process.
Observe value at breakpoint.
When you interpret ANSI encoded latin text as UTF-16 you see Chinese characters. That's clearly what is happening. So your C# code is sending ANSI encoded text somehow.
The p/invoke would be better written like this:
[DllImport(Library, CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode)]
internal static extern void SetFoo(string foo);
The [in] is not needed, it is the default. And the MarshalAs is pointless since you specified CharSet.Unicode. However, neither change affects the semantics.
The only sound explanations for what you describe in the question are:
The actual code is not as you have described it, or
There is a defect in the debugger.
I suggest that you change the unmanaged function to
DllExport void SetFoo(LPWSTR foo)
{
MessageBoxW(0, L"", foo, MB_OK);
}
If the message box displays the correct text then the conclusion would appear to be that the debugger is defective.

Function Parameters for LabVIEW DLL

I am trying to call a function from a DLL generated in LabVIEW. I thought this was going to be far more straightforward than it is turning out to be. The function is described below:
void __cdecl Device_Init(char DevName[]);
So in my C# code I am trying the following:
[DllImport(#"Device.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void Device_Init(StringBuilder name);
I call this in my application by simply using the following:
StringBuilder devName = new StringBuilder(DeviceName);
Device_Init(devName);
Rather than getting any initialization on my device, I see a LabVIEW vi window pop up that has a title akin to a different method within the dll (i.e. AF1_GetPressure.vi). The application then hangs with this LabVIEW window popped up and I have to exit the debugging session.
I guess my question is how my function signature might be erroneous... I used StringBuilder as I found an example on the NI website that seemed to indicate that LabVIEW requires this variable type to better ascertain the number of characters in the array. http://www.ni.com/example/31050/en/
I have tried all kinds of different combinations of parameter types but I simply can't seem to get this to work. If I try calling the dll from C++ then I can get things to work. Although, oddly, I had to dynamically load the dll in C++ because I was getting a dll initialization failure when I tried to load it with the application.
Any help would be greatly appreciated!
I was able to build a DLL with LabView 2012, and import it into a .NET 4.0 console application, call the function, and receive a result. Here is a screenshot of the VI:
And here is the import statement in C#:
[DllImport(#"SharedLib.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int StringLength(string str);
I would recommend trying something very simple like this and see if you can get it working.
I should note that I tried passing my parameter as a StringBuilder object and that worked as well - and I didn't expect it to!
Also, I recommend posting this question on the LabView forums. I was always able to get a very quick response there, and I think with LabView, you're likely to get a better response there than StackOverflow.
As requested, here are the contents of the .h file generated by LabView:
#include "extcode.h"
#pragma pack(push)
#pragma pack(1)
#ifdef __cplusplus
extern "C" {
#endif
/*!
* StringLength
*/
int32_t __cdecl StringLength(char String[]);
long __cdecl LVDLLStatus(char *errStr, int errStrLen, void *module);
#ifdef __cplusplus
} // extern "C"
#endif
#pragma pack(pop)

Calling C++ DLL from C# within VS fails. Run exe outside VS and it works

I have a C++ DLL that is being called like below from a C# console app.
When run in Visual Studio to debug it, it throws an exception saying the stack is unstable and to check that the method arguments are correct. However, if I run the *.exe outside of VS from Windows Explorer it retuns data to the screen as expected.
How can I get this to run within Visual Studio?
Thanks
**From the C++ header file:**
#ifdef RFIDSVRCONNECT_EXPORTS
#define RFID_CONN_API __declspec(dllexport)
#else
#define RFID_CONN_API __declspec(dllimport)
#endif
RFID_CONN_API BSTR rscListDevices( long showall ) ;
[DllImport("MyDLL.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string rscListDevices(int showall);
static void Main(string[] args)
{
string data= rscListDevices(0);
Console.WriteLine(data);
Console.ReadKey();
}
Firstly, make sure you're using the same calling convention in both C++ and C#.
I suspect that the /Gd compiler option is set (since it is set by default), so __cdecl is used as default calling convention for unmarked functions.
You can fix crashes by either specifing the same calling convention in your C# code:
[DllImport("MyDLL.dll", CallingConvention=CallingConvention.Cdecl))]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string rscListDevices(int showall);
Or changing rscListDevices's calling convention to __stdcall (which is the default in C#):
RFID_CONN_API BSTR __stdcall rscListDevices( long showall ) ;
You can also set __stdcall as the default calling convention for the unmarked functions in your C++ DLL by changing compiler option from /Gd to /Gz in manually or using the Project Properties dialog:
But if you really want to disable MDA, you can go Debug->Exceptions and uncheck Managed Debugging Assistance.
You can read more about pInvokeStackImbalance and MDA here and here.

Categories