Click here to Skip to main content
15,867,594 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
I am talking to a low-cost Tucsen camera through it's dll. Most of the commands are working, but there is one command that I can't seem to marshal correctly. The signature of the call in the CamApi.h file is:

C++
CAM_API int GetRoi(int *pPtX, int *pPtY, int *wid, int *hei, const char *devName);


The SDK also came with a working c++ sample program that calls this as follows:
C++
int Core::GetRoi(int *pPtX, int *pPtY, int *wid, int *hei, const char *devName)
{
    return (*p_GetRoi)(pPtX, pPtY, wid, hei, devName);
}

which calls
C++
p_GetRoi = (int (*)(int *, int *, int *, int *, const char *))Load_Fun("GetRoi");



My first c# attempt was:
C#
public int TCgetRoi(
    ref int pointx,
    ref int pointy,
    ref int wid,
    ref int hei,
    string devName)
{
    int iOut = TC_getRoi(out pointx,
        out pointy,
        out wid,
        out hei,
        devName);
    return iOut;

with
C#
[DllImport("CamCore.dll", EntryPoint = "GetRoi", 
    CallingConvention = CallingConvention.StdCall)]
private static extern int TC_getRoi(
    out int pointx, out int pointy,
    out int wid,
    out int hei,
    string devName);


When I call it, the return int is 0 indicating a normal execution, and devName also comes out fine, but the four integers come out all wrong: 58532, 58528, 8024, 8276. When I run the c++ example they come out with very reasonable values: 0, 0, 1280, 1024.

In the c# code, am I reading a pointer that I have to deference again in order to get the int value??

I have tried many other things, and always with the same result. Can anyone see what I cannot?
Posted
Updated 6-Sep-13 3:43am
v3
Comments
Jason Gleim 6-Sep-13 9:41am    
When you import the function from the external DLL, the framework takes care of the type conversion and marshaling. You should not need to de-reference the out values from the external function. When the call returns the values of pointx, pointy, wid, and hei should all be correct without you doing additional work. So you should be able to call:

int iOut = TC_getRoi(out pointx,
out pointy,
out wid,
out hei,
devName);

and if iOut == 0 then pointx, pointy, etc should already be 0, 0, 1280, 1024. The invoke framework should be handling all of that for you.
AS01234 6-Sep-13 9:59am    
This looks the same as what I had. Just in case there might be a difference that I did not see, I copied and pasted it in, and it still gives the same crazy result. Do you think that the dll might actually be returning this crazy result? It still seems more likely to me that I am not marshaling it properly.
Jason Gleim 6-Sep-13 10:25am    
The only other thing that came to mind was a problem with bitness of the result. The camera could be returning the results in little-endian and that is causing them to get reversed. But I didn't see any conversion in the c code so I didn't mention that. You could convert the weird values to bits, reverse the bit order, pad it out, and convert back to decimal to see if that is it. If it is, your result would be the correct number.

Another possibility is the depth. int in c# is really int32... there could be an issue with the bit depth or something like that which is causing weirdness. Maybe the values are being cast from overlapping areas of memory because the function is returning 16 bit int values? You would have to look at the api I would think to determine that.

You could also break on the function call and look at the stack to see the parameters. That would tell you right away if you are getting pointers or values passed back.
AS01234 6-Sep-13 12:07pm    
Thanks again for the reply. I tried the little endian idea, and when I reverse the bytes (but I didn't understand the padding issue), I get other crazy numbers.

This also answered the second suggestion about the depth. When I use:

Byte[] bytes = BitConverter.GetBytes(pointx);

the result for bytes is a 4 element byte array, so I am pretty sure that c# is using 4-byte int (equivalent to int32). I am less sure about the c++ int, but I am running this on a fairly standard 32-bit machine. Can a c++ int be different than 4-bytes?

I am already breaking on the line before and after the call to the pinvoke (TC_getRoi), but my vs2008 does not let me put a break right on the pinvoke itself. So I cannot seem to see that critical external call. When it stops just before and just after the call to the pinvoke TC_getRoi, I can look at the Call Stack tab, and it shows the call into TCgetRoi, which does not tell me anything new. I am not sure that this is what you mean whey you say "look at the stack," and you might be referring to a trick that I don't know. In visual studio 2012 there seems to be a "show external code" option that I do not seem have in visual studio 2008.

Unfortunately I don't have the c++ code for the dll itself-- just the .dll file itself.

If everything looks right, should I conclude that the dll is really sending back the crazy values? It is remotely possible that there is a subtle difference in the preceding calls to the dll that could make it return crazy values. When I run the c++ example I add break points to the calls to the dll, and everything looks pretty much the same as in my c# program. This is why I still consider this an unlikely possibility. Before making this conclusion, I would love to see if anyone else finds anything wrong with the original code.
Jason Gleim 6-Sep-13 13:44pm    
You might try using the bitconverter class to convert back the byte array to an int. So do the conversion to the byte array that you are doing then use BitConverter.ToInt32(bytes) and BitConverter.ToUInt32(bytes) to see if either value comes back correct. It might be a signing issue. If that isn't it... I'm out of ideas. :-(

The problem is clearly in the way the framework is converting those int values. It is supposed to work automagically but there are cases where it fails and usually with maddening results. I've seen it on complex objects but not on atomic types before.

At least with the BitConverter class you can look at the results at the bit level. If you convert height you would see an array (in hex) of {0,4,0,0}, right? There may be some value in looking at the results that way.

1 solution

The folks at Tucsen camera were very helpful. I sent them the project. They tested it, said that they modified it a little, and sent it back. It still gave the same result, but since it was working for them when they sent it I became more confident that the pinvoke code was working all along, but the dll was actually returning crazy numbers. I added some other calls to the camera prior to the GetRoi call above, and now it is working and the numbers are reasonable.

Summary: The code above was fine all along, and the camera was stuck in a state that caused the dll to return the crazy numbers. The Tucsen camera dll is a bit weak, since it should never return numbers that are outside the range of the whole image. But Tucsen gets high marks for effort and willingness to help. Special thanks to Jason Gleim, who helped with several ideas that eventually also pointed to the conclusion that the crazy numbers were actually being sent by the dll.
 
Share this answer
 
Comments
V.Lorz 15-Sep-13 13:26pm    
Hi,

When it comes too difficult for me to figure out situations like this I create one C++ mixed (managed+unmanaged) mode DLL. In that kind of DLL you can make native call directly from managed code. In some cases I create a wrapper class and in some other cases I just play with code until I get all puzzle pieces.

Using the IL dasm and assembly code debugging has for a long time been also great help for me, specially when trying to figure out the real parameter's expected size.

Most of the time it all ends up (after that research) adding some marshalling attributes to the parameters in the external function declaration, just to make sure they are marshalled in the proper size.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900