RegisterDeviceNotification() returns NULL

Heiko 1,211 Reputation points
2023-08-14T16:55:26.52+00:00

I have an app that creates FileSystemWatchers for root directories. In order for externally attached storage (USB sticks) to be removed easily, I have to release the appropriate FileSystemWatcher when Windows requests to remove a drive. If the FileSystemWatcher were left in place, Windows would display a message that the device is still in use and cannot be removed. In order for me to receive the request to remove the device in my app, I have to first register my window for the request with 'RegisterDeviceNotification()' and later release the FileSystemWatcher when I receive the WM_DEVICECHANGE message with DBT_DEVICEQUERYREMOVE.

C++/CLI:

NativeHelpers::RegisterDeviceNotificationResult^ NativeHelpers::ShellHelper::RegisterDeviceNotificationForWindow(IntPtr hWnd, String ^ drive)
{
	if (String::IsNullOrWhiteSpace(drive))
		return nullptr;

	pin_ptr<const wchar_t>	str = PtrToStringChars(drive);

	HANDLE	hDrive = CreateFile(str, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

	if (hDrive == INVALID_HANDLE_VALUE)
		return nullptr;

	DEV_BROADCAST_HANDLE	NotificationFilter;
	GUID					my_GUID_IO_MEDIA_EJECT_REQUEST;

	HRESULT	hRes = CLSIDFromString(L"{d07433d1-a98e-11d2-917a-00a0c9068ff3}", &my_GUID_IO_MEDIA_EJECT_REQUEST);

	ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
	NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
	NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
	NotificationFilter.dbch_handle = hDrive;
	if (hRes == S_OK)
		NotificationFilter.dbch_eventguid = my_GUID_IO_MEDIA_EJECT_REQUEST;

	HDEVNOTIFY	hNotify = ::RegisterDeviceNotificationW((HANDLE)hWnd.ToInt64(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);

	if (hNotify == NULL)
	{
		DWORD	err = GetLastError();

		::CloseHandle(hDrive);
		return nullptr;
	}

	RegisterDeviceNotificationResult	^ result = gcnew RegisterDeviceNotificationResult();

	result->DeviceHandle = (IntPtr)hDrive;
	result->DevNotifyHandle = (IntPtr)hNotify;
	return result;
}

C#:

private IntPtr OnWindowMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
	if (msg == NI.WM_DEVICECHANGE)	// WM_DEVICECHANGE
	{
		if (wParam.ToInt64() == 0x0000000000008001L)	// DBT_DEVICEQUERYREMOVE
		{
			handled = true;

			NI.DEV_BROADCAST_HANDLE	dbHandle = (NI.DEV_BROADCAST_HANDLE)System.Runtime.InteropServices.Marshal.PtrToStructure(lParam, typeof(NI.DEV_BROADCAST_HANDLE));

			// Unregister device notification and remove FileSystemWatcher.
			ClearDeviceNotification(dbHandle.DevNotifyHandle);
			return new IntPtr(1);	// Ejection of USB memory is allowed.
		}
	}
	return IntPtr.Zero;
}

So far this works very well with normal storage devices like USB sticks. 'OSF Mount' is an app that allows you to open vmdk files created by VMWare Player (or other container formats) and add them as a normal drive. However, when using the 'OSF Mount' app, 'RegisterDeviceNotification()' returns NULL and 'GetLastError()' returns 1066 (ERROR_SERVICE_SPECIFIC_ERROR; 'The service has returned a service-specific error code.'). So 'hNotify' is NULL. Nevertheless, when requested to eject the drive, I get a WM_DEVICECHANGE message with DBT_DEVICEQUERYREMOVE to my window. This is accompanied by a 'DevNotifyHandle', but my app does not know this, as 'RegisterDeviceNotification()' returned NULL. Therefore, my app cannot associate and release a FileSystemWatcher with this 'DevNotifyHandle'. This prevents the drive from being dismounted without errors.

DBT_DEVICEQUERYREMOVE

Does anyone have an idea how I can solve the problem? Can I fix the LastError 1066 or is it possible to determine the associated drive to the 'DevNotifyHandle' supplied by WM_DEVICECHANGE?

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,673 questions
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,422 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,253 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,529 questions
{count} votes