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;
}
Related
Working with Visual Studio 2019. Calling c functions from c# is common but when there is a runtime error, I get a popup window as such:
I would like however to actually break into the c++ code so I can debug it,in particular review the call stack, watch the local variables, add break points etc.
How to achieve this ?
If one need a demo project:
https://github.com/mprevot/InteropDemo
I'm calling a c function designed to fail:
#ifndef Pinvoke
#define Pinvoke extern "C" __declspec(dllexport)
#endif
std::wstring ToString(int number)
{
std::wstringstream s;
s << "got number " << number;
return s.str();
}
Pinvoke auto GetNumbers() -> void
{
std::vector<int> array0{11,12,13,14};
std::vector<int> array1{21,22,23};
for (size_t i = 0; i < 4; i++)
ToString(array0[i]);
for (size_t i = 0; i < 4; i++)
ToString(array1[i]);
}
I call such function from c#:
internal static class NativeLibCall
{
public const string _dll = "NativeLib.dll";
[DllImport(_dll, CallingConvention = CallingConvention.StdCall)]
internal static extern void GetNumbers();
}
public class NativeLibInterop
{
public void GetNumbers() => NativeLibCall.GetNumbers();
}
As suggested by Kevin Gosse, and detailed in MSFT documentation, just enable mixed-mode debugging:
Enable mixed-mode debugging for a managed calling app
Select the C# or Visual Basic project in Solution Explorer and select the Properties icon, press Alt+Enter, or right-click and choose
Properties.
Select the Debug tab, and then select Enable native code debugging.
Close the properties page to save the changes.
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);
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 the code below in an unmanaged C++ DLL. The ToasterHook() function is called by a C# app using P/Invoke, and WndProc is overridden to trap any WM_COPYDATA messages. Process Explorer says that my DLL has been injected into other processes, but I am only receiving the WM_COPYDATA once when my form loads.
#pragma data_seg (".SHARED")
HHOOK g_HookHandle = 0;
HINSTANCE DllHandle;
HOOKPROC hkprcSysMsg;
#pragma data_seg()
extern "C" __declspec(dllexport) int ToasterHook()
{
if(g_HookHandle != 0) return 0;
DllHandle = LoadLibrary(L"toasterHookDll.dll");
hkprcSysMsg = (HOOKPROC)GetProcAddress(DllHandle, "_ToasterInterProcFilter#12");
g_HookHandle = SetWindowsHookEx(WH_SHELL, hkprcSysMsg, DllHandle, 0);
return 0;
}
extern "C" __declspec(dllexport)
LRESULT CALLBACK ToasterInterProcFilter(int code, WPARAM wParam, LPARAM lParam)
{
if(code == HSHELL_WINDOWCREATED) {
HWND g_ToasterReceiver = FindWindow(NULL, L"toaster");
SendNotifyMessage(g_ToasterReceiver, WM_COPYDATA, wParam, lParam);
}
return CallNextHookEx(g_HookHandle, code, wParam, lParam);
}
extern "C" __declspec(dllexport) void ToasterUnHook()
{
if(g_HookHandle == 0) return;
UnhookWindowsHookEx(g_HookHandle);
}
What am I doing wrong here? Specifying the result of GetProcAddress(DllHandle, "_ToasterInterProcFilter#12") nor ToasterInterProcFilter itself for HOOKPROC seem to be working.
When sending a WM_COPYDATA message the lParam value must point to a COPYDATASTRUCT structure. This data structure contains information such as a pointer to the data to be copied and the size of the data. Windows automatically handles the marshaling of this data so that it is accessible by the application receiving the message.
Your code is currently passing the lParam accompanying the HSHELL_WINDOWCREATED. It's possible that Windows can interpret the data it points to as a COPYDATASTRUCT structure but in most cases it will fail.