using System; using System.Collections; using System.Drawing; using System.Runtime.InteropServices; using System.Text; using System.Runtime.InteropServices.ComTypes; using ComTypes = System.Runtime.InteropServices.ComTypes; namespace NativeHelpers { [Flags] public enum ProcessAccessFlags : uint { All = 0x001F0FFF, Terminate = 0x00000001, CreateThread = 0x00000002, VirtualMemoryOperation = 0x00000008, VirtualMemoryRead = 0x00000010, VirtualMemoryWrite = 0x00000020, DuplicateHandle = 0x00000040, CreateProcess = 0x000000080, SetQuota = 0x00000100, SetInformation = 0x00000200, QueryInformation = 0x00000400, QueryLimitedInformation = 0x00001000, Synchronize = 0x00100000 } public delegate bool EnumedWindow(IntPtr handleWindow, ref object lParam); public enum GetAncestorFlags { /// <summary> /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. /// </summary> GetParent = 1, /// <summary> /// Retrieves the root window by walking the chain of parent windows. /// </summary> GetRoot = 2, /// <summary> /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. /// </summary> GetRootOwner = 3 } [StructLayout(LayoutKind.Sequential)] public struct TITLEBARINFO { public const int CCHILDREN_TITLEBAR = 5; public uint cbSize; public RECT rcTitleBar; [MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)] public uint[] rgstate; } [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; public Rectangle ToRectangle() { return Rectangle.FromLTRB(Left, Top, Right, Bottom); } public override string ToString() { return "Rect: " + ToRectangle().ToString(); } } public enum TBBStates { STATE_SYSTEM_UNAVAILABLE = 0x1, STATE_SYSTEM_PRESSED = 0x8, STATE_SYSTEM_INVISIBLE = 0x8000, STATE_SYSTEM_OFFSCREEN = 0x10000, STATE_SYSTEM_FOCUSABLE = 0x100000 } public enum WindowLongParam { /// <summary>Sets a new address for the window procedure.</summary> /// <remarks>You cannot change this attribute if the window does not belong to the same process as the calling thread.</remarks> GWL_WNDPROC = -4, /// <summary>Sets a new application instance handle.</summary> GWLP_HINSTANCE = -6, GWLP_HWNDPARENT = -8, /// <summary>Sets a new identifier of the child window.</summary> /// <remarks>The window cannot be a top-level window.</remarks> GWL_ID = -12, /// <summary>Sets a new window style.</summary> GWL_STYLE = -16, /// <summary>Sets a new extended window style.</summary> /// <remarks>See <see cref="ExWindowStyles"/>.</remarks> GWL_EXSTYLE = -20, /// <summary>Sets the user data associated with the window.</summary> /// <remarks>This data is intended for use by the application that created the window. Its value is initially zero.</remarks> GWL_USERDATA = -21, /// <summary>Sets the return value of a message processed in the dialog box procedure.</summary> /// <remarks>Only applies to dialog boxes.</remarks> DWLP_MSGRESULT = 0, /// <summary>Sets new extra information that is private to the application, such as handles or pointers.</summary> /// <remarks>Only applies to dialog boxes.</remarks> DWLP_USER = 8, /// <summary>Sets the new address of the dialog box procedure.</summary> /// <remarks>Only applies to dialog boxes.</remarks> DWLP_DLGPROC = 4 } [Flags] public enum WindowStylesEx : uint { /// <summary>Specifies a window that accepts drag-drop files.</summary> WS_EX_ACCEPTFILES = 0x00000010, /// <summary>Forces a top-level window onto the taskbar when the window is visible.</summary> WS_EX_APPWINDOW = 0x00040000, /// <summary>Specifies a window that has a border with a sunken edge.</summary> WS_EX_CLIENTEDGE = 0x00000200, /// <summary> /// Specifies a window that paints all descendants in bottom-to-top painting order using double-buffering. /// This cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC. This style is not supported in Windows 2000. /// </summary> /// <remarks> /// With WS_EX_COMPOSITED set, all descendants of a window get bottom-to-top painting order using double-buffering. /// Bottom-to-top painting order allows a descendent window to have translucency (alpha) and transparency (color-key) effects, /// but only if the descendent window also has the WS_EX_TRANSPARENT bit set. /// Double-buffering allows the window and its descendents to be painted without flicker. /// </remarks> WS_EX_COMPOSITED = 0x02000000, /// <summary> /// Specifies a window that includes a question mark in the title bar. When the user clicks the question mark, /// the cursor changes to a question mark with a pointer. If the user then clicks a child window, the child receives a WM_HELP message. /// The child window should pass the message to the parent window procedure, which should call the WinHelp function using the HELP_WM_HELP command. /// The Help application displays a pop-up window that typically contains help for the child window. /// WS_EX_CONTEXTHELP cannot be used with the WS_MAXIMIZEBOX or WS_MINIMIZEBOX styles. /// </summary> WS_EX_CONTEXTHELP = 0x00000400, /// <summary> /// Specifies a window which contains child windows that should take part in dialog box navigation. /// If this style is specified, the dialog manager recurses into children of this window when performing navigation operations /// such as handling the TAB key, an arrow key, or a keyboard mnemonic. /// </summary> WS_EX_CONTROLPARENT = 0x00010000, /// <summary>Specifies a window that has a double border.</summary> WS_EX_DLGMODALFRAME = 0x00000001, /// <summary> /// Specifies a window that is a layered window. /// This cannot be used for child windows or if the window has a class style of either CS_OWNDC or CS_CLASSDC. /// </summary> WS_EX_LAYERED = 0x00080000, /// <summary> /// Specifies a window with the horizontal origin on the right edge. Increasing horizontal values advance to the left. /// The shell language must support reading-order alignment for this to take effect. /// </summary> WS_EX_LAYOUTRTL = 0x00400000, /// <summary>Specifies a window that has generic left-aligned properties. This is the default.</summary> WS_EX_LEFT = 0x00000000, /// <summary> /// Specifies a window with the vertical scroll bar (if present) to the left of the client area. /// The shell language must support reading-order alignment for this to take effect. /// </summary> WS_EX_LEFTSCROLLBAR = 0x00004000, /// <summary> /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. /// </summary> WS_EX_LTRREADING = 0x00000000, /// <summary> /// Specifies a multiple-document interface (MDI) child window. /// </summary> WS_EX_MDICHILD = 0x00000040, /// <summary> /// Specifies a top-level window created with this style does not become the foreground window when the user clicks it. /// The system does not bring this window to the foreground when the user minimizes or closes the foreground window. /// The window does not appear on the taskbar by default. To force the window to appear on the taskbar, use the WS_EX_APPWINDOW style. /// To activate the window, use the SetActiveWindow or SetForegroundWindow function. /// </summary> WS_EX_NOACTIVATE = 0x08000000, /// <summary> /// Specifies a window which does not pass its window layout to its child windows. /// </summary> WS_EX_NOINHERITLAYOUT = 0x00100000, /// <summary> /// Specifies that a child window created with this style does not send the WM_PARENTNOTIFY message to its parent window when it is created or destroyed. /// </summary> WS_EX_NOPARENTNOTIFY = 0x00000004, /// <summary> /// The window does not render to a redirection surface. /// This is for windows that do not have visible content or that use mechanisms other than surfaces to provide their visual. /// </summary> WS_EX_NOREDIRECTIONBITMAP = 0x00200000, /// <summary>Specifies an overlapped window.</summary> WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, /// <summary>Specifies a palette window, which is a modeless dialog box that presents an array of commands.</summary> WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, /// <summary> /// Specifies a window that has generic "right-aligned" properties. This depends on the window class. /// The shell language must support reading-order alignment for this to take effect. /// Using the WS_EX_RIGHT style has the same effect as using the SS_RIGHT (static), ES_RIGHT (edit), and BS_RIGHT/BS_RIGHTBUTTON (button) control styles. /// </summary> WS_EX_RIGHT = 0x00001000, /// <summary>Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default.</summary> WS_EX_RIGHTSCROLLBAR = 0x00000000, /// <summary> /// Specifies a window that displays text using right-to-left reading-order properties. /// The shell language must support reading-order alignment for this to take effect. /// </summary> WS_EX_RTLREADING = 0x00002000, /// <summary>Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input.</summary> WS_EX_STATICEDGE = 0x00020000, /// <summary> /// Specifies a window that is intended to be used as a floating toolbar. /// A tool window has a title bar that is shorter than a normal title bar, and the window title is drawn using a smaller font. /// A tool window does not appear in the taskbar or in the dialog that appears when the user presses ALT+TAB. /// If a tool window has a system menu, its icon is not displayed on the title bar. /// However, you can display the system menu by right-clicking or by typing ALT+SPACE. /// </summary> WS_EX_TOOLWINDOW = 0x00000080, /// <summary> /// Specifies a window that should be placed above all non-topmost windows and should stay above them, even when the window is deactivated. /// To add or remove this style, use the SetWindowPos function. /// </summary> WS_EX_TOPMOST = 0x00000008, /// <summary> /// Specifies a window that should not be painted until siblings beneath the window (that were created by the same thread) have been painted. /// The window appears transparent because the bits of underlying sibling windows have already been painted. /// To achieve transparency without these restrictions, use the SetWindowRgn function. /// </summary> WS_EX_TRANSPARENT = 0x00000020, /// <summary>Specifies a window that has a border with a raised edge.</summary> WS_EX_WINDOWEDGE = 0x00000100 } // IShellLink Interface [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] public interface IShellLinkW { uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags); uint GetIDList(out IntPtr ppidl); uint SetIDList(IntPtr pidl); uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); uint GetHotKey(out ushort pwHotkey); uint SetHotKey(ushort wHotKey); uint GetShowCmd(out int piShowCmd); uint SetShowCmd(int iShowCmd); uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved); uint Resolve(IntPtr hwnd, uint fFlags); uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); } // ShellLink CoClass (ShellLink object) [ComImport, ClassInterface(ClassInterfaceType.None), Guid("00021401-0000-0000-C000-000000000046")] public class CShellLink { } // WIN32_FIND_DATAW Structure [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] public struct WIN32_FIND_DATAW { public uint dwFileAttributes; public ComTypes.FILETIME ftCreationTime; public ComTypes.FILETIME ftLastAccessTime; public ComTypes.FILETIME ftLastWriteTime; public uint nFileSizeHigh; public uint nFileSizeLow; public uint dwReserved0; public uint dwReserved1; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NativeValues.MAX_PATH)] public string cFileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName; } // IPropertyStore Interface [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")] public interface IPropertyStore { uint GetCount([Out] out uint cProps); uint GetAt([In] uint iProp, out PropertyKey pkey); uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv); uint SetValue([In] ref PropertyKey key, [In] PropVariant pv); uint Commit(); } // PropertyKey Structure // Narrowed down from PropertyKey.cs of Windows API Code Pack 1.1 [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct PropertyKey { #region Fields private Guid formatId; // Unique GUID for property private Int32 propertyId; // Property identifier (PID) #endregion #region Public Properties public Guid FormatId { get { return formatId; } } public Int32 PropertyId { get { return propertyId; } } #endregion #region Constructor public PropertyKey(Guid formatId, Int32 propertyId) { this.formatId = formatId; this.propertyId = propertyId; } public PropertyKey(string formatId, Int32 propertyId) { this.formatId = new Guid(formatId); this.propertyId = propertyId; } #endregion } // PropVariant Class (only for string value) // Narrowed down from PropVariant.cs of Windows API Code Pack 1.1 // Originally from http://blogs.msdn.com/b/adamroot/archive/2008/04/11 // /interop-with-propvariants-in-net.aspx [StructLayout(LayoutKind.Explicit)] public sealed class PropVariant : IDisposable { #region Fields [FieldOffset(0)] ushort valueType; // Value type // [FieldOffset(2)] // ushort wReserved1; // Reserved field // [FieldOffset(4)] // ushort wReserved2; // Reserved field // [FieldOffset(6)] // ushort wReserved3; // Reserved field [FieldOffset(8)] IntPtr ptr; // Value #endregion #region Public Properties // Value type (System.Runtime.InteropServices.VarEnum) public VarEnum VarType { get { return (VarEnum)valueType; } set { valueType = (ushort)value; } } // Whether value is empty or null public bool IsNullOrEmpty { get { return (valueType == (ushort)VarEnum.VT_EMPTY || valueType == (ushort)VarEnum.VT_NULL); } } // Value (only for string value) public string Value { get { return Marshal.PtrToStringUni(ptr); } } #endregion #region Constructor public PropVariant() { } // Construct with string value public PropVariant(string value) { if (value == null) throw new ArgumentException("Failed to set value."); valueType = (ushort)VarEnum.VT_LPWSTR; ptr = Marshal.StringToCoTaskMemUni(value); } #endregion #region Destructor ~PropVariant() { Dispose(); } public void Dispose() { NativeCalls.PropVariantClear(this); GC.SuppressFinalize(this); } #endregion } public static class NativeValues { public const int MAX_PATH = 260; public const int INFOTIPSIZE = 1024; public const int STGM_READ = 0x00000000; // STGM constants public const int STGM_WRITE= 0x00000001; // STGM constants public const int STGM_READWRITE = 0x00000002; // STGM constants public const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags // Name = System.AppUserModel.ID // ShellPKey = PKEY_AppUserModel_ID // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 // PropID = 5 // Type = String (VT_LPWSTR) public static readonly PropertyKey AppUserModelIDKey = new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); public static readonly Guid IID_IPropertyStore = new Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"); } public static class NativeCalls { [DllImport("shell32.dll", SetLastError = true)] public static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumWindows(EnumedWindow lpEnumFunc, ref object lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern int GetWindowTextLength(IntPtr hWnd); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll", ExactSpelling = true)] public static extern IntPtr GetAncestor(IntPtr hwnd, GetAncestorFlags flags); [DllImport("user32.dll")] public static extern IntPtr GetLastActivePopup(IntPtr hWnd); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetTitleBarInfo(IntPtr hwnd, ref TITLEBARINFO pti); [DllImport("user32.dll", EntryPoint = "GetWindowLong")] public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex); public static string GetWindowTextManaged(IntPtr hWnd) { // Allocate correct string length first int length = GetWindowTextLength(hWnd); StringBuilder sb = new StringBuilder(length + 1); GetWindowText(hWnd, sb, sb.Capacity); return sb.ToString(); } public static bool IsAltTabWindow(IntPtr hwnd) { TITLEBARINFO ti = new TITLEBARINFO(); if (!IsWindowVisible(hwnd)) return false; IntPtr hwndWalk = IntPtr.Zero; IntPtr hwndTry = GetAncestor(hwnd, GetAncestorFlags.GetRootOwner); while (hwndTry != hwndWalk) { hwndWalk = hwndTry; hwndTry = GetLastActivePopup(hwndWalk); if (IsWindowVisible(hwndTry)) break; } if (hwndWalk != hwnd) return false; // the following removes some task tray programs and "Program Manager" ti.cbSize = (uint)Marshal.SizeOf(typeof(TITLEBARINFO)); GetTitleBarInfo(hwnd, ref ti); if ((ti.rgstate[0] & (uint)TBBStates.STATE_SYSTEM_INVISIBLE) == (uint)TBBStates.STATE_SYSTEM_INVISIBLE) return false; // Tool windows should not be displayed either, these do not appear in the // task bar. if (((WindowStylesEx)GetWindowLongPtr(hwnd, (int)WindowLongParam.GWL_EXSTYLE) & WindowStylesEx.WS_EX_TOOLWINDOW) == WindowStylesEx.WS_EX_TOOLWINDOW) return false; return true; } [DllImport("Ole32.dll", PreserveSig = false)] public extern static void PropVariantClear([In, Out] PropVariant pvar); [DllImport("shell32.dll", SetLastError = true)] public static extern int SHGetPropertyStoreForWindow(IntPtr handle, ref Guid riid, out IPropertyStore propertyStore); // Verify if operation succeeded. public static void VerifySucceeded(uint hresult) { if (hresult > 1) throw new InvalidOperationException("Failed with HRESULT: " + hresult.ToString("X")); } [DllImport("user32.dll", SetLastError = true)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("kernel32.dll", SetLastError = true)] public static extern Int32 GetApplicationUserModelId(IntPtr hProcess, ref UInt32 AppModelIDLength, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder sbAppUserModelID); public static string GetApplicationUserModelIdManaged(IntPtr hProcess) { UInt32 size = 256; StringBuilder sb = new StringBuilder((int)size); VerifySucceeded((uint)GetApplicationUserModelId(hProcess, ref size, sb)); return sb.ToString(); } [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId); [DllImport("kernel32.dll", SetLastError = true)] public static extern Int32 GetCurrentApplicationUserModelId(ref UInt32 AppModelIDLength, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder sbAppUserModelID); public static string GetCurrentApplicationUserModelIdManaged() { UInt32 size = 256; StringBuilder sb = new StringBuilder((int)size); VerifySucceeded((uint)GetCurrentApplicationUserModelId(ref size, sb)); return sb.ToString(); } } }