Black WPF Window when activating it from another application? - c#

i am developing a wpf app, but i have a problem
when i activate my app from another application it shows totally black(demo on the bottom)
this code of activating my window work great but the window shows black
class WinApi2
{
public const int SW_SHOWNORMAL = 5;
[DllImportAttribute("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImportAttribute("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImportAttribute("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public static void ShowToFront(string windowName)
{
IntPtr firstInstance = FindWindow(null, windowName);
ShowWindow(firstInstance, SW_SHOWNORMAL);
SetForegroundWindow(firstInstance);
}
}
// Activating my window by calling:
ShowToFront(".");
The result :
i found a solution by calling This.Show(); on the Activated Event of my wpf window
public void Window_Activated(object sender, EventArgs e)
{
this.Show();
}
but when doing that the black window still there it shows for a quick time and goes like flash that give a bad locking at the app showing.
i tried the same code to show another app(created with c plus plus) no problem, but the problem is just with wpf apps i think Winforms too ! who can i solver this problem ??

Related

Copy main menu from one window to other

I am trying to set a new parent window (host window) for the another window (client window) that belongs to other process. Also, the is a requirement to duplicate main menu of the client window so that the host window should have the same working main menu as the calient window has.
I managed to set the client window menu to the host window, but the host menu does not work. It is possible to navigate through menu, but is does not work (f.e. it not possible to open/create new file).
The question is if it possible to do this at all and if it is, what did I miss?
There is a code snippet:
public partial class Form1 : Form
{
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
private static extern IntPtr GetMenu(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool SetMenu(IntPtr hWnd, IntPtr hMenu);
private Process notepadProcess;
public Form1()
{
InitializeComponent();
}
private void Form1_Shown(object sender, EventArgs e)
{
ProcessStart();
Thread.Sleep(100);
EmbedNotepadWindow();
}
private void ProcessStart()
{
notepadProcess = new Process();
notepadProcess.StartInfo.FileName = "notepad.exe";
notepadProcess.Start();
}
private void EmbedNotepadWindow()
{
SetParent(notepadProcess.MainWindowHandle, panel1.Handle);
var menuHandle = GetMenu(notepadProcess.MainWindowHandle);
SetMenu(this.Handle, menuHandle);
}
}
Copy main menu from Client to Host window

Pre-Launch Console Configuration

I am doing a console app that I change in start up the width, is it possible to do it before the console launch? right now the console launches at default then changes.
I wish it to launch after the configuration was applied.
using C# in Visual Studio.
Thanks
It is sadly (as far as I can tell) impossible.
At first I thought "Hey, let's hide the window at start, set the size, and show the window again" like so:
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SwHide = 0;
private const int SwShow = 5;
public static void Main()
{
_consoleHandle = GetConsoleWindow();
ShowWindow(_consoleHandle, SwHide);
Console.SetWindowSize(100, 30);
ShowWindow(_consoleHandle, SwShow);
}
But that didn't work. Even when I tried hiding the console window in a static constructor (so it gets executed before main). And by "didn't work" I mean, the console showed up, vanished, and reappeared with a different size.
Then I stumbled on a Q&A here that said you could hide the console window by setting the output type to "Windows Application" instead of "Console Application". So I thought "hey, I could attach a console after the fact, let's try it":
[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SwHide = 0;
private const int SwShow = 5;
private const uint AttachParentProcess = 0x0ffffffff;
public static void Main()
{
AttachConsole(AttachParentProcess);
var handle = GetConsoleWindow();
Console.SetWindowSize(100, 30);
ShowWindow(handle, SwShow);
Console.WriteLine(Console.WindowHeight);
Console.WriteLine(Console.WindowWidth);
}
But that didn't work either, for some reason it can't attach a console and it returns an invalid handle meaning I can't do anything.

PostMessage from windows service

My issue is PostMessage windows API is not working properly as it works when running from console application.
Working code:
I have 2 application [1] is console application [2] Windows Forms application.
Requirement is I want to send message to all the running instances of application.
console application code:
class Program
{
#region Dll Imports
public const int HWND_BROADCAST = 0xFFFF;
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
#endregion Dll Imports
public static readonly int WM_ACTIVATEAPP = RegisterWindowMessage("CLOSE");
static void Main(string[] args)
{
//we tried to create a mutex, but there's already one (createdNew = false - another app created it before)
//so there's another instance of this application running
Process currentProcess = Process.GetCurrentProcess();
//get the process that has the same name as the current one but a different ID
foreach (Process process in Process.GetProcessesByName("ClientApp1"))
{
if (process.Id != currentProcess.Id)
{
IntPtr handle = process.MainWindowHandle;
//if the handle is non-zero then the main window is visible (but maybe somewhere in the background, that's the reason the user started a new instance)
//so just bring the window to front
//if (handle != IntPtr.Zero)
//SetForegroundWindow(handle);
//else
//tough luck, can't activate the window, it's not visible and we can't get its handle
//so instead notify the process that it has to show it's window
PostMessage((IntPtr)HWND_BROADCAST, WM_ACTIVATEAPP, IntPtr.Zero, IntPtr.Zero);//this message will be sent to MainForm
break;
}
}
}
}
Windows Forms application code:
public partial class Form1 : Form
{
#region Dll Imports
public const int HWND_BROADCAST = 0xFFFF;
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
#endregion Dll Imports
public static readonly int WM_ACTIVATEAPP = RegisterWindowMessage("CLOSE");
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
//someone (another process) said that we should show the window (WM_ACTIVATEAPP)
if (m.Msg == WM_ACTIVATEAPP)
this.Close();
}
}
Above code is working as expected.
My issues start from here. I want to run the same code from windows service instead of console application. Need immediate guidance.
It seems when I run this code from windows service its not getting hold of the process or service runs in different account so message is not getting delivered.
Most probably you run your service as Local System account in session 0 and it is rather isolated for good reasons. For example you don't have access to other desktops/sessions.
You have to implement a different IPC method, e.g. pipes or memory mapped files.

Activate Window and Send Input using SetForegroundWindow and SendKeys in C#

What I'm trying to do is activate another application and send a key input to it to trigger a button. However this code doesn't seem to work. It looks like it can find the application but it does not activate it, and does not send the key.
[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);
private void debugButton_Click(object sender, EventArgs e)
{
//GetProcess by Class
IntPtr rightNowHandle = FindWindow("WindowsForms10.Window.8.app.0.24dc298_r17_ad2", null);
//Get Handle by Process
Process proc = Process.GetProcessesByName("RightNow.CX")[0];
IntPtr ptrFF = proc.MainWindowHandle;
//Get a handle for the Calculator Application main window
if (rightNowHandle == IntPtr.Zero)
{
MessageBox.Show("Could Not Find Right Now");
return;
}
SetForegroundWindow(rightNowHandle); //Activate Handle By Class
//SetForegroundWindow(ptrFF); //Activate Handle By Process
SendKeys.SendWait("{F1}");
}
And here is what I pull with Window Spy
Any help would be greatly appreciated. Thanks.
Figured out my issue.
SetForegroundWindow does not show the application if it's minimized, which is what I was anticipating.
Next I used Windows Input Simulator, to send the input instead of SendKeys.
Here's the code I ended up using.
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
private const int SW_RESTORE = 9;
private void debugButton_Click(object sender, EventArgs e)
{
//GetProcess by Class
IntPtr rightNowHandle = FindWindow("WindowsForms10.Window.8.app.0.24dc298_r17_ad2", null);
//Get Handle by Process
Process proc = Process.GetProcessesByName("RightNow.CX")[0];
IntPtr ptrFF = proc.MainWindowHandle;
//Get a handle for the Calculator Application main window
if (rightNowHandle == IntPtr.Zero)
{
MessageBox.Show("Could Not Find Right Now");
return;
}
SetForegroundWindow(ptrFF); //Activate Handle By Process
ShowWindow(ptrFF, SW_RESTORE); //Maximizes Window in case it was minimized.
//SetForegroundWindow(rightNowHandle); //Activate Handle By Class
InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.LMENU, VirtualKeyCode.VK_A); //Send Left Alt + A
}

In a WPF app how do you override the Console Close command?

In my WPF application I use the Console Window as a debug/message window - I have options setup to show and hide the window as the user desires and all that works great. The only issue is that when the user closes the Console window it exits the entire program. How can I override this so that when the user click on the X it just hides the Console instead of exiting the application?
this is what I have so far:
const int SW_HIDE = 0;
const int SW_SHOW = 5;
public static bool ConsoleVisible { get; private set; }
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public static void HideConsole()
{
var handle = GetConsoleWindow();
ShowWindow(handle, SW_HIDE);
ConsoleVisible = false;
}
public static void ShowConsole()
{
var handle = GetConsoleWindow();
ShowWindow(handle, SW_SHOW);
ConsoleVisible = true;
}
** For people wanting to utilize this you need: using System.Runtime.InteropServices;
This code was derived from this answer: https://stackoverflow.com/a/3571628/649382
** Edit **
Looking around a bit more it seems like this is not possible. I saw an answer here: https://stackoverflow.com/a/12015131/649382 that talks about removing the exit button which would also be acceptable, except it looks like the code is in C++ and I can't figure out it's C# alternative.
** Edit 2 **
I was able to figure out the conversion to C# and have written it as the answer below.
So as has been discussed there is no way to prevent the Console Window from closing the WPF/Application Window. Prior to Windows Vista there were some workarounds, but since then they have been removed (probably for security reasons). The work around I was able to come up with was to disable the Exit button on the Console Window and place show/hide options into my application. The application start class looks like this:
using System;
using System.Windows;
using System.Runtime.InteropServices;
namespace MyApp
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
ConsoleVisible = true;
DisableConsoleExit();
}
#region Console Window Commands
// Show/Hide
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
const uint SW_HIDE = 0;
const uint SW_SHOWNORMAL = 1;
const uint SW_SHOWNOACTIVATE = 4; // Show without activating
public static bool ConsoleVisible { get; private set; }
public static void HideConsole()
{
IntPtr handle = GetConsoleWindow();
ShowWindow(handle, SW_HIDE);
ConsoleVisible = false;
}
public static void ShowConsole(bool active = true)
{
IntPtr handle = GetConsoleWindow();
if (active) { ShowWindow(handle, SW_SHOWNORMAL); }
else { ShowWindow(handle, SW_SHOWNOACTIVATE); }
ConsoleVisible = true;
}
// Disable Console Exit Button
[DllImport("user32.dll")]
static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
static extern IntPtr DeleteMenu(IntPtr hMenu, uint uPosition, uint uFlags);
const uint SC_CLOSE = 0xF060;
const uint MF_BYCOMMAND = (uint)0x00000000L;
public static void DisableConsoleExit()
{
IntPtr handle = GetConsoleWindow();
IntPtr exitButton = GetSystemMenu(handle, false);
if (exitButton != null) DeleteMenu(exitButton, SC_CLOSE, MF_BYCOMMAND);
}
#endregion
}
}
Hope this helps everyone out who may have a similar issue.
I think you should look into creating the console using AllocConsole and releasing it using FreeConsole. That way you may be able to give the user the ability to close the console window while keeping your WPF application running.

Categories