5

为什么会SetWindowLong(myForm.hWnd, GWL_HWNDPARENT, parentHwnd)挂?

我可以通过这三个步骤不断地重现这个问题。

  1. 创建 .NET 表单
  2. 初始化 WaitWindow COM 对象,在 COM 对象上调用 ShowWindow,同时传递 .NET Forms Handle
  3. 在 VB6 中调用 SetWindowLong 方法

C# Windows 应用程序(挂起)

private static void Main(string[] args)
{
      Form form = new Form();
      form.Show();

      Interop.WaitWindow waitWindow = new Interop.WaitWindow();
      waitWindow.ShowWindow(form.Handle.ToInt32(), Language.RISEnglish);
}

C# 控制台应用程序(不挂起)

private static void Main(string[] args)
{
      IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;     

      Interop.WaitWindow waitWindow = new Interop.WaitWindow();
      waitWindow.ShowWindow(handle.ToInt32(), Language.RISEnglish);
}

VB6 代码片段

Public Sub ShowWindow(ByVal parentHwnd As Long, ByVal language As Language)

    SetWindowLong(myForm.hWnd, GWL_HWNDPARENT, parentHwnd)  'Hangs Here
    CenterWindow (parentHwnd)

    myForm.ShowRetrieving (language)
    myForm.Show (vbModal)
End Sub

真的很感谢你的帮助:)

编辑

我明白不应该调用 SetWIndowLong 来更改父级,但我试图理解为什么它只在使用 .NET 表单句柄时挂起。

编辑2

我现在认为这个问题与 SetWindowLong 无关,而是与实际句柄本身有关。我仍在调查,但似乎当我从 .NET 调用 VB6 代码时,它会创建一个 RPC 线程。我还不确定,但我感觉它与跨线程问题有关。

4

3 回答 3

3

MSDN 文档清楚地说

您不能使用 GWL_HWNDPARENT 索引调用 SetWindowLong 来更改子窗口的父级。而是使用 SetParent 函数。

于 2012-04-18T19:55:27.870 回答
3

我设法弄清楚到底发生了什么以及如何解决问题。我没有使用 [STAThread] 属性指定我的主入口点,因此它默认为 MTA。这意味着当我调用 VB6 代码时,它创建了一个 RPC 回调线程,并且没有将调用编组到执行 UI 的主线程。

Peter Mortensen 对此写了一个很好的解释

STA 模型用于非线程安全的 COM 对象。这意味着他们不处理自己的同步。它的一个常见用途是 UI 组件。因此,如果另一个线程需要与对象交互(例如按下表单中的按钮),则消息将被编组到 STA 线程上。Windows 窗体消息泵系统就是一个例子。

于 2012-04-19T19:39:26.767 回答
1

你是在 64 位系统上运行的吗?您的 VB6 应用程序是 32 位应用程序吗?如果您陷入这种情况,它将解释为什么创建 RPC 调用,并解释为什么您的非法hack 无法正常工作。如果是这种情况,坏消息是现在有办法让它发挥作用。

您还应该知道 .net 控件的底层窗口句柄可能会在控件的生命周期内发生变化有关此问题的讨论,另请参见SO 中的这个问题。

于 2012-04-19T17:58:16.877 回答