By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,639 Members | 2,262 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,639 IT Pros & Developers. It's quick & easy.

Convert byte array to byte pointer

P: 12
Hi,

I have a C++ CLR class method that takes System::Byte *b as parameter argument. I want the CSharp caller pass a byte * to this function.
But in the CSharp prorgram, I only managed to create a byte array (byte[]).

Can anyone tell me how to convert byte array to byte pointer in CSharp?

Please show me an simple example about how to do it.

Thank you for your help
Edit/Delete Message
Jan 22 '09 #1
Share this Question
Share on Google+
18 Replies


Expert 100+
P: 750
PRR
@MrVS
You need to use unsafe keyword before using pointers... unsafe
sample code
Expand|Select|Wrap|Line Numbers
  1. public string ConvertToString(byte[] arr)
  2. {
  3.     unsafe
  4.     {
  5.         string returnStr;
  6.         fixed(byte* fixedPtr = arr)
  7.         {
  8.             returnStr = new string((sbyte*)fixedPtr);
  9.         }
  10.     }
  11.  
  12.     return (returnStr);
  13. }
  14.  
  15.  
  16.  
Jan 22 '09 #2

P: 12
@DeepBlue
I can't convert it to string, the "arr" in my code is a serializable object, setup as Class type in C#. I used the following codes to convert this Class type object into serializable byte array:

Expand|Select|Wrap|Line Numbers
  1.         public byte[] MQGMO_ToByteArray(MQGMOs obj)
  2.         {
  3.             if (obj == null)
  4.                 return null;
  5.             BinaryFormatter bf = new BinaryFormatter();
  6.             MemoryStream ms = new MemoryStream();
  7.             bf.Serialize(ms, obj);
  8.             return ms.ToArray();
  9.         }
  10.  
Thanks for your answer.
Jan 22 '09 #3

vekipeki
Expert 100+
P: 229
[EDIT:] Ooops, I just realized you were talking about C++/CLR. In that case, you are not calling a unmanaged dll.

Anyway, this part covers unmanaged interop.

You have to tell .Net that you want to marshal the unmanaged array to a managed byte[] object.

For example, if you have this signature in C++:
Expand|Select|Wrap|Line Numbers
  1. void DoSomething(byte* data, long size);
  2.  
You would marshal it as UnmanagedType.LPArray:

Expand|Select|Wrap|Line Numbers
  1. [DllImport ("SomeDll.dll")]
  2. public static extern void DoSomething(
  3.   [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
  4.   byte[] data,
  5.   long size);
  6.  
Note that managed array is an object which must have a known length. This is why the SizeParamIndex must be supplied (1 in the example above, indicating that parameter 1 (size) contains the size information.

Check Default Marshaling for Arrays for detailed info. Also google for "P/Invoke"; if you are using some known unmanaged/Win32 APIs, you can find lots of DllImports at pinvoke.net: the interop wiki!.
Jan 22 '09 #4

vekipeki
Expert 100+
P: 229
I am not sure that BinaryFormatter.Serialize() method will give you what you want. Have you tried checking the length of your MemoryStream after serialization? You can use Marshal.Copy Method (System.Runtime.InteropServices) instead.
Jan 22 '09 #5

P: 12
@vekipeki
Is there a big difference between Unmanaged dll and Unmanaged Interop?
Since I am not using Unmanaged dll, but the example you shown me still *import* dll the marshal it. Is this example still applicable to my current situation?

Thanks
Jan 22 '09 #6

P: 12
@vekipeki
the COPY method looks like for int type only.
Jan 23 '09 #7

P: 12
Hi,

Perhaps a simple project is more clear about this idea.
But I still can't get it working.

Here is my C# program:

Expand|Select|Wrap|Line Numbers
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections;
  4. using System.ComponentModel;
  5. using System.Data;
  6. using System.Text;
  7. using System.Reflection;
  8. using System.Runtime.InteropServices;
  9. using System.IO;
  10. using test_ptr;
  11.  
  12. namespace DebugEntry
  13. {
  14.  
  15.     class DebugEntry
  16.     {
  17.         static void Main(string[] args)
  18.         {
  19.             using (Managed managed = new Managed())
  20.             {
  21.                 byte[] test_byte = new byte[6] { 1, 2, 3, 4, 5, 6 };
  22.                 unsafe
  23.                 {
  24.                     fixed (byte* t_byte = test_byte)
  25.                     {
  26.                         Console.WriteLine(t_byte->ToString());
  27.                         Console.WriteLine(t_byte[1].ToString());
  28.                         int ret = managed.MQCBX(8, 9, &test_byte, sizeof(byte*));
  29.                        // int ret = managed.MQCBX(8, 9, &t_byte, sizeof(byte*));
  30.                     }
  31.                 }
  32.             }
  33.  
  34.             System.Console.WriteLine("Press ENTER to end the console..");
  35.             Console.ReadLine();
  36.         }
  37.  
  38.     }
  39. }
My C++ CLI file
Expand|Select|Wrap|Line Numbers
  1. test_ptr.h:
  2.  
  3. // test_ptr.h
  4.  
  5. #pragma once
  6.  
  7. #include <iostream>
  8.  
  9. using namespace System;
  10.  
  11. namespace test_ptr {
  12.  
  13.     public ref class Managed
  14.     {
  15.         public:
  16.             int MQCBX(int hConn, int op, System::Byte **cbd, int cbd_size);
  17.  
  18.             Managed() {}
  19.  
  20.             ~Managed() { } // Dispose()
  21.             !Managed() { } // finalizer
  22.     };
  23. }
  24.  
  25. int inline test_ptr::Managed::MQCBX(int hConn,int op,System::Byte **cbd, int cbd_size)
  26. {
  27.     System::Byte *buffer = new System::Byte[sizeof(System::Byte)];
  28.     memcpy(buffer, cbd, cbd_size);
  29.     //memcpy(p_cbd, &(cbd), sizeof(cbd));
  30.  
  31.     return 1;
  32. }
How can I change the cpp code and the c# code so that I can pass the byte pointer to c++ funciton(MQCBX)?


Very appreciate for your help.
Thanks
Jan 23 '09 #8

vekipeki
Expert 100+
P: 229
Please use the CODE tags when posting, it makes your code easier to read.

Since you are using memcpy to copy the contents of your array to a different memory place, it is enough to pass a pointer to test_byte. Your t_byte is a fixed pointer to a byte (byte*), so that is what your method should accept.

Second thing, this line:
Expand|Select|Wrap|Line Numbers
  1. System::Byte *buffer = new System::Byte[sizeof(System::Byte)];
doesn't make much sense because it creates an array of constant length (sizeof(Byte) is always the same). You should rather create an array of the same size as test_byte.

So, in C# you should be using something like:
Expand|Select|Wrap|Line Numbers
  1. int ret = managed.MQCBX(8, 9, t_byte, test_byte.Length);
and your C++ function should be changed to something like:
Expand|Select|Wrap|Line Numbers
  1. int inline test_ptr::Managed::MQCBX
  2.    (int hConn, int op, 
  3.     System::Byte *cbd, int cbd_size)
  4. {
  5.    System::Byte *buffer = new System::Byte[cbd_size];
  6.    memcpy(buffer, cbd, cbd_size);
  7.    return 1;
  8. }
Note however that if you are using Managed C++, there is no need for this. You can use managed data types in Visual C++ 2005. All you need to do is change your function to:

Expand|Select|Wrap|Line Numbers
  1. int inline test_ptr::Managed::MQCBX
  2.    (int hConn, int op,
  3.     array<System::Byte>^ cbd)
  4. {
  5.    // no need for memcpy, we are using managed array's Clone() method
  6.    array<System::Byte>^  buffer = (array<System::Byte>^)cbd->Clone();
  7.    return 1;
  8. }
Then, in C# you can avoid using unsafe code completely:
Expand|Select|Wrap|Line Numbers
  1. using (Managed managed = new Managed())
  2. {
  3.   byte[] test_byte = new byte[] { 1, 2, 3, 4, 5, 6 };
  4.   int ret = managed.MQCBX(8, 9, test_byte);
  5. }
  6.  
If you are using Managed C++, then you have .Net classes sitting there anyway, so why not use them to make your life easier? If you want to go for native C++, then it makes sense to use unsafe code in C#.
Jan 23 '09 #9

P: 12
Thanks for the help. Your fix works.
When I step into the MQCBX function in C++, and pull the variable name cbd the Watch list, I found there is funny character on each of the array index.
eg.
In the C++ Watch list:
cbd
-- [0] 1 ' '
-- [1] 2 'a'
-- [2] 3 'c'
etc

In the C# Watch list,
the cbd buffer is clearly printed as:
cbd
-- [0] 1
-- [1] 2
-- [2] 3
etc

I don't know whether this is the cbd buffer/pointer becomes garbage when it calls the C++ MQCBX function.

What is wrong with it?

Here is the complete code:
in C#:
Expand|Select|Wrap|Line Numbers
  1. using System.IO;
  2. using byte_test;
  3.  
  4. namespace DebugEntry
  5. {
  6.  
  7.     class DebugEntry
  8.     {
  9.         static void Main(string[] args)
  10.         {
  11.             using (Managed managed = new Managed())
  12.             {
  13.                byte[] test_byte = new byte[] { 1, 2, 3, 4, 5, 6 };
  14.                int ret = managed.MQCBX(8, 9, test_byte);
  15.             }
  16.  
  17.         }
  18.     }
  19. }
  20. [/quote]
  21.  
  22. In C++ CLI:
  23. [quote]
  24. pragma once
  25.  
  26. using namespace System;
  27.  
  28. namespace byte_test {
  29.  
  30.     public ref class Managed
  31.     {
  32.         public:
  33.             int MQCBX(int hConn, int op,array<System::Byte>^ cbd);
  34.  
  35.             Managed() {}
  36.  
  37.             ~Managed() { } // Dispose()
  38.             !Managed() { } // finalizer
  39.     };
  40. }
  41.  
  42.  
  43. int inline byte_test::Managed::MQCBX(int hConn, int op,array<System::Byte>^ cbd)
  44. {
  45.         array<System::Byte>^  buffer = {array<System::Byte>^)cbd->Clone();
  46.         return 1;
  47. }
  48.  
  49.  
edit by mod: [code] tags, not [quote] tags. Makes it easier to read your code.
--insertAlias (mod)

Thanks
Jan 23 '09 #10

vekipeki
Expert 100+
P: 229
What do you mean by "funny character"? Are they really ' ', 'a', 'c' as you wrote, or some different characters? C++ Watch window might show you the Ascii representation (char) for each byte, but if the value is correct, that's ok.

If this code outputs "123456", then it works:
Expand|Select|Wrap|Line Numbers
  1. for (int i = 0; i<buffer->Length; i++)
  2.      System::Console::Write(buffer[i]);
Jan 23 '09 #11

P: 12
@vekipeki
OK, it works, it prints 123456.
The *funny character* at the end of each index array is non-printable character.
Not quite understand why they are there after passed into C++.

Thank you very much for the help again.
REALLY REALLY appreciate for your help.
Jan 23 '09 #12

P: 12
Do you know how to copy the "array<System::Byte>^ cbd" into a
"System::Byte *" ?
eg.
Either this:
Expand|Select|Wrap|Line Numbers
  1.   System::Byte *buffer = (System::Byte *)(array<System::Byte>^)cbd->Clone();
  2.  
OR:
Expand|Select|Wrap|Line Numbers
  1.    array<System::Byte>^ buffer = (array<System::Byte>^)cbd->Clone();
  2.    p_mqcbd = new MQCBD[buffer->Length+1];
  3.     for (int i = 0; i<buffer->Length; i++)
  4.         strcpy(p_mqcbd[i], buffer[i]);
  5.  
is wrong.

Thanks
Jan 25 '09 #13

vekipeki
Expert 100+
P: 229
Use pin_ptr to pin a pointer to your managed array, then do something with that pointer.

Expand|Select|Wrap|Line Numbers
  1. int inline byte_test::Managed::MQCBX(int hConn,int op, array<System::Byte>^ cbd)
  2. {
  3.     array<System::Byte>^  buffer = (array<System::Byte>^)cbd->Clone();
  4.  
  5.     {
  6.         // you must _pin_ the pointer to prevent GC from
  7.         // collecting
  8.         pin_ptr<unsigned char> pin_buffer = &buffer[0];
  9.         unsigned char* p = pin_buffer;
  10.  
  11.         // call some native function with 'p' as parameter
  12.         // NativeFunction(p);
  13.  
  14.         // when 'p' goes out of scope, it is _unpinned_, 
  15.         // so it can be collected again
  16.     }
  17.  
  18.     return 1;
  19. };
Note that 'p' should only be a local variable, because you don't want your object to be fixed for a long time.

Also note the difference between System::Byte, which is a managed class, and unsigned char, which is a native C++ type.
Jan 26 '09 #14

P: 12
Hi,

I just found out the caller of the MQCBX function passed in a serialized object "cbd* from the CSharp program.
eg. in C#
Expand|Select|Wrap|Line Numbers
  1. private byte[] MQCBD_ToByteArray(MQCBDs obj)
  2.         {
  3.             if (obj == null)
  4.                 return null;
  5.             BinaryFormatter bf = new BinaryFormatter();
  6.             MemoryStream ms = new MemoryStream();
  7.             try
  8.             {
  9.                 bf.Serialize(ms, obj);
  10.             }
  11.             catch (Exception Exp)
  12.             {
  13.                 // report the error
  14.                 System.Console.WriteLine("MQQueue::Get ended with " + Exp.Message);
  15.             }             
  16.             return ms.ToArray();
  17.         }
  18.  
The problem now is how can I deserialize the "cbd" object in VC++ (CLI)?
In CSharp, do I really need to serialize the "cbd" object into byte-array before passing it to the VC++ function and deserialize it there?



Thanks for the hellp.
Jan 27 '09 #15

P: 12
@MrVS
The MQCBDs object is defined as a class in CSharp:
Expand|Select|Wrap|Line Numbers
  1. [Serializable,StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
  2.     public class MQCBDs
  3.     {
  4.         public const string MQCBD_STRUC_ID = "CBD ";
  5.         public const int MQCBD_VERSION_1 = 1;
  6.         public const int MQCBD_CURRENT_VERSION = 1;
  7.         public const int MQCBDO_NONE = 0x00000000;
  8.         public const int MQCBDO_START_CALL = 0x00000001;
  9.         public const int MQCBDO_STOP_CALL = 0x00000004;
  10.         public const int MQCBDO_REGISTER_CALL = 0x00000100;
  11.         public const int MQCBDO_DEREGISTER_CALL = 0x00000200;
  12.         public const int MQCBDO_FAIL_IF_QUIESCING = 0x00002000;
  13.         public const int MQCBT_MESSAGE_CONSUMER = 0x00000001;
  14.         public const int MQCBT_EVENT_HANDLER = 0x00000002;
  15.         public const int MQCBD_FULL_MSG_LENGTH = -1;
  16.  
  17.         //    MQCHAR4    StrucId;           /* Structure identifier */
  18.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  19.         public byte[] StrucId;
  20.         //    MQLONG     Version;           /* Structure version number */
  21.         public int Version;
  22.         //    MQLONG     CallbackType;      /* Callback function type */
  23.         public int CallbackType;
  24.         //    MQLONG     Options;           /* Options controlling message
  25.         public int Options;
  26.         //                                     consumption */
  27.         //    MQPTR      CallbackArea;      /* User data passed to the function */
  28.         public byte[] CallbackArea;
  29.         //    MQPTR      CallbackFunction;  /* FP: Callback function pointer */
  30.         //public mqCallBacks CallbackFunction;
  31.         //    MQCHAR128  CallbackName;      /* Callback name */
  32.         //       [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
  33.         //     public string CallbackName;
  34.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
  35.         public byte[] CallbackName;
  36.  
  37.         //    MQLONG     MaxMsgLength;      /* Maximum message length */
  38.         public int MaxMsgLength;
  39.  
  40.         public MQCBDs()
  41.         {
  42.             StrucId = utilities.getBytes(MQCBD_STRUC_ID);
  43.             //StrucId = getBytes(MQCBD_STRUC_ID);
  44.             Version = MQCBD_VERSION_1;
  45.             CallbackType = MQCBT_MESSAGE_CONSUMER;
  46.             Options = MQCBDO_NONE;
  47.             CallbackArea = null;
  48.             //CallbackFunction = null;
  49.             CallbackName = new byte[128];
  50.             Array.Clear(CallbackName, 0, CallbackName.Length);
  51.             MaxMsgLength = MQCBD_FULL_MSG_LENGTH;
  52.         }
  53.     }
  54.  
My goal is pass this MQCBDs object from CSharp to VC++(CLI).

Thanks
Jan 27 '09 #16

vekipeki
Expert 100+
P: 229
Are you writing both programs, or is one of them already written and cannot be changed?

Because there is no problem in passing a managed object between C# and C+/CLI -- they both accept managed code. There is no need to do any conversion.

Avoid using .NET internal binary serialization when passing objects between applications written in different languages. If you need to get a native object, use marshaling. But here, you can simply pass a managed object.

The easiest way would be to define your MQCBDs class in C++/CLI (byte_test namespace). Then your can import it to C# (using byte_test;), and you have your same managed class in both applications.
Jan 27 '09 #17

P: 12
If I can do it by passing C# class object to C++ CLI, I want to see how complicated it is. Because my programmers want to write as much as they can in C# including the MQCBDs class, then pass the MQCBDs object into C++ CLI for a lower level operation. There are just too many similar MQXXX classes/objects they have created in C#, they dont' want to convert all of them into C++ class.

Is there any example I can follow to pass C# class type object into C++ CLI?

Thanks.
Jan 27 '09 #18

vekipeki
Expert 100+
P: 229
@MrVS
But won't you be needing all those classes in C++ anyway? What good does it do it just pass a serialized object, if you don't have a class in C++ to deserialize it?

If you want to have a native struct in plain C++ (again, not CLI, because in CLI there is no need for this - it is a managed language), then you can use Marshal.Copy (http://msdn.microsoft.com/en-us/library/ms146631.aspx, I mentioned it a couple of post ago). If you add MarshalAs attributes do define the way fields are marshaled to the native side, then Marshal.Copy will create a byte array, which can be directly cast to a native struct in C++:

Expand|Select|Wrap|Line Numbers
  1. public static byte[] SerializeExact( object anything )
  2. {
  3.   int structsize = Marshal.SizeOf( anything );
  4.   IntPtr buffer = Marshal.AllocHGlobal( structsize );
  5.   Marshal.StructureToPtr( anything, buffer, false );
  6.   byte[] streamdatas = new byte[ structsize ];
  7.   Marshal.Copy( buffer, streamdatas, 0, structsize );
  8.   Marshal.FreeHGlobal( buffer );
  9.   return streamdatas;
  10. }
  11.  
and the matching Deserialize:

Expand|Select|Wrap|Line Numbers
  1. public static object RawDeserialize( byte[] rawdatas, Type anytype )
  2. {
  3.   int rawsize = Marshal.SizeOf( anytype );
  4.   if( rawsize > rawdatas.Length ) return null;
  5.   IntPtr buffer = Marshal.AllocHGlobal( rawsize );
  6.   Marshal.Copy( rawdatas, 0, buffer, rawsize );
  7.   object retobj = Marshal.PtrToStructure( buffer, anytype );
  8.   Marshal.FreeHGlobal( buffer );
  9.   return retobj;
  10. }
Of course, to get the struct back in C++, you need to define it in C++ anyway, so I am not quite sure I understand why you need to do it this way.
Jan 27 '09 #19

Post your reply

Sign in to post your reply or Sign up for a free account.