I need to detect when a USB Device is removed or inserted from my program.
I have made some research and found this but don't know how to implement/use it.
could someone help me out probably with a function that would return true on USB Insert and false on Removal so that i can call a Timer to check that function every second?
Using Delphi XE7.
Thanks.
Create an instance of TComponentUSB. Assign two event handlers to OnUSBArrival and OnUSBRemove events. Execute appropriate code in event handlers. Note that polling with TTimer is not needed.
Code sketch:
type
TMyForm = class(TForm)
FormCreate(Sender: TObject);
...
private
CUSB: ComponentUSB;
procedure USBArrival(Sender: TObject);
...
end;
procedure TMyForm.FormCreate(Sender: TObject);
begin
CUSB := ComponentUSB.Create(Self);
CUSB.OnUSBArrival = USBArrival;
end;
procedure TMyForm.USBArrival(Sender: TObject);
begin
Caption := 'I''m here now!';
end;
Related
I am in the unpleasant situation. When one child changes its state, it calls its parent to respond to this change. In my case, the parent destroys all its children and recreates them. After that the program returns to the point, where the original caller is already de-referenced.
Although I see now, that it is a bad practice and I have to change whole philosophy, but for curiosity is it possible to stop execution on the end of routine?
This is only simple illustration:
TPerson = class(TPersistent)
private
FOnChange:TNotifyEvent;
FName:string;
published
property OnStatusChange:TNotifyEvent read FOnChange write FOnChange;
property Name:string read FName write FName;
end;
.... form declaration....
implementation
procedure TForm1.FormCreate(Sender: TObject);
begin
FPerson.OnStatusChange:=ProcessStatusChange;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FPerson.Free;
end;
procedure TForm1.ProcessStatusChange(Sender:TObject);
begin
btn1.free;
// and here I would like to call something like STOP
end;
procedure TForm1.btn1Click(Sender: TObject);
begin
if Assigned(FPerson.OnStatusChange) then FPerson.OnStatusChange(Self);
ShowMessage(btn1.Name); //btn1 does not exist anymore
end;
I am working with FireDac under Delphi 10.1 Berlin.
For displaying data to the user i use data aware controls like TDBEdit.
I use TFDQuery and TDataSource to link them with the controls.
This works but long sql queries that take some time to exectute will freeze the GUI.
I am wondering how to stop the gui from freezing while performing those long running queries.
I was thinking about background threads.
On the wiki i read that FireDac can work with multithreads:
http://docwiki.embarcadero.com/RADStudio/XE6/en/Multithreading_(FireDAC)
However in embarcadero community forums thread Jeff Overcash writes:
One thing I didn't see asked or Dmitry mention is you can not have
TDataSource or LiveBindings against your background threaded queries.
If you are background threading a query that displays the results you
should disconnect the LB or DataSource, open and fetch all the data
then re establish the connection.
Those two will be trying to move the cursor on you or querying the
buffer for display while the buffer is very volatile being moved
around in a different thread.
I am wondering if someone that also uses FireDac and displays the values on a form can help me out here.
The code sample below shows one way to retrive records from an MSSql Server
in a background thread using FireDAC. This omits a few details. For example, in practice, rather than the TQueryThreads Execute opening the query only once and then terminating, you would probably want the thread's Execute to contain a while loop in which it waits on a semaphore after the call to Synchronize and then close/re-open the query to update the main thread as often as you want.
type
TForm1 = class;
TQueryThread = class(TThread)
private
FConnection: TFDConnection;
FQuery: TFDQuery;
FForm: TForm1;
published
constructor Create(AForm : TForm1);
destructor Destroy; override;
procedure Execute; override;
procedure TransferData;
property Query : TFDQuery read FQuery;
property Connection : TFDConnection read FConnection;
property Form : TForm1 read FForm;
end;
TForm1 = class(TForm)
FDConnection1: TFDConnection;
FDQuery1: TFDQuery;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
public
QueryThread : TQueryThread;
end;
[...]
constructor TQueryThread.Create(AForm : TForm1);
begin
inherited Create(True);
FreeOnTerminate := True;
FForm := AForm;
FConnection := TFDConnection.Create(Nil);
FConnection.Params.Assign(Form.FDConnection1.Params);
FConnection.LoginPrompt := False;
FQuery := TFDQuery.Create(Nil);
FQuery.Connection := Connection;
FQuery.SQL.Text := Form.FDQuery1.SQL.Text;
end;
destructor TQueryThread.Destroy;
begin
FQuery.Free;
FConnection.Free;
inherited;
end;
procedure TQueryThread.Execute;
begin
Query.Open;
Synchronize(TransferData);
end;
procedure TQueryThread.TransferData;
begin
Form.FDQuery1.DisableControls;
Form.FDQuery1.Data := Query.Data;
Form.FDQuery1.EnableControls;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
QueryThread.Resume;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
QueryThread := TQueryThread.Create(Self);
end;
MJN's comment about bookmarks tells you how to preserve the current data row position in the gui.
Btw, although I've often done this with TClientDataSets, putting this answer together was the first time I'd tried it with FireDAC. In terms of configuring the components, all I did was to drag the components off the Palette, "wire them together" as you'd expect and then set the FDConnection's Params and the FDQuery's Sql.
I'm working on something which will require monitoring of many forms. From outside the form, and without putting any code inside the form, I need to somehow capture events from these forms, most likely in the form of windows messages. But how would you capture windows messages from outside the class it's related to?
My project has an object which wraps each form it is monitoring, and I presume this handling will go in this object. Essentially, when I create a form I want to monitor, I create a corresponding object which in turn gets added to a list of all created forms. Most importantly, when that form is closed, I have to know so I can remove this form's wrapper object from the list.
These events include:
Minimize
Maximize
Restore
Close
Focus in/out
What I DON'T want:
Any code inside any forms or form units for this handling
Inheriting the forms from any custom base form
Using the form's events such as OnClose because they will be used for other purposes
What I DO want:
Handling of windows messages for these events
Any tips on how to get windows messages from outside the class
Which windows messages I need to listen for
Question re-written with same information but different approach
You need to listen for particular windows messages being delivered to the form. The easiest way to do this is to assign the WindowProc property of the form. Remember to keep a hold of the previous value of WindowProc and call it from your replacement.
In your wrapper object declare a field like this:
FOriginalWindowProc: TWndMethod;
Then in the wrapper's constructor do this:
FOriginalWindowProc := Form.WindowProc;
Form.WindowProc := NewWindowProc;
Finally, implement the replacement window procedure:
procedure TFormWrapper.NewWindowProc(var Message: TMessage);
begin
//test for and respond to the messages of interest
FOriginalWindowProc(Message);
end;
Here's a more complete example of the solution that David Provided:
private
{ Private declarations }
SaveProc : TWndMethod;
procedure CommonWindowProc(var Message: TMessage);
...
procedure TForm1.Button1Click(Sender: TObject);
var
f : tForm2;
begin
f := tForm2.Create(nil);
SaveProc := f.WindowProc;
f.WindowProc := CommonWindowProc;
f.Show;
end;
procedure TForm1.CommonWindowProc(var Message: TMessage);
begin
case Message.Msg of
WM_SIZE : Memo1.Lines.Add('Resizing');
WM_CLOSE : Memo1.Lines.Add('Closing');
CM_MOUSEENTER : Memo1.Lines.Add('Mouse enter form');
CM_MOUSELEAVE : Memo1.Lines.Add('Mouse leaving form');
// all other messages will be available as needed
end;
SaveProc(Message); // Call the original handler for the other form
end;
A better solution than trying to work outside of the form would be to make every form descend from a common base form that implements the functionality. The form event handlers are exactly the right place to add this code but you'd write it all in the ancestor form. Any descendant form could still use the form events and as long as they always call inherited somewhere in the event handler the ancestor code would still execute.
Another option is create TApplicationEvents and assign a handler to OnMessage event. Once if it fired, use the FindControl function and Msg.hWnd to check if it is the tform type and do what ever you want without hookin
Using Windows Messages can really attain a fine granularity (Yes, its part of your requirements!) but in some user cases where relying just on the VCL Event Framework suffices, a similar solution can be suggested:
unit Host;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
THostForm = class(TForm)
Memo1: TMemo;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
FFormResize: TNotifyEvent;
FFormActivate: TNotifyEvent;
FFormDeactivate: TNotifyEvent;
FFormDestroy: TNotifyEvent;
procedure _FormResize(Sender: TObject);
procedure _FormActivate(Sender: TObject);
procedure _FormDeactivate(Sender: TObject);
procedure InternalEventHandlerInit(const AForm:TForm);
public
procedure Log(const Msg:string);
procedure Logln(const Msg:string);
end;
var
HostForm: THostForm;
implementation
{$R *.dfm}
procedure THostForm.Button1Click(Sender: TObject);
var
frm: TForm;
begin
frm := TForm.Create(nil);
frm.Name := 'EmbeddedForm';
frm.Caption := 'Embedded Form';
//
InternalEventHandlerInit(frm);
//
Logln('<'+frm.Caption+'> created.');
//
frm.Show;
end;
procedure THostForm.InternalEventHandlerInit(const AForm: TForm);
begin
FFormResize := AForm.OnResize;
AForm.OnResize := _FormResize;
//
FFormActivate := AForm.OnActivate;
AForm.OnActivate := _FormActivate;
//
FFormDeactivate := AForm.OnDeactivate;
AForm.OnDeactivate := _FormDeactivate;
end;
procedure THostForm.Log(const Msg: string);
begin
Memo1.Lines.Add(Msg);
end;
procedure THostForm.Logln(const Msg: string);
begin
Memo1.Lines.Add(Msg);
Memo1.Lines.Add('');
end;
procedure THostForm._FormActivate(Sender: TObject);
begin
Log('Before OnActivate <'+(Sender as TCustomForm).Caption+'>');
//
if Assigned(FFormActivate) then
FFormActivate(Sender) // <<<
else
Log('No OnActivate Event Handler attached in <'+(Sender as TCustomForm).Caption+'>');
//
Logln('After OnActivate <'+(Sender as TCustomForm).Caption+'>');
end;
procedure THostForm._FormDeactivate(Sender: TObject);
begin
Log('Before OnDeactivate <'+(Sender as TCustomForm).Caption+'>');
//
if Assigned(FFormDeactivate) then
FFormDeactivate(Sender)
else
Log('No OnDeActivate Event Handler attached in <'+(Sender as TCustomForm).Caption+'>');
//
Logln('After OnDeactivate <'+(Sender as TCustomForm).Caption+'>');
end;
procedure THostForm._FormResize(Sender: TObject);
begin
Log('Before OnResize <'+(Sender as TCustomForm).Caption+'>');
//
if Assigned(FFormResize) then
FFormResize(Sender)
else
Log('No OnResize Event Handler attached in <'+(Sender as TCustomForm).Caption+'>');
//
Logln('After OnResize <'+(Sender as TCustomForm).Caption+'>');
end;
end.
How do I create an OnMouseLeave event?
Another alternative to the Andreas solution, is use the CM_MOUSELEAVE VCL Message which is already defined in delphi 7.
check this sample using a interposer class for the TButton
type
TButton = class(StdCtrls.TButton)
private
FOnMouseLeave: TNotifyEvent;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
protected
property OnMouseLeave: TNotifyEvent read FOnMouseLeave write FOnMouseLeave;
end;
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
private
procedure ButtonMouseLeave(Sender: TObject);
public
end;
//handle the message and call the event handler
procedure TButton.CMMouseLeave(var Message: TMessage);
begin
if (Message.LParam = 0) and Assigned(FOnMouseLeave) then
FOnMouseLeave(Self);
end;
procedure TForm1.ButtonMouseLeave(Sender: TObject);
begin
//your code goes here
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
//assign the event
Button1.OnMouseLeave:=ButtonMouseLeave;
end;
You can tell Windows to post you a message, more specifically a WM_MOUSELEAVE message, when the mouse leaves the control. To do this, call the TrackMouseEvent function. In the TRACKMOUSEEVENT structure, specify the TME_LEAVE flag.
On request, some code:
When the control has been created, and the mouse is inside the client area of the control, tell Windows that you want to be notified about the mouse leaving the control:
procedure TMyControl.SetMouseEvent;
var
tme: TTrackMouseEvent;
begin
tme.cbSize := sizeof(tme);
tme.dwFlags := TME_LEAVE;
tme.hwndTrack := Self.Handle;
TrackMouseEvent(tme);
end;
Call this procedure when the control has been created and the mouse is inside the control. Now you just have to listen to the WM_MOUSELEAVE mesage. In your WndProc procedure (a protected member of the class), add a WM_MOUSELEAVE case.
procedure TMyControl.WndProc(var Message: TMessage);
begin
inherited;
case Message.Msg of
WM_MOUSELEAVE:
beep;
end;
end;
I think that Windows removes the notification request when a message has been created, so you have to rerequest the notification when you have recieved a message. You cannot call SetMouseEvent in the WndProc, because the mouse needs to be inside the client area of the control when you call TrackMouseEvent. I think you could place your SetMouseEvent inside the OnMouseMove of the control:
procedure TMyControl.WndProc(var Message: TMessage);
begin
inherited;
case Message.Msg of
WM_MOUSELEAVE:
beep;
WM_MOUSEMOVE:
SetMouseEvent;
end;
end;
I haven't tested the code above myself, because I use a newer version of Delphi, Delphi 2009, which does things like this behind the scenes (I think, because there is now a OnMouseLeave event in controls), and I think that will interfere.
suppose i write a button1click event handler which when called(ie when button1 clicked) keeps on adding 1 to variable counter:
var
i : counter;
begin
while 1 = 1 do
inc(i);
end;
now i want to add another button on my form and name it ''stop'' which will abruptly exit
button1click(after its called) or stop it from adding 1 to counter, how do i do that?
Hopefully this will provide some ideas as to how to adapt a timer based approach for your needs:
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Timer1: TTimer; // with Interval = 1 (for example) and Enabled = FALSE
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Timer1Execute(Sender: TObject);
private
fCounter: Integer;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Timer1.Enabled := TRUE;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Timer1.Enabled := FALSE;
end;
procedure TForm1.Timer1Execute(Sender: TObject);
begin
Inc(fCounter);
end;
If your needs are more sophisticated then a TThread based approach may be more appropriate. But whatever you do, do NOT resort to Application.ProcessMessages it will only cause you grief !
Put Application.ProcessMessages inside the while. This allows other controls to process and respond to the events.
Create a global variable "Stop".
In stop-button event handler, set Stop to true.
Inside your while, check for the value of Stop and break if it is true.
But, it is not very good practice to do stuff like this. All things you do in form should be reponses to some events, so if you want to do some periodical things, you should use a Timer or something like that.
You can put your while in a separate thread. Simple TThread will be enough, but there are also special threading libraries like AsyncCalls or OmniThreadLibrary.