Jump to content

Recommended Posts

Posted

So the function pauses? Like when you call it, it doesn't come back until a change happens? That doesn't sound very asynchronous to me. I would assume the call would return right away, and the function you provided as the callback gets called when something changes in the directory.

Posted

This code works, & I got it from the last post at http://cboard.cprogramming.com/windows-programming/77061-readdirectorychangesw.html. He talks about a special message loop. I'm not sure if the message loop is hidden from the users in BMax so not sure if it can be used in BMax.

 

#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <shlwapi.h>

#if defined(_MSC_VER)
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "ole32.lib")
#endif

HINSTANCE g_hinst;


typedef void (CALLBACK *FileChangeCallback)(LPTSTR, DWORD, LPARAM);

typedef struct tagDIR_MONITOR
{
OVERLAPPED ol;
HANDLE     hDir;
BYTE       buffer[32 * 1024];
LPARAM     lParam;
DWORD      notifyFilter;
BOOL       fStop;
FileChangeCallback callback;
} *HDIR_MONITOR;

/* 
* Unpacks events and passes them to a user defined callback.
*/
VOID CALLBACK MonitorCallback(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
TCHAR                    szFile[MAX_PATH];
PFILE_NOTIFY_INFORMATION pNotify;
HDIR_MONITOR             pMonitor  = (HDIR_MONITOR) lpOverlapped;
size_t                   offset    =  0;
BOOL RefreshMonitoring(HDIR_MONITOR pMonitor);

if (dwErrorCode == ERROR_SUCCESS)
{
	do
	{
		pNotify = (PFILE_NOTIFY_INFORMATION) &pMonitor->buffer[offset];
		offset += pNotify->NextEntryOffset;

#			if defined(UNICODE)
		{
		    lstrcpynW(szFile, pNotify->FileName,
		                min(MAX_PATH, pNotify->FileNameLength / sizeof(WCHAR) + 1));
		}
#			else
		{
		    int count = WideCharToMultiByte(CP_ACP, 0, pNotify->FileName,
		                                    pNotify->FileNameLength / sizeof(WCHAR),
		                                    szFile, MAX_PATH - 1, NULL, NULL);
		    szFile[count] = TEXT('\0');
		}
#			endif

		pMonitor->callback(szFile, pNotify->Action, pMonitor->lParam);

	} while (pNotify->NextEntryOffset != 0);
}

if (!pMonitor->fStop)
{
	RefreshMonitoring(pMonitor);
}
}

/*
* Refreshes the directory monitoring.
*/
BOOL RefreshMonitoring(HDIR_MONITOR pMonitor)
{
return
ReadDirectoryChangesW(pMonitor->hDir, pMonitor->buffer, sizeof(pMonitor->buffer), FALSE,
                      pMonitor->notifyFilter, NULL, &pMonitor->ol, MonitorCallback);
}

/*
* Stops monitoring a directory.
*/
void StopMonitoring(HDIR_MONITOR pMonitor)
{
if (pMonitor)
{
	pMonitor->fStop = TRUE;

	CancelIo(pMonitor->hDir);

	if (!HasOverlappedIoCompleted(&pMonitor->ol))
	{
		SleepEx(5, TRUE);
	}

	CloseHandle(pMonitor->ol.hEvent);
	CloseHandle(pMonitor->hDir);
	HeapFree(GetProcessHeap(), 0, pMonitor);
}
}

/*
* Starts monitoring a directory.
*/
HDIR_MONITOR StartMonitoring(LPCTSTR szDirectory, DWORD notifyFilter, FileChangeCallback callback)
{
HDIR_MONITOR pMonitor = (HDIR_MONITOR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pMonitor));

pMonitor->hDir = CreateFile(szDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                            NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);

if (pMonitor->hDir != INVALID_HANDLE_VALUE)
{
	pMonitor->ol.hEvent    = CreateEvent(NULL, TRUE, FALSE, NULL);
	pMonitor->notifyFilter = notifyFilter;
	pMonitor->callback     = callback;

	if (RefreshMonitoring(pMonitor))
	{
		return pMonitor;
	}
	else
	{
		CloseHandle(pMonitor->ol.hEvent);
		CloseHandle(pMonitor->hDir);
	}
}

HeapFree(GetProcessHeap(), 0, pMonitor);
return NULL;
}

/*
* Runs a message loop that allows APCs to be despatched.
*/
int RunAPCMessageLoop(void)
{
BOOL done = FALSE;
MSG  msg  = { 0 };

while (!done)
{
	/* Wait for either an APC or a message. */
	while (WAIT_IO_COMPLETION == 
	        MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE)) /* Do nothing */;

	/* One or more messages have arrived. */
	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		if (msg.message == WM_QUIT)
		{
			done = TRUE;
			break;
		}

		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

return (int) msg.wParam;
}



void CALLBACK FileCallback(LPTSTR szFile, DWORD action, LPARAM lParam)
{
/* Shouldn't call MessageBox as it will block new notifications coming in. */
MessageBox(NULL, szFile, NULL, 0);
}

HDIR_MONITOR h;

BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
h = StartMonitoring(TEXT("C:\\Documents and Settings\\Games\\My Documents"),
                FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_FILE_NAME,
                FileCallback);

return TRUE;
}

void OnDestroy(HWND hwnd)
{
if (h) StopMonitoring(h);

PostQuitMessage(0);
}


LRESULT CALLBACK WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
   switch (uiMsg) 
   {
       HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
       HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
   }

   return DefWindowProc(hwnd, uiMsg, wParam, lParam);
}

BOOL InitApp(void)
{
   WNDCLASS wc = { 0 };

   wc.style         = 0;
   wc.lpfnWndProc   = WndProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = g_hinst;
   wc.hIcon         = NULL;
   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = TEXT("Scratch");

   if (!RegisterClass(&wc)) return FALSE;

   return TRUE;
}



int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                  LPSTR lpCmdLine, int nShowCmd)
{
   HWND hwnd;
   int  result;

   g_hinst = hinst;

   if (!InitApp()) return 0;

   hwnd = CreateWindow(
           TEXT("Scratch"),                      /* Class Name */
           TEXT("Scratch"),                      /* Title */
           WS_OVERLAPPEDWINDOW,                  /* Style */
           CW_USEDEFAULT, CW_USEDEFAULT,         /* Position */
           CW_USEDEFAULT, CW_USEDEFAULT,         /* Size */
           NULL,                                 /* Parent */
           NULL,                                 /* No menu */
           hinst,                                /* Instance */
           0);                                   /* No special parameters */

   ShowWindow(hwnd, nShowCmd);

   result = RunAPCMessageLoop();

   return result;
}

Posted

The docs say there are three ways of doing it, and the GetOverlappedResult() function seems like the least BS. However, it is still pausing when I call the function.

My job is to make tools you love, with the features you want, and performance you can't live without.

Posted

This is the OVERLAPPED structure:

typedef struct _OVERLAPPED {
 ULONG_PTR Internal;
 ULONG_PTR InternalHigh;
 union {
   struct {
     DWORD Offset;
     DWORD OffsetHigh;
   } ;
   PVOID  Pointer;
 } ;
 HANDLE    hEvent;
} OVERLAPPED, *LPOVERLAPPED;

 

Is this BMX code the same thing?:

Type OVERLAPPED

Method Delete()
	CloseHandle(hEvent)
EndMethod

Field Internal:Int
Field InternalHigh:Int
Field Offset:Int
Field OffsetHigh:Int
Field Pointer:Byte Ptr
Field hEvent:Int=CreateEventA(0,0,False,"MyEvent")
EndType

My job is to make tools you love, with the features you want, and performance you can't live without.

Posted

I wouldn't use Windows API calls. I've done a multithreaded directory reader with cross-platforms calls. I tested it by reading the whole directory of my harddrive, while running an LE app with a rotating cube. The cube didn't stutter or slow down at all.

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...