Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

(Ebook - Pdf) Kick Ass Delphi Programming

.pdf
Скачиваний:
284
Добавлен:
17.08.2013
Размер:
5.02 Mб
Скачать

if 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.

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

Go!

Keyword

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin

ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Go!

-----------

For the most part, this component was a simple interface between the form that would own the component and routines in the DLL. DLLValid is a boolean, set to indicate the result of attempting to load the DLL. DLLValid is checked by methods that call DLL routines. The DLL itself is not loaded if Delphi is in the design state.

Broadcast functions were implemented using the corresponding DLL routines, returning a value of minus one if they failed. UpdateReceiverList uses the DLL’s FirstReceiverAssigned and NextReceiverAssigned functions to prepare a fresh snapshot of the state of the registered receiver list. I decided to give the MsgSender’s owner the sole responsibility for calling

UpdateReceiverList.

For a finishing touch, I used Delphi’s Image Editor to create a resource file containing a bitmap for the face of the component. Although the process was easy, it had to be done exactly right or it wouldn’t work. So I’ll document the process here.

After opening the Image Editor, I selected the New option from the File menu. From the choices, I selected “Component Resource File (.dcr)”. I then selected the New option from the Resource menu, and selected “Bitmap”. I specified a bitmap size of 24 by 24 pixels, and VGA (16 color) resolution.

Before creating the artwork itself, I saved the resource file with the still-blank bitmap. This is the most critical part of the process. First, I clicked on the default name given to the bitmap resource (“Bitmap1”) and changed the name to “TMSGSENDER”. Although the tradition is to use all caps, it isn’t necessary. But the name must exactly match the type identifier given to the component. Next, I saved the file, with the name “MsgSendr.dcr”. Once again, this is critical: The file name must exactly match the .dcu file (which is the same as the .pas file) that contains the code for the component. Figure 15.2 shows the state of the Image Editor once I had saved the file. From that point, I was able to create my beautiful piece of artwork and save it.

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.

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

Go!

Keyword

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin

ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Go!

-----------

The final version of TMsgReceiver’s code is shown in Listing 15.6.

Listing 15.6 Code for the MsgReceiver component

{———————————————————————————————————————————————————}

{

Message Broadcasting in Delphi 2

}

{

MSGRCVR.PAS : MsgReceiver Component

}

{

By Ace Breakpoint, N.T.P.

}

{

Assisted by Don Taylor

}

{

 

}

{ This component effectively subclasses the form

}

{ it is placed on, replacing the form’s WndProc

}

{ and registering a special system-wide message

}

{ with Win95. Only one of these components is

}

{ allowed per form.

}

{

 

}

{ Written for *Kick-Ass Delphi Programming*

}

{ Copyright (c) 1996 The Coriolis Group, Inc.

}

{

Last Updated 5/2/96

}

{———————————————————————————————————————————————————}

unit MsgRcvr;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type

EOwnerNotWinControl = class(Exception); EMultipleMsgReceivers = class(Exception);

TBcastDLLValid = function : Boolean; TRegisterReceiver = function(Hnd : THandle;

CName : ShortString) : Integer;

TUnregisterReceiver = function(Hnd : THandle) : Integer;

TMsgReceiverEvent = function(wParam : Word; lParam : Longint) : Longint of object;

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.

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

Go!

Keyword

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin

ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Go!

-----------

Subclassing the Owner Window

All Delphi objects (that is, those based on TObject and hence part of the VCL) have inherent messaging capability. Figure 15.3(a) shows the event processing chain for a TObject, with its built-in Dispatch and

DefaultHandler routines.

The job of the Dispatch routine is to examine any event reaching it, first checking to see if the event’s message ID matches those of any special event handlers. These special handlers are the ones added to the TObject descendant by the programmer, and are of the form:

procedure HandleMyEvent (var Msg : TMessage); message MyEven tMessageID;

FIGURE 15.3 The event handling process for TObject and TWinControl types.

If a match is found, the special handler corresponding to the event’s message ID is called. Otherwise, the built-in DefaultHandler routine gets called. Unless modified, the standard DefaultHandler does nothing.

Standard event processing for a TWinControl descendant is slightly more sophisticated, as can be seen in Figure 15.3(b). This time there are two more methods involved, MainWndProc and WndProc. MainWndProc is the main window procedure for each type of component in an application. When a message is received by any TWinControl descendant, it is first checked by

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