При вызове функции PInvoke 'Test! DllCall :: initDll' разбалансирован стек - PullRequest
4 голосов
/ 27 сентября 2011

Немного необычный вопрос.

У меня получилось, что ошибка выдается только при запуске программы через visual studio. Если я компилирую приложение и запускаю скомпилированную программу, она работает нормально. Есть идеи, что вызвало бы это?

У меня есть класс C #, который вызывает метод в Java DLL (скомпилирован через Excelsior Jet) через JNI.

Когда я компилирую и запускаю класс 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;
}

С уважением

Ash

1 Ответ

6 голосов
/ 28 сентября 2011

Это обычно происходит, когда у вас неверный список параметров или не соответствует соглашению о вызовах.

Вы действительно должны знать, что такое соглашение о вызовах для другой DLL.Если это cdecl, тогда вы изменяете ваш P / invoke на:

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

Это необходимо сделать для всех импортов.

Другая вещь, которую нужно проверить, - соответствие ваших списков параметров,Вы указали только одну сторону границы, поэтому мы не можем это проверить.Если вы добавили другую сторону, мы могли бы что-то заметить.


Обновлено после добавления кода C

У меня есть следующие комментарии к вашему коду:

Параметры для объявления C # initDll соответствуют объявлению C, поэтому я вполне уверен, что проблема в том, что ваш код C использует cdecl соглашение о вызовах.По умолчанию C # P / invoke stdcall.Измените соглашение о вызовах в одном или другом, но не в обоих!

Вы соответствуете jobject с UInt32.Я не уверен в этом, так как я не знаю JNI.Тем не менее, я подозреваю, что вы должны вернуть jobject* и соответствовать IntPtr.Мне кажется, что jobject - это тип класса, а не то, что вы можете использовать в C #.

Наконец, один из ваших методов возвращает string.Это просто не сработает.Маршаллер C # попытается освободить его с помощью вызова CoTaskMemFree.Это утечка или бомба, в зависимости от того, какая у вас версия WindowsВы должны вернуть IntPtr и использовать Marshal.PtrToStringAnsi, чтобы объединить его в строку C #.Затем вам нужно освободить память, возвращаемую кодом JNI.Не уверен, как вы планируете это сделать.Конечно, если ваши строки действительно UTF-8, вам нужно скопировать их в байтовый массив, а затем использовать Encoding.UTF8 для преобразования в строку C #.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...