(actually this should go into "Bounties :lol:)
To get rid of DDE and all the problems caused by it once and for all, we might want to think about a solution that does without DDE again.
Under Linux, we could probably implement the functionality that we need very easily and very efficiently using msgsnd() and msgrcv(). Just take everything passed in argv[] and send them one by one. The other end reads them one by one from a poller thread and posts a wxCommandEvent to the application object. Should be like 10-15 lines of code.
If Microsoft supported named pipes for Windows98, we could have used those in the exact same way. But of course that would be too easy, named pipes only work on NT4/2000/XP... so we have to do something less elegant.
I believe something involving a mutex, a semaphore, and a shared memory area would work.
The idea is this:
- call the instance checking function as early as possible (before initialising anything)
- this may forward the commandline and terminate the application, or the application goes on like normal
- the first instance of Code::Blocks creates a mutex, a semaphore, and a shared memory page
- the primary instance also starts a poll thread which blocks in the semaphore
- all other instances know they are not the first instance from the mutex's error code
- in a non-primary instance, all argv arguments are written to a shared memory area one by one, and the semaphore is posted every time (that'll weak up the poller)
- the poller reads from the shared memory one by one and blocks again
This scheme misses one vital thing yet: we either need to append items to a linked list inside the shared memory area (lock mutex!), or we have to make sure only exactly one message can be passed at a time. Otherwise, we will almost certainly overwrite messages before the poller gets them, sooner or later.
I think a linked list is probably too complicated (we would need more excessive memory management, too).
We are probably off better by waiting for a
second semaphore (and thus blocking) until the poller thread posts the semaphore (it does that once in every iteration of its loop).
That way, exactly one commandline argument at a time can be forwarded to the primary process (no matter how many processes are being started simultaneously).
The order of appearance is not guaranteed by this, but we don't need that feature. Also, it is not overly efficient, because it generates
n messages for
n commandline arguments. However, it is
simple. Simple things are good.
Here is some code which I quickly typed together to illustrate (it will probably not compile, and it does
not prevent overwriting earlier arguments, but the general idea should become clear):
class PollingThread : public wxThread // should actually be a singleton
{
HANDLE sem;
bool abort;
void* shared;
PollingThread(HANDLE s, void* shm) : abort(false), shared(shm)
{
sem = DuplicateHandle(s);
};
virtual int Entry()
{
for(;;)
{
WaitForSingleObject(sem, INFINITE);
if(abort)
break;
int *code = (int*) shared;
char *argv = (char*) ( ((int*) shared) +1 );
wxCommandEvent e(*code);
e.SetString(cbC2U(argv));
wxTheApp->PostMessage(e);
}
return 0;
};
void NotifyAppShutdown() // have to call this at shutdown, or the app will hang
{
abort = true;
ReleaseSemaphore(sem, 1, NULL);
};
};
class InstanceChecker
{
HANDLE hMutex;
HANDLE hSemaphore;
bool primary;
void* shared;
public:
InstanceChecker()
{
hMutex = CreateMutex (NULL, FALSE, TEXT("Code::Blocks ipc mutex"));
primary = GetLastError() == ERROR_ALREADY_EXISTS; // who am I?
hSemaphore = CreateSemaphore(NULL, 0, 1, TEXT("Code::Blocks ipc semaphore"));
if(primary)
{
HANDLE hFile = CreateFile ("/temp_path/codeblocks.ipc", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
; // uh... what to do now?
SetFilePointer(hFile, 512, 0, FILE_BEGIN); // make sure we have 512 bytes of storage
SetEndOfFile(hFile);
HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
shared = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
CloseHandle(hFile);
CloseHandle(hMapping);
PollingThread t = new PollingThread(hSemaphore, shared);
t->Run();
}
else
{
// Give up time slice, for the unlikely case that two processes are started at almost exactly the same time at first run.
// In that case, we should allow the other process to create the shared memory file.
// Two full slices are ~100 ms, that should be more than enough, even in the worst of cases.
Sleep(0);
Sleep(0);
HANDLE hFile = CreateFile ("/temp_path/codeblocks.ipc", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
; // might want to retry in this case
HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
shared = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
CloseHandle(hFile);
CloseHandle(hMapping);
}
};
~InstanceChecker()
{
if(shared)
UnmapViewOfFile(shared);
// Don't delete file. For one reason, it is small and being reused.
// Also, it avoids the possible race condition with several processes being launched simultaneously.
CloseHandle(hSemaphore);
CloseHandle(hMutex);
}
void DoCheck(int argc, char* argv)
{
if (primary)
return; // continue normal operation
if(argc == 0)
{
PostOtherInstance(ACTIVATE, wxEmptyString);
}
else
{
for(unsigned int i = 0; i < argc; ++i)
PostOtherInstance(OPEN, cbC2U(argv[i]));
}
wxTheApp->ExitMainLoop();
};
void PostOtherInstance(int event, const wxString& arg)
{
int *code = (int*) shared;
char *argv = (char*) ( ((int*) shared) +1 );
*code = event;
strcpy(argv, arg);
ReleaseSemaphore(hSemaphore, 1, NULL);
};
}
Unluckily, I cannot spend the necessary time to implement this properly at the present time, as I have every minute until 30 April already planned out.

Now here's the question:
Is anybody (preferrably someone experienced in both IPC and Windows programming) volunteering to follow that idea and make it work?
