Recent

Author Topic: How Can i use a Synchronize() method in dll project? it failure!  (Read 10503 times)

dorry

  • New Member
  • *
  • Posts: 12
How Can i use a Synchronize() method in dll project? it failure!
« on: September 29, 2013, 02:56:01 am »
I have a project dll library project
but
Synchronize(DoEvents)
do not work is frozen! 
someone meet as problem?
How can i do it ?

thanks!

Mujie

  • Jr. Member
  • **
  • Posts: 64
Re: How Can i use a Synchronize() method in dll project? it failure!
« Reply #1 on: September 29, 2013, 04:24:48 am »
Hi @dorry

Did you try load a dll library with thread ? And what kind of "frozen" ? Did you notice your app memory process getting bigger continuously until crash by self? Did you free it after create it some function/methode or something whatever call it  :D?

This is an example to load a dll library with thread.

greatlibrary.dll project :
Code: [Select]
unit greatlibrary;

{$mode objfpc}{$H+}

interface

uses
  Classes, Windows
  { you can add units after this };

function MySuperCoolFunction():string;stdcall;
begin
    MessageBox(0,PChar('Cool yoo'),PChar('Attention'),MB_ICONWARNING or MB_OK);
end;

exports MySuperCoolFunction;
end.

And call it within your app which I named "superlazarus.exe"  :P , with this :
Code: [Select]
unit superlazarus;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils,
  //additional
  Dynlibs, Windows, ...; //... whatever unit uses

type
  ThreadMyAction = class(TThread)
  protected
    procedure LoadingLib;
    procedure Execute; override;
  end;


  { TfmForm1 }

  TfmForm1 = class(TForm)
........................ //bla bla bla

    procedure Button1Click(Sender: TObject);
  private
    { private declarations }
    ExecMyAction: ThreadMyAction;
  public
    { public declarations }
  end;

implementation

uses
  unit2, unit3, unit4, ...; //... whatever

type
TMyAction = function (): string; stdcall;


........................ //bla bla bla


//threading
procedure ThreadMyAction.LoadingLib;
var
  MyHandle: TLibHandle;
  MyActionCallFunc: TMyAction;
begin
  MyHandle := SafeLoadLibrary('greatlibrary.dll');
  if MyHandle<>0 then
  begin
    MyActionCallFunc := TMyAction(GetProcedureAddress(MyHandle,'MySuperCoolFunction')); //call your magic function from a greatlibrary.dl
    if Assigned(MyActionCallFunc) then
    begin
      MyActionCallFunc();
      end;
    end
  else
    begin
      MessageDlg('greatlibrary.dll is missing!',mtWarning, mbOK, 0);
    end;
  FreeLibrary(MyHandle);
end;

//execute!
procedure ThreadLoginUser.Execute;
begin
  if not Terminated then
    begin
      Synchronize(@LoadingLib);
    end;
end;

procedure TfmForm1.Button1Click(Sender: TObject);
begin
  ExecMyAction := ThreadMyAction.Create(False);
  ExecMyAction.FreeOnTerminate := True;
  Exit;
end;

........................ //bla bla bla

Hope this could help you.


marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: How Can i use a Synchronize() method in dll project? it failure!
« Reply #2 on: September 29, 2013, 12:55:25 pm »
Do you call the synchronize of the DLL, or of the main program?

Both modules (DLL and main program) probably have a full RTL+LCL, each with their own state. Read http://wiki.freepascal.org/packages for some more info.

Currently this problem is not really solved in FPC  (that would require packages), so I suggest you avoid it.

If you have to, you have to manually fix this, and some how set the state (wrt synchronization) of the DLL to use the mainprogram synchronization state and callback.,

dorry

  • New Member
  • *
  • Posts: 12
Re: How Can i use a Synchronize() method in dll project? it failure!
« Reply #3 on: September 30, 2013, 04:56:21 am »
Thank for yours answer.

i have Library Project TestDll.dll

inside TestDll

......
Synchronize(DoEvents)
......

end;

main program load the TestDll.dll and start thread in TestDll.dll by a export procedure.

but  in TestDll.dll Synchronize(DoEvents) has some problem.

on delphi there is  a way ,it can let "Synchronize(DoEvents)"  run oK! but lazarus How can i do?

.....................
Procedure SetWHNDHandle(AppHandle: THandle); stdcall;export;
begin
  try
    Application.Initialize;
    if Application.Handle = 0 then begin
      Application.CreateHandle;
    end;
    Application.Handle:= AppHandle;
    Application.Run;
  except end;
end;
« Last Edit: September 30, 2013, 05:02:29 am by dorry »

Mujie

  • Jr. Member
  • **
  • Posts: 64
Re: How Can i use a Synchronize() method in dll project? it failure!
« Reply #4 on: September 30, 2013, 05:04:55 am »
Please take a look more closer my example. Add a '@' at DoEvents

Code: [Select]
Synchronize(@DoEvents)

Graham1

  • Jr. Member
  • **
  • Posts: 57
Re: How Can i use a Synchronize() method in dll project? it failure!
« Reply #5 on: March 28, 2017, 02:44:42 am »
I spent ages trying to get a Synchronize(@Method) to run a call back from my DLL but it would always hang. None of the information about packages etc seemed very helpful, so now I've got it working I thought I'd pass on the information.

The issue is that in a multithreaded DLL the main application is in the EXE so your Synchronize might never get picked up, especially if you don't have access to the EXE code. Since the purpose of Synchronize is to stop the thread until the main thread has executed the Method, your thread never starts again. So here is an example that uses a timer to detect the Synchronize request. The timer could also be put on a form if the DLL has one, but I prefer to keep it in the actual DLL unit.

Code: Pascal  [Select][+][-]
  1. library MYDLL;
  2.  
  3. <...stuff...>
  4.  
  5. type   ACallBackFunc     = function(val:pchar):longint; stdcall;
  6.  
  7.        AThread = class(TThread)
  8.          cbSelect       : integer;
  9.          cbData         : string;
  10.        protected
  11.          procedure Execute; override;
  12.        public
  13.          procedure DoSync;
  14.        end;
  15.  
  16.        ASyncLoop = class
  17.          private
  18.            Timer        : TTimer;
  19.          public
  20.            procedure TimerSet;
  21.            procedure TimerFire(Sender:TObject);
  22.        end;
  23.  
  24. <...stuff...>
  25.  
  26. var    cbFunc           : ACallBackFunc;
  27.        MyThread         : AThread;
  28.        SyncLoop         : ASyncLoop;
  29.  
  30. <...stuff...>
  31.  
  32. function RegisterFunc(regFn:ACallBackFunc):longint; stdcall;
  33. begin
  34.    if not assigned(MyThread) then MyThread:=AThread.Create(false);
  35.    cbFunc:=regFn;
  36.    result:=0;
  37. end;
  38.  
  39. function DLLfunction(somedata:pchar):longint; stdcall;
  40. begin
  41.    if not assigned(MyThread) then MyThread:=AThread.Create(false);
  42.    <pass somedata to MyThread, perhaps via global var>
  43.    result:=0;
  44. end;
  45.  
  46. exports RegisterFunc,
  47.         DLLfunction;
  48.  
  49. <...stuff...>
  50.  
  51. procedure AThread.Execute;
  52. begin
  53.    while not Terminated do begin
  54.  
  55.       <get and clear somedata from global var via CriticalSection>
  56.  
  57.       if <callback required and assigned(cbFunc)> then begin
  58.         cbSelect:=1;
  59.         cbData:='moredata';
  60.         Synchronize(@DoSync);
  61.       end;
  62.  
  63.    end;
  64. end;
  65.  
  66. procedure AThread.DoSync;
  67. begin
  68.    if cbSelect=1 then cbFunc(pchar(cbData));
  69.    <other possible call backs>
  70. end;
  71.  
  72. <...stuff...>
  73.  
  74. procedure ASyncLoop.TimerSet;
  75. begin
  76.   Timer:=TTimer.Create(nil);
  77.   Timer.Interval:=500;
  78.   Timer.OnTimer:=@TimerFire;
  79.   Timer.Enabled:=true;
  80. end;
  81.  
  82. procedure ASyncLoop.TimerFire(Sender:TObject);
  83. begin
  84.    CheckSynchronize;
  85. end;
  86.  
  87. <...stuff...>
  88.  
  89. BEGIN
  90.  
  91. <...stuff...>
  92.  
  93. SyncLoop:=ASyncLoop.Create;
  94. SyncLoop.TimerSet;
  95.  
  96. END.
  97.  
Windows 10/11 Home 64-bit (and Linux because I have to)
Lazarus 2.0.12 / FPC 3.2.0 (because libQt5pas 1.2.6)
Linux Mint 20 (because GLIBC_2.31)

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Re: How Can i use a Synchronize() method in dll project? it failure!
« Reply #6 on: March 28, 2017, 03:18:09 am »
If Synchronize() is implemented in FreePascal/Lazarus anything like it is in Delphi, it requires the main UI message loop to process sync requests, and that cannot happen in a DLL without cooperation from the EXE that loads the DLL.  A DLL is a self-contained module, it does not share the EXE's instance of the RTL, that is why sync requests from a DLL go unprocessed by default.  The solution in Delphi is to export a function from the DLL that calls the RTL's CheckSynchronize() function, and then make the EXE call that exported function periodically.  A similar solution is likely required for FreePascal/Lazarus.
« Last Edit: March 28, 2017, 03:19:45 am by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Graham1

  • Jr. Member
  • **
  • Posts: 57
Re: How Can i use a Synchronize() method in dll project? it failure!
« Reply #7 on: March 28, 2017, 03:37:08 am »
I agree, and that is fine if you have access to the EXE code. But in many cases you are trying to write a DLL to interface with somebody else's EXE and you cannot change it. My solution will provide the CheckSynchronize within the DLL's RTL without needing to amend the calling EXE.
Windows 10/11 Home 64-bit (and Linux because I have to)
Lazarus 2.0.12 / FPC 3.2.0 (because libQt5pas 1.2.6)
Linux Mint 20 (because GLIBC_2.31)

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Re: How Can i use a Synchronize() method in dll project? it failure!
« Reply #8 on: March 28, 2017, 04:49:19 am »
I agree, and that is fine if you have access to the EXE code. But in many cases you are trying to write a DLL to interface with somebody else's EXE and you cannot change it. My solution will provide the CheckSynchronize within the DLL's RTL without needing to amend the calling EXE.

Your solution relies on the EXE loading the DLL within a thread that has an active message loop to receive and dispatch the timer messages to the timer's internal window. And worse, you are creating and starting the timer within the DLL's entry point, which is not guaranteed to be safe, per MSDN's DLL guidelines.

The better solution is to just have the DLL thread call the callback function directly without syncing it at all, and document it as such, and let the EXE implement its own UI syncing if needed.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Graham1

  • Jr. Member
  • **
  • Posts: 57
Re: How Can i use a Synchronize() method in dll project? it failure!
« Reply #9 on: March 28, 2017, 05:16:30 am »
Thank you so much for your feedback! It's exactly the sort of input I was hoping for when I posted this. I will change the lines starting the thread to be:

Code: Pascal  [Select][+][-]
  1. if not assigned(MyThread) then startThread;

and this procedure can create the thread and SyncLoop objects. That should be safer.

The EXE I am dealing with does indeed have it's own loop, which makes repeated calls to the DLL. If I tried doing the callbacks from the DLLfunction then they were nesting in a stack, as well as holding up the main application if my function took a lot of time to complete (which it does as it waits for human input). When I tried running the callbacks directly from the thread, the EXE complained: it was using QT and that said it couldn't process calls from a different thread. The result was that the EXE's forms were never updating.

Maybe my situation is too specific for this to be a general solution then.
Windows 10/11 Home 64-bit (and Linux because I have to)
Lazarus 2.0.12 / FPC 3.2.0 (because libQt5pas 1.2.6)
Linux Mint 20 (because GLIBC_2.31)

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Re: How Can i use a Synchronize() method in dll project? it failure!
« Reply #10 on: March 28, 2017, 06:47:09 am »
The EXE I am dealing with does indeed have it's own loop, which makes repeated calls to the DLL. If I tried doing the callbacks from the DLLfunction then they were nesting in a stack, as well as holding up the main application if my function took a lot of time to complete (which it does as it waits for human input). When I tried running the callbacks directly from the thread, the EXE complained: it was using QT and that said it couldn't process calls from a different thread. The result was that the EXE's forms were never updating.

What I said still applies.  If the DLL starts a thread, that thread can call the callback directly.  If the DLL does not start a thread, but instead directly calls the callback from inside DLL exported functions, then the EXE should start a thread to call the DLL. Either way, long running operations do not belong in the main UI thread.  If the callback is moved to a worker thread,  the EXE can then call Synchronize() inside the callback, and the EXE will handle the sync request normally.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

 

TinyPortal © 2005-2018