程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 如何調用com組件中包含IntPtr類型參數的函數,intptr類型參數

如何調用com組件中包含IntPtr類型參數的函數,intptr類型參數

編輯:C#入門知識

如何調用com組件中包含IntPtr類型參數的函數,intptr類型參數


背景

  公司的支付平台最近對接了西安移動的支付接口,接口中簽名的方法是對方提供了一個com組件,組件中包含了一個簽名的方法和一個驗簽的方法,注冊了簽名之後,在vs中進行了引用,引用之後,查看組件的定義如下:

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);

  三個參數分別是用於簽名的串、公鑰證書的路徑和私鑰證書的路徑。

  再看驗簽的方法:Verify(string str,string sig,IntPtr certfile);

  三個參數分別是用於簽名的串,要驗證的簽名值和公鑰證書的路徑。

  那麼問題來了,驗簽方法的第三個參數,證書的路徑怎麼是IntPtr類型呢?IntPtr到底是個什麼類型呢?我該怎麼調用這個方法呢?

解決過程

  首先我問題了接口方,接口放的對接人員倒是挺負責任,幫我看文檔,問同事,可接口方看過他們自己的文檔之後,也郁悶了,他們也不清楚文檔咋和組件裡的方法定義不一樣,他們說要請示總部,而請示總部要用郵件,而且半天也不見回復,可接口後天就要上線測試了,等回復看來不靠譜,還得靠自己,於是就開始了求助度娘。

  首先我們來看看IntPtr到底是個什麼類型?

  MSDN的解釋:

用於表示指針或句柄的平台特定類型。
備注: IntPtr 類型被設計成整數,其大小適用於特定平台。 即是說,此類型的實例在 32 位硬件和操作系統中將是 32 位,在 64 位硬件和操作系統上將是 64 位。 IntPtr 類型可以由支持指針的語言使用,並可作為在支持與不支持指針的語言間引用數據的一種通用方式。 IntPtr 對象也可用於保持句柄。 例如,IntPtr 的實例廣泛地用在 System.IO.FileStream 類中來保持文件句柄。 IntPtr 類型符合 CLS,而 UIntPtr 類型卻不符合。 只有 IntPtr 類型可用在公共語言運行時中。 UIntPtr 類型大多數是提供來維護與 IntPtr 類型之間的體系結構上的對稱性。 此類型實現 ISerializable 接口。

  其中一行是這樣說的:

  IntPtr類型可以由支持指針的語言使用,並可作為在支持與不支持指針的語言間引用數據的一種通用方式。

      引用數據的通用方式?指針?看到這個之後,我就在度娘裡輸入了“IntPtr傳字符串“幾個字,搜索結果中看到了一篇園子裡一個仁兄寫的博客,http://www.cnblogs.com/jxsoft/archive/2011/07/06/2099061.html,正式這篇博客,讓我豁然開朗,找到了問題最終的解決辦法,雖然解決的方法不是用的這位仁兄的方法,但思路是從這兒而來,所以還是要謝謝”許明吉博客“了。

  我先是用了這篇博客中的如下這個方法進行測試:

/// <summary>   
        /// 根據數據的長度申請非托管空間   
        /// </summary>   
        /// <param name="strData">要申請非托管空間的數據</param>   
        /// <returns>指向非拖管空間的指針</returns>   
        private static IntPtr mallocIntptr( string strData )  
        {  
            //先將字符串轉化成字節方式   
            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);

運行測試的頁面,當執行到signClass.Verify這個com組件驗簽的方法的時候,報了一個”沒有足夠的內存來繼續運行程序“的異常,組件內部的代碼也看不到,所以也不知道裡面怎麼處理導致了這個內存溢出的異常,好不容易找到了思路,卻有出現了這個問題,該怎麼辦呢?於是我又看了看ptrCertFile的屬性和方法,我輸出了ptrCertFile的.ToString(),發現得到了一個很大的數字,可還是不知道為什麼會內存溢出,我又仔細看了看,mallocIntptr這個方法,方法裡有一個分配內存的類引起了我的興趣,它就是Marshal,我們再來看看Mashal這個類具體是干啥的?有啥方法?

MSDN的解釋:

提供了一個方法集,這些方法用於分配非托管內存、復制非托管內存塊、將托管類型轉換為非托管類型,此外還提供了在與非托管代碼交互時使用的其他雜項方法。

從解釋中可以看出,該類主要是用於分配非托管內存和在托管類型和非托管類型之間進行轉換。

於是我就浏覽了一下Mashal類的成員,發現了一個方法:

而這個方法也本身就能實現上面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;

重新運行測試頁面,一切正常。

至此,最初遇到的IntPtr不知如何調用的問題已經解決了,但遺留了一個小問題,那就是為什麼mallocIntptr這個方法會導致內存溢出,望了解的朋友不吝賜教!

總結

對於一些com組件的方法參數IntPtr類型的,可以使用Marshal類的相關方法來處理。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved