Win32NotificationWindow


#1

Certain Windows notifications, such as mixer control changes or device arrival/removal, are only delivered as window messages. Since window message handling is inside the JUCE native code, I have implemented my own hidden window whose sole purpose is to receive these notifications.

To use it, have one of your classes inherit from this one:

class Win32MessageListener
{
protected:
	virtual void Win32MessageReceived(UINT uMsg,WPARAM wParam,LPARAM lParam) = 0;

	friend class Win32NotificationWindow;
};[/code]


Then, implement the Win32MessageReceived method in you class and then do this:

[code]Win32NotificationWindow::instance()->addListener(this);

Here is the class definition for the notification window:

[code]class Win32NotificationWindow :
public DeletedAtShutdown
{
public:
static Win32NotificationWindow *instance();
virtual ~Win32NotificationWindow();

HWND handle() 
{
	return hWindow;
}

void addListener(Win32MessageListener *wml);
void removeListener(Win32MessageListener *wml);

protected:
Win32NotificationWindow();

static DWORD WINAPI WndThread(LPVOID context);
static LRESULT CALLBACK WndProc(HWND hwnd,
								UINT uMsg,
								WPARAM wParam,
								LPARAM lParam );

HWND		hWindow;
HANDLE		hThread;

Array<Win32MessageListener *> listeners;
CriticalSection cs;

};
[/code]

And the implementation:

#ifdef _WIN32

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "juce.h"
#include "NotificationWindow.h"

static char classname[] = "notification msg window";

static Win32NotificationWindow *gInstance = NULL;


Win32NotificationWindow *Win32NotificationWindow::instance()
{
	if (NULL == gInstance)
		gInstance = new Win32NotificationWindow;

	return gInstance;
}


Win32NotificationWindow::Win32NotificationWindow() :
	hThread(NULL),
	hWindow(NULL),
	listeners(3)
{
	//
	// register the window class
	//
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 
	wcex.style				= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc		= Win32NotificationWindow::WndProc;
	wcex.cbClsExtra			= 0;
	wcex.cbWndExtra			= 0;
	wcex.hInstance			= GetModuleHandle(NULL);
	wcex.hIcon				= 0;
	wcex.hCursor			= 0;
	wcex.hbrBackground		= (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName		= 0;
	wcex.lpszClassName		= classname;
	wcex.hIconSm			= 0;

	RegisterClassEx(&wcex);

	
	//
	// make the window
	//
	hWindow = CreateWindow(classname, NULL, WS_OVERLAPPEDWINDOW,
						CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
						NULL, NULL, GetModuleHandle(NULL), NULL);
	if (hWindow == NULL)
		return;
	
	//
	// Save the "this" pointer in the window
	//
	SetWindowLongPtr(hWindow,GWLP_USERDATA,(LONG_PTR) this);

	//
	// start the thread for the window messsage loop
	//
	DWORD threadid;

	hThread = CreateThread(NULL,
							0,
							&Win32NotificationWindow::WndThread,
							this,
							0,
							&threadid);
}



Win32NotificationWindow::~Win32NotificationWindow()
{
	if (hWindow)
	{
		PostMessage(hWindow,WM_CLOSE,0,0);

		if (hThread)
		{
			WaitForSingleObject(hThread,2000);
			CloseHandle(hThread);
		}
	}
}



LRESULT CALLBACK Win32NotificationWindow::WndProc
(          
	HWND hwnd,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam
)
{
	Win32NotificationWindow *that;
	int i;
					
	that = (Win32NotificationWindow *) GetWindowLongPtr(hwnd,GWLP_USERDATA);
	if (that)
	{
		ScopedLock sl(that->cs);

		for (i = 0; i < that->listeners.size(); i++)
		{
			that->listeners[i]->Win32MessageReceived(uMsg,wParam,lParam);
		}
	}

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


DWORD WINAPI Win32NotificationWindow::WndThread(LPVOID context)
{
	MSG Msg;
	Win32NotificationWindow *that;
	int rval;

	that = (Win32NotificationWindow *) context;

	//
	// listen to the message loop
	//
	while ( (rval = GetMessage(&Msg, that->hWindow, 0, 0)) != 0)
	{
		if (-1 == rval)
			return 0;

		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	} 

	return (DWORD) Msg.wParam;
}


void Win32NotificationWindow::addListener(Win32MessageListener *wml)
{
	ScopedLock sl(cs);

	listeners.addIfNotAlreadyThere(wml);
}


void Win32NotificationWindow::removeListener(Win32MessageListener *wml)
{
	ScopedLock sl(cs);

	listeners.removeValue(wml);
}


#endif

JUCE and multiple mice input
#2

I actually made something very similar - although it was tweaked to use params that allowed the base class to be included without worry of windows header clashes.

I spoke to jules about it, and he pointed out that juce effectively does the same thing for its messaging - using a dummy window to get messages; if juce were to ever get a PlatformUtilities helper for this purpose, the main message window would be a sensible place to add support for it.

And so, I made a patch for juce to achieve this. If you apply it, you can do the following:

  • Subclass PlatformUtilities::WindowsMessageHandler
  • implement handleMessage (int message, int wParam, int lParam) as your callback.
  • call registerForMessage (int message) to make sure the message window knows where to pass the message on to.

And that’s it.

The patch includes a macro in juce_config.h to bypass support for this entirely.

Just save this to a text file of the name given (or similar - with the .patch extension), and apply it using SVN tools (assuming you’re using the SVN)

JucePlatformMessagingExtensions.patch

Index: build/win32/platform_specific_code/juce_win32_Messaging.cpp
===================================================================
--- build/win32/platform_specific_code/juce_win32_Messaging.cpp	(revision 304)
+++ build/win32/platform_specific_code/juce_win32_Messaging.cpp	(working copy)
@@ -52,7 +52,69 @@
 
 extern long improbableWindowNumber; // defined in windowing.cpp
 
+//===============================================
+#ifdef JUCE_PLATFORM_MESSAGING_EXTENSIONS
 
+class WindowsMessageAssignmentManager
+{
+public:
+
+	void assignHandlerToMessage (UINT message, PlatformUtilities::WindowsMessageHandler* handler)
+	{
+		if (registeredMessages.contains (message))
+		{
+			// Replace the assignment...
+			int i = registeredMessages.indexOf (message);
+			registeredHandlers.set (i, handler);
+		}
+		else
+		{
+			jassert (registeredMessages.size () == registeredHandlers.size());	// Sizes must match or table is broken!
+			// Add the message ID to the sorted set...
+			registeredMessages.add (message);
+			int i = registeredMessages.indexOf (message);
+			// Insert the handler at the corresponding place...
+			registeredHandlers.insert (i, handler);
+		}	
+	}
+
+	bool isMessageAssigned (UINT message)
+	{
+		return registeredMessages.contains (message);
+	}
+
+	void removeAssignmentsForHandler (PlatformUtilities::WindowsMessageHandler* handler)
+	{
+		if (handler == 0) return;
+
+		while (registeredHandlers.contains (handler))
+		{
+			int i = registeredHandlers.indexOf (handler);
+			registeredHandlers.remove (i);
+			registeredMessages.remove (i);
+		}
+	}
+
+	PlatformUtilities::WindowsMessageHandler* findHandlerFor (UINT message)
+	{
+		jassert (registeredMessages.size () == registeredHandlers.size());	// Sizes must match or table is broken!
+
+		int i = registeredMessages.indexOf (message);
+		if (i >= 0)
+		{
+			return registeredHandlers.getUnchecked (i);
+		}
+		return 0;
+	}
+
+private:
+	SortedSet<UINT,CriticalSection> registeredMessages;
+	Array<PlatformUtilities::WindowsMessageHandler*,CriticalSection> registeredHandlers;
+};
+
+WindowsMessageAssignmentManager* handlerAssignments = 0;
+
+#endif//JUCE_PLATFORM_MESSAGING_EXTENSIONS
 //==============================================================================
 static LRESULT CALLBACK juce_MessageWndProc (HWND h,
                                              const UINT message,
@@ -92,6 +154,16 @@
                 PostMessage (juce_messageWindowHandle, broadcastId, 0, (LPARAM) new String (messageString));
                 return 0;
             }
+#ifdef JUCE_PLATFORM_MESSAGING_EXTENSIONS
+			else if (handlerAssignments->isMessageAssigned (message))
+			{
+				PlatformUtilities::WindowsMessageHandler* handler = handlerAssignments->findHandlerFor (message);
+				if (handler != 0)
+				{
+					return handler->handleMessage (message, (int) wParam, (int) lParam);
+				}
+			}
+#endif//JUCE_PLATFORM_MESSAGING_EXTENSIONS
         }
     }
     JUCE_CATCH_EXCEPTION
@@ -222,6 +294,10 @@
 {
     OleInitialize (0);
 
+#ifdef JUCE_PLATFORM_MESSAGING_EXTENSIONS
+	handlerAssignments = new WindowsMessageAssignmentManager;
+#endif//JUCE_PLATFORM_MESSAGING_EXTENSIONS
+
     const String className (getMessageWindowClassName());
 
     HMODULE hmod = (HMODULE) PlatformUtilities::getCurrentModuleInstanceHandle();
@@ -248,6 +324,32 @@
     DestroyWindow (juce_messageWindowHandle);
     UnregisterClass (getMessageWindowClassName(), 0);
     OleUninitialize();
+#ifdef JUCE_PLATFORM_MESSAGING_EXTENSIONS
+	deleteAndZero (handlerAssignments);
+#endif
 }
 
+//==============================================================================
+#ifdef JUCE_PLATFORM_MESSAGING_EXTENSIONS
+PlatformUtilities::WindowsMessageHandler::WindowsMessageHandler ()
+{
+	jassert (handlerAssignments != 0);
+}
+
+PlatformUtilities::WindowsMessageHandler::~WindowsMessageHandler ()
+{
+	handlerAssignments->removeAssignmentsForHandler (this);
+}
+
+void PlatformUtilities::WindowsMessageHandler::registerForMessage (int messageToHandle)
+{
+	handlerAssignments->assignHandlerToMessage (messageToHandle, this);
+}
+
+void* PlatformUtilities::WindowsMessageHandler::getMessageWindowHandle ()
+{
+	return (void*) juce_messageWindowHandle;
+}
+#endif//JUCE_PLATFORM_MESSAGING_EXTENSIONS
+
 END_JUCE_NAMESPACE
Index: juce_Config.h
===================================================================
--- juce_Config.h	(revision 304)
+++ juce_Config.h	(working copy)
@@ -175,5 +175,10 @@
 #endif
 
 //=============================================================================
+/** Uncomment this macro to allow the additional platform-specific
+	messaging extensions in PlatformUtilities. */
+#ifndef JUCE_PLATFORM_MESSAGING_EXTENSIONS
+  #define JUCE_PLATFORM_MESSAGING_EXTENSIONS 1
+#endif
 
 #endif
Index: src/juce_core/misc/juce_PlatformUtilities.h
===================================================================
--- src/juce_core/misc/juce_PlatformUtilities.h	(revision 304)
+++ src/juce_core/misc/juce_PlatformUtilities.h	(working copy)
@@ -134,6 +134,26 @@
     */
     static void JUCE_CALLTYPE setCurrentModuleInstanceHandle (void* newHandle) throw();
 
+    /** WIN32 ONLY - A class to communicate with Windows via lower-level messages.
+			[Only available if JUCE_PLATFORM_MESSAGING_EXTENSIONS is defined
+			 in juce_Config.h]
+    */
+	class WindowsMessageHandler
+	{
+	public:
+
+		WindowsMessageHandler ();
+		virtual ~WindowsMessageHandler ();
+
+		void registerForMessage (int messageToHandle);
+		virtual int handleMessage (int message, int wParam, int lParam) = 0;
+
+		void* getMessageWindowHandle ();
+
+	private:
+
+	};
+
 #endif
 
     /** Clears the floating point unit's flags.

#3

Here’s the same thing, with a few improvements:

-message-only windows are apparently supposed to use the special handle value HWND_MESSAGE for their parent

-the window is now created in the same thread as the message loop; it cleans up more nicely this way

-now using the JUCE Thread class

haydxn’s approach looks good too. I personally prefer this way because I don’t like modifying JUCE if I can help it; it’s too much trouble to put my own diffs back in when a new version comes out.

[code]#ifdef _WIN32

#ifndef notification_window_h
#define notification_window_h

class Win32MessageListener
{
protected:
virtual void Win32MessageReceived(UINT uMsg,WPARAM wParam,LPARAM lParam) = 0;

friend class Win32NotificationWindow;

};

class Win32NotificationWindow :
public Thread,
public DeletedAtShutdown
{
public:
static Win32NotificationWindow *instance();
virtual ~Win32NotificationWindow();

HWND handle() 
{
	return hWindow;
}

void addListener(Win32MessageListener *wml);
void removeListener(Win32MessageListener *wml);

protected:
Win32NotificationWindow();

static DWORD WINAPI WndThread(LPVOID context);
static LRESULT CALLBACK WndProc(HWND hwnd,
								UINT uMsg,
								WPARAM wParam,
								LPARAM lParam );

virtual void run();

HWND		hWindow;
HANDLE		hThread;

Array<Win32MessageListener *> listeners;
CriticalSection cs;

};

#endif

#endif[/code]

[code]#ifdef _WIN32

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include “juce.h”
#include “NotificationWindow.h”

static char classname[] = “notification msg window”;

static Win32NotificationWindow *gInstance = NULL;

Win32NotificationWindow *Win32NotificationWindow::instance()
{
if (NULL == gInstance)
gInstance = new Win32NotificationWindow;

return gInstance;

}

Win32NotificationWindow::Win32NotificationWindow() :
Thread(“win32 notification thread”),
hWindow(NULL),
listeners(3)
{
startThread();
}

Win32NotificationWindow::~Win32NotificationWindow()
{
if (hWindow)
{
PostMessage(hWindow,WM_CLOSE,0,0);
waitForThreadToExit(2000);
}
}

LRESULT CALLBACK Win32NotificationWindow::WndProc
(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
LONG_PTR context;
Win32NotificationWindow *that;
int i;

context = GetWindowLongPtrA(hwnd,GWLP_USERDATA);
that = (Win32NotificationWindow *) context;
if (that)
{
	ScopedLock sl(that->cs);

	for (i = 0; i < that->listeners.size(); i++)
	{
		that->listeners[i]->Win32MessageReceived(uMsg,wParam,lParam);
	}
}

return DefWindowProc(hwnd,uMsg,wParam,lParam);

}

void Win32NotificationWindow::run()
{
MSG Msg;
int rval;

//
// register the window class
//
WNDCLASSEXA wcex;

wcex.cbSize				= sizeof(WNDCLASSEX); 
wcex.style				= CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc		= Win32NotificationWindow::WndProc;
wcex.cbClsExtra			= 0;
wcex.cbWndExtra			= 0;
wcex.hInstance			= GetModuleHandle(NULL);
wcex.hIcon				= 0;
wcex.hCursor			= 0;
wcex.hbrBackground		= (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName		= 0;
wcex.lpszClassName		= classname;
wcex.hIconSm			= 0;

RegisterClassExA(&wcex);


//
// make the window
//
hWindow = CreateWindowExA(	0,
							classname, 
							NULL, 
							0,
							0,0,0,0,
							HWND_MESSAGE,NULL,
							GetModuleHandle(NULL), NULL);
if (NULL == hWindow)
	return;

//
// Save the "this" pointer in the window
//
SetWindowLongPtrA(hWindow,(int) GWLP_USERDATA,(__int3264) (LONG_PTR) this);

//
// listen to the message loop
//
while ( (rval = GetMessage(&Msg, hWindow, 0, 0)) != 0)
{
	if (-1 == rval)
		return;

	TranslateMessage(&Msg);
	DispatchMessage(&Msg);
} 

}

void Win32NotificationWindow::addListener(Win32MessageListener *wml)
{
ScopedLock sl(cs);

listeners.addIfNotAlreadyThere(wml);

}

void Win32NotificationWindow::removeListener(Win32MessageListener *wml)
{
ScopedLock sl(cs);

listeners.removeValue(wml);

}

#endif
[/code]

[/code]


#4