I created my own component (it like a button which can move) in Delphi, installed it. Then I create a new project and new from there and added few new my component elements. But only last one added is able to move! Others not. Why does it happen? How could I fix it?
Here's the component code:
unit ModifiedButton;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
var Timer: TTimer;
type
TSpeed = (Slow,Normal,Fast);
TModifiedButton = class(TButton)
private
{ Private declarations }
FCount:integer;
Velocity:integer;
FSpeed:TSpeed;
protected
{ Protected declarations }
procedure Click;override;
procedure Move(Vel:Integer);
procedure OnTimer(Sender: TObject);
public
{ Public declarations }
procedure ShowCount;
published
{ Published declarations }
property Count:integer read FCount write FCount;
property Speed: TSpeed read FSpeed write FSpeed;
constructor Create(aowner:Tcomponent); override;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyComponents', [TModifiedButton]);
end;
{ TModifiedButton }
procedure TModifiedButton.Click;
begin
inherited Click;
FCount:=FCount+1;
if (Speed = Slow) then
Velocity:=2;
if (Speed = Normal) then
Velocity:=10;
if (Speed = Fast) then
Velocity:= 20;
Timer.Enabled:=True;
end;
constructor TModifiedButton.Create(aowner: Tcomponent);
begin
inherited Create(aowner);
Timer:=TTimer.Create(self);
Timer.Enabled:=false;
Timer.OnTimer:=OnTimer;
Timer.Interval:=10;
end;
procedure TModifiedButton.Move(Vel: Integer);
begin
Left:=Left + Vel;
end;
procedure TModifiedButton.OnTimer(Sender: TObject);
begin
Move(Velocity);
end;
procedure TModifiedButton.ShowCount;
begin
ShowMessage('You cliked '+ caption+' for '+inttostr(FCount)+' times');
end;
end
.
Since the Timer is a global variable, each new button you create will overwrite the OnTimer event handler of the previous button. Solution, make the Timer a member of your TModifiedButton class:
unit ModifiedButton;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TSpeed = (Slow,Normal,Fast);
TModifiedButton = class(TButton)
private
{ Private declarations }
FCount:integer;
Velocity:integer;
FSpeed:TSpeed;
Timer: TTimer;
protected
{ Protected declarations }
procedure Click;override;
procedure Move(Vel:Integer);
procedure OnTimer(Sender: TObject);
public
{ Public declarations }
procedure ShowCount;
published
{ Published declarations }
property Count:integer read FCount write FCount;
property Speed: TSpeed read FSpeed write FSpeed;
constructor Create(aowner:Tcomponent); override;
destructor Destroy; override;
end;
procedure Register;
implementation
{ TModifiedButton }
procedure TModifiedButton.Click;
begin
inherited Click;
FCount:=FCount+1;
Case Speed of
Slow : Velocity:=2;
Normal : Velocity:=10;
Fast : Velocity:= 20;
end;
Timer.Enabled:=True;
end;
procedure TModifiedButton.Move(Vel: Integer);
begin
Left:=Left + Vel;
end;
procedure TModifiedButton.OnTimer(Sender: TObject);
begin
Move(Velocity);
end;
procedure TModifiedButton.ShowCount;
begin
ShowMessage(Format('You clicked %s for %d times', [Caption, FCount]));
end;
constructor TModifiedButton.Create(AOwner: Tcomponent);
begin
inherited Create(AOwner);
Timer := TTimer.Create(self);
Timer.Enabled:=false;
Timer.OnTimer:=OnTimer;
Timer.Interval:=10;
end;
destructor Destroy;
begin
Timer.Enabled := False;
Timer.Free;
end;
procedure Register;
begin
RegisterComponents('MyComponents', [TModifiedButton]);
end;
end.
Related
I have a problem with Delphi 6 and Indy's TIdIcmpClient component.
I get this message when compiling the following code, in the marked line (51):
FPing.OnReply := OnPingReply;
[Error] fire.pas(51): Incompatible types: 'TComponent' and 'TIdIcmpClient'
How should I fix it?
unit fire;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdRawBase, IdRawClient,
IdIcmpClient;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TMyThread = class(TThread)
private
FIndex: Integer;
FPing: TIdIcmpClient;
FIP: string;
protected
procedure Execute; override;
procedure OnPingReply(ASender: TIdIcmpClient; AReplyStatus: TReplyStatus);
public
constructor Create(AIndex: Integer);
destructor Destroy; override;
end;
constructor TMyThread.Create(AIndex: Integer);
begin
inherited Create(False);
FIndex := AIndex;
FIP := '192.168.1.' + IntToStr(FIndex + 1);
FPing := TIdIcmpClient.Create(nil);
FPing.Host:=FIP;
FPing.ReceiveTimeout:=1500;
FPing.OnReply := OnPingReply;
end;
destructor TMyThread.Destroy;
begin
FPing.Free;
inherited;
end;
//var// icmp:array[0..10] of TIdIcmpClient;
// ip:string;
procedure TMyThread.Execute; // aici e ce face thread-ul
var
i: Integer;
begin
FPing.Ping;
// ICMP.Ping('a',1000);
// Sleep(1300);
// form1.memo1.lines.add(IntToStr(findex)+' '+ICMP.ReplyStatus.fromipaddress);
for i := 1 to 1 do
begin
// 'findex' este indexul thread-ului din matrice
form1.memo1.lines.add(inttostr(findex)+' Thread running...');
application.ProcessMessages;
Sleep(1000);
end;
end;
procedure TMyThread.OnPingReply(ASender: TIdIcmpClient; AReplyStatus: TReplyStatus);
begin
if AReplyStatus.BytesReceived > 0 then
form1.memo1.Lines.add(FIP+ ' is reachable')
else
form1.memo1.Lines.add(FIP+ ' is not reachable: ');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MyThreads: array[0..10] of TMyThread;
// icmp:array[0..10] of TIdIcmpClient;
i: Integer;
begin
{ for i := 0 to 10 do //10 fire
begin
icmp[i]:=tidicmpclient.create(nil);
icmp[i].ReceiveTimeout:=1200;
ip:=Format('%s.%d', ['192.168.1', i]);
ICMP[i].Host :=ip;
end; }
for i := 0 to 10 do //10 fire
begin
MyThreads[i] := TMyThread.Create(i);
MyThreads[i].Resume;
application.ProcessMessages;
end;
// Readln;
for i := 0 to 10 do
begin
MyThreads[i].Free;
// icmp[i].Free;
end;
end;
end.
I expected it to be compilable, but I don't see the reason why it is not.
Your event handler is declared wrong. The ASender parameter needs to be TComponent rather than TIdIcmpClient, and the AReplyStatus parameter needs to be const:
procedure OnPingReply(ASender: TComponent; const AReplyStatus: TReplyStatus);
That being said, you don't need to use the OnReply event at all in this situation. TIdIcmpClient operates synchronously, so you can simply use the TIdIcmpClient.ReplyStatus property after the TIdIcmpClient.Ping() method exits:
procedure TMyThread.Execute; // aici e ce face thread-ul
var
...
begin
FPing.Ping;
if FPing.ReplyStatus.BytesReceived > 0 then
...
else
...
...
end;
Also, you must synchronize with the main UI thread when accessing UI controls in a worker thread. You can use TThread.Synchronize() method for that.
And, you do not need to call Application.ProcessMessages() in a worker thread. Doing so will have no effect on the main UI thread.
With all of that said, try something more like this:
unit fire;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdRawBase, IdRawClient,
IdIcmpClient;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure AddText(const AText: String);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TMyThread = class(TThread)
private
FIndex: Integer;
FPing: TIdIcmpClient;
FIP: string;
FText: String;
procedure AddTextToUI(const AText: String);
procedure DoSyncText;
protected
procedure Execute; override;
public
constructor Create(AIndex: Integer);
destructor Destroy; override;
end;
constructor TMyThread.Create(AIndex: Integer);
begin
inherited Create(False);
FIndex := AIndex;
FIP := '192.168.1.' + IntToStr(FIndex + 1);
FPing := TIdIcmpClient.Create(nil);
FPing.Host := FIP;
FPing.ReceiveTimeout := 1500;
end;
destructor TMyThread.Destroy;
begin
FPing.Free;
inherited;
end;
procedure TMyThread.AddTextToUI(const AText: String);
begin
FText := AText;
Synchronize(DoSyncText);
end;
procedure TMyThread.DoSyncText;
begin
Form1.AddText(FText);
end;
procedure TMyThread.Execute; // aici e ce face thread-ul
begin
AddTextToUI(IntToStr(FIndex) + ' Thread running...');
try
FPing.Ping;
except
AddTextToUI('Error pinging ' + FIP);
Exit;
end;
if FPing.ReplyStatus.BytesReceived > 0 then
AddTextToUI(FIP + ' is reachable')
else
AddTextToUI(FIP + ' is not reachable');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MyThreads: array[0..10] of TMyThread;
I: Integer;
begin
for I := Low(MyThreads) to High(MyThreads) do //10 fire
begin
MyThreads[I] := TMyThread.Create(I);
end;
for I := Low(MyThreads) to High(MyThreads) do
begin
MyThreads[i].WaitFor;
MyThreads[i].Free;
end;
end;
procedure TForm1.AddText(const AText: String);
begin
Memo1.Lines.Add(AText);
end;
end.
I have this code with which I can set the font size of the control hint, but I want to be able somehow to adjust it later at runtime. How can I do that ?
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TMyHintWindow = class(THintWindow)
constructor Create(AOwner: TComponent); override;
end;
TMyButton = class(TButton)
protected
procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
MyButton: TMyButton;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
MyButton:=TMyButton.Create(Form1);
MyButton.Parent:=Form1;
MyButton.Caption:='Test';
MyButton.Left:=100;
MyButton.Top:=100;
MyButton.ShowHint:=true;
end;
procedure TMyButton.CMHintShow(var Message: TCMHintShow);
begin
inherited;
Message.HintInfo.HintWindowClass:=TMyHintWindow;
Message.HintInfo.HintStr:='My custom hint';
end;
constructor TMyHintWindow.Create(AOwner: TComponent);
begin
inherited;
Canvas.Font.Size:=25;
end;
end.
Since there is only one hint window instance at the time, and that instance will be created after call to CMHintShow, you can use class variables to do additional hint customization. Class variable is class member that is shared among all instances of the class and can be accessed directly through class type or class instance.
type
TMyHintWindow = class(THintWindow)
protected
class constructor ClassCreate;
public
class var FontSize: integer;
constructor Create(AOwner: TComponent); override;
end;
class constructor TMyHintWindow.ClassCreate;
begin
FontSize := 25;
end;
constructor TMyHintWindow.Create(AOwner: TComponent);
begin
inherited;
Canvas.Font.Size := FontSize;
end;
and then you can change FontSize in CMHintShow method
procedure TMyButton.CMHintShow(var Message: TCMHintShow);
begin
inherited;
TMyHintWindow.FontSize := 12;
Message.HintInfo.HintWindowClass := TMyHintWindow;
Message.HintInfo.HintStr := 'My custom hint';
end;
Starting from indications given by TLama I finally solved this problem. The key was to set Canvas.Font.Size in TMyHintWindow.CalcHintRect.
Here is the code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TMyHintData = record
FontSize: Integer;
end;
TMyHintWindow = class(THintWindow)
public
function CalcHintRect(MaxWidth: Integer; const AHint: string; AData: TCustomData): TRect; override;
end;
TMyButton = class(TButton)
private
procedure CMHintShow(var AMessage: TCMHintShow); message CM_HINTSHOW;
public
FMyHintData: TMyHintData;
constructor Create(AOwner: TComponent); override;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
MyButton: TMyButton;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
TMyButton(Sender).FMyHintData.FontSize:=44;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
MyButton:=TMyButton.Create(Form1);
MyButton.Parent:=Form1;
MyButton.Caption:='Test';
MyButton.Left:=100;
MyButton.Top:=100;
MyButton.ShowHint:=true;
MyButton.OnClick:=Button1Click;
end;
function TMyHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: string; AData: TCustomData): TRect;
begin
Canvas.Font.Size:=TMyHintData(AData^).FontSize;
Result:=inherited;
end;
constructor TMyButton.Create(AOwner: TComponent);
begin
inherited;
FMyHintData.FontSize:=25;
end;
procedure TMyButton.CMHintShow(var AMessage: TCMHintShow);
begin
inherited;
AMessage.HintInfo.HintData:=#FMyHintData;
AMessage.HintInfo.HintWindowClass:=TMyHintWindow;
AMessage.HintInfo.HintStr:='My custom hint';
end;
end.
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnShowHint:=AppOnShowHint;
end;
procedure TForm1.AppOnShowHint(var HintStr: string; var CanShow: Boolean; var HintInfo: THintInfo);
begin
{Use HintInfo (type:THintInfo) to specify some property of hint-window}
{For example: set hint-window width to the width of longest word in the hint-text}
HintInfo.HintMaxWidth:=1;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
{Set HintFont at runtime}
Screen.HintFont.Size:=strtoint(Edit1.Text);
{It's necessary to recreate the Application.FHintWindow private variable, so:}
Application.ShowHint:=False;
Application.ShowHint:=True;
end;
Tutorials that I found about how to create delphi components were nice, but they only used one of existing components as object to inherit actions from. Something like this
unit CountBtn;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TCountBtn = class(TButton)
private
FCount: integer;
protected
procedure Click;override;
public
procedure ShowCount;
published
property Count:integer read FCount write FCount;
constructor Create(aowner:Tcomponent); override;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Mihan Components', [TCountBtn]);
end;
constructor TCountBtn.Create(aowner:Tcomponent);
begin
inherited create(Aowner);
end;
procedure Tcountbtn.Click;
begin
inherited click;
FCount:=FCount+1;
end;
procedure TCountBtn.ShowCount;
begin
Showmessage('On button '+ caption+' you clicked: '+inttostr(FCount)+' times');
end;
end.
But what should I do if I need component which use few elements? Lets say, I got Button and Edit field. And on button click there in edit field should appers text the same as on button. I start to make it like this, but seems like it's not gonna work as I want:
unit TestComp;
interface
uses
System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TUiCompU = class(TCustomControl)
private
{ Private declarations }
FButton: TButton;
FEdit: TEdit;
protected
{ Protected declarations }
procedure Paint; override;
//wrong!
procedure FButton.Click;override
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
published
{ Published declarations }
//wrong!
property ButtonText: String read FButton.Caption write FButton.Caption;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Ui', [TUiCompU]);
end;
{ TUiCompU }
constructor TUiCompU.Create(AOwner: TComponent);
begin
inherited;
Width := 200;
Height := 50;
FButton := TButton.Create(Self);
FButton.SetSubComponent(True);
FButton.Parent := Self;
FButton.Top := 8;
FButton.Left := 50;
FButton.Width := 35;
FButton.Name := 'Button';
FEdit := TEdit.Create(Self);
FEdit.SetSubComponent(True);
FEdit.Parent := Self;
FEdit.Top := 8;
FEdit.Left := 84;
FEdit.Width := 121;
FEdit.Name := 'Edit';
end;
procedure TUiCompU.Paint;
begin
Canvas.Rectangle(ClientRect);
end;
end.
How should I add here Click procedure, which is realte to click on the button? And is there are good tutorial about how to made good components using others? (I need to create something like slideshow component btw).
Thank you, and sorry for my english.
You can write methods for the subcomponent events, but it has one big weakness; if you publish those subcomponents, there is a risk that someone will steal you this binding by writing own method:
type
TUiCompU = class(TCustomControl)
private
FEdit: TEdit;
FButton: TButton;
procedure ButtonClick(Sender: TObject);
procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
public
constructor Create(AOwner: TComponent); override;
end;
implementation
constructor TUiCompU.Create(AOwner: TComponent);
begin
inherited;
FButton := TButton.Create(Self);
...
FButton.OnClick := ButtonClick;
FEdit := TEdit.Create(Self);
...
FEdit.OnKeyDown := EditKeyDown;
end;
procedure TUiCompU.ButtonClick(Sender: TObject);
begin
// do whatever you want here
end;
procedure TUiCompU.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
// do whatever you want here
end;
I have a code here:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
IInnerTest = interface (IInterface)
procedure DoSth;
end;
TRekScannerData = record
Source: Integer;
Device: IInnerTest;
end;
ITest = interface (IInterface)
procedure DoSth;
end;
ATest = class(TInterfacedObject, ITest)
private
FInner: Array of TRekScannerData;
public
procedure DoSth;
constructor Create();
Destructor Destroy();override;
end;
AInnerTest = class (TInterfacedObject, IInnerTest)
private
FMainInt: ITest;
public
constructor Create(MainInt: ITest);
procedure DoSth;
Destructor Destroy();override;
end;
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;
test: ITest;
implementation
{$R *.dfm}
{ ATest }
constructor ATest.Create;
begin
SetLength(FInner, 1);
FInner[0].Device := AInnerTest.Create(self);
//<----- Here is the reason. Passing main interface to the inner interface.
end;
destructor ATest.Destroy;
begin
beep;
inherited;
end;
procedure ATest.DoSth;
begin
//
end;
{ AInnerTest }
constructor AInnerTest.Create(MainInt: ITest);
begin
FMainInt := MainInt;
end;
destructor AInnerTest.Destroy;
begin
beep;
inherited;
end;
procedure AInnerTest.DoSth;
begin
//
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
test := ATest.Create;
test.DoSth;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
test := nil;
end;
end.
The problem is that Destroy is not called when test is assigned to nil;
I would like to release all the inner interfaces by one statement ...
Is it possible? or do I need to prior to nil destroy all inner structures by using another method?
EDIT
The class structure is as follows:
Var x = ITest(ATest class) has ->
Inner Interface: IInnerTest(AInnerTest class) which has reference to:
ITest(ATest class)
Nil'ing x doesn't release all structure ...
You have a circular reference. Your implementation of IInnerTest holds a reference to ITest. And your implementation of ITest holds a reference to IInnerTest. And this circular reference means that the interface reference count can never go to zero.
The normal solution to this issue to to use a weak reference. Some useful links:
"Weak reference": down to earth explanation needed
http://www.finalbuilder.com/Resources/Blogs/PostId/410/WeakRefence-in-Delphi-solving-circular-interfac.aspx
http://delphisorcery.blogspot.co.uk/2012/06/weak-interface-references.html
I Creating a Hydra Host Application and a Hydra Plugin. I put a Procedure for Handling a Windows Message in Plugin; but in this case we can't handle this windows message. for solving this problem we can handle It in Host App and then we must talk with pluging via passing an Interface.
In this case I want to find a direct way for handle windows messages in Hydra Plugin. Please help me for solving this problem.
Update 1 for this Question:
this is a simple code for testing:
Plugin Side:
unit VisualPlugin;
interface
uses
{ vcl: } Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls,
{ Hydra: } uHYVisualPlugin, uHYIntf;
type
TVisualPlugin1 = class(THYVisualPlugin)
private
procedure WMDEVICECHANGE(var Msg: TMessage); message WM_DEVICECHANGE;
end;
implementation
uses
{ Hydra: } uHYPluginFactories;
{$R *.dfm}
procedure Create_VisualPlugin1(out anInstance: IInterface);
begin
anInstance := TVisualPlugin1.Create(NIL);
end;
resourcestring
sDescription = '';
const
sRequiredPrivilege = '';
sUserData = '';
{ TVisualPlugin1 }
procedure TVisualPlugin1.WMDEVICECHANGE(var Msg: TMessage);
begin
// ===================================
// This Line Of Code Can't Be Run!!!!!!
ShowMessage('USB Changed');
// ===================================
end;
initialization
THYPluginFactory.Create(HInstance, 'VisualPlugin1', Create_VisualPlugin1,
TVisualPlugin1, 1, 0, sRequiredPrivilege, sDescription, sUserData);
end.
PluginController in Plugin Side:
unit hcPluginController;
interface
uses
{vcl:} SysUtils, Classes,
{Hydra:} uHYModuleController, uHYIntf, uHYCrossPlatformInterfaces;
type
TPluginController = class(THYModuleController)
private
public
end;
var
PluginController : TPluginController;
implementation
uses
{Hydra:} uHYRes;
{$R *.dfm}
procedure HYGetCrossPlatformModule(out result: IHYCrossPlatformModule); stdcall;
begin
result := PluginController as IHYCrossPlatformModule;
end;
function HYGetModuleController : THYModuleController;
begin
result := PluginController;
end;
exports
HYGetCrossPlatformModule,
HYGetModuleController name name_HYGetModuleController;
resourcestring
sDescription = '';
const
sRequiredPrivilege = '';
initialization
PluginController := TPluginController.Create('Plugin.Library', 1, 0, sRequiredPrivilege, sDescription);
finalization
FreeAndNil(PluginController);
end.
Host Application Side:
unit fMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, uHYModuleManager, uHYIntf, ExtCtrls, StdCtrls;
type
TMainForm = class(TForm)
HYModuleManager1: THYModuleManager;
Panel1: TPanel;
btnLoadPlugin: TButton;
procedure btnLoadPluginClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
var
AppDir: string;
fPlugin: IHYVisualPlugin;
const
PluginDll = 'Plugin.dll';
PluginName = 'VisualPlugin1';
procedure TMainForm.btnLoadPluginClick(Sender: TObject);
begin
if HYModuleManager1.FindModule(AppDir + PluginDll) = nil then
HYModuleManager1.LoadModule(AppDir + PluginDll);
HYModuleManager1.CreateVisualPlugin(PluginName, fPlugin, Panel1);
end;
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
HYModuleManager1.ReleaseInstance(fPlugin);
HYModuleManager1.UnloadModules;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
AppDir := ExtractFilePath(Application.ExeName);
end;
end.
Not sure about the real cause of the problem, but you can use RegisterDeviceNotification function to achieve same result:
type
DEV_BROADCAST_DEVINTERFACE = record
dbcc_size: DWORD;
dbcc_devicetype: DWORD;
dbcc_reserved: DWORD;
dbcc_classguid: TGUID;
dbcc_name: short;
end;
const
DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = $4;
DBT_DEVTYP_DEVICEINTERFACE = $5;
function RegisterNotification(Handle: THandle): HDEVNOTIFY;
var
Filter: DEV_BROADCAST_DEVINTERFACE;
begin
ZeroMemory(#Filter, SizeOf(DEV_BROADCAST_DEVINTERFACE));
Filter.dbcc_size := SizeOf(DEV_BROADCAST_DEVINTERFACE);
Filter.dbcc_devicetype := DBT_DEVTYP_DEVICEINTERFACE;
Filter.dbcc_reserved := 0;
Filter.dbcc_name := 0;
Result := RegisterDeviceNotification(Handle, #Filter, DEVICE_NOTIFY_WINDOW_HANDLE or DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
end;
Now inside plugin you need something like this:
TVisualPlugin = class(THYVisualPlugin)
protected
NofitifyHandle: HDEVNOTIFY;
procedure WMDEVICECHANGE(var Msg: TMessage); message WM_DEVICECHANGE;
procedure CreateWnd; override;
procedure DestroyWindowHandle; override;
end;
procedure TVisualPlugin.CreateWnd;
begin
inherited;
if HandleAllocated then
NofitifyHandle := RegisterNotification(Self.Handle);
end;
procedure TVisualPlugin.DestroyWindowHandle;
begin
if Assigned(NofitifyHandle) then begin
UnregisterDeviceNotification(NofitifyHandle);
NofitifyHandle := nil;
end;
inherited;
end;
procedure TVisualPlugin.WMDEVICECHANGE(var Msg: TMessage);
begin
ShowMessage('USB Changed');
end;