PDA

Visualizza la versione completa : [DELPHI] Errore: MSXML not installed


VaLvOnAuTa
18-01-2006, 10:16
Salve :D

Sempre per la famosa applicazione client-server (che sto recodando usando i componenti indy) ho sviluppato un protocollo che si basa sullo scambio tra client e server di dati formattati con xml.

Un esempio di formattazione dei dati potrebbe essere questa:

<valvonauta>
<header>
<comando>LOGIN</comando>
</header>
<info>
<username>VaLvOnAuTa</username>
</info>
</valvonauta>
Ora ho creato un datamodule con un XMLDocument che ha due funzioni:
Formattare il dato in xml ed estrarre i dati da una stringa (ovviamente formattata in xml).
Il codice è questo:

unit cProtocollo;

interface

uses
SysUtils, Classes, xmldom, XMLIntf, msxmldom, XMLDoc;

type
TProtocollo = class(TDataModule)
xmlDoc: TXMLDocument;
private
public
Comando : string;
function Tipo(pXml: string) : string;
function ToXml : string;
end;

var
Protocollo: TProtocollo;

implementation

function TProtocollo.Tipo(pXml: string): string;
var nNodo: IXMLNode;
begin
xmlDoc.Active:=False;
xmlDoc.XML.Text:=pXml;
try
xmlDoc.Active:=True;
nNodo:=xmlDoc.DocumentElement.ChildNodes['Header'];
Tipo:=nNodo.ChildNodes['Comando'].Text;
xmlDoc.Active:=False;
except
Tipo := '';
end;
end;

function TProtocollo.ToXml: string;
var vXML: string;
begin
vXml:= '<valvonauta>';
vXml:=vXml + ' <Header>';
vXml:=vXml + ' <Comando>'+ Comando +'</Comando>';
vXml:=vXml + ' </Header>';
vXml:=vXml + '</valvonauta>';
ToXml:=vXML;
end;

{$R *.dfm}

end.La funzione "Tipo" mi serve per riconoscere il comando ricevuto.
Ora, il client utilizza lo stesso datamodule e riesce ad estrerre il comando correttamente.
Il server invece, mi da l'errore nel titolo.
Dato che sulla stessa macchina il client funziona (riesce ad estrarre correttamente il dato tra i tag <Comando> e </Comando>) ed il server no, credo ci sia qualcosa che mi sfugge, ma non è certo l'installazione dell' MSXML. :dhò:

alka
18-01-2006, 10:34
Devi installare il componente richiesto dal CD di Delphi oppure scaricandolo dal sito Microsoft (via preferibile) sulla macchina in cui deve eseguire la tua applicazione.

Ciao! :ciauz:

VaLvOnAuTa
18-01-2006, 10:39
Originariamente inviato da alka
Devi installare il componente richiesto dal CD di Delphi oppure scaricandolo dal sito Microsoft (via preferibile) sulla macchina in cui deve eseguire la tua applicazione.

Ciao! :ciauz: Ma non mi spiego il motivo per cui quel componente funziona su un'applicazione e non su un'altra (ho aggiunto al progetto sia del client che del server lo stesso datamodule).
Oltretutto l'applicazione (instabile) che avevo già creato prima funziona basandosi sullo stesso concetto (utilizzando gli stessi componenti) e non mi ha dato mai problemi (almeno non sull'xml) :bhò:

Comunque adesso lo cerco e lo installo... non si sa mai... :fagiano:

alka
18-01-2006, 10:49
Installa l'ultima versione su entrambe le macchine e compila il programma, poi ritenta la distribuzione.

L'alternativa è quella di utilizzare un parser differente, come Open XML (http://www.philo.de/xml/).

Ciao! :ciauz:

VaLvOnAuTa
19-01-2006, 11:25
Riprendo questa discussione perchè ho trovato qualcosa di interessante.
Il problema, cercando in giro sulla rete, riguarda il fatto che un oggetto COM (quale un XMLDocument), in un'applicazione multithread, ha bisogno di essere inizializzato in ogni thread.

Ora, l'applicazione che ho creato io è necessariamente multithread (in quanto, suppongo, il componente "IdTcpServer" utilizzi più thread per gestire più connessioni contemporanee ed ecco perchè sui client funzionava: il componente "IdTcpClient" non ha bisogno di gestire più threads dato che la connessione è unica).

Cercando ulteriormente ho trovato una FAQ in inglese che dice quanto segue:


Ever get the "CoInitialize has not been called" (800401F0 hex) error?
Sì.. ricompilando la stessa applicazione con delphi 7, l'errore cambia ma la motivazione dell'errore è la stessa.. almeno a detta della Borland

Each thread in your application that interacts with COM (i.e. creates COM objects, calls COM APIs, etc.) must initialize itself into an apartment. A thread can either join a single threaded apartment (STA) or the multithreaded apartment (MTA).

The STA is system-synchronized based on a windows message queue. Use the STA if your object or thread relies on thread-relative resources such as UI elements. The following shows how to initialize a thread into an STA:

procedure FooThreadFunc; //or TFooThread.Execute
begin
CoInitializeEx (NIL, COINIT_APARTMENTTHREADED);
... do your stuff here ...
CoUninitialize;
end;

The MTA is system-guaranteed to be ruthless. Objects in the MTA will receive incoming calls from anywhere anytime. Use the MTA for non-UI related objects, but synchronize carefully! The following shows how to initialize a thread into the MTA:

procedure FooThreadFunc; //or TFooThread.Execute
begin
CoInitializeEx (NIL, COINIT_MULTITHREADED);
... do your stuff here ...
CoUninitialize;
end;
Ok.. niente di più semplice.. basta richiamare la funzione CoInitializeEx() all'inizio della funzione che gestisce il thread e chiamare la CoUninitialize alla fine della funzione.
Quindi il codice della mia funzione dovrebbe essere:

procedure TServer.SrvExecute(AThread: TIdPeerThread);
var
vText,
vTipo: string;
begin
CoInitializeEx(NIL, COINIT_APARTMENTTHREADED);
vText := AThread.Connection.ReadLn;
vTipo := Protocollo.Tipo(vText);
showMessage(vTipo);
if (vTipo = 'LOGIN') then
begin
showMessage(vTipo);
end;
CoUninitialize;
end;Ora resta da scoprire in quale unit si cela sta funzione :fagiano:

VaLvOnAuTa
19-01-2006, 11:42
Ok trovato.

E' nella unit ComObj.. peccato che non ci sia una guida di referenza... forse su msdn.microsoft...

alka
19-01-2006, 11:44
Cerca in ComObj oppure in ActiveX.

Sono le unit che fanno parte del framework DAX, le classi Delphi per l'uso (e creazione) di componenti COM e le dichiarazioni per l'accesso alle relative API.

Ciao! :ciauz:

alka
19-01-2006, 11:48
Originariamente inviato da VaLvOnAuTa
E' nella unit ComObj.. peccato che non ci sia una guida di referenza... forse su msdn.microsoft...
Si tratta di funzioni API, quindi tutte le informazioni sul loro utilizzo sono reperibili su MSDN oppure scaricando e installando la Win32 SDK Documentation aggiornata dal sito Microsoft.

Ciao! :ciauz:

VaLvOnAuTa
19-01-2006, 11:57
Grazie mille alka.
In effetti nella unit ComObj si trova solo CoInitialize mentre nella unit ActiveX si trovano entrambe le funzioni.

Ora il problema della chiamata a Coinitialize è risolto.
Però pare che il documento COM (xml in questo caso) non venga attivato (nonostante nel codice io setti xmldoc.Active := true).

Girerò ulteriormente su internet e vedo se trovo una soluzione anche a questo. Nel caso ci riuscissi, metterò la soluzione sul forum per i posteri :D

VaLvOnAuTa
19-01-2006, 13:30
Ok ho trovato la soluzione.
Magari spiego il problema dato che ho letto in giro su internet diversi problemi simili al mio senza una soluzione.

Innanzitutto l'applicazione è di tipo Client-Server (componenti "IdTcpClient" e "IdTcpServer" rispettivamente delle palette "Indy Clients" e "Indy Servers") dove lo scambio di informazioni tra i due host avviene con dei messaggi formattati in XML (componente "XMLDocument" della palette "Internet").
Il client non ha alcun problema a processare il documento XML perchè utilizza un unico thread.
Il problema sorge sul server che, ricevendo più connessioni, deve gestire ciascun socket come un thread a sè.
Ebbene, il problema è che un ComObject (quale è il componente XMLDocument) deve essere inizializzato (e poi concluso) in ogni thread (quindi in pratica ad ogni esecuzione del server - evento "IdTcpServer.OnExecute").
Quindi diventa necessario includere la libreria "ActiveX" nella clausola "uses" dell'applicazione server e utilizzare le funzioni "CoInitialize" e "CoUnitialize" prima di eseguire qualunque operazione.


procedure Form1.IdTcpServer1Execute(AThread: TIdPeerThread);
begin
CoInitialize(NIL);

//operazioni da eseguire sul thread

CoUninitialize;
end;

Per quanto riguarda l'errore di documento non attivo, è stato un mio errore: attivavo l'XMLDocument prima di assegnargli il testo nella proprietà "XML".
That's all folks :D

Loading