Make a DLL loaded with LoadLibrary show its windows in foreground

0

I developed an add-on for SAP Business One, as a C# Windows Forms application.

In this add-on, using LoadLibraryEx, I load a native unmanaged DLL (correct me if I'm wrong) written in C++.

The add-on calls a method of the DLL, in this way:

[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

This method opens a window with which the user interacts.

Everything working fine, except that such window starts minimized in the task bar, while I need it to appear in the forground as the active window. How can I acieve this?

c#
dll
window
unmanaged
foreground
asked on Stack Overflow Jun 12, 2020 by bluish

1 Answer

0

Finally I found this hack: launching a parallel thread that looks for the window that will be opened by the DLL and bring it to the foreground with Windows API functions:

[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

You can discover window class name using FindWindow, passing window title, and then with GetClassName. Class name is more reliable than the title, which can change with the language and in the time.

answered on Stack Overflow Aug 27, 2020 by bluish • edited Aug 28, 2020 by bluish

User contributions licensed under CC BY-SA 3.0