Shared segments
Previous Top Next

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);
}
}