- 
                Notifications
    You must be signed in to change notification settings 
- Fork 1.1k
CA1838 Avoid 'StringBuilder' parameters for P/Invokes #8113
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
Changes from all commits
496a47d
              73ec2f9
              b3860a2
              199bfb5
              26a3f15
              84f87f2
              4b51246
              1af7290
              fb238ea
              09f3073
              1a4999f
              06ee405
              01345e6
              22c139e
              3cd1ae7
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|  | ||
| using System.Buffers; | ||
| using System.Diagnostics; | ||
|  | ||
| namespace Windows.Win32 | ||
| { | ||
| internal static partial class PInvoke | ||
| { | ||
| public static unsafe string GetModuleFileNameLongPath(HINSTANCE hModule) | ||
| { | ||
| Span<char> stackBuffer = stackalloc char[MAX_PATH]; | ||
| uint pathLength; | ||
| fixed (char* lpFilename = stackBuffer) | ||
| { | ||
| pathLength = GetModuleFileName(hModule, lpFilename, (uint)stackBuffer.Length); | ||
| } | ||
|  | ||
| if (pathLength == 0) | ||
| { | ||
| return string.Empty; | ||
| } | ||
|  | ||
| if (pathLength < stackBuffer.Length) | ||
| { | ||
| return new string(stackBuffer[..(int)pathLength]); | ||
| } | ||
|  | ||
| char[] buffer; | ||
| int bufferSize = 4096; | ||
| // Allocate increasingly larger portions of memory until successful or we hit short.maxvalue. | ||
| for (int i = 1; bufferSize <= short.MaxValue; i++, bufferSize = 4096 * i) | ||
| { | ||
| buffer = ArrayPool<char>.Shared.Rent(bufferSize); | ||
| fixed (char* lpFilename = buffer) | ||
| { | ||
| pathLength = GetModuleFileName(hModule, lpFilename, (uint)buffer.Length); | ||
| } | ||
|  | ||
| if (pathLength == 0) | ||
| { | ||
| ArrayPool<char>.Shared.Return(buffer); | ||
| return string.Empty; | ||
| } | ||
|  | ||
| // If the length equals the buffer size we need to check to see if we were told the buffer was insufficient (it was trimmed) | ||
| if (pathLength < buffer.Length) | ||
| { | ||
| // Get return value and return buffer to array pool. | ||
| string returnValue = new string(buffer, 0, (int)pathLength); | ||
| ArrayPool<char>.Shared.Return(buffer); | ||
| return returnValue; | ||
| } | ||
|  | ||
| // buffer was too small, return to array pool. | ||
| ArrayPool<char>.Shared.Return(buffer); | ||
| } | ||
|  | ||
| Debug.Fail($"Module File Name is greater than {short.MaxValue}."); | ||
| return string.Empty; | ||
|         
                  elachlan marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|  | ||
| namespace Windows.Win32 | ||
| { | ||
| internal static partial class PInvoke | ||
| { | ||
| /// <summary> | ||
| /// <para>The maximum length for lpszClassName is 256. If lpszClassName is greater than the maximum length, the RegisterClassEx function will fail.</para> | ||
| /// <para><see href="https://learn.microsoft.com/windows/win32/api/winuser/ns-winuser-wndclassexw#members">Read more on docs.microsoft.com</see>.</para> | ||
| /// </summary> | ||
| public const int MaxClassName = 256; | ||
| } | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -10584,7 +10584,7 @@ public void RichTextBox_CheckDefaultNativeControlVersions() | |
| using var control = new RichTextBox(); | ||
| control.CreateControl(); | ||
|  | ||
| Assert.Contains("RICHEDIT50W", GetClassName(control.Handle), StringComparison.InvariantCultureIgnoreCase); | ||
| Assert.Contains("RICHEDIT50W", GetClassName(control.HWND), StringComparison.Ordinal); | ||
| } | ||
|  | ||
| [WinFormsFact] | ||
|  | @@ -10593,13 +10593,13 @@ public void RichTextBox_CheckRichEditWithVersionCanCreateOldVersions() | |
| using (var riched32 = new RichEdit()) | ||
| { | ||
| riched32.CreateControl(); | ||
| Assert.Contains(".RichEdit.", GetClassName(riched32.Handle), StringComparison.InvariantCultureIgnoreCase); | ||
| Assert.Contains(".RichEdit.", GetClassName(riched32.HWND), StringComparison.Ordinal); | ||
| } | ||
|  | ||
| using (var riched20 = new RichEdit20W()) | ||
| { | ||
| riched20.CreateControl(); | ||
| Assert.Contains(".RichEdit20W.", GetClassName(riched20.Handle), StringComparison.InvariantCultureIgnoreCase); | ||
| Assert.Contains(".RichEdit20W.", GetClassName(riched20.HWND), StringComparison.Ordinal); | ||
|  | ||
| string rtfString = @"{\rtf1\ansi{" + | ||
| @"The next line\par " + | ||
|  | @@ -10615,8 +10615,8 @@ public void RichTextBox_CheckRichEditWithVersionCanCreateOldVersions() | |
| Assert.Equal(riched20.Text, richTextBox.Text); | ||
| Assert.Equal(richTextBox.Text.Length, richTextBox.TextLength); | ||
|  | ||
| int startOfIs = riched20.Text.IndexOf("is"); | ||
| int endOfHidden = riched20.Text.IndexOf("hidden") + "hidden".Length; | ||
| int startOfIs = riched20.Text.IndexOf("is", StringComparison.Ordinal); | ||
| int endOfHidden = riched20.Text.IndexOf("hidden", StringComparison.Ordinal) + "hidden".Length; | ||
| richTextBox.Select(startOfIs, endOfHidden - startOfIs); | ||
| Assert.Equal("is ###NOT### hidden", richTextBox.SelectedText); | ||
| } | ||
|  | @@ -10791,12 +10791,16 @@ private class SubRichTextBox : RichTextBox | |
| public new void WndProc(ref Message m) => base.WndProc(ref m); | ||
| } | ||
|  | ||
| private static string GetClassName(IntPtr hWnd) | ||
| private static unsafe string GetClassName(HWND hWnd) | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this move to the PInvoke namespace? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe there is another place where we call  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't move this because its a wrapper and isn't used in the other calls. I am happy to still move it. The other uses avoid string allocations. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you think this is a dup, feel free to remove this. I can't recall exact reasons why I copied it here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since its a wrapper of the pinvoke and we don't reuse it, I think it can stay here. | ||
| { | ||
| const int MaxClassName = 256; | ||
| StringBuilder sb = new StringBuilder(MaxClassName); | ||
| UnsafeNativeMethods.GetClassName(new HandleRef(null, hWnd), sb, MaxClassName); | ||
| return sb.ToString(); | ||
| int length = 0; | ||
| Span<char> buffer = stackalloc char[PInvoke.MaxClassName]; | ||
| fixed (char* lpClassName = buffer) | ||
| { | ||
| length = PInvoke.GetClassName(hWnd, lpClassName, buffer.Length); | ||
| } | ||
|  | ||
| return new string(buffer[..length]); | ||
| } | ||
|  | ||
| /// <summary> | ||
|  | ||
Uh oh!
There was an error while loading. Please reload this page.