OLE2 打破軟體國界

>>>  名人論史——近當代作家的史學觀點  >>> 簡體     傳統

發表日期 : 1995.02 

 

從應用層面來看,
OLE2 打破軟體國界;
從基礎架構來看,OLE2 是物件導向 Windows 作業系統的骨干。

橫跨低階與高階,
這次我為你介紹三本半 OLE2 書籍。

 

你已經在去年 11 月份的無責任書評中看到,侯捷每一篇稿子的進行時程。文章的主體都是在上上個月的 25 日左右就完成,所以具時效性的話題在這專欄里看起來,像吃冰箱里那盤兩個月前的冷凍蹄膀,你的大腦拼老命回想它甘美的滋味,你的味蕾卻被凍得有些遲緩。其實我也很想在收到當月雜志之後,看看有什麼可以立刻回應或補充的,馬上利用下個月文章的三校稿機會在其中加點料。以正常出刊時間 (每月十號) 而言 (那其實并不怎麼正常),中間還有一絲縫隙,僅容旋馬;如果是真正的「正常」出刊,再加上郵遞時間,那麼中間這個縫隙可就間不容發了。可憐賀元說他在 26 號收到當月雜志,但侯捷有更偉大的記錄。如果是郵政出問題,怎麼補寄的雜志卻又效率出奇的好呢 ? 噢,主編一定有足夠的幽默感,讓我在這里替萬千讀者請命。

所以呢,可能是因為「郵政效率日益低落」,我在這本雜志破天荒 1 號出刊的偉大月份 (去年十二月),於 22 號才看到這個專欄有了新朋友。GREAT !! 對於新文章和新朋友,衷心感到高興與敬佩。希望我們能夠繼續在這個園地中看到更廣泛的議題,更熱烈的討論。

■ 讀者來函與你共享

o. 身為一個只是偶爾用用 PE2 的終端用戶,我倒希望侯捷的「廢話」愈多愈好。 技術性的東西看不懂,文采與眼界卻頗令人折服。

o. 沒有技術背景很難輕輕松松快快樂樂地看懂無責任書評。

o. 希望能夠有另一種形式的書籍介紹 : 選定某個技術領域 (例如 Windows 程式設計), 從入門到深入到專家等級,一步步引導閱讀重要著作。(答: 這個主意很棒, 我亦認為有此必要)

o. 如果中文書評有困難,或許可以在雜志上開個 Debug 專欄,由讀者投稿指出 哪些爛書。(答: Debug 專欄是 Windows/DOS Developer's Journal 上的一個 專欄名稱,主持人每月遴選數則軟體臭蟲公布之。也許我們可以弄個爛蘋果獎, 問題是你愿意投稿嗎 ? 你是否愿意讓自己的名字上刊以昭公信 ?)

游俠列傳訪談中提到的事情,讀者也有意見 :

o. 您說要研究完 OLE2 之後才會考慮進行較淺顯的技術, 但是我認為向下應該比向上更來得重要。如果多一點人廣一點人叫好,不叫座 也不行了不是嗎 ?

o. 風聞您想在探討 OLE2 後就不再寫技術性書籍而改寫入門書籍,如果這是 真的,我將惋惜中文電腦書籍從此難見高品質作品,更惋惜您心中的理想國度 隨之幻滅。

真是順了嫂意逆了姑意,再怎麼做也不能讓大家都滿意。你其實并不明白侯捷真正要做的是什麼,但是這引發我三點感想 :

1) 每個人只關心有沒有切合自己需求的書 (這很正常)。

2) 很清楚朋友們對高階書籍期盼之殷寄望之濃。我們之所以缺乏「較大嬰兒奶粉」, 賴明宗一語中的 :「使寫書的人不會餓死,恐怕是先決條件」(v13n04 p.217)。 我早就說了,如果你不能接受 VxD Programming 書籍比 DOS User Guide 書籍 貴上三兩倍,就要有接受前者滅絕的心理準備。很棒的作者需要很棒的讀者。

3) 從任何角度任何層面闡述技術,都可以有理想有抱負。以使用介面而言,Windows 95 和 OS/2 3.0 都很有學問,有許多物件導向的精神在其中,User's Guide 的作者在 介紹操作步驟之前,應該先說明整個 UI 架構與精神。只是,許多作者本身功力薄弱, 寫不出架構與精神,於是入門書籍千篇一律就是教大家「按下表單,選擇命令, 出現對話盒,拉卷動桿選一個項目,按下 OK ...」,非常有礙觀瞻又妨害消化。 大家都說技術文字應該深入淺出,問題是作者本身的技術沒有深入,又如何將 文字淺出 ? 淺薄當淺出,低俗當通俗,國內電腦書籍寫作的設限門檻太低 (普遍看來好像是...呃...沒有門檻可言), 這才造成有膽量者皆可出書的現象。而你知道,無知是這麼一種東西 : 誰擁有它 誰就會產生巨大的膽量。

回到主題來,這個月的主題正是 OLE2。說不能夠輕松快樂看懂無責任書評的讀者,想必本篇會使你的這種感覺更強烈。我的手上有三本半的書要報告,第一本講的是基礎理論,第二本是 Microsoft Visual C++ 套件中給程式員看的技術手冊;第三本書主講如何以 Visual C++/MFC 撰寫 OLE2 的各類應用程式。至於半本,事實上是某本書的一章,但足足 86 頁,謂之半本書可也。

此外,另有三本書不特別花篇幅介紹內容。兩本是 OLE 2 Programmer's Reference 上下冊,上冊主講 OLE2 Interface API (960 頁,US$ 29.95),下冊主講 Automation (368 頁,US$ 24.95)。這兩本書也內含在VC++ 1.5 的光碟之中。第三本書是 OLE 2.0 and DDE Distilled,作者是頗有名氣的 Al Williams (著有 DOS and Windows Protected Mode)。書很薄,才 300 頁,附磁片一。雖然書名偉大令人心動,內容卻相當地...呃...不怎麼樣。我不鼓勵你吃這塊雞肋 (雞肋應該勇於棄之,不必可惜)。

■ 我的理想

到目前為止,還沒有一本 OLE 書籍符合我的理想。倒不是指深度,指的是表達方式和取材范圍。洋人一向惜圖如金 (國人也差不多),然而在 OLE 這麼龐大復雜的架構中,光是文字說明會令人如墜五里霧;解釋兩個程式間的通訊,一定要有涵蓋收發兩造往返動作的示意圖,才能收事半功倍之效。沒有一本書做到了這一點!至於取材范圍,每個人都希望挑到一雙合腳的鞋,無奈市面上盡是零碼。我理想中的 OLE2 書籍應該有 :

1) OLE2 使用者介面之解釋。OLE2 有許多專屬的 UI 介面,在許多地方改變了 使用者的習慣。程式設計之前,有必要對 OLE 之操作方式做個詳細的說明, 讓大家了解如何以 [Insert New Object] 制作一個內嵌物件,如何以 [Paste Link] 制作一個聯結物件,或是如何操作「即地編輯」(In Place Editing)。

2) OLE2 基本架構。OLE2 已不再只是單純的行程通訊 (InterProcess Communication) 的一個規格而已,它還涵括基礎的 Component Object、復合檔案、記憶體管理...。 書中最好能對這些模組有些說明,為讀者在狂飲之前墊點肚底。

3) OLE2 應用程式設計。基本上這有兩個切入點,低階是運用 OLE Interface 提供的函式,高階則是利用 MFC 的 OLE Classes,或 OWL 的 OCF (ObjecComponents Framework)。這些 Classes 把低階的 OLE Interface 包裝起來,更方便使用。我不贊成從低階切入應用程式的撰寫 (何苦自 虐若此),但若在書中舉一兩個低階介面完成的簡單范例,將有助於了解 OLE2 基礎架構。至於高階的 OLE2 Classes,運用之前你必須先有 MFC 或 OWL 的基礎。要不要在書中先介紹 MFC 或 OWL 呢 ? 我認為沒有必要, 原因是如果只能以「小量篇幅」介紹這兩種程式方法,可想而知對於「已經會 的人」只能溫故而未能知新,對於「還不會的人」則不懂依舊不懂。

■ 勤前教育

介紹書籍之前,先做點 OLE2 技術上的說明,我想對於大多數讀者的後續閱讀動作會有些幫助。請先叁考圖一,那是整個 OLE2 架構的描述。注意,下面我對 Windows Object 的解釋非常重要,對你觀念上的幫助可能不下於一篇技術性專文。

圖一  略
OLE2 架構 (摘自 Inside OLE2)。圖中的 Ixxx 代表各個模組必須支援的 Interface,本圖只是舉例,并未列出所有的 Interfaces。

 

任何深入討論 OLE2 的書籍或文章中,你都會看到 Windows Object。到底這是什麼東西 ? 為什麼說未來的 Windows 是以 Windows Object 為基礎的物件導向作業系統呢 ?

相信你一定已經清楚,DOS 是以中斷向量 INT 21h 向外界開放其功能,Windows 則是以 API 形式把作業系統的功能與性質開放出來給應用程式使用。所以你在程式中呼叫各種 Windows API,取得 Windows 三大模組 (USER、KERNEL、GDI) 的能力。這些 APIs 以各自獨立的 C 函式存在著,每一個 API 函式的地位是平等的,也就是說任何一段碼都可以呼叫任何一個 API 函式。你的 C 程式(SDK 程式) 呼叫這些 C 函式,再自然不過了。但是這些命名規則不怎麼令人欣賞的 APIs 帶來混亂而非秩序 (你能夠把 DeleteObject() 和 CreatePen() 自然聯想在一塊兒嗎 ?)

現在,進入物件導向的世界,應用程式以 C++ 語言完成,以類別和物件的形式呈現。如果 Windows 還是以無秩序的 API 函式展示它的內涵,那就不太搭軋。是的,Windows 也打算以組織緊密的物件形式展現它的內在美,這種物件就稱為 Windows Object。譬如說你可以取得一個所謂的「Allocator Object」,其成員函式 Alloc() 和Realloc() 可以取代 API 函式 GlobalAlloc() 和 GlobalReAlloc()。現在你可以想像如何利用 Windows Object 在應用程式中配置記憶體了: 取得一個 Allocator Object,并呼叫其成員函式 Alloc()。

好,object 就 object,為什麼稱為 Windows Object ? 原因是這種物件比一般的 C++ 物件更嚴格,你只能透過某種方式獲得此物件之「函式表格」指標,卻絕對得不到此物件本身的指標 --- 目的當然是為了隔絕外界對其資料的可能侵犯行為。我知道你會問,把物件的資料宣告為 private 不就有封裝效果了嗎 ? 你是對的,但或許 Microsoft 實作這些 OLE 模組時,為了內部設計方便,放了一些 public 資料,那麼就只好以上述方式加以絕緣了 (這是我的猜測,未經證實)。

上面提到 Windows Object 的「函式表格」,是一組語意相關、功能相關的函式,這個函式表格中的每一個元素,是類別中的成員函式的函式指標。函式表格被稱為 Windows Object 的 Interface (圖二)。

圖二
Object 記憶體中的第一個位置,放的總是一個指標,指向函式表格。 函式表格內的每一個元素都是函式指標。函式表格就是 Interface。

 

現在讓我們整理一下思緒。你在物件導向 Windows 作業系統中,藉著一個一個的Windows Object,利用其 Interface 中的函式,取得作業系統的功能。例如 :

#include // 此檔定義 LPMALLOC 并宣告 CoGetMalloc()
LPMALLOC pIMalloc; // LPMALLOC IMalloc FAR*
LPVOID ptr;

// 取得 Interface 指標并呼叫其中函式。
CoGetMalloc(MEMCTX_TASK, &pIMalloc);
ptr = pIMalloc->Alloc(1024);

是的,你將遭遇效率降低的痛苦,因為相同一件事情的完成,Windows Object 比Windows API 多了一層動作。但是以 Windows Object 表現出來的系統特質勢將成為最自然的一種表達方式 --- 以物件導向的精神而言。

OLE2 是一個集眾人之力規劃出來的規格,其中不同的模組規定必須支援不同的Interfaces (也就是說支援不同的函式組)。如果你能夠設計一個類別,符合圖一某個模組所要求的 Interfaces,那麼就可以說你實作出了 OLE2 的某個模組。例如,如果你能夠設計出一個含有 IUnknown、IMalloc、IClassFactory 的類別,你就實現了OLE2 的 Conponent Object Model (COM)。目前,提供 OLE2 模組的工作由 Microsoft 負担,而實際產品 (規格之實現) 就是 OLE2 的各個 DLLs。

我知道你又要舉手了 :「Windows API 用的好好的,干啥用什麼 Windows Object 讓大家不得安寧」? 我還沒有能力很具體告訴你使用 Windows Object 的好處,但我可以告訴你不使用 Windows Object 的壞處 : 未來 Windows 作業系統的新功能都將以 Windows Object 的形式呈現,APIs 只保留舊觀。堅持使用 API 的程式當然還是可以跑,但是將沒有能力使用作業系統的新特質。Inside OLE2 作者甚至把 OLE2 視為「一種新的復雜的不可思議的物種起源」。

了解基礎的 Windows Object 和 Interface 之後,下面是 OLE2 的應用主題。所謂 O-L-E 就是物件聯結 (Linked) 與內嵌 (Embedded) 的縮寫,我們可以利用 OLE 規格 (這時候你可想像它是一份通訊協定),將甲程式 (Server) 的資料包裝起來放在乙程式 (Container) 中。乙程式可以接納來自不同 Servers 的不同型態的資料(所以乙程式的英文名字是「容器」),組成一份豐富的「文件」。這種文件稱為復合文件。復合文件最適合以「復合檔案」(Compound File) 的型態儲存在磁碟中,原因是復合檔案有 Storage 和 Stream 雙層結構,意義類似磁碟子目錄和檔案的關系,很適合放置具有二維樹狀結構特性的資料。

為了讓終端客戶面對文件而不是程式 (就像美工面對的是其完稿而不是稿旁的剪刀和漿糊) ,OLE2 發展出所謂的即地編輯,也就是使用者在 Container 的復合文件中面對各式各樣的生鮮材料 (OLE Item) 做動作時 (開啟或編輯或播放...),Server 會執行起來,將 Container 改頭換面。使用者面對的還是原來的視窗,但是表單和工具欄都變了。這些變化是 Server 和 Container 協商的結果,可以經由程式控制。

即地編輯是 OLE1 進化到 OLE2 過程中,在使用者介面上最大的一項改善。OLE2 的另一項新單元是 Automation,基本上這與物件聯結與內嵌毫無關系 (圖一)。為了達到軟體分工合作,有的程式釋出資料,有的程式釋出功能 (函式),這就是 Automation 的作用。釋出 (開放,expose) 資料或函式者,稱為 Automation Server,取用資料或函式者,稱為 Automation Controller。在這里資料稱為 Property,函式稱為 Method,與Visual Basic 的稱謂一致。開放出來的資料或函式可以被 VC++ 程式或 VB 程式取用。Microsoft 準備令其 Office 軟體全部支援一種名為 VBA (Visual Basic for Application) 的語言,此語言非常類似 Visual Basic,可以存取 Office 提供的軟體機能,適合用於Automation Controller 這一端。

暖身結束,我們出發吧。

 

背景資料 :
書名 Inside OLE2
作者 Kraig Brockschmidt
出版 Microsoft Press
頁數 16 章,977 頁
售價 US$ 49.95 (含磁片兩片)

Section I Windows Objects
1. An Overview of OLE2
2. Conventions, C++, and Sample Code
3. Objects and Interfaces
4. Component Objects

Section II Object Oriented System Features : Files and Data Transfer
5. Structured Storage and Compound Files
6. Uniform Data Transfer Using Data Objects
7. Clipboard Transfers Using Data Objects
8. Drag-and-Drop Operations Using Data Objects

Section III Compound Documents : OLE
9. Compound Documents and Embedded Containers
10. Compound Documents and Embedded Object Servers (EXEs)
11. In-Process Object Handlers and Servers
12. Monikers and Linking Containers
13. Moniker Binding and Link Sources
14. Conversion, Emulation, and Compatibility with OLE 1

Section IV Compound Documents : In-Place Activation
15. Visual Editing : In-Place Activation and In-Place Containers
16. In-Place Activation for Compound Document Objects

inside-ole2.jpg (18339 bytes)

雖然去年四月已經介紹過這本書的內容、文字、磁片,我還是認為有重新介紹的必要。原因是再把此書細看之後,我發現我對它說的好話太少了,不夠公平。再者這一期專門介紹 OLE2,也不能不提此書。已經說過的不會再說,請你叁考以前的書評。這里我只想加一點補充。

如果要找一本深入介紹 OLE2 架構的書籍,這本書是你唯一的選擇。本書目的就是要把圖一的每一個模組介紹給你。實作范例方面,幾乎每一章都有程式,都是以 C++ 完成,但并不架構在 MFC 之上,而是利用 OLE Interfaces。本書以兩個一般應用程式成長為 OLE2 Container 和 Server 的過程,說明各項 OLE2 性質以及實作方式。

既然我不鼓勵各位從低階切入寫 OLE 應用程式,我也就不鼓勵各位去 trace 本書的程式 (那種痛苦不足為外人道)。各章的文字說明值得再三品味,尤其是觀念上的解釋。這些文字對於初入門者帶來多少意義我很懷疑,但的確值得你在多閱讀其他書籍多有體會之後,回頭來再思索。如果打字夠快,建議你邊看書邊把心得記錄下來,甚至整段看過之後有所體會就整段翻譯下來。也許一開始你覺得進度遲緩,不耐其煩,但是回頭看第二次第三次時的速度,絕對比別人快 5 倍不止 --- 而我可以向你保證,OLE2 的基礎觀念絕對需要一看再看才能有所體會,尤其你面對的是一本原文書。

本書第一章對 OLE 整體架構做 23 頁的介紹,圖一的每一個模組都照顧到了。第二章是作者對自己發展的小型類別庫做使用上的說明,全書的范例程式都架構在這個不太復雜的類別庫中。第三章很有價值,以一個 C++ 范例和一個 C 范例說明 Windows Object 如何實作。由於 C++ 編譯器對於虛擬函式提供了虛擬函式表格 (vtbl),以之做為 Interface 最為適當(也許這其實是因不是果),所以 C++ 是制作 Windows Object 最方便的語言。其他語言不是不行,但你必須自行處理函式表格以及建構元的問題 (C 語言的 malloc 只能配置記憶體,不像 C++ 的 new 運算子有呼叫建構元的能力)。

第四章一開始有個小程式,獲取 Allocator Object 并呼叫其成員函式,配置記憶體來使用。關於 Windows Object 的使用,這個小程式帶給我們很好的觀念。本章後面還有范例,那是大災難的開始。

第五章介紹復合檔案,也就是所謂的結構化儲存體 (Structured Storage)。在這里我們遭遇兩個 Interfaces,一是 IStorage,一是 IStream,前者類似磁碟系統中的子目錄,後者類似磁碟系統中的檔案。這種二維視野除了有助於復合文件的儲存之外,另一個好處是,如果有任何一個檔案能夠符合此一模型,任何其他也知曉這個模型的程式就能夠打開這個檔案并審查其內容。舉個例,如果復合檔案包含了一個stream,內含標準資料結構如 "Summary Information",內有標題、對象、作者、關鍵字,那麼任何熟悉此結構之應用程式就可以打開此檔并決定此文件的標題和對象、決定作者是誰、并可能搜尋關鍵字。這遠優於我們目前所有的檔案模型 --- 只有最初寫此檔案的應用程式,或是私底下擁有該檔案格式之知識者,才能夠開檔并瀏覽檔案內容。有了復合檔案,檔案瀏覽器很容易制作出來,於是使用者就可以輸入這樣的查詢 :「找出所有我寫的文件中,標題上有 MFC 這個字的文件」。

第六章介紹 Data Object,我們將遭遇 IDataObject。可以說,只要支援 IDataObject,這個 Windows Object 就是一個 Data Object。目前由各個 Windows API 完成的所有行程通訊 (IPC) 機能,包括 clipoboard,DDE 和 OLE1,都被收集在 IDataObject 中;無論你如何獲得 Data Object 的指標,你都可以隨後以標準的方法對待這些資料。OLE2 把 Data Object 的使用標準化了,把它從傳輸協定中分離出來。所謂協定,就是通訊雙方的一個標準化溝通方式,如此一來雙方都同意在某種監視之下做資料傳遞。截至目前,資料實體的傳輸都與傳輸協定緊密地包裝 (結合) 在一起,然而在 OLE2,資料的索求或設定脫離了所有的協定,可以大量簡化并均勻化你的應用程式的資料交易行為。OLE2 引入兩個新的資料結構,提供比較好的剪貼簿格式和比較好的全域記憶體,使我們有更好更豐富的資料描述(而不止是一個 UINT) 和更多的資料傳輸媒介 (而不止是一個 HGLOBAL)。

我對本書的補充到此為止。面對這本巨著的雀躍 (以為自己有救了) 以及翻開後看不懂的沮喪 (覺得自己很笨),我完全經歷過并且深感同情。同情你之馀我還得可憐我自己,因為至今我還努力在這塊磚頭上緩慢爬行。

此書已有新版 :

inside-ole.jpg (16970 bytes)

 

背景資料 :
書名 Inside Visual C++ (2nd Edition)
作者 David J.Kruglinski
出版 Microsoft Press
頁數 第 25 章 86 頁 (全書共 26 章 768 頁)
售價 US$ 39.95 (含光碟一片)

25. OLE and OLE Automation
Learning OLE
The Common Object Model (COM)
The Problem That COM Solves
The Essence of COM
What Is a COM Interface
The IUnknown Interface and the QueryInterface Member Function
Reference Counting: The AddRef and Release Function
Class Factories
COM and MFC - The CCmdTarget Class
A Working COM Example
OLE and the Windows Registration Database
Run-Time Object Registration
How a COM Client Calls a DLL Server
How a COM Client Calls an EXE Server
MFC and OLE
Containment vs. Inheritance
OLE Automation
Connecting C++ with Visual Basic for Application
Automation Controllers and Automation Servers
Microsoft Excel - A Better Visual Basic Than Visual Basic
Properties, Methods, and Collections
The Problem That OLE Automation Solves
The IDispatch Interface
OLE Automation Programming
The MFC IDispatch Implementation
An OLE Automation Server
An OLE Automation Controller
The VARIANT Type
Parameter and Return Type Conversions for Invoke
OLE Automation Examples
OLE and the Future

insidevcv2.jpg (17153 bytes)

可巧,去年四月我也在介紹 Inside OLE2 的同時介紹了 Inside Visual C++。一年之後本書有了第二版,這第二版也在去年十一月介紹過了,但是并沒有特別介紹第 25 章。這一章達 86 頁之多,內容很具唯一性,值得挑出來放在本月的 OLE2 專欄。

這一章講兩大主題,一是 OLE2 最基層的 COM (請看圖一),一是 Automation。關於 COM,作者實際設計一個名為「太空船」的物件,支援 IUnknown (因此也就符合加入 COM 俱樂部的條件) 以及另兩個 Interfaces,然後再設計使用這個太空船物件的 Client 端。這是一個很好的教育案例,比 Inside OLE2 第三章的例子還完備。這只是個模擬程式,原本 Server 應該成為 OLE2 系統的一個DLL,Client 則是一般 Windows 程式,但作者把 Server 和 Client 兩部份該做的事放到同一個程式中,并把它做成 QuickWin 程式。除了示范 Component Object 之實作技術,本例的執行結果 (一些 printf 訊息) 也可以幫助你了解 Component Object 的產生程序和 COM 的內部動作,這會對你的 C++ 能力有些長進,因為太空船物件使用 nested classes,頗為復雜。

我不解的是,這個 QuickWin 程式有一個全域物件,全域物件不是更在 main() 或 WinMain() 之前進行建構嗎 ? 但我卻在 printf 輸出訊息上看不到這一部份。於是我把這個程式改制作為 MS-DOS 程式,獲得了理想中的輸出結果。有沒有人能幫我解答這個疑惑 ?

本章剩馀部份全用來介紹 Automation,這是 Inside OLE2 一書缺乏的主題。作者在這部份采用 MFC 設計程式,你的學習重點擺在 Dispatch Map 這個表格上,其形式與 Message Map 極為類似 :

BEGIN_DISPATCH_MAP(CClikDoc, CDocument)
//{{AFX_DISPATCH_MAP(CClikDoc)
DISP_PROPERTY(CClikDoc, "text", m_str, VT_BSTR)
DISP_PROPERTY_EX(CClikDoc, "x", GetX, SetX, VT_I2)
DISP_PROPERTY_EX(CClikDoc, "y", GetY, SetY, VT_I2)
DISP_FUNCTION(CClikDoc, "RefreshWindow", Refresh,VT_EMPTY, VTS_NONE)
DISP_FUNCTION(CClikDoc, "ShowWindow", ShowWindow,VT_EMPTY, VTS_NONE)
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()

這個表格內的記錄有三種類型,一種用來直接開放 property (例如上面的 text),一種用來間接開放 property (例如上面的 x, y),另一種用來開放 method。對於使用者 (也就是 Controller) 而言,無所謂直接或間接,它都是很直覺地使用。以 VB 程式為例 :

Set clik = CreateObject("Auto.Doc")
clik.Text = Text.Text (
設定 text)
clik.X = X.Text (
設定 x)
Y.Text = clik.Y (
取得 y)

但是當存取動作透過 Automation 機制,傳到 Server 端,就不同了。 動作會直接設定 m_str, 動作會引發呼叫 SetX(), 動作會引發呼叫 GetY()。什麼時機使用直接開放 ? 什麼時機使用間接開放 ? 這和 class 設計過程中「究竟使用private 或 public 比較好」,有類似的考量。稍後介紹另一本書時,我會告訴你更具體的衡量標準。

你已經知道了學習重點 : Dispatch Map 表格中的每一筆記錄的意義都必須了解。本章在這方面表現如何 ? 還不錯。ClassWizard 幫助我們制作 Dispatch Map 的過程,也都有圖為例。你在 Dispatch Map 中看到的那些奇奇怪怪的 VT_xxx,也有詳細的介紹 --- 那其實是一種應用於 Automation 的資料型態,例如VT_I2 代表 short,VT_I4 代表 long。

本章給了四個 Controller 范例,以 Excel 為操作對象。Excel 是目前唯一支援 VBA 語言的軟體 (我不清楚最新的 Word 6.0 有沒有)。你可以獲得一些真正具有實用價值的樂趣。

示意圖還是太少,本章難得的文字內容如果再搭配我為了演講 OLE 而設計的圖,就可以得 100 分。長久以來書籍的制作太過僵化,圖就是圖,文就是文,好像硬要把它們處理成一個牛頭,一個馬嘴。這些作者吃北京烤鴨時大概都是先吃面皮,再吃鴨,再吃醬料。我心目中的理想書籍是以圖為主,「主」并不一定代表篇幅份量,而是指講述的中心。我也希望看到打破傳統方式的書籍,大膽從圖中拉線條到文字說明上,讓圖文并茂一體成型。看過 How xxx Work 系列書籍嗎 ? 那種處理方式就是我認為最理想的。應用軟體使用手冊以及 How xxx Work 這類比較通俗軟調的主題,已經有不少以圖為主的書籍,高階技術書籍則還停留在石器時代 --- 是不是寫程式會使人腦袋硬化 ?

 

背景資料 :
書名 OLE2 Classes for The MFC Library
作者 Microsoft Co.
出版 Microsoft Co.
頁數 408 頁
售價 不詳

Part 1 OLE2 Tutorial
1. The Microsoft Foundation OLE2 Classes
2. Contain: Creating an OLE Container
3. Contain Step 1: Creating an OLE Container
4. Contain Step 2: Creating an OLE Container
5. Scribble Step 7: Creating an OLE Server
6. Autoclik: Creating an OLE Automation Server
7. Autoclik Step 1: Creating an OLE Automation Server
8. Autoclik Step 2: Creating an OLE Automation Server
9. Autoclik Step 3: Creating an OLE Automation Server
Part 2 OLE2 Encyclopedia
Part 3 OLE2 Reference

VC++ v1.5 在其 MFC v2.5 中增加了 OLE2 相關類別,并提供這一本「小」冊子 (「才」400 頁出頭)。如果你想學習以 MFC 2.5 寫 OLE2 各式應用程式,就是這本。可惜 VC++ 1.5 采 CD-ROM 包裝,所有書籍都光碟化了,你若需要印刷形式的書籍,必須另外購買;糟的是如果不想花三千多元買與 VC++ 1.0 大部份雷同的手冊,而只是想要其中的 OLE2 和 ODBC 兩本新書,辦不到。

一般人對於軟體隨附的「手冊」印象不佳,興趣不大,覺得店頭的「市場書」比較有啟發性。這要看手冊的定義怎麼下。API 函式或 C 函式庫規格列表,當然不具啟發性,只是供你查閱用;Windows API Bible 這樣的書雖然也是一一介紹函式,卻有范例以及作者心得,就深具教育功能。不過,當心,你也可能會買到名為Bible 卻是函式規格列表的書籍,真是舊詞新解,讓我對所謂 Bible 有了新的體會。朋友說要送我當枕頭,我說我不要,太高了。

這本「小」書有三部份,第三部份是標準的手冊形式,一一介紹 OLE Classes 的成員函式,共 237 頁。第二部份是 OLE 百科全書 (名詞解釋),共 73 頁,很不錯的一份總體介紹。已有點概念,想學習 OLE 程式設計的人,第一部份 (98 頁) 最具價值。這一部份以 Visual C++ 中三個 OLE2 范例程式為對象,解釋 MFC OLE2 Classes 的運用。這三個范例分別是一個 Server,一個 Container,和一個 Automation Server,每個范例都分為數個步驟,一層一層增加功能,是很理想的學習方式。有光碟而沒書籍的人,我建議你把這 98 頁印出來,喔,這三個程式的完整原始碼最好一并印出。說到列印,我還建議你買一臺雷射印表機,沒哪個電腦設備的功能價格比高於這玩意兒的了。上次我從紐約帶回一臺 600 DPI,比國內便宜一萬元,抵機票差不多夠,劃算。目前許多資訊來自網路或光碟片,有一臺安靜快速的雷射印表機,是學習的利器。

回到書上。Server 范例是一個名為「涂鴉」(scribble) 的程式,這個程式有七個步驟,本書只介紹步驟七 (增加 OLE 功能),如果你要了解其前身,套件中的 Class Library User's Guide 有完整說明。Container 的設計比 Server 單純許多,所以你應該從這里開始。Automation Server 則示范各式各樣的「開放」: 資料直接開放、資料間接開放、函式開放,以及高階技術: 把 A 物件視為 B 物件的一個 property 開放出去(這種情況下,透過 Automation 機制傳遞的,是 IDispatch 指標)。

本書舉的這三個 OLE 程式實例的完整度無庸置疑,只是由於篇幅嫌少,說明就顯得不夠詳盡。最理想的情況是,首先應該告訴讀者,AppWizard 做出來的碼長什麼樣子,具備什麼功能;這些骨干程式碼中每一個為了 OLE 功能而使用的類別以及呼叫的函式,都應該詳細說明;然後,在一步一步加上某些功能的過程中,我應該一步一步加上什麼碼。如果能夠說明程式進行時使用者的某些關鍵動作會執行程式中的哪一些碼,就能夠澄清讀者的所有疑慮。噢,OLE 的書已經夠少了,做到這一部份的更是一本也沒有。這一部份不容易寫,我自己也是以除錯器設定中斷點,才能概略了解其內部過程,甚至許多行為還得半推半敲,因為 Visual C++ 的除錯器只能讓你觀察一個程式,而 OLE 卻是兩個程式的交通行為。如果想觀察即地編輯時 Server 的行為,雖然以除錯器設定了 Server 的中斷點,你卻必須在 Visual Workbench 中載入 Container,於是觀察不到 Server 的原始碼。怎麼辦呢 ? 我不知道 (大概只能在 Server 原始碼中加一些 TRACE 巨集然後觀察DebugWin 視窗了)。

背景資料 :
書名 Heavy Metal OLE 2.0 Programming
作者 Steve Holzner
出版 IDG Books
頁數 10 章,574 頁
售價 US$ 39.95 (含磁片一片)

1. A review of Visual C++
2. Creating Containers
3. Creating Servers
4. Managing Multiple OLE Items
5. Power Container Programming
6. Power Server Programming
7. Inside Drag and Drop
8. Inside OLE Automation
9. Beyond MFC: Reaching the OLE Interfaces Directly
10. Visual C++'s OLE Tools

「有一種.... 叫作酷...」,李亞明的這首歌,做為這本書的銷售主題曲,一定正點。封面上斗大的 Heavy Metal 泛著冷冷的銀輝色;兩個齒輪大概是象徵軟體合作,經過處理後的詭譎色彩使整張圖倒像是密宗的靈圖。

有了上一本書,原本我以為這本書只是聊備一格。細看過後才發現,它還是相當有價值的。本書與上一本書形式十分接近,也都是以 MFC 撰寫 OLE2 應用程式,也都是以一個個步驟,分章累加不同程度的功能,但它的安排方式比較理想一些 : 先講一個簡單的 Client,再講一個簡單的 Server,然後在 Client 上改善一些,再在 Server 上也改善一些。作者解釋函式碼時,稍嫌浪費篇幅 : 如果函式有三大機能,每個機能各 10 行碼,他會先列出前 10 行講第一個機能,再列出前 20 行講第二個機能,再列出完整的 30 行講第三個機能。這樣一段一段累加程式碼,頁數的代價高昂,而每章最後又是一個程式碼總列表。當然啦,看書時你的成就感會比較高,唰唰唰一下子又過去了好幾頁。

設計程式時有人喜歡把功能切分得很細,每一小段功能獨立為一個函式,於是可被重復使用的機率也就比較高。但我不喜歡在書上看到這種情況,函式 call 來 call 去令人頭昏眼花。本書程式在功能切割方面比上一本書單純一些,因此比較容易消化。

第一章介紹 MFC 程式設計的大觀念。有沒有必要在 OLE 書中來上這麼一章,我個人的看法前面已經表示過了。第二章介紹一個基本的 Container,示范如何在骨干程式碼中加入下列功能 :

1. 獲得 Server 端的 Item 尺寸。這牽扯到 OnUpdate、OnGetItemPosition、 OnChangeItemPosition 等虛擬成員函式,以及一個呼叫 GetExtent 的輔助函式。 我敢說初學者一定讓這四個函式搞得焦頭爛額。你可以利用 Debug 模式觀察 程式的運作過程,進而了解到 UI 上的什麼動作會引發這些函式被呼叫。

2. 以滑鼠操作 Item,包括搬移、縮放、選擇、開啟。最上層動作 就是改寫 OnLButtonDown 以及 OnLButtonDblClk。你大概會以為 Container 文件中 各個 Item 的外圍四方框,以及滑鼠經過 Item 時的各種游標變化,都是自然天成, 錯!其實那都是程式員的責任。前者利用 CRectTracker 畫上去,後者是程式員改寫 OnSetCursor 函式而完成的。

3. 支援物件聯結。

第三章介紹一個基本的 Server,示范如何在骨干程式碼加上以下功能 :

1. 設定 OLE item 的最初大小 (利用 OnGetExtent)。這個尺寸就是你在 Container 安插一個內嵌物件時 (即地編輯),視窗之最初大小。

2. 設計 OLE Item 資料型態 (本例只是簡單的一個 CString 和一個 CPoint 陣列)。

第四章介紹一個稍稍加強的 Container :

1. 支援許多個 OLE Items。這本是天經地義的事,但骨干程式碼中并未做這種 安排。為了完成此任務,你必須考慮到繪圖以及滑鼠操作兩件事情,前者 修改 OnDraw(),令它有 while 回路巡訪各個 Items,後者修改 LButtonDown(), 增加滑鼠落點測試;當然你也必須修改 LButtonDblClk(),使它能夠 對滑鼠選中的那個 Item 做動作。

2. 增加數個表單項目和工具欄按鈕,使能夠選擇前一個或後一個 Item,或對 目前選擇到的 Item 做動作。

3. 加上剪貼簿的拷貝功能。

第五章介紹一個更為強化的 Container,加強的部份在於 :

1. 改變 OLE Item 在 Container 端的大小,但不影響它在 Server 端的大小。 從這個例子你可以明白一個 OLE Item 其實在 Server 和 Container 兩端都有 一份尺寸。我們只要在 Container 維護兩個資料成員,分別貯存兩份數值即可 解決這個問題。

2. 以圖示 (icon) 顯示 OLE Item。這很簡單,在 OnDraw() 中利用 Item 的 SetDrawAspect() 設定 DVASPECT_ICON 屬性即可 (平常是 DVASPECT_CONTEXT)。

3. 復制一份 OLE Item。這也非常簡單,呼叫 Item 的 CreateCloneFrom() 即可。

4. Undo 功能 - 如果我們能夠維護一份復制的 Item,我們就隨時可以「回復上一動」。 不過你要注意,為了 Undo 而復制的這個 Item,你應該讓它成為隱形人 (利用 DoVerb(OLEIVERB_HIDE))。

5. 改善繪圖效率,這主要是利用 document 的 UpdateAllViews 函式中的 pHint 叁數, 因為它可以傳遞給 view 的 OnUpdate 函式,讓它在采取繪圖措施時有所依據。

第六章介紹一個加強的 Server :

1. 解釋即地編輯時 Container 和 Server 兩端的表單與工具欄的合并原則。

2. 改變 Server 的文件名稱。

3. 設計準備出現於即地編輯時的 Container 表單上的兩個項目 (一個用來改變 資料內容,一個用來改變資料位置)。

4. 強迫 Server 為獨立開啟式 (OLE1 風格) 而不支援即地編輯。這很簡單,即地 編輯時 OLE 會通知 Server 的 OnShow() 函式,我們只要修改它,令它呼叫 OnOpen() 即可偷天換日。Server 獨立開啟時 OLE Item 方框中密布的斜線是誰 為我們加上去的 ? 還會有誰呢,當然是你自己。

5. 支援以同一份 Server Item 產生數份聯結物件。

6. 讓 CEditView 成為 Server view 的基礎類別。這引發的問題是,資料內容儲存在 CEditView 物件的內部緩沖區中,我們如何讓 Server Item 的 OnDraw 函式也能夠 正常繪圖 ? 顯然你必須從緩沖區中取出資料放入 document,再讓 Server Item 的 OnDraw 函式從 document 中取資料出來畫。

第七章把拖拉 (Drag & Drop) 功能加到程式中。你將接觸到發送端的 DoDragDrop,以及收到端的 OnDrop、OnDragEnter、OnDragOver、OnDragLeave 等函式。記得 SDK 范例中有一個程式示范如何設計橡皮盒 (以滑鼠拖拉一個可變大小的四方形),上述最後三個函式和你處理橡皮盒的原理是一樣的 : 滑鼠第一次進入領空時,把四方形畫一次;滑鼠飛越視窗上空時,把原四方形拭去并畫上新四方形;滑鼠飛出領空時,做善後處理并結束整個程序。不過在這里我們并不繪制四方形,而是呼叫 dc.DrawFocusRect(),那就是你在拖拉過程中看到的物件虛線外框。

第八章介紹 Automation。共有五個例子,第一個例子直接開放資料,由 Controller 取得後顯示出來。第二個例子直接開放 A,B 兩筆資料,間接開放 C 一筆資料。C 資料將是A,B 的總和,由 Controller 的某個按鍵事件發生後取出顯示。這種情況下不做間接開放是絕對不行的,因為 C 的取得一定得透過 Server 的運作,才會有正確的結果(讓 C = A + B)。對於直接開放或間接開放之使用,此例帶給你具體建議: 如果Controller 對 Server 資料之取得或設定,必須在Server 的監督之下進行,那麼就必須使用間接開放。第三個例子是開放一個函式,讓 Controller 能夠開啟 Server。第四個例子示范讓開放函式帶有叁數。第五個例子也開放一個函式,允許 VB 程式取得 VC 程式的視窗 handle,隨後 VB 程式呼叫三個 Windows API,在 VC 程式的視窗中畫一條直線。

第九章介紹如何使用 OLE Interfaces。一共有三個例子,前兩個只是把 MFC OLE Classes 中的某幾個成員函式以 Interface 函式替換之,第三個例子比較有點意思,利用 Interface 函式獲得一些 MFC Classes 得不到的資訊。

安裝 Visual C++ 時你會得到一個 [OLE2 Toolkit] group,內有十來個工具,可以輔助我們開發 OLE 應用程式。很遺憾的是 Microsoft 沒有提供這些工具的使用說明。本書第十章介紹的正是這些 OLE 工具,可惜不夠深入 (甚至可以用膚淺來形容),它的作用大概只能提醒你這些工具的存在而已。LRPC Spy 和 IdataObject Viewer 所觀察到的資訊,對 OLE 兩端通訊動作帶來一些蛛絲馬跡,但是本章根本沒有介紹所獲得的資訊的意義。為德不卒 !!

■三兩心得

很樂意藉這個機會提出學習 OLE 程式設計的三兩心得。

首先你必須弄清楚,OLE 應用程式的哪些功能是 AppWizard 為我們產生的,哪些功能得勞動你自己加上去。一開始我以為滑鼠對 Item 的單擊 (意味"select")、雙擊 (意味 "activate")、拖拉,以及每個 Item 周圍的邊框、框的形式變化、滑鼠在框上的游標變化,都是「自然」造就的,也就是說我以為那都是 OLE Classes 的內建能力,但其實都不是 (可憐咱們這些程式員)。這些標準行為與性質并不牽扯應用程式的資料結構,我不知道為什麼 Microsoft 的 OLE2 Classes 不把這些性質收容進去 (也許我該試試 Borland 的 OCF)。有「事件驅動」系統設計經驗的朋友就會知道,這些性質放在 Classes 之中并非辦不到。

再來要弄清楚的,是通訊兩端所使用的 OLE2 類別 : 誰對誰溝通,誰在什麼時機上場演出。例如 Server View 有一個 OnDraw 函式,Server Item 也有一個同名函式,誰在什麼時候被呼叫 ? 又例如 Server Document 和 Server Item 各有一個 Serialize 函式,誰又在什麼時候被呼叫 ? 另外,Container 一開始欲決定 Item 尺寸時,呼叫的 GetExtent 函式系由 Server 的 OnGetExtent 函式回應之,Server 資料改變時呼叫的 NotifyChanged 函式系由Container Item 的 OnChange 函式承接,Container View 的 OnDraw 函式呼叫每一個 Item 的 Draw 函式時系由 Server Item 的 OnDraw 函式承接,凡此種種都必須搞清楚。我舉這些拉拉雜雜的例子,總括一句話就是,你必須知道一來一往的函式關系。這些知識非常難得,沒有一本書提到它(Heavy Metal 那本提到一點點)。在這樣的書出來之前,自己用除錯器設中斷點觀察吧。

有這些扎實的基礎後,再來就是多看例子,多讀書和期刊。一本書絕對不夠,知識無價,書錢別省。還有,MFC 原始碼在你手上,必要時進去巡幽訪勝一下,對你的基礎觀念有絕對的幫助。別忘了做筆記,沒有經過自己整理消化的知識,短暫如過眼云煙。

■ 天才老師

美靜 (我的妻子) 學的是音樂。基本上音樂系學生有著比一般人更早遭遇更多壓力的機會,因為他們要面對成百上千人演奏 (想像臺下黑壓壓一片人頭看著你一舉一動達兩個小時的情景),也因為他們的學習過程中很早就接觸到一對一的專任教師 (所以音樂界極重師承)。我們常常交換彼此的學習過程與教學經驗,一致的看法是: 最怕天才型指導教授。

這話需要前提: 我與美靜都是中等資質。一個人在學習過程中有名師指導,照說最是幸運不過。強將手下無弱兵的壓力雖然時時刻刻督促你挺起胸膛迎接挑戰,但做為一個學生,這是自我應該克服的;我之所以覺得給天才型老師教導的大不幸是,他從來不能了解你的疑惑在哪里。「為什麼你這里不懂」?「為什麼你那里不懂」? 你的天才老師沒有在那麼「理所當然」的地方觸礁過,如何能知道你的迷惑 ? 如果一個人擁有絕對音感,怎麼能夠想像另一個人竟然把 Do 聽成 Sol 而不自覺 ?

學習 OLE 的過程中,我對於天才老師的恐懼日復一日。所有的 OLE 書籍好像都是天才工程師寫出來的。你不能夠說它深度不夠,也不能夠說它廣度不足,你甚至不能夠說它有什麼窒礙難行之處,可是它只告訴你 How,就是不告訴你 Why。作者都以非常「理所當然」的態度告訴你采用哪一個 Class,設計哪一個函式,卻從不告訴你到底什麼時機這個函式會被呼叫,不告訴你這個函式和哪個函式彼此有什麼關聯。這些顯而易見正常該有的問題,為什麼作者們都看不見呢 ? 我自認為比一般人有更多的技術優勢: 我懂得 C++、會運用 MFC、清楚 Windows 程式的運作、也對 Windows 作業系統有相當程度的了解、甚至對於 Clipboard 和 DDE 和 DDEML 等行程通訊方法的涉獵也還算深入;顯然,發生在我身上的 OLE 疑惑絕不是因為該有的基礎不夠。

天才老師不只存在於 OLE 書籍中。目前許多程式工具 (尤其是 Application Framework 這種東西),把寫程式這件事情包裝得像玩游戲一樣,并且想盡辦法讓你相信寫程式從此輕松快樂。許許多多相關書籍也就蜻蜓點水般地盡在表面做功夫,從來不曾稍為深入一點點探索其中道理。我曾經給過「MFC 程式設計」的演講題目,會後問卷調查,學員最喜歡的內容是 MFC 程式從生到死的來龍去脈,包括 WinMain 在哪里,包括視窗類別的注冊與視窗的開啟,也包括訊息回路和視窗函式,以及訊息的繞行與映射。原來,和侯捷一樣認為這些東西很重要的人,所在多有。這些隱藏在黑盒子中的知識,才真正能夠帶給學習者對於黑盒子的全然掌握。你若不了解 (或至少適度了解) 你的愛車內部運作原理,絕對不可能駕御它發揮極致。這麼說,這些用來為程式員節省精力的黑盒子豈不是沒有達到它的黑盒效應 ? 噢,我想黑盒子的價值在於「有人幫你把事情做好了」,而不是連「花心思深入了解」的努力都可以省略。研究別人寫的碼 (那些 Classes),畢竟比自己寫碼輕松太多--- 況且專家做出來的黑盒子故障率也低些。

也許你反問我看電視之前難道必須先知道陰極射線映像管的原理嗎 ? 非也非也,電視觀眾像是軟體終端客戶,你別把自己放錯位置,你是研發人員。

不只是寫程式,軟體的使用一樣得有系統有組織地進行,而一般應用軟體學習手冊卻只教我們選這個按那個,把系統化和組織化的工作留給讀者。天才老師也存在於許多 User's Guide 書籍中,可憐!可憐!

將學問刨根究底到什麼程度,身為工程師自己當有所拿捏。一個人的成就與他做學問的態度有密切關系,你若要得心應手,千萬別忽略了基礎知識。而在沒有知識來源的情況下,善用你的 Debugger 和 TRACE,似乎是唯一可行之道。

另一種天才型式不表現在書籍內容,而在書名。也許你有這樣的經驗: 爬山爬得氣喘噓噓,迎面走來吹著口哨的快樂下山人,於是你問他還有多遠到達山頂。你可能得到兩種答案,一種人基於鼓勵 (或者因為他是健腳),告訴你再過二十分鐘光明頂可及;而你在過了第三個二十分鐘後,依然氣喘噓噓地詢問吹著口哨的快樂下山人...。另一種人據實回答,告訴你前面還有三百六十五里路,二十里處有一座危險斷崖,四十里處溪流湍急,六十里處遇食人族一村,有身首異處之虞...,於是你有了心里準備 (當然也可能打起退堂鼓)。我渴望知道的是實情。我不是健腳,而我相信大多數人也都不是健腳,需要過來人將路途之艱辛明白以告。給別人快樂和希望,卻使他誤判軍機,不能不說是一種遺憾。誰要是告訴我八天學「會」MFC,二十一天學「會」OLE,只有把「會」這個字拿掉,我才能點頭。拿到駕照不敢上路并不能夠叫做「會」開車,只會劃水不會換氣也不能夠叫做「會」游泳。

■ 兩周年

雖然你在 95 年二月份看到這篇稿子,此刻卻是 1994 年的最後一天,只剩兩個小時。這個園地已經歷兩個整年,朋友們的關懷和建議,都使我感動并倍覺榮幸。700 個日子的耕耘,侯捷努力以報各位的信任。容我在這里許個小小心愿,愿大家更熱烈叁與談書,更主動表達意見,更積極監督國內出版品質,愛護好作品揚棄壞作品,讓良幣驅逐劣幣 (從歷史來看,這個心愿大概不會實現)。 


侯捷 2010-07-28 06:59:18

[新一篇] 你正站在十字路口 (2)—論 Application Framework 軟體開發工具

[舊一篇] 我的電腦探索
回頂部
寫評論


評論集


暫無評論。

稱謂:

内容:

驗證:


返回列表