Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Calling C++ Image API from C#

Calling C++ Image API from C#

Scheduled Pinned Locked Moved C#
csharpc++data-structuresjsonperformance
24 Posts 4 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Offline
    E Offline
    econner
    wrote on last edited by
    #1

    I am working on implimenting a 3rd party API written in C++. The API pulls an image from a scanner. I have tried a few different ways of of working with the image struct but every attempt causes memory crashes with P/Invoke. Any suggestions would be appreciated. Here is the C++ code and sample that comes with the device.

    //(From header file)
    //SCANDLL_API int __stdcall WScanSelectBuf(MY_IMAGE *simage_down,MY_IMAGE *simage_up,int select);

    //(structure from C++ documentation
    typedef struct {
    int width; // image width
    int height; // image height
    int info; // bit-count (1,4,8 bit)
    unsigned char* pbuf; // image buffer
    } MY_IMAGE; // defines the image information

    //(Use of method in sample C++ code)

    MY_IMAGE img1,img2;

    img1.width = 864;
    img1.height = 3000;
    img1.info = 8; // 8bit
    img1.pbuf = (UCHAR *)new UCHAR[2*nWidth*nHeight];

    img2.width = 864;
    img2.height = 3000;
    img2.info = 8; // 8bit
    img2.pbuf = (UCHAR *)new UCHAR[2*nWidth*nHeight];

    ret=WScanSelectBuf(&img1, &img2,0);


    //C# conversion

    [StructLayout(LayoutKind.Explicit)]
    public struct MY_IMAGE
    {
    [FieldOffset(0)]
    public int width;

        \[FieldOffset(4)\]
        public int height;
        
        \[FieldOffset(8)\]
        public int info;
        
        //\[FieldOffset(12)\]
        //public byte\[\] pbuf; // image buffer as byte array???
    
        //Stuck here? 
        \[FieldOffset(12), MarshalAs(UnmanagedType.SysUInt)\]
        public IntPtr pbuf; // image buffer
    }
    

    [DllImport("scandll.dll")]
    private extern static int WT_ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select);

    public static int ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select)
    {
    return WScanSelectBuf(ref image_down, ref image_up, select);
    }

    ImageInfo img1 = new ImageInfo();
    ImageInfo img2 = new ImageInfo();

    int nWidth = 864;
    int nHeight = 1000;

    img1.width = nWidth;
    img1.height = nHeight;
    img1.info = 8; // 8bit
    //img1.pbuf = new byte[2 *nWidth * nHeight];
    //img1.pbuf = IntPtr.Zero;

    img2.width = nWidth;
    img2.height = nHeight;
    img2.info = 8; // 8bit
    //img2.pbuf = new byte[2 * nWidth * nHeight];
    //img2.pbuf = IntPtr.Zero;

    //CRASHES WITH MEMORY ACCESS/CORRUPT ERRORS
    ret = ScanSelectBuf(ref img1,ref img2, 0);

    _ L E J 4 Replies Last reply
    0
    • E econner

      I am working on implimenting a 3rd party API written in C++. The API pulls an image from a scanner. I have tried a few different ways of of working with the image struct but every attempt causes memory crashes with P/Invoke. Any suggestions would be appreciated. Here is the C++ code and sample that comes with the device.

      //(From header file)
      //SCANDLL_API int __stdcall WScanSelectBuf(MY_IMAGE *simage_down,MY_IMAGE *simage_up,int select);

      //(structure from C++ documentation
      typedef struct {
      int width; // image width
      int height; // image height
      int info; // bit-count (1,4,8 bit)
      unsigned char* pbuf; // image buffer
      } MY_IMAGE; // defines the image information

      //(Use of method in sample C++ code)

      MY_IMAGE img1,img2;

      img1.width = 864;
      img1.height = 3000;
      img1.info = 8; // 8bit
      img1.pbuf = (UCHAR *)new UCHAR[2*nWidth*nHeight];

      img2.width = 864;
      img2.height = 3000;
      img2.info = 8; // 8bit
      img2.pbuf = (UCHAR *)new UCHAR[2*nWidth*nHeight];

      ret=WScanSelectBuf(&img1, &img2,0);


      //C# conversion

      [StructLayout(LayoutKind.Explicit)]
      public struct MY_IMAGE
      {
      [FieldOffset(0)]
      public int width;

          \[FieldOffset(4)\]
          public int height;
          
          \[FieldOffset(8)\]
          public int info;
          
          //\[FieldOffset(12)\]
          //public byte\[\] pbuf; // image buffer as byte array???
      
          //Stuck here? 
          \[FieldOffset(12), MarshalAs(UnmanagedType.SysUInt)\]
          public IntPtr pbuf; // image buffer
      }
      

      [DllImport("scandll.dll")]
      private extern static int WT_ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select);

      public static int ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select)
      {
      return WScanSelectBuf(ref image_down, ref image_up, select);
      }

      ImageInfo img1 = new ImageInfo();
      ImageInfo img2 = new ImageInfo();

      int nWidth = 864;
      int nHeight = 1000;

      img1.width = nWidth;
      img1.height = nHeight;
      img1.info = 8; // 8bit
      //img1.pbuf = new byte[2 *nWidth * nHeight];
      //img1.pbuf = IntPtr.Zero;

      img2.width = nWidth;
      img2.height = nHeight;
      img2.info = 8; // 8bit
      //img2.pbuf = new byte[2 * nWidth * nHeight];
      //img2.pbuf = IntPtr.Zero;

      //CRASHES WITH MEMORY ACCESS/CORRUPT ERRORS
      ret = ScanSelectBuf(ref img1,ref img2, 0);

      _ Offline
      _ Offline
      _Erik_
      wrote on last edited by
      #2

      At a first glance, this is what I would do: Declare MY_IMAGE as a class, not as a struct; use LayoutKind.Sequential instead of Explicit; remove FieldOffset attributes; do not use MarshalAs for pbuf field (pbuf should be an IntPtr); import the function without the ref keywords. To assign the value to pbuf field you need to have the bytes in a byte[] array. Then you just have to set pbuf to the address of that array, using Marshal.UnsafeAddrOfPinnedArrayElement method.

      E 1 Reply Last reply
      0
      • E econner

        I am working on implimenting a 3rd party API written in C++. The API pulls an image from a scanner. I have tried a few different ways of of working with the image struct but every attempt causes memory crashes with P/Invoke. Any suggestions would be appreciated. Here is the C++ code and sample that comes with the device.

        //(From header file)
        //SCANDLL_API int __stdcall WScanSelectBuf(MY_IMAGE *simage_down,MY_IMAGE *simage_up,int select);

        //(structure from C++ documentation
        typedef struct {
        int width; // image width
        int height; // image height
        int info; // bit-count (1,4,8 bit)
        unsigned char* pbuf; // image buffer
        } MY_IMAGE; // defines the image information

        //(Use of method in sample C++ code)

        MY_IMAGE img1,img2;

        img1.width = 864;
        img1.height = 3000;
        img1.info = 8; // 8bit
        img1.pbuf = (UCHAR *)new UCHAR[2*nWidth*nHeight];

        img2.width = 864;
        img2.height = 3000;
        img2.info = 8; // 8bit
        img2.pbuf = (UCHAR *)new UCHAR[2*nWidth*nHeight];

        ret=WScanSelectBuf(&img1, &img2,0);


        //C# conversion

        [StructLayout(LayoutKind.Explicit)]
        public struct MY_IMAGE
        {
        [FieldOffset(0)]
        public int width;

            \[FieldOffset(4)\]
            public int height;
            
            \[FieldOffset(8)\]
            public int info;
            
            //\[FieldOffset(12)\]
            //public byte\[\] pbuf; // image buffer as byte array???
        
            //Stuck here? 
            \[FieldOffset(12), MarshalAs(UnmanagedType.SysUInt)\]
            public IntPtr pbuf; // image buffer
        }
        

        [DllImport("scandll.dll")]
        private extern static int WT_ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select);

        public static int ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select)
        {
        return WScanSelectBuf(ref image_down, ref image_up, select);
        }

        ImageInfo img1 = new ImageInfo();
        ImageInfo img2 = new ImageInfo();

        int nWidth = 864;
        int nHeight = 1000;

        img1.width = nWidth;
        img1.height = nHeight;
        img1.info = 8; // 8bit
        //img1.pbuf = new byte[2 *nWidth * nHeight];
        //img1.pbuf = IntPtr.Zero;

        img2.width = nWidth;
        img2.height = nHeight;
        img2.info = 8; // 8bit
        //img2.pbuf = new byte[2 * nWidth * nHeight];
        //img2.pbuf = IntPtr.Zero;

        //CRASHES WITH MEMORY ACCESS/CORRUPT ERRORS
        ret = ScanSelectBuf(ref img1,ref img2, 0);

        L Offline
        L Offline
        Luc Pattyn
        wrote on last edited by
        #3

        It is very fortunate the caller is responsible for the buffer, so you can have it all managed. You need to allocate a byte array of sufficient capacity, and pass its pointer (an IntPtr in .NET); you won't need any Marshal attributes, not even any unsafe stuff. Look for the GCHandle class, and read how I pass arrays in this article[^]. Don't forget to free the GCHandle once you don't need it any more (which could be well after the return of the first native function, if it happens to keep the pointer around). :)

        Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum

        Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.

        1 Reply Last reply
        0
        • _ _Erik_

          At a first glance, this is what I would do: Declare MY_IMAGE as a class, not as a struct; use LayoutKind.Sequential instead of Explicit; remove FieldOffset attributes; do not use MarshalAs for pbuf field (pbuf should be an IntPtr); import the function without the ref keywords. To assign the value to pbuf field you need to have the bytes in a byte[] array. Then you just have to set pbuf to the address of that array, using Marshal.UnsafeAddrOfPinnedArrayElement method.

          E Offline
          E Offline
          econner
          wrote on last edited by
          #4

          Hi Erik, thanks for the suggestion. Below are the changes I have made. However, I am still receiving memory errors.

          [StructLayout(LayoutKind.Sequential)]
          public class MY_IMAGE
          {

          public int width; // image width
          public int height; // image height
          public int info; // bit-count (1,4,8 bit)
          public IntPtr pbuf; // image buffer
          }

          [DllImport("scandll.dll")]
          private extern static int WScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select);

          public static int ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select)
          {
          return WScanSelectBuf(image_down, image_up, select);
          }

          And also

          public static int ScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select)
          {
          return WScanSelectBuf(image_down, image_up, select);
          }

          The API does not provide any other way to fill the MY_IMAGE struct other than calling: private extern static int WScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select); So, I do not have acces to fill the buffer with a byte[]. This should be happening from caling the method. Erik - Is there anything I missed? If so, do you have an example? Luc - Would you be able to provide an example of what you were referring to?

          _ 1 Reply Last reply
          0
          • E econner

            Hi Erik, thanks for the suggestion. Below are the changes I have made. However, I am still receiving memory errors.

            [StructLayout(LayoutKind.Sequential)]
            public class MY_IMAGE
            {

            public int width; // image width
            public int height; // image height
            public int info; // bit-count (1,4,8 bit)
            public IntPtr pbuf; // image buffer
            }

            [DllImport("scandll.dll")]
            private extern static int WScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select);

            public static int ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select)
            {
            return WScanSelectBuf(image_down, image_up, select);
            }

            And also

            public static int ScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select)
            {
            return WScanSelectBuf(image_down, image_up, select);
            }

            The API does not provide any other way to fill the MY_IMAGE struct other than calling: private extern static int WScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select); So, I do not have acces to fill the buffer with a byte[]. This should be happening from caling the method. Erik - Is there anything I missed? If so, do you have an example? Luc - Would you be able to provide an example of what you were referring to?

            _ Offline
            _ Offline
            _Erik_
            wrote on last edited by
            #5

            econner wrote:

            So, I do not have acces to fill the buffer with a byte[]. This should be happening from caling the method.

            I understand that the buffers are filled by WScanSelectBuf, am I right? If so, maybe you have to allocate these buffers before calling the function, I mean, create the instances of byte[] array, pin them with GCHandle and assing the address to the pbuf field of the MY_IMAGE objects. Can you give us a link to the documentation for the C++ library you are using? I would like to know what the three parameters are for, and what the WScanSelectBuf function is supposed to do. I think we might be able to give you a better help with a little more information.

            E 1 Reply Last reply
            0
            • E econner

              I am working on implimenting a 3rd party API written in C++. The API pulls an image from a scanner. I have tried a few different ways of of working with the image struct but every attempt causes memory crashes with P/Invoke. Any suggestions would be appreciated. Here is the C++ code and sample that comes with the device.

              //(From header file)
              //SCANDLL_API int __stdcall WScanSelectBuf(MY_IMAGE *simage_down,MY_IMAGE *simage_up,int select);

              //(structure from C++ documentation
              typedef struct {
              int width; // image width
              int height; // image height
              int info; // bit-count (1,4,8 bit)
              unsigned char* pbuf; // image buffer
              } MY_IMAGE; // defines the image information

              //(Use of method in sample C++ code)

              MY_IMAGE img1,img2;

              img1.width = 864;
              img1.height = 3000;
              img1.info = 8; // 8bit
              img1.pbuf = (UCHAR *)new UCHAR[2*nWidth*nHeight];

              img2.width = 864;
              img2.height = 3000;
              img2.info = 8; // 8bit
              img2.pbuf = (UCHAR *)new UCHAR[2*nWidth*nHeight];

              ret=WScanSelectBuf(&img1, &img2,0);


              //C# conversion

              [StructLayout(LayoutKind.Explicit)]
              public struct MY_IMAGE
              {
              [FieldOffset(0)]
              public int width;

                  \[FieldOffset(4)\]
                  public int height;
                  
                  \[FieldOffset(8)\]
                  public int info;
                  
                  //\[FieldOffset(12)\]
                  //public byte\[\] pbuf; // image buffer as byte array???
              
                  //Stuck here? 
                  \[FieldOffset(12), MarshalAs(UnmanagedType.SysUInt)\]
                  public IntPtr pbuf; // image buffer
              }
              

              [DllImport("scandll.dll")]
              private extern static int WT_ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select);

              public static int ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select)
              {
              return WScanSelectBuf(ref image_down, ref image_up, select);
              }

              ImageInfo img1 = new ImageInfo();
              ImageInfo img2 = new ImageInfo();

              int nWidth = 864;
              int nHeight = 1000;

              img1.width = nWidth;
              img1.height = nHeight;
              img1.info = 8; // 8bit
              //img1.pbuf = new byte[2 *nWidth * nHeight];
              //img1.pbuf = IntPtr.Zero;

              img2.width = nWidth;
              img2.height = nHeight;
              img2.info = 8; // 8bit
              //img2.pbuf = new byte[2 * nWidth * nHeight];
              //img2.pbuf = IntPtr.Zero;

              //CRASHES WITH MEMORY ACCESS/CORRUPT ERRORS
              ret = ScanSelectBuf(ref img1,ref img2, 0);

              E Offline
              E Offline
              econner
              wrote on last edited by
              #6

              This was an example of another library that I was using as reference. It was confusing so I removed it since it is not the code I am having issues with.

              modified on Thursday, January 13, 2011 8:48 PM

              L 1 Reply Last reply
              0
              • _ _Erik_

                econner wrote:

                So, I do not have acces to fill the buffer with a byte[]. This should be happening from caling the method.

                I understand that the buffers are filled by WScanSelectBuf, am I right? If so, maybe you have to allocate these buffers before calling the function, I mean, create the instances of byte[] array, pin them with GCHandle and assing the address to the pbuf field of the MY_IMAGE objects. Can you give us a link to the documentation for the C++ library you are using? I would like to know what the three parameters are for, and what the WScanSelectBuf function is supposed to do. I think we might be able to give you a better help with a little more information.

                E Offline
                E Offline
                econner
                wrote on last edited by
                #7

                Correct, on the buffer. Here is an excert from the docs: //Scan to buffer. int WScanSelectBuf(void* image_down, void* image_up ,int select) (param) image_down [out] structure for forward direction scan. image_up [in] NULL (reserved) select [in] 0 : forward direction & single scan (use image_down) 1 : backward direction & single scan (use image_up) 2 : both direction & single scan (use image_down & image_up) 3 : forward direction & dual scan (use image_down) 4 : backward direction& dual scan (use image_up) 5: both direction & dual scan (use image_down& image_up) Return value - Success 0, Fail > 0

                _ 1 Reply Last reply
                0
                • E econner

                  This was an example of another library that I was using as reference. It was confusing so I removed it since it is not the code I am having issues with.

                  modified on Thursday, January 13, 2011 8:48 PM

                  L Offline
                  L Offline
                  Luc Pattyn
                  wrote on last edited by
                  #8

                  Not sure what your question is here. I would comment on the code like this: 1. it does not seem complete. Where is the buffer allocated? 2. I guess, but am not sure, the Marshal attributes used are sufficient to get the buffer pinned for as long as the native method lasts, but no longer. 3. Constructing an image from native data is fine, however it requires the underlying data to remain available as long as the image object lives (see MSDN doc), and that is not very easy to implement correctly. It sure does not show in your code. :)

                  Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum

                  Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.

                  E 1 Reply Last reply
                  0
                  • E econner

                    Correct, on the buffer. Here is an excert from the docs: //Scan to buffer. int WScanSelectBuf(void* image_down, void* image_up ,int select) (param) image_down [out] structure for forward direction scan. image_up [in] NULL (reserved) select [in] 0 : forward direction & single scan (use image_down) 1 : backward direction & single scan (use image_up) 2 : both direction & single scan (use image_down & image_up) 3 : forward direction & dual scan (use image_down) 4 : backward direction& dual scan (use image_up) 5: both direction & dual scan (use image_down& image_up) Return value - Success 0, Fail > 0

                    _ Offline
                    _ Offline
                    _Erik_
                    wrote on last edited by
                    #9

                    Have you allocated and pinned the byte[] arrays before calling the function?

                    E 1 Reply Last reply
                    0
                    • _ _Erik_

                      Have you allocated and pinned the byte[] arrays before calling the function?

                      E Offline
                      E Offline
                      econner
                      wrote on last edited by
                      #10

                      No, I have not. From the C++ example, they show setting the pbuf size, but with using IntPtr I was not sure how to accomplish the same thing.

                      _ 1 Reply Last reply
                      0
                      • L Luc Pattyn

                        Not sure what your question is here. I would comment on the code like this: 1. it does not seem complete. Where is the buffer allocated? 2. I guess, but am not sure, the Marshal attributes used are sufficient to get the buffer pinned for as long as the native method lasts, but no longer. 3. Constructing an image from native data is fine, however it requires the underlying data to remain available as long as the image object lives (see MSDN doc), and that is not very easy to implement correctly. It sure does not show in your code. :)

                        Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum

                        Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.

                        E Offline
                        E Offline
                        econner
                        wrote on last edited by
                        #11

                        Ok, I have looked a little more at your article. The Marshalling and pinning of the memory is something that I dont work with much. However, it does make sense with the type of memory error that I am receiving. So, from the original code I posted, how would you maybe suggest I change the stuct, DllImport, and calling methods to be in line with the Marshalling, pinning, and GCHandle? Would the parameter types change?

                        L 1 Reply Last reply
                        0
                        • E econner

                          No, I have not. From the C++ example, they show setting the pbuf size, but with using IntPtr I was not sure how to accomplish the same thing.

                          _ Offline
                          _ Offline
                          _Erik_
                          wrote on last edited by
                          #12

                          Create a byte[] array with the same size you need in C++ for UCHAR. Use GCHandle class to pin this array, so the GC will not reallocate it in case of a garbage collection, and set its address to the pbuf field. Have a look at the documentation of GCHandle class.

                          E 1 Reply Last reply
                          0
                          • E econner

                            Ok, I have looked a little more at your article. The Marshalling and pinning of the memory is something that I dont work with much. However, it does make sense with the type of memory error that I am receiving. So, from the original code I posted, how would you maybe suggest I change the stuct, DllImport, and calling methods to be in line with the Marshalling, pinning, and GCHandle? Would the parameter types change?

                            L Offline
                            L Offline
                            Luc Pattyn
                            wrote on last edited by
                            #13

                            You now have published two code snippets, I don't know which one you're talking about right now. However this is what I would to: 1. on the former snippet, take my advice, i.e. apply the third method of passing an array (with GCHandle, without Marshal, without unsafe). I expect that to work perfectly right away. And it does not require any change on the native side. 2. on the latter snippet, if the former now works well and you see sufficient reason to modify the latter, modify it to better correspond to the former. :)

                            Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum

                            Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.

                            E 1 Reply Last reply
                            0
                            • L Luc Pattyn

                              You now have published two code snippets, I don't know which one you're talking about right now. However this is what I would to: 1. on the former snippet, take my advice, i.e. apply the third method of passing an array (with GCHandle, without Marshal, without unsafe). I expect that to work perfectly right away. And it does not require any change on the native side. 2. on the latter snippet, if the former now works well and you see sufficient reason to modify the latter, modify it to better correspond to the former. :)

                              Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum

                              Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.

                              E Offline
                              E Offline
                              econner
                              wrote on last edited by
                              #14

                              Sorry for the latter code. Please ignore it. Maybe I can delete it.

                              1 Reply Last reply
                              0
                              • E econner

                                I am working on implimenting a 3rd party API written in C++. The API pulls an image from a scanner. I have tried a few different ways of of working with the image struct but every attempt causes memory crashes with P/Invoke. Any suggestions would be appreciated. Here is the C++ code and sample that comes with the device.

                                //(From header file)
                                //SCANDLL_API int __stdcall WScanSelectBuf(MY_IMAGE *simage_down,MY_IMAGE *simage_up,int select);

                                //(structure from C++ documentation
                                typedef struct {
                                int width; // image width
                                int height; // image height
                                int info; // bit-count (1,4,8 bit)
                                unsigned char* pbuf; // image buffer
                                } MY_IMAGE; // defines the image information

                                //(Use of method in sample C++ code)

                                MY_IMAGE img1,img2;

                                img1.width = 864;
                                img1.height = 3000;
                                img1.info = 8; // 8bit
                                img1.pbuf = (UCHAR *)new UCHAR[2*nWidth*nHeight];

                                img2.width = 864;
                                img2.height = 3000;
                                img2.info = 8; // 8bit
                                img2.pbuf = (UCHAR *)new UCHAR[2*nWidth*nHeight];

                                ret=WScanSelectBuf(&img1, &img2,0);


                                //C# conversion

                                [StructLayout(LayoutKind.Explicit)]
                                public struct MY_IMAGE
                                {
                                [FieldOffset(0)]
                                public int width;

                                    \[FieldOffset(4)\]
                                    public int height;
                                    
                                    \[FieldOffset(8)\]
                                    public int info;
                                    
                                    //\[FieldOffset(12)\]
                                    //public byte\[\] pbuf; // image buffer as byte array???
                                
                                    //Stuck here? 
                                    \[FieldOffset(12), MarshalAs(UnmanagedType.SysUInt)\]
                                    public IntPtr pbuf; // image buffer
                                }
                                

                                [DllImport("scandll.dll")]
                                private extern static int WT_ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select);

                                public static int ScanSelectBuf(ref MY_IMAGE image_down, ref MY_IMAGE image_up, int select)
                                {
                                return WScanSelectBuf(ref image_down, ref image_up, select);
                                }

                                ImageInfo img1 = new ImageInfo();
                                ImageInfo img2 = new ImageInfo();

                                int nWidth = 864;
                                int nHeight = 1000;

                                img1.width = nWidth;
                                img1.height = nHeight;
                                img1.info = 8; // 8bit
                                //img1.pbuf = new byte[2 *nWidth * nHeight];
                                //img1.pbuf = IntPtr.Zero;

                                img2.width = nWidth;
                                img2.height = nHeight;
                                img2.info = 8; // 8bit
                                //img2.pbuf = new byte[2 * nWidth * nHeight];
                                //img2.pbuf = IntPtr.Zero;

                                //CRASHES WITH MEMORY ACCESS/CORRUPT ERRORS
                                ret = ScanSelectBuf(ref img1,ref img2, 0);

                                J Offline
                                J Offline
                                jschell
                                wrote on last edited by
                                #15

                                econner wrote:

                                Any suggestions would be appreciated.

                                Don't embed it in in your C#/.Net code. Instead wrap it in a C++ executable with a communication (socket/file/stdio) API and run it from C# via Process and write C# code to talk to the communication API. The advantage is that it can't take down your C# app and you can test the parts independently. Note the first part as that can happen even if your code is perfect if there is a bug in the library itself.

                                1 Reply Last reply
                                0
                                • _ _Erik_

                                  Create a byte[] array with the same size you need in C++ for UCHAR. Use GCHandle class to pin this array, so the GC will not reallocate it in case of a garbage collection, and set its address to the pbuf field. Have a look at the documentation of GCHandle class.

                                  E Offline
                                  E Offline
                                  econner
                                  wrote on last edited by
                                  #16

                                  I have modified the code as per my understanding of GCHandle. I am still getting memory errors. I believe it has something to do with the IntPtr of the pbuf in the MY_IMAGE struct. Here is my code so far:

                                  [StructLayout(LayoutKind.Sequential)]
                                  public class MY_IMAGE
                                  {
                                  public int width;
                                  public int height;
                                  public int info;
                                  public IntPtr pbuf; // image buffer
                                  }

                                  [DllImport("scandll.dll")]
                                  private extern static int WScanSelectBuf(IntPtr image_down, IntPtr image_up, int select);

                                  public static int ScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select)
                                  {

                                  GCHandle image\_downHandle = GCHandle.Alloc(image\_down, GCHandleType.Pinned);
                                  GCHandle image\_upHandle = GCHandle.Alloc(image\_up, GCHandleType.Pinned);
                                  
                                  int ret = WScanSelectBuf(image\_downHandle.AddrOfPinnedObject(),
                                                                image\_upHandle.AddrOfPinnedObject(), select);
                                  

                                  //CRASHES HERE WITH ERROR:
                                  //Attempted to read or write protected memory.
                                  //This is often an indication that other memory is corrupt.

                                  image\_downHandle.Free();
                                  image\_upHandle.Free();
                                  
                                  return ret;
                                  

                                  }

                                  MY_IMAGE img1 = new MY_IMAGE();
                                  MY_IMAGE img2 = new MY_IMAGE();

                                  int nWidth = 864;
                                  int nHeight = 1000;

                                  img1.width = nWidth;
                                  img1.height = nHeight;
                                  img1.info = 8; // 8bit
                                  //img1.pbuf = new byte[2 *nWidth * nHeight];

                                  img2.width = nWidth;
                                  img2.height = nHeight;
                                  img2.info = 8; // 8bit
                                  //img2.pbuf = new byte[2 * nWidth * nHeight];

                                  //HOW TO DEFINE BYTE[] AND PIN AND THEN ASSIGN TO BUFFER?????

                                  ret = ScanSelectBuf(img1, img2, 0);

                                  Anything I am missing ???

                                  _ 1 Reply Last reply
                                  0
                                  • E econner

                                    I have modified the code as per my understanding of GCHandle. I am still getting memory errors. I believe it has something to do with the IntPtr of the pbuf in the MY_IMAGE struct. Here is my code so far:

                                    [StructLayout(LayoutKind.Sequential)]
                                    public class MY_IMAGE
                                    {
                                    public int width;
                                    public int height;
                                    public int info;
                                    public IntPtr pbuf; // image buffer
                                    }

                                    [DllImport("scandll.dll")]
                                    private extern static int WScanSelectBuf(IntPtr image_down, IntPtr image_up, int select);

                                    public static int ScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select)
                                    {

                                    GCHandle image\_downHandle = GCHandle.Alloc(image\_down, GCHandleType.Pinned);
                                    GCHandle image\_upHandle = GCHandle.Alloc(image\_up, GCHandleType.Pinned);
                                    
                                    int ret = WScanSelectBuf(image\_downHandle.AddrOfPinnedObject(),
                                                                  image\_upHandle.AddrOfPinnedObject(), select);
                                    

                                    //CRASHES HERE WITH ERROR:
                                    //Attempted to read or write protected memory.
                                    //This is often an indication that other memory is corrupt.

                                    image\_downHandle.Free();
                                    image\_upHandle.Free();
                                    
                                    return ret;
                                    

                                    }

                                    MY_IMAGE img1 = new MY_IMAGE();
                                    MY_IMAGE img2 = new MY_IMAGE();

                                    int nWidth = 864;
                                    int nHeight = 1000;

                                    img1.width = nWidth;
                                    img1.height = nHeight;
                                    img1.info = 8; // 8bit
                                    //img1.pbuf = new byte[2 *nWidth * nHeight];

                                    img2.width = nWidth;
                                    img2.height = nHeight;
                                    img2.info = 8; // 8bit
                                    //img2.pbuf = new byte[2 * nWidth * nHeight];

                                    //HOW TO DEFINE BYTE[] AND PIN AND THEN ASSIGN TO BUFFER?????

                                    ret = ScanSelectBuf(img1, img2, 0);

                                    Anything I am missing ???

                                    _ Offline
                                    _ Offline
                                    _Erik_
                                    wrote on last edited by
                                    #17

                                    Yes, you are pinning the MY_IMAGE objects, but that is not what you have to pin. When you call a function using P/Invoke, the CLR automatically pinnes in the managed heap the references you pass, so there is no need to explicitly pin them as you have done. However, the pbuf field of each MY_IMAGE class is an IntPtr, what means that it only contains the memory address of the buffer allocated to receive de image pixels. As it is only an address, the CLR will not pin these buffers automatically, and that is why the programmer has to pin them manually before using the P/Invoke call. In this case you have not even allocated the buffers, so the values of the pbuf fields of the MY_IMAGE objects you are passing to the unmanaged function are IntPtr.Zero, I mean, the default value for IntPtr. This is what you have to do: Before you call the WScanSelectBuf you have to create the two MY_IMAGE objects and allocate the byte[] arrays where that function will place the image pixel:

                                    MY_IMAGE img1 = new MY_IMAGE();
                                    MY_IMAGE img2 = new MY_IMAGE();

                                    // Initialize the other fields, I mean, width, height and info
                                    // Then create the byte[] arrays
                                    // with the size given by the documentation
                                    byte[] buffer1 = new byte[2*img1.width*img1.height];
                                    byte[] buffer2 = new byte{2*img2.width*img2.height];

                                    // Pin these two arrays in the managed heap
                                    GCHandle bufferHandle1 = GCHandle.Alloc(buffer1, GCHandleType.Pinned);
                                    GCHandle bufferHandle2 = GCHandle.Alloc(buffer2, GCHandleType.Pinned);

                                    // Set the address of the buffers to each pbuf field
                                    img1.pbuf = bufferHandle1.AddrOfPinnedObject();
                                    img2.pbuf = bufferHandle2.AddrOfPinnedObject();

                                    // And now we can make the P/Invoke call
                                    int ret = WScanSelectBuf(img1, img2, select);

                                    // Do not forget to free the handles
                                    bufferHandle1.Free();
                                    bufferHandle2.Free();

                                    // Here you should have the pixel information in the buffer1 and buffer2 arrays

                                    Tell us if it works.

                                    E 1 Reply Last reply
                                    0
                                    • _ _Erik_

                                      Yes, you are pinning the MY_IMAGE objects, but that is not what you have to pin. When you call a function using P/Invoke, the CLR automatically pinnes in the managed heap the references you pass, so there is no need to explicitly pin them as you have done. However, the pbuf field of each MY_IMAGE class is an IntPtr, what means that it only contains the memory address of the buffer allocated to receive de image pixels. As it is only an address, the CLR will not pin these buffers automatically, and that is why the programmer has to pin them manually before using the P/Invoke call. In this case you have not even allocated the buffers, so the values of the pbuf fields of the MY_IMAGE objects you are passing to the unmanaged function are IntPtr.Zero, I mean, the default value for IntPtr. This is what you have to do: Before you call the WScanSelectBuf you have to create the two MY_IMAGE objects and allocate the byte[] arrays where that function will place the image pixel:

                                      MY_IMAGE img1 = new MY_IMAGE();
                                      MY_IMAGE img2 = new MY_IMAGE();

                                      // Initialize the other fields, I mean, width, height and info
                                      // Then create the byte[] arrays
                                      // with the size given by the documentation
                                      byte[] buffer1 = new byte[2*img1.width*img1.height];
                                      byte[] buffer2 = new byte{2*img2.width*img2.height];

                                      // Pin these two arrays in the managed heap
                                      GCHandle bufferHandle1 = GCHandle.Alloc(buffer1, GCHandleType.Pinned);
                                      GCHandle bufferHandle2 = GCHandle.Alloc(buffer2, GCHandleType.Pinned);

                                      // Set the address of the buffers to each pbuf field
                                      img1.pbuf = bufferHandle1.AddrOfPinnedObject();
                                      img2.pbuf = bufferHandle2.AddrOfPinnedObject();

                                      // And now we can make the P/Invoke call
                                      int ret = WScanSelectBuf(img1, img2, select);

                                      // Do not forget to free the handles
                                      bufferHandle1.Free();
                                      bufferHandle2.Free();

                                      // Here you should have the pixel information in the buffer1 and buffer2 arrays

                                      Tell us if it works.

                                      E Offline
                                      E Offline
                                      econner
                                      wrote on last edited by
                                      #18

                                      I modified the code as per above. The img1.pbuf and img2.pbuf are now showing an address. However, the native API funtion of WScanSelectBuf(img1, img2, select) is still crashing. I have tried passing in:

                                      WScanSelectBuf(img1, img2, select) (MY_IMAGE)
                                      WScanSelectBuf(ref img1, ref img2, select) (ref of MY_IMAGE)
                                      WScanSelectBuf(img1, img2, select) (IntPtr)

                                      and changed the code in the DLLImport and wrapper method without any success. Is there anything I am missing with Marshaling, etc? I am coming to the conclusion that it could be an issue with the 3rd party's API libary and is doing something odd with the function. However, it does work in their C++ sample. I have contacted the vendor and they are not familar with .NET. However, they are reviewing this thread and for any suggestions.

                                      _ 1 Reply Last reply
                                      0
                                      • E econner

                                        I modified the code as per above. The img1.pbuf and img2.pbuf are now showing an address. However, the native API funtion of WScanSelectBuf(img1, img2, select) is still crashing. I have tried passing in:

                                        WScanSelectBuf(img1, img2, select) (MY_IMAGE)
                                        WScanSelectBuf(ref img1, ref img2, select) (ref of MY_IMAGE)
                                        WScanSelectBuf(img1, img2, select) (IntPtr)

                                        and changed the code in the DLLImport and wrapper method without any success. Is there anything I am missing with Marshaling, etc? I am coming to the conclusion that it could be an issue with the 3rd party's API libary and is doing something odd with the function. However, it does work in their C++ sample. I have contacted the vendor and they are not familar with .NET. However, they are reviewing this thread and for any suggestions.

                                        _ Offline
                                        _ Offline
                                        _Erik_
                                        wrote on last edited by
                                        #19

                                        Ok, it's pretty weird... I am not sure what is going on here. The correct way to import MY_IMAGE type and the function is as I told you before, there is no need of marshaling for any field. You might try a little change, using Marshal.UnsafeAddrOfPinnedArrayElement instead of AddrOfPinnedObject method of GCHandle class, I mean, you first pin the buffers with GCHandle and then set the address in pbuf fields this way:

                                        img1.pbuf = Marshal.UnsafeAddrOfPinnedArrayElement(buffer1, 0);
                                        img2.pbuf = Marshal.UnsafeAddrOfPinnedArrayElement(buffer2, 0);

                                        If it still does not work, you might try allocating the buffers directly in the global heap, with Marshal.AllocHGlobal (remember to free this memory when you don't need it any more), instead of creating them in the managed heap as we did before when instantiated the buffers with new byte[size]. If this works you should then copy the buffers allocated in the global heap to the managed heap with the method Marshal.Copy.

                                        E 3 Replies Last reply
                                        0
                                        • _ _Erik_

                                          Ok, it's pretty weird... I am not sure what is going on here. The correct way to import MY_IMAGE type and the function is as I told you before, there is no need of marshaling for any field. You might try a little change, using Marshal.UnsafeAddrOfPinnedArrayElement instead of AddrOfPinnedObject method of GCHandle class, I mean, you first pin the buffers with GCHandle and then set the address in pbuf fields this way:

                                          img1.pbuf = Marshal.UnsafeAddrOfPinnedArrayElement(buffer1, 0);
                                          img2.pbuf = Marshal.UnsafeAddrOfPinnedArrayElement(buffer2, 0);

                                          If it still does not work, you might try allocating the buffers directly in the global heap, with Marshal.AllocHGlobal (remember to free this memory when you don't need it any more), instead of creating them in the managed heap as we did before when instantiated the buffers with new byte[size]. If this works you should then copy the buffers allocated in the global heap to the managed heap with the method Marshal.Copy.

                                          E Offline
                                          E Offline
                                          econner
                                          wrote on last edited by
                                          #20

                                          Ok, below are the changes I tried. However, I am receiving an error of: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

                                          [DllImport("scandll.dll")]
                                          private extern static int WScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select);

                                          public static int ScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select)
                                          {
                                          return WScanSelectBuf(image_down, image_up, select);
                                          }

                                          int nWidth = 100;
                                          int nHeight = 200;

                                          img1.width = nWidth;
                                          img1.height = nHeight;
                                          img1.info = 8; // 8bit

                                          img2.width = nWidth;
                                          img2.height = nHeight;
                                          img2.info = 8; // 8bit

                                          byte[] buffer1 = new byte[2 * img1.width * img1.height];
                                          byte[] buffer2 = new byte[2 * img2.width * img2.height];

                                          // Pin these two arrays in the managed heap
                                          GCHandle bufferHandle1 = GCHandle.Alloc(buffer1, GCHandleType.Pinned);
                                          GCHandle bufferHandle2 = GCHandle.Alloc(buffer2, GCHandleType.Pinned);

                                          // Set the address of the buffers to each pbuf field
                                          //img1.pbuf = bufferHandle1.AddrOfPinnedObject();
                                          //img2.pbuf = bufferHandle2.AddrOfPinnedObject();

                                          img1.pbuf = Marshal.UnsafeAddrOfPinnedArrayElement(buffer1, 0);
                                          img2.pbuf = Marshal.UnsafeAddrOfPinnedArrayElement(buffer2, 0);

                                          // And now we can make the P/Invoke call
                                          ret = ScanSelectBuf(img1, img2, 0);

                                          // Do not forget to free the handles
                                          bufferHandle1.Free();
                                          bufferHandle2.Free();

                                          1 Reply Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • World
                                          • Users
                                          • Groups