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

c++快速开发例子--lpcdemo之monitor,第1张

 

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

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/1324022.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-06-12
下一篇2022-06-12

发表评论

登录后才能评论

评论列表(0条)

    保存