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

(Ebook - Pdf) Kick Ass Delphi Programming

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

I stepped up to the microphone and tried to get a grip on myself. “What we’ll be covering today won’t quite be ‘finer points’ of Win95 programming. Instead you’ll hear some really basic information I’m sure you’ll all want to know—”

I felt a dull pain between my ribs, and looked down to see an old coot who had gotten up from his seat in the front row. “Listen, Sonny,” he said, poking me in the chest with the tip of his telescoping presentation pointer. “All I want to know is how you would have two different Win95 applications communicate between themselves, using standard Windows messaging.”

Specifying the Problem

“Yeah!” someone else shouted. Then, one by one, residents began to voice their requirements for a problem specification.

“What if you’ve got multiple instances of sender and receiver apps—how are you going to satisfy them all?” yelled a shriveled little man in the fourth row. “And how are you going to make sure any message you’re broadcasting throughout the system won’t trigger an unwanted side effect on some other application that happens to use the same message ID?”

“How about the ability to selectively send a message to a single form, all forms, or certain classes of forms with reception capability?” added the man in the first row with a bag over his head. He looked somehow familiar....

“Componentize it, componentize it!” shouted another man. One by one, every person in the audience began to pick up the chant. Pandemonium reigned for several minutes. I tried to get my bearings and come up with some sort of approach.

As luck would have it, several staff members came in, rolling carts loaded with takeout pepperoni pizza. Within two or three minutes, the entire room had quieted down once again, as the residents munched on the tasty fare. I was able to use those precious moments to create the diagram shown in Figure 15.1.

FIGURE 15.1 Relationship of entities comprising Sender and Receiver components.

“Ladies and gentlemen,” I began, as I projected my bitmap on the screen. “Here is one possible approach to the problem you’ve described. We could create two separate components, a sender component and a receiver component. Each of these components would share some common routines contained in a Dynamic Link Library (DLL). On creation, each receiver component would register itself with the DLL, which would maintain in shared memory an array of records. Each record would contain the handle values and class names of all active receivers, along with a boolean that indicates whether

or not that record was occupied. A receiver would unregister itself with the DLL as part of the receiver’s destructor routine.”

I swept my eyes across the crowd, looking for major objections. Seeing none, I continued. “Sender components can have access to the handles of the receivers by querying the DLL. By calling routines included in the DLL, a sender can send a message selectively to any or all of the handles it gets from the query.”

“Wait a second,” came a voice from the back of the room. “You skipped over a very important point. You’re talking about sending messages to a form in another process. How are you going to make sure the message IDs you’re going to broadcast aren’t used by some other process?”

“By being different,” I replied. “Windows gives us an API call named RegisterWindowMessage. This function accepts a string and returns a message ID that is guaranteed to be unique throughout the system. If a second application tries to register the same string, it is given the same unique message ID. This is how the sender and receiver components can share a unique message—by simply sharing a string that is made a property of each component.”

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.

Go!

Keyword

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

Kick Ass Delphi Programming

(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!

-----------

“There’s a serious flaw in your logic, Kid,” said a totally bald-headed codger sitting in front of me. “You may have a unique message, all right. But you won’t be able to create a handler for it, because you won’t know the message ID until runtime.”

I thought for a moment. How was I going to handle this one? Then it came to me.

“A radio wave,” I said.

“What? Are you crazy?” shouted some curmudgeon in the third row.

“No, really. We can send out a message the way a transmitter sends a modulated signal to a receiver. The transmitter impresses a signal—information—onto a carrier wave of a designated frequency. Because the receiver is tuned to that same designated frequency and is constantly monitoring the incoming signal, it is able to pick off the information from the carrier wave, process it, and extract the information.”

Dead silence. I looked out over the crowd. But all I could see was 249 eyes staring back at me.

“Thanks to a call to RegisterWindowMessage,” I continued, “both the sender and receiver of our intended message will wind up with the same unique message ID—call it FMessageID. That’s like the frequency of the carrier wave. The real message will be sent as a package inside the message with the unique ID—sort of a private message. The sender will transmit a message to the receiver with the standard SendMessage function, using the receiver form’s handle and the unique “carrier” message ID. But the private message ID and any additional message information will be packed into wParam and lParam. It will look something like this.” I typed in the function shown in Listing 15.1.

Listing 15.1 Prototype of a message transmission routine

function TMsgSender.DoBroadcast(Handle

: Integer;

wParam

: Word;

lParam

: Longint) : Longint;

begin

Result := BroadcastIt(FMessageID, Handle, wParam, lParam); end;

They looked skeptical, but they were still hanging in there. “On the receiver side, we declare as a property a generic event that can be called whenever the form containing the receiver detects the unique message ID. If we call the event FOnIDMessage, the routine might look something like this,” I said, punching in the procedure shown in Listing 15.2.

Listing 15.2 Prototype of a message reception routine

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;

“Hold on, Wizard Boy,” came a voice from somewhere off to the left. “Now you’ve overstepped your bounds. You’re presuming the receiver component has somehow taken over the message processing for the form’s window. But you haven’t subclassed the window.”

“It will be part of the component’s design for it to automatically subclass the form on which it resides,” I explained. “But enough of this talk. Let’s write some code so we can explore this whole concept.”

I looked around the room. For the first time, they had all sat back in their chairs. I had their undivided attention. Now I just had to do the impossible: Make them happy.

Designing the DLL

By far, the DLL would be the biggest challenge. It was also the centerpiece of the whole process, so it seemed like a logical place to start. With the residents shouting out their own ideas for what needed to be included in the DLL, we hammered out a rough version. I couldn’t believe how quickly we came up with the final version of the code, shown in Listing 15.3.

Listing 15.3 DLL for sender/receiver component support

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

{

Message Broadcasting in Delphi 2

}

{

BCASTDLL.DLL : Library File

}

{

By Ace Breakpoint, N.T.P.

}

{

Assisted by Don Taylor

}

{

 

}

{ DLL that provides the underlying function of

}

{ managing a list of registered components in

}

{ shared memory.

}

{

 

}

{ Written for *Kick-Ass Delphi Programming*

}

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

}

{

Last Updated 5/2/96

}

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

library BcastDll;

uses SysUtils, Classes, Windows;

type

PMsgReceiverRec = ^MsgReceiverRec; MsgReceiverRec =

record

Assigned : Boolean; WndHandle : HWND; WndClassName : ShortString; end; { record }

const

MaxReceivers = 100;

ArraySize = MaxReceivers * SizeOf(MsgReceiverRec); MemFileName = 'msg_reg_array';

SemaphoreName = 'msg_bcast_semaphore';

var

 

ArrayHnd

: HWND;

ArrayBasePtr : PMsgReceiverRec;

SemaphoreHnd

: HWND;

DLLValid

: Boolean;

SaveExit

: Pointer;

LastErr

: Longint;

{ BcastDLLValid returns a Boolean that lets the caller know that the shared memory and semaphores are operational, so all of the other exported routines are valid. }

function BcastDLLValid : Boolean; begin

Result := DLLValid; end;

{RegisterReceiver adds the specified handle and class name to the array of receivers in shared memory. It returns the

index (plus 1) of the new entry, or -1 if it couldn't be found. }

function RegisterReceiver(Hnd : THandle; CName : ShortString) : Integer; var

Idx : Integer; Found : Boolean;

RecPtr : PMsgReceiverRec; begin

Idx := 0; Result := -1;

RecPtr := ArrayBasePtr; Found := False;

WaitForSingleObject(SemaphoreHnd, INFINITE);

{ Find an unused slot } repeat

Found := RecPtr^.Assigned = False; if not Found

then begin Inc(Idx); Inc(RecPtr);

end;

until Found or (Idx >= MaxReceivers);

if Found

then begin { Put the record in the slot } FillChar(RecPtr^, 0, SizeOf(MsgReceiverRec)); with RecPtr^ do

begin

Assigned := True;

WndHandle := Hnd; WndClassName := CName;

end; { with } Result := Idx + 1;

end; ReleaseSemaphore(SemaphoreHnd, 1, nil);

end;

{ UnregisterReceiver removes the record with the specified handle from the array. It returns the former index (plus 1) of the specified receiver, or -1 if it couldn't be found. }

function UnregisterReceiver(Hnd : THandle) : Integer; var

Idx : Integer; Found : Boolean;

RecPtr : PMsgReceiverRec; begin

Idx := 0; Result := -1;

RecPtr := ArrayBasePtr; Found := False;

WaitForSingleObject(SemaphoreHnd, INFINITE);

{ Locate the record } repeat

Found := RecPtr^.Assigned and (RecPtr^.WndHandle = Hnd); if not Found

then begin Inc(Idx); Inc(RecPtr);

end;

until Found or (Idx >= MaxReceivers);

if Found then begin

RecPtr^.Assigned := False; Result := Idx + 1;

end;

ReleaseSemaphore(SemaphoreHnd, 1, nil); end;

{BroadcastToClass uses SendMessage to broadcast the specified message to all registered components with the specified class. Returns as a result the value from the first message that returns a non-zero result. }

function BroadcastToClass(CName : ShortString; MessageID : Word; wParam : Word;

lParam : Longint) : Longint;

var

Idx : Integer;

RecPtr : PMsgReceiverRec; begin

Idx := 0; Result := 0;

RecPtr := ArrayBasePtr; WaitForSingleObject(SemaphoreHnd, INFINITE);

{ Send the message to all matching entries } repeat

if RecPtr^.Assigned and (RecPtr^.WndClassName = CName) then begin

if Result = 0

then Result :=

SendMessage(RecPtr^.WndHandle, MessageID, wParam, lParam) else SendMessage(RecPtr^.WndHandle, MessageID, wParam, lParam);

end;

Inc(Idx);

Inc(RecPtr);

until Idx >= MaxReceivers; ReleaseSemaphore(SemaphoreHnd, 1, nil); end;

{ BroadcastToAll uses SendMessage to broadcast the specified message to all registered components. Returns as a result

the value from the first message that returns a non-zero result. } function BroadcastToAll(MessageID : Word;

wParam : Word;

lParam : Longint) : Longint;

var

Idx : Integer;

RecPtr : PMsgReceiverRec; begin

Idx := 0; Result := 0;

RecPtr := ArrayBasePtr; WaitForSingleObject(SemaphoreHnd, INFINITE);

repeat

if RecPtr^.Assigned then begin

if Result = 0 then Result :=

SendMessage(RecPtr^.WndHandle, MessageID, wParam, lParam) else SendMessage(RecPtr^.WndHandle, MessageID, wParam, lParam);

end;

Inc(Idx);

Inc(RecPtr);

until Idx >= MaxReceivers; ReleaseSemaphore(SemaphoreHnd, 1, nil); end;

{BroadcastToOne uses SendMessage to broadcast the specified message to only one specified registered component. Returns as a result the value received from the component. }

function BroadcastToOne(MessageID : Word; RcvNum : Integer;

wParam : Word;

lParam : Longint) : Longint;

var

RecPtr : PMsgReceiverRec; begin

Result := 0;

RecPtr := ArrayBasePtr;

if (RcvNum > 0) and (RcvNum <= MaxReceivers) then begin

WaitForSingleObject(SemaphoreHnd, INFINITE);

Inc(RecPtr, RcvNum - 1); if RecPtr^.Assigned

then Result :=

SendMessage(RecPtr^.WndHandle, MessageID, wParam, lParam);

ReleaseSemaphore(SemaphoreHnd, 1, nil); end;

end;

{NumClassMsgReceivers returns the quantity of registered receivers of the specified class. }

function NumClassMsgReceivers(CName : ShortString) : Integer; var

Idx : Integer;

RecPtr : PMsgReceiverRec; begin

Idx := 0; Result := 0;

RecPtr := ArrayBasePtr; WaitForSingleObject(SemaphoreHnd, INFINITE); repeat

if RecPtr^.Assigned and (RecPtr^.WndClassName = CName) then Inc (Result);

Inc(Idx);

Inc(RecPtr);

until Idx >= MaxReceivers;

ReleaseSemaphore(SemaphoreHnd, 1, nil); end;

{NumMsgReceivers returns the total quantity of registered receivers, regardless of class. }

function NumMsgReceivers : Integer; var

Idx : Integer;

RecPtr : PMsgReceiverRec; begin

Idx := 0; Result := 0;

RecPtr := ArrayBasePtr; WaitForSingleObject(SemaphoreHnd, INFINITE); repeat

if RecPtr^.Assigned then Inc(Result); Inc(Idx);

Inc(RecPtr);

until Idx >= MaxReceivers; ReleaseSemaphore(SemaphoreHnd, 1, nil); end;

{ First ReceiverAssigned returns the index + 1 of the first registered receiver in the list. If the list is empty, -1 is returned. }

function FirstReceiverAssigned : Integer; var

Idx : Integer; Found : Boolean;

RecPtr : PMsgReceiverRec;

begin

Idx := 0;

RecPtr := ArrayBasePtr; WaitForSingleObject(SemaphoreHnd, INFINITE);

repeat

Found := RecPtr^.Assigned; if not Found

then begin Inc(Idx); Inc(RecPtr);

end;

until Found or (Idx >= MaxReceivers);

if Found

then Result := Idx + 1 else Result := -1;

ReleaseSemaphore(SemaphoreHnd, 1, nil); end;

{NextReceiverAssigned returns the index number + 1

of the next registered receiver in the list, following a specified "receiver number" (i.e., the starting index + 1). If there are no more receivers in the list, -1 is returned. }

function NextReceiverAssigned(RcvrNum : Integer) : Integer; var

Idx : Integer; Found : Boolean;

RecPtr : PMsgReceiverRec; begin

if (RcvrNum >= 1) and (RcvrNum < MaxReceivers) then begin

Idx := RcvrNum;

RecPtr := ArrayBasePtr; Inc(RecPtr, Idx);

WaitForSingleObject(SemaphoreHnd, INFINITE);

repeat

Found := RecPtr^.Assigned; if not Found

then begin Inc(Idx); Inc(RecPtr);

end;

until Found or (Idx >= MaxReceivers);

if Found

then Result := Idx + 1 else Result := -1;

ReleaseSemaphore(SemaphoreHnd, 1, nil); end

else Result := -1; end;

{Finalize unmaps and releases the shared memory, then releases the semaphore. If there is another instance of

this DLL active at the moment, the memory and semaphore will not go away. When all other instances of the DLL have been closed, the OS will destroy the reserved memory and semaphore. }

procedure FinalizeLibrary; begin UnmapViewOfFile(ArrayBasePtr); CloseHandle(ArrayHnd); CloseHandle(SemaphoreHnd);

{ Restore the exit chain } ExitProc := SaveExit; end;

exports

BcastDLLValid index 1, RegisterReceiver index 2, UnregisterReceiver index 3, BroadcastToClass index 4, BroadcastToAll index 5, BroadcastToOne index 6, NumClassMsgReceivers index 7, NumMsgReceivers index 8, FirstReceiverAssigned index 9, NextReceiverAssigned index 10;

begin

{ Library initialization code }

DLLValid := False;

{Attempt to create a memory-mapped file. If it already exists, that’s fine. In either case we will get a valid handle. }

ArrayHnd := CreateFileMapping(HWND($FFFFFFFF), nil, PAGE_READWRITE, 0,

ArraySize,

MemFileName);

{If we failed, we’re done. } if (ArrayHnd = 0) then Exit;

{Map the file by getting a pointer to it } LastErr := GetLastError;

ArrayBasePtr := MapViewOfFile(ArrayHnd, FILE_MAP_WRITE, 0, 0, 0);

{If we didn't get a valid pointer, we’re done. }

if ArrayBasePtr = nil then begin

CloseHandle(ArrayHnd);

Exit;

end;

{ If we actually created the mapped memory, clear it. } if LastErr <> ERROR_ALREADY_EXISTS

then FillChar(ArrayBasePtr^, 0, ArraySize);