views:

418

answers:

2

My problem is with understanding the finer point of mixed langage programming and accessing API's in external libraries. My skills at C++ are nonexistent and at VB, mediocre.

I have a c++ dll compiled (portaudio library), and am trying to access it from VB (Visual Studio 2005). I am getting MarshallDirectiveException errors when calling a function, I believe because I am interacting incorrectly with the dll.


the C++ function and structures are defined as follows:

header info:

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 );

program usage from docs:

const PaDeviceInfo* Pa_GetDeviceInfo ( PaDeviceIndex device ) Retrieve a pointer to a PaDeviceInfo structure containing information about the specified device.

Returns: A pointer to an immutable PaDeviceInfo structure. If the device parameter is out of range the function returns NULL.

Parameters: device A valid device index in the range 0 to (Pa_GetDeviceCount()-1)

...


in the VB program I have:

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)

This feels wrong as docs state Pa_GetDeviceInfo returns a POINTER to a structure containing info about the structure, implying function creates the structure initially.

I am completely new to mixed language programming, a c++ utter noob, and a poor VB programmer. Can anyone guide me in the correct way to attack this problem ? My feeling is that I need to understand how to get VB to reference a struct in memry created in the dll, so I need to get vb to understand 'pointer to thing' as a function return.

Much appreciation for any assistance rendered. Please don't just say rtfm, I'm up to my eyes in FM's at the minute, I just don't know where to look.

Many thanks, David

+2  A: 

Your API function declaration is wrong. The function returns a pointer which is not reflected in your code. The signature translates to VB as follows:

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

Of course, using an IntPtr directly is not easy. In fact, quite some marshalling is involved:

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

(More or less important) side note: since your DLL apparently creates a new object in memory, it also needs to release/destroy it. Be sure to call the appropriate function after using the structure.

Konrad Rudolph
Yes, that works. My problem now is to understand just *why* that is so !Like I said, noob !
WaveyDavey
Hmm, actually, you can't assume anything about how the memory in the DLL is allocated. All you can do is follow the documentation. Call cleanup functions if the documentation says to do so - investigate further if you find memory is leaking, however.
Joe Soul-bringer
A: 

Yes, your structure is wrong. You indeed have to get a pointer and then read the memory to which it "points".

I've done external DLL calling in C++ before and it generally involves a huge wading-through of documentation. I don't think anyone here is going to do that for you but I'll try to point you in the proper direction.

First, a pointer is an address, a value which "points" to some location in memory. "dereferencing" a pointer is reading the memory that the pointers to (and if you read or write the wrong memory, the memory can get upset and kill your program).

Further, at a low level, calling a DLL involves copying bits of information to the stack and then having the function retrieve them. The "calling conventions" describe exactly how this is done - there are "c", pascal, and other such conventions.

You will want to call the DLL's function, get a pointer, copy the information pointed-to into your local structure and then continue. The hard thing will be figuring out exactly how to declare the DLL function. If your library documentation has some sample code, that might be where to start.

A short Google does not even show any consistent way to deal with pointers in VB at all. One idea would be to create a short C++ program which would call the DLL and return an object by value. I don't know if this would help but such kludges come up when dealing with external libraries.

Good Luck

Joe Soul-bringer