這個問題在這里已經有了答案: XML:未安裝 MSXML (3 個回答) Delphi:在執行緒中加載 xml 檔案 1 個回答 在 Delphi 執行緒中使用 CoInitialize 1 個回答 昨天關閉。
我一直在使用 IndyTIdTCPServer物件并TXMLDocument在TIdTCPServer.OnExecute事件期間實體化一個物件實體。我發現當xml.Active設定為 true時出現例外非常令人驚訝:
未安裝 Microsoft MSXML
procedure TForm4.tcpRXExecute(AContext: TIdContext);
var
sResponseXML : string;
xml:IXMLDocument;
begin
// get message from client
sResponseXML := AContext.Connection.IOHandler.ReadLn;
xml:=TXMLDocument.Create(nil);
// error here: "Microsoft MSXML is not installed"
xml.Active:=true;
xml.Encoding:='UTF-8';
xml.LoadFromXML(sResponseXML);
// use the xml document
//AContext.Connection.IOHandler.WriteLn('... message sent from server :)');
end;
深入研究,我發現發生例外的原因TMSXMLDOMDocumentFactory.TryCoCreateInstance()是無法創建正確的檔案物件實體,盡管GuidList從主執行緒接收到的與在應用程式其他部分收到的相同。我不明白如果從組件的執行緒呼叫物件,為什么不實體化。
這是應實體化物件的 Embarcadero 代碼:
class function TMSXMLDOMDocumentFactory.TryCoCreateInstance(const GuidList: array of TGUID): IUnknown;
var
I: Integer;
Status: HResult;
begin
for I := Low(GuidList) to High(GuidList) do
begin
// never successful if the XML document object was being used from the Execute event handler.
Status := CoCreateInstance(GuidList[I], nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, IDispatch, Result);
if Status = S_OK then Exit;
end;
end;
我希望它必須與CLSCTX_INPROC_SERVER或CLSCTX_LOCAL_SERVER(https://docs.microsoft.com/en-us/windows/win32/api/wtypesbase/ne-wtypesbase-clsctx)有關,但我不明白為什么這些可能是一個問題。
即使這是原因,我如何TXMLDocument在該事件處理程式中使用?
uj5u.com熱心網友回復:
MSXML 是一種基于 COM 的技術。您需要CoInitialize/Ex()在訪問 COM 介面的每個執行緒背景關系中呼叫以初始化 COM 庫。否則,在這種情況下,CoCreateInstance()將失敗并顯示CO_E_NOTINITIALIZED錯誤。Delphi 的 RTL 在主執行緒中為你初始化 COM 庫,但是你必須自己在作業執行緒中完成,比如TIdTCPServer.
默認情況下,TIdTCPServer為每個客戶端連接創建一個新執行緒。在這種情況下,初始化 COM最簡單的地方是在服務器的OnConnect事件中(因為該OnExecute事件是回圈的)。
procedure TForm4.tcpRXConnect(AContext: TIdContext);
begin
CoInitialize(nil);
end;
procedure TForm4.tcpRXDisconnect(AContext: TIdContext);
begin
CoUninitialize();
end;
但是,由于TIdTCPServer支持執行緒池,并且 COM 應該每個執行緒只初始化一次,因此在這種情況下初始化 COM的最佳位置1是直接在每個執行緒的Execute()方法中。要做到這一點,將一個 -TIdSchedulerOfThread派生組件(TIdSchedulerOfThreadDefault,TIdSchedulerOfThreadPool等)顯式分配給該TIdTCPServer.Scheduler屬性(可以在設計時完成),然后將該TIdSchedulerOfThread.ThreadClass屬性(必須在運行時完成,在服務器激活之前完成)設定為一個 -TIdThreadWithTask派生覆寫虛擬BeginExecute()和AfterExecute()方法的類。
type
TMyThreadWithTask = class(TIdThreadWithTask)
protected
procedure BeforeExecute; override;
procedure AfterExecute; override;
end;
procedure TMyThreadWithTask.BeforeExecute;
begin
CoInitialize(nil);
inherited;
end;
procedure TMyThreadWithTask.AfterExecute;
begin
inherited;
CoUninitialize();
end;
procedure TForm4.FormCreate(Sender: TObject);
begin
IdSchedulerOfThreadDefault1.ThreadClass := TMyThreadWithTask;
end;
1:至少在https://github.com/IndySockets/Indy/issues/6在 Indy 的未來版本中實作之前。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/315586.html
