天天看點

VS2005環境下的DLL應用

VS2005環境下的DLL應用

    關于DLL的好處,我就不多說了,隻需要記住幾條:

1) 可以實作代碼內建封裝。 2) 實作生成的應用程式以檔案為載體實作子產品化。在更新程式版本的時候,不用重新對應用程式進行重新編譯,則隻需要将相應的DLL檔案進行替換就行了。 3) 可以實作跨語言調用。對于一些用C#作為主要開發語言的程式,需要C++進行接近硬體的底層操作時,可以通過DLL技術,實作語言的“混合”程式設計,C#具有開發高效性的特點,C++具有運作高效性和對底層的良好操作性的優點,DLL技術可以實作兩種語言優點的結合。

     注:這些技術在WinXp和WinCe上都測試過,如果沒有特别說明,在兩種平台下都可以使用的,微軟的産品還是具有一定的通用性的。

1. VS2005建立基于C++的DLL項目

    本文主要是講基于C/C++的DLL,因為這種基于C++的DLL不像C#建立的DLL那樣依賴于.NET環境,移植性比較好。

    【檔案】->【建立】->【項目】。選擇C++語言裡面的Win32控制台應用程式

<a href="http://images.cnblogs.com/cnblogs_com/beer/WindowsLiveWriter/9aafe32e4889_A1F1/clip_image002_2.jpg"></a>

    然後點選“确定”,再到後面的向導出進行設定

<a href="http://images.cnblogs.com/cnblogs_com/beer/WindowsLiveWriter/9aafe32e4889_A1F1/clip_image004_2.jpg"></a>

    “應用程式類型”選擇“DLL”,可以選擇公共頭檔案支援“ATL”或者“MFC”,一般都選擇“MFC”。然後點選“完成”,那麼VS2005就自動建立了一個基于C++的DLL模闆了。

    生成項目,然後在對應的目錄下面看到相應的DLL檔案了,但是此時裡面還沒有任何功能,使用者需要根據實際需求為DLL編寫導出函數,然後供其它應用執行程式調用。

2. 為DLL添加自定義導出函數

    主要的函數類型有下面三種或者三種的任意組合:

1) 帶傳入參數無傳回值函數。 2) 有傳回值函數。 3) 帶傳出參數函數。

    前面的兩種類型都比較簡單,是以在下面也隻作一些簡單的介紹和代碼示範。主要是第三種類型,在實作跨語言應用DLL的時候的作用最大,也是難度最高(反正自己是這麼認為的)的一種進階應用吧,是以要進行詳細介紹。

2.1帶傳入參數無傳回值函數

    在以前的那篇關于DLL的文章中提到過,在此不再贅述了。

2.2有傳回值函數

    一般隻傳回整數或者少量的字元串,這個應用也比較簡單,使用者到網上可以查到相關資料,是以也不再詳細介紹了。

2.3帶傳出參數函數

    通過上面提到的兩種類型的函數,可以實作簡單的基本資料類型的傳入的傳出。比如,傳入兩個整數a,b到一個表示加法的導出函數中,然後傳回兩者的和。這個是可以做到的,實作起來也比較容易,是以在此不詳細說明。兩個來對兩種稍微進階點的資料傳遞進行說明:“特殊資料結構”和“大量資料集合”,這個時候如果還用那種簡單的形參傳入,傳回值傳出就無法解決問題了。還有,如果你熟練地掌握了傳出參數的使用方法,那麼你完全可以用此類形的方法實作傳回值函數的資料傳出功能。不過,關于傳出參數,要想熟練應用,還需對指針、位址等概念有比較好的掌握。

2.3.1特殊資料的傳遞

    對于大量資料的傳出,傳回值的方法是行不通的。比如,我曾經在寫一個圖像資料處理的函數的時候,需要DLL函數傳回處理完後的圖像資料,這個資料有150K,當時的想法是聲明一個150K的數組,然後傳回。但是後來遇到了一個問題:每次程式運作到這個函數的時候,都會出現問題“Microsfot Visual Studio正忙”然後就是Visual Studio假死沒有反應。

<a href="http://images.cnblogs.com/cnblogs_com/beer/WindowsLiveWriter/9aafe32e4889_A1F1/clip_image006_2.jpg"></a>

棧:在Windows下,棧是向低位址擴充的資料結構,是一塊連續的記憶體的區域。這句話的意思是棧頂的位址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就确定的常數),如果申請的空間超過棧的剩餘空間時,将提示overflow。是以,能從棧獲得的空間較小。

堆:堆是向高位址擴充的資料結構,是不連續的記憶體區域。這是由于系統是用連結清單來存儲的空閑記憶體位址的,自然是不連續的,而連結清單的周遊方向是由低位址向高位址。堆的大小受限于計算機系統中有效的虛拟記憶體。由此可見,堆獲得的空間比較靈活,也比較大。

在C#中,聲明一個150K甚至是1M的數組都是完全沒有問題的,覺得可能是因為C#的數組在聲明的時候本來就是用的new,也就是說本來就放在“堆空間”上的,然後最後用完後,由系統自動回收。但是C++中卻不行,C++函數中聲明的臨時變量都是放在“棧空間”裡面,大小是有限制的,如果太大會出現上面那種處理不過來的問題。是以,對于比較大的資料,C++中一般采用動态申請記憶體(malloc/free,new/delete),在“堆空間”上開辟存儲空間,這個裡面的上限就是剩餘的記憶體大小,是以可以進行大量資料的緩存。

C++的取位址符号對應C#中的ref引用關鍵字,可以用來傳出整形等基本資料類型

C++裡面的位元組數組BYTE數組也直接對應着C#中的BYTE數組(事先指明了大小的)

C++裡面的指針對應着C#中的IntPtr(可以用于動态配置設定記憶體的場合)

    雖然裡面還有,C++中的字元串和C#中的StringBuilder對應,但是這個時候涉及到C#中在引用DLL的導出函數的時候,要設定編碼格式,否則DLL傳出的參數會出現亂碼,XP下可以設定,WinCE下卻沒有。是以,筆者認為,這種方法不具有一般性和通用性。後來在程式設計學習的過程中,對資料的硬體存儲有了一定的概念後,終于搞明白了一點了,其實任何複雜的資料類型在硬碟的存儲形态都是01二進制編碼的,用稍微進階點的眼光來看,就是以8位為一位元組來存儲和描述的,比如:不管是什麼檔案,實際上都是二進制流;複雜點的資料如字元串,也可以用一個整數數組來描述;結構體,實際上也是一系列資料的存儲媒體上按位元組來排列存儲的。是以,任何的資料類型都可以轉成一個BYTE(unsigned char)數組進行表示,同樣,這個BYTE數組也可以還原成原先定義的那種複雜的資料類型。

    對于一些大小事先就能确定的數組,可以直接用數組作為C++語言的DLL和C#的EXE之間的共同資料通道。對于一些大小不确定的(需要在DLL程式中動态申請的記憶體塊),可以用指針來作為共同的資料通道,在C#中有個IntPtr,從DLL中傳出記憶體塊的位址和資料區域的大小後,C#的EXE程式就可以通過相應的接口函數将這些記憶體塊中的資料拷貝出來到一個BYTE數組中。

    C#中的new的資料類型,就相當于C++中的malloc一樣,動态配置設定了記憶體,隻是在C#的EXE程式中不需要由程式員自己去釋放,是以C#中new的資料,C++的DLL中可以直接把它看成malloc後的資料,同時在C#使用資料完畢後,不用自己手動釋放的(現在還不知道這個猜測是不是對的),DLL中malloc得到的動态記憶體空間傳到C#的EXE程式中後,不知道C#中是否需要手動編寫代碼進行釋放?。

    注:本來是想重點對傳出參數進行介紹,而且還附示範代碼,但是後來想想都是一些細節,寫下來太繁瑣,而且源代碼一直沒有時間去完善,使它能包含上面所有的函數示例,是以,就主要把自己的心得體會還有疑問寫下來和大家分享下,如果今後有時間的話,再對這一部分進行完善吧。今後可以會專門寫一個關于DLL跨語言傳遞動态申請空間的資料的總結的。

3. DLL的調用

3.1 C++程式的調用

3.2 C#程式的調用

4. DLL調試

    以前寫的一篇關于DLL的文章,裡面用的是VC6.0,當時還不知道其實一個“工作區”可以包含多個“項目”,是以,就可以直接實作C++的DLL和EXE源碼的聯調的,但是C#應用程式的話,還是不行。在VS2005中,這點就比較好的解決了。在VS2005的“解決方案資料總管”中,一個“解決方案”裡面可以建立多個“項目”,這些項目可以是不同的語言項目。是以,VS2005中的跨語言調試比VC6.0中更友善一些。

    首先,在VS2005的同一個解決方案中建立三個項目,一個DLL項目(用來生成DLL檔案),一個C++項目和一個C#項目(用來調用DLL并進行測試)。

對DLL項目編寫相關源碼,實作相應的導出函數,然後生成DLL檔案,對DLL的項目屬性進行參數設定,調試選項中的“指令”項設定成對應的EXE程式。将DLL檔案放到相應的EXE程式的目錄下面,然後就可以通過右鍵相應的項目選擇【調試】對相應的項目進行調試了。如果是C++的EXE項目,在調試的時候,遇到DLL的導出函數,然後單步執行,可以進入到本解決方案下的DLL項目的源碼中,實作兩個項目的代碼的聯調。對于C#執行程式,也可以進行聯調,但是要在DLL項目屬性中對“調試屬性”進行設定,調試器類型選擇“混合”模式,就可以實作不同語言的兩項目的源碼聯調了。

<a href="http://images.cnblogs.com/cnblogs_com/beer/WindowsLiveWriter/9aafe32e4889_A1F1/clip_image008_2.jpg"></a>

    上面的調試方法講的都是WinXp下的,在WinCE系統中,目前根據筆者的經曆,好像跨語言的時候是不能實作源碼級的聯調的,隻能要麼對EXE進行調試,要麼對DLL進行調試,分開調試,其實起到的效果一樣,隻是調試啟動的項目不同而已。對于同語言項目的調用,比如:從DLL項目啟動調試,調用EXE,在DLL和EXE項目中可以同時斷點成功。但是從EXE項目啟動的話,就無法斷到DLL源檔案中(XP環境下可以)。

    注:本文所講的内容,除了特别指出地差別外,其餘的技術在WinXP和WinCE平台上都是通用的,經過了實地測試的。

參考文章:

<a href="http://www.cppblog.com/oosky/archive/2006/01/21/2958.html">http://www.cppblog.com/oosky/archive/2006/01/21/2958.html</a>

<a href="http://www.cnblogs.com/beer/archive/2010/08/19/1803560.html">http://www.cnblogs.com/beer/archive/2010/08/19/1803560.html</a>

<a href="http://blog.csdn.net/xqf222/archive/2010/09/11/5877795.aspx">http://blog.csdn.net/xqf222/archive/2010/09/11/5877795.aspx</a>

------------------------------------------------------------------

Email /Gtalk:[email protected]

Notes:歡迎轉貼,但請在頁面中加個連結注明出處

Time:2010-11-20 in Whu