//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
//---------------------------------------------------------------------------
USEFORM("MainForm.cpp", frmMultiEditorMain);
//---------------------------------------------------------------------------
/*
  This is kind of boiler-plate code for using a memory mapped file to communicate
  between two processes. You can find similar code just by searching the Internet.
  (That's what I did.) The MSDN has a lot of information and examples like this.
  This code is actually much simpler in that it doesn't handle all possible errors
  that could arise. A fully robust version would require more complex techniques.
  This thread shows a similar technique and also points out some caveats to what I
  show here:

    http://www.swissdelphicenter.ch/torry/showcode.php?id=1969

  Most of the tidbits here I got from Borland's newsgroups years ago. There are probably
  classes and/or components that encapsulate some of this stuff, but I chose to do it this
  way because there were a lot of examples like this and most of this code can be used
  outside of RAD Studio. The link above shows some nice examples.

  This is very Windows-specific code so consult the MSDN for details.
*/

  // Using GUIDs to guarantee uniqueness (Ctrl-Shift-G in the IDE)
const char *SharedMemName = "{71A5C20B-F374-498B-A766-216E03E71845}";
const char *MultiEditMsgName = "{F01FFA87-69E3-411A-8EBB-A2B403565985}";

/*
  This is the only data that is shared between two instances.
  Note that a ShortString is hard-coded internally to hold 255 characters. It is not a pointer.
  If you need a command line that is longer than 255 characters, you need to provide a different
  mechanism to communicate. If you try to assign a string that is too long, it will be truncated;
  it won't overwrite memory. Be aware of that.
*/
struct TMemoryMappedData
{
  HWND hApp;           // This is written by the first instance (second instance calls SetForegroundWindow(AppHandle))
  HWND hWin;           // This is written by the first instance (second instance calls ShowWindow(WinHandle, ...))
  ShortString CmdLine; // This is written by the second instance (first instance reads it after receiving a message)
};

unsigned int MultiEditMsg;            // custom message to send to first instance
TMemoryMappedData *pMemoryMappedData; // shared data area

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
  try
  {
      // We need to create a new message (or lookup the existing one)
    MultiEditMsg = RegisterWindowMessage(MultiEditMsgName);

      // We'll use this file to communicate with another instance of this application. See MSDN for details.
    HANDLE hMemoryMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(TMemoryMappedData), SharedMemName);

      // If this is true, then we are the second instance
    if (GetLastError() == ERROR_ALREADY_EXISTS)
    {
        // Open the file with Read/Write access. See MSDN for details.
      hMemoryMap = OpenFileMapping(FILE_MAP_WRITE, false, SharedMemName);

        // Successfully opened it
      if (hMemoryMap)
      {
          // Map the portion we want (we want the whole thing)
        pMemoryMappedData = (TMemoryMappedData *)MapViewOfFile(hMemoryMap, FILE_MAP_WRITE, 0, 0, sizeof(TMemoryMappedData));

          // If the app is minimize, restore it
        if (IsIconic(pMemoryMappedData->hApp))
          ShowWindow(pMemoryMappedData->hApp, SW_RESTORE);

          // Bring it to the front
        SetForegroundWindow(pMemoryMappedData->hWin);

          // Store the command line (if any) in the memory map and send a message to the
          // other instance. This is how the communication is done between the running app
          // and this instance. The application will receive this message in the
          // DefaultHandler event handler (in the main form of this app).
        if (ParamCount > 0)
        {
            // TODO: Deal with multiple files from a new instance
            // This application wants an absolute path/filename
          pMemoryMappedData->CmdLine = (ShortString)ExpandFileName(ParamStr(1));
          SendMessage(pMemoryMappedData->hWin, MultiEditMsg, 0, 0);
        }

          // Close the file (see FlushViewOfFile for possibly more safety)
        UnmapViewOfFile(pMemoryMappedData);
        CloseHandle(hMemoryMap);
      }
      else
      {
        // TODO: Handle this?
      }
    }
      // This is the first instance
    else
    {
        // Failed to create file.
      if (!hMemoryMap)
        RaiseLastOSError();

        // Map the portion we want (we want the whole thing)
      pMemoryMappedData = (TMemoryMappedData *)MapViewOfFile(hMemoryMap, FILE_MAP_WRITE, 0, 0, sizeof(TMemoryMappedData));

      /*
        Since we need to store the handle to the window in the memory mapped file, we need a
        window. But, the window isn't created yet. So, we'll get the handles in the FormCreate
        event and store them in the memory mapped file there. (See MainForm::FormCreate)
        Note that this may not be 100% OK as it's possible (or was possible) that a window's handle
        could change during its lifetime. TODO: Maybe use GetCurrentThreadID and store that instead
        of window handle?
      */

        // The "normal" code in this file
      Application->Initialize();
      Application->CreateForm(__classid(TfrmMultiEditorMain), &frmMultiEditorMain);
      Application->Run();

        // TODO: Protect this code better from possible exceptions from 3 lines above. (__finally?)
        // Done with the memory mapped file
      UnmapViewOfFile(pMemoryMappedData);
      CloseHandle(hMemoryMap);
    }
  }
  catch (Exception &exception)
  {
    Application->ShowException(&exception);
  }
  catch (...)
  {
    try
    {
      throw Exception("");
    }
    catch (Exception &exception)
    {
      Application->ShowException(&exception);
    }
  }
  return 0;
}
//---------------------------------------------------------------------------