How to detect USB device insertion event in Windows 8? - windows

I usually detect USB device insertion and removal event by monitor WM_DEVICECHANGE message.
In Windows XP, when USB insertion event happened, message is below:
WM_DEVICECHANGE
WParam = DBT_DEVICEARRIVAL, devType = DBT_DEVTYP_DEVICEINTERFACE
WM_DEVICECHANGE
WParam = DBT_DEVICEARRIVAL, devType = DBT_DEVTYP_VOLUME
But now I am using Windows 8.1, for the same event, message is below:
WM_DEVICECHANGE
WParam = **DBT_DEVICEARRIVAL**, devType = DBT_DEVTYP_DEVICEINTERFACE
WM_DEVICECHANGE
WParam = **DBT_DEVICEREMOVECOMPLETE**, devType = DBT_DEVTYP_DEVICEINTERFACE
WM_DEVICECHANGE
WParam = DBT_DEVICEARRIVAL, devType = DBT_DEVTYP_DEVICEINTERFACE
WM_DEVICECHANGE
WParam = DBT_DEVICEARRIVAL, devType = DBT_DEVTYP_VOLUME
My question is:
Why the first 2 messages come here?
How can I detect USB device insertion event in Windows 8?

The first two messages, DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE are generated by the system when a USB device is attached to and removed from it. By listening for these messages, your code can have 'plug-and-play' functionality: when a new devices is attached, do something; when it's removed do something else... I don't know that Win 8 is any different from previous versions, but there are several example of how to go about detecting USB 'device change' events given here, here and here.
My solution is as follows, but essentially you cannot get away from listening for WM_DEVICECHANGE messages and for that you need a WinForms component wherein you override WndProc(). The MSDN article on "Device Management" is here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363224%28v=vs.85%29.aspx
As you'll see, its all unmanaged code.
#pragma once
#include <Windows.h> // Declares required datatypes.
#include <Dbt.h> // Required for WM_DEVICECHANGE messages.
#include <initguid.h> // Required for DEFINE_GUID definition (see below).
namespace USBComms
{
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Windows;
using namespace System::Windows::Forms;
// This function is required for receieving WM_DEVICECHANGE messages.
// Note: name is remapped "RegisterDeviceNotificationUM"
[DllImport("user32.dll" , CharSet = CharSet::Unicode, EntryPoint="RegisterDeviceNotification")]
extern "C" HDEVNOTIFY WINAPI RegisterDeviceNotificationUM(
HANDLE hRecipient,
LPVOID NotificationFilter,
DWORD Flags);
// Generic guid for usb devices (see e.g. http://msdn.microsoft.com/en-us/library/windows/hardware/ff545972%28v=vs.85%29.aspx).
// Note: GUIDs are device and OS specific and may require modification. Using the wrong guid will cause notification to fail.
// You may have to tinker with your device to find the appropriate GUID. "hid.dll" has a function `HidD_GetHidGuid' that returns
// "the device interfaceGUID for HIDClass devices" (see http://msdn.microsoft.com/en-us/library/windows/hardware/ff538924%28v=vs.85%29.aspx).
// However, testing revealed it does not always return a useful value. The GUID_DEVINTERFACE_USB_DEVICE value, defined as
// {A5DCBF10-6530-11D2-901F-00C04FB951ED}, has worked with cell phones, thumb drives, etc. For more info, see e.g.
// http://msdn.microsoft.com/en-us/library/windows/hardware/ff553426%28v=vs.85%29.aspx.
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
/// <summary>
/// Declare a delegate for the notification event handler.
/// </summary>
/// <param name="sender">The object where the event handler is attached.</param>
/// <param name="e">The event data.</param>
public delegate void NotificationEventHandler(Object^ sender, EventArgs^ e);
/// <summary>
/// Class that generetaes USB Device Change notification events.
/// </summary>
/// <remarks>
/// A Form is not necessary. Any type wherein you can override WndProc() can be used.
/// </remarks>
public ref class EventNotifier : public Control
{
private:
/// <summary>
/// Raises the NotificationEvent.
/// </summary>
/// <param name="e">The event data.</param>
void RaiseNotificationEvent(EventArgs^ e) {
NotificationEvent(this, e);
}
protected:
/// <summary>
/// Overrides the base class WndProc method.
/// </summary>
/// <param name="message">The Windows Message to process. </param>
/// <remarks>
/// This method receives Windows Messages (WM_xxxxxxxxxx) and
/// raises our NotificationEvent as appropriate. Here you should
/// add any message filtering (e.g. for the WM_DEVICECHANGE) and
/// preprocessing before raising the event (or not).
/// </remarks>
virtual void WndProc(Message% message) override {
if(message.Msg == WM_DEVICECHANGE)
{
RaiseNotificationEvent(EventArgs::Empty);
}
__super::WndProc(message);
}
public:
/// <summary>
/// Creates a new instance of the EventNotifier class.
/// </summary>
EventNotifier(void) {
RequestNotifications(this->Handle); // Register ourselves as the Windows Message processor.
}
/// <summary>
/// Registers an object, identified by the handle, for
/// Windows WM_DEVICECHANGE messages.
/// </summary>
/// <param name="handle">The object's handle.</param>
bool RequestNotifications(IntPtr handle) {
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_reserved = 0;
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
return RegisterDeviceNotificationUM((HANDLE)handle, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE) != NULL;
}
/// <summary>
/// Defines the notification event.
/// </summary>
virtual event NotificationEventHandler^ NotificationEvent;
};
}
Then, in the 'receiver' (the object that subscribes to and consumes our NotificationEvent, all you have to do is:
void Receiver::SomeFunction(void)
{
USBComms::EventNotifier usb = gcnew USBComms::EventNotifier();
usb->NotificationEvent += gcnew USBComms::NotificationEventHandler(this, &Receiver::USBEvent);
}
void Receiver::USBEvent(Object^ sender, EventArgs^ e)
{
// Handle the event notification as appropriate.
}

Related

RmGetList() API fails when file is locked using Ez file locker but works with another file locking utility

I am experiencing a strange issue with the Restart Manager API: RmGetlist().
To simulate a file locking scenario, i am making use of the following 3rd party file locking utilities:
Ez file locker
-http://www.xoslab.com/efl.html -
File locker
http://www.jensscheffler.de/filelocker
The strange issue here is that, both these utilities lock a certain file, however, RMGetList() fails with an Access denied error(5) with the first file locking utility(Ez File lock) whereas it works with the second file locking utility.
What could possibly be wrong here? Why would RmGetList() fail with one file locking utility but work with another?
Below is the code that is being used:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.IO;
using System.Windows.Forms;
namespace RMSession
{
class Program
{
public static void GetProcessesUsingFiles(string[] filePaths)
{
uint sessionHandle;
int error = NativeMethods.RmStartSession(out sessionHandle, 0, Guid.NewGuid().ToString("N"));
if (error == 0)
{
try
{
error = NativeMethods.RmRegisterResources(sessionHandle, (uint)filePaths.Length, filePaths, 0, null, 0, null);
if (error == 0)
{
RM_PROCESS_INFO[] processInfo = null;
uint pnProcInfoNeeded = 0, pnProcInfo = 0, lpdwRebootReasons = RmRebootReasonNone;
error = NativeMethods.RmGetList(sessionHandle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
while (error == ERROR_MORE_DATA)
{
processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
pnProcInfo = (uint)processInfo.Length;
error = NativeMethods.RmGetList(sessionHandle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
}
if (error == 0 && processInfo != null)
{
for (var i = 0; i < pnProcInfo; i++)
{
RM_PROCESS_INFO procInfo = processInfo[i];
Process proc = null;
try
{
proc = Process.GetProcessById(procInfo.Process.dwProcessId);
}
catch (ArgumentException)
{
// Eat exceptions for processes which are no longer running.
}
if (proc != null)
{
//yield return proc;
}
}
}
}
}
finally
{
NativeMethods.RmEndSession(sessionHandle);
}
}
}
private const int RmRebootReasonNone = 0;
private const int CCH_RM_MAX_APP_NAME = 255;
private const int CCH_RM_MAX_SVC_NAME = 63;
private const int ERROR_MORE_DATA = 234;
[StructLayout(LayoutKind.Sequential)]
private struct RM_UNIQUE_PROCESS
{
public int dwProcessId;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct RM_PROCESS_INFO
{
public RM_UNIQUE_PROCESS Process;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
public string strAppName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
public string strServiceShortName;
public RM_APP_TYPE ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)]
public bool bRestartable;
}
private enum RM_APP_TYPE
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[SuppressUnmanagedCodeSecurity]
private static class NativeMethods
{
/// <summary>
/// Starts a new Restart Manager session.
/// </summary>
/// <param name="pSessionHandle">A pointer to the handle of a Restart Manager session. The session handle can be passed in subsequent calls to the Restart Manager API.</param>
/// <param name="dwSessionFlags">Reserved must be 0.</param>
/// <param name="strSessionKey">A null-terminated string that contains the session key to the new session. A GUID will work nicely.</param>
/// <returns>Error code. 0 is successful.</returns>
[DllImport("RSTRTMGR.DLL", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)]
public static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
/// <summary>
/// Ends the Restart Manager session.
/// </summary>
/// <param name="pSessionHandle">A handle to an existing Restart Manager session.</param>
/// <returns>Error code. 0 is successful.</returns>
[DllImport("RSTRTMGR.DLL")]
public static extern int RmEndSession(uint pSessionHandle);
/// <summary>
/// Registers resources to a Restart Manager session.
/// </summary>
/// <param name="pSessionHandle">A handle to an existing Restart Manager session.</param>
/// <param name="nFiles">The number of files being registered.</param>
/// <param name="rgsFilenames">An array of strings of full filename paths.</param>
/// <param name="nApplications">The number of processes being registered.</param>
/// <param name="rgApplications">An array of RM_UNIQUE_PROCESS structures. </param>
/// <param name="nServices">The number of services to be registered.</param>
/// <param name="rgsServiceNames">An array of null-terminated strings of service short names.</param>
/// <returns>Error code. 0 is successful.</returns>
[DllImport("RSTRTMGR.DLL", CharSet = CharSet.Unicode)]
public static extern int RmRegisterResources(uint pSessionHandle, uint nFiles, string[] rgsFilenames, uint nApplications, [In] RM_UNIQUE_PROCESS[] rgApplications, uint nServices, string[] rgsServiceNames);
/// <summary>
/// Gets a list of all applications and services that are currently using resources that have been registered with the Restart Manager session.
/// </summary>
/// <param name="dwSessionHandle">A handle to an existing Restart Manager session.</param>
/// <param name="pnProcInfoNeeded">A pointer to an array size necessary to receive RM_PROCESS_INFO structures</param>
/// <param name="pnProcInfo">A pointer to the total number of RM_PROCESS_INFO structures in an array and number of structures filled.</param>
/// <param name="rgAffectedApps">An array of RM_PROCESS_INFO structures that list the applications and services using resources that have been registered with the session.</param>
/// <param name="lpdwRebootReasons">Pointer to location that receives a value of the RM_REBOOT_REASON enumeration that describes the reason a system restart is needed.</param>
/// <returns>Error code. 0 is successful.</returns>
[DllImport("RSTRTMGR.DLL")]
public static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded, ref uint pnProcInfo, [In, Out] RM_PROCESS_INFO[] rgAffectedApps, ref uint lpdwRebootReasons);
}
static void Main(string[] args)
{
Console.WriteLine("Starting...");
string[] file1 = new string[1];
MessageBox.Show("Debug C#");
file1[0] = #"C:\ProcessMonitor.zip";
//DirectoryInfo dirInfo = new DirectoryInfo(folder);
GetProcessesUsingFiles(file1);
Console.WriteLine("End");``
}
}
}
Easy File Locker is "locking" the file only in the informal sense of the word, i.e., it protects the files from access, but it does not do so by obtaining a lock on the file. Instead, it uses a lower-level technology (a file system filter driver) that is broadly similar to the way that anti-virus software protects its files from any unauthorized access. The Restart Manager API is not intended to, and does not, deal with this sort of scenario.
Your application almost certainly doesn't need to deal with this sort of scenario either. This means that Easy File Locker is not an appropriate tool for your particular needs; throw it away.
Why would RmGetList() fail with one file locking utility but work with
another?
To answer this we need to understand how RmGetList works internally. In your case, we provide it with a filename, and its output is an array of RM_PROCESS_INFO structures. In order to build this array, Windows must determine which processes are using the file. But how does Windows do this?
The function ZwQueryInformationFile (exported by ntdll.dll) can return a lot of information about a file. One of the options in the FILE_INFORMATION_CLASS enumeration is
FileProcessIdsUsingFileInformation
A FILE_PROCESS_IDS_USING_FILE_INFORMATION structure. This value is
reserved for system use. This value is available starting with Windows
Vista.
and in wdm.h (which is a well known file from the windows WDK) we find
typedef struct _FILE_PROCESS_IDS_USING_FILE_INFORMATION {
ULONG NumberOfProcessIdsInList;
ULONG_PTR ProcessIdList[1];
} FILE_PROCESS_IDS_USING_FILE_INFORMATION, *PFILE_PROCESS_IDS_USING_FILE_INFORMATION;
so this option is exactly which we need!
The algorithm goes like this:
open the file with FILE_READ_ATTRIBUTES access (which is sufficient for this information class)
call ZwQueryInformationFile(..,FileProcessIdsUsingFileInformation);
if NumberOfProcessIdsInList != 0 walk the ProcessIdList
open process by ProcessId, query it with ProcessStartTime (see
GetProcessTimes) and other properties to fill
RM_PROCESS_INFO
So now that we know how that works, let's look at the two utilities you're using.
The second is a very simple app, which provides source code. All it does is to call CreateFile with dwShareMode = 0. That acquires an exclusive lock on the file, ensuring that any attempt to open the file with a dwDesiredAccess
that contains FILE_READ_DATA or FILE_WRITE_DATA or DELETE will fail with ERROR_SHARING_VIOLATION. It does not, however, prevent us from opening the file with dwDesiredAccess = FILE_READ_ATTRIBUTES, so the call to RmGetList() will still work properly.
But the first tool, Easy File Locker from XOSLAB, is using a minifilter driver (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\xlkfs) to restrict access to the file. This driver returns STATUS_ACCESS_DENIED (which is converted to the Win32 ERROR_ACCESS_DENIED) for any attempt to open the file. Because of this, you get an error ERROR_ACCESS_DENIED when RmGetList attempts to open the file at step (1) and (since the API has no idea what to do about this) this error code is returned to you.
That's all there is to it. The tool simply isn't doing what you were expecting it to.

'using System.Windows.Interop;' where is it?

I saw this code for shadows around borderless windows but here i my problem. using System.Windows.Interop; is underlined and i cant find it in references. Also in public static void DropShadowToWindow(Window window) this Window is underlined so i guess its linked to Interop...
using System.Drawing.Printing;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
class DwmDropShadow
{
[DllImport("dwmapi.dll", PreserveSig = true)]
private static extern int DwmSetWindowAttribute(
IntPtr hwnd, int attr, ref int attrValue, int attrSize
);
[DllImport("dwmapi.dll")]
private static extern int DwmExtendFrameIntoClientArea(
IntPtr hWnd, ref Margins pMarInset
);
/// <summary>
/// Drops a standard shadow to a WPF Window, even a borderess window.
/// Only works with DWM (Vista and Seven).
///
/// This is much more efficient than setting AllowsTransparency to true
/// and using the DropShadow effect, as AllowsTransparency will turn off
/// acceleration for all the windows. (This is a huge performance issue.)
/// </summary>
/// <param name="window">Window to which the shadow will be applied</param>
public static void DropShadowToWindow(Window window)
{
if (!DropShadow(window))
{
window.SourceInitialized +=
new EventHandler(window_SourceInitialized);
}
}
private static void window_SourceInitialized(object sender, EventArgs e)
{
Window window = (Window)sender;
DropShadow(window);
window.SourceInitialized -= new EventHandler(window_SourceInitialized);
}
/// <summary>
/// The actual method that makes API calls to drop the shadow to the window
/// </summary>
/// <param name="window">Window to which the shadow will be applied</param>
/// <returns>True if the method succeeded, false if not</returns>
private static bool DropShadow(Window window)
{
try
{
WindowInteropHelper helper = new WindowInteropHelper(window);
int val = 2;
int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4);
if (ret1 == 0)
{
Margins m = new Margins {
Bottom = 0, Left = 0, Right = 0, Top = 0
};
int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m);
return ret2 == 0;
}
else
{
return false;
}
}
catch (Exception ex)
{
// Probably dwmapi.dll not found (incompatible OS)
return false;
}
}
}
It's in WindowsBase.DLL, which was introduced in .NET Framework 3.0. It is located in c:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll
Just in case someone comes here to search for System.Windows.Interop.CompositionMode: it didn't make it to the final .Net 4.5 version.
See http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2644120-bring-back-the-hwndhost-isredirected-and-compositi?page=2&per_page=20

How to implement per file thumbnail for file association?

I have a file type that I want to associate with my program. I could make every file of that type have the same standard icon like how all HTML files look the same or all txt files, but what I want to do is have every file show a preview of itself for its thumbnail, more like how jpg, bmp, and png show a thumbnail of that particular image file.
I mostly work in C# but I know something like this may require a little (or a lot) of C++ to do what I want to do and I'm ok with that if need be. I don't know where to begin as I've never tried this before. A little googling says that a COM object will do it, but I need more to go on than that.
EDIT
Here's what I have so far:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices.ComTypes;
namespace APKIconHandler {
[ComImport()]
[Guid("000214fa-0000-0000-c000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
//http://msdn.microsoft.com/en-us/library/windows/desktop/bb761852(v=vs.85).aspx
interface IExtractIcon {
/// <summary>
/// Gets the location and index of an icon.
/// </summary>
/// <param name="uFlags">One or more of the following values. This parameter can also be NULL.use GIL_ Consts</param>
/// <param name="szIconFile">A pointer to a buffer that receives the icon location. The icon location is a null-terminated string that identifies the file that contains the icon.</param>
/// <param name="cchMax">The size of the buffer, in characters, pointed to by pszIconFile.</param>
/// <param name="piIndex">A pointer to an int that receives the index of the icon in the file pointed to by pszIconFile.</param>
/// <param name="pwFlags">A pointer to a UINT value that receives zero or a combination of the following value</param>
/// <returns></returns>
///
[PreserveSig]
int GetIconLocation(IExtractIconuFlags uFlags, [Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 2)] StringBuilder szIconFile, int cchMax, out int piIndex, out IExtractIconpwFlags pwFlags);
/// <summary>
/// Extracts an icon image from the specified location.
/// </summary>
/// <param name="pszFile">A pointer to a null-terminated string that specifies the icon location.</param>
/// <param name="nIconIndex">The index of the icon in the file pointed to by pszFile.</param>
/// <param name="phiconLarge">A pointer to an HICON value that receives the handle to the large icon. This parameter may be NULL.</param>
/// <param name="phiconSmall">A pointer to an HICON value that receives the handle to the small icon. This parameter may be NULL.</param>
/// <param name="nIconSize">The desired size of the icon, in pixels. The low word contains the size of the large icon, and the high word contains the size of the small icon. The size specified can be the width or height. The width of an icon always equals its height.</param>
/// <returns>
/// Returns S_OK if the function extracted the icon, or S_FALSE if the calling application should extract the icon.
/// </returns>
[PreserveSig]
int Extract([MarshalAs(UnmanagedType.LPWStr)] string pszFile, uint nIconIndex, out IntPtr phiconLarge, out IntPtr phiconSmall, uint nIconSize);
}
[Flags()]
public enum IExtractIconuFlags:uint
{
GIL_ASYNC=0x0020,
GIL_DEFAULTICON =0x0040,
GIL_FORSHELL =0x0002,
GIL_FORSHORTCUT =0x0080,
GIL_OPENICON = 0x0001,
GIL_CHECKSHIELD = 0x0200
}
[Flags()]
public enum IExtractIconpwFlags : uint
{
GIL_DONTCACHE = 0x0010,
GIL_NOTFILENAME = 0x0008,
GIL_PERCLASS = 0x0004,
GIL_PERINSTANCE = 0x0002,
GIL_SIMULATEDOC = 0x0001,
GIL_SHIELD = 0x0200,//Windows Vista only
GIL_FORCENOSHIELD = 0x0400//Windows Vista only
}
[Flags]
public enum IconHandlerReturnFlags {
SimulateDoc = 0x1,
PerInstance = 0x2,
PerClass = 0x4,
NotFilename = 0x8,
DontCache = 0x10
}
public class APKHandler : IExtractIcon, IPersistFile {
private const int S_OK = 0;
private const int S_FALSE = 1;
[ComRegisterFunctionAttribute]
public void DllRegisterDll() { }
public void GetClassID(out Guid g) {
g = new Guid("405a310a-b439-49b9-894a-cc55ffc6e91d");
}
public void GetCurFile(out String str) {
str = "CurFile";
}
public int IsDirty() {
return S_OK;
}
public void Load(string pszFileName, int dwMode) {
File.AppendAllText(#"C:\ApkHandler.txt", "Load :" + pszFileName + " , " + dwMode.ToString() + Environment.NewLine);
}
public void Save(string pszFileName, bool save) {
File.AppendAllText(#"C:\ApkHandler.txt", "Save :" + pszFileName + " , " + save + Environment.NewLine);
}
public void SaveCompleted(string pszFileName) {
File.AppendAllText(#"C:\ApkHandler.txt", "SaveCompleted :" + pszFileName + Environment.NewLine);
}
public int GetIconLocation(IExtractIconuFlags uFlags, StringBuilder szIconFile, int cchMax, out int piIndex, out IExtractIconpwFlags pwFlags)//Using IExtractIcon and IPersistFile.Load
{
piIndex = 0;
pwFlags = 0;
try {
pwFlags = IExtractIconpwFlags.GIL_PERCLASS | IExtractIconpwFlags.GIL_DONTCACHE | IExtractIconpwFlags.GIL_NOTFILENAME;
File.AppendAllText(#"C:\ApkHandler.txt", "GetIconLocation...");
return S_OK;
} catch (Exception e) {
File.AppendAllText(#"C:\ApkHandler.txt", "GetIconLocation " + e.Message);
return S_FALSE;
}
}
public int Extract(string pszFile, uint nIconIndex, out IntPtr phiconLarge, out IntPtr phiconSmall, uint nIconSize)//Using IExtractIcon
{
File.AppendAllText(#"C:\ApkHandler.txt", "Extract...");
phiconSmall = phiconLarge = IntPtr.Zero;
return S_OK;
}
}
}
I have edited my registry as the bottom of this page instructs (thanks arx).
I've toyed and tweaked with this over and over and have yet to had any of my functions called (as indicated by ApkHandler.txt never appearing). I disabled UAC so I don't think there's any permissions issues with creating the file in the root, I do it all the time while debugging. I'm trying not to get frustrated but this is really getting under my skin.

How to get the Monitor Screen Resolution from a hWnd?

How to get the monitor screen resolution from a hWnd?
I am using a hWnd because the window could be located on any one of multiple monitors.
i.e. the hWnd top/left coordinate is on a Monitor that has a Screen Resolution of 800 x 600.
I program in a language called PL/B and it allows calling Windows API.
What Window APIs can be used?
Here's a C++ code example that works for me:
HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
MONITORINFO info;
info.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(monitor, &info);
int monitor_width = info.rcMonitor.right - info.rcMonitor.left;
int monitor_height = info.rcMonitor.bottom - info.rcMonitor.top;
The user32 function MonitorFromWindow allows you to pass in an hwnd, and returns a handle to the monitor it's on (or a default - see the linked MSDN article for details). With that you can call GetMonitorInfo to retrieve a MONITORINFO struct which contains a RECT detailing its resolution.
See the Multiple Screens Reference section of MSDN for more details.
I'd add example code but I don't know the language you referenced, and I don't know how useful C# example code would be to you. If you think it'll help, let me know and I'll code up something real quick.
There is GetSystemMetrics too, check it out on msdn
Here's some C# code that gets the resolution (in DPI) via P/Invoke:
public static void GetWindowDpi(IntPtr hwnd, out int dpiX, out int dpiY)
{
var handle = MonitorFromWindow(hwnd, MonitorFlag.MONITOR_DEFAULTTOPRIMARY);
GetDpiForMonitor(handle, MonitorDpiType.MDT_EFFECTIVE_DPI, out dpiX, out dpiY);
}
/// <summary>
/// Determines the function's return value if the window does not intersect any display monitor.
/// </summary>
[SuppressMessage("ReSharper", "IdentifierTypo")]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private enum MonitorFlag : uint
{
/// <summary>Returns NULL.</summary>
MONITOR_DEFAULTTONULL = 0,
/// <summary>Returns a handle to the primary display monitor.</summary>
MONITOR_DEFAULTTOPRIMARY = 1,
/// <summary>Returns a handle to the display monitor that is nearest to the window.</summary>
MONITOR_DEFAULTTONEAREST = 2
}
[DllImport("user32.dll")]
private static extern IntPtr MonitorFromWindow(IntPtr hwnd, MonitorFlag flag);
[SuppressMessage("ReSharper", "IdentifierTypo")]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private enum MonitorDpiType
{
/// <summary>
/// The effective DPI.
/// This value should be used when determining the correct scale factor for scaling UI elements.
/// This incorporates the scale factor set by the user for this specific display.
/// </summary>
MDT_EFFECTIVE_DPI = 0,
/// <summary>
/// The angular DPI.
/// This DPI ensures rendering at a compliant angular resolution on the screen.
/// This does not include the scale factor set by the user for this specific display.
/// </summary>
MDT_ANGULAR_DPI = 1,
/// <summary>
/// The raw DPI.
/// This value is the linear DPI of the screen as measured on the screen itself.
/// Use this value when you want to read the pixel density and not the recommended scaling setting.
/// This does not include the scale factor set by the user for this specific display and is not guaranteed to be a supported DPI value.
/// </summary>
MDT_RAW_DPI = 2
}
[DllImport("user32.dll")]
private static extern bool GetDpiForMonitor(IntPtr hwnd, MonitorDpiType dpiType, out int dpiX, out int dpiY);
For Monitors with hight resolution 2K 4K > 1920px
void GetDesktopResolution(int* horizontal, int* vertical)
{
HDC hScreenDC = GetDC(GetDesktopWindow());
int width = GetDeviceCaps(hScreenDC, HORZRES);
int height = GetDeviceCaps(hScreenDC, VERTRES);
ReleaseDC(GetDesktopWindow(), hScreenDC);
RECT desktop;
const HWND hDesktop = GetDesktopWindow();
GetWindowRect(hDesktop, &desktop);
if (width > 2000)
{
const POINT ptZero = { 0, 0 };
HMONITOR mon = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
DEVICE_SCALE_FACTOR f;// vers < win 8 = GetScaleFactorForDevice(DEVICE_PRIMARY);
GetScaleFactorForMonitor(mon,&f);
if (f > 110)
{
*horizontal = width * ((f+10) / 100.0);
*vertical = height * ((f+10) / 100.0);
}
else
{
*horizontal = width;
*vertical = height;
}
}
else
{
*horizontal = desktop.right;
*vertical = desktop.bottom;
}
}
RECT windowsize; // get the height and width of the screen
GetClientRect(hwnd, &windowsize);
int srcheight = windowsize.bottom;
int srcwidth = windowsize.right;

How to make a keyboard hook global across processes

I am trying to create a utility keystroke app so i can do things like kill a preprogrammed process or launch something. I am thinking I should hold cmd in any app, then type in a 4 digit command key so I can launch or kill anything quickly while programming, debugging watching a video, etc.
I figured out how to get the keyboard callback, but for whatever reason once I click into another app, my keystroke util receives no more keys. Even if I click back to my console window or msvc, I will not receive any input. This is unless its global, so how do I set the hook to be global?
My code is
int main()
{
hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, GetModuleHandle(0), 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hook);
}
In order for your keyboard hook to be accessible from all processes, it has to be placed in a DLL which will then be loaded into each process' address space. One important thing to keep in mind is that since each instance of the DLL is loaded in a separate process, each will have its own copy of any global variables in the DLL. If you need to share data between these instances, the simplest way is to create a shared data segment in the DLL. The following code is from an RSI monitoring program I wrote.
//
// some data will be shared across all
// instances of the DLL
//
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
int iKeyCount = 0;
HHOOK hKeyboardHook = 0;
HHOOK hMouseHook = 0;
#pragma data_seg()
//
// instance specific data
//
HMODULE hInstance = 0;
//
// DLL load/unload entry point
//
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD dwReason,
LPVOID lpReserved)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH :
hInstance = (HINSTANCE) hModule;
break;
case DLL_THREAD_ATTACH :
break;
case DLL_THREAD_DETACH :
break;
case DLL_PROCESS_DETACH :
break;
}
return TRUE;
}
//
// keyboard hook
//
LRESULT CALLBACK KeyboardProc(int code, // hook code
WPARAM wParam, // virtual-key code
LPARAM lParam) // keystroke-message information
{
if ((lParam & 0x80000000) != 0)
{
++iKeyCount;
}
return CallNextHookEx(hKeyboardHook, code, wParam, lParam);
}
//
// mouse hook
//
LRESULT CALLBACK MouseProc(int code, // hook code
WPARAM wParam, // message identifier
LPARAM lParam) // mouse coordinates
{
switch (wParam)
{
case WM_LBUTTONDOWN :
case WM_MBUTTONDOWN :
case WM_RBUTTONDOWN :
case WM_LBUTTONDBLCLK :
case WM_MBUTTONDBLCLK :
case WM_RBUTTONDBLCLK :
++iKeyCount;
break;
}
return CallNextHookEx(hMouseHook, code, wParam, lParam);
}
//
// install keyboard/mouse hooks
//
void KBM_API InstallHooks(void)
{
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hInstance, 0);
hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseProc, hInstance, 0);
}
//
// remove keyboard/mouse hooks
//
void KBM_API RemoveHooks(void)
{
UnhookWindowsHookEx(hKeyboardHook);
UnhookWindowsHookEx(hMouseHook);
hKeyboardHook = hMouseHook = 0;
}
//
// retrieve number of keystrokes
//
int KBM_API FetchKeyCount(bool bClear)
{
int kc = iKeyCount;
if (bClear)
iKeyCount = 0;
return kc;
}
Avoid codeproject samples. ( plenty of bugs, bad copies of MSDN )
See the tons of complete samples on MSDN on hooks (MSDN, SDK, KB, etc)
And you don't need any DLL, just use LL hooks
Re-read the introduction to hooks in the Win32 guide. (A good place to start is here.)
Specifically, if you want to hook events in other processes, you need to place your callback in a DLL, which is injected into other processes by Win32. From the code you've supplied, I can't tell if KeyboardProc is in a DLL or in the main program. It doesn't look like it, considering the HINSTANCE you're passing.
For Glopbal Keyboard Hook just for Hot Key's, then Register hotkey is the best (its done by Microsoft):
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646309(v=vs.85).aspx
Download the sample winform app and see for yourself:
https://code.msdn.microsoft.com/CppRegisterHotkey-7bd897a8 C++
https://code.msdn.microsoft.com/CSRegisterHotkey-e3f5061e C#
https://code.msdn.microsoft.com/VBRegisterHotkey-50af3179 VB.Net
Winform App:
/****************************** Module Header ******************************\
* Module Name: MainForm.cs
* Project: CSRegisterHotkey
* Copyright (c) Microsoft Corporation.
*
* This is the main form of this application. It is used to initialize the UI
* and handle the events.
*
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace CSRegisterHotkey
{
public partial class MainForm : Form
{
HotKeyRegister hotKeyToRegister = null;
Keys registerKey = Keys.None;
KeyModifiers registerModifiers = KeyModifiers.None;
public MainForm()
{
InitializeComponent();
}
/// <summary>
/// Handle the KeyDown of tbHotKey. In this event handler, check the pressed keys.
/// The keys that must be pressed in combination with the key Ctrl, Shift or Alt,
/// like Ctrl+Alt+T. The method HotKeyRegister.GetModifiers could check whether
/// "T" is pressed.
/// </summary>
private void tbHotKey_KeyDown(object sender, KeyEventArgs e)
{
// The key event should not be sent to the underlying control.
e.SuppressKeyPress = true;
// Check whether the modifier keys are pressed.
if (e.Modifiers != Keys.None)
{
Keys key = Keys.None;
KeyModifiers modifiers = HotKeyRegister.GetModifiers(e.KeyData, out key);
// If the pressed key is valid...
if (key != Keys.None)
{
this.registerKey = key;
this.registerModifiers = modifiers;
// Display the pressed key in the textbox.
tbHotKey.Text = string.Format("{0}+{1}",
this.registerModifiers, this.registerKey);
// Enable the button.
btnRegister.Enabled = true;
}
}
}
/// <summary>
/// Handle the Click event of btnRegister.
/// </summary>
private void btnRegister_Click(object sender, EventArgs e)
{
try
{
// Register the hotkey.
hotKeyToRegister = new HotKeyRegister(this.Handle, 100,
this.registerModifiers, this.registerKey);
// Register the HotKeyPressed event.
hotKeyToRegister.HotKeyPressed += new EventHandler(HotKeyPressed);
// Update the UI.
btnRegister.Enabled = false;
tbHotKey.Enabled = false;
btnUnregister.Enabled = true;
}
catch (ArgumentException argumentException)
{
MessageBox.Show(argumentException.Message);
}
catch (ApplicationException applicationException)
{
MessageBox.Show(applicationException.Message);
}
}
/// <summary>
/// Show a message box if the HotKeyPressed event is raised.
/// </summary>
void HotKeyPressed(object sender, EventArgs e)
{
//Here is the magic!!!!!!!!'
//DO SOMETHING COOL!!! Or Just activate this winform
if (this.WindowState == FormWindowState.Minimized)
{
this.WindowState = FormWindowState.Normal;
}
this.Activate();
}
/// <summary>
/// Handle the Click event of btnUnregister.
/// </summary>
private void btnUnregister_Click(object sender, EventArgs e)
{
// Dispose the hotKeyToRegister.
if (hotKeyToRegister != null)
{
hotKeyToRegister.Dispose();
hotKeyToRegister = null;
}
// Update the UI.
tbHotKey.Enabled = true;
btnRegister.Enabled = true;
btnUnregister.Enabled = false;
}
/// <summary>
/// Dispose the hotKeyToRegister when the form is closed.
/// </summary>
protected override void OnClosed(EventArgs e)
{
if (hotKeyToRegister != null)
{
hotKeyToRegister.Dispose();
hotKeyToRegister = null;
}
base.OnClosed(e);
}
}
}
HotKeyRegister.cs
/****************************** Module Header ******************************\
* Module Name: HotKeyRegister.cs
* Project: CSRegisterHotkey
* Copyright (c) Microsoft Corporation.
*
* This class imports the method RegisterHotKey and UnregisterHotKey in
* user32.dll to define or free a system-wide hot key.
*
* The method Application.AddMessageFilter is used to add a message filter to
* monitor Windows messages as they are routed to their destinations. Before a
* message is dispatched, the method PreFilterMessage could handle it. If a
* WM_HOTKEY messages was generated by the hot key that was registered by this
* HotKeyRegister object, then raise a HotKeyPressed event.
*
* This class also supplies a static method GetModifiers to get the modifiers
* and key from the KeyData property of KeyEventArgs.
*
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Security.Permissions;
namespace CSRegisterHotkey
{
public class HotKeyRegister : IMessageFilter, IDisposable
{
/// <summary>
/// Define a system-wide hot key.
/// </summary>
/// <param name="hWnd">
/// A handle to the window that will receive WM_HOTKEY messages generated by the
/// hot key. If this parameter is NULL, WM_HOTKEY messages are posted to the
/// message queue of the calling thread and must be processed in the message loop.
/// </param>
/// <param name="id">
/// The identifier of the hot key. If the hWnd parameter is NULL, then the hot
/// key is associated with the current thread rather than with a particular
/// window.
/// </param>
/// <param name="fsModifiers">
/// The keys that must be pressed in combination with the key specified by the
/// uVirtKey parameter in order to generate the WM_HOTKEY message. The fsModifiers
/// parameter can be a combination of the following values.
/// MOD_ALT 0x0001
/// MOD_CONTROL 0x0002
/// MOD_SHIFT 0x0004
/// MOD_WIN 0x0008
/// </param>
/// <param name="vk">The virtual-key code of the hot key.</param>
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id,
KeyModifiers fsModifiers, Keys vk);
/// <summary>
/// Frees a hot key previously registered by the calling thread.
/// </summary>
/// <param name="hWnd">
/// A handle to the window associated with the hot key to be freed. This parameter
/// should be NULL if the hot key is not associated with a window.
/// </param>
/// <param name="id">
/// The identifier of the hot key to be freed.
/// </param>
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
/// <summary>
/// Get the modifiers and key from the KeyData property of KeyEventArgs.
/// </summary>
/// <param name="keydata">
/// The KeyData property of KeyEventArgs. The KeyData is a key in combination
/// with modifiers.
/// </param>
/// <param name="key">The pressed key.</param>
public static KeyModifiers GetModifiers(Keys keydata, out Keys key)
{
key = keydata;
KeyModifiers modifers = KeyModifiers.None;
// Check whether the keydata contains the CTRL modifier key.
// The value of Keys.Control is 131072.
if ((keydata & Keys.Control) == Keys.Control)
{
modifers |= KeyModifiers.Control;
key = keydata ^ Keys.Control;
}
// Check whether the keydata contains the SHIFT modifier key.
// The value of Keys.Control is 65536.
if ((keydata & Keys.Shift) == Keys.Shift)
{
modifers |= KeyModifiers.Shift;
key = key ^ Keys.Shift;
}
// Check whether the keydata contains the ALT modifier key.
// The value of Keys.Control is 262144.
if ((keydata & Keys.Alt) == Keys.Alt)
{
modifers |= KeyModifiers.Alt;
key = key ^ Keys.Alt;
}
// Check whether a key other than SHIFT, CTRL or ALT (Menu) is pressed.
if (key == Keys.ShiftKey || key == Keys.ControlKey || key == Keys.Menu)
{
key = Keys.None;
}
return modifers;
}
/// <summary>
/// Specify whether this object is disposed.
/// </summary>
bool disposed = false;
/// <summary>
/// This constant could be found in WinUser.h if you installed Windows SDK.
/// Each windows message has an identifier, 0x0312 means that the mesage is
/// a WM_HOTKEY message.
/// </summary>
const int WM_HOTKEY = 0x0312;
/// <summary>
/// A handle to the window that will receive WM_HOTKEY messages generated by the
/// hot key.
/// </summary>
public IntPtr Handle { get; private set; }
/// <summary>
/// A normal application can use any value between 0x0000 and 0xBFFF as the ID
/// but if you are writing a DLL, then you must use GlobalAddAtom to get a
/// unique identifier for your hot key.
/// </summary>
public int ID { get; private set; }
public KeyModifiers Modifiers { get; private set; }
public Keys Key { get; private set; }
/// <summary>
/// Raise an event when the hotkey is pressed.
/// </summary>
public event EventHandler HotKeyPressed;
public HotKeyRegister(IntPtr handle, int id, KeyModifiers modifiers, Keys key)
{
if (key == Keys.None || modifiers == KeyModifiers.None)
{
throw new ArgumentException("The key or modifiers could not be None.");
}
this.Handle = handle;
this.ID = id;
this.Modifiers = modifiers;
this.Key = key;
RegisterHotKey();
// Adds a message filter to monitor Windows messages as they are routed to
// their destinations.
Application.AddMessageFilter(this);
}
/// <summary>
/// Register the hotkey.
/// </summary>
private void RegisterHotKey()
{
bool isKeyRegisterd = RegisterHotKey(Handle, ID, Modifiers, Key);
// If the operation failed, try to unregister the hotkey if the thread
// has registered it before.
if (!isKeyRegisterd)
{
// IntPtr.Zero means the hotkey registered by the thread.
UnregisterHotKey(IntPtr.Zero, ID);
// Try to register the hotkey again.
isKeyRegisterd = RegisterHotKey(Handle, ID, Modifiers, Key);
// If the operation still failed, it means that the hotkey was already
// used in another thread or process.
if (!isKeyRegisterd)
{
throw new ApplicationException("The hotkey is in use");
}
}
}
/// <summary>
/// Filters out a message before it is dispatched.
/// </summary>
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
public bool PreFilterMessage(ref Message m)
{
// The property WParam of Message is typically used to store small pieces
// of information. In this scenario, it stores the ID.
if (m.Msg == WM_HOTKEY
&& m.HWnd == this.Handle
&& m.WParam == (IntPtr)this.ID
&& HotKeyPressed != null)
{
// Raise the HotKeyPressed event if it is an WM_HOTKEY message.
HotKeyPressed(this, EventArgs.Empty);
// True to filter the message and stop it from being dispatched.
return true;
}
// Return false to allow the message to continue to the next filter or
// control.
return false;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Unregister the hotkey.
/// </summary>
protected virtual void Dispose(bool disposing)
{
// Protect from being called multiple times.
if (disposed)
{
return;
}
if (disposing)
{
// Removes a message filter from the message pump of the application.
Application.RemoveMessageFilter(this);
UnregisterHotKey(Handle, ID);
}
disposed = true;
}
}
}
KeyModifiers.cs:
/****************************** Module Header ******************************\
* Module Name: KeyModifiers.cs
* Project: CSRegisterHotkey
* Copyright (c) Microsoft Corporation.
*
* This enum defines the modifiers to generate the WM_HOTKEY message.
* See http://msdn.microsoft.com/en-us/library/ms646309(VS.85).aspx.
*
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
namespace CSRegisterHotkey
{
[Flags]
public enum KeyModifiers
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
// Either WINDOWS key was held down. These keys are labeled with the Windows logo.
// Keyboard shortcuts that involve the WINDOWS key are reserved for use by the
// operating system.
Windows = 8
}
}
I had to mess with global hooks when I was writing a "Picture in Picture" sort of app. This article and sample code helped me out tremendously:
http://www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx

Resources