It looks longish, but if you take out all comments and (some of the) error checking it isn't that bad

(Edit 1: added protection against buffer overrun.)
(Edit 2: I've also worked out a mailslot version.)
More on mutex here:
http://www.purebasic.fr/english/viewtopic.php?t=27439
... and filemapping objects here:
http://www.purebasic.fr/english/viewtopic.php?t=27457
... and mailslots here:
http://www.purebasic.fr/english/viewtop ... 997#198997
And here's the code:
Code: Select all
; purebasic survival guide - pb3.94
; singleinstance_1.pb - 11.06.2007 ejn (blueznl)
; http://www.xs4all.nl/~bluez/datatalk/purebasic.htm
;
; - single instancing, deduction host vs. client
; - sharing memory between applications
; - handshake mechanism for acces to shared memory between a single host and multiple clients
;
; see also filemapping_1.pb, filemapping_2.pb, mutex_1.pb
; you DEFINITELY want to have the WIN32.HLP file open and at your side
;
;
; (this sourcecode crashes the IDE sometimes, unless I edit it in a text editor and remove the ide options part, weird)
;
; okay, so first of all, all that I was looking for was a good valid way of passing command line parameters to other
; instances... then I went a bit overboard :-)
;
; it's probably easier to use a callback and the WMCOPYDATA message, but here's another approach that allows you to
; send data to another program (window)... or make a single instance of your program :-)
;
; data_size: create an executable and start that TWICE...
;
;
#MUTEX_ALL_ACCESS = $1F0001
#WAIT_TIMEOUT = $102
#WAIT_ABANDONED = $80
#WAIT_FAILED = $FFFFFFFF
#WAIT_OBJECT_0 = $0
;
Structure buffer
w_host_h.l ; handle of host window, easier sending messages to the proper window
data_max.l ; max number of bytes in the data part of the buffer, should always be kept below data_max
data_size.l ; number of bytes currently in the data part
EndStructure
buffer_size = 65536 ; let's use a 64k buffer
data_max = buffer_size-SizeOf(buffer) ; which contains max 64k - 12 bytes of data
;
error = #False ; will be #True If there were problems
;
; in this example I am registering a 'global' wakeup message, in this specific case (as I am sending the message straight
; to the receiving window) this isn't necessary and I could have used any (valid ie. not yet used) message, but what the
; heck... look for the postmessage_() instruction and replace the w_host_h with #HWND_BROADCAST
;
message_wakeup_id = RegisterWindowMessage_("wakeup") ; register the wakeup message
;
; open the window, we'll deal with the title later
;
w_main_nr = OpenWindow(#PB_Any,100,100,400,200,#PB_Window_TitleBar|#PB_Window_SystemMenu,"Interprocess communication sample")
w_main_h = WindowID(w_main_nr)
gl_main_h = CreateGadgetList(w_main_h)
g_list_nr = ListIconGadget(#PB_Any,10,10,WindowWidth()-20,WindowHeight()-60,"Log",2000)
g_exit_nr = ButtonGadget(#PB_Any,WindowWidth()-70,WindowHeight()-40,60,30,"Exit")
;
; global stuff shared by host and client
;
mutex_busy_name.s = "busy"
filemap_buffer_name.s = "buffer"
;
; flag the busy mutex, if we cannot create it, or it is already owned, then we are not the host
;
mutex_busy_h = CreateMutex_(0,#True,@mutex_busy_name) ; create and try to be the owner
lasterror = GetLastError_()
If mutex_busy_h = 0
AddGadgetItem(g_list_nr,-1,"error opening busy mutex")
error = #True
ElseIf lasterror = #ERROR_ALREADY_EXISTS
AddGadgetItem(g_list_nr,-1,"busy mutex already exists")
CloseHandle_(mutex_busy_h)
;
; *** client
;
; we can't create and own the mutex, so it's already in use, so we cannot be the host so we must be a client
; (you're still following? :-))
;
MoveWindow(WindowX()+WindowWidth(),WindowY()) ; make sure windows don't overlap :-)
AddGadgetItem(g_list_nr,-1,"client!")
SetWindowTitle(w_main_nr,"Client")
g_send_nr = ButtonGadget(#PB_Any,WindowWidth()-150,WindowHeight()-40,60,30,"Send")
;
AddGadgetItem(g_list_nr,-1,"waiting...") ; so it's sure we are the host
Repeat ; wait for messages
event = WaitWindowEvent()
event_gadget = EventGadgetID()
Select event
Case #PB_Event_Gadget
Select event_gadget
Case g_exit_nr ; exit button was pushed
event = #PB_Event_CloseWindow
Case g_send_nr ; send button was pushed
;
; try to send a message
;
message.s = "test "+Str(Random(999999)) ; random message
AddGadgetItem(g_list_nr,-1,"sending "+message)
;
filemap_buffer_h = OpenFileMapping_(#FILE_MAP_WRITE,0,@filemap_buffer_name) ; open the filemapping object
lasterror = GetLastError_()
If filemap_buffer_h = #INVALID_HANDLE_VALUE Or filemap_buffer_h = 0 ; some error
AddGadgetItem(g_list_nr,-1,"error opening filemapping object")
CloseHandle_(mutex_busy_h)
error = #True
Else ; succes
;
mutex_busy_h = OpenMutex_(#MUTEX_ALL_ACCESS,0,@mutex_busy_name) ; try to open the mutex
If mutex_busy_h = 0 ; error
AddGadgetItem(g_list_nr,-1,"can't open busy mutex")
error = #True
Else
;
result = WaitForSingleObject_(mutex_busy_h,1000) ; try to own the mutex, wait max 1 sec
If result <> 0 ; eror
AddGadgetItem(g_list_nr,-1,"troubles owning the busy mutex")
error = #True
Else ; success
;
mapview_p = MapViewOfFile_(filemap_buffer_h,#FILE_MAP_WRITE,0,0,0) ; map the memory
*buffer.buffer = mapview_p
If *buffer\data_max-*buffer\data_size < Len(message)+1 ; make sure message still fits
AddGadgetItem(g_list_nr,-1,"buffer full")
Else
PokeS(*buffer+12+*buffer\data_size,message) ; write at next empty spot
*buffer\data_size = *buffer\data_size+Len(message)+1 ; update data_size counter
w_host_h = *buffer\w_host_h ; window id of the host
EndIf
UnmapViewOfFile_(mapview_p) ; no longer any need for view
ReleaseMutex_(mutex_busy_h) ; release the mutex
;
; we could also decide to wake up the host by sending it a message, uncomment the next line to do so,
; in which case every message is shown immediately
;
; PostMessage_(w_host_h,message_wakeup_id,0,0) ; wakeup the host
;
EndIf
EndIf
CloseHandle_(mutex_busy_h)
EndIf
;
EndSelect
EndSelect
;
Until event = #PB_Event_CloseWindow
;
Else
;
; *** host
;
filemap_buffer_h = CreateFileMapping_($FFFFFFFF,0,#PAGE_READWRITE,0,buffer_size,@filemap_buffer_name)
lasterror = GetLastError_()
If filemap_buffer_h = #INVALID_HANDLE_VALUE Or filemap_buffer_h = 0 ; some error
AddGadgetItem(g_list_nr,-1,"error creating filemapping object")
CloseHandle_(mutex_busy_h)
error = #True
ElseIf lasterror = #ERROR_ALREADY_EXISTS ; if we are the host it can't exist yet
AddGadgetItem(g_list_nr,-1,"filemapping object already exists")
CloseHandle_(filemap_buffer_h)
CloseHandle_(mutex_busy_h)
error = #True
Else
;
; okay, so we are the host after all...
;
AddGadgetItem(g_list_nr,-1,"host!") ; so it's sure we are the host
SetWindowTitle(w_main_nr,"Host")
;
; any windows event will wake up the host and display any messages in its buffer, with a timer here we can
; tell the window to check every 10 seconds for messages, even if there are no other incoming events
;
; by deliberately sending the host window a message we will also trigger anything in the buffer to be dsiplayed
;
SetTimer_(w_main_h,1,10000,0)
;
mapview_p = MapViewOfFile_(filemap_buffer_h,#FILE_MAP_WRITE,0,0,0) ; map the memory
*buffer.buffer = mapview_p
*buffer\w_host_h = w_main_h
*buffer\data_max = data_max
*buffer\data_size = 0
;
ReleaseMutex_(mutex_busy_h) ; we're open for business
;
AddGadgetItem(g_list_nr,-1,"waiting...") ; so it's sure we are the host
Repeat ; wait for messages
event = WaitWindowEvent()
event_gadget = EventGadgetID()
Select event
Case #PB_Event_Gadget
Select event_gadget
Case g_exit_nr ; exit button was pushed
event = #PB_Event_CloseWindow
EndSelect
Case message_wakeup_id
AddGadgetItem(g_list_nr,-1,"received wakeup message")
EndSelect
;
If *buffer\data_size >0 ; check buffer change on every message
result = WaitForSingleObject_(mutex_busy_h,1000)
If result <> 0 ; eror
AddGadgetItem(g_list_nr,-1,"troubles owning the busy mutex")
error = #True
Else ; success
p = 0
While p < *buffer\data_size
message.s =PeekS(*buffer+12+p) ; read message
AddGadgetItem(g_list_nr,-1,message)
p = p+Len(message)+1 ; look for the next message
Wend
*buffer\data_size = 0 ; we emptied the buffer
EndIf
ReleaseMutex_(mutex_busy_h) ; and release the mutex
EndIf
;
Until event = #PB_Event_CloseWindow
;
UnmapViewOfFile_(mapview_p) ; we're done, clean up
CloseHandle_(filemap_buffer_h)
CloseHandle_(mutex_busy_h)
;
EndIf
EndIf
;