How to update progress indicator from a second thread? - delphi

I have a main form with a progress indicator on it.
In the datamodule I've ten datasets, each of them has an OnBeforeOpen event defined.
I would like to show through the progress bar in the main form a percentage of progress of the opened datasets.
Since I'm completely new to multithreading programming, can someone please give me some advice?
Thank you very much

Either post a message from the thread to the main thread and update the progress bar from there or use the TThread.Queue method to execute some code in the context of the main thread.
unit Unit12;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls;
const
WM_UPDATE_PB = WM_USER;
type
TForm12 = class(TForm)
ProgressBar1: TProgressBar;
ProgressBar2: TProgressBar;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
public
procedure WMUpdatePB(var msg: TMessage); message WM_UPDATE_PB;
end;
var
Form12: TForm12;
implementation
{$R *.dfm}
procedure UpdateFromThreadViaMessage;
var
i: integer;
begin
for i := 1 to 100 do begin
Sleep(20);
PostMessage(Form12.Handle, WM_UPDATE_PB, i, 0);
end;
end;
procedure UpdateFromThreadViaQueue;
var
i: integer;
begin
for i := 1 to 100 do begin
Sleep(20);
TThread.Queue(nil,
procedure begin
Form12.ProgressBar2.Position := i;
end);
end;
end;
procedure TForm12.Button1Click(Sender: TObject);
begin
TThread.CreateAnonymousThread(UpdateFromThreadViaMessage).Start;
TThread.CreateAnonymousThread(UpdateFromThreadViaQueue).Start;
end;
procedure TForm12.WMUpdatePB(var msg: TMessage);
begin
ProgressBar1.Position := msg.WParam;
end;
end.

Related

Delphi IdTCPClient.connect Hanging while threaded

I am trying to run 8 threads making IdTCPClient repeatedly scanning a range of IP. When these threads are running, the main form is hanging, lagging. The "IdTCClient.connect" is the issue. Is there a way to fix that ?
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TTcpThread = class(TThread)
Idx: word;
procedure Execute; override;
end;
TForm3 = class(TForm)
procedure FormShow(Sender: TObject);
private
public
end;
var
Form3: TForm3;
implementation
uses IdTCPClient;
{$R *.dfm}
procedure TTcpThread.Execute;
begin
for var i := Idx*8 to (Idx*8) + (8-1) do // 8 IP per Thread
begin
var TCP := TidTCPClient.Create(nil);
TCP.Host := '192.168.1.' + i.ToString;;
TCP.Port := 9999;
TCP.ConnectTimeout := 500;
TCP.ReadTimeout := 1000;
try
TCP.Connect;
TCP.Disconnect;
except end;
TCP.Free;
end;
end;
procedure TForm3.FormShow(Sender: TObject);
begin
for var i := 0 to 32-1 do // 32 Threads
begin
var Thread := TTcpThread.Create(True);
Thread.Idx := i;
Thread.FreeOnTerminate := True;
Thread.Start;
end;
end;
end.
The mainform will freeze/lag while the threads are running

Invalid handle passed to midiInClose

Below I have some code. What I want to happen is when a button is pressed it creates, or closes, a MIDI connection depending on the state of an existing connection.
But, when attempting to close the MIDI handle I get an error response code 5, which means an invalid handle to a MIDI device has been passed to midiInClose.
I'm not sure why this is happening by my guess is scope issues? I just can't figure out how to resolve this. Should the hMidiIn be defined as a class variable within the form?
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.UITypes, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
MMSystem, uMIDI;
type
TForm2 = class(TForm)
MidiInputCombo: TComboBox;
Label1: TLabel;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
IsConnected: Boolean = False;
SelectedMidiInputID: Integer = 0;
hMidiIn: PHMIDIIN;
implementation
{$R *.dfm}
procedure MidiInProc(hmi: PHMIDIIN; wMsg: UINT; dwInstance: DWORD_PTR; dwParam1: DWORD_PTR; dwParam2: DWORD_PTR); stdcall;
begin
// Do something
end;
procedure TForm2.Button1Click(Sender: TObject);
var
MidiInCloseResult: Integer;
begin
if IsConnected then
begin
MidiInCloseResult := midiInClose(hMidiIn^);
if MidiInCloseResult = MMSYSERR_NOERROR then
begin
IsConnected := False;
Button1.Caption := 'Connect';
end
else
MessageDlg('Response: ' + IntToStr(MidiInCloseResult), mtInformation, [mbOk], 0, mbOk);
end
else
if midiInOpen(#hMidiIn, SelectedMidiInputID, DWORD_PTR(#MidiInProc), 0, CALLBACK_FUNCTION) = MMSYSERR_NOERROR then
begin
IsConnected := True;
Button1.Caption := 'Disconnect';
end;
end;
procedure TForm2.FormCreate(Sender: TObject);
var
I: Integer;
DevCount: Integer;
Pmic: MIDIINCAPS;
begin
DevCount := MidiInputDeviceCount();
if DevCount > 0 then
for I := 0 to DevCount do
midiInGetDevCaps(I, #Pmic, SizeOf(pmic));
MidiInputCombo.Items.Add(Pmic.szPname);
MidiInputCombo.ItemIndex := SelectedMidiInputID;
end;
end.

Interface DLL form app crashing

I have problem with host application, which loads DLL form and interfaceing some function and properties.
The purpose is load a dll, show name as module name, set connection to ADOTable component and show form with data. Everything is working fine. But after close the host app a host app crashed and I get windows that hostapp.exe stopped working.
I do not know whether it is by freeing library or setting nil for interface.
Do you have any solution? Thanks.
Interface CODE
unit u_baseplugin_intf;
interface
uses Data.Win.ADODB, Data.DB;
type
IBaseModuleInterface = interface
['{060A9C46-B3CF-4BA4-B025-2DC1D9F45076}']
function GetModuleName: Ansistring;stdcall;
procedure SetConn(sConn:TAdoConnection);stdcall;
procedure showF;stdcall;
procedure freeF;stdcall;
property ModuleName: Ansistring read GetModuleName;
property Connection : TAdoConnection write SetConn;
end;
implementation
end.
DLL code
library profileslist;
uses
System.SysUtils,
System.Classes,
u_baseplugin_intf,
u_profileslist in 'u_profileslist.pas' {Form_DLL};
{$R *.res}
function LoadModule:IBaseModuleInterface;stdcall;
begin
result:=TForm_DLL.Create(nil);
end;
exports
LoadModule;
begin
end.
DLL Form code
unit u_profileslist;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.DBGrids, Vcl.StdCtrls,
u_baseplugin_intf, Data.DB,Data.Win.ADODB;
type
TForm_DLL = class(TForm, IBaseModuleInterface)
DBGrid1: TDBGrid;
ADOTable1: TADOTable;
DataSource1: TDataSource;
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
{Interface methods implementation}
function GetModuleName: AnsiString;stdcall;
procedure SetConn(sConn:TAdoConnection);stdcall;
public
{ Public declarations }
{Interface methods implementation}
procedure ShowF;stdcall;
procedure FreeF;stdcall;
end;
var
Form_DLL: TForm_DLL;
implementation
{$R *.dfm}
{Interface methods implementation}
function TForm_DLL.GetModuleName;
begin
Result := 'Profiles list';
end;
procedure TForm_DLL.SetConn(sConn: TAdoConnection);
begin
AdoTable1.Connection:=sConn;
end;
procedure TForm_DLL.ShowF;
begin
ShowModal;
end;
procedure TForm_DLL.FreeF;
begin
FreeAndNil(Form_DLL);
end;
{Form_DLL methods implementation}
procedure TForm_DLL.FormClose(Sender: TObject; var Action: TCloseAction);
begin
AdoTable1.Active:=false;
end;
procedure TForm_DLL.FormShow(Sender: TObject);
begin
AdoTable1.Active:=true;
end;
end.
HOST app code
program hostapp;
uses
Vcl.Forms,
u_hostapp in 'u_hostapp.pas' {Form1},
u_baseplugin_intf in 'u_baseplugin_intf.pas';
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Host app FORM code
unit u_hostapp;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
u_baseplugin_intf,
Data.Win.ADODB, Data.DB;
type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;
Button1: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TModuleInterface = function:IBaseModuleInterface; stdcall;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
aModuleIntf : IBaseModuleInterface;
dllHandle : cardinal;
procedure LoadModule( aLibName : pWideChar );
var
lModule : TModuleInterface;
begin
dllHandle := LoadLibrary(aLibName) ;
if dllHandle <> 0 then
begin
#lModule := GetProcAddress(dllHandle, 'LoadModule') ;
if Assigned (lModule) then
aModuleIntf := lModule //call the function
else
begin
ShowMessage('GetModuleIntf not found.') ;
FreeLibrary(dllHandle) ;
end;
end
else
begin
ShowMessage(aLibName+' not found.') ;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
aModuleIntf.Connection:=AdoConnection1;
aModuleIntf.ShowF;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
aModuleIntf.Connection:=nil;
aModuleIntf.freeF;
aModuleIntf:=nil;
FreeLibrary(dllHandle);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadModule('profileslist.dll');
Label1.Caption:=aModuleIntf.ModuleName;
end;
end.
You never assign to Form_DLL. This means that when you call FreeF, you then perform FreeAndNil(Form_DLL). Since Form_DLL is nil, this does nothing, and the form still exists.
Fix that by changing LoadModule:
function LoadModule:IBaseModuleInterface;stdcall;
begin
Assert(not Assigned(Form_DLL));
Form_DLL:=TForm_DLL.Create(nil);
result:=Form_DLL;
end;
Although, I'd probably change the design completely by removing Form_DLL altogether. The host app maintains a reference to the form, on which the call to Free can be made. In other words, remove Form_DLL and implement FreeF like this:
procedure TForm_DLL.FreeF;
begin
Free; // or Destroy
end;
Or even better, use reference counted interfaces on the implementing object and let aModuleIntf:=nil take the form down.

Simple OpenGL Code not working

Just learning some OpenGL with delphi and trying something simple but not getting a result, I belive i should get a dark green form. But when i run this i get nothing. No errors either. maybe missing something?
unit First1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls,OpenGL, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
GLContext : HGLRC;
ErrorCode: GLenum;
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormCreate(Sender: TObject);
var
pfd: TPixelFormatDescriptor;
FormatIndex: integer;
begin
fillchar(pfd,SizeOf(pfd),0);
with pfd do
begin
nSize := SizeOf(pfd);
nVersion := 1; {The current version of the desccriptor is 1}
dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL;
iPixelType := PFD_TYPE_RGBA;
cColorBits := 24; {support 24-bit color}
cDepthBits := 32; {depth of z-axis}
iLayerType := PFD_MAIN_PLANE;
end; {with}
FormatIndex := ChoosePixelFormat(Canvas.Handle,#pfd);
SetPixelFormat(Canvas.Handle,FormatIndex,#pfd);
GLContext := wglCreateContext(Canvas.Handle);
wglMakeCurrent(Canvas.Handle,GLContext);
end; {FormCreate}
procedure TForm2.FormDestroy(Sender: TObject);
begin
wglMakeCurrent(Canvas.Handle,0);
wglDeleteContext(GLContext);
end;
procedure TForm2.FormPaint(Sender: TObject);
begin
{background}
glClearColor(0.0,0.4,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
{error checking}
errorCode := glGetError;
if errorCode<>GL_NO_ERROR then
raise Exception.Create('Error in Paint'#13+
gluErrorString(errorCode));
end;
end.
Since you request a single buffered context, you must call glFinish at the end of the rendering code, to commit your drawing commands to the implementation. However I strongly suggest you switch to using a double buffered context and instead of glFinish-ing you issue a wglSwapBuffers which implies a finish.

FMX form in a DLL (firemonkey/delphi)

Im trying to make a FMX form in a dll, after about 17 hours (of trying diffrent approches) i got it working, except i get a exception trying to unload the dll. I have no idea how to make it work, maybe someone could help me and point out what im doing wrong?
side note:
i cant have a FMX form in my VCL application becouse of the AA drawing, i just need it on my text while drawing on a canvas and while having a FMX form on a VCL application, i dont get that cleartype on text :( im trying to make a some sort of OSD/HUD.
Project showing my problem:
exe unit1.pas
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
unitLoadDLL, Winapi.GDIPOBJ;
procedure TForm1.Button1Click(Sender: TObject);
begin
showme();
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
closeme();
end;
end.
exe unitLoadDll.pas
unit unitLoadDLL;
interface
uses Windows, Dialogs;
type
TShowme = procedure();
TCloseme = procedure();
var
showme : TShowme = nil;
closeme : TCloseme = nil;
DllHandle : THandle;
implementation
initialization
if DllHandle = 0 then begin
DllHandle := LoadLibrary('C:\Users\Ja\Desktop\dupa\dll\Win32\Debug\Project1.dll');
if DllHandle > 0 then begin
#showme := GetProcAddress(DllHandle,'showme');
#closeme := GetProcAddress(DllHandle,'closeme');
end
else begin
MessageDlg('Select Image functionality is not available', mtInformation, [mbOK], 0);
end;
end;
finalization
if DLLHandle <> 0 then
FreeLibrary(DLLHandle);
end.
dll project1.dpr
library Project1;
uses
FMX.Forms,
System.SysUtils,
System.Classes,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
procedure showme(); stdcall export;
begin
TForm1.showme;
end;
procedure closeme(); stdcall export;
begin
TForm1.closeme;
end;
exports
showme, closeme;
begin
end.
dll unit1.pas
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs;
type
TForm1 = class(TForm)
Label1: TLabel;
private
{ Private declarations }
public
class procedure showme();
class procedure closeme();
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
class procedure TForm1.showme();
begin
Form1 := TForm1.Create(Application);
Form1.Show;
end;
class procedure TForm1.closeme();
begin
Form1.Free;
end;
end.
EDIT (FIX):
All answers ware helpfull, but what i've done is, that the GDI+ was shutdown BEFORE the dll unload... that appear's to be the problem.
new unitLoadDll.pas
unit unitLoadDLL;
interface
uses Windows, Dialogs;
type
TShowme = procedure();
TCloseme = procedure();
var
showme : TShowme = nil;
closeme : TCloseme = nil;
DllHandle : THandle;
function LoadLib : Boolean;
procedure UnloadLib;
implementation
function LoadLib : Boolean;
begin
if DllHandle = 0 then begin
DllHandle := LoadLibrary('C:\Users\Ja\Desktop\dupa\dll\Win32\Debug\Project1.dll');
if DllHandle > 0 then begin
#showme := GetProcAddress(DllHandle,'showme');
#closeme := GetProcAddress(DllHandle,'closeme');
end
else begin
MessageDlg('Select Image functionality is not available', mtInformation, [mbOK], 0);
end;
end;
Result := DllHandle <> 0;
end;
procedure UnloadLib;
begin
if DLLHandle <> 0 then begin
FreeLibrary(DLLHandle);
DllHandle := 0;
end;
end;
initialization
LoadLib;
finalization
UnloadLib;
end.
new unit1.pas
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Winapi.GDIPOBJ;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
unitLoadDLL;
procedure TForm1.Button1Click(Sender: TObject);
begin
showme();
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
closeme();
end;
end.
in unit1.pas i moved the Winapi.GDIPOBJ to "uses" just after interface directive, and it worked...
Thank you all for your answers! See you soon! very soon...
Does it help if you import sharemem on both sides?
You are not using packages, so both sides probably have an own instance all RTL state, as well as VMT tables (though that is only a problem with certain IS and AS cases). And the memory manager is RTL state :-)

Resources