-
Notifications
You must be signed in to change notification settings - Fork 975
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
InputLanguage: Use RegLoadMUIString instead of SHLoadIndirectString API #8921
Conversation
2e1b71d
to
6524066
Compare
@JeremyKuhne any objections on this topic? |
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.
Comments are inline.
var hkey = (System.Registry.HKEY)key.Handle.DangerousGetHandle(); | ||
uint bytes = 0; | ||
if (RegLoadMUIString(hkey, keyName, null, 0, &bytes, 0, null) != WIN32_ERROR.ERROR_MORE_DATA) | ||
return false; |
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.
Our coding style requires blocks for if .. else
.
return false; | ||
} | ||
|
||
localizedValue = buffer.Slice(0, (int)bytes / sizeof(char) - 1).ToString(); |
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.
nit: use parenthesis for precedence.
|
||
namespace Windows.Win32 | ||
{ | ||
internal static partial class PInvoke |
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.
The filename is misspelled.
|
||
return false; | ||
} | ||
public override int GetHashCode() => PARAM.ToInt(_handle); |
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.
PARAM.ToInt
is no longer necessary, and somewhat misleading. Just cast it to int
. I'd also suggest changing _handle
to nint
as we've done elsewhere.
internal static partial class PInvoke | ||
{ | ||
/// <inheritdoc cref="RegLoadMUIString(System.Registry.HKEY, string, PWSTR, uint, uint*, uint, string)"/> | ||
public static unsafe bool RegLoadMUIString(RegistryKey key, string keyName, out string localizedValue) |
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 looks like something that we should create an API proposal for in the runtime. RegistryKey.GetMUIString()
maybe?
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.
To be clear- I don't think we should block here, but I would assume this functionality is generally useful for our developers.
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.
@JeremyKuhne I have added RegistryKeyExtensions.GetMUIString
extension method
public static unsafe bool RegLoadMUIString(RegistryKey key, string keyName, out string localizedValue) | ||
{ | ||
localizedValue = string.Empty; | ||
if (key.Handle.IsInvalid) |
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 expect that this is uncommon, maybe better to just let if fail with the call to RegLoadMUIString().
if (RegLoadMUIString(hkey, keyName, null, 0, &bytes, 0, null) != WIN32_ERROR.ERROR_MORE_DATA) | ||
return false; | ||
|
||
Span<char> buffer = stackalloc char[(int)bytes / sizeof(char)]; |
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.
The data here appears to be unbounded. One presumes that this could be pretty expensive if it follows through to a PE file to load a string resource just to return the length.
The safer/better choice is to stack allocate some reasonable buffer and then rent/return a bigger array from the ArrayPool
if the size isn't large enough. It may also make sense to just use the ArrayPool
, as I the benefit of skipping the rental is probably minimal at best. Just using the ArrayPool
would make the logic simpler, for sure.
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.
One presumes that this could be pretty expensive if it follows through to a PE file to load a string resource just to return the length.
I can bet that RegLoadMUIString is caching MUI strings to avoid this rereading but I'll do the changes.
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 can bet that RegLoadMUIString is caching MUI strings to avoid this rereading but I'll do the changes.
It doesn't look like it. You'll probably get some general IO caching though. The more critical issue is that the buffer size is unbounded, which is a problem for stack allocation.
6524066
to
7028c9d
Compare
uint bytes; | ||
WIN32_ERROR error; | ||
var hkey = (System.Registry.HKEY)key.Handle.DangerousGetHandle(); | ||
char[] buffer = ArrayPool<char>.Shared.Rent(MAX_PATH + 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.
Several places in code are already using MAX_PATH
for displayName
buffer...
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.
We shouldn't use MAX_PATH
as a buffer value unless it is actually explicitly tied to the value. This has nothing to do with paths. Also consider that the ArrayPool
returns buffers in fixed sizes (2^n). This would get you a buffer of 512 chars. 128 is probably a sensible initial buffer that would cover the vast majority of cases.
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.
Added a few more comments.
uint bytes; | ||
WIN32_ERROR error; | ||
var hkey = (System.Registry.HKEY)key.Handle.DangerousGetHandle(); | ||
char[] buffer = ArrayPool<char>.Shared.Rent(MAX_PATH + 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.
We shouldn't use MAX_PATH
as a buffer value unless it is actually explicitly tied to the value. This has nothing to do with paths. Also consider that the ArrayPool
returns buffers in fixed sizes (2^n). This would get you a buffer of 512 chars. 128 is probably a sensible initial buffer that would cover the vast majority of cases.
localizedValue = new string(buffer, 0, Math.Max((int)(bytes / sizeof(char)) - 1, 0)); | ||
ArrayPool<char>.Shared.Return(buffer); | ||
|
||
return HRESULT.HRESULT_FROM_WIN32(error).Succeeded; |
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.
You should only try to create the string when ERROR_SUCCESS
is returned and that should be the only thing you check to return success.
7028c9d
to
fb449f0
Compare
Hi @JeremyKuhne! Do you have any updates on this review? |
@DJm00n I'm in the process of adding a It would be good to use that to simplify this if possible. You should be able to do something similar to what I did with PInvoke.GetModuleFileNameLongPath.cs in that change. Hopefully I'll have it checked in this afternoon. |
fb449f0
to
f7f33eb
Compare
Nice! This is indeed looks more simple! Pushed updated version of |
Hi @JeremyKuhne! Any updates on this 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.
Looks great- thanks for your patience!
@DJm00n, can you rebase this? |
As recommended at https://learn.microsoft.com/windows/win32/intl/locating-redirected-strings#load-a-language-neutral-registry-value Additional changes: - Move InputLanguage GetKeyboardLayoutNameForHKL method implementation to the internal LayoutId property - Better testing of InputLanguage.LayoutName
f7f33eb
to
392e638
Compare
@dreddy-work done |
I have extracted part of #8573 into separate PR until decision is made on some aspects of that PR.
Proposed changes
RegLoadMUIString
instead of theSHLoadIndirectString
API as recommended at https://learn.microsoft.com/windows/win32/intl/locating-redirected-strings#load-a-language-neutral-registry-valueGetKeyboardLayoutNameForHKL
method implementation to the internalLayoutId
property (to ease debugging)PARAM.ToInt
instead of casts inGetHashCode
implementationCustomer Impact
Regression?
Risk
Test methodology
Auto test:
Manually:
InputLanguage_InstalledInputLanguages_Get_ReturnsExpected
test and run it under debugger.LayoutId
is00011009
under debugger.LayoutName
is "Canadian Multilingual Standard".Microsoft Reviewers: Open in CodeFlow