控件的父句柄使用 p/invoke 指向 WindowsFormsParkingWindow
Control's parent handle points to WindowsFormsParkingWindow using p/invoke
考虑以下 WinApi 功能单元测试:
public class WinApiTest
{
[TestMethod]
public void WinApiFindFormTest_SimpleNesting()
{
var form = new Form();
form.Text = @"My form";
var button = new Button();
button.Text = @"My button";
form.Controls.Add(button);
//with below line commented out, the test fails
form.Show();
IntPtr actualParent = WinApiTest.FindParent(button.Handle);
IntPtr expectedParent = form.Handle;
//below 2 lines were added for debugging purposes, they are not part of test
//and they don't affect test results
Debug.WriteLine("Actual: " + WinApi.GetWindowTitle(actualParent));
Debug.WriteLine("Expected: " + WinApi.GetWindowTitle(expectedParent));
Assert.AreEqual(actualParent, expectedParent);
}
//this is a method being tested
//please assume it's located in another class
//I'm not trying to test winapi
public static IntPtr FindParent(IntPtr child)
{
while (true)
{
IntPtr parent = WinApi.GetParent(child);
if (parent == IntPtr.Zero)
{
return child;
}
child = parent;
}
}
}
问题是要让它工作,我必须显示表格,即做 form.Show()
,否则,它会失败并显示以下输出:
Actual: WindowsFormsParkingWindow
Expected: My form
Exception thrown: 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException' in Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll
我读到了这个神秘的 WindowsFormsParkingWindow,它似乎只有在没有指定父项的情况下才有意义。因此,所有没有父级的控件都位于此 window 下。然而,在我的例子中,button
被明确指定为 form
控件的一部分。
问题:有什么正确的方法可以让这个测试通过吗?我正在尝试测试 FindParent
方法。本着单元测试的真正精神,任何东西都不应该突然出现在用户面前。可以执行 Show
和 Hide
序列,但我认为这是解决问题的一种相当 hack-ish 的方法。
下面提供了 WinApi class 的代码 - 它并没有给问题增加太多价值,但如果你绝对必须看到它,就在这里(主要部分来自 this answer on SO):
public class WinApi
{
/// <summary>
/// Get window title for a given IntPtr handle.
/// </summary>
/// <param name="handle">Input handle.</param>
/// <remarks>
/// Major portition of code for below class was used from here:
///
/// </remarks>
public static string GetWindowTitle(IntPtr handle)
{
if (handle == IntPtr.Zero)
{
throw new ArgumentNullException(nameof(handle));
}
int length = WinApi.SendMessageGetTextLength(handle, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
if (length > 0 && length < int.MaxValue)
{
length++; // room for EOS terminator
StringBuilder windowTitle = new StringBuilder(length);
WinApi.SendMessageGetText(handle, WM_GETTEXT, (IntPtr)windowTitle.Capacity, windowTitle);
return windowTitle.ToString();
}
return String.Empty;
}
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessageGetTextLength(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessageGetText(IntPtr hWnd, int msg, IntPtr wParam, [Out] StringBuilder lParam);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern IntPtr GetParent(IntPtr hWnd);
}
当您访问 Handle
属性 时,需要创建 window。 Childwindows需要有一个parentwindow,如果parentwindow还没有创建,则child window 是用停车场 window 作为其 parent 创建的。只有在创建 parent window 时,child window 才会得到 re-parented.
IntPtr actualParent = WinApiTest.FindParent(button.Handle);
IntPtr expectedParent = form.Handle;
当您访问 button.Handle
时,按钮的 window 已创建,但由于表单的 window 尚未创建,停车 window 是 parent.处理此问题的最简单方法是确保表单的 window 在按钮的 window 之前创建。确保在调用按钮句柄上的 GetParent
之前引用 form.Handle
,例如,在您的测试中,您可以颠倒赋值顺序:
IntPtr expectedParent = form.Handle;
IntPtr actualParent = WinApiTest.FindParent(button.Handle);
显然,您希望对这段代码进行注释,以便将来 reader 知道赋值顺序很重要。
我确实想知道为什么你觉得有必要进行这样的测试。我无法想象这种测试会揭示您代码中的错误。
考虑以下 WinApi 功能单元测试:
public class WinApiTest
{
[TestMethod]
public void WinApiFindFormTest_SimpleNesting()
{
var form = new Form();
form.Text = @"My form";
var button = new Button();
button.Text = @"My button";
form.Controls.Add(button);
//with below line commented out, the test fails
form.Show();
IntPtr actualParent = WinApiTest.FindParent(button.Handle);
IntPtr expectedParent = form.Handle;
//below 2 lines were added for debugging purposes, they are not part of test
//and they don't affect test results
Debug.WriteLine("Actual: " + WinApi.GetWindowTitle(actualParent));
Debug.WriteLine("Expected: " + WinApi.GetWindowTitle(expectedParent));
Assert.AreEqual(actualParent, expectedParent);
}
//this is a method being tested
//please assume it's located in another class
//I'm not trying to test winapi
public static IntPtr FindParent(IntPtr child)
{
while (true)
{
IntPtr parent = WinApi.GetParent(child);
if (parent == IntPtr.Zero)
{
return child;
}
child = parent;
}
}
}
问题是要让它工作,我必须显示表格,即做 form.Show()
,否则,它会失败并显示以下输出:
Actual: WindowsFormsParkingWindow
Expected: My form
Exception thrown: 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException' in Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll
我读到了这个神秘的 WindowsFormsParkingWindow,它似乎只有在没有指定父项的情况下才有意义。因此,所有没有父级的控件都位于此 window 下。然而,在我的例子中,button
被明确指定为 form
控件的一部分。
问题:有什么正确的方法可以让这个测试通过吗?我正在尝试测试 FindParent
方法。本着单元测试的真正精神,任何东西都不应该突然出现在用户面前。可以执行 Show
和 Hide
序列,但我认为这是解决问题的一种相当 hack-ish 的方法。
下面提供了 WinApi class 的代码 - 它并没有给问题增加太多价值,但如果你绝对必须看到它,就在这里(主要部分来自 this answer on SO):
public class WinApi
{
/// <summary>
/// Get window title for a given IntPtr handle.
/// </summary>
/// <param name="handle">Input handle.</param>
/// <remarks>
/// Major portition of code for below class was used from here:
///
/// </remarks>
public static string GetWindowTitle(IntPtr handle)
{
if (handle == IntPtr.Zero)
{
throw new ArgumentNullException(nameof(handle));
}
int length = WinApi.SendMessageGetTextLength(handle, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
if (length > 0 && length < int.MaxValue)
{
length++; // room for EOS terminator
StringBuilder windowTitle = new StringBuilder(length);
WinApi.SendMessageGetText(handle, WM_GETTEXT, (IntPtr)windowTitle.Capacity, windowTitle);
return windowTitle.ToString();
}
return String.Empty;
}
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessageGetTextLength(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessageGetText(IntPtr hWnd, int msg, IntPtr wParam, [Out] StringBuilder lParam);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern IntPtr GetParent(IntPtr hWnd);
}
当您访问 Handle
属性 时,需要创建 window。 Childwindows需要有一个parentwindow,如果parentwindow还没有创建,则child window 是用停车场 window 作为其 parent 创建的。只有在创建 parent window 时,child window 才会得到 re-parented.
IntPtr actualParent = WinApiTest.FindParent(button.Handle);
IntPtr expectedParent = form.Handle;
当您访问 button.Handle
时,按钮的 window 已创建,但由于表单的 window 尚未创建,停车 window 是 parent.处理此问题的最简单方法是确保表单的 window 在按钮的 window 之前创建。确保在调用按钮句柄上的 GetParent
之前引用 form.Handle
,例如,在您的测试中,您可以颠倒赋值顺序:
IntPtr expectedParent = form.Handle;
IntPtr actualParent = WinApiTest.FindParent(button.Handle);
显然,您希望对这段代码进行注释,以便将来 reader 知道赋值顺序很重要。
我确实想知道为什么你觉得有必要进行这样的测试。我无法想象这种测试会揭示您代码中的错误。