
// Interprocess Communication Demo
//
// This program along with the Client.dpr project, demonstrate a number
// of topics in Win32 programming. Threads, Events, Mutexes, and Shared
// memory are all used to provide communication between this monitor and
// it's clients, see IPCThrd.pas.
//
// To Run, compile this project and the Client.cpp project. Run one
// instance of the monitor and then run several instances of the client.
// You can switch between clients by clicking on the Client's window or
// by selecting it from the Client menu in the monitor.
//
// Topics Covered:
//
// Interprocess Communication
// Threads
// Events
// Mutexes
// Shared Memory
// Single instance EXE.
//
//---------------------------------------------------------------------------
#include "Monform.h"
#include "TrcView.h"
#include "About.h"
//---------------------------------------------------------------------------
#pragma resource "*.dfm"
TMonitorForm *MonitorForm;
__fastcall TMonitorForm::TMonitorForm(TComponent *Owner):TForm(Owner)
{
}
/* Private Routines */
void __fastcall TMonitorForm::ClearLabels()
{
int Index;
for (Index = cfError; Index <= cfAttach; Index++)
{
TraceLabels[Index].YLabel->Caption = 0;
TraceLabels[Index].XLabel->Caption = 0;
}
}
void __fastcall TMonitorForm::OnConnect(TIPCThread* Sender, Boolean Connecting)
{
if (Connecting)
{
FStatusText = IPCMonitor->ClientName;
SignalClientStatus();
}
else
FStatusText = "No Client";
PostMessage(Handle, WM_UPDATESTATUS, 0, 0);
}
/* When a client starts or stops we need to update the client menu.
We do this by posting a message to the Monitor Form, which in turn causes
the UpdateClientMenu method to be invoked. We use this approach, rather than
calling UpdateClientMenu directly because this code is not being executed
by the main thread, but rather by the thread used in the TMonitorThread
class. We could also have used the TThread.Synchonize method, but since
there is no need for the IPC thread to wait for the monitor to update
the menu, this approach is more effecient. */
void __fastcall TMonitorForm::OnDirectoryUpdate(TIPCThread *Sender)
{
PostMessage(Handle, WM_UPDATEMENU, 0, 0);
}
/* This event is triggered when the client has new data for us. As with
the OnDirectoryUpdate event above, we use PostMessage to get the main
thread to update the display. */
void __fastcall TMonitorForm::OnSignal(TIPCThread *Sender, const TEventData &Data)
{
FTraceMsg.X = Data.X;
FTraceMsg.Y = Data.Y;
FTraceMsg.Flag = Data.Flag;
PostMessage(Handle,
WM_SETTRACEDATA,
FTraceMsg.X,
FTraceMsg.Y);
}
void __fastcall TMonitorForm::SignalClientStatus()
{
if (PauseButton->Down == true){
TClientFlags NoFlags;
IPCMonitor->SignalClient(NoFlags);
}
else
IPCMonitor->SignalClient(FClientData.Flags);
}
void __fastcall TMonitorForm::UpdateTraceData(TWMTraceData* Msg)
{
if (FClientData.Flags.Contains(FTraceMsg.Flag))
{
TraceLabels[FTraceMsg.Flag].XLabel->Caption = String((int)Msg->X);
TraceLabels[FTraceMsg.Flag].YLabel->Caption = String((int)Msg->Y);
}
}
void __fastcall TMonitorForm::UpdateStatusBar(TMessage* Msg)
{
StatusBar->SimpleText = FStatusText;
ClearLabels();
}
void __fastcall TMonitorForm::UpdateClientMenu(TMessage* Msg)
{
int I;
int ID;
TStringList* List = new TStringList();
TMenuItem* mi;
IPCMonitor->GetClientNames(List);
while (miClients->Count > 0) {
miClients->Delete(0);
}
if (List->Count == 0)
miClients->Add(NewItem("(None)", 0, False, False, NULL, 0, ""));
else
for(I=0; I < List->Count; I++)
{
ID = (int)List->Objects[I];
mi = NewItem(List->Strings[I], 0, False, True, ClientMenuClick, 0, "");
mi->Tag = ID;
mi->RadioItem = True;
mi->GroupIndex = 1;
miClients->Add(mi);
}
delete List;
}
/* Event Handlers */
void __fastcall TMonitorForm::SetupLabelArray()
{
TraceLabels[cfMouseMove].XLabel = MoveX;
TraceLabels[cfMouseMove].YLabel = MoveY;
TraceLabels[cfMouseDown].XLabel = DownX;
TraceLabels[cfMouseDown].YLabel = DownY;
TraceLabels[cfResize].XLabel = SizeX;
TraceLabels[cfResize].YLabel = SizeY;
}
void __fastcall TMonitorForm::FormCreate(TObject* Sender)
{
IPCMonitor = new TIPCMonitor((int)Application->Handle, "Monitor");
IPCMonitor->OnSignal = OnSignal;
IPCMonitor->OnConnect = OnConnect;
IPCMonitor->OnDirectoryUpdate = OnDirectoryUpdate;
IPCMonitor->Activate();
OnDirectoryUpdate(NULL);
OnConnect(NULL, False);
FClientData.Flags << cfMouseMove << cfMouseDown << cfResize;
SetupLabelArray();
}
void __fastcall TMonitorForm::FormDestroy( TObject* Sender)
{
IPCMonitor->Free();
}
void __fastcall TMonitorForm::ClientMenuClick( TObject* Sender)
{
int NewID;
NewID = (int)((TMenuItem*)Sender)->Tag;
if (NewID != IPCMonitor->ClientID)
IPCMonitor->ClientID = NewID;
}
void __fastcall TMonitorForm::miClientsClick(TObject* Sender)
{
int I;
if (IPCMonitor->ClientID != 0)
for(I=0; I < miClients->Count; I++)
if (miClients->Items[I]->Tag == IPCMonitor->ClientID)
{
miClients->Items[I]->Checked = True;
break;
}
}
void __fastcall TMonitorForm::SetTraceFlags( TObject* Sender)
{
TClientFlag F;
F = (TClientFlag)((TCheckBox*)Sender)->Tag;
if (((TCheckBox*)Sender)->Checked)
FClientData.Flags << F;
else
FClientData.Flags >> F;
SignalClientStatus();
}
void __fastcall TMonitorForm::AutoClientSwitch1Click( TObject* Sender)
{
((TMenuItem*)Sender)->Checked = ! ((TMenuItem*)Sender)->Checked;
IPCMonitor->AutoSwitch = ((TMenuItem*)Sender)->Checked;
}
void __fastcall TMonitorForm::miFileExitClick( TObject* Sender)
{
Close();
}
void __fastcall TMonitorForm::ShowTraceButtonClick( TObject* Sender)
{
IPCMonitor->GetDebugInfo(TraceForm->TraceData->Items);
TraceForm->ShowModal();
}
void __fastcall TMonitorForm::PauseButtonClick( TObject* Sender)
{
SignalClientStatus();
}
void __fastcall TMonitorForm::ClearButtonClick( TObject* Sender)
{
IPCMonitor->ClearDebugInfo();
}
void __fastcall TMonitorForm::ExitButtonClick( TObject* Sender)
{
Close();
}
void __fastcall TMonitorForm::About1Click( TObject* Sender)
{
ShowAboutBox();
}
//-----------------------------------------------------------------------------
#include "IPCThrd.h"
#include <stdio.h>
//-----------------------------------------------------------------------------
/* Inter-Process Communication Thread Classes */
// Utility Routines
void __fastcall Error(const AnsiString Msg)
{
ShowMessage(Msg);
throw(Msg);
}
AnsiString __fastcall EventName(TEventKind Event)
{
switch (Event){
case evMonitorAttach: return "evMonitorAttach";
case evMonitorDetach: return "evMonitorDetach";
case evMonitorSignal: return "evMonitorSignal";
case evMonitorExit: return "evMonitorExit";
case evClientStart: return "evClientStart";
case evClientStop: return "evClientStop";
case evClientAttach: return "evClientAttach";
case evClientDetach: return "evClientDetach";
case evClientSwitch: return "evClientSwitch";
case evClientSignal: return "evClientSignal";
case evClientExit: return "evClientExit";
default: return "";
}
}
/* Utility function used by the monitor to determine if another monitor is
already running. This is needed to make the monitor a single instance .EXE.
This function relies on the fact that the first 4 bytes of the client
directory always contain the Application handle of the monitor, or zero if
no monitor is running. This function is used in Monitor.cpp. */
bool __fastcall IsMonitorRunning(THandle& Hndl)
{
TSharedMem* SharedMem;
bool Result;
SharedMem = new TSharedMem(CLIENT_DIR_NAME, 4);
Hndl = *((THandle*)(SharedMem->Buffer));
Result = (Hndl != 0);
delete SharedMem;
return Result;
}
/* THandledObject */
/* This is a generic class for all encapsulated WinAPI's which need to call
CloseHandle when no longer needed. This code eliminates the need for
3 identical destructors in the TEvent, TMutex, and TSharedMem classes
which are descended from this class. */
__fastcall THandledObject::~THandledObject()
{
if (FHandle != 0)
CloseHandle(FHandle);
}
/* TEvent */
/* This class encapsulates the concept of a Win32 event (not to be
confused with Delphi events), see "CreateEvent" in the Win32
reference for more information */
__fastcall TEvent::TEvent(const AnsiString Name, bool Manual)
{
AnsiString buf;
buf = Name + ".Data";
FHandle = CreateEvent(NULL, Manual, false, buf.c_str());
if (FHandle == 0) Abort();
}
void __fastcall TEvent::Reset()
{
ResetEvent(FHandle);
}
void __fastcall TEvent::Signal()
{
SetEvent(FHandle);
}
bool __fastcall TEvent::Wait(int TimeOut)
{
return WaitForSingleObject(FHandle, TimeOut) == WAIT_OBJECT_0;
}
/* TMutex */
/* This class encapsulates the concept of a Win32 mutex. See "CreateMutex"
in the Win32 reference for more information */
__fastcall TMutex::TMutex(const AnsiString Name)
{
FHandle = CreateMutex(NULL, false, Name.c_str());
if (FHandle == 0) Abort();
}
bool __fastcall TMutex::Get(int TimeOut)
{
return WaitForSingleObject(FHandle, TimeOut) == WAIT_OBJECT_0;
}
bool __fastcall TMutex::Release(void)
{
return ReleaseMutex(FHandle);
}
/* TSharedMem */
/* This class simplifies the process of creating a region of shared memory.
In Win32, this is accomplished by using the CreateFileMapping and
MapViewOfFile functions. */
__fastcall TSharedMem::TSharedMem(const AnsiString Name, int Size)
{
try{
FName = Name;
FSize = Size;
/* CreateFileMapping, when called with $FFFFFFFF for the handle value,
creates a region of shared memory. If an object with same name already
exists, then a Handle to that object will be returned. */
FHandle = CreateFileMapping((HANDLE)0xFFFFFFFF,
NULL,
PAGE_READWRITE,
0,
Size,
Name.c_str());
if (FHandle == 0) Abort();
FCreated = (GetLastError == 0);
/* We still need to map a pointer to the handle of the shared memory region */
FFileView = MapViewOfFile(FHandle,
FILE_MAP_WRITE,
0, 0, Size);
if (FFileView == NULL) Abort();
}
catch(...) {
Error(Format("Error creating shared memory %s (%d)",
(TVarRec*)Name.c_str(), GetLastError()));
}
}
__fastcall TSharedMem::~TSharedMem()
{
if (FFileView != NULL)
UnmapViewOfFile(FFileView);
}
/* Debug Tracing */
#if defined(IPCDEBUG)
/* Debug Tracing */
/* The IPCTracer class was used to create and debug the IPC classes which
follow. When developing a multi-process, multi-threaded application, it
is difficult to debug effectively using ordinary debuggers. The trace
data is displayed in a Window when you click on a speed button in the
monitor program. */
/* TIPCTracer */
__fastcall TIPCTracer::TIPCTracer(AnsiString ID)
{
strcpy(FIDName, ID.c_str());
FSharedMem = new TSharedMem(TRACE_BUFFER, TRACE_BUF_SIZE);
FMutex = new TMutex(TRACE_MUTEX);
//The first four bytes of the buffer contain the number of meaningful
//bytes in the buffer at any given time.
if (*((int*)(FSharedMem->Buffer)) == 0)
*(int*)FSharedMem->Buffer = sizeof(PTraceEntry);
}
__fastcall TIPCTracer::~TIPCTracer()
{
delete FMutex;
delete FSharedMem;
}
PTraceEntry __fastcall TIPCTracer::MakePtr(int Ofs)
{
return PTraceEntry(int(FSharedMem->Buffer) + Ofs);
}
PTraceEntry __fastcall TIPCTracer::FirstEntry()
{
return MakePtr(sizeof(PTraceEntry));
}
PTraceEntry __fastcall TIPCTracer::NextEntry(void)
{
return MakePtr(*(int*)(FSharedMem->Buffer));
}
void __fastcall TIPCTracer::Add(PChar AMsg)
{
PTraceEntry TraceEntry;
int EntrySize;
TLargeInteger TempTime;
FMutex->Get(INFINITE);
TraceEntry = NextEntry();
EntrySize = sizeof(AMsg) + sizeof(TTraceEntry) + 16;
/* If we hit the end of the buffer, just wrap around */
if ((EntrySize + *(int*)(FSharedMem->Buffer)) > FSharedMem->Size)
TraceEntry = FirstEntry();
QueryPerformanceCounter(&TempTime);
// TraceEntry->Time = TempTime.LowPart;
TraceEntry->Time = (int) TempTime.QuadPart;
TraceEntry->Size = EntrySize;
sprintf(TraceEntry->Msg,
"%s: %s", FIDName, AMsg);
*(int*)(FSharedMem->Buffer) = *(int*)(FSharedMem->Buffer) + TraceEntry->Size;
FMutex->Release();
}
void __fastcall TIPCTracer::GetList(TStrings *List)
{
PTraceEntry LastEntry;
PTraceEntry TraceEntry;
int Dif;
int LastTime;
List->BeginUpdate();
try{
LastEntry = NextEntry();
TraceEntry = FirstEntry();
LastTime = TraceEntry->Time;
List->Clear();
while (TraceEntry != LastEntry){
Dif = TraceEntry->Time - LastTime;
List->Add(Format("%x %10d %s",
ARRAYOFCONST((TraceEntry->Time,
Dif,
TraceEntry->Msg))));
LastTime = TraceEntry->Time;
(int)TraceEntry = (int)TraceEntry + TraceEntry->Size;
}
}
catch(...){
List->EndUpdate();
throw;
}
List->EndUpdate();
}
void __fastcall TIPCTracer::Clear()
{
FMutex->Get(INFINITE);
*(int*)FSharedMem->Buffer = sizeof(PTraceEntry);
FMutex->Release();
}
#endif
/* TIPCEvent */
/* Win32 events are very basic. They are either signaled or non-signaled.
The TIPCEvent class creates a "typed" TEvent, by using a block of shared
memory to hold an "EventKind" property. The shared memory is also used
to hold an ID, which is important when running multiple clients, and
a Data area for communicating data along with the event */
__fastcall TIPCEvent::TIPCEvent(TIPCThread *AOwner,
const AnsiString Name,
bool Manual)
:TEvent(Name, Manual)
{
FOwner = AOwner;
FSharedMem = new TSharedMem(Name, sizeof(TIPCEventInfo));
FEventInfo = (TIPCEventInfo*) FSharedMem->Buffer;
}
__fastcall TIPCEvent::~TIPCEvent()
{
delete FSharedMem;
}
int __fastcall TIPCEvent::GetID()
{
return FEventInfo->FID;
}
void __fastcall TIPCEvent::SetID(int Value)
{
FEventInfo->FID = Value;
}
TEventKind __fastcall TIPCEvent::GetKind()
{
return FEventInfo->FKind;
}
void __fastcall TIPCEvent::SetKind(TEventKind Value)
{
FEventInfo->FKind = Value;
}
TEventData __fastcall TIPCEvent::GetData()
{
return FEventInfo->FData;
}
void __fastcall TIPCEvent::SetData(const TEventData& Value)
{
FEventInfo->FData = Value;
}
void __fastcall TIPCEvent::Signal(TEventKind Kind)
{
SetID(OwnerID);
FEventInfo->FKind = Kind;
TEvent::Signal();
}
void __fastcall TIPCEvent::SignalID(TEventKind Kind, int ID)
{
FEventInfo->FID = ID;
FEventInfo->FKind = Kind;
TEvent::Signal();
}
void __fastcall TIPCEvent::SignalData(TEventKind Kind,
int ID,
const TEventData& Data)
{
FEventInfo->FID = ID;
FEventInfo->FData = Data;
FEventInfo->FKind = Kind;
TEvent::Signal();
}
bool __fastcall TIPCEvent::WaitFor(int TimeOut,
int ID,
TEventKind Kind)
{
bool returnval = Wait(TimeOut);
if (returnval)
returnval = (ID == FEventInfo->FID) && (Kind == FEventInfo->FKind);
else
FOwner->DbgStr(Format("Wait Failed %s Kind: %s ID: %x" ,
ARRAYOFCONST((FOwner->ClassName(),
EventName(Kind),
ID))));
return returnval;
}
/* TClientDirectory */
/* The client directory is a block of shared memory where the list of all
active clients is maintained */
__fastcall TClientDirectory::TClientDirectory(int MaxClients)
{
FMaxClients = MaxClients;
FMutex = new TMutex(CLIENT_DIR_MUTEX);
FSharedMem = new TSharedMem(CLIENT_DIR_NAME,
FMaxClients * sizeof(TClientDirEntry) + 8);
FMonitorID = (int*) FSharedMem->Buffer;
(int)FClientCount = (int)FMonitorID + sizeof(FMonitorID);
(int)FDirBuffer = (int)FClientCount + sizeof(FClientCount);
}
__fastcall TClientDirectory::~TClientDirectory()
{
delete FSharedMem;
}
int __fastcall TClientDirectory::AddClient(int ClientID, const AnsiString AName)
{
int Result = -1;
if (Count == FMaxClients)
{
String EString("Maximun of ");
EString += (int)FMaxClients;
EString += " clients allowed.";
Error(EString);
}
if (IndexOf(ClientID) > -1)
Error("Duplicate client ID");
if (FMutex->Get(TIMEOUT)){
try{
FDirBuffer[Count]->ID = ClientID;
lstrcpy(FDirBuffer[Count]->Name,
AName.c_str());
(*FClientCount)++;
Result = Count-1;
}
catch(...){
FMutex->Release();
throw;
} //end catch
} //end if (FMutex..
FMutex->Release();
return Result;
}
int __fastcall TClientDirectory::GetCount()
{
return (*FClientCount);
}
TClientDirEntry __fastcall TClientDirectory::GetClientRec(int Index)
{
if ((Index < 0) || (Index >= Count))
Error("Invalid client list index");
return *FDirBuffer[Index];
}
AnsiString __fastcall TClientDirectory::GetClientName(int ClientID)
{
int Index;
Index = IndexOf(ClientID);
if (Index >= 0)
return FDirBuffer[Index]->Name;
else
return "";
}
int __fastcall TClientDirectory::IndexOf(int ClientID)
{
int I;
for(I=0; I < Count; I++)
if (FDirBuffer[I]->ID == ClientID){
return I;
}
return -1;
}
int __fastcall TClientDirectory::Last()
{
if (Count > 0)
return FDirBuffer[Count-1]->ID;
else
return 0;
}
bool __fastcall TClientDirectory::RemoveClient(int ClientID)
{
int Index;
bool Result;
Index = IndexOf(ClientID);
if ((Index > -1) && FMutex->Get(TIMEOUT)){
try{
if ((Index > 0) && (Index < Count))
Move(FDirBuffer[Index+1],
FDirBuffer[Index],
(Count - Index) * sizeof(TClientDirEntry));
(*FClientCount)--; //Decrement client count
Result = true;
}
catch(...){
FMutex->Release();
throw;
}
FMutex->Release();
} //end if
else
Result = false;
return Result;
}
int __fastcall TClientDirectory::GetMonitorID()
{
return *FMonitorID;
}
void __fastcall TClientDirectory::SetMonitorID(int MonitorID)
{
*FMonitorID = MonitorID;
}
/* TIPCThread */
/* The TIPCThread class implements the functionality which is common between
the monitor and client thread classes. */
__fastcall TIPCThread::TIPCThread(int AID, const AnsiString AName)
:TThread(true)
{
FID = AID;
FName = AName;
#if defined(IPCDEBUG)
if (dynamic_cast<TIPCMonitor*>(this) != 0)
FTracer = new TIPCTracer(FName);
else
FTracer = new TIPCTracer(IntToHex(FID, 8));
#endif
FMonitorEvent = new TIPCEvent(this, MONITOR_EVENT_NAME, false);
FClientEvent = new TIPCEvent(this, CLIENT_EVENT_NAME, false);
FConnectEvent = new TIPCEvent(this, CONNECT_EVENT_NAME, false);
FClientDirectory = new TClientDirectory(MAX_CLIENTS);
}
__fastcall TIPCThread::~TIPCThread()
{
DeActivate();
delete FClientDirectory;
delete FClientEvent;
delete FMonitorEvent;
FState = stInActive;
#if defined(IPCDEBUG)
delete FTracer;
#endif
}
/* This procedure is called all over the place to keep track of what is
going on */
void __fastcall TIPCThread::DbgStr(const AnsiString S)
{
#if defined(IPCDEBUG)
FTracer->Add(S.c_str());
#endif
}
/* TIPCMonitor */
__fastcall TIPCMonitor::TIPCMonitor(int AID, const AnsiString AName)
: TIPCThread(AID, AName),
FAutoSwitch(true)
{
}
void __fastcall TIPCMonitor::Activate()
{
if (FState == stInActive) {
// Put the monitor handle into the client directory so we can use it to
// prevent multiple monitors from running
if (FClientDirectory->MonitorID == 0)
FClientDirectory->MonitorID = FID;
else
throw EMonitorActive("Monitor is already active.");
FState = stDisconnected;
Resume();
}
}
void __fastcall TIPCMonitor::DeActivate()
{
if ((State != stInActive) && !(Suspended)){
FClientDirectory->MonitorID = 0;
FMonitorEvent->Signal(evMonitorExit);
if (WaitForSingleObject((void *)Handle, TIMEOUT) != WAIT_OBJECT_0)
TerminateThread((void *)Handle, 0);
}
}
/* This method, and the TIPCClient.Execute method represent the meat of this
program. These two thread handlers are responsible for communcation with
each other through the IPC event classes */
void __fastcall TIPCMonitor::Execute()
{
int WaitResult;
bool bContinueThread = true;
DbgStr(FName + " Activated");
if (FClientDirectory->Count > 0)
FMonitorEvent->SignalID(evClientStart, FClientDirectory->Last());
while (bContinueThread == true){
try {
WaitResult = WaitForSingleObject(FMonitorEvent->Handle, INFINITE);
if (WaitResult >= WAIT_ABANDONED)
DisconnectFromClient(false);
else{
if (WaitResult == WAIT_OBJECT_0)
{
DbgStr("Event Signaled: " + EventName(FMonitorEvent->Kind));
switch (FMonitorEvent->Kind){
case evClientSignal:
DoOnSignal();
break;
case evClientStart:
if ((AutoSwitch == true) || (FClientID == 0))
ConnectToClient(FMonitorEvent->ID);
DoOnDirUpdate();
break;
case evClientStop:
DoOnDirUpdate();
break;
case evClientDetach:
DisconnectFromClient(false);
Sleep(100);
if (AutoSwitch != false)
ConnectToClient(FClientDirectory->Last());
break;
case evClientSwitch:
ConnectToClient(FMonitorEvent->ID);
break;
case evMonitorExit:
DisconnectFromClient(false);
bContinueThread = false;
break;
default:
break;
} //end switch
} //end if
else {
DbgStr(Format("Unexpected Wait Return Code: %d",
ARRAYOFCONST((WaitResult))));
} //end else
} //end else
} //end try
catch(Exception& E){
DbgStr(Format("Exception raised in Thread Handler: %s at %X",
ARRAYOFCONST((E.Message, ExceptAddr))));
} //end catch
}//end while
FState = stInActive;
DbgStr("Thread Handler Exited");
}
void __fastcall TIPCMonitor::ConnectToClient(int ID)
{
if (ID == FClientID) return;
if (FState == stConnected)
DisconnectFromClient(true);
if (ID == 0) return;
DbgStr(Format("Sending evMonitorAttach: %X",
ARRAYOFCONST((ID))));
/* Tell a client we want to attach to them */
FConnectEvent->SignalID(evMonitorAttach, ID);
/* Wait for the client to say "OK" */
if ((FMonitorEvent->WaitFor(TIMEOUT, ID, evClientAttach) != false) &&
(FMonitorEvent->Data.Flag == cfAttach)) {
FClientID = ID;
FState = stConnected;
if (FOnConnect != NULL)
OnConnect(this, true);
DbgStr("ConnectToClient Successful");
}
else
DbgStr("ConnectToClient Failed: " + EventName(FMonitorEvent->Kind));
}
/* If Wait is true ... */
void __fastcall TIPCMonitor::DisconnectFromClient(bool Wait)
{
if (FState == stConnected){
DbgStr(Format("Sending evMonitorDetach: %x",
ARRAYOFCONST((FClientID))));
/* Tell the client we are detaching */
FClientEvent->SignalID(evMonitorDetach, FClientID);
/* If we (the monitor) initiated the detach process, then wait around
for the client to acknowledge the detach, otherwise, just continue on */
if (Wait)
if (FMonitorEvent->WaitFor(TIMEOUT, FClientID, evClientDetach) == 0){
DbgStr(Format("Error waiting for client to detach: %x",
ARRAYOFCONST((FClientID))));
FClientDirectory->RemoveClient(FClientID);
}
FClientID = 0;
FState = stDisconnected;
if (FOnConnect != 0)
FOnConnect(this, false);
if ((! Wait) && (FOnDirUpdate != NULL))
DoOnDirUpdate();
}
}
/* This method is called when the client has new data for us */
void __fastcall TIPCMonitor::DoOnSignal(void)
{
if ((FOnSignal != NULL) && (FMonitorEvent->ID == FClientID))
FOnSignal(this, FMonitorEvent->Data);
}
/* Tell the client we have new flags for it */
void __fastcall TIPCMonitor::SignalClient(const TClientFlags Value)
{
if (FState == stConnected){
FClientEvent->EventInfo->FData.Flags = Value;
DbgStr("Signaling Client");
FClientEvent->SignalData(evMonitorSignal, FClientID, FClientEvent->Data);
}
}
AnsiString __fastcall TIPCMonitor::GetClientName()
{
return FClientDirectory->Name[FClientID];
}
void __fastcall TIPCMonitor::GetClientNames(TStringList* List)
{
int I;
AnsiString S;
int DupCnt;
List->BeginUpdate();
try{
List->Clear();
for(I=0; I < FClientDirectory->Count; I++){
S = FClientDirectory->ClientRec[I].Name;
DupCnt = 1;
/* Number duplicate names so we can distinguish them in the client menu */
while(List->IndexOf(S) > -1){
DupCnt++;
S = Format("%s (%d)",
ARRAYOFCONST((FClientDirectory->ClientRec[I].Name,
DupCnt)));
}
List->AddObject(S, (TObject*)FClientDirectory->ClientRec[I].ID);
}
}
catch(...){
List->EndUpdate();
throw;
}
List->EndUpdate();
}
void __fastcall TIPCMonitor::SetCurrentClient(int ID)
{
if (ID == 0)
ID = FClientDirectory->Last();
if (ID !=0)
FMonitorEvent->SignalID(evClientSwitch, ID);
}
void __fastcall TIPCMonitor::ClearDebugInfo()
{
#ifdef IPCDEBUG
FTracer->Clear();
#endif
}
void __fastcall TIPCMonitor::GetDebugInfo(TStrings *List)
{
#ifdef IPCDEBUG
FTracer->GetList(List);
#else
List->Add("Debug Tracing Disabled");
#endif
}
void __fastcall TIPCMonitor::SaveDebugInfo(const AnsiString FileName)
{
#if defined(IPCDEBUG)
TStringList* List;
List = new TStringList();
try{
GetDebugInfo(List);
List->SaveToFile(FileName);
}
catch(...){
delete List;
throw;
}
delete List;
#endif
}
void __fastcall TIPCMonitor::DoOnDirUpdate()
{
if (FOnDirUpdate != NULL)
FOnDirUpdate(this);
}
/* TIPCClient */
void __fastcall TIPCClient::Activate()
{
if (FState == stInActive){
FWaitEvent = FConnectEvent;
FMonitorEvent->OwnerID = FID;
FConnectEvent->OwnerID = FID;
FClientEvent->OwnerID = FID;
FClientDirectory->AddClient(FID, FName);
FState = stDisconnected;
Resume();
}
}
void __fastcall TIPCClient::DeActivate()
{
if (FClientDirectory != NULL)
FClientDirectory->RemoveClient(FID);
if ((FState != stInActive) && !(Suspended)){
FWaitEvent->Signal(evClientExit);
if (WaitForSingleObject((void *)Handle, TIMEOUT) != WAIT_OBJECT_0)
TerminateThread((void *)Handle, 0);
}
}
void __fastcall TIPCClient::Execute(void)
{
DbgStr(FName + " Activated");
bool bContinueThread = true;
if ((FClientDirectory->MonitorID) != 0)
{
FMonitorEvent->SignalID(evClientStart, FID);
}
while (bContinueThread == true){
try{
if (WaitForSingleObject(FWaitEvent->Handle, INFINITE) != WAIT_OBJECT_0)
break;
if (FWaitEvent->ID != FID){
Sleep(200);
continue;
}
DbgStr("Client Event Signaled: " + EventName(FWaitEvent->Kind));
switch(FWaitEvent->Kind){
case evMonitorSignal:
if (FOnSignal != NULL)
FOnSignal(this, FWaitEvent->Data);
break;
case evMonitorAttach:
ConnectToMonitor();
break;
case evMonitorDetach:
DisconnectFromMonitor(false);
Sleep(200);
break;
case evClientExit:
if (FClientDirectory->MonitorID != 0) {
if (FState == stConnected)
DisconnectFromMonitor(true);
else
FMonitorEvent->Signal(evClientStop);
}
bContinueThread = false;
break;
default:
break;
} //end switch
} //end try
catch(Exception& E){
DbgStr(Format("Exception raised in Thread Handler: %s at %X",
ARRAYOFCONST((E.Message, ExceptAddr))));
}//end try-catch
} //end while
FState = stInActive;
DbgStr("Thread Handler Exited");
}
void __fastcall TIPCClient::ConnectToMonitor()
{
TEventData Data;
DbgStr("ConnectToMonitor Begin");
FConnectEvent->Reset();
try {
FState = stConnected;
FWaitEvent = FClientEvent;
Data.Flag = cfAttach;
FMonitorEvent->SignalData(evClientAttach, FID, Data);
if (FOnConnect != 0)
FOnConnect(this, true);
}
catch(Exception& E){
DbgStr("Exception in ConnectToMonitor: " + E.Message);
Data.Flag = cfError;
FMonitorEvent->SignalData(evClientAttach, FID, Data);
}
DbgStr("ConnectToMonitor End");
}
void __fastcall TIPCClient::DisconnectFromMonitor(bool Wait)
{
DbgStr("DisconnectFromMonitor Begin");
if (FState == stConnected) {
if (Wait){
DbgStr("Sending evClientDetach");
FMonitorEvent->Signal(evClientDetach);
if (FClientEvent->WaitFor(TIMEOUT, FID, evMonitorDetach))
DbgStr("Got evMonitorDetach");
else
DbgStr("Error waiting for evMonitorDetach");
}
FState = stDisconnected;
FWaitEvent = FConnectEvent;
if (! Wait){
DbgStr("DisconnectFromMonitor sending evClientDetach");
FMonitorEvent->Signal(evClientDetach);
}
if (FOnConnect != NULL)
FOnConnect((TIPCThread*)this, false);
}
DbgStr("DisconnectFromMonitor End");
}
void __fastcall TIPCClient::SignalMonitor(const TEventData& Data)
{
if (FState == stConnected){
DbgStr("Signaling Monitor");
FMonitorEvent->SignalData(evClientSignal, FID, Data);
}
}
int __fastcall TIPCClient::ClientCount()
{
return FClientDirectory->Count;
}
void __fastcall TIPCClient::MakeCurrent()
{
FMonitorEvent->SignalID(evClientStart, FID);
}
//---------------------------------------------------------------------------
#include "about.h"
//---------------------------------------------------------------------------
#pragma resource "*.dfm"
TAboutBox *AboutBox;
__fastcall TAboutBox::TAboutBox(TComponent* Owner) : TForm(Owner)
{
}
void __fastcall TAboutBox::FormCreate(TObject *Sender)
{
Caption = Format("About %s",
ARRAYOFCONST((Application->Title)));
ProgramIcon->Picture->Assign(Application->Icon);
ProgramName->Caption = Application->Title;
}
void __fastcall ShowAboutBox()
{
TAboutBox* a = new TAboutBox(Application);
try{
a->ShowModal();
}
catch(...){
a->Free();
throw;
}
a->Free();
}
//-----------------------------------------------------------------------------
#include "TrcView.h"
//-----------------------------------------------------------------------------
#pragma resource "*.DFM"
TTraceForm* TraceForm;
__fastcall TTraceForm::TTraceForm(TComponent* Owner) : TForm(Owner)
{
}
void __fastcall TTraceForm::TraceDataKeyPress( TObject* Sender, Char& Key)
{
if (Key == 0x27) Close();
}
这篇博客详细介绍了Windows环境下使用C++实现多进程间通信(IPC)的示例,涵盖了线程、事件、互斥量和共享内存等概念。通过创建一个监控程序和客户端程序,展示了如何在多个进程中传递数据和信号,以及如何实现单实例应用程序。此外,还讨论了线程同步和状态管理,包括事件触发、信号量和线程同步策略。
491

被折叠的 条评论
为什么被折叠?



