Commits

Jason R. Coombs committed 1570ffd

Implemented environment change notification using SendMessageTimeout so it doesn't stall on some systems where the apps don't handle the event elegantly

Comments (0)

Files changed (3)

 Changes
 -------
 
-2.1.1
-~~~~~
+2.2
+~~~
 
 * Fixes by wkornewald for issue #1 - Symlink relative path deficiencies.
+* Added jaraco.windows.message.SendMessageTimeout.
+* Fixed issue where environment changes would stall on SendMessage.
+* SendMessage now uses the correct type for lParam, but will still accept
+  string types.
 
 2.1
 ~~~

jaraco/windows/environ.py

 from jaraco.util.editor import EditableFile
 
 from jaraco.windows import error
-from jaraco.windows.message import SendMessage, HWND_BROADCAST, WM_SETTINGCHANGE
+from jaraco.windows import message
 from .registry import key_values as registry_key_values
 
 _SetEnvironmentVariable = ctypes.windll.kernel32.SetEnvironmentVariableW
 	
 	@classmethod
 	def notify(class_):
+		"""
+		Notify other windows that the environment has changed (following
+		http://support.microsoft.com/kb/104011).
+		"""
 		# TODO: Implement Microsoft UIPI (User Interface Privilege Isolation) to
 		#  elevate privilege to system level so the system gets this notification
 		# for now, this must be run as admin to work as expected
-		SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, u'Environment')
+		return_val = ctypes.wintypes.DWORD()
+		res = message.SendMessageTimeout(
+			message.HWND_BROADCAST,
+			message.WM_SETTINGCHANGE,
+			0, # wparam must be null
+			'Environment',
+			message.SMTO_ABORTIFHUNG,
+			5000, # timeout in ms
+			return_val,
+		)
 
 class MachineRegisteredEnvironment(RegisteredEnvironment):
 	path = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'

jaraco/windows/message.py

 # $Id$
 
 import ctypes
-from ctypes.wintypes import HWND, UINT, WPARAM, LPARAM, LPVOID
+from ctypes.wintypes import HWND, UINT, WPARAM, LPARAM, DWORD
 LRESULT = LPARAM
 
+class StringCapableLPARAM(LPARAM):
+	"""
+	A special instance of LPARAM that can be constructed from a string
+	instance (for functions such as SendMessage, whose LPARAM may point to
+	a unicode string).
+	"""
+	@classmethod
+	def from_param(cls, param):
+		if isinstance(param, basestring):
+			param = ctypes.cast(ctypes.c_wchar_p(param), ctypes.c_void_p).value
+		return LPARAM.from_param(param)
+
 SendMessage = ctypes.windll.user32.SendMessageW
-SendMessage.argtypes = (HWND, UINT, WPARAM, LPVOID)
+SendMessage.argtypes = (HWND, UINT, WPARAM, StringCapableLPARAM)
 SendMessage.restype = LRESULT
 
 HWND_BROADCAST=0xFFFF
 WM_SETTINGCHANGE=0x1A
+
+# constants from http://msdn.microsoft.com/en-us/library/ms644952%28v=vs.85%29.aspx
+SMTO_ABORTIFHUNG = 0x02
+SMTO_BLOCK = 0x01
+SMTO_NORMAL = 0x00
+SMTO_NOTIMEOUTIFNOTHUNG = 0x08
+SMTO_ERRORONEXIT = 0x20
+
+SendMessageTimeout = ctypes.windll.user32.SendMessageTimeoutW
+SendMessageTimeout.argtypes = SendMessage.argtypes + (
+	UINT, UINT, ctypes.POINTER(DWORD)
+)
+SendMessageTimeout.restype = LRESULT
+
+def unicode_as_lparam(source):
+	pointer = ctypes.cast(ctypes.c_wchar_p(source), ctypes.c_void_p)
+	return LPARAM(pointer.value)
+