diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/SetLnkApp/MainForm.cs b/SetLnkApp/MainForm.cs index bfdb731..3c98b40 100755 --- a/SetLnkApp/MainForm.cs +++ b/SetLnkApp/MainForm.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/SetLnkApp/MainForm.cs b/SetLnkApp/MainForm.cs index bfdb731..3c98b40 100755 --- a/SetLnkApp/MainForm.cs +++ b/SetLnkApp/MainForm.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs index 023999d..d32eac1 100755 --- a/SetLnkApp/Program.cs +++ b/SetLnkApp/Program.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.IO; @@ -29,7 +29,7 @@ { shortcut = new ShellLink(args[1]); } - catch (Exception e) + catch (Exception) { Usage(); return; diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/SetLnkApp/MainForm.cs b/SetLnkApp/MainForm.cs index bfdb731..3c98b40 100755 --- a/SetLnkApp/MainForm.cs +++ b/SetLnkApp/MainForm.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs index 023999d..d32eac1 100755 --- a/SetLnkApp/Program.cs +++ b/SetLnkApp/Program.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.IO; @@ -29,7 +29,7 @@ { shortcut = new ShellLink(args[1]); } - catch (Exception e) + catch (Exception) { Usage(); return; diff --git a/SetLnkApp/SetLnkApp.csproj b/SetLnkApp/SetLnkApp.csproj index ec4d625..379cd46 100755 --- a/SetLnkApp/SetLnkApp.csproj +++ b/SetLnkApp/SetLnkApp.csproj @@ -32,6 +32,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + @@ -46,6 +68,9 @@ + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/SetLnkApp/MainForm.cs b/SetLnkApp/MainForm.cs index bfdb731..3c98b40 100755 --- a/SetLnkApp/MainForm.cs +++ b/SetLnkApp/MainForm.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs index 023999d..d32eac1 100755 --- a/SetLnkApp/Program.cs +++ b/SetLnkApp/Program.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.IO; @@ -29,7 +29,7 @@ { shortcut = new ShellLink(args[1]); } - catch (Exception e) + catch (Exception) { Usage(); return; diff --git a/SetLnkApp/SetLnkApp.csproj b/SetLnkApp/SetLnkApp.csproj index ec4d625..379cd46 100755 --- a/SetLnkApp/SetLnkApp.csproj +++ b/SetLnkApp/SetLnkApp.csproj @@ -32,6 +32,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + @@ -46,6 +68,9 @@ + + NativeHelpers.cs + ShellLink.cs diff --git a/ShellLink.cs b/ShellLink.cs index 6d13f4e..ba4d16d 100755 --- a/ShellLink.cs +++ b/ShellLink.cs @@ -1,15 +1,15 @@ using System; using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; + using System.Text; -using ComTypes = System.Runtime.InteropServices.ComTypes; +using System.Runtime.InteropServices.ComTypes; /**************************************************************************** * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ ***************************************************************************/ -namespace ShellLinkPlus +namespace NativeHelpers { // Modified from http://smdn.jp/programming/tips/createlnk/ // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects @@ -17,236 +17,10 @@ // Partly based on Sending toast notifications from desktop apps sample public class ShellLink : IDisposable { - #region Win32 and COM - - // IShellLink Interface - [ComImport, - InterfaceType(ComInterfaceType.InterfaceIsIUnknown), - Guid("000214F9-0000-0000-C000-000000000046")] - private 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")] - private class CShellLink { } - - // WIN32_FIND_DATAW Structure - [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] - private 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 = 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")] - private 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)] - private 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)] - private 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() - { - PropVariantClear(this); - GC.SuppressFinalize(this); - } - - #endregion - } - - [DllImport("Ole32.dll", PreserveSig = false)] - private extern static void PropVariantClear([In, Out] PropVariant pvar); - - #endregion - #region Fields private IShellLinkW shellLinkW = null; - // Name = System.AppUserModel.ID - // ShellPKey = PKEY_AppUserModel_ID - // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 - // PropID = 5 - // Type = String (VT_LPWSTR) - private readonly PropertyKey AppUserModelIDKey = - new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); - - private const int MAX_PATH = 260; - private const int INFOTIPSIZE = 1024; - - private const int STGM_READ = 0x00000000; // STGM constants - private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags - #endregion #region Private Properties (Interfaces) @@ -300,18 +74,18 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder targetPath = new StringBuilder(MAX_PATH); + StringBuilder targetPath = new StringBuilder(NativeValues.MAX_PATH); WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); - VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, - SLGP_UNCPRIORITY)); + NativeCalls.VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + NativeValues.SLGP_UNCPRIORITY)); return targetPath.ToString(); } set { - VerifySucceeded(shellLinkW.SetPath(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetPath(value)); } } @@ -320,15 +94,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + StringBuilder arguments = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); return arguments.ToString(); } set { - VerifySucceeded(shellLinkW.SetArguments(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetArguments(value)); } } @@ -337,15 +111,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder description = new StringBuilder(INFOTIPSIZE); + StringBuilder description = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); return description.ToString(); } set { - VerifySucceeded(shellLinkW.SetDescription(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetDescription(value)); } } @@ -354,16 +128,16 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder icon = new StringBuilder(INFOTIPSIZE); + StringBuilder icon = new StringBuilder(NativeValues.INFOTIPSIZE); int index; - VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + NativeCalls.VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); return new Tuple(icon.ToString(), index); } set { - VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + NativeCalls.VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); } } @@ -374,7 +148,7 @@ { using (PropVariant pv = new PropVariant()) { - VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.GetValue(NativeValues.AppUserModelIDKey, pv)); if (pv.Value == null) return "Null"; @@ -386,8 +160,8 @@ { using (PropVariant pv = new PropVariant(value)) { - VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); - VerifySucceeded(PropertyStore.Commit()); + NativeCalls.VerifySucceeded(PropertyStore.SetValue(NativeValues.AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.Commit()); } } } @@ -470,15 +244,7 @@ if (!File.Exists(file)) throw new FileNotFoundException("File is not found.", file); else - PersistFile.Load(file, STGM_READ); - } - - // Verify if operation succeeded. - public static void VerifySucceeded(uint hresult) - { - if (hresult > 1) - throw new InvalidOperationException("Failed with HRESULT: " + - hresult.ToString("X")); + PersistFile.Load(file, NativeValues.STGM_READWRITE); } #endregion diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/SetLnkApp/MainForm.cs b/SetLnkApp/MainForm.cs index bfdb731..3c98b40 100755 --- a/SetLnkApp/MainForm.cs +++ b/SetLnkApp/MainForm.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs index 023999d..d32eac1 100755 --- a/SetLnkApp/Program.cs +++ b/SetLnkApp/Program.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.IO; @@ -29,7 +29,7 @@ { shortcut = new ShellLink(args[1]); } - catch (Exception e) + catch (Exception) { Usage(); return; diff --git a/SetLnkApp/SetLnkApp.csproj b/SetLnkApp/SetLnkApp.csproj index ec4d625..379cd46 100755 --- a/SetLnkApp/SetLnkApp.csproj +++ b/SetLnkApp/SetLnkApp.csproj @@ -32,6 +32,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + @@ -46,6 +68,9 @@ + + NativeHelpers.cs + ShellLink.cs diff --git a/ShellLink.cs b/ShellLink.cs index 6d13f4e..ba4d16d 100755 --- a/ShellLink.cs +++ b/ShellLink.cs @@ -1,15 +1,15 @@ using System; using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; + using System.Text; -using ComTypes = System.Runtime.InteropServices.ComTypes; +using System.Runtime.InteropServices.ComTypes; /**************************************************************************** * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ ***************************************************************************/ -namespace ShellLinkPlus +namespace NativeHelpers { // Modified from http://smdn.jp/programming/tips/createlnk/ // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects @@ -17,236 +17,10 @@ // Partly based on Sending toast notifications from desktop apps sample public class ShellLink : IDisposable { - #region Win32 and COM - - // IShellLink Interface - [ComImport, - InterfaceType(ComInterfaceType.InterfaceIsIUnknown), - Guid("000214F9-0000-0000-C000-000000000046")] - private 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")] - private class CShellLink { } - - // WIN32_FIND_DATAW Structure - [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] - private 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 = 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")] - private 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)] - private 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)] - private 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() - { - PropVariantClear(this); - GC.SuppressFinalize(this); - } - - #endregion - } - - [DllImport("Ole32.dll", PreserveSig = false)] - private extern static void PropVariantClear([In, Out] PropVariant pvar); - - #endregion - #region Fields private IShellLinkW shellLinkW = null; - // Name = System.AppUserModel.ID - // ShellPKey = PKEY_AppUserModel_ID - // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 - // PropID = 5 - // Type = String (VT_LPWSTR) - private readonly PropertyKey AppUserModelIDKey = - new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); - - private const int MAX_PATH = 260; - private const int INFOTIPSIZE = 1024; - - private const int STGM_READ = 0x00000000; // STGM constants - private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags - #endregion #region Private Properties (Interfaces) @@ -300,18 +74,18 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder targetPath = new StringBuilder(MAX_PATH); + StringBuilder targetPath = new StringBuilder(NativeValues.MAX_PATH); WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); - VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, - SLGP_UNCPRIORITY)); + NativeCalls.VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + NativeValues.SLGP_UNCPRIORITY)); return targetPath.ToString(); } set { - VerifySucceeded(shellLinkW.SetPath(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetPath(value)); } } @@ -320,15 +94,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + StringBuilder arguments = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); return arguments.ToString(); } set { - VerifySucceeded(shellLinkW.SetArguments(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetArguments(value)); } } @@ -337,15 +111,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder description = new StringBuilder(INFOTIPSIZE); + StringBuilder description = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); return description.ToString(); } set { - VerifySucceeded(shellLinkW.SetDescription(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetDescription(value)); } } @@ -354,16 +128,16 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder icon = new StringBuilder(INFOTIPSIZE); + StringBuilder icon = new StringBuilder(NativeValues.INFOTIPSIZE); int index; - VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + NativeCalls.VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); return new Tuple(icon.ToString(), index); } set { - VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + NativeCalls.VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); } } @@ -374,7 +148,7 @@ { using (PropVariant pv = new PropVariant()) { - VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.GetValue(NativeValues.AppUserModelIDKey, pv)); if (pv.Value == null) return "Null"; @@ -386,8 +160,8 @@ { using (PropVariant pv = new PropVariant(value)) { - VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); - VerifySucceeded(PropertyStore.Commit()); + NativeCalls.VerifySucceeded(PropertyStore.SetValue(NativeValues.AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.Commit()); } } } @@ -470,15 +244,7 @@ if (!File.Exists(file)) throw new FileNotFoundException("File is not found.", file); else - PersistFile.Load(file, STGM_READ); - } - - // Verify if operation succeeded. - public static void VerifySucceeded(uint hresult) - { - if (hresult > 1) - throw new InvalidOperationException("Failed with HRESULT: " + - hresult.ToString("X")); + PersistFile.Load(file, NativeValues.STGM_READWRITE); } #endregion diff --git a/ShortcutUtil/AppResolver.cs b/ShortcutUtil/AppResolver.cs new file mode 100755 index 0000000..6daf0b5 --- /dev/null +++ b/ShortcutUtil/AppResolver.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Windows.System.Update; + +namespace NativeHelpers +{ + // CLSID_StartMenuCacheAndAppResolver {660b90c8-73a9-4b58-8cae-355b7f55341b} + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("660b90c8-73a9-4b58-8cae-355b7f55341b")] + public class CStartMenuCacheAndAppResolver { } + + public class AppResolver + { + IAppResolver_7 app7 = null; + IAppResolver_8 app8 = null; + public AppResolver() + { + Exception last = null; + try + { + app8 = (IAppResolver_8)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app8 = null; + } + + if (app8 == null) + { + try + { + app7 = (IAppResolver_7)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app7 = null; + } + } + + if (app7 == null && app8 == null) + { + throw last; + } + } + + public string GetAppIDForWindow(IntPtr hWnd) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + + public string GetAppIDForProcess(UInt32 processId) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + } + + // IID_IAppResolver_7 {46a6eeff-908e-4dc6-92a6-64be9177b41c} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("46a6eeff-908e-4dc6-92a6-64be9177b41c")] + public interface IAppResolver_7 + { + uint GetAppIDForShortcut(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } + + // IID_IAppResolver_8 {de25675a-72de-44b4-9373-05170450c140} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("de25675a-72de-44b4-9373-05170450c140")] + public interface IAppResolver_8 + { + uint GetAppIDForShortcut(); + uint GetAppIDForShortcutObject(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } +} diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/SetLnkApp/MainForm.cs b/SetLnkApp/MainForm.cs index bfdb731..3c98b40 100755 --- a/SetLnkApp/MainForm.cs +++ b/SetLnkApp/MainForm.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs index 023999d..d32eac1 100755 --- a/SetLnkApp/Program.cs +++ b/SetLnkApp/Program.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.IO; @@ -29,7 +29,7 @@ { shortcut = new ShellLink(args[1]); } - catch (Exception e) + catch (Exception) { Usage(); return; diff --git a/SetLnkApp/SetLnkApp.csproj b/SetLnkApp/SetLnkApp.csproj index ec4d625..379cd46 100755 --- a/SetLnkApp/SetLnkApp.csproj +++ b/SetLnkApp/SetLnkApp.csproj @@ -32,6 +32,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + @@ -46,6 +68,9 @@ + + NativeHelpers.cs + ShellLink.cs diff --git a/ShellLink.cs b/ShellLink.cs index 6d13f4e..ba4d16d 100755 --- a/ShellLink.cs +++ b/ShellLink.cs @@ -1,15 +1,15 @@ using System; using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; + using System.Text; -using ComTypes = System.Runtime.InteropServices.ComTypes; +using System.Runtime.InteropServices.ComTypes; /**************************************************************************** * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ ***************************************************************************/ -namespace ShellLinkPlus +namespace NativeHelpers { // Modified from http://smdn.jp/programming/tips/createlnk/ // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects @@ -17,236 +17,10 @@ // Partly based on Sending toast notifications from desktop apps sample public class ShellLink : IDisposable { - #region Win32 and COM - - // IShellLink Interface - [ComImport, - InterfaceType(ComInterfaceType.InterfaceIsIUnknown), - Guid("000214F9-0000-0000-C000-000000000046")] - private 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")] - private class CShellLink { } - - // WIN32_FIND_DATAW Structure - [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] - private 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 = 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")] - private 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)] - private 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)] - private 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() - { - PropVariantClear(this); - GC.SuppressFinalize(this); - } - - #endregion - } - - [DllImport("Ole32.dll", PreserveSig = false)] - private extern static void PropVariantClear([In, Out] PropVariant pvar); - - #endregion - #region Fields private IShellLinkW shellLinkW = null; - // Name = System.AppUserModel.ID - // ShellPKey = PKEY_AppUserModel_ID - // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 - // PropID = 5 - // Type = String (VT_LPWSTR) - private readonly PropertyKey AppUserModelIDKey = - new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); - - private const int MAX_PATH = 260; - private const int INFOTIPSIZE = 1024; - - private const int STGM_READ = 0x00000000; // STGM constants - private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags - #endregion #region Private Properties (Interfaces) @@ -300,18 +74,18 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder targetPath = new StringBuilder(MAX_PATH); + StringBuilder targetPath = new StringBuilder(NativeValues.MAX_PATH); WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); - VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, - SLGP_UNCPRIORITY)); + NativeCalls.VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + NativeValues.SLGP_UNCPRIORITY)); return targetPath.ToString(); } set { - VerifySucceeded(shellLinkW.SetPath(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetPath(value)); } } @@ -320,15 +94,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + StringBuilder arguments = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); return arguments.ToString(); } set { - VerifySucceeded(shellLinkW.SetArguments(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetArguments(value)); } } @@ -337,15 +111,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder description = new StringBuilder(INFOTIPSIZE); + StringBuilder description = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); return description.ToString(); } set { - VerifySucceeded(shellLinkW.SetDescription(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetDescription(value)); } } @@ -354,16 +128,16 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder icon = new StringBuilder(INFOTIPSIZE); + StringBuilder icon = new StringBuilder(NativeValues.INFOTIPSIZE); int index; - VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + NativeCalls.VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); return new Tuple(icon.ToString(), index); } set { - VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + NativeCalls.VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); } } @@ -374,7 +148,7 @@ { using (PropVariant pv = new PropVariant()) { - VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.GetValue(NativeValues.AppUserModelIDKey, pv)); if (pv.Value == null) return "Null"; @@ -386,8 +160,8 @@ { using (PropVariant pv = new PropVariant(value)) { - VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); - VerifySucceeded(PropertyStore.Commit()); + NativeCalls.VerifySucceeded(PropertyStore.SetValue(NativeValues.AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.Commit()); } } } @@ -470,15 +244,7 @@ if (!File.Exists(file)) throw new FileNotFoundException("File is not found.", file); else - PersistFile.Load(file, STGM_READ); - } - - // Verify if operation succeeded. - public static void VerifySucceeded(uint hresult) - { - if (hresult > 1) - throw new InvalidOperationException("Failed with HRESULT: " + - hresult.ToString("X")); + PersistFile.Load(file, NativeValues.STGM_READWRITE); } #endregion diff --git a/ShortcutUtil/AppResolver.cs b/ShortcutUtil/AppResolver.cs new file mode 100755 index 0000000..6daf0b5 --- /dev/null +++ b/ShortcutUtil/AppResolver.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Windows.System.Update; + +namespace NativeHelpers +{ + // CLSID_StartMenuCacheAndAppResolver {660b90c8-73a9-4b58-8cae-355b7f55341b} + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("660b90c8-73a9-4b58-8cae-355b7f55341b")] + public class CStartMenuCacheAndAppResolver { } + + public class AppResolver + { + IAppResolver_7 app7 = null; + IAppResolver_8 app8 = null; + public AppResolver() + { + Exception last = null; + try + { + app8 = (IAppResolver_8)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app8 = null; + } + + if (app8 == null) + { + try + { + app7 = (IAppResolver_7)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app7 = null; + } + } + + if (app7 == null && app8 == null) + { + throw last; + } + } + + public string GetAppIDForWindow(IntPtr hWnd) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + + public string GetAppIDForProcess(UInt32 processId) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + } + + // IID_IAppResolver_7 {46a6eeff-908e-4dc6-92a6-64be9177b41c} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("46a6eeff-908e-4dc6-92a6-64be9177b41c")] + public interface IAppResolver_7 + { + uint GetAppIDForShortcut(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } + + // IID_IAppResolver_8 {de25675a-72de-44b4-9373-05170450c140} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("de25675a-72de-44b4-9373-05170450c140")] + public interface IAppResolver_8 + { + uint GetAppIDForShortcut(); + uint GetAppIDForShortcutObject(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } +} diff --git a/ShortcutUtil/MainForm.Designer.cs b/ShortcutUtil/MainForm.Designer.cs index 5dbf023..8af74c7 100755 --- a/ShortcutUtil/MainForm.Designer.cs +++ b/ShortcutUtil/MainForm.Designer.cs @@ -48,6 +48,9 @@ // // entryListBox // + this.entryListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.entryListBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable; this.entryListBox.FormattingEnabled = true; this.entryListBox.Location = new System.Drawing.Point(16, 98); @@ -59,6 +62,7 @@ // // createButton // + this.createButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.createButton.Location = new System.Drawing.Point(644, 458); this.createButton.Name = "createButton"; this.createButton.Size = new System.Drawing.Size(144, 40); @@ -77,6 +81,8 @@ // // appUserModelIdBox // + this.appUserModelIdBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.appUserModelIdBox.Location = new System.Drawing.Point(16, 35); this.appUserModelIdBox.Name = "appUserModelIdBox"; this.appUserModelIdBox.Size = new System.Drawing.Size(608, 26); @@ -86,6 +92,7 @@ // // appButton // + this.appButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.appButton.Location = new System.Drawing.Point(633, 35); this.appButton.Name = "appButton"; this.appButton.Size = new System.Drawing.Size(74, 31); @@ -96,6 +103,7 @@ // // hwndButton // + this.hwndButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.hwndButton.Location = new System.Drawing.Point(712, 35); this.hwndButton.MinimumSize = new System.Drawing.Size(76, 26); this.hwndButton.Name = "hwndButton"; @@ -103,6 +111,7 @@ this.hwndButton.TabIndex = 6; this.hwndButton.Text = "HWND"; this.hwndButton.UseVisualStyleBackColor = true; + this.hwndButton.Click += new System.EventHandler(this.hwndButton_Click); // // MainForm // @@ -119,8 +128,6 @@ this.Controls.Add(this.defaultShortcutLabel); this.Name = "MainForm"; this.Text = "Form1"; - this.Shown += new System.EventHandler(this.MainForm_Shown); - this.SizeChanged += new System.EventHandler(this.MainForm_SizeChanged); this.ResumeLayout(false); this.PerformLayout(); diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/SetLnkApp/MainForm.cs b/SetLnkApp/MainForm.cs index bfdb731..3c98b40 100755 --- a/SetLnkApp/MainForm.cs +++ b/SetLnkApp/MainForm.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs index 023999d..d32eac1 100755 --- a/SetLnkApp/Program.cs +++ b/SetLnkApp/Program.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.IO; @@ -29,7 +29,7 @@ { shortcut = new ShellLink(args[1]); } - catch (Exception e) + catch (Exception) { Usage(); return; diff --git a/SetLnkApp/SetLnkApp.csproj b/SetLnkApp/SetLnkApp.csproj index ec4d625..379cd46 100755 --- a/SetLnkApp/SetLnkApp.csproj +++ b/SetLnkApp/SetLnkApp.csproj @@ -32,6 +32,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + @@ -46,6 +68,9 @@ + + NativeHelpers.cs + ShellLink.cs diff --git a/ShellLink.cs b/ShellLink.cs index 6d13f4e..ba4d16d 100755 --- a/ShellLink.cs +++ b/ShellLink.cs @@ -1,15 +1,15 @@ using System; using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; + using System.Text; -using ComTypes = System.Runtime.InteropServices.ComTypes; +using System.Runtime.InteropServices.ComTypes; /**************************************************************************** * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ ***************************************************************************/ -namespace ShellLinkPlus +namespace NativeHelpers { // Modified from http://smdn.jp/programming/tips/createlnk/ // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects @@ -17,236 +17,10 @@ // Partly based on Sending toast notifications from desktop apps sample public class ShellLink : IDisposable { - #region Win32 and COM - - // IShellLink Interface - [ComImport, - InterfaceType(ComInterfaceType.InterfaceIsIUnknown), - Guid("000214F9-0000-0000-C000-000000000046")] - private 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")] - private class CShellLink { } - - // WIN32_FIND_DATAW Structure - [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] - private 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 = 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")] - private 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)] - private 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)] - private 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() - { - PropVariantClear(this); - GC.SuppressFinalize(this); - } - - #endregion - } - - [DllImport("Ole32.dll", PreserveSig = false)] - private extern static void PropVariantClear([In, Out] PropVariant pvar); - - #endregion - #region Fields private IShellLinkW shellLinkW = null; - // Name = System.AppUserModel.ID - // ShellPKey = PKEY_AppUserModel_ID - // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 - // PropID = 5 - // Type = String (VT_LPWSTR) - private readonly PropertyKey AppUserModelIDKey = - new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); - - private const int MAX_PATH = 260; - private const int INFOTIPSIZE = 1024; - - private const int STGM_READ = 0x00000000; // STGM constants - private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags - #endregion #region Private Properties (Interfaces) @@ -300,18 +74,18 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder targetPath = new StringBuilder(MAX_PATH); + StringBuilder targetPath = new StringBuilder(NativeValues.MAX_PATH); WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); - VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, - SLGP_UNCPRIORITY)); + NativeCalls.VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + NativeValues.SLGP_UNCPRIORITY)); return targetPath.ToString(); } set { - VerifySucceeded(shellLinkW.SetPath(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetPath(value)); } } @@ -320,15 +94,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + StringBuilder arguments = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); return arguments.ToString(); } set { - VerifySucceeded(shellLinkW.SetArguments(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetArguments(value)); } } @@ -337,15 +111,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder description = new StringBuilder(INFOTIPSIZE); + StringBuilder description = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); return description.ToString(); } set { - VerifySucceeded(shellLinkW.SetDescription(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetDescription(value)); } } @@ -354,16 +128,16 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder icon = new StringBuilder(INFOTIPSIZE); + StringBuilder icon = new StringBuilder(NativeValues.INFOTIPSIZE); int index; - VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + NativeCalls.VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); return new Tuple(icon.ToString(), index); } set { - VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + NativeCalls.VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); } } @@ -374,7 +148,7 @@ { using (PropVariant pv = new PropVariant()) { - VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.GetValue(NativeValues.AppUserModelIDKey, pv)); if (pv.Value == null) return "Null"; @@ -386,8 +160,8 @@ { using (PropVariant pv = new PropVariant(value)) { - VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); - VerifySucceeded(PropertyStore.Commit()); + NativeCalls.VerifySucceeded(PropertyStore.SetValue(NativeValues.AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.Commit()); } } } @@ -470,15 +244,7 @@ if (!File.Exists(file)) throw new FileNotFoundException("File is not found.", file); else - PersistFile.Load(file, STGM_READ); - } - - // Verify if operation succeeded. - public static void VerifySucceeded(uint hresult) - { - if (hresult > 1) - throw new InvalidOperationException("Failed with HRESULT: " + - hresult.ToString("X")); + PersistFile.Load(file, NativeValues.STGM_READWRITE); } #endregion diff --git a/ShortcutUtil/AppResolver.cs b/ShortcutUtil/AppResolver.cs new file mode 100755 index 0000000..6daf0b5 --- /dev/null +++ b/ShortcutUtil/AppResolver.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Windows.System.Update; + +namespace NativeHelpers +{ + // CLSID_StartMenuCacheAndAppResolver {660b90c8-73a9-4b58-8cae-355b7f55341b} + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("660b90c8-73a9-4b58-8cae-355b7f55341b")] + public class CStartMenuCacheAndAppResolver { } + + public class AppResolver + { + IAppResolver_7 app7 = null; + IAppResolver_8 app8 = null; + public AppResolver() + { + Exception last = null; + try + { + app8 = (IAppResolver_8)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app8 = null; + } + + if (app8 == null) + { + try + { + app7 = (IAppResolver_7)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app7 = null; + } + } + + if (app7 == null && app8 == null) + { + throw last; + } + } + + public string GetAppIDForWindow(IntPtr hWnd) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + + public string GetAppIDForProcess(UInt32 processId) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + } + + // IID_IAppResolver_7 {46a6eeff-908e-4dc6-92a6-64be9177b41c} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("46a6eeff-908e-4dc6-92a6-64be9177b41c")] + public interface IAppResolver_7 + { + uint GetAppIDForShortcut(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } + + // IID_IAppResolver_8 {de25675a-72de-44b4-9373-05170450c140} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("de25675a-72de-44b4-9373-05170450c140")] + public interface IAppResolver_8 + { + uint GetAppIDForShortcut(); + uint GetAppIDForShortcutObject(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } +} diff --git a/ShortcutUtil/MainForm.Designer.cs b/ShortcutUtil/MainForm.Designer.cs index 5dbf023..8af74c7 100755 --- a/ShortcutUtil/MainForm.Designer.cs +++ b/ShortcutUtil/MainForm.Designer.cs @@ -48,6 +48,9 @@ // // entryListBox // + this.entryListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.entryListBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable; this.entryListBox.FormattingEnabled = true; this.entryListBox.Location = new System.Drawing.Point(16, 98); @@ -59,6 +62,7 @@ // // createButton // + this.createButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.createButton.Location = new System.Drawing.Point(644, 458); this.createButton.Name = "createButton"; this.createButton.Size = new System.Drawing.Size(144, 40); @@ -77,6 +81,8 @@ // // appUserModelIdBox // + this.appUserModelIdBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.appUserModelIdBox.Location = new System.Drawing.Point(16, 35); this.appUserModelIdBox.Name = "appUserModelIdBox"; this.appUserModelIdBox.Size = new System.Drawing.Size(608, 26); @@ -86,6 +92,7 @@ // // appButton // + this.appButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.appButton.Location = new System.Drawing.Point(633, 35); this.appButton.Name = "appButton"; this.appButton.Size = new System.Drawing.Size(74, 31); @@ -96,6 +103,7 @@ // // hwndButton // + this.hwndButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.hwndButton.Location = new System.Drawing.Point(712, 35); this.hwndButton.MinimumSize = new System.Drawing.Size(76, 26); this.hwndButton.Name = "hwndButton"; @@ -103,6 +111,7 @@ this.hwndButton.TabIndex = 6; this.hwndButton.Text = "HWND"; this.hwndButton.UseVisualStyleBackColor = true; + this.hwndButton.Click += new System.EventHandler(this.hwndButton_Click); // // MainForm // @@ -119,8 +128,6 @@ this.Controls.Add(this.defaultShortcutLabel); this.Name = "MainForm"; this.Text = "Form1"; - this.Shown += new System.EventHandler(this.MainForm_Shown); - this.SizeChanged += new System.EventHandler(this.MainForm_SizeChanged); this.ResumeLayout(false); this.PerformLayout(); diff --git a/ShortcutUtil/MainForm.cs b/ShortcutUtil/MainForm.cs index 680ed45..c654b19 100755 --- a/ShortcutUtil/MainForm.cs +++ b/ShortcutUtil/MainForm.cs @@ -1,5 +1,7 @@ using Entries; +using NativeHelpers; using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -23,11 +25,9 @@ private Font nameFont; private Brush fontBrush; - private bool shown = false; - private Size listSizeOffset; - private Point buttonOffset; + private AppResolver appResolver; - public MainForm(EntryList entryList) + public MainForm(EntryList entryList, AppResolver appResolver) { InitializeComponent(); @@ -38,6 +38,7 @@ fontBrush = new SolidBrush(defaultShortcutLabel.ForeColor); this.entryList = entryList; + this.appResolver = appResolver; entryListBox.Items.AddRange(this.entryList.Entries.ToArray()); @@ -122,23 +123,6 @@ e.ItemHeight = Size.Add(IconSize, new Size(0, ItemPadding.Height * 2)).Height; } - private void MainForm_SizeChanged(object sender, EventArgs e) - { - if (shown) - { - entryListBox.Size = Size.Subtract(Size, listSizeOffset); - createButton.Location = Point.Add(buttonOffset, entryListBox.Size); - } - } - - private void MainForm_Shown(object sender, EventArgs e) - { - listSizeOffset = Size.Subtract(Size, entryListBox.Size); - buttonOffset = Point.Subtract(createButton.Location, entryListBox.Size); - - shown = true; - } - private SearchForm search = new SearchForm(); private async void appButton_Click(object sender, EventArgs e) { @@ -166,5 +150,83 @@ appButton.Enabled = true; } + + private static bool GetWindowHandle(IntPtr windowHandle, ref object windowHandles) + { + ((ArrayList)windowHandles).Add(windowHandle); + return true; + } + + private void hwndButton_Click(object sender, EventArgs e) + { + hwndButton.Enabled = false; + + object handles = new ArrayList(); + NativeCalls.EnumWindows(GetWindowHandle, ref handles); + + List> titles = new List>(); + foreach (object ptrObj in (ArrayList)handles) + { + IntPtr ptr = (IntPtr)ptrObj; + if (NativeCalls.IsAltTabWindow(ptr)) + { + string title = NativeCalls.GetWindowTextManaged(ptr); + if (title != "") + { + /* + IPropertyStore store; + Guid guid = NativeValues.IID_IPropertyStore; + NativeCalls.VerifySucceeded((uint)NativeCalls.SHGetPropertyStoreForWindow(ptr, ref guid, out store)); + + using (PropVariant pv = new PropVariant()) + { + NativeCalls.VerifySucceeded(store.GetValue(NativeValues.AppUserModelIDKey, pv)); + + string appUserModelId = pv.Value; + if (appUserModelId == null) + { + uint processId; + NativeCalls.GetWindowThreadProcessId(ptr, out processId); + + IntPtr handle = NativeCalls.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, (int)processId); + + try + { + appUserModelId = NativeCalls.GetApplicationUserModelIdManaged(handle); + } + catch (Exception) { } + } + + + } + */ + + string appUserModelId = appResolver.GetAppIDForWindow(ptr); + if (appUserModelId == null) + { + uint processId; + NativeCalls.GetWindowThreadProcessId(ptr, out processId); + + appUserModelId = appResolver.GetAppIDForProcess(processId); + } + + if (appUserModelId != null) + { + titles.Add(new Tuple(ptr, title, appUserModelId == null ? "NULL" : appUserModelId)); + } + } + + } + } + + string selectedAppUserModelId = search.GetSelection("Select Window", "Available Windows:", titles.Select(t => new Tuple(t.Item3, t.Item2 + ": " + t.Item3)).ToList()); + + if (selectedAppUserModelId != null) + { + appUserModelIdBox.Text = selectedAppUserModelId; + } + + hwndButton.Enabled = true; + } } } diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/SetLnkApp/MainForm.cs b/SetLnkApp/MainForm.cs index bfdb731..3c98b40 100755 --- a/SetLnkApp/MainForm.cs +++ b/SetLnkApp/MainForm.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs index 023999d..d32eac1 100755 --- a/SetLnkApp/Program.cs +++ b/SetLnkApp/Program.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.IO; @@ -29,7 +29,7 @@ { shortcut = new ShellLink(args[1]); } - catch (Exception e) + catch (Exception) { Usage(); return; diff --git a/SetLnkApp/SetLnkApp.csproj b/SetLnkApp/SetLnkApp.csproj index ec4d625..379cd46 100755 --- a/SetLnkApp/SetLnkApp.csproj +++ b/SetLnkApp/SetLnkApp.csproj @@ -32,6 +32,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + @@ -46,6 +68,9 @@ + + NativeHelpers.cs + ShellLink.cs diff --git a/ShellLink.cs b/ShellLink.cs index 6d13f4e..ba4d16d 100755 --- a/ShellLink.cs +++ b/ShellLink.cs @@ -1,15 +1,15 @@ using System; using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; + using System.Text; -using ComTypes = System.Runtime.InteropServices.ComTypes; +using System.Runtime.InteropServices.ComTypes; /**************************************************************************** * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ ***************************************************************************/ -namespace ShellLinkPlus +namespace NativeHelpers { // Modified from http://smdn.jp/programming/tips/createlnk/ // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects @@ -17,236 +17,10 @@ // Partly based on Sending toast notifications from desktop apps sample public class ShellLink : IDisposable { - #region Win32 and COM - - // IShellLink Interface - [ComImport, - InterfaceType(ComInterfaceType.InterfaceIsIUnknown), - Guid("000214F9-0000-0000-C000-000000000046")] - private 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")] - private class CShellLink { } - - // WIN32_FIND_DATAW Structure - [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] - private 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 = 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")] - private 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)] - private 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)] - private 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() - { - PropVariantClear(this); - GC.SuppressFinalize(this); - } - - #endregion - } - - [DllImport("Ole32.dll", PreserveSig = false)] - private extern static void PropVariantClear([In, Out] PropVariant pvar); - - #endregion - #region Fields private IShellLinkW shellLinkW = null; - // Name = System.AppUserModel.ID - // ShellPKey = PKEY_AppUserModel_ID - // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 - // PropID = 5 - // Type = String (VT_LPWSTR) - private readonly PropertyKey AppUserModelIDKey = - new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); - - private const int MAX_PATH = 260; - private const int INFOTIPSIZE = 1024; - - private const int STGM_READ = 0x00000000; // STGM constants - private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags - #endregion #region Private Properties (Interfaces) @@ -300,18 +74,18 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder targetPath = new StringBuilder(MAX_PATH); + StringBuilder targetPath = new StringBuilder(NativeValues.MAX_PATH); WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); - VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, - SLGP_UNCPRIORITY)); + NativeCalls.VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + NativeValues.SLGP_UNCPRIORITY)); return targetPath.ToString(); } set { - VerifySucceeded(shellLinkW.SetPath(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetPath(value)); } } @@ -320,15 +94,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + StringBuilder arguments = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); return arguments.ToString(); } set { - VerifySucceeded(shellLinkW.SetArguments(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetArguments(value)); } } @@ -337,15 +111,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder description = new StringBuilder(INFOTIPSIZE); + StringBuilder description = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); return description.ToString(); } set { - VerifySucceeded(shellLinkW.SetDescription(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetDescription(value)); } } @@ -354,16 +128,16 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder icon = new StringBuilder(INFOTIPSIZE); + StringBuilder icon = new StringBuilder(NativeValues.INFOTIPSIZE); int index; - VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + NativeCalls.VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); return new Tuple(icon.ToString(), index); } set { - VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + NativeCalls.VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); } } @@ -374,7 +148,7 @@ { using (PropVariant pv = new PropVariant()) { - VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.GetValue(NativeValues.AppUserModelIDKey, pv)); if (pv.Value == null) return "Null"; @@ -386,8 +160,8 @@ { using (PropVariant pv = new PropVariant(value)) { - VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); - VerifySucceeded(PropertyStore.Commit()); + NativeCalls.VerifySucceeded(PropertyStore.SetValue(NativeValues.AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.Commit()); } } } @@ -470,15 +244,7 @@ if (!File.Exists(file)) throw new FileNotFoundException("File is not found.", file); else - PersistFile.Load(file, STGM_READ); - } - - // Verify if operation succeeded. - public static void VerifySucceeded(uint hresult) - { - if (hresult > 1) - throw new InvalidOperationException("Failed with HRESULT: " + - hresult.ToString("X")); + PersistFile.Load(file, NativeValues.STGM_READWRITE); } #endregion diff --git a/ShortcutUtil/AppResolver.cs b/ShortcutUtil/AppResolver.cs new file mode 100755 index 0000000..6daf0b5 --- /dev/null +++ b/ShortcutUtil/AppResolver.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Windows.System.Update; + +namespace NativeHelpers +{ + // CLSID_StartMenuCacheAndAppResolver {660b90c8-73a9-4b58-8cae-355b7f55341b} + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("660b90c8-73a9-4b58-8cae-355b7f55341b")] + public class CStartMenuCacheAndAppResolver { } + + public class AppResolver + { + IAppResolver_7 app7 = null; + IAppResolver_8 app8 = null; + public AppResolver() + { + Exception last = null; + try + { + app8 = (IAppResolver_8)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app8 = null; + } + + if (app8 == null) + { + try + { + app7 = (IAppResolver_7)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app7 = null; + } + } + + if (app7 == null && app8 == null) + { + throw last; + } + } + + public string GetAppIDForWindow(IntPtr hWnd) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + + public string GetAppIDForProcess(UInt32 processId) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + } + + // IID_IAppResolver_7 {46a6eeff-908e-4dc6-92a6-64be9177b41c} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("46a6eeff-908e-4dc6-92a6-64be9177b41c")] + public interface IAppResolver_7 + { + uint GetAppIDForShortcut(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } + + // IID_IAppResolver_8 {de25675a-72de-44b4-9373-05170450c140} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("de25675a-72de-44b4-9373-05170450c140")] + public interface IAppResolver_8 + { + uint GetAppIDForShortcut(); + uint GetAppIDForShortcutObject(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } +} diff --git a/ShortcutUtil/MainForm.Designer.cs b/ShortcutUtil/MainForm.Designer.cs index 5dbf023..8af74c7 100755 --- a/ShortcutUtil/MainForm.Designer.cs +++ b/ShortcutUtil/MainForm.Designer.cs @@ -48,6 +48,9 @@ // // entryListBox // + this.entryListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.entryListBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable; this.entryListBox.FormattingEnabled = true; this.entryListBox.Location = new System.Drawing.Point(16, 98); @@ -59,6 +62,7 @@ // // createButton // + this.createButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.createButton.Location = new System.Drawing.Point(644, 458); this.createButton.Name = "createButton"; this.createButton.Size = new System.Drawing.Size(144, 40); @@ -77,6 +81,8 @@ // // appUserModelIdBox // + this.appUserModelIdBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.appUserModelIdBox.Location = new System.Drawing.Point(16, 35); this.appUserModelIdBox.Name = "appUserModelIdBox"; this.appUserModelIdBox.Size = new System.Drawing.Size(608, 26); @@ -86,6 +92,7 @@ // // appButton // + this.appButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.appButton.Location = new System.Drawing.Point(633, 35); this.appButton.Name = "appButton"; this.appButton.Size = new System.Drawing.Size(74, 31); @@ -96,6 +103,7 @@ // // hwndButton // + this.hwndButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.hwndButton.Location = new System.Drawing.Point(712, 35); this.hwndButton.MinimumSize = new System.Drawing.Size(76, 26); this.hwndButton.Name = "hwndButton"; @@ -103,6 +111,7 @@ this.hwndButton.TabIndex = 6; this.hwndButton.Text = "HWND"; this.hwndButton.UseVisualStyleBackColor = true; + this.hwndButton.Click += new System.EventHandler(this.hwndButton_Click); // // MainForm // @@ -119,8 +128,6 @@ this.Controls.Add(this.defaultShortcutLabel); this.Name = "MainForm"; this.Text = "Form1"; - this.Shown += new System.EventHandler(this.MainForm_Shown); - this.SizeChanged += new System.EventHandler(this.MainForm_SizeChanged); this.ResumeLayout(false); this.PerformLayout(); diff --git a/ShortcutUtil/MainForm.cs b/ShortcutUtil/MainForm.cs index 680ed45..c654b19 100755 --- a/ShortcutUtil/MainForm.cs +++ b/ShortcutUtil/MainForm.cs @@ -1,5 +1,7 @@ using Entries; +using NativeHelpers; using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -23,11 +25,9 @@ private Font nameFont; private Brush fontBrush; - private bool shown = false; - private Size listSizeOffset; - private Point buttonOffset; + private AppResolver appResolver; - public MainForm(EntryList entryList) + public MainForm(EntryList entryList, AppResolver appResolver) { InitializeComponent(); @@ -38,6 +38,7 @@ fontBrush = new SolidBrush(defaultShortcutLabel.ForeColor); this.entryList = entryList; + this.appResolver = appResolver; entryListBox.Items.AddRange(this.entryList.Entries.ToArray()); @@ -122,23 +123,6 @@ e.ItemHeight = Size.Add(IconSize, new Size(0, ItemPadding.Height * 2)).Height; } - private void MainForm_SizeChanged(object sender, EventArgs e) - { - if (shown) - { - entryListBox.Size = Size.Subtract(Size, listSizeOffset); - createButton.Location = Point.Add(buttonOffset, entryListBox.Size); - } - } - - private void MainForm_Shown(object sender, EventArgs e) - { - listSizeOffset = Size.Subtract(Size, entryListBox.Size); - buttonOffset = Point.Subtract(createButton.Location, entryListBox.Size); - - shown = true; - } - private SearchForm search = new SearchForm(); private async void appButton_Click(object sender, EventArgs e) { @@ -166,5 +150,83 @@ appButton.Enabled = true; } + + private static bool GetWindowHandle(IntPtr windowHandle, ref object windowHandles) + { + ((ArrayList)windowHandles).Add(windowHandle); + return true; + } + + private void hwndButton_Click(object sender, EventArgs e) + { + hwndButton.Enabled = false; + + object handles = new ArrayList(); + NativeCalls.EnumWindows(GetWindowHandle, ref handles); + + List> titles = new List>(); + foreach (object ptrObj in (ArrayList)handles) + { + IntPtr ptr = (IntPtr)ptrObj; + if (NativeCalls.IsAltTabWindow(ptr)) + { + string title = NativeCalls.GetWindowTextManaged(ptr); + if (title != "") + { + /* + IPropertyStore store; + Guid guid = NativeValues.IID_IPropertyStore; + NativeCalls.VerifySucceeded((uint)NativeCalls.SHGetPropertyStoreForWindow(ptr, ref guid, out store)); + + using (PropVariant pv = new PropVariant()) + { + NativeCalls.VerifySucceeded(store.GetValue(NativeValues.AppUserModelIDKey, pv)); + + string appUserModelId = pv.Value; + if (appUserModelId == null) + { + uint processId; + NativeCalls.GetWindowThreadProcessId(ptr, out processId); + + IntPtr handle = NativeCalls.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, (int)processId); + + try + { + appUserModelId = NativeCalls.GetApplicationUserModelIdManaged(handle); + } + catch (Exception) { } + } + + + } + */ + + string appUserModelId = appResolver.GetAppIDForWindow(ptr); + if (appUserModelId == null) + { + uint processId; + NativeCalls.GetWindowThreadProcessId(ptr, out processId); + + appUserModelId = appResolver.GetAppIDForProcess(processId); + } + + if (appUserModelId != null) + { + titles.Add(new Tuple(ptr, title, appUserModelId == null ? "NULL" : appUserModelId)); + } + } + + } + } + + string selectedAppUserModelId = search.GetSelection("Select Window", "Available Windows:", titles.Select(t => new Tuple(t.Item3, t.Item2 + ": " + t.Item3)).ToList()); + + if (selectedAppUserModelId != null) + { + appUserModelIdBox.Text = selectedAppUserModelId; + } + + hwndButton.Enabled = true; + } } } diff --git a/ShortcutUtil/Program.cs b/ShortcutUtil/Program.cs index 9b6f955..a46ce5b 100755 --- a/ShortcutUtil/Program.cs +++ b/ShortcutUtil/Program.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; +using NativeHelpers; +using System.Runtime.InteropServices; namespace ShortcutUtil { @@ -16,6 +18,8 @@ [STAThread] static void Main() { + AppResolver appResolver = new AppResolver(); + string[] args = Environment.GetCommandLineArgs(); if (args.Length < 2) @@ -46,7 +50,7 @@ Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm(entryList)); + Application.Run(new MainForm(entryList, appResolver)); } } } diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/SetLnkApp/MainForm.cs b/SetLnkApp/MainForm.cs index bfdb731..3c98b40 100755 --- a/SetLnkApp/MainForm.cs +++ b/SetLnkApp/MainForm.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs index 023999d..d32eac1 100755 --- a/SetLnkApp/Program.cs +++ b/SetLnkApp/Program.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.IO; @@ -29,7 +29,7 @@ { shortcut = new ShellLink(args[1]); } - catch (Exception e) + catch (Exception) { Usage(); return; diff --git a/SetLnkApp/SetLnkApp.csproj b/SetLnkApp/SetLnkApp.csproj index ec4d625..379cd46 100755 --- a/SetLnkApp/SetLnkApp.csproj +++ b/SetLnkApp/SetLnkApp.csproj @@ -32,6 +32,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + @@ -46,6 +68,9 @@ + + NativeHelpers.cs + ShellLink.cs diff --git a/ShellLink.cs b/ShellLink.cs index 6d13f4e..ba4d16d 100755 --- a/ShellLink.cs +++ b/ShellLink.cs @@ -1,15 +1,15 @@ using System; using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; + using System.Text; -using ComTypes = System.Runtime.InteropServices.ComTypes; +using System.Runtime.InteropServices.ComTypes; /**************************************************************************** * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ ***************************************************************************/ -namespace ShellLinkPlus +namespace NativeHelpers { // Modified from http://smdn.jp/programming/tips/createlnk/ // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects @@ -17,236 +17,10 @@ // Partly based on Sending toast notifications from desktop apps sample public class ShellLink : IDisposable { - #region Win32 and COM - - // IShellLink Interface - [ComImport, - InterfaceType(ComInterfaceType.InterfaceIsIUnknown), - Guid("000214F9-0000-0000-C000-000000000046")] - private 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")] - private class CShellLink { } - - // WIN32_FIND_DATAW Structure - [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] - private 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 = 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")] - private 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)] - private 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)] - private 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() - { - PropVariantClear(this); - GC.SuppressFinalize(this); - } - - #endregion - } - - [DllImport("Ole32.dll", PreserveSig = false)] - private extern static void PropVariantClear([In, Out] PropVariant pvar); - - #endregion - #region Fields private IShellLinkW shellLinkW = null; - // Name = System.AppUserModel.ID - // ShellPKey = PKEY_AppUserModel_ID - // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 - // PropID = 5 - // Type = String (VT_LPWSTR) - private readonly PropertyKey AppUserModelIDKey = - new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); - - private const int MAX_PATH = 260; - private const int INFOTIPSIZE = 1024; - - private const int STGM_READ = 0x00000000; // STGM constants - private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags - #endregion #region Private Properties (Interfaces) @@ -300,18 +74,18 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder targetPath = new StringBuilder(MAX_PATH); + StringBuilder targetPath = new StringBuilder(NativeValues.MAX_PATH); WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); - VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, - SLGP_UNCPRIORITY)); + NativeCalls.VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + NativeValues.SLGP_UNCPRIORITY)); return targetPath.ToString(); } set { - VerifySucceeded(shellLinkW.SetPath(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetPath(value)); } } @@ -320,15 +94,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + StringBuilder arguments = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); return arguments.ToString(); } set { - VerifySucceeded(shellLinkW.SetArguments(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetArguments(value)); } } @@ -337,15 +111,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder description = new StringBuilder(INFOTIPSIZE); + StringBuilder description = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); return description.ToString(); } set { - VerifySucceeded(shellLinkW.SetDescription(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetDescription(value)); } } @@ -354,16 +128,16 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder icon = new StringBuilder(INFOTIPSIZE); + StringBuilder icon = new StringBuilder(NativeValues.INFOTIPSIZE); int index; - VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + NativeCalls.VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); return new Tuple(icon.ToString(), index); } set { - VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + NativeCalls.VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); } } @@ -374,7 +148,7 @@ { using (PropVariant pv = new PropVariant()) { - VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.GetValue(NativeValues.AppUserModelIDKey, pv)); if (pv.Value == null) return "Null"; @@ -386,8 +160,8 @@ { using (PropVariant pv = new PropVariant(value)) { - VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); - VerifySucceeded(PropertyStore.Commit()); + NativeCalls.VerifySucceeded(PropertyStore.SetValue(NativeValues.AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.Commit()); } } } @@ -470,15 +244,7 @@ if (!File.Exists(file)) throw new FileNotFoundException("File is not found.", file); else - PersistFile.Load(file, STGM_READ); - } - - // Verify if operation succeeded. - public static void VerifySucceeded(uint hresult) - { - if (hresult > 1) - throw new InvalidOperationException("Failed with HRESULT: " + - hresult.ToString("X")); + PersistFile.Load(file, NativeValues.STGM_READWRITE); } #endregion diff --git a/ShortcutUtil/AppResolver.cs b/ShortcutUtil/AppResolver.cs new file mode 100755 index 0000000..6daf0b5 --- /dev/null +++ b/ShortcutUtil/AppResolver.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Windows.System.Update; + +namespace NativeHelpers +{ + // CLSID_StartMenuCacheAndAppResolver {660b90c8-73a9-4b58-8cae-355b7f55341b} + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("660b90c8-73a9-4b58-8cae-355b7f55341b")] + public class CStartMenuCacheAndAppResolver { } + + public class AppResolver + { + IAppResolver_7 app7 = null; + IAppResolver_8 app8 = null; + public AppResolver() + { + Exception last = null; + try + { + app8 = (IAppResolver_8)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app8 = null; + } + + if (app8 == null) + { + try + { + app7 = (IAppResolver_7)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app7 = null; + } + } + + if (app7 == null && app8 == null) + { + throw last; + } + } + + public string GetAppIDForWindow(IntPtr hWnd) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + + public string GetAppIDForProcess(UInt32 processId) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + } + + // IID_IAppResolver_7 {46a6eeff-908e-4dc6-92a6-64be9177b41c} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("46a6eeff-908e-4dc6-92a6-64be9177b41c")] + public interface IAppResolver_7 + { + uint GetAppIDForShortcut(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } + + // IID_IAppResolver_8 {de25675a-72de-44b4-9373-05170450c140} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("de25675a-72de-44b4-9373-05170450c140")] + public interface IAppResolver_8 + { + uint GetAppIDForShortcut(); + uint GetAppIDForShortcutObject(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } +} diff --git a/ShortcutUtil/MainForm.Designer.cs b/ShortcutUtil/MainForm.Designer.cs index 5dbf023..8af74c7 100755 --- a/ShortcutUtil/MainForm.Designer.cs +++ b/ShortcutUtil/MainForm.Designer.cs @@ -48,6 +48,9 @@ // // entryListBox // + this.entryListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.entryListBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable; this.entryListBox.FormattingEnabled = true; this.entryListBox.Location = new System.Drawing.Point(16, 98); @@ -59,6 +62,7 @@ // // createButton // + this.createButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.createButton.Location = new System.Drawing.Point(644, 458); this.createButton.Name = "createButton"; this.createButton.Size = new System.Drawing.Size(144, 40); @@ -77,6 +81,8 @@ // // appUserModelIdBox // + this.appUserModelIdBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.appUserModelIdBox.Location = new System.Drawing.Point(16, 35); this.appUserModelIdBox.Name = "appUserModelIdBox"; this.appUserModelIdBox.Size = new System.Drawing.Size(608, 26); @@ -86,6 +92,7 @@ // // appButton // + this.appButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.appButton.Location = new System.Drawing.Point(633, 35); this.appButton.Name = "appButton"; this.appButton.Size = new System.Drawing.Size(74, 31); @@ -96,6 +103,7 @@ // // hwndButton // + this.hwndButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.hwndButton.Location = new System.Drawing.Point(712, 35); this.hwndButton.MinimumSize = new System.Drawing.Size(76, 26); this.hwndButton.Name = "hwndButton"; @@ -103,6 +111,7 @@ this.hwndButton.TabIndex = 6; this.hwndButton.Text = "HWND"; this.hwndButton.UseVisualStyleBackColor = true; + this.hwndButton.Click += new System.EventHandler(this.hwndButton_Click); // // MainForm // @@ -119,8 +128,6 @@ this.Controls.Add(this.defaultShortcutLabel); this.Name = "MainForm"; this.Text = "Form1"; - this.Shown += new System.EventHandler(this.MainForm_Shown); - this.SizeChanged += new System.EventHandler(this.MainForm_SizeChanged); this.ResumeLayout(false); this.PerformLayout(); diff --git a/ShortcutUtil/MainForm.cs b/ShortcutUtil/MainForm.cs index 680ed45..c654b19 100755 --- a/ShortcutUtil/MainForm.cs +++ b/ShortcutUtil/MainForm.cs @@ -1,5 +1,7 @@ using Entries; +using NativeHelpers; using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -23,11 +25,9 @@ private Font nameFont; private Brush fontBrush; - private bool shown = false; - private Size listSizeOffset; - private Point buttonOffset; + private AppResolver appResolver; - public MainForm(EntryList entryList) + public MainForm(EntryList entryList, AppResolver appResolver) { InitializeComponent(); @@ -38,6 +38,7 @@ fontBrush = new SolidBrush(defaultShortcutLabel.ForeColor); this.entryList = entryList; + this.appResolver = appResolver; entryListBox.Items.AddRange(this.entryList.Entries.ToArray()); @@ -122,23 +123,6 @@ e.ItemHeight = Size.Add(IconSize, new Size(0, ItemPadding.Height * 2)).Height; } - private void MainForm_SizeChanged(object sender, EventArgs e) - { - if (shown) - { - entryListBox.Size = Size.Subtract(Size, listSizeOffset); - createButton.Location = Point.Add(buttonOffset, entryListBox.Size); - } - } - - private void MainForm_Shown(object sender, EventArgs e) - { - listSizeOffset = Size.Subtract(Size, entryListBox.Size); - buttonOffset = Point.Subtract(createButton.Location, entryListBox.Size); - - shown = true; - } - private SearchForm search = new SearchForm(); private async void appButton_Click(object sender, EventArgs e) { @@ -166,5 +150,83 @@ appButton.Enabled = true; } + + private static bool GetWindowHandle(IntPtr windowHandle, ref object windowHandles) + { + ((ArrayList)windowHandles).Add(windowHandle); + return true; + } + + private void hwndButton_Click(object sender, EventArgs e) + { + hwndButton.Enabled = false; + + object handles = new ArrayList(); + NativeCalls.EnumWindows(GetWindowHandle, ref handles); + + List> titles = new List>(); + foreach (object ptrObj in (ArrayList)handles) + { + IntPtr ptr = (IntPtr)ptrObj; + if (NativeCalls.IsAltTabWindow(ptr)) + { + string title = NativeCalls.GetWindowTextManaged(ptr); + if (title != "") + { + /* + IPropertyStore store; + Guid guid = NativeValues.IID_IPropertyStore; + NativeCalls.VerifySucceeded((uint)NativeCalls.SHGetPropertyStoreForWindow(ptr, ref guid, out store)); + + using (PropVariant pv = new PropVariant()) + { + NativeCalls.VerifySucceeded(store.GetValue(NativeValues.AppUserModelIDKey, pv)); + + string appUserModelId = pv.Value; + if (appUserModelId == null) + { + uint processId; + NativeCalls.GetWindowThreadProcessId(ptr, out processId); + + IntPtr handle = NativeCalls.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, (int)processId); + + try + { + appUserModelId = NativeCalls.GetApplicationUserModelIdManaged(handle); + } + catch (Exception) { } + } + + + } + */ + + string appUserModelId = appResolver.GetAppIDForWindow(ptr); + if (appUserModelId == null) + { + uint processId; + NativeCalls.GetWindowThreadProcessId(ptr, out processId); + + appUserModelId = appResolver.GetAppIDForProcess(processId); + } + + if (appUserModelId != null) + { + titles.Add(new Tuple(ptr, title, appUserModelId == null ? "NULL" : appUserModelId)); + } + } + + } + } + + string selectedAppUserModelId = search.GetSelection("Select Window", "Available Windows:", titles.Select(t => new Tuple(t.Item3, t.Item2 + ": " + t.Item3)).ToList()); + + if (selectedAppUserModelId != null) + { + appUserModelIdBox.Text = selectedAppUserModelId; + } + + hwndButton.Enabled = true; + } } } diff --git a/ShortcutUtil/Program.cs b/ShortcutUtil/Program.cs index 9b6f955..a46ce5b 100755 --- a/ShortcutUtil/Program.cs +++ b/ShortcutUtil/Program.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; +using NativeHelpers; +using System.Runtime.InteropServices; namespace ShortcutUtil { @@ -16,6 +18,8 @@ [STAThread] static void Main() { + AppResolver appResolver = new AppResolver(); + string[] args = Environment.GetCommandLineArgs(); if (args.Length < 2) @@ -46,7 +50,7 @@ Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm(entryList)); + Application.Run(new MainForm(entryList, appResolver)); } } } diff --git a/ShortcutUtil/SearchForm.Designer.cs b/ShortcutUtil/SearchForm.Designer.cs index 93f58bf..350f243 100755 --- a/ShortcutUtil/SearchForm.Designer.cs +++ b/ShortcutUtil/SearchForm.Designer.cs @@ -55,6 +55,9 @@ // // optionsList // + this.optionsList.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.optionsList.FormattingEnabled = true; this.optionsList.ItemHeight = 20; this.optionsList.Location = new System.Drawing.Point(17, 104); @@ -64,6 +67,7 @@ // // selectButton // + this.selectButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.selectButton.DialogResult = System.Windows.Forms.DialogResult.OK; this.selectButton.Location = new System.Drawing.Point(661, 414); this.selectButton.Name = "selectButton"; @@ -74,6 +78,8 @@ // // searchBox // + this.searchBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.searchBox.Location = new System.Drawing.Point(17, 37); this.searchBox.Name = "searchBox"; this.searchBox.Size = new System.Drawing.Size(771, 26); diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/SetLnkApp/MainForm.cs b/SetLnkApp/MainForm.cs index bfdb731..3c98b40 100755 --- a/SetLnkApp/MainForm.cs +++ b/SetLnkApp/MainForm.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs index 023999d..d32eac1 100755 --- a/SetLnkApp/Program.cs +++ b/SetLnkApp/Program.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.IO; @@ -29,7 +29,7 @@ { shortcut = new ShellLink(args[1]); } - catch (Exception e) + catch (Exception) { Usage(); return; diff --git a/SetLnkApp/SetLnkApp.csproj b/SetLnkApp/SetLnkApp.csproj index ec4d625..379cd46 100755 --- a/SetLnkApp/SetLnkApp.csproj +++ b/SetLnkApp/SetLnkApp.csproj @@ -32,6 +32,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + @@ -46,6 +68,9 @@ + + NativeHelpers.cs + ShellLink.cs diff --git a/ShellLink.cs b/ShellLink.cs index 6d13f4e..ba4d16d 100755 --- a/ShellLink.cs +++ b/ShellLink.cs @@ -1,15 +1,15 @@ using System; using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; + using System.Text; -using ComTypes = System.Runtime.InteropServices.ComTypes; +using System.Runtime.InteropServices.ComTypes; /**************************************************************************** * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ ***************************************************************************/ -namespace ShellLinkPlus +namespace NativeHelpers { // Modified from http://smdn.jp/programming/tips/createlnk/ // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects @@ -17,236 +17,10 @@ // Partly based on Sending toast notifications from desktop apps sample public class ShellLink : IDisposable { - #region Win32 and COM - - // IShellLink Interface - [ComImport, - InterfaceType(ComInterfaceType.InterfaceIsIUnknown), - Guid("000214F9-0000-0000-C000-000000000046")] - private 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")] - private class CShellLink { } - - // WIN32_FIND_DATAW Structure - [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] - private 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 = 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")] - private 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)] - private 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)] - private 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() - { - PropVariantClear(this); - GC.SuppressFinalize(this); - } - - #endregion - } - - [DllImport("Ole32.dll", PreserveSig = false)] - private extern static void PropVariantClear([In, Out] PropVariant pvar); - - #endregion - #region Fields private IShellLinkW shellLinkW = null; - // Name = System.AppUserModel.ID - // ShellPKey = PKEY_AppUserModel_ID - // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 - // PropID = 5 - // Type = String (VT_LPWSTR) - private readonly PropertyKey AppUserModelIDKey = - new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); - - private const int MAX_PATH = 260; - private const int INFOTIPSIZE = 1024; - - private const int STGM_READ = 0x00000000; // STGM constants - private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags - #endregion #region Private Properties (Interfaces) @@ -300,18 +74,18 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder targetPath = new StringBuilder(MAX_PATH); + StringBuilder targetPath = new StringBuilder(NativeValues.MAX_PATH); WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); - VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, - SLGP_UNCPRIORITY)); + NativeCalls.VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + NativeValues.SLGP_UNCPRIORITY)); return targetPath.ToString(); } set { - VerifySucceeded(shellLinkW.SetPath(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetPath(value)); } } @@ -320,15 +94,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + StringBuilder arguments = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); return arguments.ToString(); } set { - VerifySucceeded(shellLinkW.SetArguments(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetArguments(value)); } } @@ -337,15 +111,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder description = new StringBuilder(INFOTIPSIZE); + StringBuilder description = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); return description.ToString(); } set { - VerifySucceeded(shellLinkW.SetDescription(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetDescription(value)); } } @@ -354,16 +128,16 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder icon = new StringBuilder(INFOTIPSIZE); + StringBuilder icon = new StringBuilder(NativeValues.INFOTIPSIZE); int index; - VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + NativeCalls.VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); return new Tuple(icon.ToString(), index); } set { - VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + NativeCalls.VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); } } @@ -374,7 +148,7 @@ { using (PropVariant pv = new PropVariant()) { - VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.GetValue(NativeValues.AppUserModelIDKey, pv)); if (pv.Value == null) return "Null"; @@ -386,8 +160,8 @@ { using (PropVariant pv = new PropVariant(value)) { - VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); - VerifySucceeded(PropertyStore.Commit()); + NativeCalls.VerifySucceeded(PropertyStore.SetValue(NativeValues.AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.Commit()); } } } @@ -470,15 +244,7 @@ if (!File.Exists(file)) throw new FileNotFoundException("File is not found.", file); else - PersistFile.Load(file, STGM_READ); - } - - // Verify if operation succeeded. - public static void VerifySucceeded(uint hresult) - { - if (hresult > 1) - throw new InvalidOperationException("Failed with HRESULT: " + - hresult.ToString("X")); + PersistFile.Load(file, NativeValues.STGM_READWRITE); } #endregion diff --git a/ShortcutUtil/AppResolver.cs b/ShortcutUtil/AppResolver.cs new file mode 100755 index 0000000..6daf0b5 --- /dev/null +++ b/ShortcutUtil/AppResolver.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Windows.System.Update; + +namespace NativeHelpers +{ + // CLSID_StartMenuCacheAndAppResolver {660b90c8-73a9-4b58-8cae-355b7f55341b} + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("660b90c8-73a9-4b58-8cae-355b7f55341b")] + public class CStartMenuCacheAndAppResolver { } + + public class AppResolver + { + IAppResolver_7 app7 = null; + IAppResolver_8 app8 = null; + public AppResolver() + { + Exception last = null; + try + { + app8 = (IAppResolver_8)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app8 = null; + } + + if (app8 == null) + { + try + { + app7 = (IAppResolver_7)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app7 = null; + } + } + + if (app7 == null && app8 == null) + { + throw last; + } + } + + public string GetAppIDForWindow(IntPtr hWnd) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + + public string GetAppIDForProcess(UInt32 processId) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + } + + // IID_IAppResolver_7 {46a6eeff-908e-4dc6-92a6-64be9177b41c} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("46a6eeff-908e-4dc6-92a6-64be9177b41c")] + public interface IAppResolver_7 + { + uint GetAppIDForShortcut(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } + + // IID_IAppResolver_8 {de25675a-72de-44b4-9373-05170450c140} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("de25675a-72de-44b4-9373-05170450c140")] + public interface IAppResolver_8 + { + uint GetAppIDForShortcut(); + uint GetAppIDForShortcutObject(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } +} diff --git a/ShortcutUtil/MainForm.Designer.cs b/ShortcutUtil/MainForm.Designer.cs index 5dbf023..8af74c7 100755 --- a/ShortcutUtil/MainForm.Designer.cs +++ b/ShortcutUtil/MainForm.Designer.cs @@ -48,6 +48,9 @@ // // entryListBox // + this.entryListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.entryListBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable; this.entryListBox.FormattingEnabled = true; this.entryListBox.Location = new System.Drawing.Point(16, 98); @@ -59,6 +62,7 @@ // // createButton // + this.createButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.createButton.Location = new System.Drawing.Point(644, 458); this.createButton.Name = "createButton"; this.createButton.Size = new System.Drawing.Size(144, 40); @@ -77,6 +81,8 @@ // // appUserModelIdBox // + this.appUserModelIdBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.appUserModelIdBox.Location = new System.Drawing.Point(16, 35); this.appUserModelIdBox.Name = "appUserModelIdBox"; this.appUserModelIdBox.Size = new System.Drawing.Size(608, 26); @@ -86,6 +92,7 @@ // // appButton // + this.appButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.appButton.Location = new System.Drawing.Point(633, 35); this.appButton.Name = "appButton"; this.appButton.Size = new System.Drawing.Size(74, 31); @@ -96,6 +103,7 @@ // // hwndButton // + this.hwndButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.hwndButton.Location = new System.Drawing.Point(712, 35); this.hwndButton.MinimumSize = new System.Drawing.Size(76, 26); this.hwndButton.Name = "hwndButton"; @@ -103,6 +111,7 @@ this.hwndButton.TabIndex = 6; this.hwndButton.Text = "HWND"; this.hwndButton.UseVisualStyleBackColor = true; + this.hwndButton.Click += new System.EventHandler(this.hwndButton_Click); // // MainForm // @@ -119,8 +128,6 @@ this.Controls.Add(this.defaultShortcutLabel); this.Name = "MainForm"; this.Text = "Form1"; - this.Shown += new System.EventHandler(this.MainForm_Shown); - this.SizeChanged += new System.EventHandler(this.MainForm_SizeChanged); this.ResumeLayout(false); this.PerformLayout(); diff --git a/ShortcutUtil/MainForm.cs b/ShortcutUtil/MainForm.cs index 680ed45..c654b19 100755 --- a/ShortcutUtil/MainForm.cs +++ b/ShortcutUtil/MainForm.cs @@ -1,5 +1,7 @@ using Entries; +using NativeHelpers; using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -23,11 +25,9 @@ private Font nameFont; private Brush fontBrush; - private bool shown = false; - private Size listSizeOffset; - private Point buttonOffset; + private AppResolver appResolver; - public MainForm(EntryList entryList) + public MainForm(EntryList entryList, AppResolver appResolver) { InitializeComponent(); @@ -38,6 +38,7 @@ fontBrush = new SolidBrush(defaultShortcutLabel.ForeColor); this.entryList = entryList; + this.appResolver = appResolver; entryListBox.Items.AddRange(this.entryList.Entries.ToArray()); @@ -122,23 +123,6 @@ e.ItemHeight = Size.Add(IconSize, new Size(0, ItemPadding.Height * 2)).Height; } - private void MainForm_SizeChanged(object sender, EventArgs e) - { - if (shown) - { - entryListBox.Size = Size.Subtract(Size, listSizeOffset); - createButton.Location = Point.Add(buttonOffset, entryListBox.Size); - } - } - - private void MainForm_Shown(object sender, EventArgs e) - { - listSizeOffset = Size.Subtract(Size, entryListBox.Size); - buttonOffset = Point.Subtract(createButton.Location, entryListBox.Size); - - shown = true; - } - private SearchForm search = new SearchForm(); private async void appButton_Click(object sender, EventArgs e) { @@ -166,5 +150,83 @@ appButton.Enabled = true; } + + private static bool GetWindowHandle(IntPtr windowHandle, ref object windowHandles) + { + ((ArrayList)windowHandles).Add(windowHandle); + return true; + } + + private void hwndButton_Click(object sender, EventArgs e) + { + hwndButton.Enabled = false; + + object handles = new ArrayList(); + NativeCalls.EnumWindows(GetWindowHandle, ref handles); + + List> titles = new List>(); + foreach (object ptrObj in (ArrayList)handles) + { + IntPtr ptr = (IntPtr)ptrObj; + if (NativeCalls.IsAltTabWindow(ptr)) + { + string title = NativeCalls.GetWindowTextManaged(ptr); + if (title != "") + { + /* + IPropertyStore store; + Guid guid = NativeValues.IID_IPropertyStore; + NativeCalls.VerifySucceeded((uint)NativeCalls.SHGetPropertyStoreForWindow(ptr, ref guid, out store)); + + using (PropVariant pv = new PropVariant()) + { + NativeCalls.VerifySucceeded(store.GetValue(NativeValues.AppUserModelIDKey, pv)); + + string appUserModelId = pv.Value; + if (appUserModelId == null) + { + uint processId; + NativeCalls.GetWindowThreadProcessId(ptr, out processId); + + IntPtr handle = NativeCalls.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, (int)processId); + + try + { + appUserModelId = NativeCalls.GetApplicationUserModelIdManaged(handle); + } + catch (Exception) { } + } + + + } + */ + + string appUserModelId = appResolver.GetAppIDForWindow(ptr); + if (appUserModelId == null) + { + uint processId; + NativeCalls.GetWindowThreadProcessId(ptr, out processId); + + appUserModelId = appResolver.GetAppIDForProcess(processId); + } + + if (appUserModelId != null) + { + titles.Add(new Tuple(ptr, title, appUserModelId == null ? "NULL" : appUserModelId)); + } + } + + } + } + + string selectedAppUserModelId = search.GetSelection("Select Window", "Available Windows:", titles.Select(t => new Tuple(t.Item3, t.Item2 + ": " + t.Item3)).ToList()); + + if (selectedAppUserModelId != null) + { + appUserModelIdBox.Text = selectedAppUserModelId; + } + + hwndButton.Enabled = true; + } } } diff --git a/ShortcutUtil/Program.cs b/ShortcutUtil/Program.cs index 9b6f955..a46ce5b 100755 --- a/ShortcutUtil/Program.cs +++ b/ShortcutUtil/Program.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; +using NativeHelpers; +using System.Runtime.InteropServices; namespace ShortcutUtil { @@ -16,6 +18,8 @@ [STAThread] static void Main() { + AppResolver appResolver = new AppResolver(); + string[] args = Environment.GetCommandLineArgs(); if (args.Length < 2) @@ -46,7 +50,7 @@ Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm(entryList)); + Application.Run(new MainForm(entryList, appResolver)); } } } diff --git a/ShortcutUtil/SearchForm.Designer.cs b/ShortcutUtil/SearchForm.Designer.cs index 93f58bf..350f243 100755 --- a/ShortcutUtil/SearchForm.Designer.cs +++ b/ShortcutUtil/SearchForm.Designer.cs @@ -55,6 +55,9 @@ // // optionsList // + this.optionsList.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.optionsList.FormattingEnabled = true; this.optionsList.ItemHeight = 20; this.optionsList.Location = new System.Drawing.Point(17, 104); @@ -64,6 +67,7 @@ // // selectButton // + this.selectButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.selectButton.DialogResult = System.Windows.Forms.DialogResult.OK; this.selectButton.Location = new System.Drawing.Point(661, 414); this.selectButton.Name = "selectButton"; @@ -74,6 +78,8 @@ // // searchBox // + this.searchBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.searchBox.Location = new System.Drawing.Point(17, 37); this.searchBox.Name = "searchBox"; this.searchBox.Size = new System.Drawing.Size(771, 26); diff --git a/ShortcutUtil/ShortcutUtil.csproj b/ShortcutUtil/ShortcutUtil.csproj index 975a545..3428eaa 100755 --- a/ShortcutUtil/ShortcutUtil.csproj +++ b/ShortcutUtil/ShortcutUtil.csproj @@ -35,6 +35,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll @@ -90,6 +112,10 @@ EntryList.cs + + NativeHelpers.cs + + Form @@ -105,6 +131,7 @@ SearchForm.cs + Component diff --git a/JumpListUtil.sln b/JumpListUtil.sln index 165a98a..1c5678b 100755 --- a/JumpListUtil.sln +++ b/JumpListUtil.sln @@ -12,21 +12,35 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.ActiveCfg = Debug|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Debug|x86.Build.0 = Debug|x86 {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.ActiveCfg = Release|x86 + {66F50737-A71D-4D5B-83F2-4F06B9F9F9B2}.Release|x86.Build.0 = Release|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.ActiveCfg = Debug|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Debug|x86.Build.0 = Debug|x86 {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.ActiveCfg = Release|Any CPU {443B8959-7F3D-4199-838C-7A805427CE42}.Release|Any CPU.Build.0 = Release|Any CPU + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.ActiveCfg = Release|x86 + {443B8959-7F3D-4199-838C-7A805427CE42}.Release|x86.Build.0 = Release|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.ActiveCfg = Debug|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Debug|x86.Build.0 = Debug|x86 {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|Any CPU.Build.0 = Release|Any CPU + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.ActiveCfg = Release|x86 + {91BAC03D-BBBB-403C-A8B0-2F70497F39BA}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/JumpListUtil/JumpListUtil.csproj b/JumpListUtil/JumpListUtil.csproj index 052af6c..378d940 100755 --- a/JumpListUtil/JumpListUtil.csproj +++ b/JumpListUtil/JumpListUtil.csproj @@ -47,6 +47,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -93,6 +115,9 @@ EntryList.cs + + NativeHelpers.cs + ShellLink.cs diff --git a/JumpListUtil/Program.cs b/JumpListUtil/Program.cs index 6b8192c..2c1c3ff 100755 --- a/JumpListUtil/Program.cs +++ b/JumpListUtil/Program.cs @@ -8,15 +8,13 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Shell; +using NativeHelpers; namespace JumpListUtil { static class Program - { - [DllImport("shell32.dll", SetLastError = true)] - static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); - + { /// /// The main entry point for the application. /// @@ -97,7 +95,7 @@ } } - SetCurrentProcessExplicitAppUserModelID(appUserModelId); + NativeCalls.SetCurrentProcessExplicitAppUserModelID(appUserModelId); List jumpItems = new List(); diff --git a/NativeHelpers.cs b/NativeHelpers.cs new file mode 100755 index 0000000..154d692 --- /dev/null +++ b/NativeHelpers.cs @@ -0,0 +1,609 @@ +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 + { + /// + /// Retrieves the parent window. This does not include the owner, as it does with the GetParent function. + /// + GetParent = 1, + /// + /// Retrieves the root window by walking the chain of parent windows. + /// + GetRoot = 2, + /// + /// Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent. + /// + 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 + { + /// Sets a new address for the window procedure. + /// You cannot change this attribute if the window does not belong to the same process as the calling thread. + GWL_WNDPROC = -4, + + /// Sets a new application instance handle. + GWLP_HINSTANCE = -6, + + GWLP_HWNDPARENT = -8, + + /// Sets a new identifier of the child window. + /// The window cannot be a top-level window. + GWL_ID = -12, + + /// Sets a new window style. + GWL_STYLE = -16, + + /// Sets a new extended window style. + /// See . + GWL_EXSTYLE = -20, + + /// Sets the user data associated with the window. + /// This data is intended for use by the application that created the window. Its value is initially zero. + GWL_USERDATA = -21, + + /// Sets the return value of a message processed in the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_MSGRESULT = 0, + + /// Sets new extra information that is private to the application, such as handles or pointers. + /// Only applies to dialog boxes. + DWLP_USER = 8, + + /// Sets the new address of the dialog box procedure. + /// Only applies to dialog boxes. + DWLP_DLGPROC = 4 + } + + [Flags] + public enum WindowStylesEx : uint + { + /// Specifies a window that accepts drag-drop files. + WS_EX_ACCEPTFILES = 0x00000010, + + /// Forces a top-level window onto the taskbar when the window is visible. + WS_EX_APPWINDOW = 0x00040000, + + /// Specifies a window that has a border with a sunken edge. + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 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. + /// + /// + /// 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. + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 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. + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 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. + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// Specifies a window that has a double border. + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 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. + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 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. + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// Specifies a window that has generic left-aligned properties. This is the default. + WS_EX_LEFT = 0x00000000, + + /// + /// 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. + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// Specifies a window that displays text using left-to-right reading-order properties. This is the default. + /// + WS_EX_LTRREADING = 0x00000000, + + /// + /// Specifies a multiple-document interface (MDI) child window. + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 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. + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// Specifies a window which does not pass its window layout to its child windows. + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 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. + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 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. + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// Specifies an overlapped window. + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// Specifies a palette window, which is a modeless dialog box that presents an array of commands. + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 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. + /// + WS_EX_RIGHT = 0x00001000, + + /// Specifies a window with the vertical scroll bar (if present) to the right of the client area. This is the default. + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + /// + /// 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. + /// + WS_EX_RTLREADING = 0x00002000, + + /// Specifies a window with a three-dimensional border style intended to be used for items that do not accept user input. + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 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. + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 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. + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 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. + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// Specifies a window that has a border with a raised edge. + 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(); + } + } +} \ No newline at end of file diff --git a/SetLnkApp/MainForm.cs b/SetLnkApp/MainForm.cs index bfdb731..3c98b40 100755 --- a/SetLnkApp/MainForm.cs +++ b/SetLnkApp/MainForm.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.ComponentModel; diff --git a/SetLnkApp/Program.cs b/SetLnkApp/Program.cs index 023999d..d32eac1 100755 --- a/SetLnkApp/Program.cs +++ b/SetLnkApp/Program.cs @@ -1,4 +1,4 @@ -using ShellLinkPlus; +using NativeHelpers; using System; using System.Collections.Generic; using System.IO; @@ -29,7 +29,7 @@ { shortcut = new ShellLink(args[1]); } - catch (Exception e) + catch (Exception) { Usage(); return; diff --git a/SetLnkApp/SetLnkApp.csproj b/SetLnkApp/SetLnkApp.csproj index ec4d625..379cd46 100755 --- a/SetLnkApp/SetLnkApp.csproj +++ b/SetLnkApp/SetLnkApp.csproj @@ -32,6 +32,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + @@ -46,6 +68,9 @@ + + NativeHelpers.cs + ShellLink.cs diff --git a/ShellLink.cs b/ShellLink.cs index 6d13f4e..ba4d16d 100755 --- a/ShellLink.cs +++ b/ShellLink.cs @@ -1,15 +1,15 @@ using System; using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; + using System.Text; -using ComTypes = System.Runtime.InteropServices.ComTypes; +using System.Runtime.InteropServices.ComTypes; /**************************************************************************** * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/ ***************************************************************************/ -namespace ShellLinkPlus +namespace NativeHelpers { // Modified from http://smdn.jp/programming/tips/createlnk/ // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects @@ -17,236 +17,10 @@ // Partly based on Sending toast notifications from desktop apps sample public class ShellLink : IDisposable { - #region Win32 and COM - - // IShellLink Interface - [ComImport, - InterfaceType(ComInterfaceType.InterfaceIsIUnknown), - Guid("000214F9-0000-0000-C000-000000000046")] - private 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")] - private class CShellLink { } - - // WIN32_FIND_DATAW Structure - [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] - private 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 = 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")] - private 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)] - private 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)] - private 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() - { - PropVariantClear(this); - GC.SuppressFinalize(this); - } - - #endregion - } - - [DllImport("Ole32.dll", PreserveSig = false)] - private extern static void PropVariantClear([In, Out] PropVariant pvar); - - #endregion - #region Fields private IShellLinkW shellLinkW = null; - // Name = System.AppUserModel.ID - // ShellPKey = PKEY_AppUserModel_ID - // FormatID = 9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3 - // PropID = 5 - // Type = String (VT_LPWSTR) - private readonly PropertyKey AppUserModelIDKey = - new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5); - - private const int MAX_PATH = 260; - private const int INFOTIPSIZE = 1024; - - private const int STGM_READ = 0x00000000; // STGM constants - private const uint SLGP_UNCPRIORITY = 0x0002; // SLGP flags - #endregion #region Private Properties (Interfaces) @@ -300,18 +74,18 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder targetPath = new StringBuilder(MAX_PATH); + StringBuilder targetPath = new StringBuilder(NativeValues.MAX_PATH); WIN32_FIND_DATAW data = new WIN32_FIND_DATAW(); - VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, - SLGP_UNCPRIORITY)); + NativeCalls.VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data, + NativeValues.SLGP_UNCPRIORITY)); return targetPath.ToString(); } set { - VerifySucceeded(shellLinkW.SetPath(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetPath(value)); } } @@ -320,15 +94,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder arguments = new StringBuilder(INFOTIPSIZE); + StringBuilder arguments = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity)); return arguments.ToString(); } set { - VerifySucceeded(shellLinkW.SetArguments(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetArguments(value)); } } @@ -337,15 +111,15 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder description = new StringBuilder(INFOTIPSIZE); + StringBuilder description = new StringBuilder(NativeValues.INFOTIPSIZE); - VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); + NativeCalls.VerifySucceeded(shellLinkW.GetArguments(description, description.Capacity)); return description.ToString(); } set { - VerifySucceeded(shellLinkW.SetDescription(value)); + NativeCalls.VerifySucceeded(shellLinkW.SetDescription(value)); } } @@ -354,16 +128,16 @@ get { // No limitation to length of buffer string in the case of Unicode though. - StringBuilder icon = new StringBuilder(INFOTIPSIZE); + StringBuilder icon = new StringBuilder(NativeValues.INFOTIPSIZE); int index; - VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); + NativeCalls.VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index)); return new Tuple(icon.ToString(), index); } set { - VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); + NativeCalls.VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2)); } } @@ -374,7 +148,7 @@ { using (PropVariant pv = new PropVariant()) { - VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.GetValue(NativeValues.AppUserModelIDKey, pv)); if (pv.Value == null) return "Null"; @@ -386,8 +160,8 @@ { using (PropVariant pv = new PropVariant(value)) { - VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv)); - VerifySucceeded(PropertyStore.Commit()); + NativeCalls.VerifySucceeded(PropertyStore.SetValue(NativeValues.AppUserModelIDKey, pv)); + NativeCalls.VerifySucceeded(PropertyStore.Commit()); } } } @@ -470,15 +244,7 @@ if (!File.Exists(file)) throw new FileNotFoundException("File is not found.", file); else - PersistFile.Load(file, STGM_READ); - } - - // Verify if operation succeeded. - public static void VerifySucceeded(uint hresult) - { - if (hresult > 1) - throw new InvalidOperationException("Failed with HRESULT: " + - hresult.ToString("X")); + PersistFile.Load(file, NativeValues.STGM_READWRITE); } #endregion diff --git a/ShortcutUtil/AppResolver.cs b/ShortcutUtil/AppResolver.cs new file mode 100755 index 0000000..6daf0b5 --- /dev/null +++ b/ShortcutUtil/AppResolver.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Windows.System.Update; + +namespace NativeHelpers +{ + // CLSID_StartMenuCacheAndAppResolver {660b90c8-73a9-4b58-8cae-355b7f55341b} + [ComImport, + ClassInterface(ClassInterfaceType.None), + Guid("660b90c8-73a9-4b58-8cae-355b7f55341b")] + public class CStartMenuCacheAndAppResolver { } + + public class AppResolver + { + IAppResolver_7 app7 = null; + IAppResolver_8 app8 = null; + public AppResolver() + { + Exception last = null; + try + { + app8 = (IAppResolver_8)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app8 = null; + } + + if (app8 == null) + { + try + { + app7 = (IAppResolver_7)new CStartMenuCacheAndAppResolver(); + } + catch (Exception e) + { + last = e; + app7 = null; + } + } + + if (app7 == null && app8 == null) + { + throw last; + } + } + + public string GetAppIDForWindow(IntPtr hWnd) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForWindow(hWnd, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + + public string GetAppIDForProcess(UInt32 processId) + { + IntPtr output = IntPtr.Zero; + if (app7 != null) + { + app7.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + else if (app8 != null) + { + app8.GetAppIDForProcess(processId, ref output, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + if (output != IntPtr.Zero) + { + return Marshal.PtrToStringUni(output); + } + return null; + } + } + + // IID_IAppResolver_7 {46a6eeff-908e-4dc6-92a6-64be9177b41c} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("46a6eeff-908e-4dc6-92a6-64be9177b41c")] + public interface IAppResolver_7 + { + uint GetAppIDForShortcut(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } + + // IID_IAppResolver_8 {de25675a-72de-44b4-9373-05170450c140} + [ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("de25675a-72de-44b4-9373-05170450c140")] + public interface IAppResolver_8 + { + uint GetAppIDForShortcut(); + uint GetAppIDForShortcutObject(); + uint GetAppIDForWindow(IntPtr hWnd, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + uint GetAppIDForProcess(UInt32 dwProcessId, ref IntPtr pszAppId, IntPtr pUnknown1, IntPtr pUnknown2, IntPtr pUnknown3); + } +} diff --git a/ShortcutUtil/MainForm.Designer.cs b/ShortcutUtil/MainForm.Designer.cs index 5dbf023..8af74c7 100755 --- a/ShortcutUtil/MainForm.Designer.cs +++ b/ShortcutUtil/MainForm.Designer.cs @@ -48,6 +48,9 @@ // // entryListBox // + this.entryListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.entryListBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable; this.entryListBox.FormattingEnabled = true; this.entryListBox.Location = new System.Drawing.Point(16, 98); @@ -59,6 +62,7 @@ // // createButton // + this.createButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.createButton.Location = new System.Drawing.Point(644, 458); this.createButton.Name = "createButton"; this.createButton.Size = new System.Drawing.Size(144, 40); @@ -77,6 +81,8 @@ // // appUserModelIdBox // + this.appUserModelIdBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.appUserModelIdBox.Location = new System.Drawing.Point(16, 35); this.appUserModelIdBox.Name = "appUserModelIdBox"; this.appUserModelIdBox.Size = new System.Drawing.Size(608, 26); @@ -86,6 +92,7 @@ // // appButton // + this.appButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.appButton.Location = new System.Drawing.Point(633, 35); this.appButton.Name = "appButton"; this.appButton.Size = new System.Drawing.Size(74, 31); @@ -96,6 +103,7 @@ // // hwndButton // + this.hwndButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.hwndButton.Location = new System.Drawing.Point(712, 35); this.hwndButton.MinimumSize = new System.Drawing.Size(76, 26); this.hwndButton.Name = "hwndButton"; @@ -103,6 +111,7 @@ this.hwndButton.TabIndex = 6; this.hwndButton.Text = "HWND"; this.hwndButton.UseVisualStyleBackColor = true; + this.hwndButton.Click += new System.EventHandler(this.hwndButton_Click); // // MainForm // @@ -119,8 +128,6 @@ this.Controls.Add(this.defaultShortcutLabel); this.Name = "MainForm"; this.Text = "Form1"; - this.Shown += new System.EventHandler(this.MainForm_Shown); - this.SizeChanged += new System.EventHandler(this.MainForm_SizeChanged); this.ResumeLayout(false); this.PerformLayout(); diff --git a/ShortcutUtil/MainForm.cs b/ShortcutUtil/MainForm.cs index 680ed45..c654b19 100755 --- a/ShortcutUtil/MainForm.cs +++ b/ShortcutUtil/MainForm.cs @@ -1,5 +1,7 @@ using Entries; +using NativeHelpers; using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -23,11 +25,9 @@ private Font nameFont; private Brush fontBrush; - private bool shown = false; - private Size listSizeOffset; - private Point buttonOffset; + private AppResolver appResolver; - public MainForm(EntryList entryList) + public MainForm(EntryList entryList, AppResolver appResolver) { InitializeComponent(); @@ -38,6 +38,7 @@ fontBrush = new SolidBrush(defaultShortcutLabel.ForeColor); this.entryList = entryList; + this.appResolver = appResolver; entryListBox.Items.AddRange(this.entryList.Entries.ToArray()); @@ -122,23 +123,6 @@ e.ItemHeight = Size.Add(IconSize, new Size(0, ItemPadding.Height * 2)).Height; } - private void MainForm_SizeChanged(object sender, EventArgs e) - { - if (shown) - { - entryListBox.Size = Size.Subtract(Size, listSizeOffset); - createButton.Location = Point.Add(buttonOffset, entryListBox.Size); - } - } - - private void MainForm_Shown(object sender, EventArgs e) - { - listSizeOffset = Size.Subtract(Size, entryListBox.Size); - buttonOffset = Point.Subtract(createButton.Location, entryListBox.Size); - - shown = true; - } - private SearchForm search = new SearchForm(); private async void appButton_Click(object sender, EventArgs e) { @@ -166,5 +150,83 @@ appButton.Enabled = true; } + + private static bool GetWindowHandle(IntPtr windowHandle, ref object windowHandles) + { + ((ArrayList)windowHandles).Add(windowHandle); + return true; + } + + private void hwndButton_Click(object sender, EventArgs e) + { + hwndButton.Enabled = false; + + object handles = new ArrayList(); + NativeCalls.EnumWindows(GetWindowHandle, ref handles); + + List> titles = new List>(); + foreach (object ptrObj in (ArrayList)handles) + { + IntPtr ptr = (IntPtr)ptrObj; + if (NativeCalls.IsAltTabWindow(ptr)) + { + string title = NativeCalls.GetWindowTextManaged(ptr); + if (title != "") + { + /* + IPropertyStore store; + Guid guid = NativeValues.IID_IPropertyStore; + NativeCalls.VerifySucceeded((uint)NativeCalls.SHGetPropertyStoreForWindow(ptr, ref guid, out store)); + + using (PropVariant pv = new PropVariant()) + { + NativeCalls.VerifySucceeded(store.GetValue(NativeValues.AppUserModelIDKey, pv)); + + string appUserModelId = pv.Value; + if (appUserModelId == null) + { + uint processId; + NativeCalls.GetWindowThreadProcessId(ptr, out processId); + + IntPtr handle = NativeCalls.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, (int)processId); + + try + { + appUserModelId = NativeCalls.GetApplicationUserModelIdManaged(handle); + } + catch (Exception) { } + } + + + } + */ + + string appUserModelId = appResolver.GetAppIDForWindow(ptr); + if (appUserModelId == null) + { + uint processId; + NativeCalls.GetWindowThreadProcessId(ptr, out processId); + + appUserModelId = appResolver.GetAppIDForProcess(processId); + } + + if (appUserModelId != null) + { + titles.Add(new Tuple(ptr, title, appUserModelId == null ? "NULL" : appUserModelId)); + } + } + + } + } + + string selectedAppUserModelId = search.GetSelection("Select Window", "Available Windows:", titles.Select(t => new Tuple(t.Item3, t.Item2 + ": " + t.Item3)).ToList()); + + if (selectedAppUserModelId != null) + { + appUserModelIdBox.Text = selectedAppUserModelId; + } + + hwndButton.Enabled = true; + } } } diff --git a/ShortcutUtil/Program.cs b/ShortcutUtil/Program.cs index 9b6f955..a46ce5b 100755 --- a/ShortcutUtil/Program.cs +++ b/ShortcutUtil/Program.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; +using NativeHelpers; +using System.Runtime.InteropServices; namespace ShortcutUtil { @@ -16,6 +18,8 @@ [STAThread] static void Main() { + AppResolver appResolver = new AppResolver(); + string[] args = Environment.GetCommandLineArgs(); if (args.Length < 2) @@ -46,7 +50,7 @@ Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm(entryList)); + Application.Run(new MainForm(entryList, appResolver)); } } } diff --git a/ShortcutUtil/SearchForm.Designer.cs b/ShortcutUtil/SearchForm.Designer.cs index 93f58bf..350f243 100755 --- a/ShortcutUtil/SearchForm.Designer.cs +++ b/ShortcutUtil/SearchForm.Designer.cs @@ -55,6 +55,9 @@ // // optionsList // + this.optionsList.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.optionsList.FormattingEnabled = true; this.optionsList.ItemHeight = 20; this.optionsList.Location = new System.Drawing.Point(17, 104); @@ -64,6 +67,7 @@ // // selectButton // + this.selectButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.selectButton.DialogResult = System.Windows.Forms.DialogResult.OK; this.selectButton.Location = new System.Drawing.Point(661, 414); this.selectButton.Name = "selectButton"; @@ -74,6 +78,8 @@ // // searchBox // + this.searchBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.searchBox.Location = new System.Drawing.Point(17, 37); this.searchBox.Name = "searchBox"; this.searchBox.Size = new System.Drawing.Size(771, 26); diff --git a/ShortcutUtil/ShortcutUtil.csproj b/ShortcutUtil/ShortcutUtil.csproj index 975a545..3428eaa 100755 --- a/ShortcutUtil/ShortcutUtil.csproj +++ b/ShortcutUtil/ShortcutUtil.csproj @@ -35,6 +35,28 @@ prompt 4 + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll @@ -90,6 +112,10 @@ EntryList.cs + + NativeHelpers.cs + + Form @@ -105,6 +131,7 @@ SearchForm.cs + Component diff --git a/ShortcutUtil/TTLib.cs b/ShortcutUtil/TTLib.cs new file mode 100755 index 0000000..1b1823e --- /dev/null +++ b/ShortcutUtil/TTLib.cs @@ -0,0 +1,301 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace NativeHelpers +{ + public static class TTLib + { + + // Errors returned by TTLib_Init + public enum InitResult : UInt32 + { + OK = 0, + ERR_INIT_ALREADY_INITIALIZED, + ERR_INIT_REGISTER_MESSAGE, + ERR_INIT_FILE_MAPPING, + ERR_INIT_VIEW_MAPPING, + } + + // Errors returned by TTLib_LoadIntoExplorer + public enum LoadIntoExplorerResult : UInt32 + { + OK = 0, + ERR_EXE_NOT_INITIALIZED, + ERR_EXE_ALREADY_LOADED, + ERR_EXE_FIND_TASKBAR, + ERR_EXE_OPEN_PROCESS, + ERR_EXE_VIRTUAL_ALLOC, + ERR_EXE_WRITE_PROC_MEM, + ERR_EXE_CREATE_REMOTE_THREAD, + ERR_EXE_READ_PROC_MEM, + + ERR_INJ_BEFORE_RUN = 101, + ERR_INJ_BEFORE_GETMODULEHANDLE, + ERR_INJ_BEFORE_LOADLIBRARY, + ERR_INJ_BEFORE_GETPROCADDR, + ERR_INJ_BEFORE_LIBINIT, + ERR_INJ_GETMODULEHANDLE, + ERR_INJ_LOADLIBRARY, + ERR_INJ_GETPROCADDR, + + ERR_LIB_INIT_ALREADY_CALLED = 201, + ERR_LIB_LIB_VER_MISMATCH, + ERR_LIB_WIN_VER_MISMATCH, + ERR_LIB_VIEW_MAPPING, + ERR_LIB_FIND_IMPORT, + ERR_LIB_WND_TASKBAR, + ERR_LIB_WND_TASKSW, + ERR_LIB_WND_TASKLIST, + ERR_LIB_WND_THUMB, + ERR_LIB_MSG_DLL_INIT, + ERR_LIB_WAITTHREAD, + + ERR_LIB_EXTHREAD_MINHOOK = 301, + ERR_LIB_EXTHREAD_MINHOOK_PRELOADED, + ERR_LIB_EXTHREAD_COMFUNCHOOK, + ERR_LIB_EXTHREAD_REFRESHTASKBAR, + ERR_LIB_EXTHREAD_MINHOOK_APPLY, + } + + public enum GroupType : UInt32 + { + UNKNOWN = 0, + NORMAL, + PINNED, + COMBINED, + TEMPORARY, + } + + public enum List : UInt32 + { + LABEL = 0, + GROUP, + GROUPPINNED, + COMBINE, + } + + public enum ListValue : UInt32 + { + LABEL_NEVER = 0, + LABEL_ALWAYS, + + GROUP_NEVER = 0, + GROUP_ALWAYS, + + GROUPPINNED_NEVER = 0, + GROUPPINNED_ALWAYS, + + COMBINE_NEVER = 0, + COMBINE_ALWAYS, + } + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left, Top, Right, Bottom; + + public RECT(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public RECT(System.Drawing.Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { } + + public int X + { + get { return Left; } + set { Right -= (Left - value); Left = value; } + } + + public int Y + { + get { return Top; } + set { Bottom -= (Top - value); Top = value; } + } + + public int Height + { + get { return Bottom - Top; } + set { Bottom = value + Top; } + } + + public int Width + { + get { return Right - Left; } + set { Right = value + Left; } + } + + public System.Drawing.Point Location + { + get { return new System.Drawing.Point(Left, Top); } + set { X = value.X; Y = value.Y; } + } + + public System.Drawing.Size Size + { + get { return new System.Drawing.Size(Width, Height); } + set { Width = value.Width; Height = value.Height; } + } + + public static implicit operator System.Drawing.Rectangle(RECT r) + { + return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height); + } + + public static implicit operator RECT(System.Drawing.Rectangle r) + { + return new RECT(r); + } + + public static bool operator ==(RECT r1, RECT r2) + { + return r1.Equals(r2); + } + + public static bool operator !=(RECT r1, RECT r2) + { + return !r1.Equals(r2); + } + + public bool Equals(RECT r) + { + return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom; + } + + public override bool Equals(object obj) + { + if (obj is RECT) + return Equals((RECT)obj); + else if (obj is System.Drawing.Rectangle) + return Equals(new RECT((System.Drawing.Rectangle)obj)); + return false; + } + + public override int GetHashCode() + { + return ((System.Drawing.Rectangle)this).GetHashCode(); + } + + public override string ToString() + { + return string.Format(System.Globalization.CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom); + } + } + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_Init", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern InitResult Init(); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_Uninit", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool Uninit(); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_LoadIntoExplorer", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern LoadIntoExplorerResult LoadIntoExplorer(); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_IsLoadedIntoExplorer", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool IsLoadedIntoExplorer(); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_UnloadFromExplorer", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool UnloadFromExplorer(); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_ManipulationStart", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool ManipulationStart(); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_ManipulationEnd", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool ManipulationEnd(); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetMainTaskbar", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetMainTaskbar(); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetSecondaryTaskbarCount", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool GetSecondaryTaskbarCount(out Int32 pnCount); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetSecondaryTaskbar", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetSecondaryTaskbar(Int32 nIndex); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetTaskListWindow", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetTaskListWindow(IntPtr hTaskbar); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetTaskbarWindow", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetTaskbarWindow(IntPtr hTaskbar); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetTaskbarMonitor", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetTaskbarMonitor(IntPtr hTaskbar); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetButtonGroupCount", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool GetButtonGroupCount(IntPtr hTaskbar, out Int32 pnCount); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetButtonGroup", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetButtonGroup(IntPtr hTaskbar, Int32 nIndex); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetActiveButtonGroup", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetActiveButtonGroup(IntPtr hTaskbar); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetTrackedButtonGroup", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetTrackedButtonGroup(IntPtr hTaskbar); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_ButtonGroupMove", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool ButtonGroupMove(IntPtr hTaskbar, Int32 nIndexFrom, Int32 nIndexTo); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetButtonGroupTaskbar", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool GetButtonGroupTaskbar(IntPtr hButtonGroup, out IntPtr phTaskbar); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetButtonGroupRect", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool GetButtonGroupRect(IntPtr hButtonGroup, out RECT pRect); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetButtonGroupType", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool GetButtonGroupType(IntPtr hButtonGroup, out GroupType pnType); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetButtonGroupAppId", SetLastError = true, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public static extern UInt32 GetButtonGroupAppId(IntPtr hButtonGroup, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszAppId, UInt32 nMaxSize); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetButtonCount", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool GetButtonCount(IntPtr hButtonGroup, out Int32 pnCount); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetButton", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetButton(IntPtr hButtonGroup, Int32 nIndex); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetActiveButton", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetActiveButton(IntPtr hTaskbar); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetTrackedButton", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetTrackedButton(IntPtr hTaskbar); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_ButtonMoveInButtonGroup", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool ButtonMoveInButtonGroup(IntPtr hButtonGroup, Int32 nIndexFrom, Int32 nIndexTo); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetButtonWindow", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr GetButtonWindow(IntPtr hButton); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_AddAppIdToList", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool AddAppIdToList(List nList, [MarshalAs(UnmanagedType.LPWStr)] string pszAppId, ListValue nListValue); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_RemoveAppIdFromList", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool RemoveAppIdFromList(List nList, [MarshalAs(UnmanagedType.LPWStr)] string pszAppId); + + [DllImport("ttlib32.dll", EntryPoint = "TTLib_GetAppIdListValue", SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool GetAppIdListValue(List nList, [MarshalAs(UnmanagedType.LPWStr)] string pszAppId, out ListValue pnListValue); + } +}