Commits

kankri committed 92f937b

Match windows by application description

Match windows also by the description in the executable file of the owning process.

  • Participants
  • Parent commits 9f6551d

Comments (0)

Files changed (2)

File doc/source/pygoy.rst

  * The name of the program. E.g. if you have opened a "Command Prompt"
    window, PyGo-Y matches that when you type "cmd.exe" even though it's not
    visible on the title.
+ * The description of the program. The description of the program is a string
+   embedded in the EXE file of the program. You can see it in the "Version"
+   tab of the "Properties" dialog of a file in Explorer. Windows uses this
+   string in various places, e.g. when it groups windows of a single program
+   into a combined button on the Taskbar.
 
-PyGo-Y matches your input against both the title and the program name in turn.
-If either of them match, the window is listed in the Launchy dialog. Selecting
-any of those will focus it.
+   As an example, if you have opened a "Command Prompt" window, you can match
+   it with PyGo-Y by typing "Windows Command Processor".
+
+PyGo-Y matches your input to each of the three information sources in turn. If
+any of them match, the window is listed in the Launchy dialog. Selecting any of
+those will focus it.
 
 The matching is done as a case-insensitive substring match. E.g. "Md.E" matches
 with "cmd.exe".

File plugins/pygo-y.py

 			return
 		title = unicode(win32gui.GetWindowText(hwnd), ACP)
 		path = GetProcessFilename(GetWindowProcess(hwnd))
-		if (is_match(title) or is_match(os.path.basename(path))):
+		descrs = getFileDescriptions(path)
+		if (
+			is_match(title) or
+			is_match(os.path.basename(path)) or
+			any(is_match(d) for d in descrs)
+		):
 			windowsList.append( (hwnd, title) )
 
 	_windowEnumTopLevel = staticmethod(_windowEnumTopLevel)
 
 		return filename
 
+def getFileDescriptions(filename):
+	ver_info = get_version_info(filename)
+	if ver_info is None:
+		return ()
+
+	descr = []
+	for lang, codepage in translations(ver_info):
+		value = var_info(ver_info, codepage, lang, 'FileDescription')
+		if value is not None:
+			descr.append(value)
+
+	return tuple(descr)
+
+import ctypes
+from ctypes.wintypes import BOOL, WORD, DWORD
+from ctypes import c_byte, c_wchar_p, c_int, c_uint, c_void_p
+from ctypes import POINTER, byref
+
+def win_status_check(result, func, args):
+	if result == 0:
+		raise ctypes.WinError()
+	return args
+
+GetFileVersionInfoSize = ctypes.windll.version.GetFileVersionInfoSizeW
+GetFileVersionInfoSize.argtypes = (c_wchar_p, POINTER(c_uint))
+GetFileVersionInfoSize.restype = DWORD
+GetFileVersionInfoSize.errcheck = win_status_check
+
+GetFileVersionInfo = ctypes.windll.version.GetFileVersionInfoW
+GetFileVersionInfo.argtypes = (c_wchar_p, c_uint, c_uint, c_void_p)
+GetFileVersionInfo.restype = BOOL
+GetFileVersionInfo.errcheck = win_status_check
+
+VerQueryValue = ctypes.windll.version.VerQueryValueW
+VerQueryValue.argtypes = (c_void_p, c_wchar_p, c_void_p, POINTER(c_uint))
+VerQueryValue.restype = BOOL
+VerQueryValue.errcheck = win_status_check
+
+def get_version_info(filename):
+	try:
+		dummy_handle = DWORD()
+		size = GetFileVersionInfoSize(filename, byref(dummy_handle))
+
+		buf = (size * c_byte)()
+		GetFileVersionInfo(filename, dummy_handle, size, byref(buf))
+		return buf
+	except WindowsError, e:
+		if e.winerror != 1813:
+			raise
+	return None
+
+def var_info(fixedfileinfo, lang, codepage, k):
+	key = r'\StringFileInfo\%04x%04x\%s' % (lang, codepage, k)
+	buf = ctypes.POINTER(DWORD)()
+	buf_size = c_uint()
+	try:
+		VerQueryValue(fixedfileinfo, key, byref(buf), buf_size)
+		return ctypes.wstring_at(buf)
+	except WindowsError, e:
+		if e.winerror != 1813:
+			raise
+	return None
+
+def translations(fixedfileinfo):
+	buf = ctypes.POINTER(DWORD)()
+	buf_size = c_uint()
+	ret = list()
+	try:
+		if VerQueryValue(fixedfileinfo, r'\VarFileInfo\Translation', byref(buf), buf_size):
+			for i in range(buf_size.value / ctypes.sizeof(DWORD)):
+				ret.append((buf[i] >> 16, buf[i] & 0xffff))
+	except WindowsError, e:
+		if e.winerror != 1813:
+			raise
+	return ret
+
 launchy.registerPlugin(PyGoY)