c++快速开发例子--lpcdemo之monitor

这篇博客详细介绍了Windows环境下使用C++实现多进程间通信(IPC)的示例,涵盖了线程、事件、互斥量和共享内存等概念。通过创建一个监控程序和客户端程序,展示了如何在多个进程中传递数据和信号,以及如何实现单实例应用程序。此外,还讨论了线程同步和状态管理,包括事件触发、信号量和线程同步策略。

 

// 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();
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

moneytree

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值