views:

64

answers:

3

I'm having trouble getting a string back from some c code I wrote.

First some generally unrealated background info: I'd like to receive the user readable string for a TAPI TSP from the TAPI API. I've implemented a semi workable TAPI solution relying on matching driver names to stored strings, but would like to change this to work on permanant line id's instead, as one of our customers has an (Alcatel) PBX that refuses to work any other way.

In C, I define the function in my header file as:

__declspec(dllexport) void GetDeviceName(long DeviceId, wchar_t* DeviceName);

The function is written thusly:

__declspec(dllexport) void GetDeviceName(long DeviceId, wchar_t* DeviceName)
{
    //tapi code here...

    //copy the string to DeviceName
    wcscpy(DeviceName, (wchar_t*)((char *)devCaps + devCaps->dwLineNameOffset));
}

As stated above, this will eventually do something useful but for now I'll be happy if abc is placed in my wchar_t*/StringBuilder and I can see that in C#.

In C# I define the function as:

    [DllImport("SBW.Tapi.TapiInterop.dll", CharSet = CharSet.Auto)]
    static extern void GetDeviceName(long DeviceId, StringBuilder DeviceName);

I define DeviceName as a StringBuilder because a string is immuatable and I want the DeviceName to be set in C (This is recommended by MS). I also set the return type to void on the slim hope that that also affects something (see this semi helpful mono article for a partial pseudo scientific explanation).

And call it so:

StringBuilder name = new StringBuilder();
name.EnsureCapacity(100);
long n = 0;
GetDeviceName(n, name);

I attach my debugger to the running process, set a breakpoint, and notice in the C code that somehow the StringBuilder is perverted and is provided to the unmanaged code as a null pointer.

An AccessViolationException is subsequently thrown in C#.

What's gone wrong?

removing the long parameter helps. I'm able to add "abc" to my DeviceName parameter in C. I want that long variable however! What am I doing wrong or upsetting so as to force a crash by having that long parameter there?

+1  A: 

The c++ long is 32 bits, the C# long is 64 bits, isn't it? Therefore you'd need to use an int for the first parameter.

Lucero
no need to. StringBuilder is automatically defined as [In, Out]
Matt Jacobsen
And now you've gone and confused everyone by editing your answer to something completely different, thereby making my comments nonsensical :)
Matt Jacobsen
Sorry about that. But everyone sees that it has been edited any everycone can check the history... ;)
Lucero
A: 

On 32 bits platforms long is 8 bytes (64 bits) wide in .NET and 4 bytes (32 bits) wide in native code. You need to change your P/Invoke method like this:

[DllImport("SBW.Tapi.TapiInterop.dll", CharSet = CharSet.Auto)]
static extern void GetDeviceName(int DeviceId, StringBuilder DeviceName);

This way, C# int and C long have the same width on 32 bits platforms.

Laurent Etiemble
+1  A: 

I want that long variable however

Then you should define the parameter in C as long long. As Lucero pointed out, a long in C++ is 32 bits, while a long in C# is 64 bits. So if you pass a 64 bit value where the C function expects a 32 bit value, the function reads the extra 32 bits as the second parameter, which obviously results in a wrong address for the buffer...

Thomas Levesque