How could I achieve "pin to desktop" effect (i.e immune against "Show Desktop" command) in Win 7, using FindWindow + SetParent approach, or any other approach that would work in XP as well ?
I know I could create a gadget, but I'd like to have backwards compatibility with XP, where I have this code doing it fine:
IntPtr hWnd = FindWindow(null, "Untitled - Notepad");
IntPtr hDesktop = FindWindow("ProgMan", "Program Manager");
SetParent(hWnd, hDesktop);
in my WPF app, I was able to solve it using a timer, it's working both in XP and Win 7.
public MainWindow()
{
InitializeComponent();
// have some timer to fire in every 1 second
DispatcherTimer detectShowDesktopTimer = new DispatcherTimer();
detectShowDesktopTimer.Tick += new EventHandler(detectShowDesktopTimer_Tick);
detectShowDesktopTimer.Interval = new TimeSpan(0, 0, 1);
detectShowDesktopTimer.Start();
}
#region support immunizing against "Show Desktop"
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private string GetWindowText(IntPtr handle)
{
int chars = 256;
StringBuilder buff = new StringBuilder(chars);
if (GetWindowText(handle, buff, chars) > 0)
return buff.ToString();
else
return string.Empty;
}
#endregion
private void detectShowDesktopTimer_Tick(object sender, EventArgs e)
{
IntPtr fore = GetForegroundWindow();
if (string.IsNullOrWhiteSpace(GetWindowText(fore)))
ShowDesktopDetected();
}
private void ShowDesktopDetected()
{
WindowInteropHelper wndHelper = new WindowInteropHelper(this);
SetForegroundWindow(wndHelper.Handle);
}
While working on a calendar "glued to the desktop", I ran into the same problem.
Here is my solution:
/************ win32 interop stuff ****************/
[DllImport( "user32.dll", SetLastError = true )]
static extern int SetWindowLong( IntPtr hWnd, int nIndex, IntPtr dwNewLong );
[DllImport( "user32.dll", SetLastError = true )]
static extern IntPtr FindWindow( string lpWindowClass, string lpWindowName );
[DllImport( "user32.dll", SetLastError = true )]
static extern IntPtr FindWindowEx( IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle );
const int GWL_HWNDPARENT = -8;
/************* in Form_Load or equivalent ***************/
IntPtr hprog = FindWindowEx(
FindWindowEx(
FindWindow( "Progman", "Program Manager" ),
IntPtr.Zero, "SHELLDLL_DefView", ""
),
IntPtr.Zero, "SysListView32", "FolderView"
);
SetWindowLong( this.Handle, GWL_HWNDPARENT, hprog );
The tricky part was to set the form's owner (SetWindowLong + GWL_HWNDPARENT) instead of the form's parent (SetParent). This fixed the form not being rendered on an aero desktop.
The answers to this question should help:
how to notify my application when show desktop/minimize all/ all windows minimized ?
If not, you could implement it as an active desktop application in XP. Or if it's a simple view-only application, you could actually draw on the desktop wallpaper.
Related
The standard Win + M keyboard shortcut minimizes open windows, but it does not minimize a borderless form.
I'm thinking of capturing key events of Win + M in KeyDown event but no luck, since it does not capture both but only captures the first key.
I can't programmatically minimize it using a keyboard shortcut
Any idea on how to minimize my for form by keyboard shortcut? or can I capture Win + M to trigger minimize function?
I'm using borderless form. To reproduce the problem, create a Form and set its BorderStyle to None and run the application. When you press Win + M, all the windows minimize, but my borderless form stays normal.
As an option you can enable system menu (having minimize window style) for your borderless form, then while it doesn't show the control box, but the minimize command will work as expected.
Add the following piece of code to your Form and then Win + M will work as expected:
private const int WS_SYSMENU = 0x80000;
private const int WS_MINIMIZEBOX = 0x20000;
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.Style = WS_SYSMENU | WS_MINIMIZEBOX;
return p;
}
}
As a totally different option you can register a low level keyboard hook using SetWindowsHookEx and check if you received the Win + M, then minimize the window and let the key goes through other windows.
Add the following piece of code to your Form and then Win + M will work as expected:
private const int WH_KEYBOARD_LL = 13;
[StructLayout(LayoutKind.Sequential)]
private struct KBDLLHOOKSTRUCT
{
public Keys vkCode;
public int scanCode;
public int flags;
public int time;
public IntPtr dwExtraInfo;
}
private delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(
int idHook, HookProc lpfn, IntPtr hmod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(
IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern short GetKeyState(int keyCode);
private const int KEY_PRESSED = 0x8000;
private static bool IsKeyDown(Keys key)
{
return Convert.ToBoolean(GetKeyState((int)key) & KEY_PRESSED);
}
private IntPtr ptrHook;
private HookProc hookProc;
private IntPtr CaptureKeys(int code, IntPtr wParam, IntPtr lParam)
{
if (code >= 0)
{
KBDLLHOOKSTRUCT objKeyInfo =
(KBDLLHOOKSTRUCT)Marshal.PtrToStructure(
lParam, typeof(KBDLLHOOKSTRUCT));
if (objKeyInfo.vkCode == (Keys.M) &&
(IsKeyDown(Keys.LWin) || IsKeyDown(Keys.RWin)))
{
this.WindowState = FormWindowState.Minimized;
return (IntPtr)0;
}
}
return CallNextHookEx(ptrHook, code, wParam, lParam);
}
bool HasAltModifier(int flags)
{
return (flags & 0x20) == 0x20;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
var module = Process.GetCurrentProcess().MainModule;
hookProc = new HookProc(CaptureKeys);
ptrHook = SetWindowsHookEx(
WH_KEYBOARD_LL, hookProc, GetModuleHandle(module.ModuleName), 0);
}
How can I embed a non-native application (application installed in windows 7) in a windows form using c#.
What I have tried so far:
public partial class Form1 : Form
{
Process process = new Process();
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
public Form1()
{
InitializeComponent();
// panel1.Container.Add(process);
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = false;
string path = Path.Combine(Path.GetFullPath(#"C:\Program Files (x86)\Plan-G v3.2.0"), "Plan-G3.exe");
process = Process.Start(path);
Debug.WriteLine(process.MainWindowHandle);
while (process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(1000); // Don't hog the CPU
process.Refresh(); // You need this since `MainWindowHandle` is cached
}
Form1.SetForegroundWindow(process.MainWindowHandle);
SetForegroundWindow(process.MainWindowHandle);
SetParent(process.MainWindowHandle, panel1.Handle);
MoveWindow(process.MainWindowHandle, 0, 0, panel1.Width - 90, panel1.Height, true);
}
}
I am able to get this to work sometimes when I start with debugging.
But when i start without debugging, the application always opens outside of the form. I don't understand why this happens.
I have also tried the [ForceForegroundWindow][1]() workaround.
Is what I am trying to accomplish possible?
Based on other forum answers, it seems it might not be...
My App in WPF is a kind of system monitor, so I want to have it always visible, even in each Virtual Desktop of Windows 10.
Is it possible using only C#?
Looks like all you have to do is set WS_EX_TOOLWINDOW on your main window. Per https://superuser.com/questions/950960/pin-applications-to-multiple-desktops-in-windows-10
Example code:
[DllImport( "user32.dll", EntryPoint = "GetWindowLongPtr" )]
public static extern IntPtr GetWindowLongPtr( IntPtr hWnd, GWL nIndex );
[DllImport( "user32.dll", EntryPoint = "SetWindowLongPtr" )]
public static extern IntPtr SetWindowLongPtr( IntPtr hWnd, GWL nIndex, IntPtr dwNewLong );
const long WS_EX_TOPMOST = 0x00000008L;
public enum GWL : int
{
GWL_WNDPROC = (-4),
GWL_HINSTANCE = (-6),
GWL_HWNDPARENT = (-8),
GWL_STYLE = (-16),
GWL_EXSTYLE = (-20),
GWL_USERDATA = (-21),
GWL_ID = (-12)
}
public static void SetToolWindow( Window window )
{
var wih = new WindowInteropHelper( window );
var style = GetWindowLongPtr( wih.Handle, GWL.GWL_EXSTYLE );
style = new IntPtr( style.ToInt64() | WS_EX_TOPMOST );
SetWindowLongPtr( wih.Handle, GWL.GWL_EXSTYLE, style );
}
I found wrapper .Net 4.6 compatible:
VirtualDesktop
Nice solution!
TY all ;-)
So I'm stuck with a problem, I'm trying to send keys to a game and I have the game in the foreground with help of SetForegroundWindow and I'm using SendInputs API to send the keys to the game.
If I focus on another application the keys are sent to that application but as soon as I focus on the application I want the keys to be sent to, they don't appear there.
I'm trying to save me some time to recruit guild members for my guild and with that I'm trying to send keys to the game.
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr GetMessageExtraInfo();
[DllImport("user32.dll", SetLastError = true)]
static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
Process[] procs = Process.GetProcessesByName("BlackDesert64");
if (procs.Length > 0)
{
if (procs[0].MainWindowHandle != IntPtr.Zero)
{
SetForegroundWindow(procs[0].MainWindowHandle);
Thread.Sleep(1000);
}
}
INPUT[] inputs = new INPUT[]
{
new INPUT
{
type = INPUT_KEYBOARD,
u = new InputUnion
{
ki = new KEYBDINPUT
{
wVk = 0x49,
wScan = 0049,
dwFlags = KEYEVENTF_UNICODE,
dwExtraInfo = GetMessageExtraInfo(),
}
}
},
new INPUT
{
type = INPUT_KEYBOARD,
u = new InputUnion
{
ki = new KEYBDINPUT
{
wVk = 0x49,
wScan = 0049,
dwFlags = KEYEVENTF_KEYUP,
dwExtraInfo = GetMessageExtraInfo(),
}
}
}
};
SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
Rest of the code:
https://pastebin.com/RUm7A311
UPDATE
So I've found the API Interceptor that allows to send keys to a game that uses DirectX and I've set it up but still no outcome.. anyone who can point me in the right direction?
What does value SendInput return?
If it returns 0, then its an indication that some error has happened. You can try to invoke GetLastError, to see if the input was blocked by the UIPI, alternatively try to run your code with local administrator privileges.
Are you sure that procs[0].MainWindowHandle is the correct window handle?
Lastly try to send the message directly to the handle using SendMessage.
Implementation using SendMessage (no need to focus on the window).
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll")]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindows);
[DllImport("User32.dll")]
private static extern Int32 SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, StringBuilder lParam);
void SendKeys()
{
IntPtr hWnd = FindWindow("Notepad", "Untitled - Notepad");
if (!hWnd.Equals(IntPtr.Zero))
{
IntPtr edithWnd = FindWindowEx(hWnd, IntPtr.Zero, "Edit", null);
if (!edithWnd.Equals(IntPtr.Zero))
{
SendMessage(edithWnd, WM_SETTEXT, IntPtr.Zero, new StringBuilder("Test"));
}
}
}
Reference: how-do-i-input-to-another-application
For your problem here is trick,
Use
String Keys = "Test";
SendKeys.Send(Keys);
this code to send keys to any application.
Just put this code in timer_start()
add some delay before starting of timer and stop timer after execution.
Now run your project which will initiate timer, before timeout open your game and wait for keys to press!!
Check this link which contains all Keys and their code to send
https://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.send(v=vs.110).aspx
I am working on a project with some specific requirement: I need to create a program which can monitor and runs exe inside of it using c#. However, an approach of using Console program to actually host another exe seems to be no end. So i used WinForm.
I have been looking and found some quite good solution, which host an UI Application inside the WinForm. But in my case, the exe has no UI, but it is capable of create an UI (OpenGL), so it is not applicable for those solutions. Is there any way to host this kind of exe inside of the WinForm? Which i can run many of it simultaneously?
Thanks
Hosting a process within another one makes no sens. If you want to launch an exe from another one, you can use System.thread.Process and if those process need to interact with each other, well, WCF is made just for that.
Thank you for all your suggestion. However, i did find out another way around to grab the correct Window which created by my console exe, and put it into the correct winform. This is quite a cheat though.
This idea is originally comes from the Window Tabifier from codeproject: http://www.codeproject.com/KB/cs/WindowTabifier.aspx . As my exe does not have the corresponding UI for the process.WaitForInputIdle, i do a cheat on Thread.Sleep(2000) to skip the starting time, and grab the created Window of the console based on the name of it.
Library import:
public delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
[DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true,
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
private static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private extern static bool EnumThreadWindows(int threadId, EnumWindowsProc callback, IntPtr lParam);
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)]
private extern static int GetWindowText(IntPtr hWnd, StringBuilder text, int maxCount);
Some methods to find the correct opened window
public IntPtr FindWindowInProcess(Process process, Func<string, bool> compareTitle)
{
IntPtr windowHandle = IntPtr.Zero;
foreach (ProcessThread t in process.Threads)
{
windowHandle = FindWindowInThread(t.Id, compareTitle);
if (windowHandle != IntPtr.Zero)
{
break;
}
}
return windowHandle;
}
private IntPtr FindWindowInThread(int threadId, Func<string, bool> compareTitle)
{
IntPtr windowHandle = IntPtr.Zero;
EnumThreadWindows(threadId, (hWnd, lParam) =>
{
StringBuilder text = new StringBuilder(200);
GetWindowText(hWnd, text, 200);
if (compareTitle(text.ToString()))
{
windowHandle = hWnd;
return false;
}
else
{
windowHandle = FindChildWindow(hWnd, compareTitle);
if (windowHandle != IntPtr.Zero)
{
return false;
}
}
return true;
}, IntPtr.Zero);
return windowHandle;
}
private IntPtr FindChildWindow(IntPtr hWnd, Func<string, bool> compareTitle)
{
IntPtr windowHandle = IntPtr.Zero;
EnumChildWindows(hWnd, (hChildWnd, lParam) =>
{
StringBuilder text = new StringBuilder(200);
GetWindowText(hChildWnd, text, 200);
if (compareTitle(text.ToString()))
{
windowHandle = hWnd;
return false;
}
return true;
}, IntPtr.Zero);
return windowHandle;
}
Last, start the processes:
String fileName = "myexe.exe";
String dir = Path.GetDirectoryName(fileName);
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = fileName;
process.StartInfo.WorkingDirectory = dir;
process.Start();
// Wait for process to be created and enter idle condition
Thread.Sleep(5000);
IntPtr appWin = FindWindowInProcess(process, s => s.StartsWith("Built on"));
// Put it into this form
SetParent(appWin, this.Handle);
// Remove border and whatnot
SetWindowLong(appWin, GWL_STYLE, WS_VISIBLE);
// Move the window to overlay it on this window
MoveWindow(appWin, 0, 0, this.Width, this.Height, true);
See Is it possible to log message to cmd.exe in C#/.Net? for information how to create/attach to a console.
Also, look at e.g. Poderosa for an open source terminal emulator that you might be able to embed.