Thread, Pipes, maj VCL et Objets COM : Figeage total !
Bonjour à tous.
J'aurai besoin de votre aide sur un sujet qui me dépasse complètement.
J'ai une application composée de 2 process séparés : un moteur et une interface graphique (HMI). Tout le travail est fait par le process moteur, mais toutes les interactions utilisateur sont prises en charge par le process HMI. La HMI affiche ce que fait le moteur et lui envoie des commandes lorsque l'utilisateur clique quelque part.
Le moteur et sa HMI sont 2 objets COM. Chacun connait et exploite l'interface COM de l'autre pour communiquer.
Actuellement, l'exécution dans le moteur est synchrone avec l'affichage dans la HMI.
Je souhaite désynchroniser l'affichage dans la HMI d'avec l'exécution dans le moteur.
J'ai donc mis en place un pipe de communication entre les 2 process. Le pipe est créé par la HMI, laquelle attends ensuite la connexion du moteur puis lorsque la connexion est établie, la HMI crée un thread qui se chargera de lire les données reçues du moteur et de mettre à jour l'interface graphique.
La HMI accuse un léger retard par rapport au moteur, mais ca permet au moteur de tourner plus vite. Bref, ça fonctionne bien.
Jusqu'à ce que l'utilisateur fasse un clic quelque part : la HMI envoie la demande au moteur à travers son interface COM. Et là, il arrive de façon aléatoire que la HMI se fige complètement. TOTALEMENT.
Je ne comprends pas ce qu'il se passe, je n'ai aucune piste. Je croyais avoir bien fait les choses, autant dans la gestion du pipe que dans celle des objets COM.
Voici le code du moteur :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
{$REGION 'Moteur'}
{$REGION 'Connexion au pipe'}
repeat
kernel.CursorPipeHandle := CreateFile(pwidechar(format('\\.\pipe\%0:s',[_IN_sCurseurPipeName])), GENERIC_WRITE, 0, nil,
OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0);
until kernel.CursorPipeHandle <> INVALID_HANDLE_VALUE ;
{$ENDREGION}
{$REGION 'Ecriture dans le pipe'}
procedure EcireDansPipe();
const
g_PipeBufferSizeCursors = 100 ;
var
l_iWrittenInDefectPipe : Cardinal ;
l_acBufferCursorPipe: array [0 .. g_PipeBufferSizeCursors - 1] of char;
l_sStrCursorPipe: pchar;
begin
l_sStrCursorPipe := PWideChar(format('blabla',[]));
fillchar(l_acBufferCursorPipe, g_PipeBufferSizeCursors, #0);
move(l_sStrCursorPipe[0], l_acBufferCursorPipe[0], Length(l_sStrCursorPipe) * Sizeof(char));
WriteFile(kernel.CursorPipeHandle, l_acBufferCursorPipe[0], Length(l_sStrCursorPipe) * Sizeof(char), l_iWrittenInDefectPipe, nil);
if(l_iWrittenInDefectPipe <> (Length(l_sStrCursorPipe) * Sizeof(char)))then
begin
exit ;
end;
end;
{$ENDREGION}
{$ENDREGION} |
Et voici le code de la HMI :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
{$REGION 'HMI'}
const
g_PipeBufferSize = 32768 ;
{$REGION 'Création du pipe et attente de connexion'}
procedure TCursorPipeReaderManager.Execute;
var
l_iHandleNewPipe : THandle;
begin
l_iHandleNewPipe := CreateNamedPipe(PWidechar(format('\\.\pipe\%0:s',[self.mPr_sPipeName])), PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, g_PipeBufferSize, g_PipeBufferSize, 0, nil);
ConnectNamedPipe(l_iHandleNewPipe, nil);
mPr_PipeReader := TPipeReaderCursors.Create(l_iHandleNewPipe, true);
mPr_PipeReader.FreeOnTerminate := true ;
mPr_PipeReader.sName := self.mPr_sPipeName ;
mPr_PipeReader.InfoNotification := self.mPr_InfoNotification ;
mPr_PipeReader.Start ;
end;
{$ENDREGION}
{$REGION 'Thread de lecture du pipe'}
procedure TPipeReaderCursors.Execute; // thread
var
l_iRead: cardinal;
l_dOccupationEnPourcentage : double ;
l_sMsg : string ;
l_acBuffer: array[0..g_PipeBufferSize - 1] of char;
l_bSuccess : boolean ;
procedure Traiter(const _IN_sReçu : string);
begin
l_dOccupationEnPourcentage := (mPr_iRemplissage/g_PipeBufferSize) * 100 ;
if(assigned(mPr_InfoNotification))then
begin
Synchronize(procedure
begin
// Mise à jour de la HMI.
mPr_InfoNotification(self.sName, l_dOccupationEnPourcentage, _IN_sReçu);
end);
end;
end;
procedure LocalFinalize();
begin
DisconnectNamedPipe(mPr_iHandlePipe);
CoUninitialize();
end;
begin
CoInitialize(nil);
try
while not self.terminated do
begin
l_sMsg := '';
FillChar(l_acBuffer, g_PipeBufferSize, #0);
l_bSuccess := ReadFile(mPr_iHandlePipe, l_acBuffer[0], g_PipeBufferSize, l_iRead, nil);
if Terminated then
begin
LocalFinalize();
exit ;
end;
if((l_bSuccess = false) or (l_iRead = 0))then
begin
LocalFinalize();
exit ;
end
else
begin
l_sMsg := l_sMsg + Copy(l_acBuffer, 0, l_iRead);
mPr_iRemplissage := GetFileSize(mPr_iHandlePipe,nil) ;
Traiter(l_sMsg);
end;
end;
Except on E : exception do
begin
LocalFinalize();
exit;
end;
end;
LocalFinalize();
end;
{$ENDREGION}
{$REGION 'Méthode évènementielle sur HMI'}
procedure onBtnClick(Sender : TObject);
begin
// Appel d'une méthode chez le Moteur via son interface COM.
// L'exécution de cette ligne provoque de façon aléatoire le figeage total de la HMI.
Hmi.m_fKernel.HtkEvent_BtnOnClick(...);
end;
{$ENDREGION}
{$ENDREGION} |
Quelqu'un a une idée ? 8O