bill parish 0 Posted July 27, 2020 (edited) Delphi10.2.3: OTL, FMX, ACTIVEX DCOM i have a big problem in my code to call the parallel.Async many times i have 6 ActiveX DCOM in Delphi, and wanna call these in same time, to calculate an invoice, so in my exe i work with OTL to async all calls activex, i don't like to wait result or freeze my screen. Parallel.Async( procedure (const task: IOmniTask) var iInnerExc: integer; begin /// ***********************************************************/// { return Log} TLogSingleton.Instance().Log('Services XX calcul: Début appel.',True); //************************* PARALLEL ASYNC ****************************// // executed in background thread try // {Create instance de ViewModel annexes: XXServiceXX} mainViewModelPreCalculOTLLevel03 := CreateMainViewModelOTLLevel03Class; {Affecter le même Model principal} mainViewModelPreCalculOTLLevel03.Model := fModel; {Suscribe the observer in list} mainViewModelPreCalculOTLLevel03.Provider.Subscribe(fSubscriber); {Call VieModel.ServiceXX} mainViewModelPreCalculOTLLevel03.StartPrecalcul; except on e:exception do {call the raise, juste ci dessous, dans le onterminated Handle} raise Exception.Create('Exception lors du traitement de Service XX.'); end; end, Parallel.TaskConfig.OnTerminated( procedure (const task: IOmniTaskControl) var excp: Exception; begin // executed in main thread if assigned(task.FatalException) then begin {expoiler l'exception, captcher ci dessus} excp := task.DetachException; TErrorSingleton.Instance().Log(Format('Service XX: [Fin TASK Error]. %s:%s',[excp.ClassName, excp.Message]),True); FreeAndNil(excp); end else begin // executed in main thread { return Log} TLogSingleton.Instance().Log('Services calcul: [Fin TASK] Fin appel traitement.',True); SendNotification([actServiceXXFinish]); end; end )); the Active X Call in exe FMX: (the server activeX is integreted in framework, i don't have the source code, and i don't know haw it writen) _Dm_Dcom := CreateObject('SERVICE.XX.CLT.SERVICEXX', 'DEFAUT') as ICoServiceXX; //Instance of the DLL //CALL the function in ServiceXX_impl _Dm_Dcom.GETTRaitementServiceXX(SafeArrayIn,SafeArrayOut,FListnerForPushNotif ); //with SafeArrayIn =Json IN, and take result in SafeArrayOut; FListnerForPushNotif to send notification (Type Interface: Iunknow) the ActiveX initialization in every SERVICEXX (DLL) TAutoObjectFactory.Create(ComServer, TTest, Class_Test, ciMultiInstance, tmApartment); in this DLL; we have ServiceXX_impl unit, we have one function to call traitement in another class, we can found a communication with the IInterface (published) to send notif like this: (FListnerForPushNotif as IDataListnerForPushNotification).PushNotificationForLog('Message to send to client side'); //we have many logs types: event, Error, Exception //PushNotificationForLog, PushNotificationForError,PushNotificationForException this is my call, i repeat it 6 times, Parallel.Async( CALL01 ); Parallel.Async( CALL01 ); Parallel.Async( CALL02 ); Parallel.Async( CALL06 ); i don't need to freeze my exe, and i don't need to wait, with SendNotification([actServiceXXFinish]); or other Action like "actService01Finish", "actService02Finish" i can kown if my traitement is finished or not, finaly, if i write all 6 calls, i will fall in many and many problems, like freeze screen, and deadlock (the only solution kill the process crl+alt+supp), but if i write only one call, it will be perfect, i have, my result, but the freeze screen persist, Conclusion: 1- with many calls: problems: screen freeze, traitement not working, --> kill the process. 2- with one only call: problems: screen freeze: the "TAniIndicator" not working and the screen freeze when Delphi enter in the other DLL (activeX). Nota: the Exe is FMX (in another step it will be a Form application), the 6 services are DLL (ActiveX DCOM). plz help me Edited July 27, 2020 by bill parish orthographe Share this post Link to post
Primož Gabrijelčič 223 Posted July 27, 2020 I'm very sorry but I really don't speak any Frech. Can you please translate your question? Share this post Link to post
bill parish 0 Posted July 27, 2020 1 hour ago, Primož Gabrijelčič said: I'm very sorry but I really don't speak any Frech. Can you please translate your question? it's ok now, 🙂 Share this post Link to post
Primož Gabrijelčič 223 Posted July 27, 2020 This is actually a COM problem, I believe, and I know almost nothing about COM, so please consider that my thinking may be entirely wrong. I believe that you are having problems because you initialize your server in tmApartment mode but then access it from multiple threads. http://docwiki.embarcadero.com/RADStudio/Sydney/en/Choosing_a_Threading_Model says for tmApartment: " All client calls use the thread in which the object was created." And the object is created in the main thread. Does your code work if you use TThread instead of OTL to run async tasks? Share this post Link to post
David Heffernan 2345 Posted July 28, 2020 https://stackoverflow.com/questions/63111541/omnithread-delphi-many-calls-in-the-same-function For reference, here is the same Q on SO Share this post Link to post
darnocian 84 Posted December 15, 2020 I realise this is an old post... The other thing - usually with COM, you need to CoInitialize() in each thread, and if you are a nice citizen, CoUninitialize(). To coordinate shutdown, you may also want to look at TCountdownEvent from System.SyncObjs. TCountdownEvent waits for a counter to reach zero. A signal in a thread would trigger the countdown. Could do something like (sorry, I automatically referenced TTask.Run rather than Parallel.Async) // This is pseudocode, so might not be 100% accurate with params var event := TCountdownEvent.Create(); event.AddCount(length(workList)); for var work in workList do begin TTask.Run(procedure begin CoInitialize(); try process(work); finally CoUninitalize(); event.signal(); end; end; end; event.WaitFor(INFINITE); With the above code, if run from a UI would still block, so that should also be threaded if the UI is meant to remain responsive. Share this post Link to post
David Heffernan 2345 Posted December 15, 2020 9 minutes ago, darnocian said: The other thing - usually with COM, you need to CoInitialize() in each thread, and if you are a nice citizen, CoUninitialize(). This could be pretty expensive if the task is short lived. As a general rule, the owner of the thread should take charge of initialising COM. In this case it's probably the thread pool. I'd be looking for a feature of the threadpool whereby the client could request that threads created by the thread pool come initialised for COM. Then the initialisation can happen once for each thread, rather than for each task. Share this post Link to post
darnocian 84 Posted December 18, 2020 Performance wise 100% agree with you David. It was more an example to check it out initialisation in case that was an issue. Share this post Link to post