使加载了 LoadLibrary 的 DLL 在前台显示其 windows

Make a DLL loaded with LoadLibrary show its windows in foreground

我为 SAP Business One 开发了一个附加组件,作为 C# Windows Forms 应用程序。

在这个附加组件中,我使用 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

此方法打开一个 window 用户与之交互。

一切正常,除了 这样的 window 在任务栏中开始最小化 ,而我需要它作为活动 window 出现在前台].我该如何实现?

最后我发现了这个 hack:启动一个并行线程来查找将由 DLL 打开的 window 并使用 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 找到 window class 名称,传递 window 标题,然后使用 GetClassName。 Class名字比标题更可靠,名字会随着语言和时间的变化而变化。