Passing values from C# from/to a C++ dll

B

Bob Yang

Hi, I have this in C++ and I like to call it from c# to get the value
but I fail. it will be good if you can give me some information. I
tried it in VB.net it works but I use almost the same way as VB in C#
but it doens't work.



c++: (csp2.dll)
NoMangle long DLL_IMPORT_EXPORT csp2TimeStamp2Str(unsigned char
*Stamp, char *value, long nMaxLength);




VB.net: (this works correctly)

Declare Function csp2TimeStamp2Str Lib "csp2.dll" (ByRef Stamp As
Byte, ByVal value As String, ByVal nMaxLength As Integer) As Integer

Dim nRC As Integer
Dim arrbyteBarcode(99) As Byte '100 elements
Dim nBytesRead As Integer
Dim bstrTmp As New VB6.FixedLengthString(50)

nBytesRead = csp2GetPacket(arrbyteBarcode(0), i, 100)

nRC = csp2TimeStamp2Str(arrbyteBarcode(nBytesRead - 4), bstrTmp.Value,
Len(bstrTmp.Value))
TextBox1.text= VB.Left(bstrTmp.Value, 20)




C#: (this doesn't work :( )

[System.Runtime.InteropServices.DllImport("csp2.DLL")] static extern
int csp2TimeStamp2Str(byte value, string Stamp, int nMaxLength);

int nRC, nBytesRead;
byte[] arrbyteBarcode= new byte[100];
FixedLengthString bstrTmp = new FixedLengthString(50);

nBytesRead = csp2GetPacket(arrbyteBarcode[0], i, 100);
bstrTmp=" ";

nRC = csp2TimeStamp2Str(arrbyteBarcode[nBytesRead-4],
bstrTmp.Value, bstrTmp.Value.Length);
TextBox1.text= bstrTmp.Value.toString();
 
W

Willy Denoyette [MVP]

Bob Yang said:
Hi, I have this in C++ and I like to call it from c# to get the value
but I fail. it will be good if you can give me some information. I
tried it in VB.net it works but I use almost the same way as VB in C#
but it doens't work.



c++: (csp2.dll)
NoMangle long DLL_IMPORT_EXPORT csp2TimeStamp2Str(unsigned char
*Stamp, char *value, long nMaxLength);




VB.net: (this works correctly)

Declare Function csp2TimeStamp2Str Lib "csp2.dll" (ByRef Stamp As
Byte, ByVal value As String, ByVal nMaxLength As Integer) As Integer

Dim nRC As Integer
Dim arrbyteBarcode(99) As Byte '100 elements
Dim nBytesRead As Integer
Dim bstrTmp As New VB6.FixedLengthString(50)

nBytesRead = csp2GetPacket(arrbyteBarcode(0), i, 100)

nRC = csp2TimeStamp2Str(arrbyteBarcode(nBytesRead - 4), bstrTmp.Value,
Len(bstrTmp.Value))
TextBox1.text= VB.Left(bstrTmp.Value, 20)




C#: (this doesn't work :( )

[System.Runtime.InteropServices.DllImport("csp2.DLL")] static extern
int csp2TimeStamp2Str(byte value, string Stamp, int nMaxLength);

int nRC, nBytesRead;
byte[] arrbyteBarcode= new byte[100];
FixedLengthString bstrTmp = new FixedLengthString(50);

nBytesRead = csp2GetPacket(arrbyteBarcode[0], i, 100);
bstrTmp=" ";

nRC = csp2TimeStamp2Str(arrbyteBarcode[nBytesRead-4],
bstrTmp.Value, bstrTmp.Value.Length);
TextBox1.text= bstrTmp.Value.toString();



..... csp2TimeStamp2Str(byte[] value, StringBuilder Stamp, int nMaxLength);

int nRC, nBytesRead;
byte[] arrbyteBarcode= new byte[100];
StringBuilder sb = new StringBuilder(" ", 100);
nBytesRead = csp2GetPacket(arrbyteBarcode[0], i, 100);
ArraySegment<byte> as = new ArraySegment<byte>(arrbyteBarcode,
nBytesRead-4, 4); // [2]
nRC = csp2TimeStamp2Str(as.Array, sb, sb.Length); // [3]
TextBox1.text= sb.toString();

First you have to pass a byte[] as first parameter to the function, you are
passing the first byte of the array by value. Note that the VB code is
flawed too, you should pass a byte[] not a refrence to a byte, this code
will fail on 64 bit Windows!
Second [1]get rid of the VB6 dependency and use a StringBuilder to pass a
fixed string buffer. Not sure why you are passing a " " char when calling
this function though.
[2] and [3] are used to get a byte array segment out of the original array.

Willy.
 
B

Ben Voigt [C++ MVP]

.... csp2TimeStamp2Str(byte[] value, StringBuilder Stamp, int nMaxLength);

int nRC, nBytesRead;
byte[] arrbyteBarcode= new byte[100];
StringBuilder sb = new StringBuilder(" ", 100);
nBytesRead = csp2GetPacket(arrbyteBarcode[0], i, 100);
ArraySegment<byte> as = new ArraySegment<byte>(arrbyteBarcode,
nBytesRead-4, 4); // [2]
nRC = csp2TimeStamp2Str(as.Array, sb, sb.Length); // [3]
TextBox1.text= sb.toString();

[snip]
[2] and [3] are used to get a byte array segment out of the original
array.

No, it doesn't. ArraySegment<T>.Array is the entire array, not a subset.
 
B

Bob Yang

Thank you! However, it doens't really work. sb.ToString() turns a
space " "

I capture some screens and code here: http://docs.google.com/Doc?id=dd5djd97_44d5nz2p

another question, this is not really related, how does it handle
access the value to "sb" wiht out "ref" or "out"? does c# handle this
by itself? I just wonder how to use it in the pure c# without c++
without global variables nor ref or out.


Hi, I have this in C++ and I like to call it from c# to get the value
but I fail. it will be good if you can give me some information. I
tried it in VB.net it works but I use almost the same way as VB in C#
but it doens't work.
c++: (csp2.dll)
NoMangle long DLL_IMPORT_EXPORT csp2TimeStamp2Str(unsigned char
*Stamp, char *value, long nMaxLength);
VB.net: (this works correctly)
Declare Function csp2TimeStamp2Str Lib "csp2.dll" (ByRef Stamp As
Byte, ByVal value As String, ByVal nMaxLength As Integer) As Integer
Dim nRC As Integer
Dim arrbyteBarcode(99) As Byte '100 elements
Dim nBytesRead As Integer
Dim bstrTmp As New VB6.FixedLengthString(50)
nBytesRead = csp2GetPacket(arrbyteBarcode(0), i, 100)
nRC = csp2TimeStamp2Str(arrbyteBarcode(nBytesRead - 4), bstrTmp.Value,
Len(bstrTmp.Value))
TextBox1.text= VB.Left(bstrTmp.Value, 20)
C#: (this doesn't work :( )
[System.Runtime.InteropServices.DllImport("csp2.DLL")] static extern
int csp2TimeStamp2Str(byte value, string Stamp, int nMaxLength);
int nRC, nBytesRead;
byte[] arrbyteBarcode= new byte[100];
FixedLengthString bstrTmp = new FixedLengthString(50);
nBytesRead = csp2GetPacket(arrbyteBarcode[0], i, 100);
bstrTmp=" ";
nRC = csp2TimeStamp2Str(arrbyteBarcode[nBytesRead-4],
bstrTmp.Value, bstrTmp.Value.Length);
TextBox1.text= bstrTmp.Value.toString();

.... csp2TimeStamp2Str(byte[] value, StringBuilder Stamp, int nMaxLength);

int nRC, nBytesRead;
byte[] arrbyteBarcode= new byte[100];
StringBuilder sb = new StringBuilder(" ", 100);
nBytesRead = csp2GetPacket(arrbyteBarcode[0], i, 100);
ArraySegment<byte> as = new ArraySegment<byte>(arrbyteBarcode,
nBytesRead-4, 4); // [2]
nRC = csp2TimeStamp2Str(as.Array, sb, sb.Length); // [3]
TextBox1.text= sb.toString();

First you have to pass a byte[] as first parameter to the function, you are
passing the first byte of the array by value. Note that the VB code is
flawed too, you should pass a byte[] not a refrence to a byte, this code
will fail on 64 bit Windows!
Second [1]get rid of the VB6 dependency and use a StringBuilder to pass a
fixed string buffer. Not sure why you are passing a " " char when calling
this function though.
[2] and [3] are used to get a byte array segment out of the original array.

Willy.- -

- -
 
B

Bob Yang

thank you. you are right so I change to this but I still not able to
get the value for "sb". any recommadation? thank you!

byte[] bb2 = new byte[100];
nBytesRead2 = nBytesRead - 4;

for (i = 0; i < 4; i++)
{
bb2 = arrbyteBarcode[nBytesRead2];
nBytesRead2++;
}


nRC = csp2TimeStamp2Str(bb2, sb, sb.Length);





.... csp2TimeStamp2Str(byte[] value, StringBuilder Stamp, int nMaxLength);
int nRC, nBytesRead;
byte[] arrbyteBarcode= new byte[100];
StringBuilder sb = new StringBuilder(" ", 100);
nBytesRead = csp2GetPacket(arrbyteBarcode[0], i, 100);
ArraySegment<byte> as = new ArraySegment<byte>(arrbyteBarcode,
nBytesRead-4, 4); // [2]
nRC = csp2TimeStamp2Str(as.Array, sb, sb.Length); // [3]
TextBox1.text= sb.toString();
[snip]

[2] and [3] are used to get a byte array segment out of the original
array.

No, it doesn't. ArraySegment<T>.Array is the entire array, not a subset.
 
B

Bob Yang

I changed something and start to read value now. even it is not what I
want yet I think I may pass the wrong bb2.. I will try to test more.
and thank you to all of you! once I am done I will post the final
codes and share with everyone.

by the way, if someone can tell me how does c# assign a value to a
parameter without "ref" or "out" it will be great. thank you!


thank you. you are right so I change to this but I still not able to
get the value for "sb". any recommadation? thank you!

byte[] bb2 = new byte[100];
nBytesRead2 = nBytesRead - 4;

for (i = 0; i < 4; i++)
{
bb2 = arrbyteBarcode[nBytesRead2];
nBytesRead2++;
}

nRC = csp2TimeStamp2Str(bb2, sb, sb.Length);


.... csp2TimeStamp2Str(byte[] value, StringBuilder Stamp, int nMaxLength);
int nRC, nBytesRead;
byte[] arrbyteBarcode= new byte[100];
StringBuilder sb = new StringBuilder(" ", 100);
nBytesRead = csp2GetPacket(arrbyteBarcode[0], i, 100);
ArraySegment<byte> as = new ArraySegment<byte>(arrbyteBarcode,
nBytesRead-4, 4); // [2]
nRC = csp2TimeStamp2Str(as.Array, sb, sb.Length); // [3]
TextBox1.text= sb.toString();
[2] and [3] are used to get a byte array segment out of the original
array.
No, it doesn't. ArraySegment<T>.Array is the entire array, not a subset.- -

- -
 
W

Willy Denoyette [MVP]

Ben Voigt said:
.... csp2TimeStamp2Str(byte[] value, StringBuilder Stamp, int
nMaxLength);

int nRC, nBytesRead;
byte[] arrbyteBarcode= new byte[100];
StringBuilder sb = new StringBuilder(" ", 100);
nBytesRead = csp2GetPacket(arrbyteBarcode[0], i, 100);
ArraySegment<byte> as = new ArraySegment<byte>(arrbyteBarcode,
nBytesRead-4, 4); // [2]
nRC = csp2TimeStamp2Str(as.Array, sb, sb.Length); // [3]
TextBox1.text= sb.toString();

[snip]
[2] and [3] are used to get a byte array segment out of the original
array.

No, it doesn't. ArraySegment<T>.Array is the entire array, not a subset.

Very true, ArraySegment is of little use in general and especially here.
Important point is that a byte[] must be passed as an argument, like this...
....
nBytesRead = csp2GetPacket(arrbyteBarcode[0], i, 100);
byte[] ba = new byte[4];
Array.Copy(arrbyteBarcode, nBytesRead - 4, ba, 0, 4);
nRC = csp2TimeStamp2Str(ba, sb, sb.Length);
...

The same applies to the csp2GetPacket function, which is wrong too.

Willy.
 
B

Bob Yang

thank you to Willy and Ben! yes, the key point is using "unsigned char
*Stamp, char *value" in c# to the right type.

1. by the way... how to make a method able to assign the value to the
parameter without using "ref" or "out"? thank you! I was surprice, it
can assign "arrbyteBarcode" and "sb" values wihtout using those
keyword ref and out!!

2. moreover, how come "char *value" =StringBuilder but "unsigned char
*Stamp" is NOT = to StringBuilder and must use byte array?






for everyone's information. here is the finally code in c#: (please
see previous post for the c++ and VB.net part)



[System.Runtime.InteropServices.DllImport("csp2.DLL")]
static extern int csp2TimeStamp2Str(byte[] value, StringBuilder
Stamp, int nMaxLength);


int nRC;
int nBytesRead, nBytesRead2;
byte[] arrbyteBarcode = new byte[100];
byte[] bb2 = new byte[100];

StringBuilder sb = new StringBuilder(" ", 100);

nBytesRead = csp2GetPacket(arrbyteBarcode, 1, 100); //get
the packages's byte data into arrbyteBarcode; return the array size to
nBytesRead

nBytesRead2 = nBytesRead - 4; // find the starting point
for the last 4 byte

for (i = 0; i < 4; i++) // assing last
4 bytes data to the new byte array
{
bb2 =
arrbyteBarcode[nBytesRead2];
nBytesRead2++;
}

////get timestamp

nRC = csp2TimeStamp2Str(bb2, sb,
100);
richTextBox1.Text = richTextBox1.Text
+ "Time: " + sb.ToString() + "\n";
 
W

Willy Denoyette [MVP]

See inline
Willy.

Bob Yang said:
thank you to Willy and Ben! yes, the key point is using "unsigned char
*Stamp, char *value" in c# to the right type.

1. by the way... how to make a method able to assign the value to the
parameter without using "ref" or "out"? thank you! I was surprice, it
can assign "arrbyteBarcode" and "sb" values wihtout using those
keyword ref and out!!

When you pass a *reference type* like byte[] (or any other array type), the
interop layer pins the array instance and passes a pointer to the first
element in the array to the callee.
It's important that you pass an array when the C function is expecting a
pointer (to an array), you should not pass a *reference* to an array
element.
// OK
[DllImport...] ..... Test(byte[] ar, int size);
byte[] ba =...
Test(ba, ...);

// NOK
[DllImport...] ... Test(ref byte, int size);
byte[] ba = ...;
Test(ref ba[0], ...);

The latter may work on 32 bit windows, but fails on 64 bit windows due to an
optimization in the interop layer of the 64 bit CLR

2. moreover, how come "char *value" =StringBuilder but "unsigned char
*Stamp" is NOT = to StringBuilder and must use byte array?
The underlying buffer of StringBuilder is a (UNICODE) char array, the
interop layer will apply the necessary conversions as defined by the
marshaling attributes (MarshalAs) applied to the parameter.

for everyone's information. here is the finally code in c#: (please
see previous post for the c++ and VB.net part)



[System.Runtime.InteropServices.DllImport("csp2.DLL")]
static extern int csp2TimeStamp2Str(byte[] value, StringBuilder
Stamp, int nMaxLength);


int nRC;
int nBytesRead, nBytesRead2;
byte[] arrbyteBarcode = new byte[100];
byte[] bb2 = new byte[100];

StringBuilder sb = new StringBuilder(" ", 100);

nBytesRead = csp2GetPacket(arrbyteBarcode, 1, 100); //get
the packages's byte data into arrbyteBarcode; return the array size to
nBytesRead

nBytesRead2 = nBytesRead - 4; // find the starting point
for the last 4 byte

for (i = 0; i < 4; i++) // assing last
4 bytes data to the new byte array
{
bb2 =
arrbyteBarcode[nBytesRead2];
nBytesRead2++;
}

////get timestamp

nRC = csp2TimeStamp2Str(bb2, sb,
100);
richTextBox1.Text = richTextBox1.Text
+ "Time: " + sb.ToString() + "\n";
 
B

Bob Yang

thank you! great information.

1. in DllImport situation, C++ is able to change value for a private
variable (I think it is because c++ will locate the variable's address
and change the value there?). what's about in the pure c# without
using any dll? for example, like the code below. is it possible to
change the passing parameters value without using "out" or "ref"?
thank you!


class OutReturnExample
{
static void Method(out int i, out string s1, out string s2)
{
i = 44;
s1 = "I've been returned";
s2 = null;
}
static void Main()
{
int value;
string str1, str2;
Method(out value, out str1, out str2);
// value is now 44
// str1 is now "I've been returned"
// str2 is (still) null;
}
}





2. if I have a c++ as (unsigned char[] ca1, char[] ca2), how can I
call it correctly in c#? is it equal to (byte[] ca1, byte[] ca2)?
Moreover, ca1 and ca2 are only passing IN the the value and c++ cannot
change the value because it is not a pointer in c++?

thank you!




See inline
Willy.


thank you to Willy and Ben! yes, the key point is using "unsigned char
*Stamp, char *value" in c# to the right type.
1. by the way... how to make a method able to assign the value to the
parameter without using "ref" or "out"? thank you! I was surprice, it
can assign "arrbyteBarcode" and "sb" values wihtout using those
keyword ref and out!!

When you pass a *reference type* like byte[] (or any other array type), the
interop layer pins the array instance and passes a pointer to the first
element in the array to the callee.
It's important that you pass an array when the C function is expecting a
pointer (to an array), you should not pass a *reference* to an array
element.
// OK
[DllImport...] ..... Test(byte[] ar, int size);
byte[] ba =...
Test(ba, ...);

// NOK
[DllImport...] ... Test(ref byte, int size);
byte[] ba = ...;
Test(ref ba[0], ...);

The latter may work on 32 bit windows, but fails on 64 bit windows due to an
optimization in the interop layer of the 64 bit CLR
2. moreover, how come "char *value" =StringBuilder but "unsigned char
*Stamp" is NOT = to StringBuilder and must use byte array?

The underlying buffer of StringBuilder is a (UNICODE) char array, the
interop layer will apply the necessary conversions as defined by the
marshaling attributes (MarshalAs) applied to the parameter.




for everyone's information. here is the finally code in c#: (please
see previous post for the c++ and VB.net part)
[System.Runtime.InteropServices.DllImport("csp2.DLL")]
static extern int csp2TimeStamp2Str(byte[] value, StringBuilder
Stamp, int nMaxLength);
int nRC;
int nBytesRead, nBytesRead2;
byte[] arrbyteBarcode = new byte[100];
byte[] bb2 = new byte[100];
StringBuilder sb = new StringBuilder(" ", 100);
nBytesRead = csp2GetPacket(arrbyteBarcode, 1, 100); //get
the packages's byte data into arrbyteBarcode; return the array size to
nBytesRead
nBytesRead2 = nBytesRead - 4; // find the starting point
for the last 4 byte
for (i = 0; i < 4; i++) // assing last
4 bytes data to the new byte array
{
bb2 =
arrbyteBarcode[nBytesRead2];
nBytesRead2++;
}

////get timestamp
nRC = csp2TimeStamp2Str(bb2, sb,
100);
richTextBox1.Text = richTextBox1.Text
+ "Time: " + sb.ToString() + "\n";- -

- -
 
B

Ben Voigt [C++ MVP]

Bob Yang said:
thank you! great information.

2. if I have a c++ as (unsigned char[] ca1, char[] ca2), how can I
call it correctly in c#? is it equal to (byte[] ca1, byte[] ca2)?

More precisely ca2 would be sbyte[], but byte[] should also work.
Moreover, ca1 and ca2 are only passing IN the the value and c++ cannot
change the value because it is not a pointer in c++?

The DLL cannot change the caller's variable to a different array. It can
however change the content of the array passed in the call, which is shared
with the caller.
 

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