I have a dll that provides cross-platform app UI. For windows the underlying platform uses Win 32 API. I am trying to create a Win UI 3 app wrapping this dll and adding additional functionality. Specifically, after I make the call to DLL which launches a window, I want to be able to get hold of that window (the dll call doesn't return anything, just launches the window) and programmatically add menu to it. Is it possible to do this? Looking for a C# solution.
Thanks to stackoverflow and Copilot, I managed to solve this. Here is an outline of the solution.
Thread.Sleep(10); // Without sleep, EnumWindows didn't return the window
IntPtr hWnd = FindWindowEx(className, null);
if (hWnd == IntPtr.Zero) { // for some reason FindWindowEx didn't work so as a fallback I used EnumWindows
EnumWindows(EnumWindowsCallback, IntPtr.Zero);
} else
{
AppMenu.SetupMenu(hWnd);
}
[DllImport("User32.dll")]
static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
static bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam)
{
uint processId;
GetWindowThreadProcessId(hWnd, out processId);
if (processId != currentProcessId) // I already have current process id using Process.GetCurrentProcess().Id;
{
return true;
}
string windowTitle = GetWindowTitle(hWnd);
if (windowTitle == appName)
{
AppMenu.SetupMenu(hWnd);
}
return true;
}
static string GetWindowTitle(IntPtr hWnd)
{
const int nChars = 256;
StringBuilder sb = new StringBuilder(nChars);
GetWindowText(hWnd, sb, nChars);
return sb.ToString();
}
[DllImport("User32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
public static void SetupMenu(IntPtr hWnd)
{
IntPtr hMenu = CreateMenu();
IntPtr fMenu = CreatePopupMenu();
AppendMenu(fMenu, MF_STRING, IDM_OPEN_GIF, "&Open GIF File\tCtrl+N");
AppendMenu(fMenu, MF_STRING, IDM_OPEN_GIF_URL, "&Open GIF URL\tCtrl+Shift+N");
// more menu items
AppendMenu(hMenu, MF_POPUP, (uint)fMenu, "File");
// more menus
}
[DllImport("User32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
var result = SetWinEventHook(EVENT_OBJECT_INVOKED, EVENT_OBJECT_INVOKED, IntPtr.Zero, WinEventProc, (uint)App.currentProcessId, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNTHREAD);
if (result == IntPtr.Zero)
{
Debug.WriteLine("Error setting up win event hook");
}
MSG msg;
while (GetMessage(out msg, IntPtr.Zero, 0, 0))
{
TranslateMessage(ref msg);
DispatchMessage(ref msg);
if (msg.message == WM_QUIT)
break;
}
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
public IntPtr hwnd;
public uint message;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public POINT pt;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}