0

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

编辑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
4

1 回答 1

0

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

任何你看到带有 的 Windows 类型的地方_PTR,你都应该使用PointerType- 带有 JNA 的平台包包括对DWORD_PTR和朋友的定义。

最后,您的G39DDC_IF_CALLBACK. 您需要使用PointerNIO 缓冲区;Pointer.getShortArray()然后可以short[]通过提供所需的数组长度来提取 .

编辑

是的,您需要在将回调结构中的回调字段传递给您的本机函数之前对其进行初始化,否则您只是传递了一个 NULL 指针,这将导致 Java 或本机端或两者的投诉。

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

myStruct.callbackField = new MyCallback() {
    public void invoke(int arg) {
        // do your stuff here
    }
};
于 2013-06-19T00:00:48.260 回答