(Ebook - Pdf) Kick Ass Delphi Programming
.pdfif DLLValid
then Result := NumMsgReceivers else Result := -1;
end;
procedure TMsgSender.UpdateReceiverList; var
RcvrNum : Integer; begin ReceiverList.Clear; if DLLValid
then begin
RcvrNum := FirstReceiverAssigned; if RcvrNum > 0
then begin ReceiverList.Add(IntToStr(RcvrNum)); repeat
RcvrNum := NextReceiverAssigned(RcvrNum);
if RcvrNum > 0 then ReceiverList.Add(IntToStr(RcvrNum)); until RcvrNum < 0;
end;
end;
end;
procedure Register; begin
RegisterComponents('Ace''s Stuff', [TMsgSender]); end;
end.
Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.
FIGURE 15.2 The Delphi Image Editor and the blank bitmap.
Creating the Receiver Component
The pizza now entirely consumed, the natives once again became restless. I was about to suggest an intermission when I looked at my watch in horror: It still read 1:30! Absolutely no time had passed!
I made a mental note to get the watch to the local jeweler. When I glanced up, I saw that one of the residents had his hand up. Finally, I thought. Decorum is about to break out.
“Excuse me,” the man said.
I nodded for him to proceed with his question.
“Who are you? And what are you doing here?” he asked. He looked around at the other programmers and continued. “And where is Arnold? Isn’t Arnold supposed to be here?”
A titter drifted through the audience, and some jerk in the third row holding a bucket of extra greasy chicken began to guffaw. Totally confused, I looked to Dinah for a clue.
“Who’s Arnold?” I asked her.
“One of the guests,” she replied. “An inveterate troublemaker. He’s been put in Detention for violating the Internet curfew,” she said.
I pointed with my thumb to the man who had raised his hand. “And what about this guy?” I asked, trying hard not to move my mouth.
She didn’t make a sound, but I could read her lips. “That’s Bernie,” she said. “Error 1: Out of memory.”
Whoever said that truth was stranger than fiction certainly hadn’t experienced this place. I sighed and began the task of creating the receiver component.
TMsgReceiver turned out to be more of a challenge than TMsgSender. I should have expected that, since I knew going in I would have to subclass the window of the component’s owner.
The MsgReceiver component would have to perform only four basic tasks:
1.Register itself with the DLL when it is created, if the application is executing;
2.Register the unique message string with Windows to obtain the unique message ID;
3.Unregister itself with the DLL when the component is destroyed; and
4. Call a specified handler procedure when the component receives the unique message ID.
Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.
TMsgReceiver = class(TComponent) private
FIDString : ShortString; FMessageID : Word; FReceiverNum : Integer; LibraryHandle : THandle; DLLValid : Boolean; OriginalWndProc : TFarProc; NewWndProc : TFarProc; WndProcHooked : Boolean; OwnerHandle : THandle; BcastDLLValid : TBcastDLLValid;
RegisterReceiver : TRegisterReceiver; UnregisterReceiver : TUnregisterReceiver; procedure SetIDStr(NewID : ShortString); procedure RegisterIDStr;
protected
FOnIDMessage : TMsgReceiverEvent;
procedure WndProc(var Msg : TMessage); virtual; procedure DefaultHandler(var Msg); override;
procedure HandleWMDestroy(var Msg : TWMDestroy); message WM_Destroy;
public
constructor Create(AOwner : TComponent); override; destructor Destroy; override;
published
property IDString : ShortString read FIDString write SetIDStr; property ReceiverNum : Integer read FReceiverNum;
property OnIDMessage : TMsgReceiverEvent read FOnIDMessage write FOnIDMessage;
end;
procedure Register;
implementation
constructor TMsgReceiver.Create(AOwner : TComponent); var
i : Integer; begin
DLLValid := False;
FReceiverNum := -1;
if not (AOwner is TWinControl) then raise
EOwnerNotWinControl.Create('Owner must be TWinControl or descendant');
for i := 0 to AOwner.ComponentCount - 1 do if AOwner.Components[i] is TMsgReceiver then raise
EMultipleMsgReceivers.Create('Only one TMsgReceiver allowed per form');
inherited Create(AOwner);
OwnerHandle := (AOwner as TWinControl).Handle; NewWndProc := MakeObjectInstance(WndProc);
FMessageID := 0;
if not (csDesigning in ComponentState) then begin
OriginalWndProc :=
TFarProc(GetWindowLong((AOwner as TWinControl).Handle, gwl_WndProc));
SetWindowLong((AOwner as TWinControl).Handle, gwl_WndProc, Longint(NewWndProc));
WndProcHooked := True;
LibraryHandle := LoadLibrary('BCASTDLL.DLL'); if LibraryHandle > HINSTANCE_ERROR
then begin @RegisterReceiver :=
GetProcAddress(LibraryHandle, 'RegisterReceiver'); @UnregisterReceiver :=
GetProcAddress(LibraryHandle, 'UnregisterReceiver'); @BcastDLLValid :=
GetProcAddress(LibraryHandle, 'BcastDLLValid'); DLLValid := BcastDLLValid;
RegisterIDStr; end
else MessageDlg('Could not load DLL "BCASTDLL.DLL"', mtError, [mbOK], 0);
end;
if DLLValid then FReceiverNum := RegisterReceiver(OwnerHandle, AOwner.ClassName);
end;
destructor TMsgReceiver.Destroy; begin
if WndProcHooked
then SetWindowLong((Owner as TWinControl).Handle, gwl_WndProc, Longint(OriginalWndProc));
FreeObjectInstance(NewWndProc);
if DLLValid then UnregisterReceiver(OwnerHandle);
if not (csDesigning in ComponentState)
then if LibraryHandle > HINSTANCE_ERROR then FreeLibrary(LibraryHandle);
Inherited Destroy; end;
procedure TMsgReceiver.HandleWMDestroy(var Msg : TWMDestroy); begin
SetWindowLong((Owner as TWinControl).Handle, gwl_WndProc, Longint(OriginalWndProc));
WndProcHooked := False; end;
procedure TMsgReceiver.WndProc(var Msg : TMessage); begin
if Msg.Msg = FMessageID then begin
if Assigned(FOnIDMessage)
then Msg.Result := FOnIDMessage(Msg.wParam, Msg.lParam); end
else Dispatch(Msg); end;
procedure TMsgReceiver.DefaultHandler(var Msg); begin
with TMessage(Msg) do
Result := CallWindowProc(OriginalWndProc, (Owner as TWinControl).Handle,
Msg, wParam, lParam);
end;
procedure TMsgReceiver.SetIDStr(NewID : ShortString); begin
if NewID <> FIDString then begin
FIDString := NewID; RegisterIDStr; end;
end;
procedure TMsgReceiver.RegisterIDStr; var
IDStr : Array [0..255] of Char; begin
if not (csDesigning in ComponentState) and (Length(FIDString) > 0) then begin
StrPCopy(IDStr, FIDString);
FMessageID := RegisterWindowMessage(IDStr); end
else FMessageID := 0; end;
procedure Register; begin
RegisterComponents('Ace''s Stuff', [TMsgReceiver]); end;
end.
Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.
MainWndProc to see if the message relates to an exception. If not, the message is passed to the virtual method WndProc.
Classically, a window’s WndProc routine is sort of “Message Central.” It is within this routine that certain messages can be trapped or handled in a special way. The WndProc method for a TWinControl descendant, for example, passes keyboard events to Dispatch only if the component isn’t being dragged.
Under normal circumstances, Delphi provides a great deal of flexibility for handling special events. The ability to create special handlers for any TObject descendant as described above is a simple and powerful technique, as long as you know the message IDs of the events you want to handle at compile time.
But what we want here cannot be done that simply, for two reasons. First, we won’t know the message ID we’re looking for until the program is executing and has called RegisterWindowMessage. And second, the component must receive the event first, before its owner form sees it. In other words, we must break into the message chain of the owner, handle anything corresponding to the special message ID, and then pass everything else back to the owner to process in the normal manner.
Figure 15.4 illustrates the process. Here we have broken the chain in the form, so MainWndProc passes events to a new WndProc method written for the MsgReceiver component. The MsgReceiver’s WndProc is aware of the special message ID and the procedure assigned to handle that ID.
FIGURE 15.4 Subclassing the TForm’s window with TMsgReceiver.
If the ID of the event matches the special message ID, the assigned OnIDMessage handler gets called. Otherwise, MsgReceiver’s Dispatch method is called. Since there are no special handlers defined for MsgReceiver, the event is passed on to DefaultHandler. DefaultHandler has been modified to simply send the event back to the form’s original WndProc, where processing resumes normally. That means any special event handlers written for the form will execute as if nothing had been changed.
This process of breaking in to a window’s message chain and inserting an additional WndProc is called subclassing. It’s a standard technique used all the time in Windows programming. There is just less need for it when programming with Delphi.
In this example, the subclassing is performed dynamically in the MsgReceiver’s constructor. The call to MakeObjectInstance creates NewWndProc, a pointer to a procedure of the form
procedure AWndProc(var Msg : TMessage),
which is a prototype for a method used to handle messages sent to window procedures. If the application is executing (i.e., not in design mode), three