Page 1 of 2

Using Win32 functions GlobalFindAtom and GlobalAddAtom

Posted: Thu Jul 31, 2003 2:20 am
by kwag
I am trying to use the functions GlobalFindAtom_ and GlobalAddAtom_
I can't get GlobalFindAtom_ to work, no matter what I try :!:
Are these Win32 functions "non-functional" in PureBasic :?:
I believe the introduction mentions "The Win32 API is fully supported"
So how can these functions be used :?:
I am trying to use them, so I can detect an instance of the same application in memory, and avoid running it more than once.

Any help with this appreciated,
-Karl

Posted: Thu Jul 31, 2003 2:33 am
by newbie
In the FAQ there is this solution :

viewtopic.php?p=24945

may be it can help.

Posted: Thu Jul 31, 2003 3:04 am
by kwag
Thanks newbie :mrgreen:
Working like charm :cool:

Code: Select all

; These two lines prevent this app from being run more than once. 
; You must put these lines at the start of your app's code (obviously). 
MutexID=CreateMutex_(0,1,"YourAppName") : MutexError=GetLastError_() 
If MutexID=0 Or MutexError<>0 : ReleaseMutex_(MutexID) : CloseHandle_(MutexID) : End : EndIf 
; 
; Your app's code now goes here, until quitting time, which executes the next line. 
; 
CloseHandle_(MutexID) : End 
But I still wonder why I can't use those Win32 functions :roll:

-Karl

Posted: Thu Jul 31, 2003 4:53 am
by Paul
Atom functions work find here...

Code: Select all

Atom=GlobalAddAtom_("Test")
Debug Atom
 
 
Buffer$=Space(256)
GlobalGetAtomName_(Atom,@Buffer$,Len(Buffer$))
Debug PeekS(Buffer$)
 
 
FindAtom=GlobalFindAtom_("Test")
If FindAtom=Atom
  Debug "Found Atom"
EndIf
But for what you want to do, you want to use Mutex (as mentioned above) and not Atom.

Posted: Thu Jul 31, 2003 5:18 am
by kwag
Hi Paul,

I wasn't comparing the result of GlobalAddAtom and GlobalFindAtom :oops:
I was comparing the result of GlobalFindAtom to 0 or a value, assuming true or false return :oops: :twisted:
The Atom solution is more elegant than the Mutex solution ;)

Thanhs :D
-Karl

Posted: Tue Jul 27, 2004 11:11 am
by Randy Walker
kwag wrote: The Atom solution is more elegant than the Mutex solution ;)
Thanhs :D
-Karl
Hi Paul/Karl,

Pardon me. I'm a novice/newcomer here and this is an old topic, but I have to agree with Karl. I used a method similar to the mutex approach and gave that up because global atoms can not only help manage instance control, they can also be used as flags allowing two or more programs to communicate.

The approach is not only simple (keep in mind I am a part time novice), it is far more simple than messing with DDE client/server -IF- your needs are not so complex. Currently I'm juggling about 20 different global atoms between four different programs to manage various tasks. This method is also MS safe, but be sure to proof out your control logic and add necessary code so one program doesn't make your other program get out of sync, crash, etc.

I just got here and waiting for the passord on my purchase so I can start translating code. When I do, I will come back and post some examples here if anyone is interested.

BTW Paul, Thanks for the example.

..

Posted: Tue Jul 27, 2004 5:05 pm
by NoahPhense
Here is my lib.. only has two functions:
Download here: mutex_lib.zip

As for using the win32 stuff here it is..

Code: Select all

; mutex example
MutexID = CreateMutex_(0, 1, "whatever you want - usually app name")
MutexError = GetLastError_()
If MutexID = 0 Or MutexError <> 0
  ReleaseMutex_(MutexID)
  CloseHandle_(MutexID)
  End
EndIf


OpenConsole()

PrintN("press a key")
Repeat:Delay(1):Until Inkey()<>""

CloseConsole()



ReleaseMutex_(MutexID)
CloseHandle_(MutexID)

End
- np

Posted: Mon Aug 23, 2004 8:02 am
by Randy Walker
Read the first post after this code for instructions before you run these sample programs. This is program 1 of 2:

Code: Select all

;This section MUST go at very start of your program!
;
; ------ "Prevent multiple instances" ------ Prg1
Global GA101$,GA201$,GA202$,GA203$,GA204$
GA101$="flag101"+Chr(0) ;Progarm 1 Active
GA202$="flag202"+Chr(0) ;Program 2 downloading
GA203$="flag203"+Chr(0) ;Program 2 processing data
GA204$="flag204"+Chr(0) ;Update installation request
;Debug "TESTING FOR ATOM"
Atom = GlobalFindAtom_(GA101$)  ; Compare Low Word Only
;Debug Hex(Atom & $FFFF)  ;  Absent=0  ;  Present>0
If Atom & $FFFF ;Terminate here if "flag101" atom exists
  Atom=GlobalDeleteAtom_(Atom) 
  ;Debug "Prg1 - Abort this instance!" 
  End       ; Only 1 Instance Allowed!!!
Else
  Atom = GlobalAddAtom_(GA101$) 
EndIf 
;
;      ------ "Resume Normal Code" ------ Prg1
;     (Initialize your variables, windows, etc.)
;
;
If OpenWindow(0, 300,300, 600, 240, #PB_Window_SystemMenu, "")
  If CreateGadgetList(WindowID()) 
    WstatusLine=TextGadget(0,10,220,220,25,"Watch here and run Prg2") 
    ;
    ; First thing before entering main loop - Add Prg1 flag!
    ; Always test first before "Adding" Atoms!!
    Atom = GlobalFindAtom_(GA101$)
    If (Atom & $FFFF) = 0    ; KEEP "flag101" up!!!
      Atom = GlobalAddAtom_(GA101$) 
    EndIf
    ;
    Repeat; ------ "Main Event Handling loop" ------ Prg1
      ; Use Prg2 atoms as flags to update the status bar here.
      Atom = GlobalFindAtom_(GA202$)    ;Check for Pgr2 "flag202"
      If Atom & $FFFF
        SetGadgetText(0,"Downloading new data.") ; Respond to "flag202" atom
        While Atom & $FFFF
          Atom=GlobalDeleteAtom_(Atom) 
          Atom = GlobalFindAtom_(GA202$)
        Wend 
      EndIf
      Atom = GlobalFindAtom_(GA203$)    ;Check for Pgr2 "flag203"
      If Atom & $FFFF
        SetGadgetText(0,"Processing new data.") ; Respond to "flag203" atom
        While Atom & $FFFF
          Atom=GlobalDeleteAtom_(Atom) 
          Atom = GlobalFindAtom_(GA203$)
        Wend 
      EndIf
      Atom = GlobalFindAtom_(GA204$)    ;Check for Pgr2 "flag204"
      If Atom & $FFFF
        SetGadgetText(0,"Updating installation - Please wait...") ; Respond to "flag204" atom
        While Atom & $FFFF
          Atom=GlobalDeleteAtom_(Atom) 
          Atom = GlobalFindAtom_(GA204$)
        Wend
        Delay(4000) ; (delay for demo only)
        Break  ; optional exit for demo purposes
      EndIf
      ; -----------------------------------------------
      ;
      Event = WindowEvent()
      ;         (Enter your own event checking here)
      ;
      Delay(20)
      ; -----------------------------------------------
    Until Event = #PB_Event_CloseWindow
  EndIf
EndIf
;
;
; ------ "Cleanup for program termination" ------ Prg1
;
;           (Your main clean up here)
; 
;Debug "DELETING ATOM"
Atom = GlobalFindAtom_(GA101$) ; Always test first!!
;Debug Hex(Atom & $FFFF)
If Atom & $FFFF   ;Terminate here if "flag101" atom exists
  Atom=GlobalDeleteAtom_(Atom) 
  ;Debug "Terminate here" 
EndIf 
;
CloseWindow(0)
End  ; Cleanup complete ; close Prg1

Posted: Mon Aug 23, 2004 8:04 am
by Randy Walker
This code must be use with code in previous post. Read the first post after this code for instructions before you run these sample programs. This is program 2 of 2

Code: Select all

;This first section MUST go at very start of program (Prg2)!
;
; ------ "Prevent multiple instances" ------ Prg2
Global GA101$,GA201$,GA202$,GA203$,GA204$
GA101$="flag201"+Chr(0)    ;Prg1 Active
GA201$="flag201"+Chr(0)    ;Prg2 Active
GA202$="flag202"+Chr(0)    ;Prg2 downloading
GA203$="flag203"+Chr(0)    ;Prg2 processing data
GA204$="flag204"+Chr(0)    ;Prg2 updating
;Debug "TESTING FOR Prg2 ATOM"
Atom = GlobalFindAtom_(GA201$)  ; Compare Low Word ONLY!!
;Debug Hex(Atom & $FFFF)  ;  Absent=0  ;  Present>0
If Atom & $FFFF   ;Terminate here if "flag101" atom exists
  ;Debug "Prg2 - Abort this instance!" 
  End ; Only 1 Instance Allowed!!!
Else              ;Otherwise, raise "flag201" immediately
  Atom = GlobalAddAtom_(GA201$)
EndIf 
; ----------------------------------------------------
;
;
;        ------ Resume Main Code -----   Prg2
;     (Initialize your variables, windows, etc.)
;
;
; ----------------------------------------------------
;      ------ "Atoms Signal Prg1 with Prg2 Progress -----   Prg2
;Differnt atoms used to signal different stages of Prg2 progress.
Atom = GlobalFindAtom_(GA202$)  ;Test for "flag202" atom
If (Atom & $FFFF) = 0
  Atom = GlobalAddAtom_(GA202$) ; Downloading - Notify Prg1
  Debug "one"
EndIf
;           "perform download" (sample task)
;
;
Delay(4000) ; (simulates processing time)
;
Atom = GlobalFindAtom_(GA203$)  ;Test for "flag203" atom
If (Atom & $FFFF) = 0
  Atom = GlobalAddAtom_(GA203$) ; Processing - Notify Prg1
  Debug "two"
EndIf
;           "perform processing" (sample task)
;
;
Delay(4000) ; (simulates processing time)
;
Atom = GlobalFindAtom_(GA204$)  ;Test for "flag204" atom
If (Atom & $FFFF) = 0
  Atom = GlobalAddAtom_(GA204$) ; Updating - Notify Prg1
  Debug "three"
EndIf
;             "perform update" (sample task)
;
;
Delay(4000) ; (simulates processing time)
; ----------------------------------------------------
;
;
;
; ------ "Cleanup for program termination" ------ Prg2
; Do whatever cleanup you need and then delete Prg2 Atom last.
Atom = GlobalFindAtom_(GA201$)
While Atom & $FFFF
  Atom=GlobalDeleteAtom_(Atom) 
  Atom = GlobalFindAtom_(GA201$)
Wend 
;
Debug "done"
End  ; Cleanup complete ; Terminate Prg2

Posted: Mon Aug 23, 2004 8:05 am
by Randy Walker
The two sample programs above are designed to work together, but compile and run them separately.
Prg1 represents the main program. (RUN THIS PROGRAM FIRST)
Prg2 signals it's progress to Prg1 using global atoms.

Several things to note here:

1. Additional code at the top of each sample insures only one instance for each program.

2. I terminate my atom string using a null byte according to MS spec:
GA$ = "flag101" + Chr(0)
and I also use global variables so I can control atoms inside procedures.

3. Using global atoms as flags really requires only 3 instructions.
Atom = GlobalFindAtom_(GA$)
Atom = GlobalAddAtom_(GA$)
Atom = GlobalDeleteAtom_(Atom)

4. Atoms can accumalate so extra code has been added to prevent program failures.

5. These demo programs only illustrate how a text field can be updated by another program. The same strategy can be used to control program execution between the two (or more) programs. The actual intention for atoms is beyond me, but I've learned I can use them as glogal flags to allow my apps to negotiate fixed tasks and messaging. Global atoms can be seen system wide by any app that knows where to look. Its your code so you know where to look.

Posted: Mon Aug 23, 2004 12:11 pm
by blueznl
randy, strings always terminate with a null in purebasic

Posted: Mon Aug 23, 2004 5:27 pm
by Henrik
Cool exambles and nice info, thanx Randy 8)

Best regards
Henrik.

Re: Using Win32 functions GlobalFindAtom and GlobalAddAtom

Posted: Mon Aug 23, 2004 9:45 pm
by Randy Walker
kwag wrote:I can't get GlobalFindAtom_ to work, no matter what I try :!:
Are these Win32 functions "non-functional" in PureBasic :?:
@kwag
It is very important to note: You must mask the return for each global atom function with $FFFF to compare only the low word. I stumbled on this one myself because my old Basic masked for me. I also strongly suggest extra code to prevent atoms from accumalating... say from a program crash that is unrelated to your atom manipulations.

@Bluenzl
Do you mean PureBasic "automatically" appends a null byte to strings? I got the impression it did, but wasn't sure.

@Henrick
Thanks! Threads were unavailable to me before so my alternative was multiple apps working together in the background. Threads don't look like they would require atoms and I see advantages in both strategies. For the moment I will stick to mutiple apps.

Posted: Mon Aug 23, 2004 10:00 pm
by GreenGiant
Randy, I think if you're communicating between two applications then user defined messages are normally the way to go. For one thing this means you are only processing the messages, not constantly checking atom states. It'd also removes synchronisation issues I think. Anyway, I've wirtten a couple of little example programs to show you what I mean. Listening program:

Code: Select all

Global usermessage.l

Procedure Callback(WindowID, Message, wParam, lParam)
  Result = #PB_ProcessPureBasicEvents
  If Message=usermessage
    SetGadgetText(1,"Button "+Str(wParam))
  EndIf
  
  ProcedureReturn Result
EndProcedure

OpenWindow(0,0,0,200,70,#PB_Window_ScreenCentered | #PB_Window_SystemMenu,"Listening program")
CreateGadgetList(WindowID(0))
Frame3DGadget(0,5,5,190,60,"Last pressed button:")
StringGadget(1,10,20,180,40,"No buttons pressed yet",#PB_String_ReadOnly | #PB_String_Multiline | #ES_AUTOVSCROLL | #WS_VSCROLL | #ESB_DISABLE_LEFT| #ESB_DISABLE_RIGHT)

usermessage=RegisterWindowMessage_("comm")

SetWindowCallback(@Callback())

Repeat
ev=WaitWindowEvent()
  If ev=#PB_Event_CloseWindow
    quit=#True
  EndIf
  
Until Quit=#True
And then a 'speaking' program.

Code: Select all

OpenWindow(0,0,0,160,60,#PB_Window_ScreenCentered | #PB_Window_SystemMenu,"Speaking program")
CreateGadgetList(WindowID(0))
ButtonGadget(1,10,10,40,40,"1")
ButtonGadget(2,60,10,40,40,"2")
ButtonGadget(3,110,10,40,40,"3")

usermessage=RegisterWindowMessage_("comm")

Repeat
ev=WaitWindowEvent()
  If ev=#PB_Event_CloseWindow
    quit=#True
  EndIf
  
  If ev=#PB_Event_Gadget
    gad=EventGadgetID()
    listener=FindWindow_(0,"Listening program")
    If listener
      SendMessage_(listener,usermessage,gad,0)
    EndIf 
  EndIf
  
Until Quit=#True
Using this method you can safely communicate between as many programs as you like. Hope this is of some use to someone.

Edit: In case anyone's wondering why I need all those flags on the string gadget, I dont. I just started off with some other code that I adapted for this example.

Posted: Tue Aug 24, 2004 12:10 am
by Randy Walker
GreenGiant wrote:Randy, I think if you're communicating between two applications then user defined messages are normally the way to go.
This is an excellent alternative!... if it works. I will have to test it out further. I noticed you are using FindWindow, which I found to be unreliable in some vesions of Windows, and the reason I say... if it works. Could be FindWindow failed on me previously because of a deficiency in my old Basic... don't know. Atoms always work reliably though. One guru friend suggested that FindWindow "is" unreliable and so EnumerateWindows should be used for this operation.

Also, I can't see that it applies here exactly, but MS warns against one app messaging directly to another app as it could interfere with normal message handling.