也許是進度太快,前面的章節沒有很好的把握,我在讀第10章的內容的時候,感覺有很多知識點還沒有清楚,而且這章結構組織沒之前的清楚,甚至可以說有點混亂。再次回顧可以加深之前內容的掌握,在這裡有一個關鍵的點就是——完完整整的把課本的代碼走一遍,理解怎麼寫為什麼寫這些代碼。
對於本章的代碼,也是老問題了,此書作者可沒有花時間在講解代碼調試上。由於年代久遠,按書上說的nmake -f makefile會發現出現很多錯誤,花了好一些時間調整好可以編譯調試。現在把一些調試本章代碼的體會寫下來。
這些老代碼,一般是使用nmake命令來編譯,我們在新的VS環境下可以匯入到VS環境中更好的跟蹤代碼,理解COM的精粹。但說回來,對編譯過程沒有足夠的瞭解或經驗有些欠缺,不容易修改好。
本章的標題是:EXE中的伺服器。一種是同一進程中扮演伺服器的角色,另一種是不同進程中扮演伺服器角色。
進入原始代碼目錄Chap10中找到makefile——如果不懂makefile是什嗎?呃,那就要先學makefile了。
always:nmake -f make-onenmake -f make-one OUTPROC=1
可以看出,就是執行了這兩個命令,開啟make-one的檔案,找到all標籤:
all : Client.exe $(TARGETS)
再找到Client.exe的編譯依賴
Client.exe : Client.obj Guids.obj Util.objlink $(EXE_LINK_FLAGS) Client.obj Guids.obj Util.obj \libcmtd.lib libcimtd.lib $(LIBS)
Guids.obj?代碼裡沒有這個檔案,怎麼辦?再看書,這是IDL搞的鬼!需要用MIDL工具產生這東西!為什嗎?看書去!在make-one檔案裡有這麼一行:
midl /h iface.h /iid guids.c /proxy proxy.c server.idl
沒錯,就是它!使用server.idl檔案產生一個標頭檔和兩個c檔案。
好了,這裡是Client.exe的內容,還記得有個&(TARGETS)吧?現在在看看這東西怎麼編譯:
!IF "$(OUTPROC)" == ""!MESSAGE Building in-proc server.SERVER =TARGETS = Server.dllDIR_SERVER = InProc!ELSE!MESSAGE Building local/remote server.SERVER=/D_OUTPROC_SERVER_TARGETS = Server.exe Proxy.dllDIR_SERVER = OutProc!ENDIF
回想makefile裡面第二條編譯指令帶著OUTPROC=1這個參數,第一個是不帶的。並且可以找到這樣的說明Use OUTPROC=1 to build out-of-proc version. 結合書本上講的內容,這就明白了,不帶OUTPROC=1參數的是進程內服務,帶的是進程間服務。先來搞進程內服務的,也就是產生Server.dll。找到其編譯依賴:
SERVER_OBJS = $(DIR_SERVER)\Server.obj \ $(DIR_SERVER)\Cmpnt1.obj \ $(DIR_SERVER)\Cmpnt2.obj \ $(DIR_SERVER)\Cmpnt3.obj \ $(DIR_SERVER)\Registry.obj \ $(DIR_SERVER)\Cfactory.obj \ $(DIR_SERVER)\Cunknown.obj \ $(DIR_SERVER)\Util.obj \ Guids.obj!IF "$(OUTPROC)" == ""Server.dll: $(SERVER_OBJS) Server.def link $(DLL_LINK_FLAGS) $(SERVER_OBJS) libcmtd.lib \libcimtd.lib $(LIBS) /DEF:Server.def regsvr32 -s Server.dll
很清楚,需要哪些obj,再跟到obj的依賴上,可以列出全部需要的檔案集合。
$(DIR_SERVER)\server.obj : server.cpp cunknown.h cfactory.h iface.hcl $(CPP_FLAGS) /Fo"$*.obj" server.cpp$(DIR_SERVER)\cmpnt1.obj : cmpnt1.cpp cmpnt1.h iface.h registry.h \CUnknown.hcl $(CPP_FLAGS) /Fo"$*.obj" cmpnt1.cpp$(DIR_SERVER)\cmpnt2.obj : cmpnt2.cpp cmpnt2.h iface.h registry.h \CUnknown.hcl $(CPP_FLAGS) /Fo"$*.obj" cmpnt2.cpp$(DIR_SERVER)\cmpnt3.obj : cmpnt3.cpp cmpnt3.h iface.h registry.h \CUnknown.hcl $(CPP_FLAGS) /Fo"$*.obj" cmpnt3.cpp$(DIR_SERVER)\CUnknown.obj : CUnknown.cpp CUnknown.hcl $(CPP_FLAGS) $(SERVER) /Fo"$*.obj" CUnknown.cpp$(DIR_SERVER)\CFactory.obj : CFactory.cpp CFactory.hcl $(CPP_FLAGS) $(SERVER) /Fo"$*.obj" CFactory.cpp$(DIR_SERVER)\registry.obj : registry.cpp registry.hcl $(CPP_FLAGS) $(SERVER) /Fo"$*.obj" registry.cpp# util.cpp compiled for server.$(DIR_SERVER)\util.obj : util.cpp util.hcl $(CPP_FLAGS) $(SERVER) /Fo"$*.obj" util.cpp
好了,現在基本的工程邏輯弄清楚了,需要兩個工程:Client.exe工程,server.dll是服務元件的工程(需要註冊後才能使用,怎樣註冊是很早章節的事情了)。中間用到的iface.h guids.c proxy.c是用IDL檔案產生的,需要在VS工程編譯之前完成(當然也可以在工程中加入IDL檔案,設定產生的檔案名稱)。
我就是按上面的步驟做,把工程建立起來調試理解當中的代碼的。當中大部分使用char的情況我都改成TCHAR來運行。把我建立的工程上傳到資源中,希望協助到和我一樣學習COM技術的朋友。
另外,上面沒有提到進程外服務元件的工程編譯,其實也是相同的道理。關鍵是從makefile入手,結合書上的內容理解編譯步驟(前提當然是書上的內容是正確的),然後弄清楚建立幾個工程,需要哪些資源和檔案,啟動並執行時候注意什麼內容。
$(DIR_SERVER)\server.obj : server.cpp cunknown.h cfactory.h iface.hcl $(CPP_FLAGS) /Fo"$*.obj" server.cpp$(DIR_SERVER)\cmpnt1.obj : cmpnt1.cpp cmpnt1.h iface.h registry.h \CUnknown.hcl $(CPP_FLAGS) /Fo"$*.obj" cmpnt1.cpp$(DIR_SERVER)\cmpnt2.obj : cmpnt2.cpp cmpnt2.h iface.h registry.h \CUnknown.hcl $(CPP_FLAGS) /Fo"$*.obj" cmpnt2.cpp$(DIR_SERVER)\cmpnt3.obj : cmpnt3.cpp cmpnt3.h iface.h registry.h \CUnknown.hcl $(CPP_FLAGS) /Fo"$*.obj" cmpnt3.cpp$(DIR_SERVER)\CUnknown.obj : CUnknown.cpp CUnknown.hcl $(CPP_FLAGS) $(SERVER) /Fo"$*.obj" CUnknown.cpp$(DIR_SERVER)\CFactory.obj : CFactory.cpp CFactory.hcl $(CPP_FLAGS) $(SERVER) /Fo"$*.obj" CFactory.cpp$(DIR_SERVER)\registry.obj : registry.cpp registry.hcl $(CPP_FLAGS) $(SERVER) /Fo"$*.obj" registry.cpp# util.cpp compiled for server.$(DIR_SERVER)\util.obj : util.cpp util.hcl $(CPP_FLAGS) $(SERVER) /Fo"$*.obj" util.cpp
http://download.csdn.net/detail/eagleatustb/4972005