A call to PInvoke function 'Test!DllCall::initDll' has unbalanced the stack
Bit of an unusual question.
I have worked out that the error is only thrown when running the program via visual studio. If I compile the application and run the compiled program it works fine. Any ideas what would cause this?
I have a C# class that calls a method in a Java DLL ( compiled through excelsior jet ) via JNI.
When I compile and run the C# class an an executable everything works fine. Now I have built the C# class as a DLL and am trying to call it from another class.
At this point I get the followign error message: A call to PInvoke function 'Test!DllCall::initDll' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
Could anyone explain why Im getting this and how to fix it?
Please let me know if you need anymore code/information
Heres the C# code:
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();
}
}
I know this is a lot of code but as requested heres the code in the 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()
{
jmeth开发者_运维知识库odID 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;
}
Kind Regards
Ash
This usually happens when you have an incorrect parameter list or mismatching calling convention.
You really ought to know what calling convention the other DLL is. If it is cdecl
then you change your P/invoke to:
[DllImport("Stubs", CallingConvention=CallingConvention.Cdecl)]
You need to do this for all imports.
The other thing to check is that your parameter lists match. You only showed one side of the boundary so we can't check that for you. If you added the other side then we may be able to spot something.
Updated following addition of C code
I have the following comments on your code:
The parameters for the C# declaration of initDll
match the C declaration so I'm pretty confident that the issue is that your C code uses cdecl
calling convention. The C# P/invoke defaults to stdcall
. Change the calling convention in one or the other, but not both!
You are matching jobject
with UInt32
. I'm not sure about this since I don't know JNI. However, I suspect you should be returning jobject*
and matching with IntPtr
. It looks to me as though jobject
is a class type and not something that you can marshal to C#.
Finally, one of your methods returns a string
. That just won't work. The C# marshaller will attempt to deallocate it with a call to CoTaskMemFree
. That will leak or bomb, dependent on which Windows version you have. You should return an IntPtr
and use Marshal.PtrToStringAnsi
to marshal it into a C# string. Then you need to deallocate the memory returned by the JNI code. Not sure how you plan to do that. Of course, if your strings are really UTF-8 then you would need to copy to a byte array and then use Encoding.UTF8 to convert to a C# string.
精彩评论