views:

80

answers:

2

Hi,

I am a C/C++ programmer, but I was asked to update a program that was written in C# to communicate with a device. My knowledge of C# is very basic.

The previous version was totally written in C#, but now the API that in fact access the device was changed to C. I found out that I can import the C function APIs by using:

[DllImport("myapi.dll")]
public static extern int myfunct( 
                                 [MarshalAs(UnmanagedType.LPTStr)] string lpDeviceName,
                                 IntPtr hpDevice);

In C this function prototype is:

int myFunct( LPTStr lpDeviceName, HANDLE* hpDevice );

Where HANDLE is defined as :

typedef void *HANDLE;

However this function does not work as expected. In fact, in the C# code call what kind of type I should declare and pass to the C# method?

Thanks for the help and sorry for any stupid question.

+2  A: 

Hi, you are passing IntPtr instead of ref IntPtr, the definition should look like this:

[DllImport("myapi.dll")]
public static extern int myfunct( 
    [MarshalAs(UnmanagedType.LPTStr)] string lpDeviceName,
    ref IntPtr hpDevice);
Yakeen
In fact your solution was partially correct, I learned it in another forum that it is better to pass a StringBuilder instead of Marshalling a LPTStr.
zlogdan
This works just used a Stringbuilder instead of a string Marshalled that way.
zlogdan
+4  A: 

Actually, this is the wrong way of marshalling HANDLE *. It'll work, but not be reliable in the face of exceptions.

The function you posted looks like an object creation function (it's treating hpDevice as an output parameter, and returning an int status result).

The correct way to marshal it depends on exactly what type of object it's creating and how it is closed. Assuming that the HANDLE is closed by calling CloseHandle (which is true for most but not all HANDLE objects), then you can probably use one of the types that inherits from SafeHandleZeroOrMinusOneIsInvalid. For example, if the object is a registry key, then use SafeRegistryHandle; if it's a file, then use SafeFileHandle.

If it's some type for which there isn't an existing safe handle type (but does use CloseHandle to close it), then you'll have to define your own safe handle type derived from SafeHandleZeroOrMinusOneIsInvalid. If it's some type that doesn't use CloseHandle to close it, then you'll have to define your own safe handle type derived from SafeHandle.

Once you have determined the correct SafeHandle-derived type, then you can use it in the function call (using SafeFileHandle as an example):

[DllImport("myapi.dll")]
public static extern int myFunct(
    [MarshalAs(UnmanagedType.LPTStr)] string lpDeviceName,
    out SafeFileHandle hpDevice);
Stephen Cleary
I have tried this and yet it compiles the result is not Ok. This HANDLE is for a device that is connected to the serial port and when I wrote the application in C, I've just declared it as "HANDLE myHandle" and pass its address to the function and inside the API, this HANDLE was initialized . I also have tried to use ref InPtr without any luck as well.
zlogdan
Is the HANDLE the serial port, or is it used as an opaque handle to a "device" *connected* to the serial port? `SafeFileHandle` could be used only if the HANDLE is the serial port itself, not an opaque device handle. Another thing to check is a [calling convention](http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention.aspx) mismatch: the C++ default is `cdecl` and the C# default is `stdcall`.
Stephen Cleary
I have no means to know how this handle is used, the third party API simply says they "wrap either serial or USB connections" when this function is called. As I said, I made a test before trying to integrate this on the C# project and all I needed to do was to declare a handle as in HANDLE h and pass its address to the function, it just returned the proper one without complaints.
zlogdan
Well, C# is more type-safe than that. That's why it's not as simple. :) Even if you don't know what the `HANDLE` refers to, you should be able to know how it is "closed." What function do you have to call to close the handles?
Stephen Cleary
DWORD WINAPI McpClose (HANDLE hDevice);
zlogdan
In this case, you need to use your own `SafeHandle`-derived type. `SafeFileHandle` will not work.
Stephen Cleary
Hi, the part of the HANDLE was correct man. Just like Yakeen said, It was needed only a IntPtr hpDevice declaration. The part that was not working as [MarshalAs(UnmanagedType.LPTStr)] string lpDeviceName,this should be StringBuilder lpDeviceName and that1s why it was not working.
zlogdan
"It'll work, but not be reliable in the face of exceptions." (me)
Stephen Cleary