Tips-'N'-Tricks
How can I define a task so that it is not displayed in Start Task and Edit menus?
Name a task this way: CLASSIC-TASK-#-õõõ.
#( CLASSIC-TASK-#-xxx NoActive Action: MSG: "CLASSIC-TASK-#-xxx" )#
Instead of "õõõ", you can put any characters except for blank spaces (e.g. CLASSIC-TASK-#-ABCD, CLASSIC-TASK-#-123456 or CLASSIC-TASK-#-my_task). To start such a task manually, execute from a command prompt:
nncron -run CLASSIC-TASK-#-my_task
Note, please: there is no records about execution of the CLASSIC-TASK-#-õõõ tasks in nnCron log file. If you need such a record in your log file - just use the words LOG: or CRON-LOG in the task body.
How can I pass arguments from command line to a task?
It can be done if you start a task with key -runfile:
nncron.exe -runfile <file_name>
Everything that follows the file name in command line is treated as an arguments and can be read by Forth construct BL WORD (blank space is used as a delimiter). Here is an example which displays a consecutive series of message boxes with all found arguments of command line:
\ ---begin of args.spf---- : main BEGIN BL WORD COUNT ?DUP \ Blank space as delimiter \ BL SKIP [CHAR] " SKIP [CHAR] " WORD COUNT ?DUP \ Skipping blank spaces, quote as delimiter WHILE MsgBox REPEAT DROP ; \ ---end of args.spf--- \ start: nncron.exe -wp -runfile args.spf one two three four
Besides, nnCron has word get-string, which performs all the "menial" work when parsing an argument: if a string is surrounded by quotation marks, then everything inside the quotation marks is treated as an argument; otherwise, only that part of the string which precedes the first encountered blank space will be considered an argument.
Example:
\ ---begin of args1.spf--- CREATE str1 256 ALLOT : main get-string str1 PLACE MSG: "%str1 COUNT%" ; \ ---end of args1.spf--- \ start: \ nncron.exe -wp -runfile args1.spf "Very long argument in quotes"
How can I count weeks, e.g. start a task only on the second Monday of every month or on the third Sunday of December?
\ here is 2nd Monday of each January (08:00) Time: 0 8 8-14 Jan Mon * \ and here is 3rd Sunday of each December (08:00) Time: 0 8 15-21 Dec Sun *
The same trick can be also used for the first (1-7) and fourth (22-28) week, but it won't work for the last week of the month. Here you should invent something more complicated. But the value of the day of the month is easily accessible (Day@), and so is the value of the day of the week (WDay@). So you can get as complicated as you wishand please also keep in mind word OnceAWeek.
How can I find out the name of the task which is currently being executed and the name of the crontab file where this task is located?
Name of task:
%CUR-NODE CRON-NAME @ COUNT%
Name of crontab file:
%CUR-NODE CRON-FILENAME @ COUNT%
To get the task name from the task itself, use word CUR-TASK-NAME ( -- a u ).
How can I display a message box with a specified title and message?
This is rather simple:
0 S" Title" DROP S" Message" DROP 0 MessageBoxA DROP
You can control message box icon and available message box buttons as well. Just use one of the following values instead of first zero when calling a message box:
0 MB_OK 1 MB_OKCANCEL 2 MB_ABORTRETRYIGNORE 3 MB_YESNOCANCEL 4 MB_YESNO 5 MB_RETRYCANCEL 6 MB_CANCELTRYAGAINCONTINUE 16 MB_ICONERROR 32 MB_ICONQUESTION 48 MB_ICONEXCLAMATION 64 MB_ICONINFORMATION 128 MB_USERICON 4096 MB_SYSTEMMODAL 8192 MB_TASKMODAL 16384 MB_HELP 32768 MB_NOFOCUS 262144 MB_TOPMOST 524288 MB_RIGHT 1048576 MB_RTLREADING
Examples:
\ displaying messagebox with error-icon: 16 S" Title" DROP S" Message" DROP 0 MessageBoxA DROP \ displaying messagebox with three buttons (Yes, No, Cancel): 3 S" Title" DROP S" Message" DROP 0 MessageBoxA DROP
MessageBoxA returns a numerical value which can be used to determine which button was pressed by the user. Here is the list of return values:
1 OK 2 CANCEL 3 ABORT 4 RETRY 5 IGNORE 6 YES 7 NO 10 TRY AGAIN 11 CONTINUE
Examples:
#( test_retrycancel NoActive Action: 5 S" test_retrycancel" DROP S" Click me!" DROP 0 MessageBoxA 4 = IF MSG: "Retry" ELSE MSG: "Cancel" THEN )# #( test_yesnocancel NoActive Action: 3 S" test_yesnocancel" DROP S" Click me!" DROP 0 MessageBoxA DUP 6 = IF DROP MSG: "Yes" ELSE 7 = IF MSG: "No" ELSE MSG: "Cancel" THEN THEN )#
How can I "tell" nnCron that a certain
task should be started only after startup/restart of my computer?
Solution of this problem consists of two simple steps:
1) We must make sure first that on each startup a special flag file is created. For example, we can place in Startup folder a link to a batch file containing the following line:
echo started > c:\started.sem
2) Now it only remains to write the task itself:
#( after_system_start WatchFile: "c:\started.sem" Action: FILE-DELETE: "c:\started.sem" \ after this, we perform all the required action )#
Voilà! Now this task will be started only after system startup. And, unlike the tasks which are started with the help of word START-TIME, our task will be started immediately after startup, without waiting for a new minute to begin.
Here is another method: word GetTickCount puts on stack the time that has passed since the system startup (in milliseconds). Therefore, we can check this time and if it is less then a minute or two, that would mean that the system has just started.
Example:
#( system_restart Time: START-TIME Rule: GetTickCount 90000 < Action: \ here we perform all the required actions )#
If a computer starts slowly, we might want to increase the length of time to which we compare GetTickCount (90000 - 150000).
How can I temporarily block mouse and keyboard input?
Here's how:
WINAPI: BlockInput USER32.DLL : BlockTheInput TRUE BlockInput DROP ; : UnblockTheInput FALSE BlockInput DROP ;
But this will only work in Windows 9*/2000/XP. Besides, it has been found that in Win9*, the above construct doesn't just block the user's activities, but also blocks key words SEND-KEYS: and (apparently) MOUSE* as well. Be careful!
Example:
#( test_hint WINAPI: BlockInput USER32.DLL : BlockInput TRUE BlockInput DROP ; : UnblockInput FALSE BlockInput DROP ; Action: BlockInput HINT: ">>>>> Warning! <<<<<%crlf%User input is blocked!%crlf%" PAUSE: 2000 START-APP: "Notepad" PAUSE: 1000 SEND-KEYS-DELAY: 200 500 WIN-SEND-KEYS: "*Notepad" "t{ENTER}te{ENTER}tes{ENTER}test" SEND-KEYS: "{ENTER}test{ENTER}tes{ENTER}te{ENTER}t" PAUSE: 1000 HINT-OFF UnblockInput THINT: "User input unblocked!" 3 )#
Please note also that the user input will be only blocked for the time or execution of the task which has called BlockTheInput. As soon as the task is completed, the user input is automatically unblocked.
How can I "instruct" nnCron to keep a single log file and not to create a new one every day?
To do this, you should change values of variables Cronlog and (optionally) LogTimeFormat in nncron.ini:
\ path of log Cronlog: "log\nncron.log" \ log time pattern LogTimeFormat: "%DD% %MMM% %hh%:%mm%:%ss% %ThreadId%"
Here is how information in the log will look:
11 Jul 13:25:04 436 Start nnCron 11 Jul 13:25:07 436 Load crontab 11 Jul 13:25:08 436 D:\TOOLS\NNCRON\nncron.tab 11 Jul 13:25:08 436 D:\TOOLS\NNCRON\vk.tab 11 Jul 13:25:08 436 D:\TOOLS\NNCRON\hotkey.tab 11 Jul 13:25:08 436 D:\TOOLS\NNCRON\test.tab 11 Jul 13:25:10 988 TASK: system_restart 11 Jul 13:25:35 508 TASK: hotkey_Ctrl+Alt+F 11 Jul 13:25:36 508 Start: d:\tools\far\far.exe 11 Jul 13:25:36 508 Start result: 0
By the way, the single log can be "cut down" from time to time with the help of word FILE-CROP:.
How can I stop a running task (e.g. in case of an error)?
Use word EXIT (it belongs to Forth kernel), which is used to exit the current word. It can be used because Action: also is a Forth word.
#( test_exit NoActive Action: \ ... doing something useful \ and checking for any errors: FILE-EXIST: "error.log" IF \ there is an error, \ deleting the file and stopping the task FILE-DELETE: "error.log" EXIT THEN \ ... if there are no errors, going on with our work )#
Attention, please: word EXIT cannot be used within DO ... LOOP construct. To exit both a loop and a task, use UNLOOP EXIT: construct
DO ... IF UNLOOP EXIT THEN ... LOOP
How can I stop a task from another task?
For example, this way (it will only work in Windows NT/2000/ÕÐ):
#( task1 VARIABLE t1-id Action: GetCurrentThreadId t1-id ! )#
#( task2 WINAPI: OpenThread KERNEL32.DLL Action: t1-id @ ?DUP IF 0 1 OpenThread ?DUP IF STOP THEN THEN )#
How can I make the task Active/NoActive programmatically?
From inside the current task:
\ activating CUR-NODE CF-ACTIVE SET-CRON-FLAG \ deactivating CUR-NODE CF-ACTIVE CLR-CRON-FLAG
From the another task:
\ activating <task_name> @ CF-ACTIVE SET-CRON-FLAG \ deactivating <task_name> @ CF-ACTIVE CLR-CRON-FLAG
Note, please: you are not changing the crontab physically, when programmatically setting or clearing NoActive task option. Thus, the task keeps it's new active/non-active status until you reload crontabs for the next time.
Using the following words you can check if the task is active or not:
\ checking if the task is active from the task itself CF-ACTIVE? IF \ ... THEN \ checking if the task is active from another task <task_name> @ CRON-FLAGS @ CF-ACTIVE AND? IF \ ... THEN
How can I intercept exceptions so that I can handle them myself?
nnCron provides a possibility to intercept any exception and decide afterwards what you are going to do with them.
This is how exceptions are intercepted :
['] word_name CATCH
['] looks up the following word in a dictionary (in compilation
mode) and compiles its address xt (execution token) into the definition
as a numeric literal.
CATCH takes xt from the stack, "remembers" state
of the stack (broadly speaking) and executes this xt. If an exception
arises while xt is being executed, it won't go anywhere beyond CATCH.
World CATCH returns 0 if there are no exceptions, and error
code if there was one.
Therefore, in a "real-life" situation this will look somewhat like this:
word_attributes ['] word_name CATCH ?DUP IF \ processing the error \ error code will be on top of the stack \ and below it there will be some garbage, \ depending on parameters THEN
Example:
S" from-path" S" to-path" ['] FCOPY CATCH ?DUP IF \ writing error code to 'nncron.out' and to the console ." Copy error # " . CR \ removing the garbage 2DROP 2DROP ELSE \ this branch doesn't have to exist, actually ." Copied successfully" CR THEN
You can only use CATCH directly with "postfix" words. If it is necessary to process an exception of a "prefix" word, you should first create a new postfix word based on this "prefix" word.
Example:
#( task1 : copy1 FILE-COPY: "path-from" "path-to" ; \ ... Action: ['] copy1 CATCH ?DUP IF \ ... )#
Sometimes, when a task is large and there are several operators that can throw an exception, the following trick can help: we can place the entire content of Action: section into a new word and use CATCH to "watch" it.
Example:
#( task2 NoActive : task-body \ placing the content of Action: section \ into a new word task-body \ ... ; Action: \ executing the new word and catching the mistakes at the same time ['] task-body CATCH ?DUP IF ." Error # " . CR THEN )#
How can I find out the meaning of error numbers that I see on Forth console, log file and in nncron.out?
If you want the errors to be displayed on console in user-friendly (verbose) format rather then as numerical codes (5, 2003, FILE ERROR # 3 etc.), download and extract to nnCron home directory the following file: http://www.nncron.ru/download/spf_err.rar (~24k). Here you can also find descriptions of errors recorded in log file and nncron.out (you can look them up by error number).
How can I change a string's encoding from WIN to OEM and vice versa?
nnCron's kernel contains definitions of "recoding" words OemToCharBuffA and CharToOemBuffA. Here is how you can use them to change encoding of a string:
#( test_recode NoActive \ defining "recoding" words : WIN2OEM ( a u -- a u ) 2DUP SWAP DUP CharToOemBuffA DROP ; : OEM2WIN ( a u -- a u ) 2DUP SWAP DUP OemToCharBuffA DROP ; \ creating a buffer to place a string \ whose encoding is going to be changed CREATE RECODE_BUFFER 256 ALLOT Action: S" a test string" RECODE_BUFFER PLACE \ writing text in OEM (ASCII) encoding: RECODE_BUFFER COUNT WIN2OEM S" c:\test_oem.txt" FWRITE \ writing text in WIN (ANSI) encoding: RECODE_BUFFER COUNT OEM2WIN S" c:\test_win.txt" FWRITE )#
Nicholas strongly recommends to copy a string to a buffer and change its encoding there (as in above example), instead of changing encoding right where the the string is stored.
How can I use nnCron to change attributes of a file?
Let's look up what Windows API says about this:
BOOL SetFileAttributes( LPCTSTR lpFileName, // file name DWORD dwFileAttributes // attributes );
That means, for example, that we can do the following :
#( test_file_attrib WINAPI: SetFileAttributesA Kernel32.dll NoActive Action: \ setting the "hidden file" attribute: 2 S" c:\temp\test.txt" DROP SetFileAttributesA DROP )#
Here is a list of constants (with their values) that can be used instead of dwFileAttributes:
FILE_ATTRIBUTE_READONLY 1 FILE_ATTRIBUTE_HIDDEN 2 FILE_ATTRIBUTE_SYSTEM 4 FILE_ATTRIBUTE_DIRECTORY 16 FILE_ATTRIBUTE_ARCHIVE 32 FILE_ATTRIBUTE_NORMAL 128 FILE_ATTRIBUTE_COMPRESSED 2048
How can I check a condition on one computer in a network and execute a task on another one (provided, of course, that a copy of nnCron is running on both computers)?
The simplest way to do this is to use an intermediate file created on a shared network drive accessible from both computers.
In the first place, you can use this file as a flag: one computer is used to create it, and the other one to watch for it.
You can also use such a file to pass to the second copy of nnCron the text of the task to be executed. Here is an example: one copy of nnCron creates a file named remote_task.spf with the following content:
S" Hello" MsgBox
And on the second computer, the following task is being executed:
#( remote_task WatchFile: remote_task.spf Action: 1000 PAUSE S" remote_task.spf" INCLUDED S" remote_task.spf" DELETE-FILE DROP )#
Please also keep in mind that it is possible to run a task stored in a text file by using command line key -runfile
How can I find out the current display resolution?
Word WIN-RECT returns four values which are coordinates of upper left and lower right corners of a specified window. This word takes the window handle of this window as its argument. Therefore, to learn the display resolution you only have to learn the coordinates of Program Manager window: the last two values will show the current display resolution.
Example:
#( test_screen_resolution NoActive Action:
\ getting window handle of Program Manager WIN-EXIST: "Progman" IF \ printing the coordinates of Program Manager window to console WIN-HWND WIN-RECT . . . . CR THEN )#
How can I programmatically empty the recycle bin?
1. Create a file clearbin.spf with the following contents:
WINAPI: SHEmptyRecycleBinA shell32.dll : main 7 0 0 SHEmptyRecycleBinA DROP ;
2. Define a task:
#( clear_task \ Clearing the recycle bin on hotkey combination Ctrl+Alt+Shift+Ñ AsLoggedUser LoadProfile WatchHotKey: "^@+c" Action: ShowNormal NormalPriority START-APP: %ModuleDirName%nncron.exe -wp -runfile clearbin.spf )#
Why all these complexities? Well, a user profile should be loaded in order to empty the recycle bin correctly, and a user profile will not load properly without starting a separate thread. However, if you are using Windows 9õ, this problem does not affect you, for you can use the following construct right within a task:
7 0 0 SHEmptyRecycleBinA DROP
By the way, here are some constants you can use in emptying the recycle bin:
SHERB_NOCONFIRMATION 0x00000001 SHERB_NOPROGRESSUI 0x00000002 SHERB_NOSOUND 0x00000004
How can I find short name of the file (8.3) when I'm inside the FOR-FILES: loop?
You can define a special word (say, FOUND-SHORTFILENAME) and use it inside the FOR-FILES: loop.
Example:
<% : FOUND-SHORTFILENAME __FFB cAlternateFileName ASCIIZ> DUP 0= IF 2DROP FOUND-FILENAME THEN ; %> #( test_shortnames NoActive Action: FOR-FILES: "c:\temp\*" MSG: "%FOUND-FILENAME% : %FOUND-SHORTFILENAME%" ;FOR-FILES )#
How can I start my own task by double-clicking on nnCron system tray icon?
Just create a postfix word (you can do this right in nncron.ini):
: launch-your-task S" your-task-name" SFIND IF EXECUTE LAUNCH ELSE 2DROP THEN ;
and edit the value of nncron.ini variable TrayIconDoubleClick:
TrayIconDoubleClick: launch-your-task
How can I create my own pop-up menu which is shown when right-clicking on nnCron system tray icon?
Create a plain text file menu.f in nnCron home directory and place the following definitions inside this file:
201 CONSTANT MI_ITEM1 202 CONSTANT MI_ITEM2 204 CONSTANT MI_ITEM31 205 CONSTANT MI_ITEM32 206 CONSTANT MI_ITEM33 : make-my-popup ( -- h ) POPUPMENU S" item1 " MI_ITEM1 MENUITEM S" item2 " MI_ITEM2 MENUITEM POPUP S" item31 " MI_ITEM31 MENUITEM MENUSEPARATOR S" item32 " MI_ITEM31 MENUITEM S" item33 " MI_ITEM31 MENUITEM S" item3" END-POPUP END-MENU ; : def-item-action S" item2" MsgBox ; \ setting the action for each menu item : start-my-menu-item ( id -- ) CASE MI_ITEM1 OF S" item1" MsgBox ENDOF MI_ITEM2 OF def-item-action ENDOF MI_ITEM31 OF S" item31" MsgBox ENDOF MI_ITEM32 OF S" item32" MsgBox ENDOF MI_ITEM33 OF S" item33" MsgBox ENDOF ENDCASE ; : my-menu CronIcon hWnd @ SetForegroundWindow DROP make-my-popup >R \ setting one of the menu item as 'default' (should be \ executed by double-clicking on nnCron tray icon) 0 MI_ITEM2 R@ SetMenuDefaultItem DROP 0 CronIcon hWnd @ CalcMenuYX ( TPM_RETURNCMD) 256 R@ TrackPopupMenuEx ?DUP IF start-my-menu-item THEN R> DestroyMenu DROP ;
Now just edit your nncron.ini like this:
S" menu.f" INCLUDED TrayIconDoubleClick: def-item-action TrayIconRightButton: my-menu
That's all! Now you have an example of fully-functional multilevel pop-up menu, that will be shown by right-clicking on nnCron tray icon. Note, please, that the default menu item (the bold one) will be executed by double-clicking on nnCron tray icon as well.
How can I launch a task using windows shortcut (*.lnk)?
This is rather easy if you use the -run <task_name> command line option. Here is the procedure:
1) Create a task. Mark this task NoActive if you don't want this task to be executed automatically:
#( test_task_shortcut NoActive Action: MSG: "Hi there!" )#
2) Now create windows shortcut in the desired location: mouse right-click - New - Shortcut and browse to 'nncron.exe'. Edit the Location of the item field like this:
<path_where_nncron_is_installed>\nncron.exe -run test_task_shortcut
Press Next and give the meaningful name for the newly created shortcut (test_task_shortcut for example). Press Finish.
3) That's it. Your shortcut is ready! Double-click it with your mouse to run test_task_shortcut task.
How can I launch a program using windows shortcut (*.lnk)?
It is not difficult at all:
#( test_shortcut NoActive Action: 5 0 0 Z" c:\temp\cmd.exe.lnk" Z" open" 0 ShellExecuteA DROP 5 0 0 Z" c:\temp\notepad.exe.lnk" Z" open" 0 ShellExecuteA DROP )#
The example will look slightly different if you need to authorize the task:
#( test_shortcut_authorized NoActive AsLoggedUser Action: START-APP: tm.exe 5 0 0 Z" c:\temp\cmd.exe.lnk" Z" open" 0 ShellExecuteA HALT START-APP: tm.exe 5 0 0 Z" c:\temp\notepad.exe.lnk" Z" open" 0 ShellExecuteA HALT )#
There is another one example of launching an application from the windows shortcut with authorization. This example uses JScript. Note the forward slashes in shortcut paths:
#( test_shortcut_authorized1 NoActive AsLoggedUser Action: <JScript> var WshShell = new ActiveXObject("WScript.Shell"); WshShell.Run("c:/temp/cmd.exe.lnk"); WshShell.Run("c:/temp/notepad.exe.lnk"); </SCRIPT> )#
How can I copy all the files and all the subdirectories tree?
The easiest solution is to use the nnBackup utility, which was created especially for this purpose:
#( test_backup NoActive Action: START-APP: nnbackup.exe copy -i "c:\data" -o "d:\backup" -v -s )#
On the other hand, the problem can be solved by using nnCron as the only available tool. Since FILE-COPY: does not create subdirectories when copying files, you need to have nnCron create them as shown:
#( test_backup1 NoActive Action: RECURSIVE FOR-FILES: "c:\data\*" IS-DIR? IF \ creating intermediate directories DIR-CREATE: "d:\backup\%FOUND-RELPATH%" ELSE \ copying files FILE-COPY: "%FOUND-FULLPATH%" "d:\backup\%FOUND-RELPATH%" THEN ;FOR-FILES )#
How can I restart nnCron right from the given task?
Take a look at the example (restarting nnCron every night, at 23:59):
#( test_restart Time: 59 23 Action: SWHide StartIn: "d:\TOOLS\NNCRON" START-APP: %COMSPEC% /c net stop nncron && \ start/wait nncron.exe 5000 PAUSE BYE && \ net start nncron )#
How can I determine the element of active window over which the mouse pointer is hovered?
Just send the windows message WM_NCHITTEST to the active window and analyze the numerical value that was returned. Here is the list of possible return values:
0 On the screen background or on a dividing line between windows (HTNOWHERE). 1 In a client area (HTCLIENT). 2 In a title bar (HTCAPTION). 3 In a window menu or in a Close button in a child window (HTSYSMENU). 4 In a size box (same as HTGROWBOX) (HTSIZE, HTGROWBOX). 5 In a menu (HTMENU). 6 In a horizontal scroll bar (HTHSCROLL). 7 In the vertical scroll bar (HTVSCROLL). 8 In a Minimize button (HTMINBUTTON, HTREDUCE). 9 In a Maximize button (HTMAXBUTTON, HTZOOM). 10 In the left border of a resizable window (the user can click the mouse to resize the window horizontally) (HTLEFT). 11 In the right border of a resizable window (the user can click the mouse to resize the window horizontally) (HTRIGHT). 12 In the upper-horizontal border of a window (HTTOP). 13 In the upper-left corner of a window border (HTTOPLEFT). 14 In the upper-right corner of a window border (HTTOPRIGHT). 15 In the lower-horizontal border of a resizable window (the user can click the mouse to resize the window vertically) (HTBOTTOM). 16 In the lower-left corner of a border of a resizable window (the user can click the mouse to resize the window diagonally) (HTBOTTOMLEFT). 17 In the lower-right corner of a border of a resizable window (the user can click the mouse to resize the window diagonally) (HTBOTTOMRIGHT). 18 In the border of a window that does not have a sizing border (HTBORDER). 20 In a Close button (HTCLOSE). 21 In a Help button (HTHELP). -1 In a window currently covered by another window in the same thread (the message will be sent to underlying windows in the same thread until one of them returns a code that is not HTTRANSPARENT) (HTTRANSPARENT).
-2 On the screen background or on a dividing line between windows (same as HTNOWHERE, except that the DefWindowProc function produces a system beep to indicate an error) (HTERROR).
Example:
#( test_buttons \ appropriate message is shown when you hover your mouse over \ 'Minimize', 'Maximize' or 'Close' buttons in active window SingleInstance Action: BEGIN MOUSE-POS 16 LSHIFT OR \ converting x,y to lParam \ checking the cursor position: 0 132 GetForegroundWindow SendMessageA DUP 8 = \ cursor is over the Minimize button: IF DROP MSG: "Minimize Button!" ELSE DUP 9 = \ cursor is over the Maximize button: IF DROP MSG: "Maximize Button!" ELSE 20 = \ cursor is over the Close button: IF MSG: "Close Button!" THEN THEN THEN PAUSE: 100 AGAIN )#
Use the keystate.spf plugin to monitor the mouse-click on a specified element of active window instead of mouse pointer hovering: this plugin allows you to determine wheter specified mouse button is pressed or not.
Example:
#( test_buttons_rightclick \ appropriate message is shown when you right-click on \ 'Minimize', 'Maximize' or 'Close' buttons in active window. \ Make sure that plug-in 'keystate.spf' is loaded. SingleInstance VARIABLE allowMB \ this flag allows to work with the current window Action: BEGIN VK_RBUTTON KEY-PRESSED? \ right mouse button IF allowMB @ \ first press? IF MOUSE-POS 16 LSHIFT OR \ converting x,y to lParam \ checking the cursor position: 0 132 GetForegroundWindow SendMessageA DUP 8 = \ cursor is over the Minimize button: IF DROP MSG: "Minimize Button!" ELSE DUP 9 = \ cursor is over the Maximize button: IF DROP MSG: "Maximize Button!" ELSE 20 = \ cursor is over the Close button: IF MSG: "Close Button!" THEN THEN THEN THEN ELSE \ the mouse button was released, set the flag to ON: allowMB ON THEN PAUSE: 100 AGAIN )#
There is another one useful example: "How can I hide any window to system tray by right-clicking on a Minimize button?".
How can I hide any window to system tray by right-clicking on a Minimize button?
There is an example from the RU.NNCRON news-group (make sure taht plugins keystate.spf and win2tray.spf are loaded):
#( test_min2tray_mouseRB \ put the current window into system tray by \ right-clicking on the 'Minimize' button SingleInstance VARIABLE allowMB1 \ this flag allows to minimize the current window Action: BEGIN VK_RBUTTON KEY-PRESSED? \ right mouse button IF allowMB1 @ \ first press? IF MOUSE-POS 16 LSHIFT OR \ converting x,y to lParam \ checking the cursor position: 0 132 GetForegroundWindow SendMessageA 8 = \ if the cursor is on the Minimize button: IF WIN-TO-TRAY: "%GetForegroundWindow%" THEN allowMB1 OFF \ set flag to OFF THEN ELSE \ the mouse button was released, set the flag to ON: allowMB1 ON THEN PAUSE: 100 AGAIN )#