%LOGME = 0 '-------------------------------------------------------------------------------- ' ** Constants ** '-------------------------------------------------------------------------------- %TICK = 15 ' smaller amount of time(ms) used in transitions %TIMER_SHOW = 2001 %TIMER_PAUSE = 2002 %TIMER_HIDE = 2003 %ITALIC = 2 %UNDERLINE = 4 %STRIKETHROUGH = 8 %BOLD = 16 %GWL_STYLE = -16 %GWL_EXSTYLE = -20 %HWND_TOPMOST = &HFFFFFFFF??? %SWP_NOSIZE = &H0001 %SWP_NOMOVE = &H0002 %LWA_COLORKEY = &H00000001 %LWA_ALPHA = &H00000002 %SEE_MASK_NOCLOSEPROCESS = &H00000040 %SEE_MASK_FLAG_NO_UI = &H00000400 %SEE_MASK_UNICODE = &H00004000 %WM_SETICON = &H80 '-------------------------------------------------------------------------------- %INVALID_HANDLE_VALUE = &HFFFFFFFF??? '%TRUE = -1 '%FALSE = 0 %INFINITE = &HFFFFFFFF??? ' Infinite timeout '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- ' ** Globals ** '-------------------------------------------------------------------------------- GLOBAL toast_hFnt AS DWORD ' fonts GLOBAL toast_width AS LONG GLOBAL toast_height AS LONG GLOBAL toast_alpha AS LONG GLOBAL toast_colf AS LONG GLOBAL toast_colb AS LONG GLOBAL toast_text AS STRING GLOBAL toast_pause AS LONG GLOBAL toast_dialog() AS DWORD GLOBAL toast_thread() AS DWORD '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- ' ** Constructors ** '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_SetFont (family AS STRING, siz AS LONG, weight AS LONG) LOGME " Toast_SetFont: " + family + ", " + FORMAT$(siz) + ", " + FORMAT$(weight) ' weight: 0 normal, 2 italic, 4 bold IF toast_hFnt <> 0 THEN FONT END toast_hFnt ' new font -> release previous one FONT NEW family, siz, weight TO toast_hFnt END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_SetWidth (w AS LONG) ' w in pixels LOGME " Toast_SetWidth: " + FORMAT$(w) toast_width = w END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_SetHeight (h AS LONG) ' h in pixels LOGME " Toast_SetHeight: " + FORMAT$(h) toast_height = h END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_SetAlpha (a AS LONG) ' 0 <= a <= 255 LOGME " Toast_SetAlpha: " + FORMAT$(a) toast_alpha = a END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_SetText (t AS STRING) LOGME " Toast_SetText: " + $DQ + t + $DQ toast_text = t END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_SetDuration (p AS LONG) LOGME " Toast_SetDuration: " + FORMAT$(p) + " ms" toast_pause = p END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- FUNCTION RGB2BGR(rgb_c AS STRING) AS STRING ' convert a RGB color to a BGR color LOCAL bgr_c AS STRING bgr_c = RIGHT$(rgb_c,2) + MID$(rgb_c,3,2) + LEFT$(rgb_c,2) FUNCTION = bgr_c END FUNCTION '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_SetTextColor (c AS STRING) ' c = hexadecimal color "RRGGBB" LOGME " Toast_SetTextColor: &H" + c toast_colf = VAL("&H00" + RGB2BGR(c)) END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_SetBackgroundColor (c AS STRING) ' c = hexadecimal color "RRGGBB" LOGME " Toast_SetBackgroundColor: &H" + c toast_colb = VAL("&H00" + RGB2BGR(c)) END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- ' ** Functions and Subs ** '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- MACRO TOAST_MAKEDLG ' (internal) common to Toast_Show_Sync() & Toast_Show_Async() LOCAL hDlg AS DWORD DIALOG NEW PIXELS, 0, EXE.NAME$, 0, 0, toast_width, toast_height TO hDlg DIALOG SET COLOR hDlg, -1, %RGB_MAGENTA CONTROL ADD GRAPHIC, hDlg, 1000, "", 0, 0, toast_width, toast_height END MACRO '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_Show_Sync () ' Synchronous (blocking) call LOCAL i AS LONG TOAST_MAKEDLG i = Toast_Create(hDlg) LOGME "Toast NO_" + FORMAT$(i) + " (" + FORMAT$(hDlg) _ + ") has been created as a new Synchronous (blocking) toast" DIALOG SHOW MODAL hDlg CALL Toast_CB END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_Show_Async () ' Asynchronous (non-blocking) call LOCAL hThread AS DWORD LOCAL i AS LONG THREAD CREATE Toast_NewThread(0) TO hThread ARRAY SCAN toast_thread(), =0, TO i toast_thread(i) = hThread END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- THREAD FUNCTION Toast_NewThread (BYVAL h AS DWORD) AS LONG ' thread used by Async LOCAL i AS LONG TOAST_MAKEDLG i = Toast_Create(hDlg) LOGME "Toast NO_" + FORMAT$(i) + " (" + FORMAT$(hDlg) _ + ") has ben created as a new Asynchronous (NON-blocking) toast" DIALOG SHOW MODAL hDlg CALL Toast_CB FUNCTION = -1 END FUNCTION '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- FUNCTION Toast_Create (BYVAL hDlg AS DWORD) AS LONG IF UBOUND(toast_dialog) < 0 THEN REDIM toast_dialog(1 TO 99) REDIM toast_thread(1 TO 99) END IF LOCAL i AS LONG ARRAY SCAN toast_dialog(), =0, TO i toast_dialog(i) = hDlg FUNCTION = i END FUNCTION '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- FUNCTION Toast_Find (BYVAL hDlg AS DWORD) AS LONG LOCAL i AS LONG ARRAY SCAN toast_dialog(), =hDlg, TO i FUNCTION = i END FUNCTION '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_Release (BYVAL hDlg AS DWORD) LOCAL i, lRes AS LONG ARRAY SCAN toast_dialog(), =hDlg, TO i toast_dialog(i) = 0 IF toast_thread(i) <> 0 THEN THREAD CLOSE toast_thread(i) TO lRes toast_thread(i) = 0 END IF END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_Enumerate () LOCAL i AS LONG LOGME "Enumerating toasts..." FOR i = 1 TO 99 IF toast_dialog(i) = 0 THEN EXIT FOR LOGME " - Toast NO_" + FORMAT$(i) + " (" + FORMAT$(toast_dialog(i)) + ")" NEXT END SUB '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- 'DECLARE FUNCTION SetTimer LIB "User32.dll" ALIAS "SetTimer" _ ' (BYVAL hWnd AS DWORD, BYVAL nIDEvent AS DWORD, BYVAL uElapse AS DWORD, _ ' OPTIONAL BYVAL lpTimerFunc AS DWORD) AS DWORD '-------------------------------------------------------------------------------- DECLARE FUNCTION KillTimer LIB "User32.dll" ALIAS "KillTimer" _ (BYVAL hWnd AS DWORD, BYVAL nIDEvent AS DWORD) AS LONG '-------------------------------------------------------------------------------- DECLARE FUNCTION WaitForSingleObject LIB "Kernel32.dll" _ ALIAS "WaitForSingleObject" (BYVAL hHandle AS DWORD, _ BYVAL dwMilliseconds AS DWORD) AS DWORD '-------------------------------------------------------------------------------- DECLARE FUNCTION CloseHandle LIB "Kernel32.dll" ALIAS "CloseHandle" _ (BYVAL hObject AS DWORD) AS LONG '-------------------------------------------------------------------------------- DECLARE FUNCTION SetWindowLong LIB "User32.dll" ALIAS "SetWindowLongW" _ (BYVAL hWnd AS DWORD, BYVAL nIndex AS LONG, BYVAL lNewLong AS LONG) AS LONG '-------------------------------------------------------------------------------- DECLARE FUNCTION GetWindowLong LIB "User32.dll" ALIAS "GetWindowLongW" _ (BYVAL hWnd AS DWORD, BYVAL nIndex AS LONG) AS LONG '-------------------------------------------------------------------------------- DECLARE FUNCTION SetWindowPos LIB "User32.dll" ALIAS "SetWindowPos" _ (BYVAL hWnd AS DWORD, BYVAL hWndInsertAfter AS DWORD, BYVAL x AS LONG, _ BYVAL y AS LONG, BYVAL cx AS LONG, BYVAL cy AS LONG, _ BYVAL wFlags AS DWORD) AS LONG '-------------------------------------------------------------------------------- DECLARE FUNCTION SetLayeredWindowAttributes LIB "User32.dll" _ ALIAS "SetLayeredWindowAttributes" (BYVAL hwnd AS DWORD, _ BYVAL crKey AS DWORD, BYVAL bAlpha AS BYTE, BYVAL dwFlags AS DWORD) AS LONG '-------------------------------------------------------------------------------- DECLARE FUNCTION SendMessage LIB "User32.dll" ALIAS "SendMessageW" _ (BYVAL hWnd AS DWORD, BYVAL dwMsg AS DWORD, BYVAL wParam AS DWORD, _ BYVAL lParam AS LONG) AS LONG '-------------------------------------------------------------------------------- 'DECLARE FUNCTION SetWindowsHookEx LIB "User32.dll" ALIAS "SetWindowsHookExW" _ ' (BYVAL idHook AS LONG, BYVAL lpfn AS DWORD, BYVAL hMod AS DWORD, _ ' BYVAL dwThreadId AS DWORD) AS DWORD '-------------------------------------------------------------------------------- DECLARE FUNCTION GetCurrentThreadId LIB "Kernel32.dll" _ ALIAS "GetCurrentThreadId" () AS DWORD '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- MACRO CreateTimer (evt, tid) evt = SetTimer(CB.HNDL, tid, %TICK, BYVAL 0) DIALOG POST CB.HNDL, %WM_TIMER, tid, 0 END MACRO '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- CALLBACK FUNCTION Toast_CB ' Common Toast CallBack STATIC x, y, t, evtShow, evtPause, evtHide AS LONG STATIC t0 AS DOUBLE LOCAL e AS STRING LOCAL i AS LONG SELECT CASE CB.MSG CASE %WM_INITDIALOG t0 = TIMER Toast_Fill CB.HNDL ' Set toast properties & initialize its content i = Toast_Find(CB.HNDL) x = (i - 1) * (toast_width + 1) y = -toast_height CreateTimer (evtShow, %TIMER_SHOW) ' Start timer event for dialog show CASE %WM_DESTROY e = "Toast NO_" + FORMAT$(Toast_Find(CB.HNDL)) + " (" + FORMAT$(CB.HNDL) + ")" LOGME e + " destroyed after displaying for " + FORMAT$(INT((TIMER-t0)*1000)) + " ms" Toast_Release CB.HNDL CASE %WM_TIMER ' Timer to show the toast IF CB.WPARAM = %TIMER_SHOW THEN IF y < 0 THEN y += 5 DIALOG SET LOC CB.HNDL, x, y ELSE KillTimer CB.HNDL, evtShow t = 1 CreateTimer (evtPause, %TIMER_PAUSE) ' Start timer event to pause the toast END IF ' Timer to pause the toast ELSEIF CB.WPARAM = %TIMER_PAUSE THEN IF t = 0 THEN EXIT FUNCTION ' Cannot pause until toast is fully shown IF t < toast_pause THEN t += %TICK ELSE KillTimer CB.HNDL, evtPause CreateTimer (evtHide, %TIMER_HIDE) ' Start timer event to hide the toast END IF ' Timer to hide the toast ELSEIF CB.WPARAM = %TIMER_HIDE THEN IF y > -toast_height THEN y -= 5 DIALOG SET LOC CB.HNDL, x, y ELSE KillTimer CB.HNDL, evtHide DIALOG END CB.HNDL END IF END IF END SELECT END FUNCTION '-------------------------------------------------------------------------------- '-------------------------------------------------------------------------------- SUB Toast_Fill (BYVAL hDlg AS DWORD) LOCAL w, h AS LONG ' Initialize Styles and Extended Styles of dialog SetWindowLong hDlg, %GWL_style, %WS_POPUP OR %WS_BORDER OR %DS_NOFAILCREATE _ OR %WS_SYSMENU OR %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_3DLOOK _ OR %DS_MODALFRAME OR %WS_DLGFRAME OR %DS_SETFONT OR %WS_CAPTION SetWindowLong hDlg, %GWL_EXstyle, %WS_EX_WINDOWEDGE OR %WS_EX_CONTROLPARENT _ OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR _ OR %WS_EX_TOOLWINDOW ' Set Always On Top SetWindowPos hDlg, %HWND_TOPMOST, 0, 0, 0, 0, %SWP_NOMOVE OR %SWP_NOSIZE ' Remove Caption SetWindowLong hDlg, %GWL_style, GetWindowLong(hDlg, %GWL_style) XOR %WS_CAPTION ' Set Dialog alpha / Transparent Font / Transparent Background SetWindowLong hDlg, %GWL_EXSTYLE, GetWindowLong(hDlg, %GWL_EXstyle) OR %WS_EX_LAYERED SetLayeredWindowAttributes hDlg, %RGB_MAGENTA, toast_alpha, %LWA_Colorkey OR %LWA_ALPHA ' Initialize Dialog Content GRAPHIC ATTACH hDlg, 1000, REDRAW GRAPHIC COLOR toast_colf, toast_colb GRAPHIC SET FONT toast_hFnt GRAPHIC CLEAR CONTROL GET SIZE hDlg, 1000 TO w, h WRAP toast_text, w GRAPHIC REDRAW END SUB '-------------------------------------------------------------------------------- '----------------------------------------------------------------------------------------------- SUB WRAP (FullString AS STRING, w AS LONG) LOCAL Part1, Part2 AS STRING gbSplitWord FullString, w, Part1, Part2 WHILE LEN(Part2) GRAPHIC PRINT Part1 gbSplitWord Part2, w, Part1, Part2 WEND GRAPHIC PRINT Part1 END SUB '----------------------------------------------------------------------------------------------- '----------------------------------------------------------------------------------------------- SUB gbSplitWord(BYVAL FullString AS STRING, BYVAL w AS LONG, Part1 AS STRING, Part2 AS STRING) LOCAL i, ww, hh AS LONG LOCAL PreviousWords, CurrentWord AS STRING 'if only one word is in the string (no delimiter), use the entire string as Part1 i = INSTR(FullString, $SPC) IF i = 0 THEN Part1 = FullString : Part2 = "" : EXIT SUB 'if first word is wider than available width, use that word as Part1 Part1 = LEFT$(FullString, i-1) GRAPHIC TEXT SIZE Part1 TO ww, hh IF ww > w THEN Part2 = MID$(FullString, i+1) : EXIT SUB 'go through all words FOR i = 1 TO PARSECOUNT(FullString, $SPC) 'cycle through all words CurrentWord = PARSE$(FullString, $SPC, i) GRAPHIC TEXT SIZE PreviousWords + IIF$(i=1,"",$SPC) + CurrentWord TO ww, hh IF ww > w THEN 'adding CurrentWord WILL exceed available width Part1 = PreviousWords Part2 = MID$(FullString, LEN(Part1)+2) EXIT SUB ELSE 'adding CurrentWord WILL NOT exceed available width PreviousWords += IIF$(i=1,"",$SPC) + CurrentWord Part1 = PreviousWords Part2 = "" END IF NEXT END SUB '----------------------------------------------------------------------------------------------- '----------------------------------------------------------------------------------------------- SUB LOGME (e AS STRING) IF ISFALSE %LOGME THEN EXIT SUB LOCAL ff AS LONG LOCAL d, t AS STRING ' Create TimeStamp d = DATE$ d = RIGHT$(d,4) + LEFT$(d,2) + MID$(d, 4, 2) t = TIME$ REPLACE ":" WITH "" IN t t = d + "-" + t + "," t = t + FORMAT$((TIMER*1000) MOD 1000, "000") ' Write To Log File ff = FREEFILE OPEN EXE.PATH$ + EXE.NAME$ + ".log" FOR APPEND AS #ff PRINT #ff, "[" + t + "] " + e CLOSE #ff END SUB '-----------------------------------------------------------------------------------------------