C# prototype of C: void**

F

frankh

I want to access a function implemented in a C-dll with the following
prototype:
rval = imgSnap(SESSION_ID sid, void** bufAddr)
Now:
1. How would the "void** bufAddr" part look like in the corresponding
C# prototype?
2. How do I call this C# method (rval is uint, sid is int)?

"bufAddr" exists allready, it must not be allocated, and I don't want
to copy data. imgSnap shall simply write its half a million bytes from
&&bufAddr on.

Any idea?
 
N

Nicholas Paldino [.NET/C# MVP]

I would declare it like this:

// The assmption is that it returns an integer.
[DllImport("some.dll")]
private static extern int imgSnap(int sid, ref IntPtr bufAddr);

Pointers are typically passed around in IntPtr variables, and since you
have a pointer to a pointer, you need to pass it by ref.

However, accessing this in C# will require you to marshal the values in
memory from unmanaged to managed code, which can get costly for 1/2 million
bytes.

You might want to consider using unsafe code here and casting the
pointer to a byte array, and performing whatever operation you have to do in
there.

Hope this helps.
 
F

frankh

I declared the method as you suggested and tried to call it like this:
--
public unsafe void Capture (int width, int height, byte[] buffer)
{ fixed (byte* bp = &buffer[0])
errcode = imgSnap(sessionID, ref (IntPtr)bp);
}
--
but I got the error message "A ref or out argument must be an lvalue".

So how do I do the type casting? (I am rather new to c#).
As arrays are reference types, I guess that the "byte[] buffer"
declaration in the parameter list of Capture allows changing of the
array components, correct?

I would declare it like this:

// The assmption is that it returns an integer.
[DllImport("some.dll")]
private static extern int imgSnap(int sid, ref IntPtr bufAddr);

Pointers are typically passed around in IntPtr variables, and since you
have a pointer to a pointer, you need to pass it by ref.

However, accessing this in C# will require you to marshal the values in
memory from unmanaged to managed code, which can get costly for 1/2 million
bytes.

You might want to consider using unsafe code here and casting the
pointer to a byte array, and performing whatever operation you have to do in
there.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

I want to access a function implemented in a C-dll with the following
prototype:
rval = imgSnap(SESSION_ID sid, void** bufAddr)
Now:
1. How would the "void** bufAddr" part look like in the corresponding
C# prototype?
2. How do I call this C# method (rval is uint, sid is int)?

"bufAddr" exists allready, it must not be allocated, and I don't want
to copy data. imgSnap shall simply write its half a million bytes from
&&bufAddr on.

Any idea?
 
W

Willy Denoyette [MVP]

What you need is a call like this
IntPtr ptr = IntPt.Zero;
errcode = imgSnap(sessionID, ref ptr);
...
// access the unmanaged buffer using unsafe code, or copy the buffer to a
managed array.

Willy.


I declared the method as you suggested and tried to call it like this:
--
public unsafe void Capture (int width, int height, byte[] buffer)
{ fixed (byte* bp = &buffer[0])
errcode = imgSnap(sessionID, ref (IntPtr)bp);
}
--
but I got the error message "A ref or out argument must be an lvalue".

So how do I do the type casting? (I am rather new to c#).
As arrays are reference types, I guess that the "byte[] buffer"
declaration in the parameter list of Capture allows changing of the
array components, correct?

I would declare it like this:

// The assmption is that it returns an integer.
[DllImport("some.dll")]
private static extern int imgSnap(int sid, ref IntPtr bufAddr);

Pointers are typically passed around in IntPtr variables, and since
you
have a pointer to a pointer, you need to pass it by ref.

However, accessing this in C# will require you to marshal the values
in
memory from unmanaged to managed code, which can get costly for 1/2
million
bytes.

You might want to consider using unsafe code here and casting the
pointer to a byte array, and performing whatever operation you have to do
in
there.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

I want to access a function implemented in a C-dll with the following
prototype:
rval = imgSnap(SESSION_ID sid, void** bufAddr)
Now:
1. How would the "void** bufAddr" part look like in the corresponding
C# prototype?
2. How do I call this C# method (rval is uint, sid is int)?

"bufAddr" exists allready, it must not be allocated, and I don't want
to copy data. imgSnap shall simply write its half a million bytes from
&&bufAddr on.

Any idea?
 
F

frankh

I simply don't get it. Let me summarize and add some new information:

1. My C prototype is
rval = imgSnap(SESSION_ID sid, void** bufAddr)
2. I have an allocated (managed) array "byte[] data" which I want to
link somehow with bufAddr.
3. imgSnap will write about half a million bytes, so unnecessary
copying should be avoided.
4. I will call imgSnap repeatedly, so I guess a new allocation of the
byte array at each call would be inefficient too.

What I tried so far is:

1. [DllImport("imaq.dll")] public static extern
int imgSnap (SESSION_ID sid, ref IntPtr bufPtr);

a) errCode = imgSnap (sessionID, ref (IntPtr)data);
*** Cannot convert ...
b) errCode = imgSnap (sessionID, ref &data);
*** Cannot take address...of a managed type

2. [DllImport("imaq.dll")] public static unsafe extern
int imgSnap (SESSION_ID sid, byte* bufPtr);

a) fixed(byte* bp = data) errCode = imgSnap (sessionID, bp);
*** compiles, but only the first 4 bytes of data receive
values, probably an address
b) fixed(byte* bp = data) errCode = imgSnap (sessionID, &bp);
*** cannot take address ...

What am I missing?
 
W

Willy Denoyette [MVP]

You said .. "bufAddr" exists already, ... and the argument is void** right?
That means you don' need to allocate that buffer in the calling code, and
this is what you do in 1 and 2 when calling the API.

Do as I said before:
Allocate a IntPtr wrapper for the pointer retuned by the function call.
IntPtr ptr = IntPt.Zero;
Pass the IntPtr ptr by reference.
errcode = imgSnap(sessionID, ref ptr);

at return ptr will hold an IntPtr wrapping a pointer to the unmanaged
buffer,
So you need to extract the pointer from the IntPtr uisng:

IntPtr bufPtr = MarshalReadIntPtr(ptr);

now you can copy the contents of this buffer to a managed buffer array using
Marshal.Copy(...).
Marshal.Copy(bufPtr , destination, 0, length); // only problem here is that
you must know the length og the native buffer...
or, you can access the native buffer using the Marshal.Readxxx()
overloads....

Willy.
 
F

frankh

Willy said:
You said .. "bufAddr" exists already, ... and the argument is void** right?

No, "bufAddr" is just a formal parameter. What already exists is
"byte[] data".
My problem is: what would be the actual parameter corresponding to
"bufAddr", so that imgSnap writes its bytes into "data".
That means you don' need to allocate that buffer in the calling code, and
this is what you do in 1 and 2 when calling the API.

Do as I said before:
Allocate a IntPtr wrapper for the pointer retuned by the function call.
IntPtr ptr = IntPt.Zero;
Pass the IntPtr ptr by reference.
errcode = imgSnap(sessionID, ref ptr);

at return ptr will hold an IntPtr wrapping a pointer to the unmanaged
buffer,
So you need to extract the pointer from the IntPtr uisng:

IntPtr bufPtr = MarshalReadIntPtr(ptr);

now you can copy the contents of this buffer to a managed buffer array using
Marshal.Copy(...).
Marshal.Copy(bufPtr , destination, 0, length); // only problem here is that
you must know the length og the native buffer...
or, you can access the native buffer using the Marshal.Readxxx()
overloads....

I know the amount of data to be transferred as well as buffer sizes.
But what I wanted to avoid is first writing to one buffer and then
copying to another, managed one.

By the way, thanks for your patience.

Frank

I simply don't get it. Let me summarize and add some new information:

1. My C prototype is
rval = imgSnap(SESSION_ID sid, void** bufAddr)
2. I have an allocated (managed) array "byte[] data" which I want to
link somehow with bufAddr.
3. imgSnap will write about half a million bytes, so unnecessary
copying should be avoided.
4. I will call imgSnap repeatedly, so I guess a new allocation of the
byte array at each call would be inefficient too.

What I tried so far is:

1. [DllImport("imaq.dll")] public static extern
int imgSnap (SESSION_ID sid, ref IntPtr bufPtr);

a) errCode = imgSnap (sessionID, ref (IntPtr)data);
*** Cannot convert ...
b) errCode = imgSnap (sessionID, ref &data);
*** Cannot take address...of a managed type

2. [DllImport("imaq.dll")] public static unsafe extern
int imgSnap (SESSION_ID sid, byte* bufPtr);

a) fixed(byte* bp = data) errCode = imgSnap (sessionID, bp);
*** compiles, but only the first 4 bytes of data receive
values, probably an address
b) fixed(byte* bp = data) errCode = imgSnap (sessionID, &bp);
*** cannot take address ...

What am I missing?
 
W

Willy Denoyette [MVP]

Willy said:
You said .. "bufAddr" exists already, ... and the argument is void**
right?

No, "bufAddr" is just a formal parameter. What already exists is
"byte[] data".
My problem is: what would be the actual parameter corresponding to
"bufAddr", so that imgSnap writes its bytes into "data".


Ok, in that case you can't do this without copying....

rval = imgSnap(, void** bufAddr)

[DllImport("some.dll",CallingConvention=CallingConvention.Winapi)]
static extern int imgSnap(SESSION_ID sid , ref IntPtr p);

byte[] data= new byte[500000];
....

IntPtr ptr = Marshal.AllocHGlobal(data.Length);
int rval = imgSnap(......., ref ptr);
Marshal.Copy(ptr, data, 0, ar.Length);

Willy.
 
D

David Smith

As it happens, I use this library myself. Here's how I did it (this is
done within a single project inside the solution):

internal class ImaqSDK
{
[DllImport("imaq.dll", EntryPoint="imgSnap", ExactSpelling=true)]
internal static extern int Snap(uint sID, ref IntPtr buffer);
}

ushort[,] preallocatedArray = new ushort[rows, columns];

fixed (void* framePtr = preallocatedArray)
{
IntPtr tmpPtr = new IntPtr(framePtr);
err = ImaqSDK.Snap(sessionID, ref tmpPtr);
}
 
F

frankh

Below follow two unsafe methods for data transfer into a one- and into
a two-dimensional array. The part not shown here calls some other
functions of imaq.dll in order to get the image size. it then checks if
dat has the right dimensions.

[DllImport("imaq.dll")] static extern
int imgSnap (SESSION_ID sid, ref IntPtr bufPtr);


public void Capture (byte[] dat)
{ int errCode, len;
uint t, l, h, w, needed;
IntPtr ptr;

// check dat and image size
// ... snip ...
// allocate buffer, aquire image and copy buffer to dat
ptr = Marshal.AllocHGlobal (len);
errCode = imgSnap (sessionID, ref ptr);
ImgErrorHandling (errCode);
Marshal.Copy (ptr, dat, 0, len);
} // Capture


public unsafe void Capture (byte[,] dat)
{ int errCode, len;
uint t, l, h, w, needed;

// check dat and image size
// ... snip ...
// allocate buffer, aquire image and copy buffer to dat
fixed (void* framePtr = dat)
{ IntPtr tmpPtr = new IntPtr(framePtr);
errCode = imgSnap(sessionID, ref tmpPtr);
}
ImgErrorHandling (errCode);
} // Capture
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top