SEHException (0x80004005) when using JNI to run C# code - c#

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.

Related

How to call C++ code, and the code it calls, from C#

TL;DR
I have legacy c++ code which does stuff (sometimes returns stuff), calls other cpp code, but is not a full class/obj. This code I cannot alter. I am making fresh c# code which I am looking to call the c++ code from. I don't understand whether to create a dll that calls the original legacy c++, or create a CLR?? which also calls the legacy cpp. Below I have example code that I have implemented (with problems).
Main
I have legacy.cpp and legacy.h which I can not alter.
This is not a class/object and only has public functions, values, and #defines.
legacy.cpp and .h both #include other files and cpp libraries to do its job.
I have a new project where I can add C#, C++ code
I am having trouble understanding what I need to do/research in order to call any of the functions defined in legacy.cpp (or the values/defines) from within my new C# code.
Some of what I have looked at include
Managed CLR wrappers
https://drthitirat.wordpress.com/2013/06/03/use-c-codes-in-a-c-project-wrapping-native-c-with-a-managed-clr-wrapper/
https://web.archive.org/web/20140806022309/http://blogs.msdn.com/b/borisj/archive/2006/09/28/769708.aspx
DLLs??
How to call C++ DLL in C#
CLR
I have currently tried to create a CLR (thought I feel like it is not what I need in this situation), but I had problems in the clr_wrapper.cpp, it could not find the reference to foo()
//Wrapper.cpp
#include "Wrapper.h"
#include "abs\path\to\legacy\code\legacy.h"
#include "abs\path\to\legacy\code\legacy.cpp"
int foo_Wrapper()
{
return foo(); //int foo() is declared in legacy.h and defined in legacy.cpp
}
#pragma once
#include "abs\path\to\legacy\code\legacy.h"
#include "abs\path\to\legacy\code\legacy.cpp"
using namespace System; //What things use this?
//Can I just prepend System:: to whatever needs it?
namespace Wrapper {
public ref class Wrapper
{
public:
int foo_Wrapper();
};
}
the foo_Wrapper() is not able to call foo().
My confusion with this method is that it looks like I would need to make the clr wrapper an object class with member functions that will be called as needed. Leading to a syntax of obj.foo(). Is this what needs to be done if I chose to do some sort of CLR wrapper?
DLL
I have also looked at making this all a dll like in (How to call C++ DLL in C#)
However I am confused on setting this up. My current idea is to have a cpp dll call the original cpp (ie create legacyDll.dll which would make calls to foo(), then my main c# would call the __declspec(dllexport) functions defined within extern "C" {}
current setup (from "How to call c dll in c sharp")
dllmain.cpp
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <iostream>
#include <Windows.h>
using namespace std;
extern "C"
{
__declspec(dllexport) void bar_Dll()
{
cout << "calling bar() in legacy code" << endl;
}
__declspec(dllexport) int foo_Dll()
{
cout << "calling foo() in legacy code" << endl;
//realistically I would have,
//return foo()
}
}
Class1.cs
using System;
using System.Runtime.InteropServices;
namespace Test_DLL_Calling
{
class Class1
{
[DllImport("dllmain.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void bar_Dll();
[DllImport("dllmain.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int foo_Dll();
public static void Main(string[] arg)
{
bar_Dll(); //The specified module could not be found (Exception)
Console.WriteLine(foo_Dll()); //Im guessing this will also ^^
}
}
}
This part I don't follow. What and why are the attributes done the way they are?
Alright, so you have a header and cpp which you need to use. In order to use it you have to have make the c++ into C code. This is pretty much what you see in the DLL example code that you showed. However, I do suggest that you remove the includes from the header as I'm unsure how that would translate to the C code. Put the includes in the cpp files instead.
I find it rather difficult to answer this questions other than just showing a whole bunch of example code. Full code in: https://github.com/ze413X/Cpp-Code-Gems/ Where "CSharpExampleUsingCpp" calls from MainWindow.xaml.cs the "DLLExample" which uses the file in the directory includes and source.
DLL:
The header which is exposing functions to be used:
#pragma once
#define DLLExport _declspec(dllexport)
extern "C" DLLExport void __cdecl GetCppText(char* str, int* strLength);
extern "C" DLLExport void __cdecl DLLPrint();
.cpp
#include "../includes/DLLExampleCode.h"
#include <string>
#include <iostream>
void __cdecl GetCppText(char* str, int* strLength)
{
std::string text = "This is called from within the DLL.\0";
if (*strLength < text.length() || str == nullptr)
{
return;
}
memset((void*)str, 0, (*strLength) * sizeof(char));
strcpy_s(str, *strLength,text.c_str());
*strLength = text.length();
}
void __cdecl DLLPrint()
{
std::cout << "This is printed from inside DLLExample.dll.\n";
}
C#:
using System.Runtime.InteropServices;
namespace CSharpExampleUsingCpp
{
public partial class MainWindow : Window
{
const string PATH = "DLLExample.dll";
[DllImport(PATH, CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void GetCppText(byte[] str, out System.Int32 strLength);
....
private void CppInteropButton_Click(object sender, RoutedEventArgs e)
{
System.Int32 size = 256;
System.Byte[] str = new byte[size];
for (int i = 0; i < size; i++)
{
str[i] = (byte)'1';
}
GetCppText(str, out size);
string result = System.Text.Encoding.UTF8.GetString(str, 0, size);
CppInteropButtonTextBox.Text = result;
}
Although, rereading my solution of obtaining a string might not be the best way of doing it. You could probably marshal that thing to avoid all this stupid char* conversions. I probably had some good reason at that point in time when I wrote it. That should be much easier to google though.

Problems Interoping C++ (DLL) with C# console app

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.

System.AccessViolationException thrown by Visual C# on call to Lapack inside wrapped C++\CLI dll

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.

Call C++ method from .NET

I have a dll for third party app and normally communicate with it via Named Pipes. But NPs works only when the other app is started. Can I directly call a method from the dll to see its version.
C++ VS2012
#ifdef MTFST_EXPORTS
#define MTFST_API __declspec(dllexport)
#else
#define MTFST_API __declspec(dllimport)
#endif
#define LIBRARY_VERSION "3.0"
....
using namespace std;
MTFST_API char *__stdcall FST_LibraryVersion()
{
return LIBRARY_VERSION;
}
I tried the following code, but it doesn't work. .NET 4.
internal class Program
{
[DllImport("Library.dll")]
private static extern char[] FST_LibraryVersion();
private static void Main(string[] args)
{
Console.WriteLine(new string(FST_LibraryVersion()));
}
}
.NET arrays aren't compatible with raw pointers. You'll need to either use IntPtr or pass in a destination buffer to the function:
void __stdcall FST_LibraryVersion(char *dest)
{
strcpy(dest, LIBRARY_VERSION);
}
Obviously, you'll need to include checks to prevent buffer overflow.
Also, see PInvoke for C function that returns char *

CoCreateInstance() -858993460 (in comip.h) C++

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.

Categories