I use the following program for Hide/Show desktop Items using c#. It was working Fine.
public partial class Form1 : Form
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, GetWindow_Cmd uCmd);
enum GetWindow_Cmd : uint
{
GW_HWNDFIRST = 0,
GW_HWNDLAST = 1,
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
GW_OWNER = 4,
GW_CHILD = 5,
GW_ENABLEDPOPUP = 6
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private const int WM_COMMAND = 0x111;
static void ToggleDesktopIcons()
{
var toggleDesktopCommand = new IntPtr(0x7402);
IntPtr hWnd = GetWindow(FindWindow("Progman", "Program Manager"), GetWindow_Cmd.GW_CHILD);
SendMessage(hWnd, WM_COMMAND, toggleDesktopCommand, IntPtr.Zero);
}
public Form1()
{
InitializeComponent();
}
private void button1_Click_1(object sender, EventArgs e)
{
ToggleDesktopIcons();
}
}
But my problem is I have only one button in form. If I press the button the hide and show desktop items will happen on the same button.
I need to separate those show and Hide. Which means i need to create one more button in a form, so totally i have 2 buttons now. If I press first button I need to hide desktop items. If I press second button I need to show Desktop Items. How can I do this?
You can store internal state inside Form1 class, for example some bool property.
If you click on Show button and bool property indicates, that your item is already shown - do nothing, else ToggleDesktopIcons. The same for hide button.
See the answer in the this question.
The answer contains an IsVisible method that allows you to see if the icons are hidden or shown.
In the show button: call IsVisible, if it returns false, execute ToggleDesktopIcons, otherwise return.
In the hide button: call IsVisible, if it returns true, execute ToggleDesktopIcons, otherwise return.
You can also use the result of calling IsVisible to decide if you want to enable/disable the show and hide buttons.
Related
So there are various questions on this topic already (from 4-5 years ago) and I have followed them to come up with the following solution to avoid my window reacting to Win+D (Show Desktop) Command:
public partial class MainWindow : Window
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hP, IntPtr hC, string sC, string sW);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
public MainWindow()
{
InitializeComponent();
Loaded += OnWindowLoaded;
}
private void OnWindowLoaded(object sender, RoutedEventArgs e)
{
IntPtr nWinHandle = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Progman", null);
nWinHandle = FindWindowEx(nWinHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
SetParent(new WindowInteropHelper(this).Handle, nWinHandle);
}
}
However this does not seem to work for me (the above code is in a brand new project).
Can anyone explain if there has been any change to the WinAPI, should this still work? This is the answer I come across on almost every question I find on this topic.
I am running:
Edition: Windows 10 Pro
Version: 21H2
Build: 19044.1645
Experience: Windows Feature Experience Pack 120.2212.4170.0,
Ok so I found the solution that works for me, I know setting the window as a child of the desktop has its problems/risks, but for people that still need to do it, here is what I found:
The "SHELLDLL_DefView" desktop window is not always a child of "Progman", sometimes it is a child of a "WorkerW" window instead. This is why the code in my original question didn't work for me.
So rather than finding the "Progman" window and finding the "SHELLDLL_DefView" child, you instead need to enumerate through all windows and find the window which has "SHELLDLL_DefView" as a child.
This is done with the EnumWindows function (https://www.pinvoke.net/default.aspx/user32.enumwindows)
The below code is a mashup of the one of the examples from the pinvoke.net link above and the following 2 answers:
https://stackoverflow.com/a/60856252/9420881
https://stackoverflow.com/a/66832500/9420881
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hP, IntPtr hC, string sC,
string sW);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumWindows(EnumedWindow lpEnumFunc, ArrayList
lParam);
public delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);
public static bool GetWindowHandle(IntPtr windowHandle, ArrayList
windowHandles)
{
windowHandles.Add(windowHandle);
return true;
}
private void SetAsDesktopChild()
{
ArrayList windowHandles = new ArrayList();
EnumedWindow callBackPtr = GetWindowHandle;
EnumWindows(callBackPtr, windowHandles);
foreach (IntPtr windowHandle in windowHandles)
{
IntPtr hNextWin = FindWindowEx(windowHandle, IntPtr.Zero,
"SHELLDLL_DefView", null);
if (hNextWin != IntPtr.Zero)
{
var interop = new WindowInteropHelper(_window);
interop.EnsureHandle();
interop.Owner = hNextWin;
}
}
}
Now my WPF stays on the desktop after Show Desktop/Win+D as intended.
There are some Windows operations that cannot be overridden by the developer, such as:
Always showing your tray icon on the main taskbar
Overriding the Win+D to always show your window on the desktop
Forcing notifications to always be shown
These are policies of the Windows operating system to ensure the user is always in control, and not the developer.
Having said that, you can try another approach to making your "widget" visible. Might I suggest the following:
Create a 1 second System.Threading.Timer
In the timer's callback, check to see if there are any visible desktop windows. When there are none (i.e. due to Win+D or the user just closing/minimizing every window), make your widget window visible.
A 1-second delay to show your widget is a small cost to your UX, in my opinion.
I'm trying to intercept Revit and keep a window from opening. Specifically, I'm trying to apply a keynote to an object and then let the user create a keynote tag, however any way I do it it lets them place the keynote but then immediately gives them the dialog to select a keynote, but I don't want that dialog to come up because I already know what the selection should be. However every way I can think of isn't able to interrupt the process to apply the keynote before the user gets the dialog. Is it possible to perhaps monitor for the window to appear then close it via Windows API? or even better intercept when it's going to be shown and stop it from showing?
you can always delete warrnings with:failuresAccessor.DeleteWarning(fma);
this is what i use for my code
public class FloorPreProcessor : IFailuresPreprocessor
{
FailureProcessingResult
IFailuresPreprocessor.PreprocessFailures(
FailuresAccessor failuresAccessor)
{
IList<FailureMessageAccessor> fmas
= failuresAccessor.GetFailureMessages();
if (fmas.Count == 0)
{
return FailureProcessingResult.Continue;
}
// We already know the transaction name.
if (fmas.Count != 0)
{
foreach (FailureMessageAccessor fma in fmas)
{
// DeleteWarning mimics clicking 'Ok' button.
failuresAccessor.DeleteWarning(fma);
}
return FailureProcessingResult
.ProceedWithCommit;
}
return FailureProcessingResult.Continue;
}
}
I hope it will help
Try the following, it searches for a window name, button name, then clicks this button:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
private const uint BM_CLICK = 0x00F5;
public static bool clickButton (string popUpTitle, string ButtonName)
{
// Get the handle of the window
IntPtr windowHandle = FindWindow((string)null, popUpTitle);
if (windowHandle.ToInt32() == 0)
{
return false;
}
// Get button handle
IntPtr buttonHandle = FindWindowEx(windowHandle, IntPtr.Zero, (string)null, ButtonName);
if (buttonHandle.ToInt32() == 0)
{
return false;
}
// Send click to the button
SendMessage(buttonHandle, BM_CLICK, 0, 0);
return true;
}
You should set the popUpTitle (window name) and the ButtonName to click.
Call this into a timer event that waits for a window to pop-up.
Timer timer = new Timer();
timer.Start();
timer.Tick += new EventHandler(timer_Tick);
//when done call timer.Stop();
private void timer_Tick(object sender, EventArgs e)
{
//set your code to clickButton("","")
}
Try it and let me know.
Ok well since there was a new comment I will make this an official answer. The best I came up with is that you can call OverrideResult() on the dialog even though you can't cancel it. It sill flashes the dialog which isn't ideal but it's better than it was... If anyone has a better way I'd love to hear it :)
I'm trying to build a global hotkey application with C# in Visual Studio 2012 to run on Windows 7. I have everything working except the SendKeys are never showing in the application.
Here is the code I am using to send the keystrokes:
Updated to debug with GetFocusedWindow example.
StringBuilder className = new StringBuilder(256);
IntPtr hWnd = GetForegroundWindow();
GetClassName(hWnd, className, className.Capacity);
Debug.WriteLine("Foreground window: {0}={1}", hWnd.ToInt32().ToString("X"), className);
hWnd = GetFocusedWindow();
GetClassName(hWnd, className, className.Capacity);
Debug.WriteLine("Focused window: {0}={1}", hWnd.ToInt32().ToString("X"), className);
SendKeys.Send("Hello World");
When I debug the program, focus Notepad, and hit the hotkey I get the following debug message and the keystrokes are never inserted into Notepad:
Foreground Window: 4F02B6=Notepad
Focused Window: 1B6026A=WindowsForms10.Window.8.app.0.bf7d44_r11_ad1
How can I send keystrokes to the current foreground window?
Foreground windows doesn't necessary mean focused window. A child window of the top-level foreground window may have the focus, while you're sending keys to its parent.
Retrieving the focused child window from another process is a bit tricky. Try the following implementation of GetFocusedWindow, use it instead of GetForegroundWindow (untested):
static IntPtr GetFocusedWindow()
{
uint currentThread = GetCurrentThreadId();
IntPtr activeWindow = GetForegroundWindow();
uint activeProcess;
uint activeThread = GetWindowThreadProcessId(activeWindow, out activeProcess);
if (currentThread != activeThread)
AttachThreadInput(currentThread, activeThread, true);
try
{
return GetFocus();
}
finally
{
if (currentThread != activeThread)
AttachThreadInput(currentThread, activeThread, false);
}
}
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern IntPtr GetFocus();
[DllImport("user32.dll", SetLastError = true)]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
Updated to address the comment:
When I use this function it sets the focus to my application window.
It's hard to tell what's wrong on your side, the following works for me when the focus is inside Notepad:
private async void Form1_Load(object sender, EventArgs e)
{
var className = new StringBuilder(200);
while (true)
{
await Task.Delay(500);
IntPtr focused = GetFocusedWindow();
GetClassName(focused, className, className.Capacity);
var classNameStr = className.ToString();
this.Text = classNameStr;
if (classNameStr == "Edit")
SendKeys.Send("Hello!");
}
}
We have a legacy program with a GUI that we want to use under control of a C# program to compute some values. We can successfully enter values in the numerical input controls, press the compute button, and read the produced answers from text display boxes.
But we can't seem to control a pair of radio buttons .
Calling CheckRadioButton() returns a code of success, but the control does not change state.
Sending a message of BM_CLICK does not change the state.
Attempts at sending WM_LBUTTONDOWN and WM_LBUTTONUP events haven't changed the state.
Has anyone been successful at "remote control" of radio buttons?
Portions of code to illustrate what we are doing:
[DllImport("user32.dll", EntryPoint="SendMessage")]
public static extern int SendMessageStr(int hWnd, uint Msg, int wParam, string lParam);
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, long wParam, long lParam);
[DllImport("user32.dll", EntryPoint="FindWindow", SetLastError=true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", EntryPoint="CheckRadioButton")]
public static extern bool CheckRadioButton(IntPtr hwnd, int firstID, int lastID, int checkedID);
static IntPtr GetControlById(IntPtr parentHwnd, int controlId) {
IntPtr child = new IntPtr(0);
child = GetWindow(parentHwnd, GetWindow_Cmd.GW_CHILD);
while (GetWindowLong(child.ToInt32(), GWL_ID) != controlId) {
child = GetWindow(child, GetWindow_Cmd.GW_HWNDNEXT);
if (child == IntPtr.Zero) return IntPtr.Zero;
}
return child;
}
// find the handle of the parent window
IntPtr ParenthWnd = new IntPtr(0);
ParenthWnd = FindWindowByCaption(IntPtr.Zero, "Legacy Window Title");
// set "N" to 10
IntPtr hwndN = GetControlById(ParenthWnd, 17);
SendMessageStr(hwndN.ToInt32(), WM_SETTEXT, 0, "10");
// press "compute" button (seems to need to be pressed twice(?))
int hwndButton = GetControlById(ParenthWnd, 6).ToInt32();
SendMessage(hwndButton, BM_CLICK, 0, 0);
SendMessage(hwndButton, BM_CLICK, 0, 0);
// following code runs succesfully, but doesn't toggle the radio buttons
bool result = CheckRadioButton(ParenthWnd, 12, 13, 12);
Send the BM_SETCHECK message. Be sure to use a tool like Spy++ to see the messages.
in this case i used another message BM_SETSTATE
SendMessage((IntPtr)hWnd, Win32Api.BM_SETSTATE, (IntPtr)newState, IntPtr.Zero);
When you use a Windows Forms TextBox, the default number of tab stops (spaces) is 8. How do you modify this?
First add the following namespace
using System.Runtime.InteropServices;
Then add the following after the class declaration:
private const int EM_SETTABSTOPS = 0x00CB;
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr h,
int msg,
int wParam,
int [] lParam);
Then add the following to the Form_Load event:
// define value of the Tab indent
int[] stops = {16};
// change the indent
SendMessage(this.textBox1.Handle, EM_SETTABSTOPS, 1, stops);