【问题标题】:Make a DLL loaded with LoadLibrary show its windows in foreground使加载有 LoadLibrary 的 DLL 在前台显示其窗口
【发布时间】:2020-10-02 12:04:24
【问题描述】:

我为 SAP Business One 开发了一个插件,作为 C# Windows 窗体应用程序。

在这个插件中,我使用 LoadLibraryEx 加载了一个用 C++ 编写的本机非托管 DLL(如果我错了,请纠正我)。

插件调用DLL的方法,这样:

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);

[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int _MyDllMethod();

//...

//load library
var handle = LoadLibraryEx(libPath, IntPtr.Zero, 0x00000008 /*LOAD_WITH_ALTERED_SEARCH_PATH*/);

//invoke method
Type delegateFunType = typeof(_MyDllMethod);
IntPtr funAddr = GetProcAddress(handle, delegateFunType.Name);
var fun = Convert.ChangeType(Marshal.GetDelegateForFunctionPointer(funAddr, delegateFunType), delegateFunType);
int result = fun.Invoke(); //now a window appears

此方法打开一个用户与之交互的窗口。

一切正常,除了这样的窗口开始在任务栏中最小化,而我需要它作为活动窗口出现在前台。我怎样才能做到这一点?

【问题讨论】:

  • 当你调用这个函数时,它是否碰巧返回了一个窗口句柄(HWND)?这是唯一一个“由 DLL 创建”具有任何相关性的地方,否则您将面临寻找窗口并将其置于前台的一般问题。
  • @BenVoigt 谢谢,不幸的是,我只是调用了 DLL 的一个函数,并在其执行过程中打开了一个窗口,所以我什至不知道何时何地将窗口强制置于前台,因为执行权在 DLL 手中...
  • 如果 DLL 没有为您提供 HWND,那么您必须使用查找不是您自己创建的任何窗口的技术来查找窗口。它来自DLL并不重要。重要的是它在运行代码的同一个桌面会话中。使用像 Spy++ 这样的工具来查看您正在寻找的窗口是否有任何独特之处,例如它的类名,或者被您控制的窗口“拥有”,或者类似的东西。然后调用FindWindowEnumWindows 来寻找符合这些特征的那个。最后ShowWindow(SW_NORMAL) 与你得到的HWND。

标签: c# dll window unmanaged foreground


【解决方案1】:

最后我发现了这个技巧:启动一个并行线程来查找将由 DLL 打开的窗口并使用 Windows API 函数将其置于前台:

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);

[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int _MyDllMethod();

[DllImport("User32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);

[DllImport("User32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("User32.dll")]
private static extern bool ShowWindow(IntPtr handle, int nCmdShow);

[DllImport("User32.dll")]
private static extern bool IsIconic(IntPtr handle);

[DllImport("User32.dll")]
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);

private const int ALT = 0xA4;
private const int EXTENDEDKEY = 0x1;
private const int KEYUP = 0x2;
private const int SW_RESTORE = 9;

//...

//launch a parallel thread that looks for library window
//and brings it to the foreground
ThreadPool.QueueUserWorkItem(delegate {

    bool windowFound = false;
    int attempts = 0;
    while (!windowFound && attempts < 10)
    {
        attempts++;

        //check frequently
        Thread.Sleep(200);

        //look for the window using its class name
        IntPtr handle = FindWindow("DllWindowClassName", null);

        //check it is a running process
        if (handle != IntPtr.Zero)
        {
            bool success = false;

            //if window is minimized to icon
            if (IsIconic(handle))
            {
                //then show it
                success = ShowWindow(handle, SW_RESTORE);
            }
            //bring window to front
            success = SetForegroundWindow(handle);
            //once done, this thread can terminate
            if (success)
            {
                windowFound = true;
            }
            else
            {
                //in case of failure, try this hack
                //simulate a key press and release (hack for SetForegroundWindow to work)
                keybd_event((byte)ALT, 0x45, EXTENDEDKEY | 0, 0);
                keybd_event((byte)ALT, 0x45, EXTENDEDKEY | KEYUP, 0);

                //bring window to front
                success = SetForegroundWindow(handle);

                //once done, this thread can terminate
                if (success)
                {
                    windowFound = true;
                }
            }
        }
    }
}, null);

//load library
var handle = LoadLibraryEx(libPath, IntPtr.Zero, 0x00000008 /*LOAD_WITH_ALTERED_SEARCH_PATH*/);

//invoke method
Type delegateFunType = typeof(_MyDllMethod);
IntPtr funAddr = GetProcAddress(handle, delegateFunType.Name);
var fun = Convert.ChangeType(Marshal.GetDelegateForFunctionPointer(funAddr, delegateFunType), delegateFunType);
int result = fun.Invoke(); //now a window appears

您可以使用FindWindow 发现窗口类名,传递窗口标题,然后使用GetClassName。类名比标题更可靠,可以随语言和时间变化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-09
    • 2018-08-19
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多