Win32 C++ DLL project is saved in my bin/Debug file with the other DLLs.
Running Debug x86 mode on my C# project.
From previous attempts to solve this problem I have changed the Build Configuration to x86 from x64 but I still receive the same error.
namespace ComputerToArduino
{
public partial class Form1 : Form
{
[DllImport("MySimpleLib.dll", CharSet = CharSet.Unicode)]
public static extern int AddNumber(int a, int b);
public Form1()
{
InitializeComponent();
disableControlsArduino();
disableControlsMotor();
getAvailableComPorts();
chartInit();
int result = AddNumber(1, 2);
Console.Write(result);
}
}
}
I created a DLL project in Visual studio. This is my main DLL code:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
extern "C" __declspec(dllexport) int AddNumber(int n1, int n2);
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
int AddNumber(int n1, int n2)
{
return n1 + n2;
}
I am receiving this error message which I do not understand:
Exception thrown: 'System.BadImageFormatException' in ComputerToArduino.exe
An unhandled exception of type 'System.BadImageFormatException' occurred in ComputerToArduino.exe
An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
'ComputerToArduino.exe' (CLR v4.0.30319: ComputerToArduino.exe): Loaded 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\Remote Debugger\x64\Runtime\Microsoft.VisualStudio.Debugger.Runtime.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
The program '[14748] ComputerToArduino.exe' has exited with code -1 (0xffffffff).
Error after adding extern to C++ function:
Managed Debugging Assistant 'PInvokeStackImbalance' : 'A call to PInvoke function 'ComputerToArduino!ComputerToArduino.Form1::AddNumber' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.'
In x86 (in contrast to x64), there are different calling conventions. The language C (and extern "C" in C++) defaults to the cdecl calling convention, whereas C# defaults to the stdcall calling convention. Therefore, you must set the calling convention to cdecl in C#, like this:
[DllImport("MySimpleLib.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int AddNumber(int a, int b);
Related
I am currently working on migrating our company's x86 software to x64 and have encountered a snag. Our method from our unmanaged dll that returns a char* crashes with 0xc0000374 when the solution platform is switched to x64 and I'd like to know why.
C++ Native Code:
#include "stdafx.h"
#include <windows.h>
#include "windef.h"
#define VERSION "3.3.12"
extern "C"
{
__declspec(dllexport) char* WINAPI GetMyVersion()
{
return (char*)VERSION;
}
}
C# DllImport:
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace InteropTest.Adapter
{
[SuppressUnmanagedCodeSecurity]
public unsafe class MyAdapter
{
private const string DLLName = "VersionNumber";
private const string EntryPointName = "GetMyVersion";
[DllImport(DLLName, EntryPoint = EntryPointName, CharSet = CharSet.Ansi)]
internal static extern IntPtr GetMyVersionPtr();
[DllImport(DLLName, EntryPoint = EntryPointName, CharSet = CharSet.Ansi)]
internal static extern string GetMyVersionString();
}
}
GetMyVersionString() only works on x86 platforms and crashes on x64 ones. However, I am able to get the IntPtr and to use the Marshal class to convert it to a managed string on both x86 and x64 like such:
using System.Runtime.InteropServices;
using System.Windows;
using InteropTest.Adapter;
namespace InteropTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
var ptr = MyAdapter.GetMyVersionPtr();
var convertedString = Marshal.PtrToStringAnsi(ptr);
// This crashes in x64 build
var directString = MyAdapter.GetMyVersionString();
VersionTextBlock.Text = convertedString;
}
}
}
I have also tried specifying the MarshalAs Attribute for the return value explicitly without success. It seems like the 64 bit char* is used as a 32 bit pointer if you do not explicitly use the Marshal class. Is this just a bug in .NET or did I do something wrong? From My point of view, converting with the Marshal class should be equivalent to using the MarshalAs Attribute.
EDIT:
Here is a sample project I've made to replicate the bug:
https://github.com/chenhuang444/dotNet40InteropCrash
EDIT 2:
The program and exits with code 0xc0000374 without native debugging and only states "InteropTest.exe has triggered a breakpoint" if I have native debugging on, with a stack trace of:
ntdll.dll!00007ffc0fdc4cfa() Unknown
ntdll.dll!00007ffc0fdcc806() Unknown
ntdll.dll!00007ffc0fdccad1() Unknown
ntdll.dll!00007ffc0fd69a55() Unknown
ntdll.dll!00007ffc0fd77347() Unknown
mscorlib.ni.dll!00007ffbd566759e() Unknown
[Managed to Native Transition]
> InteropTest.exe!InteropTest.MainWindow.OnLoaded(object sender, System.Windows.RoutedEventArgs e) Line 23 C#
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) Unknown
EDIT 3:
The unmanaged C++ code is a project in our solution and the x64/x86 configurations are set up, similar to the example project linked above.
Well, your version is not a "string", it's indeed a pointer to raw memory. The .NET framework will try to deallocate this pointer if you tell it it's a string.
So, you have two options:
use an IntPtr like you do, which is fine.
allocate a real string that .NET can understand, using the COM allocator, so in your case, your C++ code could be replace by this:
.
__declspec(dllexport) char* WINAPI GetMyVersion() {
char* p = (char*)CoTaskMemAlloc(7);
CopyMemory(p, VERSION, 7);
return p;
}
note: you can also use BSTRs.
I am calling unamanaged function from the managed code. But Unamanaged call is not happening.
Managed C# code:
(Created a project (Sampletest) from Visual C# -> Console App)
Sampletest:
namespace Sampletest
{
class Program
{
const string Dllpath2 = #"C:\Users\Sampletest\SampleDll\Debug\SampleDll.dll";
[DllImport(Dllpath2, EntryPoint = #"IsTherePower", CallingConvention = CallingConvention.Cdecl)]
public static extern Boolean IsTherePower();
static void Main(string[] args)
{
var test = IsTherePower();
Console.ReadKey();
}
}
}
Unmanaged C++ code:
(Created a dll project (SampleDll) from Visual C++ -> Windows Desktop -> Dynamic Link Library)
"IsTherePower()" definition is there in SampleDll.cpp
#include "stdafx.h"
BOOL IsTherePower()
{
BOOL bRetValue = FALSE;
return bRetValue;
}
But when we are making unmanaged call, first it is going to dllmain.cpp file present in unmanaged code.
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
then it is not going to
BOOL IsTherePower() function
and comming back to the managed call at "var test = IsTherePower();"
showing the error "Unhandled exception at 0x7705D6C7 (ntdll.dll) in Sampletest.exe: 0xC0000096: Privileged instruction.
Settings i Made:
For C# project,
Debug-> Selected "Enable native code debugging"
And I selected "Debug", "x86"
Please help me to resolve this issue.
You have to declare IsUPSPresent using the __declspec(dllexport) attribute or use a .def-file. Also, to overcome C++ name mangling, your definition has to be extern "C" in C++-code.
extern "C" {
BOOL __declspec(dllexport) IsUPSPresent()
{
BOOL bRetValue = FALSE;
return bRetValue;
}
}
After looking for solutions already proposed to a similar question to mine,
and since this is the first time I'm using a non-.NET DLL in a .NET application,
I really need your help.
I have an WPF application, using MVVM Pattern, and in my ViewModel class I need to use a DLL done in C++ to recover a token. I have an example in C++ that uses this DLL, so I have the method's names, but I can't do the same in C#. I know that I must use DllImport to use this methods, but how implement it and use the pointer in C#??
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <string>
int main()
{
HINSTANCE hinst = NULL;
typedef bool ( *GetTokenProto )( char ** );
typedef void ( *FreeTokenProto )( char * );
GetTokenProto GetToken;
FreeTokenProto FreeToken;
std::string str = "DllName.dll";
std::string token;
if ( (hinst = LoadLibraryA(str.c_str()) ) )
{
GetToken = (GetTokenProto) GetProcAddress(hinst, "GetToken");
FreeToken = (FreeTokenProto) GetProcAddress(hinst, "FreeToken");
if (GetToken && FreeToken)
{
char *buf;
if (GetToken(&buf))
{
token = buf;
FreeToken(buf);
std::cout << "Token:" << token << std::endl;
}
else
{
std::cerr << "DLL loaded but no token" << std::endl;
exit(1);
}
}
else
{
std::cerr << "DLL loaded but missing proc address(es)" << std::endl;
exit(1);
}
FreeLibrary(hinst);
}
else
{
std::cerr << "Failed to load DLL" << std::endl;
exit(1);
}
return 0;
}
Update
[DllImport("DllName.dll", EntryPoint = "GetToken", CallingConvention = CallingConvention.Cdecl)]
public static extern bool get_token(ref string token);
[DllImport("DllName.dll", EntryPoint = "FreeToken", CallingConvention = CallingConvention.Cdecl)]
public static extern void free_token(ref string token);
public static string a_token;
public string get_token_method()
{
try
{
string buffer = null;
if (get_token(ref buffer))
{
a_token = buffer;
free_token(ref buffer);
Debug.WriteLine("token : " + a_refresh_token);
}
else
{
Debug.WriteLine("DLL Loaded but no token");
}
}
catch (Exception ex)
{
Debug.WriteLine("\n" + ex.Message);
}
return a_refresh_token;
}
The error
I have an Exception "System.DllNotFoundException" : Unable to load DLL
'DllName.dll': The specified module could not be found. (Exception
from HRESULT: 0x8007007E).
Dll file is in the same folder of the .exe (..\bin\Debug)
If your DLL is in the same Directory, there are still a few things that might be the problem.
Firstly
The DLL may have dependencies
The native DLL you use may have other dependencies which have to be
installed (try Dependency Walker). If the native DLL requires for
example registry settings, config files etc. these should also be
present. It should be distributed to user machines the same way you
installed it on the dev machine.
Most likely you are missing the C++ redistributable package (which one I'm not sure) however Dependency Walker should tell you.
Secondly
It could be targeting a different bitness i.e x86 x64, so I'd try changing your project to see if that helps
Right click your project, and select properties.
In properties, select the build tab. Under platform target, select x86.
Hit Ctrl+Shift+S to save all files, right click the solution and select "Clean" to get rid of old binaries. Any builds after that
should be 32 bit
You can use SetDllDirectory method to set path of your dll before invoking your dll
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetDllDirectory(string lpPathName);
You should also use StringBuilder instead of string on c# side if you want to get string from c++.
I have a C# application that is calling a C++ function from a DLL. The C++ function shows a dialog box and a exit button only.
The C++ function in DLL is looking like:
//the exported function for clients to call in DLL
HINSTANCE hInstance;
__declspec(dllexport) int __cdecl StartDialog(string inString)
{
hInstance = LoadLibrary(TEXT("My.dll"));
DialogBox(hInstance, MAKEINTRESOURCE(ID_DLL_Dialog), NULL, DialogProc);
return 1;
}
BOOL CALLBACK DialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDD_BUTTON_EXIT:
DestroyWindow(hwnd);
return TRUE;
}
}
return FALSE;
}
If I call my StartDialog function in a simple C++ program, it works. I can show the dialog, and can close it properly when I click exit in my dialog.
typedef int(__cdecl *StartDialogFunc)(string);
StartDialogFunc StartDialog = nullptr;
HINSTANCE hDll = LoadLibrary(TEXT("My.dll"));
StartDialog = (StartDialogFunc)GetProcAddress(hDll, "StartDialog");
int i = StartDialog("hello"); //this is working
cout << i;
If I call it in my C# application, after I click the exit button, the dialog closes, and give me an exception. The C# code looks like:
[DllImport("My.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int StartDialog(string s);
int i = StartDialog("hello"); //this causes a exception
The error message is like:
Debug Assertion Failed!
Program: (some paths...) My.dll
File: d:\program files (x86)\microsoft visual studio 14.0\vc\include\xmemory0
Line: 100
Expression: "(_Ptr_user & (_BIG_ALLOCATION_ALIGNMENT - 1)) == 0" && 0
For information on how your program can cause an assertion failure, see the Visual C++ documentation on asserts.
How can I know what is exactly wrong in my DLL?
Try changing the C++ function signature to take a WCHAR*, as C++'s std::string is incompatible with C#. Also, to get a handle to the current DLL, use GetModuleHandle(NULL), not LoadLibrary.
HINSTANCE hInstance;
__declspec(dllexport) int __cdecl StartDialog(const WCHAR* inString)
{
hInstance = GetModuleHandle(NULL);
DialogBox(hInstance, MAKEINTRESOURCE(ID_DLL_Dialog), NULL, DialogProc);
return 1;
}
I have created a WIN32 DLL project and its dllmain.cpp is as follows;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
_declspec(dllexport) float RGBCompare()
{
return 100;
}
My target is to call method RGBCompare from a C# project and as per rule I have mentioned dllexport tag before it.
On the other side in C# project I have defined an entry point as follows;
namespace LogoFinderWrapper
{
public class LogoFinder
{
[DllImport("LogoIdentifier.dll", EntryPoint = "RGBCompare")]
private static extern float Api_RGBCompare();
public static float RGBCompare()
{
return Api_RGBCompare();
}
}
}
When I call DLL it raises exception System.EntryPointNotFoundException.
Please could any one help me in this regard?
Your native code is C++ and the name is mangled before export. Possible solutions:
Use the mangled name in the EntryPoint parameter. Find out the mangled name with dumpbin or Dependency Viewer.
Use a .def file rather than __declspec(dllexport) to control which functions are exported.
Suppress mangling with extern "C" in your C++ source code.
The final option would look like this:
extern "C"
{
__declspec(dllexport) float RGBCompare()
{
return 100;
}
}