Related
I am programming a mouse cursor to be used in the operating system that has motion blur on Windows 10. I decided to use Unity3D for this because of Unity3D has very powerful in terms of effects and hardware rendering. I coded an overlay window to be rendered as top most. However, context menus on any application are getting rendered on top of the window and the objects on the form are getting behind the context menu. Is there a way to achieve this? I don't need to use Unity3D for this so any suggestions are welcome.
Here is the code I wrote for the window.
using System;
using System.Runtime.InteropServices;
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class TransparentWindow : MonoBehaviour
{
private struct MARGINS
{
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
}
// Define function signatures to import from Windows APIs
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
[DllImport("Dwmapi.dll")]
private static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margins);
[DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
static extern int SetLayeredWindowAttributes(IntPtr hwnd, int crKey, byte bAlpha, int dwFlags);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
private static extern int SetWindowPos(IntPtr hwnd, int hwndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
// Definitions of window styles
const int GWL_STYLE = -16;
const uint WS_POPUP = 0x80000000;
const uint WS_VISIBLE = 0x10000000;
const uint WS_EX_LAYERED = 0x00080000;
const uint WS_EX_TRANSPARENT = 0x00000020;
const uint WS_EX_TOPMOST = 0x00000008;
const int HWND_TOPMOST = -1;
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOZORDER = 0x0004;
const UInt32 SWP_NOREDRAW = 0x0008;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_FRAMECHANGED = 0x0020;
const UInt32 SWP_SHOWWINDOW = 0x0040;
const UInt32 SWP_HIDEWINDOW = 0x0080;
const UInt32 SWP_NOCOPYBITS = 0x0100;
const UInt32 SWP_NOOWNERZORDER = 0x0200;
const UInt32 SWP_NOSENDCHANGING = 0x0400;
const UInt32 SWP_DRAWFRAME = SWP_FRAMECHANGED;
const UInt32 SWP_NOREPOSITION = SWP_NOOWNERZORDER;
const UInt32 SWP_DEFERERASE = 0x2000;
const UInt32 SWP_ASYNCWINDOWPOS = 0x4000;
// This static method is required because legacy OSes do not support
// SetWindowLongPtr
public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, uint dwNewLong)
{
if (IntPtr.Size == 8)
return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
else
return new IntPtr(SetWindowLong32(hWnd, nIndex, dwNewLong));
}
[DllImport("user32.dll", EntryPoint = "SetWindowLong")]
private static extern int SetWindowLong32(IntPtr hWnd, int nIndex, uint dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, uint dwNewLong);
void Start()
{
#if !UNITY_EDITOR // You really don't want to enable this in the editor..
int fWidth = Screen.width;
int fHeight = Screen.height;
var margins = new MARGINS() { cxLeftWidth = -1 };
var hwnd = GetActiveWindow();
SetWindowLong(hwnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
SetWindowLongPtr(hwnd, -20, WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT); // GWL_EXSTYLE -20
SetLayeredWindowAttributes(hwnd, 0, 255, 2);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
DwmExtendFrameIntoClientArea(hwnd, ref margins);
#endif
}
void OnRenderImage(RenderTexture from, RenderTexture to)
{
Graphics.Blit(from, to, m_Material);
}
}
Here is the screenshot animation shows my issue. Please see the second mouse cursor in this screenshot which follows operating system's mouse cursor:
I am writing an application that needs to draw outside of it's main window area. I already have to code to actually do the drawing:
[DllImport("User32.dll")]
public static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("User32.dll")]
public static extern void ReleaseDC(IntPtr hwnd, IntPtr dc);
IntPtr desktopPtr = GetDC(IntPtr.Zero);
Graphics g = Graphics.FromHdc(desktopPtr);
g.DrawLine(Pens.White, 0, 0, Screen.FromControl(this).WorkingArea.Width, Screen.FromControl(this).WorkingArea.Height);
g.Dispose();
ReleaseDC(IntPtr.Zero, desktopPtr);
However the on paint event is an unsuitable place to put the code because it's not called when something outside of the form is redrawn. So my question is, where could this code be placed so it is called whenever part of the screen is redrawn?
If you want content painted on the screen, you should always create a window to hold that content. Painting on the desktop (a window that you don't own) is a bad idea.
The solution is to create a window, with the extended style WS_EX_NOACTIVATE and draw on that in response to WM_PAINT messages. For a WinForms application, the runtime calls Form.OnPaint when you get a WM_PAINT so you can handle that event and do the painting there. To demonstrate:
[DllImport("User32.dll")]
private static extern IntPtr GetWindowLong(IntPtr hWnd, int index);
[DllImport("User32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int index, IntPtr value);
[DllImport("User32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
private const int WS_EX_NOACTIVATE = 0x08000000;
private const int GWL_EXSTYLE = -20;
private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_FRAMECHANGED = 0x0020;
private const uint StyleUpdateFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED;
public Form1()
{
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
this.Paint += Form1_Paint;
this.Shown += Form1_Shown;
}
private void Form1_Shown(object sender, EventArgs e)
{
IntPtr currentStyle = GetWindowLong(this.Handle, GWL_EXSTYLE);
int current = currentStyle.ToInt32();
current |= WS_EX_NOACTIVATE;
SetWindowLong(this.Handle, GWL_EXSTYLE, new IntPtr(current));
SetWindowPos(this.Handle, IntPtr.Zero, 0, 0, 0, 0, StyleUpdateFlags);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.Black);
}
If you want your window to float on top set the form's TopMost property to true. If you want your window to stick to the bottom of the Z-Order (the exact opposite of TopMost) then add the following logic to your form:
private struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public uint flags;
}
private const int WM_WINDOWPOSCHANGING = 0x0046;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_WINDOWPOSCHANGING)
{
if (m.LParam != IntPtr.Zero)
{
WINDOWPOS posInfo = Marshal.PtrToStructure<WINDOWPOS>(m.LParam);
posInfo.hwndInsertAfter = HWND_BOTTOM;
Marshal.StructureToPtr(posInfo, m.LParam, true);
m.Result = IntPtr.Zero;
return;
}
}
base.WndProc(ref m);
}
This handles the WM_WINDOWPOSCHANGING window message, and prevents the window from moving up in the Z-Order by telling the window manager to put it at the bottom.
We want our application to run in full screen mode with no title bar on a Win CE 5.0 powered device. The application is being developed using .NET Compact Framework 3.5 (C#).
I have followed this tutorial, but I encountered an error. Here is my code:
namespace DatalogicDeviceControl
{
public partial class Form1 : Form
{
public const int SWP_ASYNCWINDOWPOS = 0x4000;
public const int SWP_DEFERERASE = 0x2000;
public const int SWP_DRAWFRAME = 0x0020;
public const int SWP_FRAMECHANGED = 0x0020;
public const int SWP_HIDEWINDOW = 0x0080;
public const int SWP_NOACTIVATE = 0x0010;
public const int SWP_NOCOPYBITS = 0x0100;
public const int SWP_NOMOVE = 0x0002;
public const int SWP_NOOWNERZORDER = 0x0200;
public const int SWP_NOREDRAW = 0x0008;
public const int SWP_NOREPOSITION = 0x0200;
public const int SWP_NOSENDCHANGING = 0x0400;
public const int SWP_NOSIZE = 0x0001;
public const int SWP_NOZORDER = 0x0004;
public const int SWP_SHOWWINDOW = 0x0040;
public const int HWND_TOP = 0;
public const int HWND_BOTTOM = 1;
public const int HWND_TOPMOST = -1;
public const int HWND_NOTOPMOST = -2;
public Form1()
{
InitializeComponent();
HideStartBar();
}
[DllImport("coredll.dll", EntryPoint = "FindWindowW", SetLastError = true)]
private static extern IntPtr FindWindowCE(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
public void HideStartBar()
{
IntPtr handle;
try
{
// Find the handle to the Start Bar
handle = FindWindowCE("HHTaskBar", null);
// If the handle is found then hide the start bar
if (handle != IntPtr.Zero)
{
// Hide the start bar
SetWindowPos(handle, 0, 0, 0, 0, 0, SWP_HIDEWINDOW);
}
}
catch
{
MessageBox.Show("Could not hide Start Bar.");
}
}
}
}
I have encountered the following error:
The best overloaded method match for 'DatalogicDeviceControl.Form1.SetWindowPos(System.IntPtr, int, int, int, int, uint)' has some invalid arguments
#dzerow: Your answer is correct: Windows Mobile does not support the user32.dll library.
Use the coredll.dll library instead.
private const int SRCCOPY = 0x00CC0020;
private const string CORE_DLL = "coredll.dll";
private static IntPtr _taskBar;
private static IntPtr _sipButton;
private static string _deviceId, _deviceIp;
private static DateTime _lastUpdateCheck, _startTime;
[DllImport(CORE_DLL)]
public static extern int BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport(CORE_DLL)]
public static extern bool CeRunAppAtEvent(string appName, int Event);
[DllImport(CORE_DLL, EntryPoint = "FindWindowW", SetLastError = true)]
public static extern IntPtr FindWindowCE(string lpClassName, string lpWindowName);
[DllImport(CORE_DLL)]
private static extern IntPtr GetDC(IntPtr hwnd);
[DllImport(CORE_DLL)]
public static extern bool MessageBeep(int uType);
[DllImport(CORE_DLL, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
Below is a lot of the code from an application I used to manage. Parts of it may be incomplete and there could be too much info. If something is missing, just comment and I will fill that in.
Basically, the routine I have written enables you to call ShowWindowsMenu(bool enable) to either enable or disable the HHTaskBar (task bar) and the MS_SIPBUTTON (soft input button).
public static void ShowWindowsMenu(bool enable) {
try {
if (enable) {
if (_taskBar != IntPtr.Zero) {
SetWindowPos(_taskBar, IntPtr.Zero, 0, 0, 240, 26, (int)WindowPosition.SWP_SHOWWINDOW); // display the start bar
}
} else {
_taskBar = FindWindowCE("HHTaskBar", null); // Find the handle to the Start Bar
if (_taskBar != IntPtr.Zero) { // If the handle is found then hide the start bar
SetWindowPos(_taskBar, IntPtr.Zero, 0, 0, 0, 0, (int)WindowPosition.SWP_HIDEWINDOW); // Hide the start bar
}
}
} catch (Exception err) {
ErrorWrapper(enable ? "Show Start" : "Hide Start", err);
}
try {
if (enable) {
if (_sipButton != IntPtr.Zero) { // If the handle is found then hide the start bar
SetWindowPos(_sipButton, IntPtr.Zero, 0, 0, 240, 26, (int)WindowPosition.SWP_SHOWWINDOW); // display the start bar
}
} else {
_sipButton = FindWindowCE("MS_SIPBUTTON", "MS_SIPBUTTON");
if (_sipButton != IntPtr.Zero) { // If the handle is found then hide the start bar
SetWindowPos(_sipButton, IntPtr.Zero, 0, 0, 0, 0, (int)WindowPosition.SWP_HIDEWINDOW); // Hide the start bar
}
}
} catch (Exception err) {
ErrorWrapper(enable ? "Show SIP" : "Hide SIP", err);
}
}
Be sure to turn these features back on when your program exits, or the user will have to reboot the device to get those re-enabled.
EDIT: I forgot the WindowPosition enumerated value I created:
public enum WindowPosition {
SWP_HIDEWINDOW = 0x0080,
SWP_SHOWWINDOW = 0x0040
}
Sorry about that.
Anything else?
Unfortunately Windows Mobile doesn't contain user32.dll, as well as many other normal Windows API DLLs. I had to P/Invoke into coredll.dll instead. For signatures, see PInvoke.net's section (at the bottom left) for "Smart Device Functions".
Hey all. i was wondering how would i make a GUI that is "transparent" in C#. Now im not talking about transparencykey etc.
I want to make a window that uses vistas aero theme but instead of a control in the form i just want it to show more of that seethru aero look. And also i want to remove all buttons and icons and text from the window. How would i do this?
EDIT... I found a PERFECT example of what i want to create. Load up the windows mobility center on vista. How can i create something like that but without the boxes in it.
How to Glass into your Client Area
Here is the API I used when I did this for our Windows app a few years ago.
Check out the method ExtendGlassIntoClientArea(Form form, int leftMargin, int topMargin, int rightMargin, int bottomMargin)
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace UserInterface
{
public enum TextStyle
{
Normal,
Glowing
}
public static class Glass
{
[DllImport("dwmapi.dll")]
private static extern void DwmIsCompositionEnabled(ref bool pfEnabled);
[DllImport("dwmapi.dll")]
private static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMargins);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true)]
private static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport("UxTheme.dll", CharSet = CharSet.Unicode)]
private static extern int DrawThemeTextEx(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, string text, int iCharCount, int dwFlags, ref RECT pRect, ref DTTOPTS pOptions);
[DllImport("gdi32.dll")]
static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BITMAPINFO pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);
[StructLayout(LayoutKind.Sequential)]
private struct DTTOPTS
{
public int dwSize;
public int dwFlags;
public int crText;
public int crBorder;
public int crShadow;
public int iTextShadowType;
public POINT ptShadowOffset;
public int iBorderSize;
public int iFontPropId;
public int iColorPropId;
public int iStateId;
public bool fApplyOverlay;
public int iGlowSize;
public int pfnDrawTextCallback;
public IntPtr lParam;
}
private const int DTT_COMPOSITED = 8192;
private const int DTT_GLOWSIZE = 2048;
private const int DTT_TEXTCOLOR = 1;
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public POINT(int x, int y)
{
this.x = x;
this.y = y;
}
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private class BITMAPINFO
{
public int biSize;
public int biWidth;
public int biHeight;
public short biPlanes;
public short biBitCount;
public int biCompression;
public int biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant;
public byte bmiColors_rgbBlue;
public byte bmiColors_rgbGreen;
public byte bmiColors_rgbRed;
public byte bmiColors_rgbReserved;
}
[StructLayout(LayoutKind.Sequential)]
private struct MARGINS
{
public int left, right, top, bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public RECT(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public RECT(Rectangle rectangle)
{
Left = rectangle.X;
Top = rectangle.Y;
Right = rectangle.Right;
Bottom = rectangle.Bottom;
}
public Rectangle ToRectangle()
{
return new Rectangle(Left, Top, Right - Left, Bottom - Top);
}
public override string ToString()
{
return "Left: " + Left + ", " + "Top: " + Top + ", Right: " + Right + ", Bottom: " + Bottom;
}
}
public static bool IsCompositionEnabled
{
get
{
if (Environment.OSVersion.Version.Major < 6)
return false;
bool compositionEnabled = false;
DwmIsCompositionEnabled(ref compositionEnabled);
return compositionEnabled;
}
}
public static void ExtendGlassIntoClientArea(Form form, int leftMargin, int topMargin, int rightMargin, int bottomMargin)
{
MARGINS m = new MARGINS();
m.left = leftMargin;
m.right = rightMargin;
m.top = topMargin;
m.bottom = bottomMargin;
DwmExtendFrameIntoClientArea(form.Handle, ref m);
}
public static void DrawText(Graphics graphics, string text, Font font, Rectangle bounds, Color color, TextFormatFlags flags)
{
DrawText(graphics, text, font, bounds, color, flags, TextStyle.Normal);
}
public static void DrawText(Graphics graphics, string text, Font font, Rectangle bounds, Color color, TextFormatFlags flags, TextStyle textStyle)
{
IntPtr primaryHdc = graphics.GetHdc();
// Create a memory DC so we can work offscreen
IntPtr memoryHdc = CreateCompatibleDC(primaryHdc);
// Create a device-independent bitmap and select it into our DC
BITMAPINFO info = new BITMAPINFO();
info.biSize = Marshal.SizeOf(info);
info.biWidth = bounds.Width;
info.biHeight = -bounds.Height;
info.biPlanes = 1;
info.biBitCount = 32;
info.biCompression = 0; // BI_RGB
int ppvBits;
IntPtr dib = CreateDIBSection(primaryHdc, ref info, 0, out ppvBits, IntPtr.Zero, 0);
SelectObject(memoryHdc, dib);
// Create and select font
IntPtr fontHandle = font.ToHfont();
SelectObject(memoryHdc, fontHandle);
// Draw glowing text
System.Windows.Forms.VisualStyles.VisualStyleRenderer renderer = new System.Windows.Forms.VisualStyles.VisualStyleRenderer(System.Windows.Forms.VisualStyles.VisualStyleElement.Window.Caption.Active);
DTTOPTS dttOpts = new DTTOPTS();
dttOpts.dwSize = Marshal.SizeOf(typeof(DTTOPTS));
if (textStyle == TextStyle.Glowing)
{
dttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE | DTT_TEXTCOLOR;
}
else
{
dttOpts.dwFlags = DTT_COMPOSITED | DTT_TEXTCOLOR;
}
dttOpts.crText = ColorTranslator.ToWin32(color);
dttOpts.iGlowSize = 8; // This is about the size Microsoft Word 2007 uses
RECT textBounds = new RECT(0, 0, bounds.Right - bounds.Left, bounds.Bottom - bounds.Top);
DrawThemeTextEx(renderer.Handle, memoryHdc, 0, 0, text, -1, (int)flags, ref textBounds, ref dttOpts);
// Copy to foreground
const int SRCCOPY = 0x00CC0020;
BitBlt(primaryHdc, bounds.Left, bounds.Top, bounds.Width, bounds.Height, memoryHdc, 0, 0, SRCCOPY);
// Clean up
DeleteObject(fontHandle);
DeleteObject(dib);
DeleteDC(memoryHdc);
graphics.ReleaseHdc(primaryHdc);
}
}
}
if WPF, it's a matter of turning transparency on, and setting the right values for the background (additionally, WPF supports AERO natively for borders and what-not).
For traditional winforms... things start getting hard.
plus there's always Form.Opacity property of the windows Form.
Have you tried to check out this which was referred in another question?
I've been using Rainlendar for some time and I noticed that it has an option to put the window "on desktop". It's like a bottomMost window (as against topmost).
How could I do this on a WPF app?
Thanks
My answer is in terms of the Win32 API, not specific to WPF (and probably requiring P/Invoke from C#):
Rainlendar has two options:
"On Desktop", it becomes a child of the Explorer desktop window ("Program Manager"). You could achieve this with the SetParent API.
"On Bottom" is what you describe - its windows stay at the bottom of the Z-order, just in front of the desktop. It's easy enough to put them there to begin with (see SetWindowPos) - the trick is to stop them coming to the front when clicked. I would suggest handling the WM_WINDOWPOSCHANGING message.
This is what I used so the window is always "on bottom":
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
...
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
public static void SetBottom(Window window)
{
IntPtr hWnd = new WindowInteropHelper(window).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
}
I was trying to do the same...
i've used a lot of ideas arround, but i was able to to it and prevent the flickering.
I managed to override WndProc, used one setwindowpos before to put it in the background, and another to prevent it from getting the focus...
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_NOZORDER = 0x0004;
const int WM_ACTIVATEAPP = 0x001C;
const int WM_ACTIVATE = 0x0006;
const int WM_SETFOCUS = 0x0007;
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
const int WM_WINDOWPOSCHANGING = 0x0046;
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd,
IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr BeginDeferWindowPos(int nNumWindows);
[DllImport("user32.dll")]
static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);
private void Window_Loaded(object sender, RoutedEventArgs e)
{
IntPtr hWnd = new WindowInteropHelper(this).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SETFOCUS)
{
IntPtr hWnd = new WindowInteropHelper(this).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
handled = true;
}
return IntPtr.Zero;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.RemoveHook(new HwndSourceHook(this.WndProc));
}
The OnDesktop version that Im using:
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
public static void SetOnDesktop(Window window)
{
IntPtr hWnd = new WindowInteropHelper(window).Handle;
IntPtr hWndProgMan = FindWindow("Progman", "Program Manager");
SetParent(hWnd, hWndProgMan);
}
I was having some trouble finding the Program Manager window, but Kimmo, the creator from Rainlendar gave me a link to the code:
http://www.ipi.fi/~rainy/legacy.html
If anybody needs more detail just look in library/rainwindow.cpp for the function SetWindowZPos.
The attached property version of #HrejWaltz's answer:
Update (12/28/2016)
public class WindowSinker
{
#region Properties
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_NOZORDER = 0x0004;
const int WM_ACTIVATEAPP = 0x001C;
const int WM_ACTIVATE = 0x0006;
const int WM_SETFOCUS = 0x0007;
const int WM_WINDOWPOSCHANGING = 0x0046;
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
Window Window = null;
#endregion
#region WindowSinker
public WindowSinker(Window Window)
{
this.Window = Window;
}
#endregion
#region Methods
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr BeginDeferWindowPos(int nNumWindows);
[DllImport("user32.dll")]
static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);
void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
var Handle = (new WindowInteropHelper(Window)).Handle;
var Source = HwndSource.FromHwnd(Handle);
Source.RemoveHook(new HwndSourceHook(WndProc));
}
void OnLoaded(object sender, RoutedEventArgs e)
{
var Hwnd = new WindowInteropHelper(Window).Handle;
SetWindowPos(Hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
var Handle = (new WindowInteropHelper(Window)).Handle;
var Source = HwndSource.FromHwnd(Handle);
Source.AddHook(new HwndSourceHook(WndProc));
}
IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SETFOCUS)
{
hWnd = new WindowInteropHelper(Window).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
handled = true;
}
return IntPtr.Zero;
}
public void Sink()
{
Window.Loaded += OnLoaded;
Window.Closing += OnClosing;
}
public void Unsink()
{
Window.Loaded -= OnLoaded;
Window.Closing -= OnClosing;
}
#endregion
}
public static class WindowExtensions
{
#region Always On Bottom
public static readonly DependencyProperty SinkerProperty = DependencyProperty.RegisterAttached("Sinker", typeof(WindowSinker), typeof(WindowExtensions), new UIPropertyMetadata(null));
public static WindowSinker GetSinker(DependencyObject obj)
{
return (WindowSinker)obj.GetValue(SinkerProperty);
}
public static void SetSinker(DependencyObject obj, WindowSinker value)
{
obj.SetValue(SinkerProperty, value);
}
public static readonly DependencyProperty AlwaysOnBottomProperty = DependencyProperty.RegisterAttached("AlwaysOnBottom", typeof(bool), typeof(WindowExtensions), new UIPropertyMetadata(false, OnAlwaysOnBottomChanged));
public static bool GetAlwaysOnBottom(DependencyObject obj)
{
return (bool)obj.GetValue(AlwaysOnBottomProperty);
}
public static void SetAlwaysOnBottom(DependencyObject obj, bool value)
{
obj.SetValue(AlwaysOnBottomProperty, value);
}
static void OnAlwaysOnBottomChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var Window = sender as Window;
if (Window != null)
{
if ((bool)e.NewValue)
{
var Sinker = new WindowSinker(Window);
Sinker.Sink();
SetSinker(Window, Sinker);
}
else
{
var Sinker = GetSinker(Window);
Sinker.Unsink();
SetSinker(Window, null);
}
}
}
#endregion
}
Based on the answers of HrejWaltz and James M, I want to provide a modified solution that intercepts and modifies incoming WM_WINDOWPOSCHANGING messages by setting the SWP_NOZORDER flag instead of calling SetWindowPos everytime a WM_SETFOCUS message is received.
The class offers attached properties to directly add to a WPF Window using WindowSinker.AlwaysOnBottom="True".
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
public class WindowSinker
{
#region Windows API
// ReSharper disable InconsistentNaming
private const int WM_WINDOWPOSCHANGING = 0x0046;
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_NOACTIVATE = 0x0010;
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public uint flags;
}
private static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
// ReSharper restore InconsistentNaming
#endregion
#region WindowSinker
private readonly Window window;
private bool disposed;
public WindowSinker(Window window)
{
this.window = window;
if (window.IsLoaded)
{
OnWindowLoaded(window, null);
}
else
{
window.Loaded += OnWindowLoaded;
}
window.Closing += OnWindowClosing;
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
window.Loaded -= OnWindowLoaded;
window.Closing -= OnWindowClosing;
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~WindowSinker()
{
Dispose(false);
}
#endregion
#region Event Handlers
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy,
uint uFlags);
private void OnWindowLoaded(object sender, RoutedEventArgs e)
{
SetWindowPos(new WindowInteropHelper(window).Handle, HWND_BOTTOM, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
var source = HwndSource.FromHwnd(new WindowInteropHelper(window).Handle);
source?.AddHook(WndProc);
}
private void OnWindowClosing(object sender, CancelEventArgs e)
{
var source = HwndSource.FromHwnd(new WindowInteropHelper(window).Handle);
source?.RemoveHook(WndProc);
}
private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_WINDOWPOSCHANGING)
{
var windowPos = Marshal.PtrToStructure<WINDOWPOS>(lParam);
windowPos.flags |= SWP_NOZORDER;
Marshal.StructureToPtr(windowPos, lParam, false);
}
return IntPtr.Zero;
}
#endregion
#region Attached Properties
private static readonly DependencyProperty SinkerProperty = DependencyProperty.RegisterAttached(
"Sinker",
typeof(WindowSinker),
typeof(WindowSinker),
null);
public static readonly DependencyProperty AlwaysOnBottomProperty = DependencyProperty.RegisterAttached(
"AlwaysOnBottom",
typeof(bool),
typeof(WindowSinker),
new UIPropertyMetadata(false, OnAlwaysOnBottomChanged));
public static WindowSinker GetSinker(DependencyObject d)
{
return (WindowSinker) d.GetValue(SinkerProperty);
}
private static void SetSinker(DependencyObject d, WindowSinker value)
{
d.SetValue(SinkerProperty, value);
}
public static bool GetAlwaysOnBottom(DependencyObject d)
{
return (bool) d.GetValue(AlwaysOnBottomProperty);
}
public static void SetAlwaysOnBottom(DependencyObject d, bool value)
{
d.SetValue(AlwaysOnBottomProperty, value);
}
private static void OnAlwaysOnBottomChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (sender is Window window)
{
if ((bool) e.NewValue)
{
SetSinker(window, new WindowSinker(window));
}
else
{
GetSinker(window)?.Dispose();
SetSinker(window, null);
}
}
}
#endregion
}