none
請問直接以指標形式把 managed array 的位址傳給 native dll 會有問題嗎? RRS feed

  • 問題

  • 我爬了不少文,大多都是把指標轉成 IntPtr 再傳給 C 寫成的 dll
    不過我試著直接用指標去傳, 發現程式也可以執行無誤 ...

    我想知道的是:

    一、經過 fixed statement 後,我就可以把 managed array 交給 native dll 去處理嗎?
    二、如果我不用 IntPtr 傳遞指標會有什麼不良的後果嗎?

    請好心的大大為我解惑, 謝謝!

    測試程式如下:

    namespace zLibTest {
    
    	unsafe class Program {
    
    		public const Int32 zlNone = 0;
    		public const Int32 zlPoor = 1;
    		public const Int32 zlFair = 2;
    		public const Int32 zlAverage = 3;
    		public const Int32 zlNormal = 4;
    		public const Int32 zlGood = 5;
    		public const Int32 zlVeryGood = 6;
    		public const Int32 zlBest = 7;
    		public const Int32 zlSuperCompressed = 8;
    		public const Int32 zlMaxCompression = 9;
    
    		public const Int32 zrOK = 0;
    		public const Int32 zrSTREAM_END = 1;
    		public const Int32 zrNEED_DICT = 2;
    		public const Int32 zrERRNO = -1;
    		public const Int32 zrSTREAM_ERROR = -2;
    		public const Int32 zrDATA_ERROR = -3;
    		public const Int32 zrMEM_ERROR = -4;
    		public const Int32 zrBUF_ERROR = -5;
    		public const Int32 zrVERSION_ERROR = -6;
    
    		[DllImport("zLib.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)]
    		public static extern Int32 compress2(void* pBuffer, ref Int32 iBufferSize, void* pBlock, Int32 iBlockSize, Int32 iLevel);
    
    		[DllImport("zLib.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)]
    		public static extern Int32 uncompress(void* pBuffer, ref Int32 iBufferSize, void* pBlock, Int32 iBlockSize);
    
    		static void Main(string[] args) {
    
    			Byte[] bBlock;
    			Byte[] bBuffer;
    
    			FileStream fs = new FileStream("C:\\Test.dat", FileMode.Open, FileAccess.Read, FileShare.Read);
    			bBlock = new Byte[fs.Length];
    			fs.Read(bBlock, 0, bBlock.Length);
    			bBuffer = new Byte[bBlock.Length];
    			fs.Dispose();
    
    			Int32 iBufferSize = bBuffer.Length;
    			Int32 zRet = bBuffer.Length;
    
    			fixed (void* pBlock = bBlock, pBuffer = bBuffer) {
    				zRet = compress2(pBuffer, ref iBufferSize, pBlock, iBufferSize, zlMaxCompression);
    			}
    
    			Int32 iBufferSize2 = bBlock.Length;
    			Byte[] bBuffer2 = new Byte[iBufferSize2];
    
    			fixed (void* pBuffer2 = bBuffer2, pBuffer = bBuffer) {
    				zRet = uncompress(pBuffer2, ref iBufferSize2, pBuffer, iBufferSize);
    			}
    
    			Boolean nSucc = false;
    
    			fixed (void* pBlock = bBlock, pBuffer2 = bBuffer2) {
    				nSucc = Memory.Compare(pBlock, pBuffer2, bBlock.Length);
    			}
    
    			Console.WriteLine(nSucc ? "Successful!" : "Failed ...");
    			Console.ReadKey();
    
    		}
    
    	}
    
    }
    2009年10月17日 下午 06:14

解答

  • 一、正是

    二、不會, 你去查看IntPtr 的內部的話, 就是把void*封裝成struct而已, 大致程式碼如下
    struct IntPtr
    {
        private viod* ptr;
    }
    所以void*和IntPtr是一模一樣的東西


    另外DllImport中.NET會自動幫你fixed Managed物件, 詳細閱讀MSDN即可了解
    所以可以把dll宣告寫成
    [DllImport("zLib.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)]
    public static extern Int32 compress2(byte[] pBuffer, ref int iBufferSize, byte[] pBlock, int iBlockSize, int iLevel);


    這邊有C#寫成的zlib, 直接參考引用, 就不用DllImport了
    http://www.componentace.com/zlib_.NET.htm
    C#學習者,修練中,ACM解題魂 - http://kgame-blog.spaces.live.com/
    • 已標示為解答 loveCandy 2009年10月18日 上午 04:20
    2009年10月18日 上午 01:44
  • .Net 3.0 以後有內建 Zip 類別...

    在 在伺服器端產生 Word 2007 文件(http://msdn.microsoft.com/zh-tw/magazine/cc163526.aspx) 建議:
    產生第一個 DOCX 檔案
    雖然有幾個現成的程式庫可用來讀取和寫入 ZIP 檔案,您還是應該盡可能使用 .NET Framework 3.0 隨附且屬於 WindowsBase.dll 組件一部份的新封裝 API,因為封裝 API 看得懂 Office Open XML 檔案格式。例如,有些方便的方法可以輕鬆將關係元素新增至關係項目,也可以將內容類型元素新增至內容類型項目。而封裝 API 使事情更簡單,因為您完全不需要直接處理這些元素和項目。

    由於上面網頁的版本還是 CTP ,所以建議直接參見線上手冊 ZipPackage 的範例:
    讀取封裝範例 (解壓縮 XPS) ms-help://MS.MSDNQTR.v90.cht/wpf_samples/html/449a67e0-52ee-46b7-a6ea-be81a557ec94.htm
    撰寫封裝範例 (壓縮成 XPS) ms-help://MS.MSDNQTR.v90.cht/wpf_samples/html/339dc5e1-da68-4683-a5ef-e0b0e99f3ad8.htm
    (http://msdn.microsoft.com/zh-tw/library/aa972149.aspx)

    另一個範例:
    封裝資料的全新標準 http://msdn.microsoft.com/zh-tw/magazine/cc163372.aspx





    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    • 已標示為解答 loveCandy 2009年10月18日 上午 04:20
    2009年10月18日 上午 02:37

所有回覆

  • 一、正是

    二、不會, 你去查看IntPtr 的內部的話, 就是把void*封裝成struct而已, 大致程式碼如下
    struct IntPtr
    {
        private viod* ptr;
    }
    所以void*和IntPtr是一模一樣的東西


    另外DllImport中.NET會自動幫你fixed Managed物件, 詳細閱讀MSDN即可了解
    所以可以把dll宣告寫成
    [DllImport("zLib.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)]
    public static extern Int32 compress2(byte[] pBuffer, ref int iBufferSize, byte[] pBlock, int iBlockSize, int iLevel);


    這邊有C#寫成的zlib, 直接參考引用, 就不用DllImport了
    http://www.componentace.com/zlib_.NET.htm
    C#學習者,修練中,ACM解題魂 - http://kgame-blog.spaces.live.com/
    • 已標示為解答 loveCandy 2009年10月18日 上午 04:20
    2009年10月18日 上午 01:44
  • .Net 3.0 以後有內建 Zip 類別...

    在 在伺服器端產生 Word 2007 文件(http://msdn.microsoft.com/zh-tw/magazine/cc163526.aspx) 建議:
    產生第一個 DOCX 檔案
    雖然有幾個現成的程式庫可用來讀取和寫入 ZIP 檔案,您還是應該盡可能使用 .NET Framework 3.0 隨附且屬於 WindowsBase.dll 組件一部份的新封裝 API,因為封裝 API 看得懂 Office Open XML 檔案格式。例如,有些方便的方法可以輕鬆將關係元素新增至關係項目,也可以將內容類型元素新增至內容類型項目。而封裝 API 使事情更簡單,因為您完全不需要直接處理這些元素和項目。

    由於上面網頁的版本還是 CTP ,所以建議直接參見線上手冊 ZipPackage 的範例:
    讀取封裝範例 (解壓縮 XPS) ms-help://MS.MSDNQTR.v90.cht/wpf_samples/html/449a67e0-52ee-46b7-a6ea-be81a557ec94.htm
    撰寫封裝範例 (壓縮成 XPS) ms-help://MS.MSDNQTR.v90.cht/wpf_samples/html/339dc5e1-da68-4683-a5ef-e0b0e99f3ad8.htm
    (http://msdn.microsoft.com/zh-tw/library/aa972149.aspx)

    另一個範例:
    封裝資料的全新標準 http://msdn.microsoft.com/zh-tw/magazine/cc163372.aspx





    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    • 已標示為解答 loveCandy 2009年10月18日 上午 04:20
    2009年10月18日 上午 02:37
  • 感謝 kgame 的回覆,你的答案直接又精確 ...呵

    坦白說
    我也不想去呼叫 native dll
    實在是因為純 safe code 所寫出來的壓縮效能都讓人難以接受 ...

    目前流行的那些壓縮組件我幾乎全都 try 過了(花了我不少時間)
    以 DotNetZip 1.8 為例,即便是我把它加入 GAC、做了 NGEN ...(我不曉得我還有什麼可以做卻沒做的),速度還是遠遠不及 native dll
    (保守估計慢了至少 5 至 8 倍,而且在我的測試中我還特意刁難了 native dll ...)

    結果頂多只有百來 K 的自訂區塊用這些 managed code 寫出來的組件去壓縮卻花費相當可觀的時間 ...
    所以我才決定在 server 端走回頭路,用以前 VB6 時代的老方法 ...唉
    如果 System.IO.Compression 裡的類別支援壓縮 level 的話,我就不會考慮這麼做了
    因為 ...看得出來它們也是呼叫 native dll 完成壓縮的
    2009年10月18日 上午 04:43
  • System.IO.Compression 裡面並沒有 Zip ,只有 GZip ...
    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    2009年10月18日 上午 07:25