简体   繁体   中英

void ** handles with P/Invoke

I am using a C API DLL from a 3rd party vendor. The problem I have is that I can't seem to find a good template for Marshalling the following C code:

API_Open( void ** handle );
API_Close( void * handle );

The calls are simplified, but the handle is a void *, which is (in C) passed into the API_Open call as &handle, and then passed into API_Close as handle.

I've tried to do the same in C#, but can't figure out how to Marshal handle properly. My C# version (latest try) is:

[DllImport("External.dll",EntryPoint="API_Open")]
public static extern int API_Open( out IntPtr handle );
[DllImport("External.dll",EntryPoint="API_Close")]
public static extern int API_Close( IntPtr handle );

public static int Wrapper_API_Open( ref Int32 handle )
{
    int rc = SUCCESS;

    // Get a 32bit region to act as our void**
    IntPtr voidptrptr = Marshal.AllocHGlobal(sizeof(Int32));

    // Call our function.  
    rc = API_Open(out voidptrptr);

    // In theory, the value that voidptrptr points to should be the 
    // RAM address of our handle.
    handle = Marshal.ReadInt32( Marshal.ReadIntPtr(voidptrptr) );

    return rc;
}

public static int Wrapper_API_Close(ref Int32 handle)
{
    int rc = SUCCESS;

    // Get a 32bit region to act as our void *
    IntPtr voidptr = Marshal.AllocHGlobal(sizeof(Int32));

    // Write the handle into it.
    Marshal.WriteInt32(voidptr,handle);

    // Call our function.
    rc = API_Close(voidptr);

    return rc;
}


public void SomeRandomDrivingFunction()
{
    .
    .
    .
    Int32 handle;
    Wrapper_API_Open( ref handle );
    .
    .
    .
    Wrapper_API_Close( ref handle );
    .
    .
    .
}

The API return code is always INVALID_DEVICE_OBJECT when I call API_Close. Any thoughts? I thought this would be pretty straightforward, but I'm having trouble wrapping my head around the void** and void* parts of the function calls.

Thanks

You seem to be over-complicating this significantly. I don't know why you want to introduce Int32 for the handles since they do need to be pointer sized. You should use IntPtr .

The API_Open accepts the address of the variable where the handle is returned. The caller allocates that variable and passes it to the callee, which populates the variable. The C function might look like this:

int API_Open(void **handle)
{
    *handle = InternalCreateHandle();
    return CODE_SUCCESS;
}

You'd call that in C like this:

void *handle;
int retval = API_Open(&handle);
if (retval != CODE_SUCCESS)
    // handle error
// go one and use handle

Translated to C#, the void* maps to IntPtr , and the use of the double pointer is just a means to get around the fact that C only supports pass-by-value. In C# you would use pass-by-reference.

For API_Close it is even simpler because we are passing the handle by value:

int API_Close(void *handle)
{
    InternalCloseHandle(handle);
    return CODE_SUCCESS;
}

And the calling code is simply:

int retval = API_Close(handle);
if (retval != CODE_SUCCESS)
    // handle error

So, the C# wrapper functions should be:

public static int Wrapper_API_Open(out IntPtr handle)
{
    return API_Open(out handle);
}

public static int Wrapper_API_Close(IntPtr handle)
{
    return API_Close(handle);
}

At which point these wrapper methods do look somewhat pointless!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM