Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,18 @@
// See the LICENSE file in the project root for more information.

using System.Runtime.InteropServices;
using System.Text;

internal partial class Interop
{
internal partial class Kernel32
{
[DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
public static extern uint FormatMessageW(
public static extern unsafe uint FormatMessageW(
FormatMessageOptions dwFlags,
IntPtr lpSource,
uint dwMessageId,
uint dwLanguageId,
#pragma warning disable CA1838 // Avoid 'StringBuilder' parameters for P/Invokes
StringBuilder lpBuffer,
#pragma warning restore CA1838 // Avoid 'StringBuilder' parameters for P/Invokes
char* lpBuffer,
int nSize,
IntPtr arguments);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,39 @@
// See the LICENSE file in the project root for more information.

using System.Runtime.InteropServices;
using System.Text;

internal static partial class Interop
{
internal static partial class Shell32
{
[DllImport(Libraries.Shell32, ExactSpelling = true, EntryPoint = "DragQueryFileW", CharSet = CharSet.Unicode)]
#pragma warning disable CA1838 // Avoid 'StringBuilder' parameters for P/Invokes
private static extern uint DragQueryFileWInternal(IntPtr hDrop, uint iFile, StringBuilder? lpszFile, uint cch);
#pragma warning restore CA1838 // Avoid 'StringBuilder' parameters for P/Invokes
private static extern unsafe uint DragQueryFileWInternal(IntPtr hDrop, uint iFile, char* lpszFile, uint cch);

public static uint DragQueryFileW(IntPtr hDrop, uint iFile, StringBuilder? lpszFile)
public static unsafe uint DragQueryFileW(IntPtr hDrop, uint iFile, ref string? lpszFile)
{
if (lpszFile is null || lpszFile.Capacity == 0 || iFile == 0xFFFFFFFF)
if (lpszFile is null || iFile == 0xFFFFFFFF)
{
lpszFile = string.Empty;
return DragQueryFileWInternal(hDrop, iFile, null, 0);
}

uint resultValue = 0;
uint capacity;

// iterating by allocating chunk of memory each time we find the length is not sufficient.
// Performance should not be an issue for current MAX_PATH length due to this
if ((resultValue = DragQueryFileWInternal(hDrop, iFile, lpszFile, (uint)lpszFile.Capacity)) == lpszFile.Capacity)
// passing null for buffer will return actual number of characters in the file name.
// So, one extra call would be suffice to avoid while loop in case of long path.
if ((capacity = DragQueryFileWInternal(hDrop, iFile, null, 0)) < Kernel32.MAX_UNICODESTRING_LEN)
{
// passing null for buffer will return actual number of characters in the file name.
// So, one extra call would be suffice to avoid while loop in case of long path.
uint capacity = DragQueryFileWInternal(hDrop, iFile, null, 0);
if (capacity < Kernel32.MAX_UNICODESTRING_LEN)
Span<char> charSpan = new char[(int)capacity];
fixed (char* pCharSpan = charSpan)
{
lpszFile.EnsureCapacity((int)capacity);
resultValue = DragQueryFileWInternal(hDrop, iFile, lpszFile, (uint)capacity);
}
else
{
resultValue = 0;
resultValue = DragQueryFileWInternal(hDrop, iFile, pCharSpan, capacity);
}

// Set lpszFile to the buffer's data.
lpszFile = charSpan.Slice(0, (int)resultValue).SliceAtFirstNull().ToString();
}

lpszFile.Length = (int)resultValue;
return resultValue;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@
// See the LICENSE file in the project root for more information.

using System.Runtime.InteropServices;
using System.Text;

internal static partial class Interop
{
internal static partial class Shlwapi
{
[DllImport(Libraries.Shlwapi, ExactSpelling = true, CharSet = CharSet.Unicode)]
#pragma warning disable CA1838 // Avoid 'StringBuilder' parameters for P/Invokes
public static extern HRESULT SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, IntPtr ppvReserved);
#pragma warning restore CA1838 // Avoid 'StringBuilder' parameters for P/Invokes
public static extern unsafe HRESULT SHLoadIndirectString(string pszSource, char* psZOutBuf, uint cchOutBuf, IntPtr ppvReserved);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@
// 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.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
using static Interop;

namespace System.Windows.Forms
{
internal static class UnsafeNativeMethods
{
[DllImport(Libraries.User32)]
#pragma warning disable CA1838 // Avoid 'StringBuilder' parameters for P/Invokes
public static extern int GetClassName(HandleRef hwnd, StringBuilder lpClassName, int nMaxCount);
#pragma warning restore CA1838 // Avoid 'StringBuilder' parameters for P/Invokes
[DllImport(Libraries.User32, CharSet = CharSet.Unicode)]
public static extern unsafe int GetClassName(HandleRef hwnd, char* lpClassName, int nMaxCount);

[DllImport(Libraries.Comdlg32, SetLastError = true, CharSet = CharSet.Auto)]
public static extern HRESULT PrintDlgEx([In, Out] NativeMethods.PRINTDLGEX lppdex);
Expand All @@ -22,39 +20,47 @@ internal static class UnsafeNativeMethods
public static extern bool GetOpenFileName([In, Out] NativeMethods.OPENFILENAME_I ofn);

[DllImport(Libraries.Kernel32, CharSet = CharSet.Auto, SetLastError = true)]
#pragma warning disable CA1838 // Avoid 'StringBuilder' parameters for P/Invokes
public static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);
#pragma warning restore CA1838 // Avoid 'StringBuilder' parameters for P/Invokes
public static extern unsafe int GetModuleFileName(HandleRef hModule, char* buffer, int length);

public static StringBuilder GetModuleFileNameLongPath(HandleRef hModule)
public static unsafe string GetModuleFileNameLongPath(HandleRef hModule)
{
StringBuilder buffer = new StringBuilder(Kernel32.MAX_PATH);
Span<char> buffer = new char[Kernel32.MAX_PATH];
int noOfTimes = 1;
int length = 0;
int length;
int error = 0;

// Iterating by allocating chunk of memory each time we find the length is not sufficient.
// Performance should not be an issue for current MAX_PATH length due to this change.
while (((length = GetModuleFileName(hModule, buffer, buffer.Capacity)) == buffer.Capacity)
&& Marshal.GetLastWin32Error() == ERROR.INSUFFICIENT_BUFFER
&& buffer.Capacity < Kernel32.MAX_UNICODESTRING_LEN)
while (((length = GetModuleFileName(hModule, buffer)) == buffer.Length)
&& (error = Marshal.GetLastWin32Error()) == ERROR.INSUFFICIENT_BUFFER
&& buffer.Length < Kernel32.MAX_UNICODESTRING_LEN)
{
noOfTimes += 2; // Increasing buffer size by 520 in each iteration.
int capacity = noOfTimes * Kernel32.MAX_PATH < Kernel32.MAX_UNICODESTRING_LEN ? noOfTimes * Kernel32.MAX_PATH : Kernel32.MAX_UNICODESTRING_LEN;
buffer.EnsureCapacity(capacity);
buffer = new char[capacity];
}

buffer.Length = length;
return buffer;
if (error == ERROR.INSUFFICIENT_BUFFER)
{
// probably should localize this.
throw new Win32Exception("Error getting the module file names.");
}

return buffer.Slice(0, length).SliceAtFirstNull().ToString();
}

[DllImport(Libraries.Comdlg32, SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool GetSaveFileName([In, Out] NativeMethods.OPENFILENAME_I ofn);

[DllImport(Libraries.Kernel32, CharSet = CharSet.Auto)]
#pragma warning disable CA1838 // Avoid 'StringBuilder' parameters for P/Invokes
public static extern void GetTempFileName(string tempDirName, string prefixName, int unique, StringBuilder sb);
#pragma warning restore CA1838 // Avoid 'StringBuilder' parameters for P/Invokes

[DllImport(Libraries.Oleacc, ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern int CreateStdAccessibleObject(HandleRef hWnd, int objID, ref Guid refiid, [In, Out, MarshalAs(UnmanagedType.Interface)] ref object? pAcc);

private static unsafe int GetModuleFileName(HandleRef hModule, Span<char> buffer)
{
fixed (char* pBuffer = buffer)
{
return GetModuleFileName(hModule, pBuffer, buffer.Length);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,8 @@ public static string ExecutablePath
{
if (s_executablePath is null)
{
StringBuilder sb = UnsafeNativeMethods.GetModuleFileNameLongPath(NativeMethods.NullHandleRef);
s_executablePath = Path.GetFullPath(sb.ToString());
string path = UnsafeNativeMethods.GetModuleFileNameLongPath(NativeMethods.NullHandleRef);
s_executablePath = Path.GetFullPath(path);
}

return s_executablePath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// 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.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
using static Interop;
using static Interop.User32;

Expand All @@ -16,7 +16,6 @@ public partial class ComboBox
/// </summary>
private class AutoCompleteDropDownFinder
{
private const int MaxClassName = 256;
private const string AutoCompleteClassName = "Auto-Suggest Dropdown";
private bool _shouldSubClass;

Expand Down Expand Up @@ -53,11 +52,20 @@ private BOOL Callback(IntPtr hWnd)
return BOOL.TRUE;
}

static string GetClassName(HandleRef hRef)
private static unsafe string GetClassName(HandleRef hRef)
{
StringBuilder sb = new StringBuilder(MaxClassName);
UnsafeNativeMethods.GetClassName(hRef, sb, MaxClassName);
return sb.ToString();
Span<char> buffer = stackalloc char[256];
fixed (char* valueChars = buffer)
{
int result = UnsafeNativeMethods.GetClassName(hRef, valueChars, buffer.Length);
if (result == 0)
{
// probably should localize this.
throw new Win32Exception("Failed to get window class name.");
}
}

return buffer.SliceAtFirstNull().ToString();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using static Interop;
using static Interop.Ole32;

Expand Down Expand Up @@ -1323,40 +1322,43 @@ public unsafe override void SetValue(object component, object value)
}
else if (errorInfo is null)
{
StringBuilder strMessage = new StringBuilder(256);

uint result = Kernel32.FormatMessageW(
Kernel32.FormatMessageOptions.FROM_SYSTEM | Kernel32.FormatMessageOptions.IGNORE_INSERTS,
IntPtr.Zero,
(uint)hr,
Kernel32.GetThreadLocale().RawValue,
strMessage,
255,
IntPtr.Zero);

if (result == 0)
Span<char> buffer = stackalloc char[256];
fixed (char* valueChars = buffer)
{
errorInfo = string.Format(CultureInfo.CurrentCulture, string.Format(SR.DispInvokeFailed, "SetValue", hr));
}
else
{
errorInfo = TrimNewline(strMessage);
uint length = Kernel32.FormatMessageW(
Kernel32.FormatMessageOptions.FROM_SYSTEM |
Kernel32.FormatMessageOptions.IGNORE_INSERTS,
IntPtr.Zero,
(uint)hr,
Kernel32.GetThreadLocale().RawValue,
valueChars,
buffer.Length,
IntPtr.Zero);
if (length == 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really should test this code to see what it does with insufficient buffer sizes and handle growth if needed. Can't stack alloc in that case. Fine for that to be a follow up issue.

{
errorInfo = string.Format(CultureInfo.CurrentCulture,
string.Format(SR.DispInvokeFailed, "SetValue", hr));
}
else
{
errorInfo = TrimNewline(buffer.Slice(0, (int)length).SliceAtFirstNull().ToString());
}
}
}

throw new ExternalException(errorInfo, (int)hr);
}
}

private static string TrimNewline(StringBuilder errorInfo)
private static string TrimNewline(string errorInfo)
{
int index = errorInfo.Length - 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better to have a static array of these chars and just call string.Trim(s_newlineCharacters).

while (index >= 0 && (errorInfo[index] == '\n' || errorInfo[index] == '\r'))
{
index--;
}

return errorInfo.ToString(0, index + 1);
return errorInfo.Substring(0, index + 1);
}

/// <summary>
Expand Down
19 changes: 8 additions & 11 deletions src/System.Windows.Forms/src/System/Windows/Forms/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Windows.Forms.Automation;
using System.Windows.Forms.Layout;
using Microsoft.Win32;
Expand Down Expand Up @@ -2502,7 +2501,7 @@ public int Height
set => SetBounds(_x, _y, _width, value, BoundsSpecified.Height);
}

internal bool HostedInWin32DialogManager
internal unsafe bool HostedInWin32DialogManager
{
get
{
Expand All @@ -2517,22 +2516,20 @@ internal bool HostedInWin32DialogManager
{
IntPtr parentHandle = User32.GetParent(this);
IntPtr lastParentHandle = parentHandle;

StringBuilder sb = new StringBuilder(32);

SetState(States.HostedInDialog, false);

Span<char> buffer = stackalloc char[256];
while (parentHandle != IntPtr.Zero)
{
int len = UnsafeNativeMethods.GetClassName(new HandleRef(null, lastParentHandle), null, 0);
if (len > sb.Capacity)
int length = 0;
fixed (char* valueChars = buffer)
{
sb.Capacity = len + 5;
length = UnsafeNativeMethods.GetClassName(new HandleRef(null, lastParentHandle), valueChars, buffer.Length);
}

UnsafeNativeMethods.GetClassName(new HandleRef(null, lastParentHandle), sb, sb.Capacity);
string result = buffer.Slice(0, length).SliceAtFirstNull().ToString();

if (sb.ToString() == "#32770")
// class name #32770
if (result == "#32770")
{
SetState(States.HostedInDialog, true);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,24 +391,24 @@ private static object ReadObjectFromHandleDeserializer(Stream stream, bool restr
/// </summary>
private static string[]? ReadFileListFromHandle(IntPtr hdrop)
{
uint count = Shell32.DragQueryFileW(hdrop, 0xFFFFFFFF, null);
string? fileName = null;
uint count = Shell32.DragQueryFileW(hdrop, 0xFFFFFFFF, ref fileName);
if (count == 0)
{
return null;
}

var sb = new StringBuilder(Kernel32.MAX_PATH);
var files = new string[count];
for (uint i = 0; i < count; i++)
{
uint charlen = Shell32.DragQueryFileW(hdrop, i, sb);
if (charlen == 0)
uint length = Shell32.DragQueryFileW(hdrop, i, ref fileName);
if (length == 0 || string.IsNullOrEmpty(fileName))
{
continue;
}

string s = sb.ToString(0, (int)charlen);
files[i] = s;
string fullPath = Path.GetFullPath(fileName);
files[i] = fileName;
}

return files;
Expand Down
Loading