Convert byte array to byte pointer

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • MrVS
    New Member
    • Jan 2009
    • 12

    Convert byte array to byte pointer

    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
  • PRR
    Recognized Expert Contributor
    • Dec 2007
    • 750

    #2
    Originally posted by MrVS
    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
    You need to use unsafe keyword before using pointers... unsafe
    sample code
    Code:
    public string ConvertToString(byte[] arr)
    {
        unsafe
        {
            string returnStr;
            fixed(byte* fixedPtr = arr)
            {
                returnStr = new string((sbyte*)fixedPtr);
            }
        }
    
        return (returnStr);
    }

    Comment

    • MrVS
      New Member
      • Jan 2009
      • 12

      #3
      Originally posted by DeepBlue
      You need to use unsafe keyword before using pointers... unsafe
      sample code
      Code:
      public string ConvertToString(byte[] arr)
      {
          unsafe
          {
              string returnStr;
              fixed(byte* fixedPtr = arr)
              {
                  returnStr = new string((sbyte*)fixedPtr);
              }
          }
      
          return (returnStr);
      }
      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:

      Code:
              public byte[] MQGMO_ToByteArray(MQGMOs obj)
              {
                  if (obj == null)
                      return null;
                  BinaryFormatter bf = new BinaryFormatter();
                  MemoryStream ms = new MemoryStream();
                  bf.Serialize(ms, obj);
                  return ms.ToArray();
              }
      Thanks for your answer.

      Comment

      • vekipeki
        Recognized Expert New Member
        • Nov 2007
        • 229

        #4
        [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++:
        Code:
        void DoSomething(byte* data, long size);
        You would marshal it as UnmanagedType.L PArray:

        Code:
        [DllImport ("SomeDll.dll")]
        public static extern void DoSomething(
          [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
          byte[] data,
          long size);
        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!.

        Comment

        • vekipeki
          Recognized Expert New Member
          • Nov 2007
          • 229

          #5
          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 .InteropService s) instead.

          Comment

          • MrVS
            New Member
            • Jan 2009
            • 12

            #6
            Originally posted by vekipeki
            [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++:
            Code:
            void DoSomething(byte* data, long size);
            You would marshal it as UnmanagedType.L PArray:

            Code:
            [DllImport ("SomeDll.dll")]
            public static extern void DoSomething(
              [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
              byte[] data,
              long size);
            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!.
            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

            Comment

            • MrVS
              New Member
              • Jan 2009
              • 12

              #7
              Originally posted by vekipeki
              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 .InteropService s) instead.
              the COPY method looks like for int type only.

              Comment

              • MrVS
                New Member
                • Jan 2009
                • 12

                #8
                Hi,

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

                Here is my C# program:

                Code:
                using System;
                using System.Collections.Generic;
                using System.Collections;
                using System.ComponentModel;
                using System.Data;
                using System.Text;
                using System.Reflection;
                using System.Runtime.InteropServices;
                using System.IO;
                using test_ptr;
                
                namespace DebugEntry
                {
                
                    class DebugEntry
                    {
                        static void Main(string[] args)
                        {
                            using (Managed managed = new Managed())
                            {
                                byte[] test_byte = new byte[6] { 1, 2, 3, 4, 5, 6 };
                                unsafe
                                {
                                    fixed (byte* t_byte = test_byte)
                                    {
                                        Console.WriteLine(t_byte->ToString());
                                        Console.WriteLine(t_byte[1].ToString());
                                        int ret = managed.MQCBX(8, 9, &test_byte, sizeof(byte*));
                                       // int ret = managed.MQCBX(8, 9, &t_byte, sizeof(byte*));
                                    }
                                }
                            }
                
                            System.Console.WriteLine("Press ENTER to end the console..");
                            Console.ReadLine();
                        }
                
                    }
                }
                My C++ CLI file
                Code:
                test_ptr.h:
                
                // test_ptr.h
                
                #pragma once
                
                #include <iostream>
                
                using namespace System;
                
                namespace test_ptr {
                
                	public ref class Managed
                	{
                		public:
                			int MQCBX(int hConn, int op, System::Byte **cbd, int cbd_size);
                
                			Managed() {}
                
                			~Managed() { } // Dispose()
                			!Managed() { } // finalizer
                	};
                }
                
                int inline test_ptr::Managed::MQCBX(int hConn,int op,System::Byte **cbd, int cbd_size)
                {
                	System::Byte *buffer = new System::Byte[sizeof(System::Byte)];
                	memcpy(buffer, cbd, cbd_size);
                	//memcpy(p_cbd, &(cbd), sizeof(cbd));
                
                	return 1;
                }
                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

                Comment

                • vekipeki
                  Recognized Expert New Member
                  • Nov 2007
                  • 229

                  #9
                  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:
                  Code:
                  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:
                  Code:
                  int ret = managed.MQCBX(8, 9, t_byte, test_byte.Length);
                  and your C++ function should be changed to something like:
                  Code:
                  int inline test_ptr::Managed::MQCBX
                     (int hConn, int op, 
                      System::Byte *cbd, int cbd_size)
                  {
                     System::Byte *buffer = new System::Byte[cbd_size];
                     memcpy(buffer, cbd, cbd_size);
                     return 1;
                  }
                  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:

                  Code:
                  int inline test_ptr::Managed::MQCBX
                     (int hConn, int op,
                      array<System::Byte>^ cbd)
                  {
                     // no need for memcpy, we are using managed array's Clone() method
                     array<System::Byte>^  buffer = (array<System::Byte>^)cbd->Clone();
                     return 1;
                  }
                  Then, in C# you can avoid using unsafe code completely:
                  Code:
                  using (Managed managed = new Managed())
                  {
                    byte[] test_byte = new byte[] { 1, 2, 3, 4, 5, 6 };
                    int ret = managed.MQCBX(8, 9, test_byte);
                  }
                  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#.

                  Comment

                  • MrVS
                    New Member
                    • Jan 2009
                    • 12

                    #10
                    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#:
                    Code:
                    using System.IO;
                    using byte_test;
                    
                    namespace DebugEntry
                    {
                    
                        class DebugEntry
                        {
                            static void Main(string[] args)
                            {
                                using (Managed managed = new Managed())
                                {
                                   byte[] test_byte = new byte[] { 1, 2, 3, 4, 5, 6 };
                                   int ret = managed.MQCBX(8, 9, test_byte);
                                }
                    
                            }
                        }
                    }
                    [/QUOTE]
                    
                    In C++ CLI:
                    [QUOTE]
                    pragma once
                    
                    using namespace System;
                    
                    namespace byte_test {
                    
                    	public ref class Managed
                    	{
                    		public:
                    			int MQCBX(int hConn, int op,array<System::Byte>^ cbd);
                    
                    			Managed() {}
                    
                    			~Managed() { } // Dispose()
                    			!Managed() { } // finalizer
                    	};
                    }
                    
                    
                    int inline byte_test::Managed::MQCBX(int hConn, int op,array<System::Byte>^ cbd)
                    {
                    		array<System::Byte>^  buffer = {array<System::Byte>^)cbd->Clone();
                    		return 1;
                    }
                    edit by mod: [CODE] tags, not [QUOTE] tags. Makes it easier to read your code.
                    --insertAlias (mod)

                    Thanks

                    Comment

                    • vekipeki
                      Recognized Expert New Member
                      • Nov 2007
                      • 229

                      #11
                      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:
                      Code:
                      for (int i = 0; i<buffer->Length; i++)
                           System::Console::Write(buffer[i]);

                      Comment

                      • MrVS
                        New Member
                        • Jan 2009
                        • 12

                        #12
                        Originally posted by vekipeki
                        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:
                        Code:
                        for (int i = 0; i<buffer->Length; i++)
                             System::Console::Write(buffer[i]);
                        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.

                        Comment

                        • MrVS
                          New Member
                          • Jan 2009
                          • 12

                          #13
                          Do you know how to copy the "array<System:: Byte>^ cbd" into a
                          "System::By te *" ?
                          eg.
                          Either this:
                          Code:
                            System::Byte *buffer = (System::Byte *)(array<System::Byte>^)cbd->Clone();
                          OR:
                          Code:
                             array<System::Byte>^ buffer = (array<System::Byte>^)cbd->Clone();
                             p_mqcbd = new MQCBD[buffer->Length+1];
                          	for (int i = 0; i<buffer->Length; i++)
                          		strcpy(p_mqcbd[i], buffer[i]);
                          is wrong.

                          Thanks

                          Comment

                          • vekipeki
                            Recognized Expert New Member
                            • Nov 2007
                            • 229

                            #14
                            Use pin_ptr to pin a pointer to your managed array, then do something with that pointer.

                            Code:
                            int inline byte_test::Managed::MQCBX(int hConn,int op, array<System::Byte>^ cbd)
                            {
                            	array<System::Byte>^  buffer = (array<System::Byte>^)cbd->Clone();
                            
                            	{
                            		// you must _pin_ the pointer to prevent GC from
                            		// collecting
                            		pin_ptr<unsigned char> pin_buffer = &buffer[0];
                            		unsigned char* p = pin_buffer;
                            
                            		// call some native function with 'p' as parameter
                            		// NativeFunction(p);
                            
                            		// when 'p' goes out of scope, it is _unpinned_, 
                            		// so it can be collected again
                            	}
                            	
                            	return 1;
                            };
                            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.

                            Comment

                            • MrVS
                              New Member
                              • Jan 2009
                              • 12

                              #15
                              Hi,

                              I just found out the caller of the MQCBX function passed in a serialized object "cbd* from the CSharp program.
                              eg. in C#
                              Code:
                              private byte[] MQCBD_ToByteArray(MQCBDs obj)
                                      {
                                          if (obj == null)
                                              return null;
                                          BinaryFormatter bf = new BinaryFormatter();
                                          MemoryStream ms = new MemoryStream();
                                          try
                                          {
                                              bf.Serialize(ms, obj);
                                          }
                                          catch (Exception Exp)
                                          {
                                              // report the error
                                              System.Console.WriteLine("MQQueue::Get ended with " + Exp.Message);
                                          }             
                                          return ms.ToArray();
                                      }
                              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.

                              Comment

                              Working...