Newsgroup: it.comp.lang.delphi
Titolo: Thread e Marshalling

Buongiorno,
vi propongo il mio problema.
Sto sviluppando un'applicazione che sfrutta le librerie Indy,
perchè devo ricevere dei dati da 5 lettori di codice a barre
collegati in ethernet; in particolare utilizzo un oggetto TCPServer,
per ogni lettore, i quali, alla ricezione di un codice, alza
il suo evento "Execute".
Questo evento "Execute" è un "Thread" a tutti gli effetti, il chè mi
va
benissimo, perchè devo eseguire velocissimamente del codice
dopo la ricezione di ogni codice a barre, quindi i lettori di codice
devono restare indipendenti l'uno dall'altro.
Bene.
Il codice da eseguire all'interno di ogni evento "Execute" sfrutta
una libreria ad oggetti di tipo "COM" per andare a leggere dei
dati da un PLC, cioè un partner della rete ethernet;
la libreria è la dOPC di un certo Kassl, la quale gestisce la
comunicazione Ethernet dei PLC della serie Siemens via OPC.
Per semplificare la comprensione ecco com'è il codice:
TCPServer.Execute
begin
  coInitialize(nil);
  dOPC.Active:= true;
  ...
  Lettura dati dal PLC;
  Processo Dati;
  ...
  dOPC.Active:= false;
  coUninitialize();
end;
In pratica, apro la connessione col PLC con
l'istruzione dOPC.Active:= true
(la quale fa partire un suo Thread),
poi leggo i dati e chiudo la connessione
(il che provoca il terminate del Thread).
Così funziona, ma il tempo di esecuzione
dell'istruzione "dOPC.Active:=true" è troppo alto,
ed allora ho pensato di portare fuori
questa istruzione dall'evento "Execute"
nel FormCreate principale, in modo tale
da attivare la connessione al lancio dell'applicazione
e lasciarla attiva fino al termine della stessa.
COSI' NON FUNZIONA!
Perchè?
Quando faccio così, appena entro
all'interno dell'Execute ed eseguo le
istruzioni per leggere i dati nel PLC,
mi viene ritornato questo errore:
Class eOleSysError with Message 'L'applicazione ha
chiamato un'interfaccia su cui era stato eseguito il
marshalling per  un thread differente'.
In pratica, la presenza del Thread di collegamento
col PLC non permette l'esecuzione dell'Execute,
anche usando i famosi "coInitialize" e Company.
Qualcuno mi può aiutare a capire cosa significa?
Grazie mille soprattutto per aver letto questo papiro!
Fabrizio.

 COSI' NON FUNZIONA!
La documentazione di CoInitialize dice questo:
"Initializes the COM library on the current thread and identifies the
concurrency model as single-thread apartment (STA). "
Dice "On the current thread": quindi va chiamata per OGNI thread che crei.
Devi usare l'API CoInitializeEx per la gestione multithread: purtroppo io
non so aiutarti di piu' perche' va oltre le mie conoscenze.
Saluti
ACSH
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 non so aiutarti di piu' perche' va oltre le mie conoscenze.
Anche io non sono molto ferrato nell'usare COM, perň l'OP potrebbe a 
questo punto spostare la creazione dell'oggetto COM e la sua distruzione 
direttamente nel thread.
Creando il thread sospeso e ovviamente anche gli oggetti che devono 
comunicare con il PLC, puoi lasciare all'interno dell'Execute del thread 
stesso tutto il codice di gestione. Ovviamente, sposta all'interno del 
thread anche TCPServer.

 direttamente nel thread.
Grande Guastatore :-)
Io faccio pesante uso di COM con il framework con cui lavoro, e la tua idea
è aderente con quanto fatto da me sino a d'ora.
Il codice che segue, opportunamente ripulito di parti prive di interesse, è
l'execute di un thread istanziato da un servizio.
Al suo interno, come si può vedere, costruisco dei COM e li uso senza
problemi.
Quando il servizio invoca il terminate del thread, le risorse vengono
deallocate con il couninitialize presente nell'execute.
------------------------------------------------------------------
procedure TFetchSpeedyTaskOrigineThread.Execute;
begin
  CoInitialize(nil);
  Tasks := CoSpeedyTasks.Create;  
  Tasks.Filter.TipoFamiglia.Value.AsDouble :=
FTask.TipoFamiglia.Value.AsDouble;  
  SortItems := CoSortItems.Create;
  SortItems.AddItem( Tasks.Filter.PrioritaEsecuzione,TypeSortDescendent);
  Tasks.SortItems := SortItems;
  Tasks.Info.Connection := FConnection.Connection;
 
  try
    Count := 0;
    while not Terminated do
      begin
        Inc(Count);
        if (Count >= FMyProcess.SecondsFetchInterval.Value.AsInteger) then
          begin
            Count := 0;
            rc := Tasks.CursorOpen;
            if rc > 0 then
              begin
                Tasks.CursorItem;
                if FMyProcess.IsSynchronized.Value.AsBoolean then
                begin
                  DoSynch(Tasks.Cursor)
                end
                else
                begin
                  DoASynch(Tasks.Cursor)
                end;
              end
            else if rc < 0 then
                FSenderLog.SendMessage('Errore durante apertura cursore
task: rc='+FloatToStr(rc), LetError,MethodName);
            Tasks.CursorClose;
          end;
        Sleep(1000);
      end;
  finally
    CoUninitialize;
  end;
end;
----------------------------------------------------------------
-- 
Morde

  end;

 accade un errore, non compi l'uninitialize.
Hai ragione, ho spostato il tutto all'interno del try.
Grazie.
-- 
Morde