2

我的问题是理解混合语言编程和访问外部库中的 API 的更好点。我在 C++ 方面的技能不存在,而在 VB 方面则平庸。

我编译了一个 c++ dll(portaudio 库),并试图从 VB(Visual Studio 2005)访问它。我在调用函数时遇到 MarshallDirectiveException 错误,我相信是因为我与 dll 交互不正确。


C++ 函数和结构定义如下:

标题信息:

typedef int PaHostApiIndex;
...
typedef double PaTime;
...
typedef struct PaDeviceInfo
 {
     int structVersion;  /* this is struct version 2 */
     const char *name;
     PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
     int maxInputChannels;
     int maxOutputChannels;
     PaTime defaultLowInputLatency;
     PaTime defaultLowOutputLatency;
     PaTime defaultHighInputLatency;
     PaTime defaultHighOutputLatency;
     double defaultSampleRate;
 } PaDeviceInfo;
 ...
 const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device );

文档中的程序用法:

const PaDeviceInfo* Pa_GetDeviceInfo    (   PaDeviceIndex   device   )  

检索指向包含指定设备信息的 PaDeviceInfo 结构的指针。

返回: 指向不可变 PaDeviceInfo 结构的指针。如果设备参数超出范围,则函数返回 NULL。

参数:设备 0 到 (Pa_GetDeviceCount()-1) 范围内的有效设备索引


在VB程序中,我有:

Private Declare Function Pa_GetDeviceInfo Lib "portaudio_x86.dll" (ByVal dindex As Integer) As PaDeviceInfo
...
Private Structure PaDeviceInfo
        Dim structVersion As Integer
        <MarshalAs(Runtime.InteropServices.UnmanagedType.LPStr)> Dim name As String
        Dim hostapi As Integer
        Dim maxInputChannels As Integer
        Dim maxOutputChannels As Integer
        Dim defaultLowInputLatency As Double
        Dim defaultLowOutputLatency As Double
        Dim defaultHighInputLatency As Double
        Dim defaultHighOutputLatency As Double
        Dim defaultSampleRate As Double
End Structure
...
        Dim di As New PaDeviceInfo
        di = Pa_GetDeviceInfo(outputParameters.device)

这感觉是错误的,因为文档状态 Pa_GetDeviceInfo 返回一个指向包含结构信息的结构的指针,这意味着函数最初创建了结构。

我对混合语言编程完全陌生,完全是 C++ 菜鸟,也是一个糟糕的 VB 程序员。谁能指导我以正确的方式解决这个问题?我的感觉是我需要了解如何让 VB 引用在 dll 中创建的内存中的结构,所以我需要让 vb 将“指向事物的指针”理解为函数返回。

非常感谢提供的任何帮助。请不要只说 rtfm,我现在在 FM 中取决于我的眼睛,我只是不知道在哪里看。

非常感谢,大卫

4

2 回答 2

3

您的 API 函数声明错误。该函数返回一个未反映在您的代码中的指针。签名转换为 VB 如下:

Private Declare Function Pa_GetDeviceInfo Lib "portaudio_x86.dll" ( _
    ByVal dindex As Integer _
) As IntPtr

当然,IntPtr直接使用 an 并不容易。事实上,涉及到相当多的编组:

Dim obj As PaDeviceInfo = _
    DirectCast(Marshal.PtrToStructure(ptr, GetType(PaDeviceInfo)), PaDeviceInfo)

(或多或少重要)旁注:由于您的 DLL 显然在内存中创建了一个新对象,因此它还需要释放/销毁它。请务必在使用该结构后调用相应的函数。

于 2009-01-24T22:42:01.130 回答
0

是的,你的结构是错误的。您确实必须获得一个指针,然后读取它“指向”的内存。

我之前在 C++ 中完成了外部 DLL 调用,它通常涉及大量文档。我认为这里没有人会为您这样做,但我会尽力为您指明正确的方向。

首先,指针是一个地址,一个“指向”内存中某个位置的值。“取消引用”指针正在读取指针指向的内存(如果您读取或写入错误的内存,内存可能会变得不安并杀死您的程序)。

此外,在底层,调用 DLL 涉及将信息位复制到堆栈,然后让函数检索它们。“调用约定”准确描述了这是如何完成的——有“c”、pascal 和其他此类约定。

您将需要调用 DLL 的函数,获取一个指针,将指向的信息复制到您的本地结构中,然后继续。困难的事情将是弄清楚如何声明 DLL 函数。如果您的库文档有一些示例代码,那可能是开始的地方。

一个简短的 Google 甚至根本没有显示任何一致的方式来处理 VB 中的指针。一个想法是创建一个简短的 C++ 程序,该程序将调用 DLL 并按值返回一个对象。我不知道这是否会有所帮助,但是在处理外部库时会出现这样的问题。

祝你好运

于 2009-01-24T22:58:59.630 回答