I need to create a record file activity on my USB drive.
What I can do so far:
It detects the insertion and extraction (detects the extraction after the memory was removed) of a USB memory using ManagementEventWatcher:
using System.Management; var insertQuery = new WqlEventQuery("SELECT * FROM Win32_VolumeChangeEvent WHERE EventType = 2"); var insertWatcher = new ManagementEventWatcher(insertQuery); insertWatcher.EventArrived += DeviceInsertedEvent; insertWatcher.Start();
Using
FileSystemWatcher
I can record all the action, ex. Files created, modified, deleted and renamed:FileSystemWatcher m_Watcher = new FileSystemWatcher(); m_Watcher.Path = e.PathDevice + "\\"; string strFilter = "*.*"; m_Watcher.Filter = strFilter; m_Watcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.FileName; m_Watcher.IncludeSubdirectories = true; m_Watcher.Created += new FileSystemEventHandler(OnCreated); m_Watcher.EnableRaisingEvents = true;
The problem:
As FileSystemWatcher is looking at the unit, if I try to eject the unit safely it tells me that I can not because the process of my application is using it. This variable when set to true EnableRaisingEvents = true;
enable the events to listen to the changes in the memory and is the one that does not allow me to remove the memory safely, I did a test and start the application and I set it to false EnableRaisingEvents = false;
and I can remove the memory safely.
Possible solution:
How can I detect the removal of the device before the operating system removes it? This way I can stop using the FileSystemWatcher on the device and remove the usb device safely.
After several days looking for an answer to the planted problem I found the solution.
This article was very helpful to solve the problem and this.
The solution changes if we are using a service or a windows form application. My answer is based on a service.
Services have a control handler which receives all messages from Windows. These might include codes to stop or pause the service, or as in our case, device events. We need to register our own service handler, so we could catch device events. This would disable all callbacks like OnStop except OnStart, which is called before we tell Windows to use our handler.
The Windows API function for this is RegisterServiceCtrlHandlerEx, which accepts a service name and a callback function to call when a message is received. We will call it in the OnStart function in our service.
The service control handler's signature is like this:
Register for Device Notifications
In the OnStart method, apart from registering for a control handler, we will register for device notifications by using the
Win32
API function RegisterDeviceNotification. We give it our service's handle, a pointer to aDEV_BROADCAST_DEVICEINTERFACE
struct (telling the function to register for a class of devices), and some flags, among which is theDEVICE_NOTIFY_SERVICE_HANDLE
, which specifies that the caller is a service and not a window, for example. It returns a handle, which we must preserve in order to unregister when we don't need device messages anymore (for example, we could do this in theSERVICE_CONTROL_STOP
event).Using this function allows us to capture the
DBT_DEVICEARRIVAL
andDBT_DEVICEREMOVECOMPLETE
event types. We get them through the eventType parameter of our Service Control Handler. There, we can handle theSERVICE_CONTROL_DEVICEEVENT
and do anything we like.DBT_DEVICEQUERYREMOVE - The Solution
Here is the main thing to answer the question I needed notifies to application of a change to the hardware configuration of a device. Specifically a USB memory change before SO will remove the memory. Thanks to @ZdeněkJelínek I was able to find the event
DBT_DEVICEQUERYREMOVE
that permission is requested to remove a device or piece of media. This event is being held just before a device is about to be removed.The solution is to create a handle to the device itself, use it in a
DEV_BROADCAST_HANDLE
structure, and register with it to our Service Control Handler. In order to accomplish all this, it takes a couple of things:Find which drive letter the device. I was able using the
ManagementEventWatcher
class, which allowed me to subscribe to the device insertion event and it provides me with information about the inserted device. All this was done with the in order to get a device handle using theCreateFileHandle
function. Only after that are we able to get theDBT_DEVICEQUERYREMOVE
event, disable theFileSystemWatcher
, and allow the USB to be freely removed.