使用定義檔案和WINAPI宏編寫自己的API動態庫

來源:互聯網
上載者:User
<!--StartFragment-->
    如果你想要自己編寫的動態庫可以適用更多的其它程式設計語言,你可以使用定義檔案和WINAPI宏來編寫自己的API動態庫。你將會發現,使用這樣的動態庫輸出函數就象使用API函數一樣。
   
    一、為什麼要使用DEF檔案?

    因為微軟的MFC動態庫都是使用DEF檔案建立的。
    這回答夠有份量吧,但有點大帽子壓人的感覺。下面呢,我從原理上來說說為什麼要使用DEF檔案。
    無論使用C語言或者C++語言來編寫動態庫,其編譯器都會為每個函數甚至變數產生一個對應的修飾名(我是這樣翻譯的。原文是the decorated names),連接器將編譯後的目標代碼串連成DLL,其輸出函數名或變數名依舊是編譯後的修飾名。並且修飾名是與編譯器相關的,也就是說你的來源程式是C,產生的修飾名是一個樣子;如果你的來源程式是C++,則產生的修飾名是另一種樣子。(關於修飾名的討論,我將放在一個單獨的章節進行,敬請等待。糧草未動,廣告先行。真是的....)而我們的應用習慣是直接使用函數名,而非修飾名,我們在用API時一直就是如此。那麼,問題就來了,比如你在VB6中使用VC6寫的動態庫:你先在VB6中使用函數名來描述你要調用的函數,然後寫好調用代碼,接下來運行,你的VB6這時會告訴你,它在動態庫中找不到你剛剛描述過的函數的進入點,你的程式拒絕執行了。怎麼辦?解決問題的方法至少有兩種:1、修改你的VB6代碼中對動態庫輸出函數的描述部分,在別名欄添加動態庫輸出函數的修飾名。2、修改你的動態庫,添加一個DEF檔案,並使用DEF檔案的EXPORTS項來輸出你的動態庫函數(在其下簡單地列出你要輸出的函數名即可)。雖然,這樣一來問題是解決了(還沒有解決?可能嗎?有可能啦。但這裡先停一下,隨後再說。),但我們有必要將該問題進一步討論下去。我們寫動態庫的目的大多是想讓我們的動態庫不局限於某一種程式設計語言,是為了更加廣泛地被應用,我是喜歡用VB寫介面,而用VC來完成更重要的工作,如對資料進行分析、訪問硬體連接埠等,而且VC寫好的動態庫很少再改動,VB寫的介面倒是一改再改。說這麼多,目的只有一個,寫動態庫要著眼於“大局”,要一切符合“標準”。什麼是大局?大局就是走可持續發展的道路,就是複用(好象是在做政治報告)。什麼是標準呢?就是符合API的標準(也就是使用DEF檔案輸出函數,就象微軟的MFC動態庫)。
    其實,使用DEF檔案來輸出函數的一個最主要目的就是:將編譯器產生的函數修飾名去掉,用更加自然的、容易理解的、容易記憶的名字,而不是修飾名來輸出函數。這裡的名字可以不是函數名,這時須使用DEF檔案的NAME格式。但由於習慣,大多情況下,只使用函數名,因為這樣最簡單省事。是否存在偷懶的嫌疑?我的理解,不管是羅列函數名,還是其它輸出名,其本質上是一樣的,即都是使用了定義檔案的NAME格式。直接羅列函數名,就相當於“函數名”=“函數修飾名”,只是可以忽略等號後面的部分,而連接器會自動完成函數入口的匹配和設定工作。而一旦決定使用非函
數名的其它名字輸出函數,則必須書寫完整的格式,即“函數輸出名”=“要輸出的函數修飾名”,這裡等號後面的部分必須書寫正確,否則,串連時就通不過了。舉個例子:假設動態庫中一個函數描述如下,
int WINAPI TestAdd(int A,int B)
{
    return (A+B);
}

其DEF檔案的EXPORTS段描述如下,
EXPORTS
  TestAdd
  Add=?TestAdd@@YGHHH@Z

這裡TestAdd和Add實際上指向同一個入口。如果在VB程式中調用TestAdd和Add,其結果是一樣的。
   
    二、為什麼要使用WINAPI宏?
    看看上面的舉例,在函數前加了一個WINAPI宏。這一點很重要,它直接關係著函數輸出什麼樣子的修飾名,使用WINAPI宏的TestAdd函數,對應的輸出修飾名就是“?TestAdd@@YGHHH@Z”。
    為什麼要使用WINAPI呢?這牽涉到動態庫的另一個特徵,調用協議(Calling convention)。如果沒有一定的協議,動態庫的調用是不可想象的。一般常用的動態庫調用協議有:
   __cdecl
   __stdcall
   __fastcall
這些協議各有各的長處,這裡暫不一一描述。上面在談到解決VB程式調用VC寫的動態庫時,曾列舉兩種解決方案,但並不一定可以實現,它還取決於所使用的調用協議。VB所遵循的是PASCAL協議,如果在動態庫中沒有使用相應的協議,則VB程式執行時就會報告“調用協議錯”。而PASCAL協議在VC6中已被廢棄,取而代之的是__stdcall,即標準調用協議,這也是大多32位程式設計語言支援的一種通用協議。在WINDOWS.H中WINAPI也是被定義為__stdcall。這裡提議使用WINAPI的理由也就在這,它能夠表達出更加多的資訊----這樣定義的輸出函數(的調用協議)和 WINDOWS API函數(的調用協議)一樣。
    其它一些使用WINAPI宏的理由:你只要在所定義的函數前加上該宏,就不必要在每次串連時再去理會各種與調用協議相關的設定。況且,你可能並不需要將所有定義的函數都輸出,為了提高執行速度,你可能會將沒有輸出的函數使用__fastcall來定義,為了使用變參,你可能使用__cdecl來定義某些非輸出函數,或者諸如此類的理由......需要提醒的是,VC預設的調用協議是__cdecl。如果你在沒有修改調用協議的情況下,直接使用DEF檔案輸出函數,編譯串連都不會出錯,但是VB調用的時候肯定出錯。而如果使用了WINAPI宏,你不必再去理會這些。編譯器自動使用WINAPI的定義替代Integration Environment裡的相關設定,這裡函數前的說明優先順序最高。
    回過頭來再看上面有關輸出函數的修飾名的討論,上面提到修飾名與語言有關,另外,它還與調用協議有關。如果需要使用非函數名的名字用來輸出,你必須清楚你使用的調用協議及語言種類,也就是你必須清楚修飾名的建置規則,或者你採用一些技巧,讓DUMPBIN.EXE工具來幫忙。

    三、總結
    一句話,如果想建立自己的標準API動態庫,建議使用WINAPI描述你要輸出的函數,然後使用定義檔案輸出它。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.