• Welcome to PowerBasic Museum 2020-A.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

WatchDog

Started by Patrice Terrier, November 06, 2016, 10:50:12 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Patrice Terrier

The purpose of this application is to monitor extra disk activity, based on the notifications sent by the Windows file system or Shell.

The GUI is the same than with the zTrace utility, using a popup toolwindow (no icon in task bar).

All options could be set from the contextual popup menu, they are:
- "Use horizontal scrollbar"
- "Send selection to printer"
- "Copy selection to clipboard"
- "Clear content"
- "Set window TopMost"
- "Create WatchDog.txt report"
- "Save window coordinates"


'+--------------------------------------------------------------------------+
'|                                                                          |
'|                             WatchDog 1.00                                |
'|                                                                          |
'|       Monitor notifications from the Windows file system or Shell        |
'|                                                                          |
'+--------------------------------------------------------------------------+
'|                                                                          |
'|               Author Pierre Bellisle & Patrice TERRIER                   |
'|                                                                          |
'|                       http://www.zapsolution.com                         |
'|                                                                          |
'+--------------------------------------------------------------------------+
'|                  Project started on : 11-04-2016 (MM-DD-YYYY)            |
'|                        Last revised : 11-04-2016 (MM-DD-YYYY)            |
'+--------------------------------------------------------------------------+

#COMPILE EXE "WatchDog.exe"

#INCLUDE "windows.inc"
#INCLUDE "commdlg.inc"

DECLARE FUNCTION zTrace LIB "zTrace.DLL" ALIAS "zTrace" (zMessage AS ASCIIZ) AS LONG

'----------------------------------------------------------------------
%CSIDL_DESKTOP             = &H0000???
%SHCNRF_InterruptLevel     = &H0001???
%SHCNRF_ShellLevel         = &H0002???
%SHCNRF_RecursiveInterrupt = &H1000???
%SHCNRF_NewDelivery        = &H8000???

%SHCNE_RENAMEITEM          = &H00000001???
%SHCNE_CREATE              = &H00000002???
%SHCNE_DELETE              = &H00000004???
%SHCNE_MKDIR               = &H00000008???
%SHCNE_RMDIR               = &H00000010???
%SHCNE_MEDIAINSERTED       = &H00000020???
%SHCNE_MEDIAREMOVED        = &H00000040???
%SHCNE_DRIVEREMOVED        = &H00000080???
%SHCNE_DRIVEADD            = &H00000100???
%SHCNE_NETSHARE            = &H00000200???
%SHCNE_NETUNSHARE          = &H00000400???
%SHCNE_ATTRIBUTES          = &H00000800???
%SHCNE_UPDATEDIR           = &H00001000???
%SHCNE_UPDATEITEM          = &H00002000???
%SHCNE_SERVERDISCONNECT    = &H00004000???
%SHCNE_UPDATEIMAGE         = &H00008000???
%SHCNE_DRIVEADDGUI         = &H00010000???
%SHCNE_RENAMEFOLDER        = &H00020000???
%SHCNE_FREESPACE           = &H00040000???

%SHCNE_DISKEVENTS          = &H0002381F???
%SHCNE_GLOBALEVENTS        = &H0C0581E0???
%SHCNE_ALLEVENTS           = &H7FFFFFFF???
%SHCNE_INTERRUPT           = &H80000000??? 

type SHITEMID
    cb      as word
    abID(0) as byte
end type

type ITEMIDLIST
    mkid as SHITEMID
end type

type SHChangeNotifyEntry
    pidl       as ITEMIDLIST ptr
    fRecursive as long
end type

$Nil = "Nil"
$SPACE = " "
declare function SHGetPathFromIDList LIB "Shell32.dll" alias "SHGetPathFromIDListA" (byval pidl as ITEMIDLIST ptr, pszPath as asciiz) as long
declare function SHGetSpecialFolderLocation LIB "Shell32.dll" alias "SHGetSpecialFolderLocation" (byval hwnd as dword, byval csidl as long, ppidl as any) as long
declare function SHChangeNotifyRegister LIB "Shell32.dll" alias "SHChangeNotifyRegister" (byval hwnd as dword, byval fSources as long, byval fEvents as long, byval wMsg as dword, byval cEntries as long, pshcne as any) as dword
declare function SHChangeNotifyDeregister LIB "Shell32.dll" alias "SHChangeNotifyDeregister" (byval ulID as dword) as long

%dwStyle = %WS_VISIBLE or %WS_CLIPSIBLINGS or %WS_CLIPCHILDREN or %WS_CAPTION or %WS_SYSMENU or %WS_THICKFRAME
%dwExStyle = %WS_EX_TOOLWINDOW

$WATCHDOG            = "WatchDog 1.00"

%BRUSHCOLOR          = &HF8D2B0

%HORIZONTAL_EXTENT   = 6000 '// Maximum value for the horizontal scrollbar
%MIN_WIDTH           = 300  '// default client width size
%MIN_HEIGHT          = 104  '// default client height size

%ID_LISTBOX          = 1

%IDM_About           = 101  '// menu popup
%IDM_Hscroll         = 103  '// menu popup
%IDM_Print           = 104  '// menu popup
%IDM_CopyToClipboard = 105  '// menu popup
%IDM_ClearContent    = 106  '// menu popup
%IDM_TopMost         = 107  '// menu popup
%IDM_Report          = 108  '// menu popup
%IDM_SaveCoordinates = 109  '// menu popup

type PROP
    backbrush       as dword
    x               as long
    y               as long
    w               as long
    h               as long
    savecoordinates as long
    topmost         as long
    report          as long
    usescrollbar    as long
end type

global gP as PROP

function FileSize(szFileSpec as asciiz) as dword
    local fd as WIN32_FIND_DATA
    local nRet, hFind as dword
    if (len(szFileSpec)) then
        hFind = FindFirstFile(szFileSpec, fd)
        if (hFind <> %INVALID_HANDLE_VALUE) then
            FindClose(hFind)
            nRet = fd.nFileSizeLow
        end if
    end if
    function = nRet
end function

sub zLoadSaveCoordinates (byref x as long, byref y as long, byref w as long, byref h as long, byval RW as long)
    local zFileName as asciiz * %MAX_PATH
    local hFile, BufferSize, dwBytes as dword
    static sWasCoordinates as string

    zFileName = Get_TempPath + "WatchDog.cfg"
    if (RW) then
        sWasCoordinates = ltrim$(str$(x)) + "," + ltrim$(str$(y)) + "," + ltrim$(str$(w)) + "," + ltrim$(str$(h)) + "," + _
                          ltrim$(str$(gP.topmost)) + "," + _
                          ltrim$(str$(gP.savecoordinates)) + "," + _
                          ltrim$(str$(gP.usescrollbar)) + "," + _
                          ltrim$(str$(gP.report))

        hFile = CreateFile(zFilename, %GENERIC_WRITE, 0, byval %NULL, %CREATE_ALWAYS, %FILE_ATTRIBUTE_NORMAL, byval %NULL)
        if (hFile <> %INVALID_HANDLE_VALUE) then
            WriteFile(hFile, byval strptr(sWasCoordinates), len(sWasCoordinates), dwBytes, byval %NULL)
            CloseHandle(hFile)
        end if
    else
       if (len(sWasCoordinates) = 0) then
           BufferSize = FileSize(zFilename)
           if (BufferSize) then
               sWasCoordinates = space$(BufferSize)
               hFile = CreateFile(zFilename, %GENERIC_READ, 0, byval %NULL, %OPEN_ALWAYS, %FILE_ATTRIBUTE_NORMAL, byval %NULL)
               if (hFile <> %INVALID_HANDLE_VALUE) then
                   ReadFile(hFile, byval strptr(sWasCoordinates), BufferSize, dwBytes, byval %NULL)
                   CloseHandle(hFile)
               end if
           end if
           x = val(parse$(sWasCoordinates, 1))
           y = val(parse$(sWasCoordinates, 2))
           w = val(parse$(sWasCoordinates, 3))
           h = val(parse$(sWasCoordinates, 4))
           gP.topmost          = val(parse$(sWasCoordinates, 5))
           gP.savecoordinates  = val(parse$(sWasCoordinates, 6)): if (gP.savecoordinates = 0) then w = 0
           gP.usescrollbar     = val(parse$(sWasCoordinates, 7))
           gP.report           = val(parse$(sWasCoordinates, 8))
       end if
       if ((w = 0) or (h = 0)) then w = %MIN_WIDTH: h = %MIN_HEIGHT: x = 0: y = 0
    end if
end sub

function List_GetText(byval hList as long, byval nItem as long) as string
    local sItem as string, nLen as long
    if (nItem > 0) then
        decr nItem
        nLen = SendMessage(hList, %LB_GETTEXTLEN, nItem, 0)
        sItem = space$(nLen)
        SendMessage(hList, %LB_GETTEXT, nItem, byval strptr(sItem))
        function = sItem
    end if
end function

' Get the system temp path
function Get_TempPath () as string
    local dwSize as dword, sTempPath as string
    sTempPath = space$(GetTempPath(0, byval %NULL))
    dwSize = GetTempPath(len(sTempPath), byval strptr(sTempPath))
    function = RTRIM$(sTempPath, any CHR$(0,92)) + "\"
end function

' Copy string to a sequential ASCII file.
sub WriteText(zMessage as asciiz)
    static NeverBeenThere as long
    static hFile as dword
    local sBuffer as string
    local dwBytesWritten as dword
    if ((hFile) and (len(zMessage) = 0)) then
        CloseHandle(hFile): hFile = 0
        NeverBeenThere = 0
        exit sub
    end if
    if (len(zMessage)) then
        if (NeverBeenThere = 0) then
            NeverBeenThere = -1
            hFile = CreateFile("WatchDog.txt", %GENERIC_WRITE, 0, byval %NULL, %CREATE_ALWAYS, %FILE_ATTRIBUTE_NORMAL, byval %NULL)
        end if
        if (hFile) then
            sBuffer = zMessage + $CRLF
            WriteFile(hFile, byval strptr(sBuffer), len(sBuffer), dwBytesWritten, byval %NULL)
        end if
    end if
end sub

function List_AddString(byval hCtrl as dword, byval zPtr as dword) as long
    local nRet as long
    if (gP.report) then
        if (zPtr) then WriteText(byval zPtr)
    end if
    nRet = SendMessage(hCtrl, %LB_ADDSTRING, 0, zPtr)
    SendMessage(hCtrl, %LB_SETTOPINDEX, nRet, 0)
    function = nRet
end function

function ToolProc (byval hWnd as dword, byval Msg as long, byval wParam as long, byval lParam as long) as long

    local zPath1 as asciiz * %MAX_PATH
    local zPath2 as asciiz * %MAX_PATH
    local sMsg as string

    local rc, rw as RECT
    local K, nCount, nSelItems as long
    local sBuffer as string

    local hCtrl, hPrinter as dword
    local tm as TEXTMETRIC
    local di as DOCINFO
    local yChar, nLinesPerPage, nCharsPerLine as long
    local pd as tagPRINTDLG

    select case long (Msg)
    case %WM_CREATE:
         '// A good place to initiate things, declare variables,
         '// create controls and read/set settings from a file, etc.
         '-------------------------------------------------------
         if (gP.backbrush = 0) then gP.backbrush = CreateSolidBrush(%BRUSHCOLOR)

    case %WM_GETMINMAXINFO:
         local pMM as MINMAXINFO ptr
         SetRect(rc, 0, 0, %MIN_WIDTH, %MIN_HEIGHT)
         AdjustWindowRectEx(rc, %dwStyle, %FALSE, %dwExStyle)  ' Adjust Window To True Requested Size
         pMM = lParam
         @pMM.ptMinTrackSize.x = rc.nRight
         @pMM.ptMinTrackSize.y = rc.nBottom

    case %WM_APP '// SHChangeNotifyRegister/SHCNE_DISKEVENTS notification
         '// If %SHCNRF_NewDelivery is not specified, wParam is a pointer
         '// to two PIDLIST_ABSOLUTE pointers, and lParam specifies the event.
         '// The two PIDLIST_ABSOLUTE pointers can be NULL, depending on the event being sent.
       
         dim p(0 TO 1) as ITEMIDLIST pointer at wParam 'Set an array of two ITEMIDLIST pointers from CBWPARAM
         if (p(0)) then
             SHGetPathFromIDList(p(0), byval varptr(zPath1))
         else
             zPath1 = $Nil '// Test if ZERO or a valid pointer
         end if
         if (p(1)) then
             SHGetPathFromIDList(p(1), byval varptr(zPath2))

         else
             zPath2 = $Nil '// Test if ZERO or a valid pointer
         end if
         sMsg = ""
         select case lParam
         case %SHCNE_MEDIAINSERTED : sMsg = "MediaInserted " + zPath1
         case %SHCNE_MEDIAREMOVED  : sMsg = "MediaRemoved  " + zPath1
         case %SHCNE_DRIVEREMOVED  : sMsg = "DriveRemoved  " + zPath1
         case %SHCNE_DRIVEADD      : sMsg = "DriveAdd      " + zPath1
       
         case %SHCNE_RENAMEITEM    : sMsg = "RenameItem    " + ltrim$(zPath1 + $SPACE + zPath2)
         case %SHCNE_CREATE        : sMsg = "Create        " + ltrim$(zPath1 + $SPACE + zPath2)
         case %SHCNE_DELETE        : sMsg = "Delete        " + ltrim$(zPath1 + $SPACE + zPath2)
         case %SHCNE_MKDIR         : sMsg = "MakeDir       " + ltrim$(zPath1 + $SPACE + zPath2)
         case %SHCNE_RMDIR         : sMsg = "RemoveDir     " + ltrim$(zPath1 + $SPACE + zPath2)
         case %SHCNE_UPDATEITEM    : sMsg = "UpdateItem    " + ltrim$(zPath1 + $SPACE + zPath2)
         case %SHCNE_RENAMEFOLDER  : sMsg = "RenameFolder  " + ltrim$(zPath1 + $SPACE + zPath2)
         end select
         if (len(sMsg)) then List_AddString(GetDlgItem(hWnd, %ID_LISTBOX), byval strptr(sMsg))

    case %WM_COMMAND:
         'Messages from controls and menu items are handled here.
         '-------------------------------------------------------
         select case long lo(word, wParam)
         case %IDCANCEL:
              if (hi(word, wParam) = %BN_CLICKED) then
                  SendMessage(hWnd, %WM_CLOSE, 0, 0)
                  exit function
              end if
     
         case %IDM_About:
              sBuffer = "                   WatchDog window" + _
                        "||      by Pierre Bellisle & Patrice TERRIER" + _
                        "||                 www.zapsolution.com"
              replace "|" with $CR in sBuffer
     
              MessageBox(0, (sBuffer), $WATCHDOG, %MB_OK)
     
         case %IDM_Hscroll:
              hCtrl = GetDlgItem(hWnd, %ID_LISTBOX)
              gP.usescrollbar = NOT gP.usescrollbar
              if (gP.usescrollbar) then
                  ShowScrollBar(hCtrl, %SB_HORZ, %TRUE)
                  SendMessage(hCtrl, %LB_SETHORIZONTALEXTENT, %HORIZONTAL_EXTENT, 0)
              else
                  SendMessage(hCtrl, %LB_SETHORIZONTALEXTENT, 1, 0)
                  ShowScrollBar(hCtrl, %SB_HORZ, %FALSE)
              end if
              UpdateWindow(hCtrl)
     
         case %IDM_Print:
              hCtrl = GetDlgItem(hWnd, %ID_LISTBOX)
              nCount = SendMessage(hCtrl, %LB_GETCOUNT, 0, 0)
              if (nCount > 0) then
                  pd.lStructSize = sizeof(pd)
                  '// get rid of PD_RETURNDEFAULT on the line below if you'd like to
                  '// see the "Printer Settings" dialog!
                  pd.Flags = %PD_RETURNDEFAULT or %PD_RETURNDC
                  if (PrintDlg(pd)) then
                      hPrinter = pd.hDC
                      GetTextMetrics(pd.hDC, tm)
     
                      yChar = tm.tmHeight + tm.tmExternalLeading
                      nLinesPerPage = GetDeviceCaps(pd.hDC, %VERTRES) / yChar
                      nCharsPerLine = GetDeviceCaps(pd.hDC, %HORZRES) / tm.tmAveCharWidth
                 
                      di.cbSize = sizeof(di)
                      StartDoc(hPrinter, di)
                          StartPage(hPrinter)
                              nSelItems = SendMessage(hCtrl, %LB_GETSELCOUNT, 0, 0)
                              if (nSelItems > 0) then
                                  redim SelItem(nSelItems - 1) as long
                                  nCount = SendMessage(hCtrl, %LB_GETSELITEMS, nSelItems, varptr(SelItem(0)))
                                  for K = 0 TO nSelItems - 1
                                      sBuffer = List_GetText(hCtrl, SelItem(K))
                                      TextOut(pd.hDC, 0, (yChar * K), byval strptr(sBuffer), len(sBuffer))
                                  next
                              else
                                  for K = 0 TO nCount - 1
                                      sBuffer = List_GetText(hCtrl, K)
                                      TextOut(pd.hDC, 0, (yChar * K), byval strptr(sBuffer), len(sBuffer))
                                  next
                              end if
                          EndPage(hPrinter)
                      EndDoc(hPrinter)
                      DeleteDC(hPrinter)
                  end if
              end if
     
         case %IDM_CopyToClipboard:
              hCtrl = GetDlgItem(hWnd, %ID_LISTBOX)
              nCount = SendMessage(hCtrl, %LB_GETCOUNT, 0, 0)
              if (nCount > 0) then
                  sBuffer = ""
     
                  nSelItems = SendMessage(hCtrl, %LB_GETSELCOUNT, 0, 0)
                  if (nSelItems > 0) then
                      REDIM SelItem(1 TO nSelItems) as long
                      nCount = SendMessage(hCtrl, %LB_GETSELITEMS, nSelItems&, varptr(SelItem&(1)))
                      for K = 1 TO nSelItems
                          sBuffer = sBuffer + List_GetText(hCtrl, SelItem(K)) + $CRLF
                      next
                  else
                      for K = 0 TO nCount - 1
                          sBuffer = sBuffer + List_GetText(hCtrl, K) + $CRLF
                      next
                  end if
     
                  local hClipData, hGlob as dword
                  hClipData = GlobalAlloc(%GMEM_MOVEABLE or %GMEM_DDESHARE, len(sBuffer) + 1)
                  hGlob = GlobalLock(hClipData)
                  POKE$ hGlob, sBuffer + CHR$(0)
                  GlobalUnlock(hClipData)
                  if (OpenClipboard(0)) then
                      EmptyClipboard()
                      SetClipboardData(%CF_TEXT, hClipData)
                      CloseClipboard()
                  else
                      GlobalFree(hClipData)
                  end if
     
              end if
     
         case %IDM_ClearContent:
              SendMessage(GetDlgItem(hWnd, %ID_LISTBOX), %LB_RESETCONTENT, 0, 0)
     
         case %IDM_TopMost:
              gP.topmost = NOT gP.topmost
              if (gP.topmost) then
                  SetWindowPos(hWnd, %HWND_TOPMOST, 0, 0, 0, 0, %SWP_NOSIZE or %SWP_NOMOVE or %SWP_SHOWWINDOW)
              else
                  SetWindowPos(hWnd, %HWND_NOTOPMOST, 0, 0, 0, 0, %SWP_NOSIZE or %SWP_NOMOVE or %SWP_SHOWWINDOW)
              end if
     
         case %IDM_Report:
              gP.report = NOT gP.report
              if (gP.report) then
                  hCtrl = GetDlgItem(hWnd, %ID_LISTBOX)
                  nCount = SendMessage(hCtrl, %LB_GETCOUNT, 0, 0)
                  if (nCount > 0) then
                      for K = 0 TO nCount - 1
                          WriteText(List_GetText(hCtrl, K))
                      next
                  end if
              else
                  WriteText("") '// Close WriteText.txt if already open.
              end if
     
         case %IDM_SaveCoordinates:
              gP.savecoordinates = NOT gP.savecoordinates
     
         end select

    case %WM_CTLCOLORLISTBOX:
         '// wParam is handle of control's display context (hDC)
         '// lParam is handle of control
         '------------------------------------------------------
         if (lParam = GetDlgItem(hWnd, %ID_LISTBOX)) then
             SetBkColor(wParam, %BRUSHCOLOR)
             function = gP.backbrush
             exit function
         end if

    case %WM_RBUTTONDOWN:
         local hMenu, nChoice as long, p as POINTAPI
         local menuStyle as long
         hMenu = CreatePopupMenu()
         if (hMenu) then
             AppendMenu(hMenu, %MF_STRING, %IDM_About          , "About")
             AppendMenu(hMenu, %MF_SEPARATOR, 102              , "")
             if (gP.usescrollbar) then menuStyle = %MF_STRING or %MF_CHECKED else menuStyle = %MF_STRING
             AppendMenu(hMenu, menuStyle,  %IDM_Hscroll        , "Use horizontal scrollbar")
             AppendMenu(hMenu, %MF_STRING, %IDM_Print          , "Send selection to printer")
             AppendMenu(hMenu, %MF_STRING, %IDM_CopyToClipboard, "Copy selection to clipboard")
             AppendMenu(hMenu, %MF_STRING, %IDM_ClearContent   , "Clear content")
             if (gP.topmost) then menuStyle = %MF_STRING or %MF_CHECKED else menuStyle = %MF_STRING
             AppendMenu(hMenu, menuStyle,  %IDM_TopMost        , "Set window TopMost")
             if (gP.report) then menuStyle = %MF_STRING or %MF_CHECKED else menuStyle = %MF_STRING
             AppendMenu(hMenu, menuStyle,  %IDM_Report, "create WatchDog.txt report")
             if (gP.savecoordinates) then menuStyle = %MF_STRING or %MF_CHECKED else menuStyle = %MF_STRING
             AppendMenu(hMenu, menuStyle,  %IDM_SaveCoordinates, "Save window coordinates")

             GetCursorPos(p)
             nChoice = TrackPopupMenuEx(hMenu, %TPM_RETURNCMD, p.X, p.Y, hWnd, byval %NULL)
             DestroyMenu(hMenu)
             if (nChoice) then SendMessage(hWnd, %WM_COMMAND, MAKLNG(nChoice, 0), 0)
         end if

    case %WM_MOVE:
         GetWindowRect(hWnd, rw): gP.x = rw.nLeft: gP.y = rw.nTop

    case %WM_SIZE:
         if (wParam <> %SIZE_MINIMIZED) then
             gP.w = LOWRD(lParam): gP.h = HIWRD(lParam)
             MoveWindow(GetDlgItem(hWnd, %ID_LISTBOX), 0, 0, gP.w, gP.h, %TRUE)
             UpdateWindow(hWnd)

             GetWindowRect(hWnd, rw): gP.x = rw.nLeft: gP.y = rw.nTop

         end if

    case %WM_DESTROY:
         if (gP.backbrush) then
             DeleteObject(gP.backbrush): gP.backbrush = 0
             zLoadSaveCoordinates (gP.x, gP.y, gP.w, gP.h, 1)
         end if
         WriteText("")
         PostQuitMessage(0)
         function = 0: exit function

    end select

    function = DefWindowProc(hWnd, Msg, wParam, lParam)
end function

function WinMain (byval hInstance as dword, byval hPrevInstance as dword, _
                  byval lpCmdLine as asciiz ptr, byval nCmdShow as long) as long
    local Msg as tagMsg
    local IsInitialized as long
    local hWnd, hCtrl as dword

    local wcx as WndClassEx, szClassName as asciiz * 80
    local rc, rw as RECT

    szClassName = $WATCHDOG

    wcx.cbSize = sizeof(wcx)
    IsInitialized = GetClassInfoEx(hInstance, szClassName, wcx)
    if (IsInitialized   = 0) then
        wcx.cbSize        = sizeof(wcx)
        wcx.style         = %CS_HREDRAW or %CS_VREDRAW or %CS_DBLCLKS
        wcx.lpfnWndProc   = codeptr(ToolProc)
        wcx.cbClsExtra    = 0
        wcx.cbWndExtra    = 0
        wcx.hInstance     = hInstance
        wcx.hIcon         = 0
        wcx.hCursor       = LoadCursor(%NULL, byval %IDC_ARROW)
        wcx.hbrBackground = %COLOR_BTNSHADOW
        wcx.lpszMenuName  = %NULL
        wcx.lpszClassName = varptr(szClassName)
        wcx.hIconSm       = wcx.hIcon
        if (RegisterClassEx(wcx)) then IsInitialized = %TRUE
    end if

    if (IsInitialized) then
        local UseX, UseY, UseW, UseH as long
        zLoadSaveCoordinates (UseX, UseY, UseW, UseH, 0)

        SetRect(rc, 0, 0, UseW, UseH)
        AdjustWindowRectEx(rc, %dwStyle, %FALSE, %dwExStyle)
        hWnd = CreateWindowEx(%dwExStyle, szClassName, szClassName, _
                              %dwStyle, _
                              UseX, UseY, _
                              rc.nRight - rc.nLeft, _  ' Calculate Window Width
                              rc.nBottom - rc.nTop, _  ' Calculate Window Height
                              0, 0, hInstance, byval %NULL)
        if (hWnd) then

            local nSH_OK as long
            local ChangeInfo as SHChangeNotifyEntry
            local RegID as dword
            SHGetSpecialFolderLocation(hWnd, %CSIDL_DESKTOP, ChangeInfo.pidl) 'Get pidl for desktop to monitor all drives
            if (ChangeInfo.pidl) then 'pidl was valid
                ChangeInfo.fRecursive = %TRUE 'Do sub-folder
                '// Ask Windows to send us message at %WM_APP for many events.
                local nEvents as long, wMsg as dword
                nEvents = %SHCNE_CREATE or %SHCNE_DELETE or %SHCNE_MKDIR or %SHCNE_RENAMEFOLDER or %SHCNE_RENAMEITEM or _
                          %SHCNE_RMDIR or %SHCNE_DISKEVENTS or %SHCNE_UPDATEITEM or %SHCNE_UPDATEDIR or %SHCNE_DRIVEADD or _
                          %SHCNE_DRIVEREMOVED or %SHCNE_MEDIAINSERTED or %SHCNE_MEDIAREMOVED or %SHCNE_SERVERDISCONNECT
                RegID = SHChangeNotifyRegister(hwnd, %SHCNRF_SHELLLEVEL, nEvents, %WM_APP, 1, byval varptr(ChangeInfo))
                if (RegID > 0) then  'SHChangeNotifyRegister successful
                   nSH_OK = -1
                else
                    MessageBox(0, "SHChangeNotifyRegister error !", $WATCHDOG, %MB_OK)
                end if
            else
                MessageBox(0, "SHGetSpecialFolderLocation error !", $WATCHDOG, %MB_OK)
            end if

            if (nSH_OK) then
                hCtrl = CreateWindowEx(0, "ListBox", byval %NULL, _
                                       %WS_CHILD or %WS_VISIBLE or %WS_VSCROLL or %WS_HSCROLL or %LBS_MULTIPLESEL or _
                                       %LBS_HASSTRINGS or %LBS_NOINTEGRALHEIGHT or %LBS_EXTENDEDSEL or %LBS_DISABLENOSCROLL, _
                                       0, 0, UseW, UseH, _
                                       hWnd, %ID_LISTBOX, hInstance, byval %NULL)
                SendMessage(hCtrl, %WM_SETFONT, GetStockObject(%ANSI_FIXED_FONT), 0)
                if (gP.usescrollbar) then
                    SendMessage(hCtrl, %LB_SETHORIZONTALEXTENT, %HORIZONTAL_EXTENT, 0)
                    ShowScrollBar(hCtrl, %SB_HORZ, %TRUE)
                else
                    ShowScrollBar(hCtrl, %SB_HORZ, %FALSE)
                end if
               
                if (gP.topmost) then
                    SetWindowPos(hWnd, %HWND_TOPMOST, 0, 0, 0, 0, %SWP_NOSIZE or %SWP_NOMOVE or %SWP_SHOWWINDOW)
                else
                    ShowWindow(hWnd, %SW_SHOW)
                end if
                UpdateWindow(hWnd)
               
                while GetMessage(Msg, %NULL, 0, 0)
                    '// Easier to detect the right mouse button click on the listBox from the message pump
                    if ((Msg.hWnd = hCtrl) and (Msg.Message = %WM_RBUTTONDOWN)) then
                        ToolProc(hWnd, %WM_RBUTTONDOWN, 0, 0)
                    else
                        TranslateMessage(Msg)
                        DispatchMessage(Msg)
                    end if
                wend
               
                SHChangeNotifyDeregister(RegID) '// Clean up on exit
                function = msg.wParam
            end if

       end if

    end if

end function
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Norbert Spoerl

Thank you guys, this is very usefull for me!

Carlo Pagani

Thanks Patrice and Pierre

From a technical discussion point I would like to know if you have any reason for using SHChangeNotifyRegister over ReadDirectoryChangesW?

Ciao - Carlo

Patrice Terrier

Registers a window to receive notifications from the file system or Shell

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Pierre Bellisle

#4
Hey,

For me, both are nice, always depends on what you want.
One thing for sure is that SHChangeNotifyRegister offer more options if you want to customize the code.

ReadDirectoryChangesW function https://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx

SHChangeNotifyRegister function https://msdn.microsoft.com/en-us/library/windows/desktop/bb762120(v=vs.85).aspx

Options:
SHCNE_ALLEVENTS SHCNE_ASSOCCHANGED SHCNE_ATTRIBUTES SHCNE_CREATE SHCNE_DELETE SHCNE_DRIVEADD SHCNE_DRIVEADDGUI
SHCNE_DRIVEREMOVED SHCNE_EXTENDED_EVENT SHCNE_FREESPACE SHCNE_MEDIAINSERTED SHCNE_MEDIAREMOVED SHCNE_MKDIR
SHCNE_NETSHARE SHCNE_NETUNSHARE SHCNE_RENAMEFOLDER SHCNE_RENAMEITEM SHCNE_RMDIR SHCNE_SERVERDISCONNECT SHCNE_UPDATEDIR
SHCNE_UPDATEIMAGE SHCNE_UPDATEITEM SHCNE_DELETE SHCNE_RENAMEITEM SHCNE_DISKEVENTS SHCNE_GLOBALEVENTS SHCNE_INTERRUPT

For those who may be interested here is some piece of code that play with ReadDirectoryChangesW.

Pierre

#COMPILE EXE
#INCLUDE "win32Api.inc"

DECLARE FUNCTION ReadDirectoryChangesW LIB "Kernel32.dll" ALIAS "ReadDirectoryChangesW" _
(BYVAL hDirectory AS DWORD, BYVAL lpBuffer AS DWORD, BYVAL nBufferLength AS DWORD, _
BYVAL bWatchSubtree AS LONG, BYVAL dwNotifyFilter AS DWORD, lpBytesReturned AS DWORD, _
lpOverlapped AS OVERLAPPED, BYVAL lpCompletionRoutine AS DWORD) AS LONG

GLOBAL hDlg AS DWORD

%Edit01 = 101
'__________________________________________________________________________

SUB TextAdd(BYVAL sText AS STRING)

'Move the caret to the end of text.
SendDlgItemMessage(hDlg, %Edit01, %EM_SETSEL, -1, -1)

sText = sText & $CRLF 'Add a CRLF if needed

'Insert the string at caret position.
SendDlgItemMessage(hDlg, %Edit01, %EM_REPLACESEL, %TRUE, BYVAL STRPTR(sText))

END SUB
'______________________________________________________________________________

THREAD FUNCTION GetFolderChangeNotification(BYVAL pFolder AS STRING POINTER) AS LONG
  LOCAL pChangeInfo        AS FILE_NOTIFY_INFORMATION POINTER
  LOCAL sAnsiFileName      AS STRING
  LOCAL sLog               AS STRING
  LOCAL hFile              AS DWORD
  LOCAL pChangeInfoBuffer  AS DWORD
  LOCAL BufferSize         AS DWORD
  LOCAL BytesReturnedCount AS DWORD
  LOCAL SubFolder          AS LONG

  hFile = CreateFile(BYVAL pFolder, %FILE_LIST_DIRECTORY, _
                     %FILE_SHARE_READ OR %FILE_SHARE_WRITE OR %FILE_SHARE_DELETE, _
                     BYVAL %NULL, %OPEN_EXISTING, %FILE_FLAG_BACKUP_SEMANTICS, %NULL)
  IF hFile = %INVALID_HANDLE_VALUE THEN
    MessageBox(%HWND_DESKTOP, "CreateFile error!", "ReadDirectoryChangesW", %MB_TOPMOST)
  ELSE
    BufferSize        = 1023
    pChangeInfoBuffer = GlobalAlloc(%GMEM_FIXED OR %GMEM_ZEROINIT, BufferSize)
    SubFolder         = %FALSE
    DO 'Wait for a change
      ReadDirectoryChangesW(hFile, pChangeInfoBuffer, GlobalSize(pChangeInfoBuffer), SubFolder, _
                            %FILE_NOTIFY_CHANGE_FILE_NAME      OR _
                            %FILE_NOTIFY_CHANGE_LAST_WRITE     OR _
                            _ %FILE_NOTIFY_CHANGE_DIR_NAME     OR _
                            _ %FILE_NOTIFY_CHANGE_ATTRIBUTES   OR _
                            _ %FILE_NOTIFY_CHANGE_SIZE         OR _
                            _ %FILE_NOTIFY_CHANGE_LAST_ACCESS  OR _
                            _ %FILE_NOTIFY_CHANGE_CREATION     OR _
                            _ %FILE_NOTIFY_CHANGE_SECURITY     OR _
                            0, BytesReturnedCount, BYVAL 0, 0)
      IF BytesReturnedCount THEN
        sLog = "BytesReturnedCount " & STR$(BytesReturnedCount) & $CRLF & $CRLF
        pChangeInfo = pChangeInfoBuffer
        DO
          sAnsiFileName = NUL$(@pChangeInfo.FileNameLength)
          WideCharToMultiByte(%CP_ACP, %NULL, BYVAL pChangeInfo + 12, @pChangeInfo.FileNameLength / 2, _
                              BYVAL STRPTR(sAnsiFileName), @pChangeInfo.FileNameLength / 2, BYVAL %NULL, BYVAL %NULL)
          sAnsiFileName = RTRIM$(sAnsiFileName, $NUL)

          sLog = sLog & "NextEntryOffset" & STR$(@pChangeInfo.NextEntryOffset) & $CRLF & _
                        "Action " & CHOOSE$(@pChangeInfo.Action, _
                                            "%FILE_ACTION_ADDED", "%FILE_ACTION_REMOVED", "%FILE_ACTION_MODIFIED", _
                                            "%FILE_ACTION_RENAMED_OLD_NAME", "%FILE_ACTION_RENAMED_NEW_NAME") & $CRLF & _
                        "FileNameLength " & STR$(@pChangeInfo.FileNameLength) & $CRLF & _
                        "FileName " & sAnsiFileName & $CRLF & $CRLF
          IF @pChangeInfo.NextEntryOffset = 0 THEN EXIT LOOP
          pChangeInfo = pChangeInfo + @pChangeInfo.NextEntryOffset
        LOOP
        TextAdd(sLog & STRING$(80, 95) & $CRLF)
      END IF
    LOOP
    GlobalFree(pChangeInfoBuffer)
    CloseHandle(hFile)
  END IF

END FUNCTION
'______________________________________________________________________________

CALLBACK FUNCTION ShowDIALOG1Proc()
LOCAL sFolder AS STRING
LOCAL hThread AS DWORD

SELECT CASE AS LONG CBMSG

   CASE %WM_INITDIALOG
     sFolder = "C:\Tmp"
     TextAdd("Monitoring folder " & sFolder & $CRLF & STRING$(80, 95) & $CRLF)
     PostMessage(GetDlgItem(hDlg, %Edit01), %EM_SETSEL, -1, -1)
     THREAD CREATE GetFolderChangeNotification(BYVAL STRPTR(sFolder)) TO hThread
     THREAD CLOSE hThread TO hThread

   CASE %WM_SIZE
     IF CBWPARAM <> %SIZE_MINIMIZED THEN
       SetWindowPos(GetDlgItem(hDlg, %Edit01), 0, 5, 5, LO(WORD, CBLPARAM) - 10, HI(WORD, CBLPARAM) - 10, %SWP_NOZORDER)
     END IF

END SELECT

END FUNCTION
'______________________________________________________________________________

FUNCTION PBMAIN()
LOCAL hIcon AS DWORD

DIALOG NEW %HWND_DESKTOP, "ReadDirectoryChangesW", , , 400, 250, %WS_POPUP OR %WS_BORDER OR _
%WS_DLGFRAME OR %WS_THICKFRAME OR %WS_CAPTION OR %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR %WS_CLIPSIBLINGS OR _
%WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR _
%WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg

hIcon = ExtractIcon(GetModuleHandle(""), "%SystemRoot%\system32\powrprof.dll", 1)
SetClassLong(hDlg, %GCL_HICON, hIcon)

CONTROL ADD TEXTBOX, hDlg, %Edit01, _
"Create, copy, rename, edit, delete files etc. in folder...", _
5, 5, 940, 225, %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %WS_HSCROLL OR %WS_VSCROLL OR _
%ES_LEFT OR %ES_MULTILINE OR %ES_AUTOHSCROLL OR %ES_AUTOVSCROLL OR %ES_WANTRETURN OR _
%ES_NOHIDESEL, %WS_EX_CLIENTEDGE OR %WS_EX_LEFT OR %WS_EX_RIGHTSCROLLBAR OR %WS_EX_LTRREADING

DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc
DestroyIcon(hIcon)

END FUNCTION
'______________________________________________________________________________
'


Carlo Pagani

QuoteRegisters a window to receive notifications from the file system or Shell
I suspect that this may not work as a service which is why I probably use the  ReadDirectoryChangesW

Pierre Bellisle

#6
Hey,

Yep, since a service by nature do not include a window and the corespondent callback function,
to use SHChangeNotifyRegister you sure will need some more work.

SHChangeNotifyRegister: Registers a window to receive notifications...  ... wMsg: Message to be posted to the window procedure...

Pierre

Added:
You could use RegisterClass, CreateWindow to create a hidden window in
a thread. (Not WS_VISIBLE) or experiment with RegisterServiceCtrlHandlerEx.

Patrice Terrier

#7
The purpose of the WatchDog UI when receiving notifications
was to easily, cut and paste, save, print, what is going on.
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com