Sunday, December 13, 2009

Using JNA to query power status on Windows CE device - with a crash

Introduction

I am using the wonderful JNA library to use the Microsoft Remote API (RAPI) to access Windows Mobile Devices on Windows desktops. It allows you to connect to an attached mobile device using ActiveSync. This works all wonderfully easy thanks to the JNA library. I do not need to write any JNI code anymore. The RAPI offers a method to query the power status of the device which is somewhat limited. It does not return more detailed information about the battery type or voltage. There is a function available in the WinCE devices own libraries (coredll.dll) that allows me to query such informations (GetSystemPowerStatusEx2).

How to call methods directly on the device

To call a method on the device that is not exposed by RAPI itself, you can use the CeRapiInvoke function. So I implemented the following function according to the RAPI specs:
EXTERN_C int __declspec(dllexport) GetPowerInfos(DWORD cbInput, BYTE* pInput, DWORD* pcbOutput, BYTE** ppOutput, LPVOID pStream) {

 *pcbOutput = sizeof(SYSTEM_POWER_STATUS_EX2);
 *ppOutput = (SYSTEM_POWER_STATUS_EX2*)LocalAlloc(LPTR, *pcbOutput);

 return GetSystemPowerStatusEx2(*ppOutput, *pcbOutput, FALSE);
}
The spec requires to LocalAlloc the memory you return. The caller of CeRapiInvoke must then LocalFree the output memory. Now, you may wonder how that can work?

Memory management in CeRapiInvoke

The way I understand how it works is like this: For the pInput parameter you have to call LocalAlloc on the Windows machine and CeRapiInvoke will copy the content of this memory block into a newly created WindowsCE readable block of memory and hand it over to GetPowerInfos function as pInput. It is of course not the same memory pointer that I handed in on the Win32 side, it just contains the same data as I handed in. CeRapiInvoke also calls LocalFree on the pInput that I handed in. The same way around with the ppOutput parameter. I have to allocate it using LocalAlloc on the WindowsCE device. CeRapiInvoke will then copy the content of my returned buffer into a desktop process accessible block of memory that it allocates with LocalAlloc on the Windows machine. I, the caller of CeRapiInvoke am then responsible for calling LocalFree on the ppOutput that CeRapiInvoke created for me. Thats the theory. Lets see how to get the returned ppOutput into the Java world using JNA.

Calling CeRapiInvoke from Java using JNA

We define this prototype in our JNA library interface for RAPI.
public interface Rapi extends W32API {

    static Rapi instance = (Rapi) Native.loadLibrary("rapi", Rapi.class, DEFAULT_OPTIONS); //$NON-NLS-1$

    HRESULT CeRapiInvoke(String dllPath, String functionName, int cbInput, Pointer pInput, IntByReference outBuffer, PointerByReference outBufferSize, PointerByReference ppIRAPIStream, int dwReserved);
}
We also declare the SYSTEM_POWER_STATUS_EX2 structure:
static class SystemPowerStatusEx extends Structure {
 public SystemPowerStatusEx() {
 }

 protected SystemPowerStatusEx(final Pointer p) {
  super(p);
 }

 public byte ACLineStatus;
 public byte BatteryFlag;
 public byte BatteryLifePercent;
 public byte Reserved1;

 public int BatteryLifeTime;
 public int BatteryFullLifeTime;
 public byte Reserved2;
 public byte BackupBatteryFlag;
 public byte BackupBatteryLifePercent;
 public byte Reserved3;
 public int BackupBatteryLifeTime;

 public int BackupBatteryFullLifeTime;
}

class SystemPowerStateEx2 extends Structure {
 public SystemPowerStateEx2(final Pointer p) {
  super(p);
  read();
 }

 public SystemPowerStatusEx base;

 public int BatteryVoltage;
 public int BatteryCurrent;
 public int BatteryAverageCurrent;
 public int BatteryAverageInterval;
 public int BatterymAHourConsumed;
 public int BatteryTemperature;
 public int BackupBatteryVoltage;
 public byte BatteryChemistry;
}
Now we can call our DLLs exported function:
final PointerByReference outBuffer = new PointerByReference();

try {
 final IntByReference outBufferSize = new IntByReference();
 Rapi.instance.CeRapiInvoke("powerex.dll", "GetPowerInfos", 0, null, outBufferSize, outBuffer, null, 0);
 return new SystemPowerStateEx2(outBuffer.getValue());
} finally {
 Kernel32.INSTANCE.LocalFree(outBuffer.getValue());
}
This code has been simplified and usually contains return value checking and error handling. However it returns the power infos we would suspect. I also ommited the initialization of RAPI here.

The LocalFree crash problem

Calling this in a loop will eventually lead to an Access Violation and a crash of the VM. If I comment out the LocalFree call in the finally block, the program runs for hours. But it then leaks memory of course. I have played with different output parameters, pointer handling and more. Without finding a solution to the crashes. Enabling JNAs own VM crash protected reveals, that it sometimes causes an Access Violation inside the native code. It almost seems like the native code overwrites some memory internally. Most likely inside RAPI itself. However calling the same remote function from a C++ Win32 program in a loop does not cause any memory corruption or crash. I hope someone of the community has maybe an idea whats going on here? If not, you got at least a short introduction on how to use JNA and the Microsoft RAPI ;)

2 comments:

  1. I've been using JNA heavily lately with quite complex C++ DLL calls and I've been too very satisfied with it.

    ReplyDelete