Test!DllCall::initDll'使堆栈不平衡
本文关键字:堆栈 不平衡 DllCall initDll Test | 更新日期: 2023-09-27 18:09:41
有点不寻常的问题。
我已经弄清楚,只有在通过visual studio运行程序时才会抛出错误。如果我编译应用程序并运行编译后的程序,它就可以正常工作。你知道是什么导致的吗?
我有一个c#类,它通过JNI调用Java DLL(通过excelsior jet编译)中的方法。
当我编译并运行c#类和可执行文件时,一切都很好。现在我已经将c#类构建为DLL,并试图从另一个类调用它。
在这一点上,我得到以下错误消息:调用PInvoke函数Test!dll::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/调用默认为stdcall
。更改其中一个调用约定,但不要同时更改两个!
您正在匹配jobject
和UInt32
。我不确定这一点,因为我不知道JNI。但是,我怀疑您应该返回jobject*
并与IntPtr
匹配。在我看来,jobject
似乎是一个类类型,而不是你可以封送到c#的东西。
最后,其中一个方法返回string
。这是行不通的。c#编组程序将尝试通过调用CoTaskMemFree
来释放它。它会泄漏或爆炸,这取决于你使用的是哪个Windows版本。您应该返回IntPtr
并使用Marshal.PtrToStringAnsi
将其封送到c#字符串中。然后需要释放由JNI代码返回的内存。不知道你打算怎么做。当然,如果您的字符串确实是UTF-8,那么您需要复制到字节数组,然后使用Encoding。UTF8转换为c#字符串