Alyce
Jun 18, 2007
- "Edited for spelling, grammar and clarity."
=Running a LB program from the System Tray.= ==Author : Duncan Bushaw== ==Published June 17, 2007.== ===__Introduction.__=== The exampleprogramprogram, 'TrayIcon.bas' (hereafter refered to as the program) uses BrentThorns' WMLiberty.dllThorn's WMLiberty.dll, which is included in this file [[file:TrayIcon.CAB]]. The source code for BrentThorns'Thorn's WMLiberty.dll is available@at Bay 6 Software@at [[http://www.b6sw.com/|http://www.b6sw.com/.]] A full working package of the program can be downloaded from my domain@at [[http://duncanb.nh-networks.com/|http://duncanb.nh-networks.com/.]] For the purpose of this article a windowless control is any type of control that does not necessarily have to be assigned to a active window (ie; Liberty BASIC controls such as NOTICE or POPUPMENU;windowsWindows API controls such as ShellAboutA or MessageBoxExA), and a Window is any window created using the Liberty BASIC 'OPEN' command. Any standard approach towrittingwriting Liberty BASIC code to place a program in the system tray only seems to work for very basic programsthat have justcontaining only a popup menu. At some point inwrittingwriting more complex code the program starts togetgenerate variouserror while testing it.errors during testing. The most common errors I see and hear about are the 'Invalid branch label', 'stack overflow', and 'popup menu already active' errors. The errors have to do with the CALLBACK function TrayIcon() [This is the CALLBACK function that istrippedinvoked by the CALLDLL commandwhich uses the WMLiberty.dll that I used in(using theprogram]WMLiberty.dll) which is called whenever the mouse (cursor) moves over the icon on the systemtray thistray. This causes the program to look only at the codein the programroutine that is containedwithin the TrayIcon() CALLBACK funtion. I used the GLOBAL variable (TrayIconPointer) to remind the program what it should be doing whenever the TrayIcon() CALLBACK function istripped.invoked. The program uses many Liberty BASIC commands and a few API controls, and it shows one technique that seems to encompass most controls andwindows withwindows. It has 2 Liberty BASIC windows opened and active at onetime and enabledtime, without any of the previously mentioned errors occurring. The technique used in this program is not the only way of writing a program in Liberty BASIC which can run on the systemtraytray, but I find itIto be a useful technique that seems to encompass all possible controls whether Liberty BASIC native commands or windows APIcalls .calls. To do this I had to 'Think Outside Of The Box' when using function callsI createdin theprogram and oneprogram. You will see a lot of WAIT commands, which in itself is notunexpected, but theunexpected. The difference is that when the TrayIcon() CALLBACK function is called and a window is then opened usingitit, the program does not exit the function which is called to open thewindowwindow, whereas when the function which opens the window is called directly by the program and not through the TrayIcon() CALLBACKfunction infunction, it must be exited properly for the program to continue working correctly. Think of it thisway, onlyway. Only when a windowless control such as a notice message, has beenactivatedactivated, will the program exit the TrayIcon() CALLBACKfunction and iffunction. If any window orno windowlessregular control has been activated the program will encounter the Liberty BASIC 'WAIT'command andcommand. It will then only refer to the code in TrayIcon() CALLBACKfunction,function if no windows are active. If the 'Test Window' is active the program will call the Testwindow() function and then only refer to the code in this function. The same idea happenswithwhen the dialog windowactiveis active, but it will call the Retrieve()functionfunction, which contains duplicated code from the TestWindow()functionfunction, so both windows can be enabed and all controlsworking.can work. When the program closes the 'Test window' and the TrayIcon() CALLBACK function isactiveactive, the program must encounter a 'WAIT' command and not exit the Testwindow()function, iffunction. If the TrayIcon() CALLBACK function is not active the program must exit the Testwindow() function. Toshow thisdemonstrate this, the Testwindow() function can be called twowaysways; before or after the TrayIcon() CALLBACK command is initiated. [[code]] IF INSTR(CommandLine$,"-TestWindow")>0 then OP=1:null=TestWindow() [[code]] The above line of code is why the TKN file and the runtime engine must be in the same folder/directory as the BASfile as itfile. This is how themenuitemmenu item 'Test window on Restart' opens the window before putting the icon on the system tray. If the CommandLine$ contains the string "-TestWindow" init thenit, the program sets the GLOBAL variable (OP) to the value of 1 and then calls the TestWindow()functionfunction, which will then open the Test Window and the program will wait and update the clock in the GRAPHICBOX control every second using the TIMER command. All branch labels for the window are contained within the function code itself and when the close button on the title bar is clicked or (ALT-F4) is pressed the program looks for the branch lable [quit.WindowIcon] within the TestWindow() function and turns off the TIMER, closes the window, sets the TrayIconPointer to the value of Zero, and then checks the (OP) variable. If it is 0 then the program does notexitsexit thefunction butfunction. Instead it waits andmust do soexits when called from the TrayIcon() CALLBACK function to prevent errors from happening. However if the dialog window has also been opened then the branch label [quit.WindowIcon] is looked for in the Retrieve() function as that is the active function at that time. ===__TrayIcon.bas the program.__=== The Liberty BASIC runtime engine (run403.exe) copied and renamed as TrayIcon.exe and the TKN (tokenized BAS file) must be in the same dir./folder as TrayIcon.bas for all the program features to work without errors. A full working package can be downloaded from my domain@at [[http://duncanb.nh-networks.com/|http://duncanb.nh-networks.com/.]] The programstarts upbegins by extracting the icon from TrayIcon.exe and puts this icon on the systemtray, fromtray. From here there are 2 popup menus available. Double clicking the left mouse button calls1one of the popup menus andwitha single click of the right mouse button causes a popup menuwillto appear when the button is released. It is best not to make a popup menu appear when a mouse button is pressed down but when the mouse button isreleased, thisreleased. This allows any other popup menus thatmaybemay be active to close before the new popup menu is displayed. The left button menu has 4menutiemsmenu items which are 'About', 'Test window', 'NOTICE', and 'Help'. The right button menu has 4menutiemsmenu items which are 'Test window', 'Test window on Restart', 'About', and 'Exit'. The 'About'menuitemsmenu item displays thesystemssystem's about dialog window. The 'NOTICE'menuitemmenu item uses the native LB 'NOTICE' command. The 'Help'menuitemmenu item opens thesystemssystem's default browser and goes to this LBPE web site. The 'Exit'menuitemmenu item ends the program. The 'Test window on Restart' starts another instance of the program, which will show the Test window before putting the icon on the system tray, and then ends the first instance of the program.YOUYou must have a compiled (TKN) file in the same directory as TrayIcon.bas, and have a copy of the runtime engine (run403.exe) named TrayIcon.exe for this to work. The 'Test window' will open the Test window which is of a window_nf type, and the tray icon will have a different popup menu available with the right mouse button. The left mouse button is disabled while the Test window is open. ===The Test window.=== Is of a window_nf type. I have included many native Liberty BASIC controls in the TestWindow, theyWindow. They are BUTTON, CHECKBOX, COMBOBOX, GRAPHICBOX, GROUPBOX, LISTBOX, MENU, NOTICE, POPUPMENU, RADIOBUTTON, STATICTEXT, TEXTBOX, TEXTEDITOR, and TIMER. Selecting a CHECKBOX, COMBOBOX, LISTBOX, or RADIOBUTTON item causes a NOTICE windowwillto appear displaying the selected item or state of control (set/reset). The GRAPHICBOX is used to display a clock which I ported from 'clasic Face clock.bas' (see this article of mine [[A True API Popup Menu - By Duncan B|http://lbpe.wikispaces.com/A+True+API+Popup+Menu+-+By+Duncan+B]]) which uses the TIMER control to update the clock every second. The 'Retrieve Text' button will retrieve anything in the TEXTBOX and display it in a NOTICE window. The 'Retrieve All Items' button will cause a POPUPMENU to appear and the user can select which window the dialog_nf_modal window will be attached to (the hidden window or the Test Window). After selecting the window to attach the dialog_nf_modal window to the program will then open the dlalog_nf_modal window with only a TEXTEDITOR control in the window (neat no menu bar in this window) and then retrieve the current state of all CHECKBOXES and RADIOBUTTONS (set/reset), the current selected LISTBOX and COMBOBOX items (if any), the contents of the COMBOBOX, TEXTBOX, and TEXTEDITOR (if any), and then display them in the TEXTEDITOR in the dialog_nf_modal window. ===__The 'Retrieved All Items' window.__=== Is of a dialog_nf_modal type. When this window is opened the tray icon will have a different popupmenu again (right click only) and the clock in the GRAPHICBOX in the 'Test Window' will continue to be updated every second. If this window is attached to the 'Test Window' then the 'Test Window' will be disabled but will notlook like it isappear to be disabled, as the program makes the 'Test Window' the foreground window even though the dialog_nf_modal window is on top. Clicking on the 'Test Window' with the mouse will cause it to gray and make the dialog_nf_modal window the foreground window. If this window is attached to the hidden window then the 'Test Window' will not be disabled and will be set as the foreground window putting it on top of the dialog_nf_modal window. Moving the mouse over (do not click) the tray icon the 'Retrieved All Items' window will be set as the foreground window putting it on top of the 'Test Window'. I did this to show that the CALLBACK function will be tripped just by moving the mouse over the tray icon. The 'Test Window' will remain enabled and all the controls will still work. ===__The TrayIconpointer.__=== This variable works best if it is set before any windows or windowless controls have been opened or are made active . The TrayIconpointer variable has 4 states: * [-1] - This value tells the program that a windowless contol is active and to exit the Traylcon() CALLBACK function right away. * [0] - This value tells the program that there are no windowless controls or windows active and causes the program to wait inside the Traylcon() CALLBACK function. * [1] - This value tells the program that the Test window is active and causes the program to jump to the Testwindow() function and to wait inside the function. * [2] - This value tells the program that the dialog window is active and causes the program to jump to the Retrieve() function and to wait inside the function. I hope that this helps those frustrated LB programmers who have tinkered with the WMLiberty.dll, I spent many hours trying different codes and this solution seems so obvious to me now. Enjoy!