Because Thinstall does not support shared segments within an EXE, below is
sample code developers may find useful.
The Windows PE file format allows for shared memory segments inside an EXE file.
These segments are specially marked by the compiler and allow multiple running
instances of an EXE to access the same copy of data. The most common use for this
by developers is to prevent multiple instances of their application from running.
Typically such applications send a message to the first application to open a
file, and then quit.
Thinstall does not support shared memory segments, so this should be accomplished
using anonymous memory mapped files. This has the advantage over shared memory
segments of working properly even if the EXE is copied and run from another location.
The code below demonstrates how to use anonymous memory mapped files to tell
a single instance of the application to process commands.
#include <windows.h>
// replace INSTANCE_CMD_MESSAGE with your own value if you need to
static const char *instance_name= "my application unique string" ;
enum { INSTANCE_CMD_MESSAGE=WM_APP };
// InvokeFirstInstanceCommand: checks for another running copy. If it is found
// the command string is copied to the global file memory map and a window message
// is sent to the other application's main thread queue. After this the program quits.
// For the main application, the return value is the location of where strings will
// be placed from other instances before the send the window message
char *InvokeFirstInstanceCommand( const char *command)
{
// see if another instance is running
HANDLE h=OpenFileMapping(FILE_MAP_ALL_ACCESS, 0 , instance_name);
if (h)
{
// yes, find out the thread id of the first instance
DWORD *data=(DWORD *)MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0 , 0 , 0 );
strncpy(( char *)(data+ 1 ), command, _MAX_PATH- 4 );
PostThreadMessage(data[ 0 ], INSTANCE_CMD_MESSAGE, 0 , 0 );
ExitProcess( 0 );
return 0 ;
}
else
{
// create a new memory map and store our thread id there.
HANDLE fm=CreateFileMapping((HANDLE) 0xffffffff , 0 , PAGE_READWRITE | SEC_COMMIT, 0 , _MAX_PATH,
instance_name);
DWORD *data=(DWORD *)MapViewOfFile(fm, FILE_MAP_ALL_ACCESS, 0 , 0 , 0 );
data[ 0 ]=GetCurrentThreadId();
// force the message queue to be created
MSG msg;
PeekMessage(&msg, 0 , WM_USER, WM_USER, PM_NOREMOVE);
return ( char *)(data+ 1 );
}
}
void main()
{
// if the function below returns, that means we are the main application
// command_location points to where strings from other instances will appear
char *command_location=InvokeFirstInstanceCommand( "open_file myfile.txt" );
// replace the code below with your own window loop
HWND my_window_handle=CreateWindow( "STATIC" , 0 , WS_POPUP | WS_VISIBLE, 0 , 0
, 0 , 0 , 0 , 0 , (HINSTANCE)GetModuleHandle(
0 ), 0 );
ShowWindow(my_window_handle, SW_SHOWNORMAL);
MSG msg;
// note: GetMessage must be called with NULL window handle to recieve the event from other instances
while (GetMessage(&msg, 0 , 0 , 0 ))
{
TranslateMessage(&msg);
// INSTANCE_CMD_MESSAGE indicated a new command has appeared at the command_location and needs processing
if (msg.message==INSTANCE_CMD_MESSAGE)
printf( "Another instance sent this command: %s\n" , command_location);
DispatchMessage(&msg);
}
}