Mailslots and co

Share your advanced PureBasic knowledge/code with the community.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Mailslots and co

Post by blueznl »

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

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
Last edited by blueznl on Tue Jun 12, 2007 3:36 pm, edited 1 time in total.
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

just to say it's pb3.94 code.
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

You're right. When pasting the code I missed the first line:

Code: Select all

; purebasic survival guide - pb3.94
If you look around you may notice that I try to add that line every time. Sometimes I forget :-)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Dare
Addict
Addict
Posts: 1965
Joined: Mon May 29, 2006 1:01 am
Location: Outback

Post by Dare »

Hi Flype and Blueznl

This looks like something that I can use to have computers on a LAN become aware of others on the LAN independent to (and also in conjunction with) using one as server.

Of the comps hooked in there are a handful that can almost always be guaranteed to be up and running at any given time. Their machine names are known.

I am thinking I can use these and mailslots as a way to propogate information to other PCs and get info from other PCs. Sort of an internal DNS service manager with fallback so that if any one of the majors is not up, another can take over the role.

I am wondering if you guys can give me any info or guidance that might assist with this. Also on potential security risks (eg, one of the, um, "lesser " or infrequent PCs gets virused or hacked - would doing something like this then increase the risk for the others?).



Just to put this in context, I am looking for a way to have a network always have a server for my LAN when just a handful of units are guaranteed to have, between them, at least one "online" at any time. Also a quick-ish way for other PC's to know which is the server (the mailslots can send info like machine names, IP addresses, etc, through the network and each PC can tap into this via mailslots. By my figuring, anyhow. :))


If there is another way (pipes? * gets nervous *) then perhaps you could clue me up on that as well.

Appreciate the efforts with this work on mailslots and thanks in advance for any info you can give me.
Dare2 cut down to size
dell_jockey
Enthusiast
Enthusiast
Posts: 767
Joined: Sat Jan 24, 2004 6:56 pm

Post by dell_jockey »

Please note that - apart from the payload of only a bit over 420 bytes - there is another disadvantage when using mailsots:
The underlying transmission method uses broadcasts, which in most properly configured networks are NOT routed.
So, use mailslots only if you're sure that all clients reside within a single network segment or if you're sure that broadcasts are routed between network segments, in which case the local network admin really deserves a thorough whipping! ;)
cheers,
dell_jockey
________
http://blog.forex-trading-ideas.com
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

I revisited the mailslot example...

Code: Select all

; purebasic survival guide - pb4.30
; mailslot_1.pb - 04.06.2009 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, we could also
; address whole groups:
;
;   \\<computername>\mailslot\<path>\<name>
;   \\<domainname>\mailslot\<path>\<name>
;   \\.\mailslot\myusers\peter
;   \\.\mailslot\myusers\jan
;   \\.\mailslot\myusers\*
;
; 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,"Interprocess communication sample",#PB_Window_TitleBar|#PB_Window_SystemMenu)
w_main_h = WindowID(w_main_nr)
g_list_nr = ListIconGadget(#PB_Any,10,10,WindowWidth(w_main_nr)-20,WindowHeight(w_main_nr)-60,"Log",2000)
g_exit_nr = ButtonGadget(#PB_Any,WindowWidth(w_main_nr)-70,WindowHeight(w_main_nr)-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? :-))
  ;
  ResizeWindow(w_main_nr,WindowX(w_main_nr)+WindowWidth(w_main_nr),WindowY(w_main_nr),#PB_Ignore,#PB_Ignore)                        ; make sure windows don't overlap :-)
  AddGadgetItem(g_list_nr,-1,"client!")
  SetWindowTitle(w_main_nr,"Client")
  g_send_nr = ButtonGadget(#PB_Any,WindowWidth(w_main_nr)-150,WindowHeight(w_main_nr)-40,60,30,"Send")
  ;
  AddGadgetItem(g_list_nr,-1,"press [send]...")                                     
  Repeat                                                                       ; wait for messages
    event = WaitWindowEvent()
    event_gadget = EventGadget()
    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 wonderful... yeah, right)
        ;
        write_bytes.l
        file_h = CreateFile_(@mailslot_name,#GENERIC_WRITE,#FILE_SHARE_READ,0,#OPEN_EXISTING,0,0)
        WriteFile_(file_h,@message,StringByteLength(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 for incoming messages...")             ; yeah, we know we are waiting...
    Repeat                                                                     ; wait for messages
      event = WaitWindowEvent(1024)
      event_gadget = EventGadget()
      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
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

But I thought why not wrap the winapi a little better so it's easier to use for others?

As usual, lots of bloat, but it shows how to do it... first how you can use it in your program, to make sure only a single instance is up and running, and pass on any command line parameters or something similar from later instances.

Looks big here, just fold the procedures with all the commands and you're fine :-)

Code: Select all

; purebasic survival guide - pb4.30
; mailslot_2.pb - 04.06.2009 ejn (blueznl)
; http://www.xs4all.nl/~bluez/datatalk/purebasic.htm
;
; - set of wrappers / procedures to simplify single instance and inter application communication
;

Procedure.i x_interapp_firstinstance(instancename.s,wakeupmessage.s="",mode.i=#PB_Ascii)
  Protected lasterror.i
  Global x_interapp_instancename.s, x_interapp_mutex_h.i, x_interapp_wakeupmessage.i, x_retval.i
  ;
  ; *** interapplication - try to take host role (first instance)
  ;
  ; in:      instancename.s        - name of instance
  ;          wakeupmessage.s       - message that will be registered with windows and broadcasted to all apps upon activity
  ;          mode.i                - unicode mode (not used, ignore)
  ; retval:  -2                    - error using / creating mutex
  ;          -1                    - another instance is already host
  ;          0                     - this instance is now host
  ;
  ; notes:
  ;
  ; - don't forget to call x_interapp_endfirstinstance() when done (to free up the mutex)
  ; - the 'wakeup' message should be as unique as possible
  ; - instancename.s will become part of the channel (mailslot) name, unless otherwise specified in x_interapp_sendstring()
  ; - regardless of being the first instance or not, the application will try to register the wakeupmessage
  ;
  ; i was actually considering calling this one 'take the bridge number one' :-)
  ;
  ;
  ; always register the broadcast message, if one was given
  ;
  If wakeupmessage <> ""
    x_interapp_wakeupmessage = RegisterWindowMessage_(wakeupmessage)
  Else
    x_interapp_wakeupmessage = -1
  EndIf
  ;
  ; try to create a mutex
  ;
  x_interapp_instancename = instancename
  x_interapp_mutex_h = CreateMutex_(0,#True,@instancename)
  lasterror = GetLastError_()
  ;
  If x_interapp_mutex_h = 0
    ;
    ; error creating mutex
    ;
    x_retval = -2
    ;
  ElseIf lasterror = #ERROR_ALREADY_EXISTS
    ;
    ; mutex already in use
    ;
    CloseHandle_(x_interapp_mutex_h)
    x_retval = -1
    ;
  Else
    ;
    x_retval = 0
    ;
  EndIf
  ;
  ProcedureReturn x_retval
EndProcedure

Procedure x_interapp_endfirstinstance()
  Global x_interapp_mutex_h.i
  ;
  ; *** interapplication - end host role
  ;
  If x_interapp_mutex_h <> 0
    ReleaseMutex_(x_interapp_mutex_h)
    CloseHandle_(x_interapp_mutex_h)
  EndIf
  ;
EndProcedure

Procedure x_interapp_wakeupfirstinstance()
  ;
  ; *** interapplication - send a broadcast message to all windows apps
  ;
  ; notes:
  ;
  ; - this would wake up any 'first instance' of the application, and can be used to speed up mailslot handling etc.
  ;
  If x_interapp_wakeupmessage > 0
    PostMessage_(#HWND_BROADCAST,x_interapp_wakeupmessage,0,0)
  EndIf
  ;
EndProcedure

Procedure.i x_interapp_openchannel(channelname.s,mode.i=#PB_Ascii)
  Global x_retval.i, x_interapp_instancename.s
  ;
  ; *** interapplication - open a channel (mailslot) for receiving messages
  ;
  ; in:      channelname.s         - name of channel to open, see notes
  ;          mode.i                - unicode mode (not used, ignore)
  ; retval:  .i = n                - channel number (mailslot handle)
  ;             = 0                - failed
  ; out:     x_retval.i            - as retval
  ;
  ; notes:
  ;
  ; - though i call them channels these are actually mailslots
  ; - unless the name of the channel starts with two backslashes it will use the name used in x_interapp_numberone() as part of the channel (mailslot) name
  ;
  ;
  ; 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, we could also
  ; address whole groups:
  ;
  ;   \\<computername>\mailslot\<path>\<name>
  ;   \\<domainname>\mailslot\<path>\<name>
  ;   \\.\mailslot\myusers\peter
  ;   \\.\mailslot\myusers\jan
  ;   \\.\mailslot\myusers\*
  ;
  ; yes, this is a brilliant stupid wrapper for a single winapi call :-)
  ;
  If Left(channelname,2) <> "\\"
    If x_interapp_instancename <> ""
      channelname = "\\.\mailslot\"+x_interapp_instancename+"\"+channelname
    Else
      channelname = "\\.\mailslot\"+channelname
    EndIf
  EndIf
  ;
  x_retval = CreateMailslot_(@channelname,0,0,0)                           ; create mailslot
  ;
  ProcedureReturn x_retval
EndProcedure

Procedure.i x_interapp_closechannel(channelhandle.i)
  ;
  ; *** interapplication - close a channel (mailslot)
  ;
  ; in:      channelhandle.i        - number of channel to close, as returned by x_interapp_openchannel()
  ; retval:  .i = 0                 - failed
  ;             <> 0                - success
  ;
  ProcedureReturn CloseHandle_(channelhandle)
EndProcedure

Procedure.i x_interapp_sendmessage(channelname.s,message.s,mode.i=#PB_Ascii)
  Protected file_h.i, message_p.i, message_l.i
  Global x_interapp_wakeupmessage.i
  ;
  ; *** interapplication - send a string to the specified channel (mailslot)
  ;
  ; in:     channelname.s          - name of the channel to use, see also x_interapp_openchannel() for format
  ;         message.s              - message to send
  ;         mode.i                 - unicode mode
  ; retval  .i = n                 - number of bytes written
  ;            = -1                - error
  ;
  ; writing using regular pb commands is possible, but reading needs winapi
  ;
  ;   CreateFile(1,mailslot_name)
  ;   WriteString(message)
  ;   CloseFile(1)
  ;
  ; complete channel (mailslot) name if needed
  ;
  If Left(channelname,2) <> "\\"
    If x_interapp_instancename <> ""
      channelname = "\\.\mailslot\"+x_interapp_instancename+"\"+channelname
    Else
      channelname = "\\.\mailslot\"+channelname
    EndIf
  EndIf
  ;
  ; i'm a bit surprised I need to specify #OPEN_EXISTING, but without it there's no go...
  ; (and the docs on (using)mailslots are outright wonderful... yeah, right!)
  ;
  file_h = CreateFile_(@channelname,#GENERIC_WRITE,#FILE_SHARE_READ,0,#OPEN_EXISTING,0,0)
  If file_h <= 0
    x_retval = -1
  Else
    ;
    ; turn the string into the requested format (unicode, asci, etc.) then write it out to the mailslot
    ;
    message_l = StringByteLength(message,mode)
    message_p = AllocateMemory(message_l+2)
    PokeS(message_p,message,message_l,mode)
    WriteFile_(file_h,message_p,message_l,@x_retval,0)
    CloseHandle_(file_h)
    FreeMemory(message_p)
  EndIf
  ;
  ; send out a broadcast message if requested, see x_interapp_firstinstance()
  ;
  If x_interapp_wakeupmessage > 0
    PostMessage_(#HWND_BROADCAST,x_interapp_wakeupmessage,0,0)
  EndIf
  ;
  ProcedureReturn x_retval
EndProcedure

Procedure.i x_interapp_getmessage(channelhandle.i,mode.i=#PB_Ascii)
  Protected message_l.i, message_number.i, message_p.i
  Global x_message.s, x_retval.i
  ;
  ; *** interapplication - try to read a string from the specified channel (mailslot)
  ;
  ; in:      channelhandle.i       - channel as returned by x_interapp_openchannel
  ;          mode.i                - unicode mode
  ; retval:  .i = n                - length in BYTES of retrieved message
  ;             = 0                - no messages
  ;             = -1               - error
  ; out:     x_retval.i            - as retval
  ;          x_message.s           - string received
  ;
  ;
  message_l = 0          ; size of message to read
  message_number = 0     ; number of messages in the queue (not used)
  x_retval = 0           ; size of message (in bytes) read, or error code
  ;
  If GetMailslotInfo_(channelhandle,0,@message_l,@message_numer,0) = 0
    ;
    ; error
    ;
    x_retval = -1
    x_message = ""
    ;
  ElseIf message_l <= 0
    ;
    ; nothing in there
    ;
    x_retval = 0
    x_message = ""
  Else
    ;
    ; read a message, convert from ascii, utf8 etc.
    ;
    message_p = AllocateMemory(message_l+2)
    ReadFile_(channelhandle,message_p,message_l,@x_retval,0)
    x_message = PeekS(message_p,x_retval,mode)
    FreeMemory(message_p)
    ;
  EndIf
  ;
  ProcedureReturn x_retval
EndProcedure


;
; *** main program
;
If x_interapp_firstinstance("interapp","wakeup") = 0
  ;
  ; we're the first instance, open up a channel for incoming messages
  ; in this case we'll open up a channel called 'command', other instances
  ; can send their messages to that channel; you can of course open up
  ; multiple channels if you want
  ;
  h = x_interapp_openchannel("command")
  ;
  OpenWindow(1,10,10,300,300,"Interapplication communication",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  ListViewGadget(1,10,10,280,280)
  AddGadgetItem(1,-1,"start another instance of this program")
  ;
  Repeat
    event = WaitWindowEvent(1024)
    If x_interapp_getmessage(h) > 0
      ;
      ; process any messages received via the mailslot here, for example command line parameters etc.
      ;
      AddGadgetItem(1,-1,x_message)
    EndIf
    ;
    ; instead of checking for any messages in the mailslot as above we could also opt for reacting to a broadcast message
    ;
    ;   If event = x_interapp_wakeupmessage
    ;   EndIf
    ;
  Until event = #PB_Event_CloseWindow
  CloseWindow(1)
  ;
  ; close channel and instance protection
  ;
  x_interapp_closechannel(h)
  x_interapp_endfirstinstance()
  ;
Else
  ;
  ; this is not the first instance, so pass on some information and quit
  ;
  x_interapp_sendmessage("command",FormatDate("%HH:%II:%SS",Date())+" hi there from another instance!")
  ;
EndIf
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
dell_jockey
Enthusiast
Enthusiast
Posts: 767
Joined: Sat Jan 24, 2004 6:56 pm

Post by dell_jockey »

Blueznl,

thank you for revisiting your mailslot code and publishing it here
cheers,
dell_jockey
________
http://blog.forex-trading-ideas.com
jassing
Addict
Addict
Posts: 1885
Joined: Wed Feb 17, 2010 12:00 am

Re: Mailslots and co

Post by jassing »

Old thread, but I had an app that I needed to update to x64 that used the mail slots.... unfortunately it seems to hang, not just the app, but the entire OS requiring a power down to reset...
Any ideas?
User avatar
JHPJHP
Addict
Addict
Posts: 2251
Joined: Sat Oct 09, 2010 3:47 am

Re: Mailslots and co

Post by JHPJHP »

Hi jassing,

After 3 hard-reboots and 1 rebuild of indexes (chkdsk). :lol:
- Windows 7 64-bit
- PureBasic 5.31 (x86) / (x64)

Add [ message_l = 2048 ] to the top of the [ Else ] otherwise [ message_l = 4294967295 ]:
- this should at least let you run the program successfully

Code: Select all

Procedure.i x_interapp_getmessage(channelhandle.i,mode.i=#PB_Ascii)
  Protected message_l.i, message_number.i, message_p.i
  Global x_message.s, x_retval.i
  ;
  ; *** interapplication - try to read a string from the specified channel (mailslot)
  ;
  ; in:      channelhandle.i       - channel as returned by x_interapp_openchannel
  ;          mode.i                - unicode mode
  ; retval:  .i = n                - length in BYTES of retrieved message
  ;             = 0                - no messages
  ;             = -1               - error
  ; out:     x_retval.i            - as retval
  ;          x_message.s           - string received
  ;
  ;
  message_l = 0          ; size of message to read
  message_number = 0     ; number of messages in the queue (not used)
  x_retval = 0           ; size of message (in bytes) read, or error code
  ;
  If GetMailslotInfo_(channelhandle,0,@message_l,@message_numer,0) = 0
    ;
    ; error
    ;
    x_retval = -1
    x_message = ""
    ;
  ElseIf message_l <= 0
    ;
    ; nothing in there
    ;
    x_retval = 0
    x_message = ""
  Else
    message_l = 2048
    ;
    ; read a message, convert from ascii, utf8 etc.
    ;
    message_p = AllocateMemory(message_l+2)
    ReadFile_(channelhandle,message_p,message_l,@x_retval,0)
    x_message = PeekS(message_p,x_retval,mode)
    FreeMemory(message_p)
    ;
  EndIf
  ;
  ProcedureReturn x_retval
EndProcedure

If you're not investing in yourself, you're falling behind.

My PureBasic StuffFREE STUFF, Scripts & Programs.
My PureBasic Forum ➤ Questions, Requests & Comments.
jassing
Addict
Addict
Posts: 1885
Joined: Wed Feb 17, 2010 12:00 am

Re: Mailslots and co

Post by jassing »

Wow. That was quick, and honestly, I thought for such an old thread it was lost cause...
Thanks for that, on both the confirmation of error & the fix!
you rebooted less than I did -- I was convienced that it was something else; took me hours to rotue it down to that running the original code 'as is' would do it... countless loss sessions & frustrations...
Really unfortunate that it wreaked such havoc...
Thanks!
-josh
Post Reply