I'm integrating with an external DLL. One of the functions has a callback:
typedef int (*PROC_DOWNLOAD)(DWORD DataType, DWORD Size, BYTE *pData, DWORD UserDefined);
WORD CALL StartDownload (BYTE CplNum, PROC_DOWNLOAD pfunc, DWORD reserved, DWORD UserDefined);
So the callback passes data, of length SIZE, into pData.
My Callback implementation is as follows:
private static class StartDownloadCallback extends Callback
{
private final UInt32 dataType = DWORD();
private final UInt32 size = DWORD();
private final PrimitiveArray pDataArray = new PrimitiveArray(Int8.class, 0);
private final ExternalArrayPointer pData = new ExternalArrayPointer(pDataArray);
private final UInt32 userDefined = DWORD();
private final Int32 returnValue = INT();
private StartDownloadCallback()
{
init(new Parameter[] {dataType, size, pData, userDefined}, returnValue);
}
public void callback()
{
System.out.println("Callback invoked.");
// extract data
returnValue.setValue(0);
}
}
The first time that it gets called, it receives the correct data (extracted from pData).
However, after that, I get a VM error reported by Windows, with Exception Code c0000005.
Am I taking the correct approach to this? I've looked at the documentation, samples, and forum, and I still wasn't quite sure what to do.
Note that I also tried the same technique as in the sample file CallbackWithFillExternalArraySample.java - i.e. pass in a Pointer.Void instance, and use castTo() on a Pointer to a PrimitiveArray. This had
the same effect as above - worked first time, then failed after that.
Thanks,
Calum
Hi Calum,
The declaration of your callback seems to be correct. But you have missed quite important fact in your description of the problem: you did not specify how you read the data in the .callback() method. Or it's just empty?
My point is that callbacks are working in general (you can simply verify that by another examples or for example by WinPack), therefore there must be something in your code (in the callback() method) or in a native code which produces such c0000005 exception (a null pointer exception) on a native side.
So if you can provide more details on this or a test case which reproduces this issue then we can provide a more precise solution.
-Serge
Hi Serge
In my callback, where the sample code which I posted had "// extract data", I actually had the following code:
System.out.println("dataType = " + dataType.getValue());
System.out.println("size = " + size.getValue());
pData.readArray((int)size.getValue());
System.out.println("pData = " + HexStrings.toHexString(pDataArray.getBytes()));
System.out.println("userDefined = " + userDefined.getValue());
The callback was called once, and my print statements indicated that correct values were passed in.
However, the callback was not called again, and the JVM crashed.
From what you say, it would appear that I'm using the callback correctly - and it does actually work the first time.
Is there anything related to JNIWrapper that I'm doing, or not doing, that might cause the JVM to crash subsequently? Or do I really need to go and look at the details of the DLL function StartDownload itself, to check that I'm using that correctly?
BTW, the DLL is a third-party DLL, so I don't have access to its source etc.
Thanks,
Calum
Hi again Serge
When I was writing my code, I wasn't quite sure about how to initialise pDataArray. It appeared that "new PrimitiveArray(Int8.class, 0)" worked - for the first callback at least, but I wasn't quite clear about how/why this was correct; for example, it wasn't clear to me as to how to set the length.
Can pDataArray and pData be directly reused by subsequent callbacks? Or should I be doing something to "reinitialise" them?
For example, if pDataArray has been filled with a particular size from the first callback, will a subsequent callback - which will use a different size - work successfully?
Could this be a reason for the failure?
Thanks,
Calum
Hi Calum,
It's still difficult to say what might cause such exception. In order to verify that this is not a problem of callback() method implementation you need to do the following:
1) use try/catch for handling any exception in callback method;
2) check the pData pointer for null before reading data from it, for example:
// if pointer is valid
if (!pData.isNull()) {
pData.readArray((int)size.getValue());
System.out.println("pData = " + HexStrings.toHexString(pDataArray.getBytes()));
System.out.println("userDefined = " + userDefined.getValue());
}
-Serge
Hi,
Yes, such declaration of an array of unknown size is quite correct and all callback parameters can be reused for all subsequent calls. So, you do not need to reinitialize it every time.
Though please try my suggestion that I posted before.
-Serge
Hi Serge
Thanks again for the quick responses.
I tried your suggestions, but pData.isNull() returns false, and I can correctly access a valid byte array from pData using readArray().
Also, my callback method completes successfully, so no exceptions are getting thrown. In general, all the parameters contain valid data which I can print out.
For your information, I'm also checking with the suppliers of the DLL that I'm using it correctly. When a problem such as this arises, it can be tricky to work out whether it the usage of JNIWrapper or of the DLL which is incorrect. (Or if the DLL itself is buggy...).
Thanks,
Calum
Hi
We still haven't resolved this...
One of my colleagues has checked that the callback works OK directly from C code, and it works fine.
Another thing that comes to my mind: does it matter what thread the callback is made on - for example, is there any assumption about it being the same thread each time?
I'll get my colleague to check what happens in C, but do you think this could be a possible cause of the problem?
Thanks,
Calum
Hi,
We have not seen such problem with callbacks before. Please send me a simple test case (library plus java code) that reproduces it. I will investigate this issue and let you know the solution.
-Serge
I've attached C source for a DLL which does a callback (which a similar API to the callback I'm actually using), along with another C file which demonstrates the callback working correctly from C. (There are also built versions - a DLL and EXE.)
I've also attached a Java file which exercises it using JNIWrapper. Just as with my original example, the callback is called correctly the first time. After my debug logging from the callback appears, a C++ dialog pops up with an error, and the callback is not called again before the program exits.
I should point out that my actual usage of the original DLL fails at a similar point, but it was a VM crash. I presume that this is just a different symptom of the same underlying problem.
I should also note that we are using the latest version of JNIWrapper - 3.6.1.
I look forward to hearing back from you.
Thanks,
Calum
Hi,
Thanks for the sample code and additional information. We will investigate this issue and send you the solution soon.
-Serge
Hi,
I found the problem. It was incorrect calling convention of the MyCallback class ![]()
Here is the fix:
private MyCallback() {
init(new Parameter[] {type, size, pData, userDefined}, returnValue);
setCallingConvention(Function.CDECL_CALLING_CONVENTION); // <- just add this line
}
Please try this solution in your real application and let me know the results.
-Serge