I'm trying to interop C++ DLL (dissasembly benefits) with C# app, so I created a C++ Win32 Project (DLL) with export symbols like this:
LicensePolicy32.h:
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the LICENSEPOLICY32_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// LICENSEPOLICY32_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef LICENSEPOLICY32_EXPORTS
#define LICENSEPOLICY32_API __declspec(dllexport)
#else
#define LICENSEPOLICY32_API __declspec(dllimport)
#endif
LICENSEPOLICY32_API char* GetXmlTokenNode(void);
LicensePolicy32.cpp:
#include "stdafx.h"
#include "LicensePolicy32.h"
bool GetLicense()
{
DWORD dwType = REG_SZ;
HKEY hKey = 0;
char value[1024];
DWORD value_length = 1024;
LPCWSTR subkey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\WSESecurityPolicy";
LONG openReg = RegOpenKeyEx(HKEY_LOCAL_MACHINE,subkey,0, KEY_WOW64_64KEY | KEY_QUERY_VALUE, &hKey);
if (openReg==ERROR_SUCCESS)
{
return true;
}
else
{
return false;
}
LONG getReg = RegQueryValueEx(hKey, L"WSEHostProcessID", NULL, &dwType, (LPBYTE)&value, &value_length);
if (getReg==ERROR_SUCCESS)
{
return true;
}
else
{
return false;
}
}
LICENSEPOLICY32_API char* GetXmlTokenNode()
{
char *orig;
bool resultLicense = GetLicense();
if (!resultLicense)
{
return "";
}
else
{
return "/{0}:Envelope/{0}:Header";
}
}
My C# test code is as follows:
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace TestDllConsoleApplication
{
class Program
{
[STAThread]
static void Main(string[] args)
{
Test testObj = new Test();
}
}
public class Test
{
[MarshalAs(UnmanagedType.LPStr)]
public string result;
[DllImport(#"D:\Proyectos\NET\Dll\LicensePolicy32\Release\LicensePolicy32.dll", CharSet=CharSet.Ansi, EntryPoint = "GetXmlTokenNode")]
public static extern string GetXmlTokenNode();
public Test()
{
try
{
result = GetXmlTokenNode();
result += " result";
}
catch (DllNotFoundException exDll)
{
string error = "Dll no encontrado";
}
catch (BadImageFormatException exBad)
{
string error = "Plataforma Ensamblado incompatible";
}
catch (Win32Exception exWin32)
{
string error = "Error general de Win32";
}
catch (EntryPointNotFoundException exPoint)
{
string error = "No encontró punto de entrada al método";
}
catch (Exception ex)
{
string error = "Error otros";
}
}
}
}
Path to DLL is good, however when I run C# test project throws EntryPointNotFoundException.
I'd appreciate your help, thanks
These are not the droids you are looking for. Your c++ names will be decorated and the actual name will not be GetXmlTokenNode. To avoid this, use extern "C" as part of the signature for the methods you want to export. This will avoid name decoration.
You may find this whitepaper useful.
C++ compiler add a suffix to any function name. The suffix defines the parameter types and orders - that way the compiler can handle overloading.
To avoid it you can compile using C (change the cpp suffix to C, and if you don't have special C++ features in the code VS will understand what you want to do).
Alternatively find the real name of the native DLL function and use it as the entry point.
Related
I have a project written in Kotlin, and I want to use a library written in C#. So I used the JNI to make a "proxy" in C++ witch make the link between C# and the JVM.
I was able to load the library in Kotlin but I have this error when I call a function:
Fatal error. System.Runtime.InteropServices.SEHException (0x80004005): External component has thrown an exception.
at <Module>.Java_Authenticator_authenticate(JNIEnv_*, _jobject*, _jstring*, _jstring*)
:run (Thread[Execution worker for ':' Thread 2,5,main]) completed. Took 1.965 secs.
This is my Java class witch use the library:
public class Authenticator {
static {
System.loadLibrary("CppProxy");
}
public native boolean authenticate(String username, String password);
public void runLib() {
System.out.println("run");
boolean valid = authenticate("admin", "admin");
if(valid) {
System.out.println("Valid");
}
else {
System.out.println("No valid");
}
}
}
This is the Authenticator.h file in C++ project, generated automatically with the command javac -h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Authenticator */
#ifndef _Included_Authenticator
#define _Included_Authenticator
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Authenticator
* Method: authenticate
* Signature: (Ljava/lang/String;Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_Authenticator_authenticate
(JNIEnv *, jobject, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
This is my CppProxy.cpp file:
#include "pch.h"
#include "CppProxy.h"
#include <string.h>
#include "Authenticator.h"
String^ toString(const char* chars) {
int len = (int)strlen(chars);
array<unsigned char>^ a = gcnew array<unsigned char>(len);
int i = 0;
while (i < len) {
a[i] = chars[i];
i++;
}
return System::Text::Encoding::UTF8->GetString(a);
}
bool authenticate(const char* username, const char* password) {
return LibreHardwareMonitorWrapper::SharpAuthenticator::SharpAuthenticated(toString(username), toString(password));
}
JNIEXPORT jboolean JNICALL Java_Authenticator_authenticate
(JNIEnv* env, jobject c, jstring username, jstring password) {
jboolean isCopyUsername;
const char* c_username = env->GetStringUTFChars(username, &isCopyUsername);
jboolean isCopyPassword;
const char* c_password = env->GetStringUTFChars(password, &isCopyPassword);
jboolean result = authenticate(c_username, c_password);
// cleaning
env->ReleaseStringUTFChars(username, c_username);
env->ReleaseStringUTFChars(username, c_password);
return result;
}
And this is my C# code:
namespace LibreHardwareMonitorWrapper
{
public static class SharpAuthenticator
{
public static bool SharpAuthenticated(String username, String password)
{
return username == "admin" && password == "admin";
}
}
}
I'm using the oracle JDK 17 and the C# project is currently using .NET6.
Cpp project target x64 platform and C# project Any CPU.
The Cpp project is build in a folder call "lib" witch is inside java.library.path.
CppProxy has for reference LibreHardwareMonitorWrapper.
I tried to run .\gradlew run and expected to see Valid but I have an error in my external function at execution.
Finally found what was the problem. I could not find the CLR option in properties of C# project, whereas it's crucial for running C# code in C++.
So I think .net6 is not yet compatible with this option, I tried with .net framework 4.x and it works.
Also, you should know that it is necessary to put the assemblies of C# project in the /bin of JDK.
I am working with specific hardware and got .lib and .dll from the h/w vendor. Need to use native dll function from C# so I am trying to use C++/CLI concept in between.
My native code is building fine and producing .lib and .dll file, I used this file with C++/CLI DLL and it also build well and giving .dll file. when using this file in C# , I am getting error like:-
Unhandled Exception: System.IO.FileLoadException: A procedure imported by 'CLI_DLL.dll' could not be loaded.
at C_sharp_Code.Program.Main(String[] args)
Important thing is when I am trying to access native dll from CLR console application, it is working fine but when i am calling native function from CLR DLL then dll is building fine but giving below error when using same from C# console application.
C++/CLI .h file
#pragma once
using namespace System;
extern "C" _declspec(dllimport) bool N_Enabale_Port();
namespace CLI_DLL {
public ref class Class1
{
// TODO: Add your methods for this class here.
public:
bool M_Enabale_Port();
};
}
C++/CLI CPP
#include "stdafx.h"
#include "CLI_DLL.h"
bool CLI_DLL::Class1::M_Enabale_Port()
{
bool temp = N_Enabale_Port();
return temp;
}
C# Code
using CLI_DLL;
namespace C_sharp_Code
{
class Program
{
static void Main(string[] args)
{
try
{
Class1 obj = new Class1();
bool x = obj.M_Enabale_Port();
Console.WriteLine(x);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
Any Advice where I am missing something..
I am working with a C++ MFC plug-in dll which is automatically launched from an off-the-shelf application at runtime. I need to modify the plugin to support bidirectional communication with a .NET based C# application.
I have access to the plugin source code but I am not able to change the C++ project to use /clr. I get a runtime error from the off-the-shelf application:
'r6033 attempt to use msil code from this assembly during native code initialization'
I have tried to use "#pragma unmanaged" around the Main method with no luck. So I decided to go the COM route.
I have successfully created a COM based C# dll with a UI and methods implemented which I can call from C++. That gets me one direction. I need to implement a way of making calls to the C++ plugin from the C# application. I started down the path of events. I created an event in C# which are raised on the C# side from the new UI but I was not able to figure out how to create and hook to the events on the C++ side. Any help would be appreciated.
Here is the C# COM Code:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.IO;
namespace MyInterop
{
[Guid("03AD5D2D-2AFD-439f-8713-A4EC0705B4D9")]
public interface IMyDotNetInterface
{
void ShowCOMDialog();
}
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("63058ba6-5675-43d6-b38b-fd261fe64950")]
public interface IIntelliWaveEvents
{
[DispId(1)]
void CallIWave(string a_IWaveCommand);
}
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IIntelliWaveEvents))]
[Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")]
public class MyDotNetClass : IMyDotNetInterface
{
public IIntelliWaveEvents callbackObject;
// Need a public default constructor for COM Interop.
public MyDotNetClass()
{ }
public void ShowCOMDialog()
{
MainForm frm = new MainForm()
{
StartPosition = FormStartPosition.CenterScreen,
TopMost = true
};
frm.CallIWave += new MainForm.CallIWaveEventHandler(frm_CallIWave);
frm.Show();
}
void frm_CallIWave(object sender, Events.IWaveEvents.CallIWaveEventArgs e)
{
string filename = #"C:\Companies\ComEvent.txt";
File.WriteAllText(filename, "COM Event Called at " + DateTime.Now);
//Pass the event to c++
if (callbackObject != null)
{
callbackObject.CallIWave(e.Message);
}
}
}
}
Here is the interface WaveDLL_PAI.cpp code:
#include "stdafx.h"
#include "WaveDLL_PAI.h"
#include "IUserPrograms1.h"
using namespace std;
#import "C:\Program Files\SampleCOM\com.MyInterop.tlb" named_guids raw_interfaces_only
using namespace MyInterop;
//------------------------------------------------------------------------
// This function must exist in all PlugIn(s).
// It creates an instance of the API class and returns a pointer to it.
HRESULT CreateInstance(void **p)
{
*p = new CWaveDLL_PAI();
return (*p != NULL);
}
//---------------------------------------------------------------------------------------------------
// Initialize native variables here
CWaveDLL_PAI::CWaveDLL_PAI() : CWaveDLL_BaseI()
{
}
//---------------------------------------------------------------------------------------------------
// deallocate any memory here
CWaveDLL_PAI::~CWaveDLL_PAI()
{
Release();
}
//---------------------------------------------------------------------------------------------------
// This function releases any memory allocated for this class
ULONG CWaveDLL_PAI::Release()
{
return 0;
}
//---------------------------------------------------------------------------------------------------
// This function must be used to create the class including memory allocation.
// Allocate memory or objects here only.
// Do not display any window or dialog here
ULONG CWaveDLL_PAI::Create(CWnd *pParent, void *a, void *b, void *c)
{
CoInitialize(NULL); //Initialize all COM Components
MyInterop::IMyDotNetInterfacePtr pDotNetCOMPtr;
HRESULT hRes = pDotNetCOMPtr.CreateInstance(MyInterop::CLSID_MyDotNetClass);
if (hRes == S_OK)
{
BSTR str;
pDotNetCOMPtr->ShowCOMDialog(); //call .NET COM exported function ShowDialog ()
}
CoUninitialize(); //DeInitialize all COM Components
return true;
}
//---------------------------------------------------------------------------------------------------
// This function gives the PlugIn access to data and controls
// This function must be defined and have the code listed below.
long CWaveDLL_PAI::SetData(CUserControl *ctrl, SIntelliWaveDataExt *data, LPCTSTR path)
{
CWaveDLL_BaseI::SetData(ctrl,data,path);
return true;
}
Here is the interface WaveDLL_PAI.h Code:
#include "WaveDLL_BaseI.h"
class AFX_EXT_CLASS CWaveDLL_PAI : public CWaveDLL_BaseI
{
public:
int MyVariable;
CWaveDLL_PAI();
~CWaveDLL_PAI();
int MyFunction(int hello = 0);
ULONG Release();
ULONG Create(CWnd *pParent=0, void *a = 0, void *b = 0, void *c = 0);
long SetData(CUserControl *ctrl, SIntelliWaveDataExt *data, LPCTSTR path = 0);
long Execute(LPCTSTR cmd, void *a = 0, void *b = 0, void *c = 0);
};
Here is the WaveDLL_PA.cpp Code:
#include "stdafx.h"
#include <afxdllx.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
HINSTANCE hInstanceOptic01;
static AFX_EXTENSION_MODULE WaveDLL_PADLL = { NULL, NULL };
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
// Remove this if you use lpReserved
UNREFERENCED_PARAMETER(lpReserved);
hInstanceOptic01 = hInstance;
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("WaveDLL_PA.DLL Initializing!\n");
// Extension DLL one-time initialization
if (!AfxInitExtensionModule(WaveDLL_PADLL, hInstance))
return 0;
// Insert this DLL into the resource chain
// NOTE: If this Extension DLL is being implicitly linked to by
// an MFC Regular DLL (such as an ActiveX Control)
// instead of an MFC application, then you will want to
// remove this line from DllMain and put it in a separate
// function exported from this Extension DLL. The Regular DLL
// that uses this Extension DLL should then explicitly call that
// function to initialize this Extension DLL. Otherwise,
// the CDynLinkLibrary object will not be attached to the
// Regular DLL's resource chain, and serious problems will
// result.
new CDynLinkLibrary(WaveDLL_PADLL);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("WaveDLL_PA.DLL Terminating!\n");
// Terminate the library before destructors are called
AfxTermExtensionModule(WaveDLL_PADLL);
}
return 1; // ok
}
I have written some code in native C++ which makes calls to fortran routines of the blas and lapack library (linked as dll's). The native code runs fine, with no compiler or runtime errors or warnings.
Now, I was trying to wrap the native C++ into a dll to use in the .NET framework, so I started a Visual C++ project, wrote a wrapper ref class, compiled this into a dll and included that dll into a C# project.
After I finally managed to get the C# project to compile, I tried to run it. But I get a runtime error at the first call to a lapack routine! (i.e. at the line dpbtrf_( &LOWER, &n, &izero, D.data(), &ione, &info ); in Test.cpp)
The error says:
System.AccessViolationException was unhandled
Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Source=DotNetTest
Am I missing some compiler option?
Or is there really corrupt memory which the native C++ compiler\runtime did not see?
Any tips on how to debug this?
Thanks a lot!
Here's a tiny reproduction that generates the same problem as the original code (which is too extensive to post here):
the native C++ Test.h:
#ifndef TEST_H_INCLUDED
#define TEST_H_INCLUDED
#include <vector>
namespace NativeTest
{
class Test
{
public:
Test( int size );
~Test();
void set( int i, double d );
double get( int i ) const;
int chol();
private:
std::vector<double> D;
int n;
}; // class Test
} // namespace NativeTest
#endif // TEST_H_INCLUDED
the native C++ Test.cpp (included in the VC++ project) (!EDITTED with __cdecl):
#include "Test.h"
using namespace NativeTest;
const int ione = 1;
const int izero = 0;
const char LOWER = 'L';
extern "C"
{
// factorization for banded matrix
void __cdecl dpbtrf_( const char *UPLO, const int *N, const int *KD,
double *AB, const int *LDAB, int *INFO );
}
Test::Test( int size ) : n( size )
{ D.resize( n ); }
Test::~Test() { }
void Test::set( int i, double d ) { D[ i ] = d; }
double Test::get( int i ) const { return D[ i ]; }
int Test::chol()
{
int info = 0;
dpbtrf_( &LOWER, &n, &izero, D.data(), &ione, &info );
return info;
}
The C++\CLI wrapper:
// TestNet.h
#pragma once
#include "Test.h"
using namespace System;
namespace DotNetTest {
public ref class TestNet
{
public:
TestNet( int size ) { test = new NativeTest::Test( size ); }
~TestNet() { this->!TestNet(); }
!TestNet() { delete test; test = NULL; }
void set( int i, double d ) { test->set( i, d ); }
double get( int i ) { return test->get( i ); }
int chol() { return test->chol(); }
protected:
NativeTest::Test * test;
};
}
The C# call:
DotNetTest.TestNet t = new DotNetTest.TestNet(2);
t.set(0, 2);
t.set(1, 3);
int info = t.chol();
I got it fixed, at least for the test code posted here. I wrongly added the gcc-compiled lapack\blas binaries, the same as I used for my original project (which also used gcc).
Today, I downloaded the precompiled binaries for windows from here, put those in the bin folder of my C# project (together with the 3 MinGW dll's: libgcc_s_dw2-1.dll, libgfortran-3.dll and libquadmath-0.dll) and made sure the Visual C++\CLI project used the same libraries to link to (by setting the Additional Link Directory to the C# bin folder). This fixed it, and I got the result I wanted (no access violation and the calculation was correct).
I also removed the /clr flag in the compile options for the Test.cpp file, and switch the 'Use precompiled headers' off for that file.
I will spend a little time explaining my project stucture:
There are three dlls:
mclController.dll - a third party dll written in C# to control the hardware..
MCLWrapper.dll - I wrote this ll in C# such that it will be working as a COM to expose the mclControl.dll to a native C++ dll.
ThorDetectorSwitch.dll - I wrote this dll with native C++.
Structure:
The ThorDetectorSwitch.dll calls the MCLWrapper.dll which wraps mclController.dll.
I am implementing a small testing console application in C++, TDSTest.exe to call ThorDetecttorSwitch.dll.
So it basically works like this: TDSTest.exe -> ThorDetectorSwitch.dll -> MCLWrapper -> mclController.dll
Some code:
-How TDSTest.exe (Windows console application, built with x64 configuration) calls ThorDetectorSwitch.dll:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <math.h>
#include <windows.h>
#include "TDSTest.h"
typedef long (*TDSFindDevices)(long&);
typedef long (*TDSGetParam)(const long, double&);
typedef long (*TDSTeardownDevice)();
typedef long (*TDSStartPosition)();
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
if (argc < 2)
{
cout<<"This is ThorDetecttorSwitch test program."<<endl;
return 1;
}
HINSTANCE hInst = LoadLibrary(_T(".\\Modules_Native\\ThorDetectorSwitch.dll"));
if( hInst == NULL )
{
DWORD err = GetLastError();
cout<<"Error loading ThorDetectorSwitch.dll. Program exiting..."<<endl;
return 1;
}
}
-Constructor of the ThorDetectorSwitch.dll EDITTED! on 06/15/2013, Central Time 19:41
ThorDetectorSwitch::ThorDetectorSwitch() :_mcSwitch(ComHelper(__uuidof(MCLControlClass)))
{
CoInitialize(NULL);
MCLWrapper::MCLControlPtr mclSmartPtr;
HRESULT hr = CoCreateInstance(__uuidof(MCLWrapper::MCLControlClass), NULL, CLSCTX_ALL, __uuidof(MCLWrapper::MCLControl), (void**)&mclSmartPtr); // program breaks right here!!!
_mcSwticth = mclSmartPtr;
_A = WstringToBSTR(L"A");
_B = WstringToBSTR(L"B");
_C = WstringToBSTR(L"C");
_D = WstringToBSTR(L"D");
_deviceDetected = FALSE;
}
The MCLWrapper that makes a COM object
// C# COM wrapper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using mcl_RF_Switch_Controller64;
using System.Runtime.InteropServices;
// for function reference see miniCircuit RF controller manual
namespace MCLWrapper
{
[Guid("7C312A7C-2E77-4de7-A76F-990F268AB818")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface MCLControl
{
[DispId(1)]
void Connect(string SerialNumber);
[DispId(2)]
void Set_Switch(string SwitchName, int Val);
[DispId(3)]
void Set_SwitchesPort(byte binVal);
[DispId(4)]
void GetSwitchesStatus(int statusRet);
[DispId(5)]
void Disconnect();
};
[Guid("BEC33A1D-BB98-4332-B326-92D480ECC246"),
ClassInterface(ClassInterfaceType.None)]
public class MCLControlClass : MCLControl
{
private USB_RF_SwitchBox _sb = new USB_RF_SwitchBox();
public void Connect(string SerialNumber)
{
_sb.Connect(ref SerialNumber);
}
public void Set_Switch(string SwitchName, int Val)
{
_sb.Set_Switch(ref SwitchName, ref Val);
}
public void Set_SwitchesPort(byte binVal)
{
_sb.Set_SwitchesPort(ref binVal);
}
public void GetSwitchesStatus(int statusRet)
{
_sb.GetSwitchesStatus(ref statusRet);
}
public void Disconnect()
{
_sb.Disconnect();
}
}
}
My Problem:
When the TDSTest is executed, it first hits
HINSTANCE hInst = LoadLibrary(_T(".\\Modules_Native\\ThorDetectorSwitch.dll"));
then it breaks at:
hr = CoCreateInstance(......) in the ThorDetectorSwitch.cpp
hr = -858993460 is the return;
A few additionals
I am kept be told it was because CoInitialized() did not get called, and that is the reason, but I feel that is not the reason because this ThorDetectorSwitch.dll works perfectly fine with another application, and I beleive I have called CoInitialized() in my code.
I have registered my MCLWrapper.dll with regasm MCLWrapper.dll /tlb:MCLWrapper.tlb /codebase
Debugger output: "Attempting managed execution inside OS Loader lock. Do not attempt to run managed code inside a DllMain or image initalization function since doing so can cause the application of hang."
So right now I have no idea what direction I should go, and I have been struglling with this problem for days. So I really hope someone can give some pointers for me. Thanks!
You need to lazily construct your object instead of have it as a global variable created on DLL load.
Maybe you could have your DLL provide an Initialize() function that would be called by the client? Assuming you can't make your object "not global at all" of course.