gpt4 book ai didi

c# - 对 PInvoke 函数 'Test!DllCall::initDll' 的调用使堆栈失衡

转载 作者:太空狗 更新时间:2023-10-30 00:56:30 25 4
gpt4 key购买 nike

有点不寻常的问题。

我发现只有在通过 visual studio 运行程序时才会抛出错误。如果我编译应用程序并运行编译后的程序,它就可以正常工作。有什么想法会导致这种情况吗?

我有一个 C# 类,它通过 JNI 调用 Java DLL(通过 excelsior jet 编译)中的方法。

当我编译并运行 C# 类和可执行文件时,一切正常。现在我已经将 C# 类构建为 DLL,并试图从另一个类调用它。

此时我收到以下错误消息:对 PInvoke 函数“Test!DllCall::initDll”的调用使堆栈失衡。这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。检查 PInvoke 签名的调用约定和参数是否与目标非托管签名匹配。

谁能解释为什么我会收到这个问题以及如何解决它?

如果您需要更多代码/信息,请告诉我

这是 C# 代码:

    using System;
using System.Runtime.InteropServices;
using System.Text;

public class DllCall
{
[DllImport("Stubs")]
public static extern int initDll(String userDllName);

[DllImport("Stubs")]
public static extern void finalizeDll();

[DllImport("Stubs")]
public static extern UInt32 newClassInstance();

[DllImport("Stubs")]
public static extern int invokeStaticMethod();

[DllImport("Stubs")]
public static extern String request(UInt32 hClassInst, String input);

[DllImport("Stubs")]
public static extern void close();

[DllImport("Stubs")]
public static extern void voidtest(UInt32 hClassInst);


}


public class Test
{
public UInt32 hClass;


public Test()
{
//when compiled as a DLL this line throws the error.
int rc = DllCall.initDll("dllClass.dll");
Console.Write("---> initDll() rc = {0}\n", rc);

hClass = DllCall.newClassInstance();
Console.Write("---> hClass = {0}\n", hClass);
}

// When compiled as an executable this method runs fine.
public static void Main(string[] args)
{
int rc = DllCall.initDll("dllClass.dll");
string rs;
Console.Write("---> initDll() rc = {0}\n", rc);

UInt32 hClass = DllCall.newClassInstance();
Console.Write("---> hClass = {0}\n", hClass);

rs = DllCall.request(hClass, "moo");
Console.Write("---> request() rs = {0}\n", rs);

DllCall.close();

DllCall.finalizeDll();
}



public string request( string xmlInput )
{
string rs;
rs = DllCall.request(hClass, xmlInput);
Console.Write("---> request() rs = {0}\n", rs);
return rs;
}

public void finalizeDll()
{
DllCall.finalizeDll();
}

public void closeConnection()
{
DllCall.close();
}
}

我知道这是很多代码,但根据要求,这是 C Dll 中的代码......

#include <jni.h>
#include <windows.h>

JNIEnv *env;
JavaVM *jvm;
HANDLE hUserDll;
jclass jClass;
char* dllname;

/*
* Load dll.
*/
HANDLE loadDll(char* name)
{
HANDLE hDll = LoadLibrary (name);

if (!hDll) {
printf ("Unable to load %s\n", name);
exit(1);
}

printf ("%s loaded\n", name);

return hDll;
}

jint (JNICALL * JNI_GetDefaultJavaVMInitArgs_func) (void *args);
jint (JNICALL * JNI_CreateJavaVM_func) (JavaVM **pvm, void **penv, void *args);



/*
* Initialize JET run-time.
*/
void initJavaRT(HANDLE myDllHandle, JavaVM** pjvm, JNIEnv** penv)
{
int result;
JavaVMInitArgs args;

JNI_GetDefaultJavaVMInitArgs_func =
(jint (JNICALL *) (void *args))
GetProcAddress (myDllHandle, "JNI_GetDefaultJavaVMInitArgs");

JNI_CreateJavaVM_func =
(jint (JNICALL *) (JavaVM **pvm, void **penv, void *args))
GetProcAddress (myDllHandle, "JNI_CreateJavaVM");

if(!JNI_GetDefaultJavaVMInitArgs_func) {
printf ("%s doesn't contain public JNI_GetDefaultJavaVMInitArgs\n", dllname);
exit (1);
}

if(!JNI_CreateJavaVM_func) {
printf ("%s doesn't contain public JNI_CreateJavaVM\n", dllname);
exit (1);
}

memset (&args, 0, sizeof(args));
args.version = JNI_VERSION_1_2;

result = JNI_GetDefaultJavaVMInitArgs_func(&args);
if (result != JNI_OK) {
printf ("JNI_GetDefaultJavaVMInitArgs() failed with result %d\n", result);
exit(1);
}

/*
* NOTE: no JVM is actually created
* this call to JNI_CreateJavaVM is intended for JET RT initialization
*/
result = JNI_CreateJavaVM_func (pjvm, (void **)penv, &args);
if (result != JNI_OK) {
printf ("JNI_CreateJavaVM() failed with result %d\n", result);
exit(1);
}

printf ("JET RT initialized\n");
fflush (stdout);
}


/*
* Look for class.
*/
jclass lookForClass (JNIEnv* env, char* name)
{
jclass clazz = (*env)->FindClass (env, name);

if (!clazz) {
printf("Unable to find class %s\n", name);
exit(1);
}

printf ("Class %s found\n", name);
fflush (stdout);

return clazz;
}


int initDll(char* userDllName)
{
jClass = NULL;
hUserDll = loadDll(userDllName);
dllname = userDllName;
initJavaRT(hUserDll, &jvm, &env);
jClass = lookForClass(env, "mooMain/mooGeminiX3/mooGeminiX3IFX");
return jClass ? 1 : 0;
}

/** finalizeDll() - stop Java VM
*/
void finalizeDll ()
{
(*jvm)->DestroyJavaVM (jvm);
FreeLibrary((HMODULE)hUserDll);
hUserDll = NULL;
jClass = NULL;
}


jobject newClassInstance()
{

jmethodID MID_init;
jobject obj;
jstring name;
jobjectArray ret;
jclass sclass;
jobjectArray arr;

MID_init = (*env)->GetMethodID (env, jClass, "<init>", "([Ljava/lang/String;)V");
if (!MID_init) {
printf("Error: dllClass.<init>() not found\n");
return NULL;
}
sclass = (*env)->FindClass(env, "java/lang/String");
arr = (*env)->NewObjectArray(env, 6, sclass, NULL);


name = (*env)->NewStringUTF(env,"@C:\\Users\\Ash\\Desktop\\moo-Test\\moo-Test\\mooRoot.cfg");
(*env)->SetObjectArrayElement(env,arr,0,name);
name = (*env)->NewStringUTF(env,"-cfg");
(*env)->SetObjectArrayElement(env,arr,1,name);
name = (*env)->NewStringUTF(env,"C:\\Users\\Ash\\Desktop\\moo-CVS\\moo-PCB\\Application\\Configuration\\Linear\\Application\\moo.cfg");
(*env)->SetObjectArrayElement(env,arr,2,name);
name = (*env)->NewStringUTF(env,"-startupLog");
(*env)->SetObjectArrayElement(env,arr,3,name);
name = (*env)->NewStringUTF(env,"C:\\Users\\Ash\\Desktop\\moo-Test\\moo-Test\\Log\\mooStartup.log");
(*env)->SetObjectArrayElement(env,arr,4,name);
name = (*env)->NewStringUTF(env,"-geminiX3");
(*env)->SetObjectArrayElement(env,arr,5,name);












obj = (*env)->NewObject(env, jClass, MID_init, arr);
if (!obj) {
printf("Error: failed to allocate an object\n");
return NULL;
}
return obj;
}

const char* request(jobject obj, char* input )
{
jstring inputString;
jstring outputString;
const char *nativeString;


jmethodID mID = (*env)->GetMethodID (env, jClass, "request", "(Ljava/lang/String;)Ljava/lang/String;");
if (!mID){
printf("\nError: dllClass.request() not found\n");
return 0;
}
printf("here"); fflush(stdout);
inputString = (*env)->NewStringUTF(env, input);
printf("here2"); fflush(stdout);

outputString = (*env)->CallObjectMethod(env, obj, mID, inputString);

nativeString = (*env)->GetStringUTFChars(env, outputString, 0);


return nativeString;
}

void voidtest(jobject obj )
{/*
jmethodID mID = (*env)->GetMethodID (env, jClass, "request", "([Ljava/lang/String;)[Ljava/lang/String;");

if (!mID){
printf("\nError: dllClass.request() not found\n");
return 0;
}

char inputString[] = (*env)->NewStringUTF(env, "Moo");

(*env)->CallVoidMethod(env, obj, mID, inputString);*/
}

void close()
{
jmethodID mID = (*env)->GetMethodID (env, jClass, "close", "()V");
if (!mID){
printf("\nError: dllClass.close() not found\n");
}

(*env)->CallVoidMethod(env,jClass, mID);
}


int invokeStaticMethod()
{
jmethodID MID_init;
jobject obj;
jstring name;
jobjectArray ret;
jclass sclass;
jobjectArray arr;
jmethodID mID = (*env)->GetStaticMethodID(env, jClass, "start", "([Ljava/lang/String;)V");

if (!mID){
printf("\nError: dllClass.sfstart() not found\n");
return 0;
}

sclass = (*env)->FindClass(env, "java/lang/String");
arr = (*env)->NewObjectArray(env, 5, sclass, NULL);


name = (*env)->NewStringUTF(env,"@C:\\Users\\Ash\\Desktop\\moo-Test\\moo-Test\\mooRoot.cfg");
(*env)->SetObjectArrayElement(env,arr,0,name);
name = (*env)->NewStringUTF(env,"-cfg");
(*env)->SetObjectArrayElement(env,arr,1,name);
name = (*env)->NewStringUTF(env,"C:\\Users\\Ash\\Desktop\\moo-CVS\\moo-PCB\\Application\\Configuration\\Linear\\Application\\moo.cfg");
(*env)->SetObjectArrayElement(env,arr,2,name);
name = (*env)->NewStringUTF(env,"-startupLog");
(*env)->SetObjectArrayElement(env,arr,3,name);
name = (*env)->NewStringUTF(env,"C:\\Users\\Ash\\Desktop\\moo-Test\\moo-Test\\Log\\mooStartup.log");
(*env)->SetObjectArrayElement(env,arr,4,name);



(*env)->CallStaticVoidMethod(env,jClass, mID, arr);
printf("\nGot to here\n");

return 1;
}

亲切的问候

最佳答案

当您的参数列表不正确或调用约定不匹配时,通常会发生这种情况。

你真的应该知道另一个 DLL 是什么调用约定。如果它是 cdecl 那么您将 P/invoke 更改为:

[DllImport("Stubs", CallingConvention=CallingConvention.Cdecl)]

您需要为所有导入执行此操作。

要检查的另一件事是您的参数列表是否匹配。您只显示了边界的一侧,因此我们无法为您检查。如果您添加了另一面,那么我们可能会发现一些东西。


在添加 C 代码后更新

我对您的代码有以下评论:

initDll 的 C# 声明的参数与 C 声明相匹配,因此我非常确信问题在于您的 C 代码使用了 cdecl 调用约定。 C# P/invoke 默认为 stdcall。更改其中一个调用约定,但不能同时更改两个调用约定!

您正在将 jobjectUInt32 匹配。我不确定这一点,因为我不知道 JNI。但是,我怀疑您应该返回 jobject* 并与 IntPtr 匹配。在我看来,jobject 是一种类类型,而不是您可以编码为 C# 的类型。

最后,您的方法之一返回一个字符串。那是行不通的。 C# 编码器将尝试通过调用 CoTaskMemFree 来释放它。这会泄漏或爆炸,具体取决于您拥有的 Windows 版本。您应该返回一个 IntPtr 并使用 Marshal.PtrToStringAnsi 将其编码为 C# 字符串。然后您需要释放 JNI 代码返回的内存。不确定你打算如何做到这一点。当然,如果您的字符串确实是 UTF-8,那么您需要复制到字节数组,然后使用 Encoding.UTF8 转换为 C# 字符串。

关于c# - 对 PInvoke 函数 'Test!DllCall::initDll' 的调用使堆栈失衡,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7567590/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com