Mailslots and co
Posted: Mon Jun 11, 2007 11:32 pm
After me fooling around with shared memory and mutexes Flype posted something about mailslots...
http://www.purebasic.fr/english/viewtopic.php?t=27513
...turned out to be a useful feature
I was planning to use shared memory to help me passing parameters between two instances...
http://www.purebasic.fr/english/viewtop ... 883#198883
... but this may work just as well. Again, the code looks very bloated, but that's because I put in all error handling and comments
http://www.purebasic.fr/english/viewtopic.php?t=27513
...turned out to be a useful feature

I was planning to use shared memory to help me passing parameters between two instances...
http://www.purebasic.fr/english/viewtop ... 883#198883
... but this may work just as well. Again, the code looks very bloated, but that's because I put in all error handling and comments
Code: Select all
; purebasic survival guide - pb3.94
; mailslot_1.pb - 12.06.2007 ejn (blueznl)
; http://www.xs4all.nl/~bluez/datatalk/purebasic.htm
;
; - single instancing, deduction host vs. client
; - sharing information between applications
; - RegisterWindowMessage_()
; - CreateMutex_()
; - ReleaseMutex_()
; - CreateMailSlot_()
; - GetMailSlotInfo_()
;
; see also filemapping_1.pb, filemapping_2.pb, mutex_1.pb, singleinstance_1.pb
; you DEFINITELY want to have the WIN32.HLP file open and at your side
;
;
; this pogram is similar to singleinstance_1.pb, but uses mailslots instead
; this is a smaller and easier approach to data exchange, but may prove to be somewhat slower (unconfirmed)
;
; mailslots can exist locally, or somewhere on the network, messages can have any size unless they are broadcasted
; onto a domain, in which case they are limited to 400 bytes
;
; (note: I ran into samples stating this could be 450 or 540, let's stick to 400 for safety reasons)
;
; the owner always creates a mailslot using the following format:
;
; \\.\mailslot\<path>\<name>
;
; another computer on the network could address this slot by using the computername or even the domainname:
;
; \\<computername>\<path>\<name>
; \\<domainname>\<path>\<name>
;
; some mutex stuff (just used to properly detect client vs. server)
;
#MUTEX_ALL_ACCESS = $1F0001
#WAIT_TIMEOUT = $102
#WAIT_ABANDONED = $80
#WAIT_FAILED = $FFFFFFFF
#WAIT_OBJECT_0 = $0
;
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_instance_name.s = "instance"
mailslot_name.s = "\\.\mailslot\mailslot" ; an awe inspiring choice of name :-)
;
; flag the instance mutex, if we cannot create it, or it is already owned, then we are not the host
;
mutex_instance_h = CreateMutex_(0,#True,@mutex_instance_name) ; create and try to be the owner
lasterror = GetLastError_()
If mutex_instance_h = 0
AddGadgetItem(g_list_nr,-1,"error opening instance mutex")
error = #True
ElseIf lasterror = #ERROR_ALREADY_EXISTS
AddGadgetItem(g_list_nr,-1,"instance mutex already exists")
CloseHandle_(mutex_instance_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)
;
; writing using regular pb commands
;
; CreateFile(1,mailslot_name) ; we need to use winapi to read the file via the mailslot
; WriteString(message) ; but we can write to it using regular pb
; CloseFile(1)
;
; writing using winapi
;
; I'm a bit surprised I need to specify #OPEN_EXISTING, but without it there's no go...
; (and the docs on mailslots are outright wonderfull... yeah, right)
;
write_bytes.l
file_h = CreateFile_(@mailslot_name,#GENERIC_WRITE,#FILE_SHARE_READ,0,#OPEN_EXISTING,0,0)
WriteFile_(file_h,@message,Len(message),@write_bytes,0)
CloseHandle_(file_h)
;
; if we want the host to respond immediately send it a message, by uncommenting the next line
;
; PostMessage_(#HWND_BROADCAST,message_wakeup_id,0,0) ; wakeup the host
;
EndSelect
EndSelect
;
Until event = #PB_Event_CloseWindow
;
Else
;
; *** host
;
; okay, so we are the host after all...
;
AddGadgetItem(g_list_nr,-1,"host!") ; yeah, we know 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 would also trigger anything in the buffer to be displayed)
;
SetTimer_(w_main_h,1,10000,0)
;
; CreateMailslot_( <mailslotname> , <maxsize> , <timeout> , <security_attributes> )
;
mailslot_h = CreateMailslot_(@mailslot_name,0,0,0) ; create mailslot
If mailslot_h = 0
AddGadgetItem(g_list_nr,-1,"error creating mailslot") ; yeah, we know we are waiting...
error = #True
Else
AddGadgetItem(g_list_nr,-1,"waiting...") ; yeah, we know we are waiting...
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
;
; GetMailSlotInfo_() can unload some information, such as the size of the next message, and the number of remaining messages
;
; GetMailSlotInfo_( <mailslothandle> , <@max message size> , <@next message size> , <@number of messages > , <@read timeout> )
;
message_number.l = 0 ; used to store how many messages are left
message_size.l = 0 ; used to store the size of the next message
If GetMailslotInfo_(mailslot_h,0,@message_size,@message_number,0) = 0
AddGadgetItem(g_list_nr,-1,"error getting mailslot info")
error = #True
Else
While message_number > 0 ; for any messages found
message.s = Space(message_size) ; create a string to receive the message
bytes_read.l = 0 ; reserve a long to store the number of bytes read
ReadFile_(mailslot_h,@message,message_size,@bytes_read,0) ; read specified number of bytes from the mailslot handle into the string message.s
AddGadgetItem(g_list_nr,-1,message) ; show message
GetMailslotInfo_(mailslot_h,0,@message_size,@message_number,0) ; more to read?
Wend
EndIf
;
Until event = #PB_Event_CloseWindow
CloseHandle_(mailslot_h)
EndIf
;
ReleaseMutex_(mutex_instance_h)
CloseHandle_(mutex_instance_h)
;
EndIf