Skip to content

Commit

Permalink
App modules/product name and version: use kernel32::GetPackageFullNam…
Browse files Browse the repository at this point in the history
…e to obtain product info for immersive (hosted) apps (nvaccess#10114)

* App modules/product name and version: obtain product info for immersive (hosted) apps via kernel32::GetPackageFullName function. Re nvaccess#10108.

On Windows 8 and later, some apps can run inside a container. This is the case for WinRT/UWP apps, some web apps, and converted desktop apps (such as Microsoft Office 365 downloaded from Microsoft Store). For these apps, kernel32.dll::GetPackageFullName returns the 'real' product info such as name and version. Because it'll be returned in a serialized string representation, parse the first two values (name and version).
To accomodate this change, the former method of obtaining product name and version via file version info has been moved to an internal function inside product info setter method. This function will be invoked if:
* This is Windows 7/Server 2008 R2 (no support for containers yet).
* An immersive app that is really a native pap (such as File Explorer).
* Converted desktop apps that will not expose version info via file version info structure (such as Notepad in 20H1 Preview build 18963 and later).
For consistency with immersive app info structure, the modified function will return a 2-tuple that records product name and version in that order.

* App module handler/product name and version: _executableFileInfo -> _getExecutableFileInfo. Re nvaccess#10108.

Reviewed by Mick Curran (NV Access): because of what the internal function does, it is better to give it a more descriptive name than just 'executable file info'.

* App module handler/product name and version for immersive apps: typo fix.

* Product name and version: separate executable file info function to its own method in base pap module. Re nvaccess#10108.

Suggested by Leonard de Ruijter (Babbage): transform executable file info function from an internal one to a private method in base app module for ease of future maintenance.
  • Loading branch information
josephsl authored and michaelDCurran committed Sep 10, 2019
1 parent 327482d commit e02b548
Showing 1 changed file with 44 additions and 9 deletions.
53 changes: 44 additions & 9 deletions source/appModuleHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,21 +342,56 @@ def __init__(self,processID,appName=None):
self.helperLocalBindingHandle=None
self._inprocRegistrationHandle=None

def _setProductInfo(self):
"""Set productName and productVersion attributes.
"""
# Sometimes (I.E. when NVDA starts) handle is 0, so stop if it is the case
if not self.processHandle:
raise RuntimeError("processHandle is 0")
def _getExecutableFileInfo(self):
# Used for obtaining file name and version for the executable.
# This is needed in case immersive app package returns an error,
# dealing with a native app, or a converted desktop app.
# Create the buffer to get the executable name
exeFileName = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
length = ctypes.wintypes.DWORD(ctypes.wintypes.MAX_PATH)
if not ctypes.windll.Kernel32.QueryFullProcessImageNameW(self.processHandle, 0, exeFileName, ctypes.byref(length)):
if not ctypes.windll.Kernel32.QueryFullProcessImageNameW(
self.processHandle, 0, exeFileName, ctypes.byref(length)
):
raise ctypes.WinError()
fileName = exeFileName.value
fileinfo = getFileVersionInfo(fileName, "ProductName", "ProductVersion")
self.productName = fileinfo["ProductName"]
self.productVersion = fileinfo["ProductVersion"]
return (fileinfo["ProductName"], fileinfo["ProductVersion"])

def _setProductInfo(self):
"""Set productName and productVersion attributes.
There are at least two ways of obtaining product info for an app:
* Package info for hosted apps
* File version info for other apps and for some hosted apps
"""
# Sometimes (I.E. when NVDA starts) handle is 0, so stop if it is the case
if not self.processHandle:
raise RuntimeError("processHandle is 0")
# No need to worry about immersive (hosted) apps and friends until Windows 8.
# Python 3.7 introduces platform_version to sys.getwindowsversion tuple,
# which returns major, minor, build.
if winVersion.winVersion.platform_version >= (6, 2, 9200):
# Some apps such as File Explorer says it is an immersive process but error 15700 is shown.
# Therefore resort to file version info behavior because it is not a hosted app.
# Others such as Store version of Office are not truly hosted apps,
# yet returns an internal version anyway.
# For immersive apps, default implementation is generic - returns Windows version information.
# Thus probe package full name and parse the serialized representation of package info structure.
length = ctypes.c_uint()
buf = ctypes.windll.kernel32.GetPackageFullName(self.processHandle, ctypes.byref(length), None)
packageFullName = ctypes.create_unicode_buffer(buf)
if ctypes.windll.kernel32.GetPackageFullName(
self.processHandle, ctypes.byref(length), packageFullName
) == 0:
# Product name is of the form publisher.name for a hosted app.
productInfo = packageFullName.value.split("_")
else:
# File Explorer and friends which are really native aps.
productInfo = self._getExecutableFileInfo()
else:
# Not only native apps, but also converted desktop aps such as Office.
productInfo = self._getExecutableFileInfo()
self.productName = productInfo[0]
self.productVersion = productInfo[1]

def _get_productName(self):
self._setProductInfo()
Expand Down

0 comments on commit e02b548

Please sign in to comment.