对 PInvoke 函数的调用'Test!DllCall::initDll'使堆栈不平衡



有点不寻常的问题。

我已经弄清楚,只有在通过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 %sn", name);
        exit(1);
    }
    printf ("%s loadedn", 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_GetDefaultJavaVMInitArgsn", dllname);
        exit (1);
    }
    if(!JNI_CreateJavaVM_func) {
        printf ("%s doesn't contain public JNI_CreateJavaVMn", 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 %dn", 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 %dn", result);
        exit(1);
    }
    printf ("JET RT initializedn");
    fflush (stdout);
}

/*
 * Look for class.
 */
jclass lookForClass (JNIEnv* env, char* name)
{
    jclass clazz = (*env)->FindClass (env, name);
    if (!clazz) {
        printf("Unable to find class %sn", name);
        exit(1);
    }
    printf ("Class %s foundn", 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 foundn");
        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 objectn");
        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 foundn");
        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 foundn");
        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 foundn");
    }
    (*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 foundn");
        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 heren");
    return 1;
}

亲切的问候火山灰

这通常发生在参数列表不正确或调用约定不匹配时。

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

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

您需要对所有导入都这样做。

另一件要检查的事情是参数列表是否匹配。你只展示了边界的一边,所以我们无法为你检查。如果你加上另一边,我们可能会发现一些东西。


在添加C代码后更新

我对你的代码有如下注释:

initDll的c#声明的参数匹配C声明,所以我很有信心,问题是你的C代码使用cdecl调用约定。c# P/调用默认为stdcall。更改其中一个调用约定,但不要同时更改两个!

您正在匹配jobjectUInt32。我不确定这一点,因为我不知道JNI。但是,我怀疑您应该返回jobject*并与IntPtr匹配。在我看来,jobject似乎是一个类类型,而不是你可以封送到c#的东西。

最后,其中一个方法返回string。这是行不通的。c#编组程序将尝试通过调用CoTaskMemFree来释放它。它会泄漏或爆炸,这取决于你使用的是哪个Windows版本。您应该返回IntPtr并使用Marshal.PtrToStringAnsi将其封送到c#字符串中。然后需要释放由JNI代码返回的内存。不知道你打算怎么做。当然,如果您的字符串确实是UTF-8,那么您需要复制到字节数组,然后使用Encoding。UTF8转换为c#字符串

最新更新