From 3f7d88e8478609ee8899d81e3ac8ed18830086bf Mon Sep 17 00:00:00 2001 From: Chris Kaczor Date: Wed, 30 Apr 2014 17:00:26 -0400 Subject: [PATCH] Initial commit --- .gitattributes | 63 ++++++ .gitignore | 156 +++++++++++++ Common.Native.csproj | 163 ++++++++++++++ Common.Native.sln | 34 +++ Constants.cs | 227 +++++++++++++++++++ Functions.cs | 208 ++++++++++++++++++ Properties/AssemblyInfo.cs | 36 +++ Structures.cs | 198 +++++++++++++++++ WinEvent.cs | 436 +++++++++++++++++++++++++++++++++++++ WinHook.cs | 39 ++++ 10 files changed, 1560 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Common.Native.csproj create mode 100644 Common.Native.sln create mode 100644 Constants.cs create mode 100644 Functions.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 Structures.cs create mode 100644 WinEvent.cs create mode 100644 WinHook.cs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1bc915c --- /dev/null +++ b/.gitignore @@ -0,0 +1,156 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + + +#LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac desktop service store files +.DS_Store diff --git a/Common.Native.csproj b/Common.Native.csproj new file mode 100644 index 0000000..28e5ffb --- /dev/null +++ b/Common.Native.csproj @@ -0,0 +1,163 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84} + Library + Properties + Common.Native + Common.Native + v4.0 + 512 + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + Client + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + true + ..\ChrisKaczor.pfx + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + bin\Debug\Common.Native.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + AllRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + true + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + bin\Release\Common.Native.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + AllRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + bin\Debug\Common.Native.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + AllRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + bin\Release\Common.Native.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + AllRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + true + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + true + + + + + 3.5 + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Common.Native.sln b/Common.Native.sln new file mode 100644 index 0000000..b7a3584 --- /dev/null +++ b/Common.Native.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30110.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common.Native", "Common.Native.csproj", "{ED1C07A1-54F5-4796-8B06-2A0BB1960D84}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Debug|x64.ActiveCfg = Debug|x64 + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Debug|x64.Build.0 = Debug|x64 + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Debug|x86.ActiveCfg = Debug|x86 + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Debug|x86.Build.0 = Debug|x86 + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Release|Any CPU.Build.0 = Release|Any CPU + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Release|x64.ActiveCfg = Release|x64 + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Release|x64.Build.0 = Release|x64 + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Release|x86.ActiveCfg = Release|x86 + {ED1C07A1-54F5-4796-8B06-2A0BB1960D84}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Constants.cs b/Constants.cs new file mode 100644 index 0000000..8d7df85 --- /dev/null +++ b/Constants.cs @@ -0,0 +1,227 @@ +using System; + +namespace Common.Native +{ + public static class Constants + { + /// + /// SetWindowPos flags + /// + [Flags] + public enum WindowPositionFlags + { + AsyncWindowPosition = 0x4000, + DeferErase = 0x2000, + DrawFrame = 0x0020, + FrameChanged = 0x0020, + HideWindow = 0x0080, + NoActivate = 0x0010, + NoCopyBits = 0x0100, + NoMove = 0x0002, + NoOwnerZorder = 0x0200, + NoRedraw = 0x0008, + NoReposition = 0x0200, + NoSendChanging = 0x0400, + NoSize = 0x0001, + NoZorder = 0x0004, + ShowWindow = 0x0040, + } + + /// + /// WM_SYSCOMMAND resize direction + /// + public enum ResizeDirection + { + Left = 61441, + Right = 61442, + Top = 61443, + TopLeft = 61444, + TopRight = 61445, + Bottom = 61446, + BottomLeft = 61447, + BottomRight = 61448, + } + + /// + /// Window messages + /// + public enum WindowMessage + { + Create = 0x0001, + Destroy = 0x0002, + Move = 0x0003, + Moving = 0x0216, + Size = 0x0005, + Activate = 0x0006, + SetFocus = 0x0007, + KillFocus = 0x0008, + Enable = 0x000a, + SetRedraw = 0x000b, + SetText = 0x000c, + GetText = 0x000d, + GetTextLength = 0x000e, + Paint = 0x000f, + Close = 0x0010, + QueryEndSession = 0x0011, + Quit = 0x0012, + QueryOpen = 0x0013, + EraseBackground = 0x0014, + SystemColorChange = 0x0015, + + WindowPositionChanging = 0x0046, + WindowPositionChanged = 0x0047, + + QueryDragIcon = 0x0037, + GetIcon = 0x007F, + SetIcon = 0x0080, + CreateNonClientArea = 0x0081, + DestroyNonClientArea = 0x0082, + CalculateNonClientSize = 0x0083, + HitTestNonClientArea = 0x0084, + PaintNonClientArea = 0x0085, + ActivateNonClientArea = 0x0086, + GetDialogCode = 0x0087, + SyncPaint = 0x0088, + NonClientMouseMove = 0x00a0, + NonClientLeftButtonDown = 0x00a1, + NonClientLeftButtonUp = 0x00a2, + NonClientLeftButtonDoubleClick = 0x00a3, + NonClientRightButtonDown = 0x00a4, + NonClientRightButtonUp = 0x00a5, + NonClientRightButtonDoubleClick = 0x00a6, + NonClientMiddleButtonDown = 0x00a7, + NonClientMiddleButtonUp = 0x00a8, + NonClientMiddleButtonDoubleClick = 0x00a9, + + SysKeyDown = 0x0104, + SysKeyUp = 0x0105, + SysChar = 0x0106, + SysDeadChar = 0x0107, + SysCommand = 0x0112, + + Hotkey = 0x312, + + DwmCompositionChanged = 0x031e, + User = 0x0400, + App = 0x8000, + } + + public enum HitTestValues + { + Error = -2, + Transparent = -1, + Nowhere = 0, + Client = 1, + Caption = 2, + SystemMenu = 3, + GrowBox = 4, + Menu = 5, + HorizontalScroll = 6, + VerticalScroll = 7, + MinimizeButton = 8, + MaximizeButton = 9, + Left = 10, + Right = 11, + Top = 12, + TopLeft = 13, + TopRight = 14, + Bottom = 15, + BottomLeft = 16, + BottomRight = 17, + Border = 18, + Object = 19, + Close = 20, + Help = 21 + } + + [Flags] + public enum ExtendedWindowStyles + { + ToolWindow = 0x00000080, + } + + public enum GetWindowLongFields + { + ExStyle = -20, + } + + [Flags] + public enum ProcessAccess : uint + { + Terminate = 0x00000001, + CreateThread = 0x00000002, + SetSessionId = 0x00000004, + VmOperation = 0x00000008, + VmRead = 0x00000010, + VmWrite = 0x00000020, + DuplicateHandle = 0x00000040, + CreateProcess = 0x00000080, + SetQuota = 0x00000100, + SetInformation = 0x00000200, + QueryInformation = 0x00000400, + StandardRightsRequired = 0x000F0000, + Synchronize = 0x00100000, + + AllAccess = + Terminate | CreateThread | SetSessionId | VmOperation | VmRead | VmWrite | DuplicateHandle | + CreateProcess | SetQuota | SetInformation | QueryInformation | StandardRightsRequired | Synchronize + } + + [Flags] + public enum SnapshotFlags : uint + { + HeapList = 0x00000001, + Process = 0x00000002, + Thread = 0x00000004, + Module = 0x00000008, + Module32 = 0x00000010, + All = (HeapList | Process | Thread | Module), + Inherit = 0x80000000, + } + + public enum ShowWindowCommand : uint + { + Hide = 0, + ShowNormal = 1, + Normal = 1, + ShowMinimized = 2, + ShowMaximized = 3, + Maximize = 3, + ShowNoActivate = 4, + Show = 5, + Minimize = 6, + ShowMinNoActive = 7, + ShowNa = 8, + Restore = 9 + } + + [Flags] + public enum SendMessageTimeoutFlags : uint + { + Normal = 0x0, + Block = 0x1, + AbortIfHung = 0x2, + NoTimeoutIfNotHung = 0x8 + } + + public const int ICON_BIG = 1; + public const int ICON_SMALL = 0; + + public enum ClassLongFlags : int + { + GCLP_MENUNAME = -8, + GCLP_HBRBACKGROUND = -10, + GCLP_HCURSOR = -12, + GCLP_HICON = -14, + GCLP_HMODULE = -16, + GCL_CBWNDEXTRA = -18, + GCL_CBCLSEXTRA = -20, + GCLP_WNDPROC = -24, + GCL_STYLE = -26, + GCLP_HICONSM = -34, + GCW_ATOM = -32 + } + + public static readonly IntPtr InvalidHandle = new IntPtr(-1); + } +} diff --git a/Functions.cs b/Functions.cs new file mode 100644 index 0000000..4262dc0 --- /dev/null +++ b/Functions.cs @@ -0,0 +1,208 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; + +namespace Common.Native +{ + public static class TypeExtensions + { + public static int HighWord(this int value) + { + return ((short) (((value) >> 16) & 0xFFFF)); + } + + public static int LowWord(this int value) + { + return ((short) value); + } + + public static int HighWord(this IntPtr value) + { + return ((int) value).HighWord(); + } + + public static int LowWord(this IntPtr value) + { + return ((int) value).LowWord(); + } + } + + public static class Functions + { + public static class Window + { + public static bool IsChild(IntPtr childHandle, IntPtr parentHandle) + { + if (childHandle == parentHandle) + return true; + + if (childHandle == IntPtr.Zero) + return false; + + return IsChild(User32.GetParent(childHandle), parentHandle); + } + + public static string GetText(IntPtr hWnd) + { + // Allocate correct string length first + int length = User32.GetWindowTextLength(hWnd); + var sb = new StringBuilder(length + 1); + User32.GetWindowText(hWnd, sb, sb.Capacity); + return sb.ToString(); + } + + public static string GetClass(IntPtr hWnd) + { + // Allocate correct string length first + const int length = 255; + var sb = new StringBuilder(length + 1); + User32.RealGetWindowClass(hWnd, sb, sb.Capacity); + return sb.ToString(); + } + + public static void Show(IntPtr hWnd, Constants.ShowWindowCommand command) + { + var windowPlacement = new Structures.WindowPlacement(); + User32.GetWindowPlacement(hWnd, ref windowPlacement); + windowPlacement.ShowCommand = command; + User32.SetWindowPlacement(hWnd, ref windowPlacement); + } + + public static Icon GetSmallIcon(IntPtr hWnd) + { + try + { + IntPtr hIcon; + + User32.SendMessageTimeout(hWnd, + (uint) Constants.WindowMessage.GetIcon, + (IntPtr) Constants.ICON_SMALL, + IntPtr.Zero, + Constants.SendMessageTimeoutFlags.AbortIfHung, + 1000, + out hIcon); + + if (hIcon == IntPtr.Zero) + { + hIcon = User32.GetClassLongPtr(hWnd, Constants.ClassLongFlags.GCLP_HICONSM); + } + + if (hIcon == IntPtr.Zero) + { + User32.SendMessageTimeout( + hWnd, + (uint) Constants.WindowMessage.QueryDragIcon, + IntPtr.Zero, + IntPtr.Zero, + Constants.SendMessageTimeoutFlags.AbortIfHung, + 1000, + out hIcon); + } + + return hIcon == IntPtr.Zero ? null : Icon.FromHandle(hIcon); + } + catch (Exception) + { + return null; + } + } + } + + public static class User32 + { + public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowPlacement(IntPtr hWnd, ref Structures.WindowPlacement lpwndpl); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref Structures.WindowPlacement lpwndpl); + + [DllImport("user32.dll")] + public static extern uint RealGetWindowClass(IntPtr hwnd, [Out] StringBuilder pszType, int cchType); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern int GetWindowTextLength(IntPtr hWnd); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, Constants.SendMessageTimeoutFlags flags, uint timeout, out IntPtr result); + + [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] + public static extern IntPtr GetParent(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll")] + public static extern bool SetForegroundWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong); + + public static IntPtr SetWindowLongPtrSmart(IntPtr hWnd, int nIndex, IntPtr dwNewLong) + { + if (IntPtr.Size == 8) + return SetWindowLongPtr(hWnd, nIndex, dwNewLong); + + return new IntPtr(SetWindowLong(hWnd, nIndex, dwNewLong.ToInt32())); + } + + [DllImport("user32.dll", EntryPoint = "GetClassLong")] + public static extern uint GetClassLongPtr32(IntPtr hWnd, Constants.ClassLongFlags nIndex); + + [DllImport("user32.dll", EntryPoint = "GetClassLongPtr")] + public static extern IntPtr GetClassLongPtr64(IntPtr hWnd, Constants.ClassLongFlags nIndex); + + public static IntPtr GetClassLongPtr(IntPtr hWnd, Constants.ClassLongFlags nIndex) + { + if (IntPtr.Size > 4) + return GetClassLongPtr64(hWnd, nIndex); + + return new IntPtr(GetClassLongPtr32(hWnd, nIndex)); + } + } + + public static class Kernel32 + { + [DllImport("kernel32.dll", EntryPoint = "SetLastError")] + public static extern void SetLastError(int dwErrorCode); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr CreateToolhelp32Snapshot(Constants.SnapshotFlags flags, uint processId); + + [DllImport("kernel32.dll")] + public static extern bool Process32First(IntPtr handle, ref Structures.ProcessEntry32 processInfo); + + [DllImport("kernel32.dll")] + public static extern bool Process32Next(IntPtr handle, ref Structures.ProcessEntry32 processInfo); + + [DllImport("kernel32.dll")] + public static extern bool CloseHandle(IntPtr handle); + + [DllImport("kernel32.dll")] + public static extern IntPtr OpenProcess(Constants.ProcessAccess desiredAccess, bool inheritHandle, uint processId); + + [DllImport("kernel32.dll")] + public static extern bool GetProcessTimes(IntPtr processHandle, out long creationTime, out long exitTime, out long kernelTime, out long userTime); + + [DllImport("kernel32.dll")] + public static extern uint GetCurrentThreadId(); + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a8f9d25 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Common.Native")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Common.Native")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("efb734ae-c52e-4719-a333-c8abde2c2823")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Structures.cs b/Structures.cs new file mode 100644 index 0000000..2c033f9 --- /dev/null +++ b/Structures.cs @@ -0,0 +1,198 @@ +using System; +using System.Runtime.InteropServices; + +namespace Common.Native +{ + public static class Structures + { + [StructLayout(LayoutKind.Sequential)] + public struct Point + { + public int X; + public int Y; + + public Point(int x, int y) + { + X = x; + Y = y; + } + + public Point(System.Drawing.Point pt) : this(pt.X, pt.Y) { } + + public static implicit operator System.Drawing.Point(Point p) + { + return new System.Drawing.Point(p.X, p.Y); + } + + public static implicit operator Point(System.Drawing.Point p) + { + return new Point(p.X, p.Y); + } + } + + [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); + + 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); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct CWPRETSTRUCT + { + public IntPtr lResult; + public IntPtr lParam; + public IntPtr wParam; + public uint message; + public IntPtr hwnd; + } + + [StructLayout(LayoutKind.Sequential)] + public struct WindowPosition + { + public IntPtr Handle; + public IntPtr HandleInsertAfter; + public int Left; + public int Top; + public int Width; + public int Height; + public Constants.WindowPositionFlags Flags; + + public int Right { get { return Left + Width; } } + public int Bottom { get { return Top + Height; } } + + public bool IsSameLocationAndSize(WindowPosition compare) + { + return (compare.Left == Left && compare.Top == Top && compare.Width == Width && compare.Height == Height); + } + + public bool IsSameSize(WindowPosition compare) + { + return (compare.Width == Width && compare.Height == Height); + } + + public bool IsSameLocation(WindowPosition compare) + { + return (compare.Left == Left && compare.Top == Top); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct ProcessEntry32 + { + public uint Size; + public uint Usage; + public uint ProcessId; + public IntPtr DefaultHeapId; + public uint ModuleId; + public uint Threads; + public uint ParentProcessId; + public int PriorityClassBase; + public uint Flags; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string ExeFilename; + } + + public struct WindowPlacement + { + public int Length; + public int Flags; + public Constants.ShowWindowCommand ShowCommand; + public Point MinPosition; + public Point MaxPosition; + public Rect NormalPosition; + } + } +} diff --git a/WinEvent.cs b/WinEvent.cs new file mode 100644 index 0000000..a0091e7 --- /dev/null +++ b/WinEvent.cs @@ -0,0 +1,436 @@ +using System; +using System.Runtime.InteropServices; + +namespace Common.Native +{ + public class WinEvent + { + public enum Event + { + Min = 0x00000001, + Max = 0x7FFFFFFF, + + /* + * EVENT_SYSTEM_SOUND + * Sent when a sound is played. Currently nothing is generating this, we + * this event when a system sound (for menus, etc) is played. Apps + * generate this, if accessible, when a private sound is played. For + * example, if Mail plays a "New Mail" sound. + * + * System Sounds: + * (Generated by PlaySoundEvent in USER itself) + * hwnd is NULL + * idObject is OBJID_SOUND + * idChild is sound child ID if one + * App Sounds: + * (PlaySoundEvent won't generate notification; up to app) + * hwnd + idObject gets interface pointer to Sound object + * idChild identifies the sound in question + * are going to be cleaning up the SOUNDSENTRY feature in the control panel + * and will use this at that time. Applications implementing WinEvents + * are perfectly welcome to use it. Clients of IAccessible* will simply + * turn around and get back a non-visual object that describes the sound. + */ + SystemSound = 0x0001, + + /* + * EVENT_SYSTEM_ALERT + * System Alerts: + * (Generated by MessageBox() calls for example) + * hwnd is hwndMessageBox + * idObject is OBJID_ALERT + * App Alerts: + * (Generated whenever) + * hwnd+idObject gets interface pointer to Alert + */ + SystemAlert = 0x0002, + + /* + * EVENT_SYSTEM_FOREGROUND + * Sent when the foreground (active) window changes, even if it is changing + * to another window in the same thread as the previous one. + * hwnd is hwndNewForeground + * idObject is OBJID_WINDOW + * idChild is INDEXID_OBJECT + */ + SystemForeground = 0x0003, + + /* + * Menu + * hwnd is window (top level window or popup menu window) + * idObject is ID of control (OBJID_MENU, OBJID_SYSMENU, OBJID_SELF for popup) + * idChild is CHILDID_SELF + * + * EVENT_SYSTEM_MENUSTART + * EVENT_SYSTEM_MENUEND + * For MENUSTART, hwnd+idObject+idChild refers to the control with the menu bar, + * or the control bringing up the context menu. + * + * Sent when entering into and leaving from menu mode (system, app bar, and + * track popups). + */ + SystemMenuStart = 0x0004, + SystemMenuEnd = 0x0005, + + /* + * EVENT_SYSTEM_MENUPOPUPSTART + * EVENT_SYSTEM_MENUPOPUPEND + * Sent when a menu popup comes up and just before it is taken down. Note + * that for a call to TrackPopupMenu(), a client will see EVENT_SYSTEM_MENUSTART + * followed almost immediately by EVENT_SYSTEM_MENUPOPUPSTART for the popup + * being shown. + * + * For MENUPOPUP, hwnd+idObject+idChild refers to the NEW popup coming up, not the + * parent item which is hierarchical. You can get the parent menu/popup by + * asking for the accParent object. + */ + SystemMenuPopupStart = 0x0006, + SystemMenuPopupEnd = 0x0007, + + + /* + * EVENT_SYSTEM_CAPTURESTART + * EVENT_SYSTEM_CAPTUREEND + * Sent when a window takes the capture and releases the capture. + */ + SystemCaptureStart = 0x0008, + SystemCaptureEnd = 0x0009, + + /* + * Move Size + * EVENT_SYSTEM_MOVESIZESTART + * EVENT_SYSTEM_MOVESIZEEND + * Sent when a window enters and leaves move-size dragging mode. + */ + SystemMoveSizeStart = 0x000A, + SystemMoveSizeEnd = 0x000B, + + /* + * Context Help + * EVENT_SYSTEM_CONTEXTHELPSTART + * EVENT_SYSTEM_CONTEXTHELPEND + * Sent when a window enters and leaves context sensitive help mode. + */ + SystemContextHelpStart = 0x000C, + EventSystemContextHelpEnd = 0x000D, + + /* + * Drag & Drop + * EVENT_SYSTEM_DRAGDROPSTART + * EVENT_SYSTEM_DRAGDROPEND + * Send the START notification just before going into drag&drop loop. Send + * the END notification just after canceling out. + * Note that it is up to apps and OLE to generate this, since the system + * doesn't know. Like EVENT_SYSTEM_SOUND, it will be a while before this + * is prevalent. + */ + SystemDragDropStart = 0x000E, + SystemDragdropEnd = 0x000F, + + /* + * Dialog + * Send the START notification right after the dialog is completely + * initialized and visible. Send the END right before the dialog + * is hidden and goes away. + * EVENT_SYSTEM_DIALOGSTART + * EVENT_SYSTEM_DIALOGEND + */ + SystemDialogStart = 0x0010, + SystemDialogEnd = 0x0011, + + /* + * EVENT_SYSTEM_SCROLLING + * EVENT_SYSTEM_SCROLLINGSTART + * EVENT_SYSTEM_SCROLLINGEND + * Sent when beginning and ending the tracking of a scrollbar in a window, + * and also for scrollbar controls. + */ + SystemScrollingStart = 0x0012, + SystemScrollingEnd = 0x0013, + + /* + * Alt-Tab Window + * Send the START notification right after the switch window is initialized + * and visible. Send the END right before it is hidden and goes away. + * EVENT_SYSTEM_SWITCHSTART + * EVENT_SYSTEM_SWITCHEND + */ + SystemSwitchStart = 0x0014, + SystemSwitchEnd = 0x0015, + + /* + * EVENT_SYSTEM_MINIMIZESTART + * EVENT_SYSTEM_MINIMIZEEND + * Sent when a window minimizes and just before it restores. + */ + SystemMinimizeStart = 0x0016, + SystemMinimizeEnd = 0x0017, + + + SystemDesktopSwitch = 0x0020, + + + EventSystemEnd = 0x00FF, + + OemDefinedStart = 0x0101, + OemDefinedEnd = 0x01FF, + + UiaEventIdStart = 0x4E00, + UiaEventIdEnd = 0x4EFF, + + UiaPropIdStart = 0x7500, + UiaPropIdEnd = 0x75FF, + + ConsoleCaret = 0x4001, + ConsoleUpdateRegion = 0x4002, + ConsoleUpdateSimple = 0x4003, + ConsoleUpdateScroll = 0x4004, + ConsoleLayout = 0x4005, + ConsoleStartApplication = 0x4006, + ConsoleEndApplication = 0x4007, + + ConsoleEnd = 0x40FF, + + /* + * Object events + * + * The system AND apps generate these. The system generates these for + * real windows. Apps generate these for objects within their window which + * act like a separate control, e.g. an item in a list view. + * + * When the system generate them, dwParam2 is always WMOBJID_SELF. When + * apps generate them, apps put the has-meaning-to-the-app-only ID value + * in dwParam2. + * For all events, if you want detailed accessibility information, callers + * should + * * Call AccessibleObjectFromWindow() with the hwnd, idObject parameters + * of the event, and IID_IAccessible as the REFIID, to get back an + * IAccessible* to talk to + * * Initialize and fill in a VARIANT as VT_I4 with lVal the idChild + * parameter of the event. + * * If idChild isn't zero, call get_accChild() in the container to see + * if the child is an object in its own right. If so, you will get + * back an IDispatch* object for the child. You should release the + * parent, and call QueryInterface() on the child object to get its + * IAccessible*. Then you talk directly to the child. Otherwise, + * if get_accChild() returns you nothing, you should continue to + * use the child VARIANT. You will ask the container for the properties + * of the child identified by the VARIANT. In other words, the + * child in this case is accessible but not a full-blown object. + * Like a button on a titlebar which is 'small' and has no children. + */ + + /* + * For all EVENT_OBJECT events, + * hwnd is the dude to Send the WM_GETOBJECT message to (unless NULL, + * see above for system things) + * idObject is the ID of the object that can resolve any queries a + * client might have. It's a way to deal with windowless controls, + * controls that are just drawn on the screen in some larger parent + * window (like SDM), or standard frame elements of a window. + * idChild is the piece inside of the object that is affected. This + * allows clients to access things that are too small to have full + * blown objects in their own right. Like the thumb of a scrollbar. + * The hwnd/idObject pair gets you to the container, the dude you + * probably want to talk to most of the time anyway. The idChild + * can then be passed into the acc properties to get the name/value + * of it as needed. + * + * Example #1: + * System propagating a listbox selection change + * EVENT_OBJECT_SELECTION + * hwnd == listbox hwnd + * idObject == OBJID_WINDOW + * idChild == new selected item, or CHILDID_SELF if + * nothing now selected within container. + * Word '97 propagating a listbox selection change + * hwnd == SDM window + * idObject == SDM ID to get at listbox 'control' + * idChild == new selected item, or CHILDID_SELF if + * nothing + * + * Example #2: + * System propagating a menu item selection on the menu bar + * EVENT_OBJECT_SELECTION + * hwnd == top level window + * idObject == OBJID_MENU + * idChild == ID of child menu bar item selected + * + * Example #3: + * System propagating a dropdown coming off of said menu bar item + * EVENT_OBJECT_CREATE + * hwnd == popup item + * idObject == OBJID_WINDOW + * idChild == CHILDID_SELF + * + * Example #4: + * + * For EVENT_OBJECT_REORDER, the object referred to by hwnd/idObject is the + * PARENT container in which the zorder is occurring. This is because if + * one child is zordering, all of them are changing their relative zorder. + */ + ObjectCreate = 0x8000, // hwnd + ID + idChild is created item + ObjectDestroy = 0x8001, // hwnd + ID + idChild is destroyed item + ObjectShow = 0x8002, // hwnd + ID + idChild is shown item + ObjectHide = 0x8003, // hwnd + ID + idChild is hidden item + ObjectReorder = 0x8004, // hwnd + ID + idChild is parent of zordering children + /* + * NOTE: + * Minimize the number of notifications! + * + * When you are hiding a parent object, obviously all child objects are no + * longer visible on screen. They still have the same "visible" status, + * but are not truly visible. Hence do not send HIDE notifications for the + * children also. One implies all. The same goes for SHOW. + */ + + ObjectFocus = 0x8005, // hwnd + ID + idChild is focused item + ObjectSelection = 0x8006, // hwnd + ID + idChild is selected item (if only one), or idChild is OBJID_WINDOW if complex + ObjectSelectionAdd = 0x8007, // hwnd + ID + idChild is item added + ObjectSelectionRemove = 0x8008, // hwnd + ID + idChild is item removed + ObjectSelectionWithin = 0x8009, // hwnd + ID + idChild is parent of changed selected items + + /* + * NOTES: + * There is only one "focused" child item in a parent. This is the place + * keystrokes are going at a given moment. Hence only send a notification + * about where the NEW focus is going. A NEW item getting the focus already + * implies that the OLD item is losing it. + * + * SELECTION however can be multiple. Hence the different SELECTION + * notifications. Here's when to use each: + * + * (1) Send a SELECTION notification in the simple single selection + * case (like the focus) when the item with the selection is + * merely moving to a different item within a container. hwnd + ID + * is the container control, idChildItem is the new child with the + * selection. + * + * (2) Send a SELECTIONADD notification when a new item has simply been added + * to the selection within a container. This is appropriate when the + * number of newly selected items is very small. hwnd + ID is the + * container control, idChildItem is the new child added to the selection. + * + * (3) Send a SELECTIONREMOVE notification when a new item has simply been + * removed from the selection within a container. This is appropriate + * when the number of newly selected items is very small, just like + * SELECTIONADD. hwnd + ID is the container control, idChildItem is the + * new child removed from the selection. + * + * (4) Send a SELECTIONWITHIN notification when the selected items within a + * control have changed substantially. Rather than propagate a large + * number of changes to reflect removal for some items, addition of + * others, just tell somebody who cares that a lot happened. It will + * be faster an easier for somebody watching to just turn around and + * query the container control what the new bunch of selected items + * are. + */ + + ObjectStateChange = 0x800A, // hwnd + ID + idChild is item w/ state change + /* + * Examples of when to send an EVENT_OBJECT_STATECHANGE include + * * It is being enabled/disabled (USER does for windows) + * * It is being pressed/released (USER does for buttons) + * * It is being checked/unchecked (USER does for radio/check buttons) + */ + ObjectLocationChange = 0x800B, // hwnd + ID + idChild is moved/sized item + + /* + * Note: + * A LOCATIONCHANGE is not sent for every child object when the parent + * changes shape/moves. Send one notification for the topmost object + * that is changing. For example, if the user resizes a top level window, + * USER will generate a LOCATIONCHANGE for it, but not for the menu bar, + * title bar, scrollbars, etc. that are also changing shape/moving. + * + * In other words, it only generates LOCATIONCHANGE notifications for + * real windows that are moving/sizing. It will not generate a LOCATIONCHANGE + * for every non-floating child window when the parent moves (the children are + * logically moving also on screen, but not relative to the parent). + * + * Now, if the app itself resizes child windows as a result of being + * sized, USER will generate LOCATIONCHANGEs for those dudes also because + * it doesn't know better. + * + * Note also that USER will generate LOCATIONCHANGE notifications for two + * non-window sys objects: + * (1) System caret + * (2) Cursor + */ + + ObjectNameChange = 0x800C, // hwnd + ID + idChild is item w/ name change + ObjectDescriptionChange = 0x800D, // hwnd + ID + idChild is item w/ desc change + ObjectValueChange = 0x800E, // hwnd + ID + idChild is item w/ value change + ObjectParentChange = 0x800F, // hwnd + ID + idChild is item w/ new parent + ObjectHelpChange = 0x8010, // hwnd + ID + idChild is item w/ help change + ObjectDefaultActionChange = 0x8011, // hwnd + ID + idChild is item w/ def action change + ObjectAcceleratorChange = 0x8012, // hwnd + ID + idChild is item w/ keybd accel change + + ObjectInvoked = 0x8013, // hwnd + ID + idChild is item invoked + ObjectTextSelectionChanged = 0x8014, // hwnd + ID + idChild is item w? test selection change + + /* + * EVENT_OBJECT_CONTENTSCROLLED + * Sent when ending the scrolling of a window object. + * + * Unlike the similar event (EVENT_SYSTEM_SCROLLEND), this event will be + * associated with the scrolling window itself. There is no difference + * between horizontal or vertical scrolling. + * + * This event should be posted whenever scroll action is completed, including + * when it is scrolled by scroll bars, mouse wheel, or keyboard navigations. + * + * example: + * hwnd == window that is scrolling + * idObject == OBJID_CLIENT + * idChild == CHILDID_SELF + */ + ObjectContentScrolled = 0x8015, + + SystemArrangmentPreview = 0x8016, + + ObjectEnd = 0x80FF, + + AiaStart = 0xA000, + AiaEnd = 0xAFFF + } + + public enum ObjectIdentifier : uint + { + Window = 0x00000000, + SystemMenu = 0xFFFFFFFF, + TitleBar = 0xFFFFFFFE, + Menu = 0xFFFFFFFD, + Client = 0xFFFFFFFC, + VerticalScroll = 0xFFFFFFFB, + HorizontalScroll = 0xFFFFFFFA, + SizeGrip = 0xFFFFFFF9, + Caret = 0xFFFFFFF8, + Cursor = 0xFFFFFFF7, + Alert = 0xFFFFFFF6, + Sound = 0xFFFFFFF5 + } + + public enum ChildIdentifier + { + Self = 0 + } + + [Flags] + public enum SetWinEventHookFlags : uint + { + OutOfContext = 0x0000, // Events are ASYNC + SkipOwnThread = 0x0001, // Don't call back for events on installer's thread + SkipOwnProcess = 0x0002, // Don't call back for events on installer's process + InContext = 0x0004, // Events are SYNC, this causes your dll to be injected into every process + } + + public delegate void WinEventDelegate(IntPtr hWinEventHook, Event eventType, IntPtr hwnd, ObjectIdentifier idObject, int idChild, uint dwEventThread, uint dwmsEventTime); + + [DllImport("user32.dll")] + public static extern IntPtr SetWinEventHook(Event eventMin, Event eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, SetWinEventHookFlags dwFlags); + + [DllImport("user32.dll")] + public static extern bool UnhookWinEvent(IntPtr hWinEventHook); + } +} \ No newline at end of file diff --git a/WinHook.cs b/WinHook.cs new file mode 100644 index 0000000..75eace3 --- /dev/null +++ b/WinHook.cs @@ -0,0 +1,39 @@ +using System; +using System.Runtime.InteropServices; + +namespace Common.Native +{ + public class WinHook + { + public enum HookType + { + WH_JOURNALRECORD = 0, + WH_JOURNALPLAYBACK = 1, + WH_KEYBOARD = 2, + WH_GETMESSAGE = 3, + WH_CALLWNDPROC = 4, + WH_CBT = 5, + WH_SYSMSGFILTER = 6, + WH_MOUSE = 7, + WH_HARDWARE = 8, + WH_DEBUG = 9, + WH_SHELL = 10, + WH_FOREGROUNDIDLE = 11, + WH_CALLWNDPROCRET = 12, + WH_KEYBOARD_LL = 13, + WH_MOUSE_LL = 14 + } + + public delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, uint dwThreadId); + + [DllImport("user32.dll")] + public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool UnhookWindowsHookEx(IntPtr hhk); + } +}