• .Net调用非托管代码数据类型不一致的问题


    什么是Net互操作?.Net不能直接操作非托管代码,这时就需要互操作了。
       c#中调用非托管c++函数,此函数又包含指向某个结构的指针,譬如指向c#中的byte数组。对于这样的参数,考虑到非托管变量不能直接在托管代码中使用,那么应该如何去处理呢?

     上例子:

     private string getSelText(int start,int Scount)
          {
              try
              {
                  StringBuilder a = new StringBuilder(Scount);
                 
                  IntPtr pdf_pag = FPDFView.FPDF_LoadPage(pdf_doc, currentPage);
                  IntPtr cur_textpag = FPDFText.FPDFText_LoadPage(pdf_pag);
                  int count=Scount/512;
                  for (int i = 0; i <= count; i++)
                  {
                      byte[] copyText = new byte[1024];//托管数组
                      IntPtr pdata = Marshal.AllocHGlobal(1024);//申请内存
                      Marshal.Copy(copyText, 0, pdata, 1024);//再向里copy数据
                      if (i != Scount / 512)
                      {
                          FPDFText_GetText(cur_textpag, start + i * 512, 512, copyText);//非托管c++函数,参数copyText是带有返回数据的非托管数组
                      }
                      else
                      {
                          FPDFText_GetText(cur_textpag, start + i * 512, Scount % 512, copyText);
                      }
                      Marshal.FreeHGlobal(pdata);//以及释放内存
                    a.Append(  UnicodeEncoding.Unicode.GetString(copyText));
                      
                  }
                 
                  DeleteObject(pdf_pag);
                  DeleteObject(cur_textpag);
                  return a.ToString();
              }
              catch (Exception ex)
              {
                  
                  MessageBox.Show(ex.Message);
                  return null;
              }
             
          }

    (此处需注意的是:需要非托管指针byte[]参数的内存要先申请,再向里copy数据;还有最后要记得释放申请的内存. )

         细心的读者可能会发现方法中有总的数据长度,为什么要分n个固定长度去读取呢?这是因为要操作的数据过大时,程序就会报内存不够用的错误,所以使用小数组循环读取,避免此问题的出现。可为什么数组长度是1024,而每次读取的长度是512呢?假如我们使用长度为512的数组 结果会怎样呢?

         答案是只会返回一部分字符串数据,那缺少的部分哪去了呢,问题出在哪呢?

         很容易也很关键的就想到,byte[]长度不够,字符数据被截取了,问题很可能就出在c++非托管代码返回的数据类型与c#托管代码数据类型不一致造成的偏差(定义函数的人真坑)。我们转到定义查看FPDFText_GetText的声明。

    c#代码:

            [DllImport("fpdfsdk.dll", CharSet = CharSet.Unicode)]
            public static extern int FPDFText_GetText(IntPtr text_page, int start_index, int count, byte[] result);

        没错啊,byte[] 类型的,让我们再来看下关于此非托管函数的c++文档说明:

    DLLEXPORT int STDCALL FPDFText_GetText ( FPDF_TEXTPAGE  text_page,
        int  start_index,
        int  count,
        unsigned short *  result 
      )

       答案出现了, c++的unsigned short类型的指针能不能用c#中byte[]类型承接?查资料 ,非托管c++unsigned short与c#中的  System.UInt16是等价的,均是 16 位的,而c#的byte是8位的,难怪总是出现数据丢失的现象。

       该怎么解决呢?由于c#中没有现成的UInt16转字符串的方法,唯有抛弃UInt16,改用原来的byte,只要每次读入固定的长度,而承接的byte[]的长度是该长度的两倍即可。

    另一种解决办法是使用Marshal.PtrToStringUni(ptr)转化,代码如下:

     int BufferLength = 0;            
                  BufferLength = FPDFText.FPDFText_PageToText(pdf_doc, CurrentPage, IntPtr.Zero, 0, 0);
                  IntPtr ptr = Marshal.AllocCoTaskMem(BufferLength * sizeof(UInt16));//申请内存大小,Uint16,16位等价于c/c++中wchar_t,unsigned short
                  FPDFText.FPDFText_PageToText(pdf_doc, CurrentPage, ptr, BufferLength, 0);
                  string str = Marshal.PtrToStringUni(ptr);      
  • 相关阅读:
    今天VSS 了一把
    中文字母检索
    当心! 您也可能犯得js错 eval()不等于eval("")!
    腾讯微博邀请码2010年6月9日11:14:28
    存储过程原理
    腾讯微博邀请码2010年5月25日16:44:24
    《QQ我的好友想到的信息架构》
    8小时之外(Beyond the 8 Hours)
    超搞笑漫画比喻!如果浏览器是出行工具
    Nginx环境下配置PHP使用的SSL认证(https)
  • 原文地址:https://www.cnblogs.com/bile/p/3361570.html
Copyright © 2020-2023  润新知