//---------------------------------------------------------------------------
#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;
}
//---------------------------------------------------------------------------