montyBurns80
montyBurns80

Reputation: 15

Invoking DLL Library with Java JNA

I have an SDK with DLL libraries. I am invoking the libraries from Java 1.8 and JNA v3.0.9 I am having trouble defining the structures

The SDK documentation states:

Call NET_DVR_STDXMLConfig to pass through the request URL: GET /ISAPI/AccessControl/UserInfo/capabilities?format=json for getting the person management capability to know the configuration details and notices.

NET_DVR_STDXMLConfig API call definiton is:

BOOL NET_DVR_STDXMLConfig(
LONG                         lUserID,
NET_DVR_XML_CONFIG_INPUT     *lpInputParam,
NET_DVR_XML_CONFIG_OUTPUT    *lpOutputParam
);

lpInputParam
[IN] Input parameters, refer to the structure NET_DVR_XML_CONFIG_INPUT for details.

lpOutputParam
[IN/OUT] Output parameters, refer to the structure NET_DVR_XML_CONFIG_OUTPUT for details.

Definition of NET_DVR_XML_CONFIG_INPUT structure:

struct{
DWORD       dwSize;
void        *lpRequestUrl;
DWORD       dwRequestUrlLen;
void        *lpInBuffer;
DWORD       dwInBufferSize; 
DWORD       dwRecvTimeOut;
BYTE        byForceEncrpt;
BYTE        byNumOfMultiPart;  
BYTE        byRes[30];
}

dwSize
Structure size.

lpRequestUrl
Request URL (command) for implement different functions, and it is in string format.

dwRequestUrlLen
Request URL size.

lpInBuffer
Buffer for storing input parameters (request messages), see the input content details structure in NET_DVR_MIME_UNIT.

dwInBufferSize
Input buffer size.

dwRecvTimeOut
Receiving timeout, unit: ms, 0-5000ms (default).

byForceEncrpt
Whether to enable force encryption (the messages will be encrypted by AES algorithm for transmission): 0-no, 1-yes.

byNumOfMultiPart
Number of message segments: 0-invalid; other values-number of message segments, which is transmitted by the parameter lpInBuffer in the structure NET_DVR_MIME_UNIT.

The definition of NET_DVR_MIME_UNIT structure is

struct{
  char     szContentType[32];
  char     szName[MAX_FILE_PATH_LEN/*256*/];
  char     szFilename[MAX_FILE_PATH_LEN/*256*/];
  DWORD    dwContentLen;
  char*    pContent;
  BYTE     byRes[16];
}

szContentType
Content type (corresponds to Content-Type field in the message), e.g., text/json. text/xml, and so on. The content format must be supported by HTTP.

zName
Content name (corresponds to name field in the message), e.g., name="upload".

szFilename
Content file name (corresponds to filename field in the message), e.g., filename="C:\Users\test\Desktop\11.txt".

dwContentLen
Content size.

pContent
Data point.

And this is what I have done in Java so far: DLL call and Structure definition

//DLL native call
boolean NET_DVR_STDXMLConfig(int lUserID, Pointer lpInputParam, Pointer lpOutputParam);

public static class NET_DVR_XML_CONFIG_INPUT extends Structure
{
    public int      dwSize;
    public Pointer  lpRequestUrl;
    public int      dwRequestUrlLen;
    public Pointer  lpInBuffer;
    public int      dwInBufferSize;
    public int      dwRecvTimeOut;
    public byte     byForceEncrpt;
    public byte     byNumOfMultiPart;
    public byte[]   byRes = new byte[30];
    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList("dwSize", "lpRequestUrl", "dwRequestUrlLen", "lpInBuffer", "dwInBufferSize", 
                "dwRecvTimeOut","byForceEncrpt","byNumOfMultiPart", "byRes");
    }       
}

public static class NET_DVR_XML_CONFIG_OUTPUT extends Structure {
    public int dwSize;
    public Pointer lpOutBuffer;
    public int dwOutBufferSize;
    public int dwReturnedXMLSize;
    public Pointer lpStatusBuffer;
    public int dwStatusSize;
    public byte[] byRes = new byte[32];
    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList("dwSize", "lpOutBuffer",
                "dwOutBufferSize", "dwReturnedXMLSize", "lpStatusBuffer",
                "dwStatusSize", "byRes");
    }
}

public static class NET_DVR_MIME_UNIT extends Structure{
    public byte[] szContentType = new byte[32];
    public byte[] szName = new byte[MAX_FILE_PATH_LEN];
    public byte[] szFilename = new byte[MAX_FILE_PATH_LEN];
    public int dwContentLen;
    public String pContent;
    public byte[] byRes = new byte[16];

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList("szContentType", "szName", "szFilename", "dwContentLen", "pContent","byRes");
    }               
}   

And this is my library invocation

NET_DVR_XML_CONFIG_INPUT    struInput = new NET_DVR_XML_CONFIG_INPUT();
NET_DVR_XML_CONFIG_OUTPUT   struOuput = new NET_DVR_XML_CONFIG_OUTPUT();
String strInput = new String("GET /ISAPI/AccessControl/UserInfo/capabilities?format=json\r\n\"");
byte[] byInput = strInput.getBytes();
System.arraycopy(byInput, 0, struInput.lpRequestUrl, 0, byInput.length);        // Nullpointer exception
struInput.dwRequestUrlLen = byInput.length;
NET_DVR_MIME_UNIT mimeUnit = new NET_DVR_MIME_UNIT();
//mimeUnit.pContent = new Memory(MAX_XML_CONFIG_LEN);                       // Dont know what to put in here
struInput.lpInBuffer = mimeUnit.getPointer();
struOuput.dwOutBufferSize = MAX_XML_CONFIG_LEN;
struOuput.dwStatusSize = struOuput.dwOutBufferSize;     

struInput.write();  
struOuput.write();

NET_DVR_STDXMLConfig(userID,struInput.getPointer(), struOuput.getPointer());    // Getting a generic "parameters not valid" error msg

I am not sure about my structures. Im gettign a nullpointer when setting the requestURL Also my invocation returns a generic "parameters not valid" error message.

Any help much appreciated.

Thanks in advance

Upvotes: 2

Views: 619

Answers (1)

Daniel Widdis
Daniel Widdis

Reputation: 9130

Welcome to StackOverflow.

Your structure mappings look good. You didn't list the SDK structure or docs for NET_DVR_XML_CONFIG_OUTPUT so I can't help much with what you expect there.

Your NET_DVR_STDXMLConfig method call could be improved. While it works passing raw Pointer values, it would be better to pass the appropriate structures there instead. JNA will automatically treat them as their reference pointers in the method arguments, and handle the read() and write() automatically. So this is a better mapping:

boolean NET_DVR_STDXMLConfig(int lUserID, 
    NET_DVR_XML_CONFIG_INPUT lpInputParam, NET_DVR_XML_CONFIG_OUTPUT lpOutputParam);

Not sure why you're doing new String("string"); instead of just using the original "string".

You're getting the NPE on the arraycopy because you haven't created an array for struInput.lpRequestUrl; it's simply a Pointer. Rather than an array copy, you probably want to define that String buffer as a Memory object, and then copy the string into it, e.g.,

Memory requestUrl = new Memory(strInput.length + 1); // add space for null
requestUrl.clear(); // ensure last byte is null
requestUrl.setString(0, strInput); // set all the other bytes
struInput.lpRequestUrl = requestUrl;
struInput.dwRequestUrlLen = requestUrl.size();

Passing the Structure pointer for the other argument is good but you need to set the struInput.dwInBufferSize to match that structure's size(). As written it looks like you're passing a size of 0 (the default int) in the input structure for that buffer size, which may be the source of the invalid argument. Or it's possible you haven't fully initialized the NET_DVR_MIME_UNIT structure, as you've left the String null and didn't set its length.

You will need to define your own buffers similar to the above for the OUTPUT structure, as well, and since you don't know the full size of the strings, you would put a max value (I assume MAX_XML_CONFIG_LEN based on what you're trying to do but I haven't read the DLL docs) which you'll have to find the value of of in your dll's header files.

One final note -- you should probably try to use the most recent version of JNA, as there are always improvements and bug fixes.

Upvotes: 1

Related Questions