1. 程式人生 > >Delphi 編寫COM+元件的知識和樣例

Delphi 編寫COM+元件的知識和樣例

一、COM元件的基礎知識介紹:

1、什麼是COM

         COM是微軟定的一種元件編寫的規範,目的是為了實現元件的重用,不管你是用C、DELPHI、VB什麼語言寫的,只要遵守這種規範就是COM元件,就能相互之間呼叫。那這個規範是什麼呢,最簡單的,就是按規定使用介面,微軟定了個IUnkown介面,只要實現了這個介面的類,就可以稱為COM元件。在此基礎上可以加上自己的介面,實現特定的功能,這就成了有用的COM元件了。

2、COM、DCOM、COM+

         COM是一個基於二進位制的標準。打個比方,我們用Delphi實現了一個物件,一般情況下,我們只能在Delphi來生成這個物件的例項並呼叫,而如果我們用Delphi實現了一個COM物件的話,我們可以用VC、VB或者其他任何一種支援COM物件的語言來生成例項和呼叫。反過來也一樣,我們可以在Delphi中使用各種COM物件,而不用介意它是用什麼語言編寫的。COM提供了分散式COM物件的機制,形象地說你可以呼叫另一臺機器中的COM物件。COM+則是MTS的一個升級,在COM的基礎上進一步提供了事務處理和其他很多Pool技術。

2、DELPHI中如何建立COM元件(兩個步驟)

  (1)、建立一個ActiveX Library,這是個DLL;【ActiveX選項卡】

   (2)、建立一個COM Object或者Automation Object,二者選其一,這兩者都是COM元件,但是當然還是有區別的,差在呼叫的方法上,這個後面再說。【ActiveX選項卡】;

(建立了一個ActiveX Library,名叫mylib。一個Automation Object,名叫myobj。並且編譯註冊。)

  注意:COM Object只能通過介面呼叫,Automation Object可以調介面或名稱呼叫。

3、DELPHI中如何呼叫COM元件

(兩種方法呼叫),

第一種方法(用介面來呼叫)
        適用於支援介面的語言,如C、DELPHI;VB就不行了,好處是速度快,因為不用根據名稱去查找了,還有開發時編譯器能檢查,缺點是要匯入型別庫。

需要如下操作:

        在use里加上mylib_TLB型別庫(如果不是自己開發的沒有TLB可以從DLL匯入生成的),程式碼是var myobj:Imyobj;myobj=Comyobj.create();之後就可以用了。如果有興趣可以看看型別庫的程式碼裡,Comyobj.create裡呼叫了CreateComObject,這函式返回的是IUnkown介面,再用as操作轉成你需要的Imyobj介面。

第二種方法(用名稱來呼叫)
        適用於所有的語言,好處是不用匯入型別了,缺點是呼叫的速度慢,還有開發時編譯器不檢查。

        用第二種方法不需要型別庫了,程式碼是var myobj:variant;myobj=CreateOleObject('mylib.myobj');之後就可以用了,這個方便吧。其實為了實現用名字呼叫,需要加上IDispatch介面,不過delphi都自動給我們加好了,不用管直接用就行。

1>、 idispatch是COM object的介面, 在Delphi中通常指一個OleObject.

2>、 OleVariant是一種COM object相容的Variant型別, 可以通用任何Ole Automation 型別, 他與CreateOleObject建立的idispatch相容 。

(COM元件的編寫和COM+元件的編寫差不多,所以只寫COM+元件的編寫情況)

二、COM+編寫的具體例項:

1、Com+的編寫:

   1>、File---->New---->Other....---->ActiveX Library 標籤下的 Transactional Object

   2>、然後填寫: CoClss Name :類的名字,比如:ComPlus Threading Modal :執行緒模式:Apartment 選項: Supports transactions

         當建立COM+ 時選擇的事務模式為Requires a Transaction, COM+會根據客戶的的請求建立相應的事務,不僅僅是資料庫,還會有系統資源等事務.成功SetComplete.回滾SetAbort. 選擇Requires a Transaction表示當用戶呼叫這個COM+元件時,COM+環境會為這個元件建立一個新的事務上下文,這和資料庫的事務不是一回事。當你的COM+元件提交資料時如果出錯,應該告訴事務上下文,只要呼叫COM+元件的SetAbort方法就可以。這樣一來,處於同一個事務上下文的所有COM+元件都會Rollback。如果資料提交成功,應該呼叫SetComplete,不呼叫這個方法也可以,因為在預設情況下,COM+元件的事務狀態設定為EnableCommite。當處於同一事務上下文的所有COM+元件物件都呼叫了SetComplete時,該事務上下文才會真正的向資料庫提交資料。

   3>、然後在View--->Library的對話方塊中增加方法 注意:如果引數為輸出的話,則型別要是指標型,比如:Long * ,然後修改後面的引數in :out,ret

   4>、最後完善增加的方法就ok了

2、客戶端呼叫的編寫:

   1>、先倒入Com+的介面型別. Project --->import Type Library---->選中你編寫的Com+的型別,然後選擇:Create Unit

3、安裝COM+元件有兩種方式,

   1>、第一種(推薦):如果是在IDE環境裡,點選“Run->Install COM+ Objects”即可把開啟的Active Library專案安裝到COM+環境中,注意:如果開啟的專案是 一個普通的Application專案,是不能被安裝到COM+環境中的。 將要安裝的com+打上勾,然後在application中有兩個選項:install to existing application :表示你的com+安裝在com伺服器的哪個元件包中, install to New application:表示將當前com+元件安裝到一個新的元件包中.

   2>、第二種辦法:開啟控制面板-> 管理工具->元件服務->計算機->我的電腦->COM+應用程式,在COM+應用程式的樹項上點選 滑鼠右鍵,選擇“新建->應用程式”->建立一個空的應用程式,併為此應用程式命名,接下來點選“下一步”直到結束即可。建立了空的COM+應用程式後,接下來就是把COM DLL安裝 到COM+應用程式中了。在剛建立的空應用程式的樹項中新建一個元件,選擇“安裝新元件”, 在開啟檔案對話方塊中選擇要安裝到COM+環境中的DLL檔案,之後跟著嚮導做都可以了,要把多個COM DLL安裝到同一個COM+應用程式包中,只需重複以上步驟即可。

4、匯出客戶端元件包

匯出客戶端元件包指的是把已經註冊的元件匯出為.msi格式的檔案,這些檔案在客戶端安裝後,只會在客戶端註冊元件,而不會安裝多餘的檔案。如果不在客戶端註冊元件,是不能呼叫位於伺服器上的元件的(此指伺服器和客戶端分佈在不同的機器上時)。

 5、除錯Com+程式 ---ok

   1>、開啟Windows中的元件管理,找到要除錯的元件包,點右鍵,選擇屬性,在高階這頁裡選擇除錯選項,打勾; 然後在下面的除錯路徑中找到/processID:{xxxxxxxxxxxxxxxxxxxxxxxxxxxxx} 複製出來

   2>、在dephi中Run | Parameters… HOST APPLICATION 填入 {系統路徑}\system32\dllhost.exe PARAMETERS 粘巾 /processID:{xxxxxxxxxxxxxxxxxxxxxxxxxxxxx}

   3>、很關鍵的一點:元件程式:project|option|linker|Include TD32 debug info 和Include remote debug symbols打勾

   4>、啟動delphi,執行要除錯的Com+程式,設定斷點,然後執行客戶端程式即可進入到Com+斷點.

   5>、除錯完後記得要在 Windows中的元件管理中的高階這頁裡除錯選項勾去掉喲.

6、Com+需要注意的地方:

   1>、客戶機執行就會報 interface not supported 錯誤.大致原因: COM+的許可權依賴於Windows的許可權配置,在伺服器需要有客戶機的使用者名稱和密碼。 如果還不行,就在伺服器上重新安裝com+,重新匯出.

   2>、建立工程時,com+不能包含在工程組中.(我的實踐)

   3>、COM+不支援Oracle嗎?在用事務的時候出錯:Using Oracle with Microsoft Transaction Server and COM+

7、在Com+中新增遠端資料模組

   1>、File---->New---->Other....---->Multitier 標籤 下的 Transactional Data Module

   2>、然後在View--->Library的對話方塊中增加方法.

8、Com+中傳遞陣列

   1>、先定義陣列:

     type TDataRecord = record

        A: Byte;

        B: LongWord;

        C: Word;

        D: LongWord;

     end;

   2>、伺服器端:

     function GetData: OleVariant;

     var

        P: Pointer;

     begin

        VarClear(Result);//不知D5有沒。

        try

           Result := VarArrayCreate([0, SizeOf(TDataType)], varByte);

           P := VarArrayLock(Result); //Data: TDataType為你要傳的記錄型別

           Move(Data, P^, SizeOf(TDataType)); // 把Data的值傳給P指向的變體型別

        finally

           VarArrayUnLock(Result);

        end;

     end;

   3>、客戶端:

     procedure GetFile(const FileName: string);

     var

        P: Pointer;

        V: OleVariant;

        Data: TDataType;

     begin

        FillChar(Data, SizeOf(Data), 0);

        V := SocketConnection1.AppServer.GetData;

        try

           P := VarArrayLock(V);

           Move(P^, Data, SizeOf(Data));

        finally

           VarArrayUnLock(V);

        end;

     end;

9、Com+中傳遞記錄集

伺服器端:

Uses  ADOInt;

你可以將ADO的資料作為一個Variant型別的變數進行傳送:adodataset1.RecordSet這是原生的ado資料

 這是服務端的一個方法的程式碼:把CodeSet的型別改為Variant*[in,out]

function TADORec.getData: OleVariant;

begin

   AdoDataSet1.Open;

   result := adodataset1.RecordSet;

end;

//可以將ADO的資料作為一個Variant型別的變數進行傳送:adodataset1.RecordSet這是原生的ado資料

客戶端

uses adoint;

var

    MyRecordset :_recordset;

begin

    MyRecordset := IUnknown(CodeSet) as _recordset

End;

10、Com有需要研究和有疑問的地方:

   1、Com+的模式:

   2、到Com+的資源Pooling機制

   3、用delphi6 開發Com+,用Neutral模式,儘量用Object Pooling,當然就是要無狀態了,事務要儘量短,避免死鎖,用ADO不要用BDE.Dbexpress等.

   4、我的做法是,按功能劃分元件,把查詢和更新分為兩個功能元件,因為查詢不需要事務,所以只要支援事務就行了。更新一般需要事務。

   5、COM+的事務屬性 COM+的事務預設級別是序列 Read,而不是我們認為的commit Read,而且我們設定AdoConnection的隔離級是不管用的,只能強制用顯式的SQL才能起效果。例如: 一箇中間層COM :select * from a where py_code like 'S%' 繼續下面有很多程式碼;另一箇中間層COM: update a set py_code=py_code where py_code like 'B%'; 這時這個COM將會死鎖等待,雖然改動的不是一個數據也會死鎖 用Commit Read 或UnCommit Read不會建議: 所以被多個子系統更新或讀的表,要在最後開啟和更新,不要早。另外可以強制改變ADOCONNECTION的隔離級

6、Com+中如何進行事務處理? 在COM+中,如何使用SetComplete和SetAbort進行事務管理?SetComplete和SetAbort怎麼能在客戶端呼叫呢?這樣肯定是不行的。要保證事務,就在伺服器端宣告一個專門儲存的介面方法,例如:

procedure UpdateMyData(var AData1, AData2: OleVariant; var AMaxError, AErrorCount: Integer); begin    

    try

        DataSetProvider1.Data := AData1;

        DataSetProvider1.ApplyUpdate(AMaxError, AErrorCount);

        DataSetProvider2.Data := AData2;

        DataSetProvider2.ApplyUpdate(AMaxError, AErrorCount);

        SetComplete;

    except

        SetAbort;

    end;

end;

   7、在元件管理器中的“事務列表”裡也看不到事務(應該有事務的時候),怎麼看?

   8、Com+的兩大研究:事務和 pooling

   9、做一個提交用的COM+物件和一個協呼叫的COM+物件。

   10、建議:多個DLL在一個包,一個DLL中的COM公用一個ADOCONNECTION

   11、問題:我已經在Transactional Data Module的Pooled屬性裡面設定了True了,但是在Win2000的元件服務管理的“元件屬性”的“啟用”一欄裡面,仍然無法開啟“啟用物件共用”的選項 --->您可以將執行緒模式設定為tmNeutral或者tmBoth都可以。