什么是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);