Sunday, September 9, 2012

Get file change notifications in C++ (using C#'s FileSystemWatcher)

In order to make the workflow in ShootEditor more efficient, I speeded up the process that takes places after a resource is changed (textures, mesh, shaders (Yes Shaders!! Shoot now supports GLSL shaders but that is another subject!!).

Previously, I needed to manually select the entity referencing the changed resource and click "Reload" (or reload the entire level if the concerned entity was not instantiated from a template). If there were other entities referencing the same resource, they'd get updated as well. It worked fine for a while, but it quickly became annoying when I started using shaders and needed to frequently switch back and forth between my shader editor and ShootEditor.

This process is now much more efficient. By getting file change notifications, I can automatically reload the concerned resources whenever a file changes in my data folder.

At first, I tried to implement this feature using C++/Win32, using FindFirstChangeNotification. This function turned out to be completely useless, because it doesn't communicate information on what file changed. It merely gives a notification with zero information. Then, I found about ReadDirectoryChangesW which seemed more powerful, but much more complex to use and properly undocumented.

Fortunately, ReadDirectoryChangesW was such a pain to use that I came across a really nice alternative while I was googling for more info. It is called FileSystemWatcher. The only thing is that it is part of the C# framework, and uhhhmmm.. both Shoot and ShootEditor are in C++.

But no panic, it's possible to use a C# component from C++, using Microsoft's CLR component. I did it before on the dead Shoot CLR build that was destined to run as unmanaged code in XNA. Few words about CLR: it is a powerful runtime component of the .NET framework that allows execution of code compiled from a mix different languages. This is a great way to get the best out of 2 worlds: C++ and C#. There is a new syntax to learn that is slightly different from C++, but nothing major.

So without further delays, here is an example of how to use FileSystemWatcher from C++. The only thing you need to make sure of, is that you compile with CLR support (See VCProj->Properties->General)


#include <iostream>

#include <vcclr.h>
#using <System.dll>
using namespace System::IO;
using namespace System;
using namespace System::Runtime::InteropServices;

void OnFileChanged(Object^ source, FileSystemEventArgs^ args)
{
 char* cstr = (char*)((void*)Marshal::StringToHGlobalAnsi(args->FullPath)); 
 std::cout << "File " << cstr << "Changed\n";
}

int main(int argc, char** argv)
{
 FileSystemWatcher^ watcher = gcnew FileSystemWatcher("./data", "*.*");
 watcher->NotifyFilter = NotifyFilters::LastWrite;
 watcher->IncludeSubdirectories = true;

 // register handler
 watcher->Changed += gcnew FileSystemEventHandler(OnFileChanged);

 // start watching file system
 watcher->EnableRaisingEvents = true;

 while(true)
 {
 }

 return 0;
}

No comments:

Post a Comment