-
-
Notifications
You must be signed in to change notification settings - Fork 630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ensure that NVDA can get file version information for binaries in system32 on 64-bit versions of Windows #12943
Ensure that NVDA can get file version information for binaries in system32 on 64-bit versions of Windows #12943
Conversation
- Clean start imports - Update copyright header - Use enums and add some new CSIDL constants
…n systes32 by temporarily suspending SysWOW64 redirection
@LeonarddeR Given our discussion in #12852 you may be interested in reviewing this. |
source/appModuleHandler.py
Outdated
from functools import wraps | ||
|
||
# Path to the native system32 directory. | ||
nativeSys32: str = shlobj.SHGetFolderPath(None, shlobj.CSIDL.SYSTEM) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we have this initialization as part of the initialize method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is a good idea here. I really believe we should try to avoid usages of global
if at all possible - it makes any refactors unnecessarily difficult. My dislike for global
aside please note that appmoduleHandler.initialize
is also executed when re-initializing app modules and therefore we would be calling SHGetFolderPath
for no reason ( these calls are probably pretty cheap but the return values cannot change during NVDA's lifetime). It should be possible to add additional argument to initialize
- something like isFirstInit
but it would make code more complex without any real benefit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be my daily job being very C# oriented nowadays, but I consider executing code at the module level pretty terrible.
I don't insist though, let's see what NV Access people say.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would agree that initializing this in a method would be much better.
If _suspendWow64RedirectionForFileInfoRetrieval
could be useful in other modules, I would suggest abstracting this into a new module an initializing this in core
to avoid the "re-initializing" you mention.
@@ -126,14 +128,6 @@ def getUserDefaultConfigPath(useInstalledPathIfExists=False): | |||
return installedUserConfigPath | |||
return os.path.join(globalVars.appDir, 'userConfig') | |||
|
|||
def getSystemConfigPath(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is likely to break backwards compat. I don't think that's a problem when this will be 2022.1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've mentioned that consideration in the PR description. Given that we're branching for 2021.3 already this should not be a problem. Could you add this PR to the 2022.1 milestone so it is not forgotten?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, I must have missed that.
Given development cycle for 2022.1 has began I'm marking this as ready for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @lukaszgo1
source/appModuleHandler.py
Outdated
from functools import wraps | ||
|
||
# Path to the native system32 directory. | ||
nativeSys32: str = shlobj.SHGetFolderPath(None, shlobj.CSIDL.SYSTEM) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would agree that initializing this in a method would be much better.
If _suspendWow64RedirectionForFileInfoRetrieval
could be useful in other modules, I would suggest abstracting this into a new module an initializing this in core
to avoid the "re-initializing" you mention.
Although Wow64DisableWow64FsRedirection only affects the current thread (which at least means we don't need to worry about concurrency issues), the more work we do with redirect disabled hightens the chance that Windows or a component in this thread may try to load a dll from system32. And while redirection is disabled, this would be the wrong system32. Thus per the documentation of Wow64DisableWow64FsRedirection, it is strongly recommended to only do this around the lowest API calls possible. |
I believe all requested changes are now addressed.
|
source/shlobj.py
Outdated
|
||
|
||
class CSIDL(enum.IntEnum): | ||
class FOLDERID(enum.Enum): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class FOLDERID(enum.Enum): | |
class FOLDERID(str, enum.Enum): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you be able to explain what this change is for? I'm going to apply it regardless as not to block this from further reviews, but this pattern is new to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds type information to the Enum
Some examples and more information:
@michaelDCurran - thoughts on the updated approach? I've reviewed the code otherwise |
What would happen if there was a 32-bit app in c:\windows\syswow64 (c+\windows\system32 from the point of view of the app in question), and NVDA tried to get its file version info? |
Co-authored-by: Sean Budd <seanbudd123@gmail.com>
…lukaszgo1/nvda into file-info-for-system32-binaries
There is no special code for this, but that is because this is handled well by Windows itself. To make sure I'm right I've tested as follows:
|
@lukaszgo1 thanks for confirming that. Just to be doubly sure, I also placed a test 32-bit executable file in
And verified that NVDA's appModule for that executable had an appPath of |
@seanbudd When adding change log entry for this PR you've forgot the one listed under the 'changes for developers' section. |
The following seems to fail:
Update: AH, seems I have to append value to get the real string. This approach seems a bit confusing. Why not make the enum a comtypes.GUID enum? Also I'm slightly concerned about the new |
Having said that I am not fully satisfied with the current solution, so if you have better ideas please do tell.
My understanding, (although it seems this is not covered by our coding standards) was that we follow the style that you've described above for our own enumerations, but when the given enum merely maps win32 constants we attempt to follow Windows conventions. If there is general consensus that we should follow our own style even in these cases I'm happy to rename as appropriate but this should certainly be documented in the coding standards. For another example where I followed the same logic see #12753 in particular |
Thanks for point this out - I will draft a PR (#12986) to fix this and we can discuss the casing issue. CamelCase is the standard we have discussed, but as @lukaszgo1 mentions, this hasn't been consistent.
I'm not sure if that will resolve that issue, like @lukaszgo1 mentions, but I think it would be an improvement. I'll draft a PR that creates a base |
Actually resolving this seems too difficult to be worth doing. The following (standard) pattern relies on class _GUIDEnumMeta(EnumMeta, type(GUID)):
pass
class GUIDEnum(GUID, Enum, metaclass=_GUIDEnumMeta):
pass
class FolderId(GUIDEnum):
example = GUID("{}")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\sean\AppData\Local\Programs\Python\Python37-32\lib\enum.py", line 175, in __new__
enum_class = super().__new__(metacls, cls, bases, classdict)
TypeError: _ctypes.PyCStructType.__new__(_GUIDEnumMeta) is not safe, use type.__new__() |
Note: this pull request made NVDA remote incompatible. appropriate changes to the NVDA remote should be made. |
Fixup of #12943 Summary of the issue: Changelog entries were missing for #12943 UpperCamelCase is the standard for class names, including enums, however FOLDERID was used as the casing for the enum, which mirrors the windows constants. There is a similar issue for the enum members (which used UpperCamelCase instead of CAP_SNAKE_CASE). If we want to change our practice to be consistent with what an Enum represents, we should update codingStandards.md Also the enum type was not being fully leveraged by SHGetKnownFolderPath, requiring .value to be used unnecessarily when calling the function. Description of how this pull request fixes the issue: Updates the casing of the enum and it's members. Updates the change log, and fixes up some earlier entries.
…s to handle paths from different disks (#13009) PR #12943 improved code which retrieves version information for a given executable files placed in system32 under 64-bit versions of Windows. To do so it is necessary to check if path of the given binary starts with path of system32 directory for the current system. The recommended way to compare path's in a case insensitive way was os.pathcommonPath`, however it turns out it does not work when path's are from different drives. In case of reporter of #13008 they had Office installed in a custom location probably on non system disk. Description of how this pull request fixes the issue: This PR simplifies the code to use standard str.casefold for comparisons.
Link to issue number:
None, discussion in #12852
Summary of the issue:
NVDA can retrieve information for a binary file such as application name and version from which given appModule has been created. While they're not shown to the user directly they can be inspected as part of the developer info - also NVDA relies on them for various stuff internally. For binaries placed in system32 (such as
LogonUI.exe
) this process fails on 64-bit versions of Windows as follows:This occurs because NVDA is a 32-bit process and when trying to access
C:\Windows\System32
Windows redirects it toC:\Windows\SysWOW64
and some binaries aren't duplicated there.Description of how this pull request fixes the issue:
For a duration of retrieving file info for a binary placed in
system32
and when running on a version of Windows which have bothsystem32
andSysWOW64
WOW64 redirection is disabled. While at it I've removed deprecatedshlobj.SHGetFolderPath
and replaced it withSHGetKnownFolderPath
and removed unused function from the config module ((while not strictly related it relied onSHGetFolderPath
).Testing strategy:
On 64-bit version of Windows installed self signed build from this branch, enabled service debug, started an application with the admin privileges, inspected developer info for the UAC screen and made sure that for
consent.exe
file version and file name can be retrieved. Made sure that this code is not executed on 32-bit version of Windows and that file version info can still be retrieved there.Known issues with pull request:
Change log entries:
Bug fixes
For Developers
config.getSystemConfigPath
has been removed - there is no replacementshlobj.SHGetFolderPath
has been removed - please useshlobj.SHGetKnownFolderPath
insteadCode Review Checklist: