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

  • Participants
  • Parent commits 23c6af6
  • Tags 2.2

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
 ~~~

File 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'

File 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)
+