背景
公司的支付平臺最近對接了西安移動的支付接口,接口中簽名的方法是對方提供了一個com組件,組件中包含了一個簽名的方法和一個驗(yàn)簽的方法,注冊了簽名之后,在vs中進(jìn)行了引用,引用之后,查看組件的定義如下:
using System;using System.Runtime.InteropServices;namespace UMPAYLib{ [ClassInterface(0)] [Guid("E92EB0AA-00CC-4F93-A76D-632BEA94E980")] [TypeLibType(2)] [ComConversionLoss] public class SignClass : ISign, Sign { public SignClass(); [DispId(1)] public virtual string Sign(string str, string certfile, string keyfile); [DispId(2)] public virtual int Verify(string str, string sig, IntPtr certfile); }}首先先看一下簽名的方法:Sign(string str,string certfile,string keyfile);
三個參數(shù)分別是用于簽名的串、公鑰證書的路徑和私鑰證書的路徑。
再看驗(yàn)簽的方法:Verify(string str,string sig,IntPtr certfile);
三個參數(shù)分別是用于簽名的串,要驗(yàn)證的簽名值和公鑰證書的路徑。
那么問題來了,驗(yàn)簽方法的第三個參數(shù),證書的路徑怎么是IntPtr類型呢?IntPtr到底是個什么類型呢?我該怎么調(diào)用這個方法呢?
解決過程
首先我問題了接口方,接口放的對接人員倒是挺負(fù)責(zé)任,幫我看文檔,問同事,可接口方看過他們自己的文檔之后,也郁悶了,他們也不清楚文檔咋和組件里的方法定義不一樣,他們說要請示總部,而請示總部要用郵件,而且半天也不見回復(fù),可接口后天就要上線測試了,等回復(fù)看來不靠譜,還得靠自己,于是就開始了求助度娘。
首先我們來看看IntPtr到底是個什么類型?
MSDN的解釋:
用于表示指針或句柄的平臺特定類型。備注:IntPtr 類型被設(shè)計(jì)成整數(shù),其大小適用于特定平臺。 即是說,此類型的實(shí)例在 32 位硬件和操作系統(tǒng)中將是 32 位,在 64 位硬件和操作系統(tǒng)上將是 64 位。IntPtr 類型可以由支持指針的語言使用,并可作為在支持與不支持指針的語言間引用數(shù)據(jù)的一種通用方式。IntPtr 對象也可用于保持句柄。 例如,IntPtr 的實(shí)例廣泛地用在 System.IO.FileStream 類中來保持文件句柄。IntPtr 類型符合 CLS,而 UIntPtr 類型卻不符合。 只有 IntPtr 類型可用在公共語言運(yùn)行時中。 UIntPtr 類型大多數(shù)是提供來維護(hù)與 IntPtr 類型之間的體系結(jié)構(gòu)上的對稱性。此類型實(shí)現(xiàn) ISerializable 接口。
其中一行是這樣說的:
IntPtr類型可以由支持指針的語言使用,并可作為在支持與不支持指針的語言間引用數(shù)據(jù)的一種通用方式。
引用數(shù)據(jù)的通用方式?指針?看到這個之后,我就在度娘里輸入了“IntPtr傳字符串“幾個字,搜索結(jié)果中看到了一篇園子里一個仁兄寫的博客,http://m.survivalescaperooms.com/jxsoft/archive/2011/07/06/2099061.html,正式這篇博客,讓我豁然開朗,找到了問題最終的解決辦法,雖然解決的方法不是用的這位仁兄的方法,但思路是從這兒而來,所以還是要謝謝”許明吉博客“了。
我先是用了這篇博客中的如下這個方法進(jìn)行測試:
/// <summary> /// 根據(jù)數(shù)據(jù)的長度申請非托管空間 /// </summary> /// <param name="strData">要申請非托管空間的數(shù)據(jù)</param> /// <returns>指向非拖管空間的指針</returns> PRivate static IntPtr mallocIntptr( string strData ) { //先將字符串轉(zhuǎn)化成字節(jié)方式 Byte[] btData = System.Text.Encoding.Default.GetBytes(strData); //申請非拖管空間 IntPtr m_ptr = Marshal.AllocHGlobal(btData.Length); //給非拖管空間清0 Byte[] btZero = new Byte[btData .Length+ 1]; //一定要加1,否則后面是亂碼,原因未找到 Marshal.Copy(btZero, 0, m_ptr, btZero.Length); //給指針指向的空間賦值 Marshal.Copy(btData, 0, m_ptr, btData.Length); return m_ptr; }
測試的代碼如下:
UMPAYLib.SignClass signClass = new UMPAYLib.SignClass();IntPtr ptrCertFile = mallocIntptr(certFile);int b = signClass.Verify(prestr, SIGN, ptrCertFile);
運(yùn)行測試的頁面,當(dāng)執(zhí)行到signClass.Verify這個com組件驗(yàn)簽的方法的時候,報(bào)了一個”沒有足夠的內(nèi)存來繼續(xù)運(yùn)行程序“的異常,組件內(nèi)部的代碼也看不到,所以也不知道里面怎么處理導(dǎo)致了這個內(nèi)存溢出的異常,好不容易找到了思路,卻有出現(xiàn)了這個問題,該怎么辦呢?于是我又看了看ptrCertFile的屬性和方法,我輸出了ptrCertFile的.ToString(),發(fā)現(xiàn)得到了一個很大的數(shù)字,可還是不知道為什么會內(nèi)存溢出,我又仔細(xì)看了看,mallocIntptr這個方法,方法里有一個分配內(nèi)存的類引起了我的興趣,它就是Marshal,我們再來看看Mashal這個類具體是干啥的?有啥方法?
MSDN的解釋:
提供了一個方法集,這些方法用于分配非托管內(nèi)存、復(fù)制非托管內(nèi)存塊、將托管類型轉(zhuǎn)換為非托管類型,此外還提供了在與非托管代碼交互時使用的其他雜項(xiàng)方法。
從解釋中可以看出,該類主要是用于分配非托管內(nèi)存和在托管類型和非托管類型之間進(jìn)行轉(zhuǎn)換。
于是我就瀏覽了一下Mashal類的成員,發(fā)現(xiàn)了一個方法:

而這個方法也本身就能實(shí)現(xiàn)上面mallocIntptr這個方法的功能,于是我就把代碼修改為如下:
UMPAYLib.SignClass signClass = new SignClass();string certPath = MobileWapPayConfig.CertFile;IntPtr ptrCertFile = Marshal.StringToBSTR(certPath);int result = signClass.Verify(prestr, sign, ptrCertFile);Marshal.FreeBSTR(ptrCertFile);return result == 0;
重新運(yùn)行測試頁面,一切正常。
至此,最初遇到的IntPtr不知如何調(diào)用的問題已經(jīng)解決了,但遺留了一個小問題,那就是為什么mallocIntptr這個方法會導(dǎo)致內(nèi)存溢出,望了解的朋友不吝賜教!
總結(jié)
對于一些com組件的方法參數(shù)IntPtr類型的,可以使用Marshal類的相關(guān)方法來處理。
新聞熱點(diǎn)
疑難解答
圖片精選