使用JNA通过DLL的导出函数访问回调的C / C ++结构

时间:2013-06-18 21:35:40

标签: dll callback jna

我有一个供应商提供的.DLL和一个在线API,我用来与一个无线电硬件进行交互;我正在使用JNA通过Java访问导出的函数(因为我不知道C / C ++)。我可以调用基本方法并成功使用一些API结构,但是我遇到了回调结构的问题。我遵循了TutorTutor指南here并尝试了Wall先生的权威指南here,但我无法为结构中正确设置的回调制定Java端语法。

我需要使用导出的功能

BOOL __stdcall SetCallbacks(INT32 hDevice,
                            CONST G39DDC_CALLBACKS *Callbacks, DWORD_PTR UserData);

此功能引用 C / C ++结构

typedef struct{
    G39DDC_IF_CALLBACK               IFCallback;
    //more omitted
} G39DDC_CALLBACKS;

...根据API有这些成员(注意这不是导出的函数):

VOID __stdcall IFCallback(CONST SHORT *Buffer, UINT32 NumberOfSamples,
                          UINT32 CenterFrequency, WORD Amplitude,
                          UINT32 ADCSampleRate, DWORD_PTR UserData);
//more omitted

我有一个G39DDCAPI.java,我在JNA的帮助下加载了DLL库并用Java重现了API导出的函数。对此工作的简单调用很好。

我还有一个G39DDC_CALLBACKS.java,我已经以适用于其他API结构的格式实现了上述C / C ++结构。这个回调结构是我不确定语法的地方:

import java.util.Arrays;
import java.util.List;
import java.nio.ShortBuffer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.BaseTSD.DWORD_PTR;
import com.sun.jna.win32.StdCallLibrary.StdCallCallback;

public class G39DDC_CALLBACKS extends Structure {
    public G39DDC_IF_CALLBACK               IFCallback;
    //more omitted

    protected List getFieldOrder() {
        return Arrays.asList(new String[] {
            "IFCallback","DDC1StreamCallback" //more omitted
        });
    }

    public static interface G39DDC_IF_CALLBACK extends StdCallCallback{
         public void invoke(ShortBuffer _Buffer,int NumberOfSamples,
                            int CenterFrequency, short  Amplitude,
                            int ADCSampleRate, DWORD_PTR UserData);
    }
}

编辑:我让我的论点更加安全,正如Technomage建议的那样。我仍然得到一个空指针异常,有几次尝试调用回调。由于我不确定上面关于回调结构的语法,我无法在下面的主要内容中查明我的问题。现在相关部分看起来像这样:

int NumberOfSamples=65536;//This is usually 65536.
ShortBuffer _Buffer = ShortBuffer.allocate(NumberOfSamples);
int CenterFrequency=10000000;//Specifies center frequency (in Hz) of the useful band
                             //in received 50 MHz wide snapshot. 
short Amplitude=0;//The possible value is 0 to 32767.
int ADCSampleRate=100;//Specifies sample rate of the ADC in Hz.
DWORD_PTR UserData = null;

G39DDC_CALLBACKS callbackStruct= new G39DDC_CALLBACKS();
lib.SetCallbacks(hDevice,callbackStruct,UserData);
     //hDevice is a handle for the hardware device used-- works in other uses
     //lib is a reference to the library in G39DDCAPI.java-- works in other uses
     //The UserData is a big unknown-- I don't know what to do with this variable
          //as a DWORD_PTR
callbackStruct.IFCallback.invoke(_Buffer, NumberOfSamples, CenterFrequency,
                                 Amplitude, ADCSampleRate,  UserData);

编辑NO 2:

我有一个回调工作,但我无法控制缓冲区。更令人沮丧的是,调用该方法的单个调用将导致多次运行自定义回调,通常具有多个输出文件(结果在运行之间变化很大)。我不知道是不是因为我没有在Java端正确分配内存,因为我无法释放C / C ++端的内存,或者因为我没有告诉Java访问缓冲区等的提示相关代码如下:

//before this, main method sets library, starts DDCs, initializes some variables...

//API call to start IF
System.out.print("Starting IF...             "+lib.StartIF(hDevice, Period)+"\n")
G39DDC_CALLBACKS  callbackStructure = new G39DDC_CALLBACKS();
callbackStructure.IFCallback = new G39DDC_IF_CALLBACK(){

    @Override 
    public void invoke(Pointer _Buffer,  int NumberOfSamples, int CenterFrequency,
            short Amplitude, int ADCSampleRate,   DWORD_PTR UserData  )  {

    //notification
        System.out.println("Invoked IFCallback!!");

        try {
    //ready file and writers
            File filePath = new File("/users/user/G39DDC_Scans/");
            if (!filePath.exists()){
                System.out.println("Making new directory...");
                filePath.mkdir();
            }

            String filename="Scan_"+System.currentTimeMillis();
            File fille= new File("/users/user/G39DDC_Scans/"+filename+".txt");
            if (!fille.exists()) {
                System.out.println("Making new file...");
                fille.createNewFile();
            }

            FileWriter fw = new FileWriter(fille.getAbsoluteFile());
    //callback body
            short[] deBuff=new short[NumberOfSamples];
            int offset=0;
            int arraySize=NumberOfSamples;

            deBuff=_Buffer.getShortArray(offset,arraySize); 
            for (int i=0; i<NumberOfSamples; i++){
                String str=deBuff[i]+",";
                fw.write(str);
            }
                fw.close();
        } catch (IOException e1) {
            System.out.println("IOException: "+e1);
        }
    }
};

lib.SetCallbacks(hDevice, callbackStructure,UserData);
System.out.println("Main, before callback invocation");

callbackStructure.IFCallback.invoke(s_Pointer, NumberOfSamples, CenterFrequency, Amplitude, ADCSampleRate, UserData);
System.out.println("Main, after callback invocation");

//suddenly having trouble stopping DDCs or powering off device; assume it has to do with dll using the functions above
    //System.out.println("StopIF:   " + lib.StopIF(hDevice));//API function returns boolean value
    //System.out.println("StopDDC2: " + lib.StopDDC2( hDevice, Channel));
    //System.out.println("StopDDC1: " + lib.StopDDC1( hDevice, Channel ));
    //System.out.println("test_finishDevice: " + test_finishDevice( hDevice, lib));

System.out.println("Program Exit");

//END MAIN METHOD

1 个答案:

答案 0 :(得分:0)

您需要扩展StdCallCallback,否则当本机代码尝试调用Java代码时,您可能会崩溃。

如果您看到带有_PTR的Windows类型的任何地方,则应使用PointerType - 带有JNA的平台包中包含DWORD_PTR和朋友的定义。

最后,您的G39DDC_IF_CALLBACK中不能有原始数组参数。您需要使用Pointer或NIO缓冲区;然后可以使用Pointer.getShortArray()通过提供所需的数组长度来提取short[]

修改

是的,你需要在回调结构中初始化你的回调字段,然后再将它传递给你的本机函数,否则你只是传递一个NULL指针,这会引起对Java或本机或两者的抱怨。

这是使用声明的回调函数接口的匿名实例创建回调所需的:

myStruct.callbackField = new MyCallback() {
    public void invoke(int arg) {
        // do your stuff here
    }
};