I want use messagesin my program and i've a question: Can I use messages in a class procedure or Can I use messages in a procedure without class?
Here is my code:
const
WM_CUSTOM_TCP_CLIENT = WM_USER + 10;
type
TFeedbackEvent = class
public
class procedure feedback(var msg: TMessage); message WM_CUSTOM_TCP_CLIENT;
end;
The Delphi returns the following message:
[Error] unit.pas(33): Invalid message parameter list
Thank you very much.
There is a very nice article on the topic: Handling Messages in Delphi 6. This is a must read.
Handling or processing a message means that your application responds
in some manner to a Windows message. In a standard Windows
application, message handling is performed in each window procedure.
By internalizing the window procedure, however, Delphi makes it much
easier to handle individual messages; instead of having one procedure
that handles all messages, each message has its own procedure. Three
requirements must be met for a procedure to be a message-handling
procedure:
The procedure must be a method of an object.
The procedure must take one var parameter of a TMessage or other message-specific record type.
The procedure must use the message directive followed by the constant value of the message you want to process.
As you can read in the article, the procedure must be a method of an object, not a class. So you cannot just use message handlers in a class procedure.
A possible workaround to handle messages in a class instance (also in object instance or window-less applications), is to manually create window handle via AllocateHWND, and process messages yourself via a WndProc procedure.
There is a good example on this in delphi.about.com: Sending messages to non-windowed applications (Page 2):
The following sample is a version of the above example, modified to work with class method. (If using class method is not really required, use original example from the link above instead):
First, you need to declare a window handle field and a WndProc procedure:
TFeedbackEvent = class
private
FHandle: HWND;
protected
class procedure ClassWndProc(var msg: TMessage);
end;
procedure WndProc(var msg: TMessage);
Then, process the messages manually:
procedure WndProc(var msg: TMessage);
begin
TFeedbackEvent.ClassWndProc(msg);
end;
procedure TFeedbackEvent.ClassWndProc(var msg: TMessage);
begin
if msg.Msg = WM_CUSTOM_TCP_CLIENT then
// TODO: Handle your message
else
// Let default handler process other messages
msg.Result := DefWindowProc(FHandle, msg.Msg, msg.wParam, msg.lParam);
end;
Finally, at the end of the file, declare initialization and finalization section to create/destroy the handle:
initialization
FHandle := AllocateHWND(WndProc);
finalization
DeallocateHWnd(FHandle);
Of course, this is not the recommended way to do this (especially watch for problems with initialization/finalization), it was just an example to show that it is possible.
Unless you have some very strange requirement to use class method, its better to use regular class method and object constructor and destructor instead initialization and finalization sections (as shown in Sending messages to non-windowed applications (Page 2)).
Related
One day ago I had started to rewrite one of my old components and I decided to improve its readability.
My component is a typical TWinControl that has overridden WndProc to handle a lot of messages of my own. There are so many code for each message and it became a problem for me to read code.
So, looking for a solution to improve code inside WndProc, I have organized these large pieces of code in procedures that called each time when appropriate message has delivered in WndProc. That's how it looks now:
procedure TMyControl.WndProc(var Message: TMessage);
begin
case Message.Msg of
WM_WINDOWPOSCHANGED:
WMWINDOWPOSCHANGED(Message);
WM_DESTROY:
WMDESTROY(Message);
WM_STYLECHANGED:
WMSTYLECHANGED(Message);
// lots of the same procedures for Windows messages
// ...
MM_FOLDER_CHANGED:
MMFOLDERCHANGED(Message);
MM_DIRECTORY_CHANGED:
MMDIRECTORYCHANGED(Message);
// lots of the same procedures for my own messages
// ...
else
Inherited WndProc(Message);
end;
end;
Unfortunately Inherited word in these procedures doesn't work anymore!
Important note: in some of WM_XXX messages I didn't call Inherited to perform my own handling of such message, so code shown below will break down my efforts to implement some features.
procedure TMyControl.WndProc(var Message: TMessage);
begin
Inherited WndProc(Message);
case Message.Msg of
WM_WINDOWPOSCHANGED:
WMWINDOWPOSCHANGED(Message);
// further messages
// ...
end;
end;
I also want to avoid inserting Inherited after each message-ID as shown below, because it looks awful and I think there is exists more elegant way to override WndProc.
procedure TMyControl.WndProc(var Message: TMessage);
begin
case Message.Msg of
WM_WINDOWPOSCHANGED:
begin
Inherited WndProc(Message);
WMWINDOWPOSCHANGED(Message);
end;
// further messages
// ...
end;
end;
So my question is:
how to properly override WndProc to have an ability to use code grouped in procedures and to be able to call for original window procedure only for some messages?
As RM's answer stated, your message handling methods can call inherited WndProc(Message) instead of just inherited, and that will work fine.
However, by introducing methods with the same names as the messages they are processing, you are exposing knowledge of the specific messages you are processing. So you may find it easier to just use Message Methods instead of overriding WndProc, eg:
type
TMyControl = class(...)
private
procedure WMWindowPosChanged(var Message: TMessage); message WM_WINDOWPOSCHANGED;
procedure WMDestroy(var Message: TMessage); message WM_DESTROY;
procedure WMStyleChanged(var Message: TMessage); message WM_STYLECHANGED;
// and so on ...
end;
Your message methods can then call inherited (or not) as needed, eg:
procedure TMyControl.WMWindowPosChanged(var Message: TMessage);
begin
inherited;
//...
end;
Calling inherited WndProc from WMWINDOWPOSCHANGED will call the inherited one. So you can do it like this:
procedure WMWINDOWPOSCHANGED(var Message: TMessage)
begin
// call inherited WndProc if you need to
inherited WndProc(Message);
.. do you own processing
end;
I need to get all data from a TcxTreeList (a custom Devexpress control) from an external application. I know how to get data from standard controls in external applicatioins (i.e. Tree Views, List boxes, Memos, List Views and so on) sending the right windows messages, but I don't know how to do that for this kind of custom controls.
Reading the documentation from Devexpress I can see the following class hierarchy: TcxTreeList -> TcxCustomTreeList -> TcxExtEditingControl -> TcxEditingControl -> TcxControl -> TCustomControl, but unfortunately I don't know what kind of windows messages to send in order to get data.
For example: in order to get data for a standard Tree View I send messages with parameters like TVGN_CHILD, TVGN_NEXT, TVM_GETITEM and so on in the right sequence.
Can someone suggest me how to get data for the TcxTreeList control?
Of course I'm asking this because it's absolutely impossible to modify the source code of the external application (I don't have it) or to rewrite it.
Thank you in advance
I think you may be in for a bit of an uphill struggle with this. Try the following:
Download & Install the Devex VCL Trial
Create a new Delphi project, add a TcxTreelist to its main form, add two columns to the
tree and, using the tree's Items editor, add two top-level item node to it and a sub-item
to each of these.
Compile & run the project, then inspect its behavoiur using WinSpy++ of similar.
Note that until you click any of the tree nodes, the cxTreelist has no internal windows (I mean
windows from an OS pov).
While you edit one of the data nodes in the tree, a window of class TcxCustomInnerTextEdit is created.
TcxCustomInnerTextEdit is declared in the Devex VCL source in the unit cxTextEdit.Pas and is a descendant
of TCustomEdit. It has message handlers as follows (in addition to TCustomEdit's)
{ private }
procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
procedure EMReplaceSel(var Message: TMessage); message EM_REPLACESEL;
procedure EMSetSel(var Message: TMessage); message EM_SETSEL;
procedure WMChar(var Message: TWMChar); message WM_CHAR;
procedure WMClear(var Message: TMessage); message WM_CLEAR;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
procedure WMIMEChar(var Message: TMessage); message WM_IME_CHAR;
procedure WMIMEComposition(var Message: TMessage); message WM_IME_COMPOSITION;
procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;
procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
procedure WMSetFont(var Message: TWMSetFont); message WM_SETFONT;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMUndo(var Message: TWMSize); message WM_UNDO;
protected
procedure MouseEnter(AControl: TControl); dynamic;
procedure MouseLeave(AControl: TControl); dynamic;
As you say how to get data from standard Windows controls, maybe these handler declarations are sufficient for you to see whether you could do what you want.
If you can select the node you want by sending a message to the app, depending
on what you wanting to do, you may be able to do it using the messages for those handlers.
Whether you could do similar using UI-automation, I'm not sure. Generate an import unit
from UIAutomationCore.dll and experiment with what you can do with that, following the example
in How get current url address on mains browsers using UIAutomation?.
You'll notice that one of the enumeration constants in the import unit is
TreeScope_Children but I'd be v. surprised if you could use that to get at
the individual data nodes of the cxTreeList, but you never know.
Btw, TcxCustomInnerEdit implements a couple of interfaces
IcxCustomInnerEdit = interface(IcxContainerInnerControl)
['{468D21B5-48AA-4077-8ED5-4C6112D460B1}']
function CallDefWndProc(AMsg: UINT; WParam: WPARAM; LParam: LPARAM): LRESULT;
function CanProcessClipboardMessages: Boolean;
function GetEditValue: TcxEditValue;
function GetOnChange: TNotifyEvent;
function GetReadOnly: Boolean;
procedure LockBounds(ALock: Boolean);
procedure SafelySetFocus;
procedure SetEditValue(const Value: TcxEditValue);
procedure SetParent(Value: TWinControl);
procedure SetOnChange(Value: TNotifyEvent);
procedure SetReadOnly(Value: Boolean);
property EditValue: TcxEditValue read GetEditValue write SetEditValue;
property Parent: TWinControl write SetParent;
property ReadOnly: Boolean read GetReadOnly write SetReadOnly;
property OnChange: TNotifyEvent read GetOnChange write SetOnChange;
end;
{ IcxInnerEditHelper }
IcxInnerEditHelper = interface
['{35667555-6DC8-40D5-B705-B08D5697C621}']
function GetHelper: IcxCustomInnerEdit;
end;
and IcxContainerInnerControl is declared as
IcxContainerInnerControl = interface
['{1B111318-D9C9-4C35-9EFF-5D95793C0106}']
function GetControl: TWinControl;
function GetControlContainer: TcxContainer;
property Control: TWinControl read GetControl;
property ControlContainer: TcxContainer read GetControlContainer;
end;
but I have no idea whether you could invoke any of these interfaces from a separate Delphi app, even if they are of any use for what you're at.
I haven't studied the code of TcxTreeList as a whole, but I imagine that the image
of each data node is painted "virtually" by the treelist as a whole, except for the currently-focused node if any. So, I wouldn't expect to find
co-existing sub-controls for each of the data nodes and other innards of the treelist.
Depnding or your budget, purchasing the Devex VCL library, which comes with full source, may be a good investment.
Good luck!
In a VCL Forms program, I have a Form that implements a method for handling windows messages and updating some controls on the Form, something like:
procedure OnMsgTest (var Msg: TMessage); message WM_CUSTOMTEST;
I use PostMessage with a custom message to this Form, using a code like this:
h := FindWindow('TFrmTest', nil);
if IsWindow(h) then begin
PostMessage(h, WM_CUSTOMTEST, 0, 0);
end;
When the Form is instantiated several times, using the above code to send the message, only one Form instance updates the information on the screen. I would like all open and instantiated Forms to receive the message.
An important note: PostMessage can occur within the Form process itself, but also from another process. So, I believe a loop through the Forms would not work.
What would be the best approach to reach my goal?
You would have to enumerate all running top-level windows, posting the message to each matching window individually. You could use EnumWindows() or a FindWindow/Ex() loop for that, but a simpler solution is to use PostMessage(HWND_BROADCAST) to broadcast a message that is registered with RegisterWindowMessage(). Only windows that handle the registered message will react to it, other windows will simply ignore it. For example:
type
TMyForm = class(TForm)
protected
procedure WndProc(var Msg: TMessage); override;
end;
...
var
WM_CUSTOMTEST: UINT = 0;
procedure TMyForm.WndProc(var Msg: TMessage);
begin
if (Msg.Msg = WM_CUSTOMTEST) and (WM_CUSTOMTEST <> 0) then
begin
...
end else
inherited;
end;
initialization
WM_CUSTOMTEST := RegisterWindowMessage('SomeUniqueNameHere');
Then you can do this when needed:
if WM_CUSTOMTEST <> 0 then
PostMessage(HWND_BROADCAST, WM_CUSTOMTEST, 0, 0);
In one old project, which I was instructed to develop, there is a field of type TScrollBox.
FScroll : TScrollBox;
To be able to handle the events of navigation buttons, the class must contain a WM_GETDLGCODE message handler. So I created a new class:
TScrollBoxArrowBtn = class(TScrollBox)
protected
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
end;
Implementation
procedure TScrollBoxArrowBtn.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
Message.Result := DLGC_WANTARROWS;
end;
And replaced the TScrollBox type with TScrollBoxArrowBtn.
FScroll : TScrollBoxArrowBtn;
The component began to respond to pressing the arrow button. But the copy, delete, SelectAll methods stopped working. This happened because the previous developer added to the verification methods like this:
"VariableName".ClassType = TScrollBox
I replaced them for verification:
"VariableName" is TScrollBox
After this methods of editing began to work. But I'm not sure that such a test will not be applied elsewhere in the project. So I decided to leave
FScroll : TScrollBox;
And made TScrollBoxArrowBtn an helper class:
TScrollBoxArrowBtn = class helper for TScrollBox
protected
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
end;
Unfortunately this method does not work. Verifications like "VariableName".ClassType = TScrollBox began to work perfectly, but project stopped responding to events arrow button. What did I do wrong?
I'm convinced that my version of IDE supports helper methods.
I did not find the answer specifically about the message methods in the helpers classes, but I found a way to solve my problem. In addition, I learned about many other bad features of the helper class, which finally convinced me to abandon their use. So my answer is - do not use the class helpers. At this time this is a very unstable tool. Perhaps in the future it will be improved.
Now about my decision. As I feared, the problem of checking the type of the following kind:
"VariableName".ClassType = TScrollBox
again appeared when merging the previously created branches. So I decided to replace the TScrollBox window procedure. I added field in the TScrollBox-field container class and I added a new window procedure for TScrollBox-field in the container class:
TCADParamsGroupBlockBaseScheme = class (TCADGroupBlockParams)
.....................................................
protected
Old_FScroll_WindowProc : TWndMethod;
procedure New_FScroll_WindowProc(var Message: TMessage);
.....................................................
end;
implementation
procedure TCADParamsGroupBlockBaseScheme.New_FScroll_WindowProc(var Message:
TMessage);
begin
//Для обработки событий нажатий Key_Up/Down/Left/Right в DoKeyDown
if Message.Msg = WM_GETDLGCODE then
Message.Result := DLGC_WANTARROWS
else Old_FScroll_WindowProc(Message);
end;
And in the constructor of the container class, I saved the pointer to the old TScrollBox-field window procedure and assigned it a new window procedure:
constructor TCADParamsGroupBlockBaseScheme.Create(const AOwner: TWinControl);
begin
...........................................
FScroll := TScrollBox.Create(FHost.Owner);
Old_FScroll_WindowProc := FScroll.WindowProc;
FScroll.WindowProc := New_FScroll_WindowProc;
............................................
end;
After reading the articles "Simmering Unicode, bring DPL to a boil" and "Simmering Unicode, bring DPL to a boil (Part 2)" of "The Oracle at Delphi" (Allen Bauer), Oracle is all I understand :)
The article mentions Delphi Parallel Library (DPL), lock free data structures, mutual exclusion locks and condition variables (this Wikipedia article forwards to 'Monitor (synchronization)', and then introduces the new TMonitor record type for thread synchronization and describes some of its methods.
Are there introduction articles with examples which show when and how this Delphi record type can be used? There is some documentation online.
What is the main difference between TCriticalSection and TMonitor?
What can I do with the Pulse and PulseAllmethods?
Does it have a counterpart for example in C# or the Java language?
Is there any code in the RTL or the VCL which uses this type (so it could serve as an example)?
Update: the article Why Has the Size of TObject Doubled In Delphi 2009? explains that every object in Delphi now can be locked using a TMonitor record, at the price of four extra bytes per instance.
It looks like TMonitor is implemented similar to Intrinsic Locks in the Java language:
Every object has an intrinsic lock
associated with it. By convention, a
thread that needs exclusive and
consistent access to an object's
fields has to acquire the object's
intrinsic lock before accessing them,
and then release the intrinsic lock
when it's done with them.
Wait, Pulse and PulseAll in Delphi seem to be counterparts of wait(), notify() and notifyAll() in the Java programming language. Correct me if I am wrong :)
Update 2: Example code for a Producer/Consumer application using TMonitor.Wait and TMonitor.PulseAll, based on an article about guarded methods in the Java(tm) tutorials (comments are welcome):
This kind of application shares data
between two threads: the producer,
that creates the data, and the
consumer, that does something with it.
The two threads communicate using a
shared object. Coordination is
essential: the consumer thread must
not attempt to retrieve the data
before the producer thread has
delivered it, and the producer thread
must not attempt to deliver new data
if the consumer hasn't retrieved the
old data.
In this example, the data is a series of text messages, which are shared through an object of type Drop:
program TMonitorTest;
// based on example code at http://download.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
{$APPTYPE CONSOLE}
uses
SysUtils, Classes;
type
Drop = class(TObject)
private
// Message sent from producer to consumer.
Msg: string;
// True if consumer should wait for producer to send message, false
// if producer should wait for consumer to retrieve message.
Empty: Boolean;
public
constructor Create;
function Take: string;
procedure Put(AMessage: string);
end;
Producer = class(TThread)
private
FDrop: Drop;
public
constructor Create(ADrop: Drop);
procedure Execute; override;
end;
Consumer = class(TThread)
private
FDrop: Drop;
public
constructor Create(ADrop: Drop);
procedure Execute; override;
end;
{ Drop }
constructor Drop.Create;
begin
Empty := True;
end;
function Drop.Take: string;
begin
TMonitor.Enter(Self);
try
// Wait until message is available.
while Empty do
begin
TMonitor.Wait(Self, INFINITE);
end;
// Toggle status.
Empty := True;
// Notify producer that status has changed.
TMonitor.PulseAll(Self);
Result := Msg;
finally
TMonitor.Exit(Self);
end;
end;
procedure Drop.Put(AMessage: string);
begin
TMonitor.Enter(Self);
try
// Wait until message has been retrieved.
while not Empty do
begin
TMonitor.Wait(Self, INFINITE);
end;
// Toggle status.
Empty := False;
// Store message.
Msg := AMessage;
// Notify consumer that status has changed.
TMonitor.PulseAll(Self);
finally
TMonitor.Exit(Self);
end;
end;
{ Producer }
constructor Producer.Create(ADrop: Drop);
begin
FDrop := ADrop;
inherited Create(False);
end;
procedure Producer.Execute;
var
Msgs: array of string;
I: Integer;
begin
SetLength(Msgs, 4);
Msgs[0] := 'Mares eat oats';
Msgs[1] := 'Does eat oats';
Msgs[2] := 'Little lambs eat ivy';
Msgs[3] := 'A kid will eat ivy too';
for I := 0 to Length(Msgs) - 1 do
begin
FDrop.Put(Msgs[I]);
Sleep(Random(5000));
end;
FDrop.Put('DONE');
end;
{ Consumer }
constructor Consumer.Create(ADrop: Drop);
begin
FDrop := ADrop;
inherited Create(False);
end;
procedure Consumer.Execute;
var
Msg: string;
begin
repeat
Msg := FDrop.Take;
WriteLn('Received: ' + Msg);
Sleep(Random(5000));
until Msg = 'DONE';
end;
var
ADrop: Drop;
begin
Randomize;
ADrop := Drop.Create;
Producer.Create(ADrop);
Consumer.Create(ADrop);
ReadLn;
end.
Now this works as expected, however there is a detail which I could improve: instead of locking the whole Drop instance with TMonitor.Enter(Self);, I could choose a fine-grained locking approach, with a (private) "FLock" field, using it only in the Put and Take methods by TMonitor.Enter(FLock);.
If I compare the code with the Java version, I also notice that there is no InterruptedException in Delphi which can be used to cancel a call of Sleep.
Update 3: in May 2011, a blog entry about the OmniThreadLibrary presented a possible bug in the TMonitor implementation. It seems to be related to an entry in Quality Central. The comments mention a patch has been provided by a Delphi user, but it is not visible.
Update 4: A blog post in 2013 showed that while TMonitor is 'fair', its performance is worse than that of a critical section.
TMonitor combines the notion of a critical section (or a simple mutex) along with a condition variable. You can read about what a "monitor" is here:
http://en.wikipedia.org/wiki/Monitor_%28synchronization%29
Any place you would use a critical section, you can use a monitor. Instead of declaring a TCriticalSection, you can simple create a TObject instance and then use that:
TMonitor.Enter(FLock);
try
// protected code
finally
TMonitor.Exit(FLock);
end;
Where FLock is any object instance. Normally, I just create a TObject:
FLock := TObject.Create;