CopyFileEx Won't write metadata on large file (Delayed Write Failure) - c#

I am using CopyFileEx pinvoke in c# to copy small and large files from a secondary drive to a flash drive. The files in question are MP4 files. The smaller files copy fine however the larger files around 1GB or more, copy the size of the file, but don't include the metadata which makes the MP4 non-playable. What can cause this providing that I've done this right?
After closing the program Windows will tell me there is a delayed write failure.
public class XCopy
{
#region DLL Imports
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName, CopyProgressRoutine lpProgressRoutine, IntPtr lpData, ref int pbCancel, CopyFileFlags dwCopyFlags);
#endregion
#region Delegates
private delegate CopyProgressResult CopyProgressRoutine(long TotalFileSize, long TotalBytesTransferred, long StreamSize,
long StreamBytesTransferred, uint dwStreamNumber, CopyProgressCallbackReason dwCallbackReason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData);
#endregion
#region Fields
private int isCancelled;
private int filePercentCompleted;
private static bool finished = true;
private static bool errorOccurred;
private static License license;
#endregion
#region Properties
/// <summary>
/// Gets the flag indicating that copying has finished.
/// </summary>
public static bool Finished => finished;
/// <summary>
/// Gets the flag indicating that an error occurred while copying.
/// </summary>
public static bool ErrorOccurred => errorOccurred;
#endregion
#region Event Handlers
private event EventHandler<ProgressChangedEventArgs> ProgressChanged;
#endregion
#region Enumerations
/// <summary>
/// Progress result.
/// </summary>
private enum CopyProgressResult : uint
{
PROGRESS_CONTINUE = 0
}
/// <summary>
/// Callback reason.
/// </summary>
private enum CopyProgressCallbackReason : uint
{
CALLBACK_CHUNK_FINISHED = 0x00000000
}
/// <summary>
/// File flags.
/// </summary>
[Flags]
private enum CopyFileFlags : uint
{
COPY_FILE_FAIL_IF_EXISTS = 0x00000001,
COPY_FILE_NO_BUFFERING = 0x00001000,
COPY_FILE_RESTARTABLE = 0x00000002
}
#endregion
#region Constructors
/// <summary>
/// Constructor that is internal.
/// </summary>
private XCopy()
{
license = LicenseManager.Validate(typeof(XCopy), this);
isCancelled = 0;
}
/// <summary>
/// Constructor.
/// </summary>
static XCopy()
{
license = LicenseManager.Validate(typeof(XCopy), IntPtr.Zero);
}
#endregion
#region Internal Members
/// <summary>
/// Copy source file to destination.
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
/// <param name="overwrite"></param>
/// <param name="nobuffering"></param>
/// <param name="handler"></param>
private void CopyInternal(string source, string destination, bool overwrite, bool nobuffering, EventHandler<ProgressChangedEventArgs> handler)
{
errorOccurred = false;
finished = false;
try
{
var copyFileFlags = CopyFileFlags.COPY_FILE_RESTARTABLE;
if (!overwrite)
{
copyFileFlags |= CopyFileFlags.COPY_FILE_FAIL_IF_EXISTS;
}
if (nobuffering)
{
copyFileFlags |= CopyFileFlags.COPY_FILE_NO_BUFFERING;
}
if (handler != null)
{
ProgressChanged += handler;
}
var result = CopyFileEx(source, destination, CopyProgressHandler, IntPtr.Zero, ref isCancelled, copyFileFlags);
if (!result)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
catch (Exception e)
{
LogManager.Log(LogType.ERROR, "Copy: " + e.Message);
if (handler != null)
{
ProgressChanged -= handler;
errorOccurred = true;
}
finished = true;
}
}
/// <summary>
/// Progress handler.
/// </summary>
/// <param name="total"></param>
/// <param name="transferred"></param>
/// <param name="streamSize"></param>
/// <param name="streamByteTrans"></param>
/// <param name="dwStreamNumber"></param>
/// <param name="reason"></param>
/// <param name="hSourceFile"></param>
/// <param name="hDestinationFile"></param>
/// <param name="lpData"></param>
/// <returns></returns>
private CopyProgressResult CopyProgressHandler(long total, long transferred, long streamSize, long streamByteTrans, uint dwStreamNumber,
CopyProgressCallbackReason reason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData)
{
if (reason == CopyProgressCallbackReason.CALLBACK_CHUNK_FINISHED)
{
OnProgressChanged((transferred / (double)total) * 100.0);
}
if (transferred >= total)
{
finished = true;
}
return CopyProgressResult.PROGRESS_CONTINUE;
}
#endregion
#region External Members
/// <summary>
/// Copy using an overwrite and buffer flag.
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
/// <param name="overwrite"></param>
/// <param name="nobuffering"></param>
public static void Copy(string source, string destination, bool overwrite, bool nobuffering)
{
Task.Factory.StartNew(() =>
{
try
{
new XCopy().CopyInternal(source, destination, overwrite, nobuffering, null);
}
catch (AggregateException e)
{
foreach (var error in e.InnerExceptions)
{
LogManager.Log(LogType.DEBUG, error.Message);
}
}
});
}
/// <summary>
/// Copy using an overwrite and buffer flag along with a progress event handler.
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
/// <param name="overwrite"></param>
/// <param name="nobuffering"></param>
/// <param name="handler"></param>
public static void Copy(string source, string destination, bool overwrite, bool nobuffering, EventHandler<ProgressChangedEventArgs> handler)
{
Task.Factory.StartNew(() =>
{
try
{
new XCopy().CopyInternal(source, destination, overwrite, nobuffering, handler);
}
catch (AggregateException e)
{
foreach (var error in e.InnerExceptions)
{
LogManager.Log(LogType.DEBUG, error.Message);
}
}
});
}
/// <summary>
/// Dispose of the license.
/// </summary>
public void Dispose()
{
if (license != null)
{
license.Dispose();
license = null;
}
}
#endregion
#region Events
/// <summary>
/// Handle changing progress.
/// </summary>
/// <param name="percent"></param>
private void OnProgressChanged(double percent)
{
if ((int)percent > filePercentCompleted)
{
filePercentCompleted = (int)percent;
ProgressChanged?.Invoke(this, new ProgressChangedEventArgs(filePercentCompleted, null));
}
}
#endregion
}

Related

EasyHook sample project "RemoteFileMonitor" get a unexpected result

I have downloaded the project 'RemoteFileMonitor' here:
https://github.com/EasyHook/EasyHook-Tutorials/tree/master/Managed/RemoteFileMonitor
This project generate a console log of all files opened by an input process id.
the application run without issue but the log show unexpected result.
I have tested it with different process (notepad included) with the some result:
In short if you open multiple times the some file, the log show it only first time and show more results only for different files.
I need it to monitor in real-time, the file access of an external process, but it is frequently that the process try to open the some file and is important for me this information inside the log.
Here the main part of the original source code:
namespace FileMonitorHook
{
public class InjectionEntryPoint: EasyHook.IEntryPoint
{
/// <summary>
/// Reference to the server interface within FileMonitor
/// </summary>
ServerInterface _server = null;
/// <summary>
/// Message queue of all files accessed
/// </summary>
Queue<string> _messageQueue = new Queue<string>();
/// <summary>
/// EasyHook requires a constructor that matches <paramref name="context"/> and any additional parameters as provided
/// in the original call to <see cref="EasyHook.RemoteHooking.Inject(int, EasyHook.InjectionOptions, string, string, object[])"/>.
///
/// Multiple constructors can exist on the same <see cref="EasyHook.IEntryPoint"/>, providing that each one has a corresponding Run method (e.g. <see cref="Run(EasyHook.RemoteHooking.IContext, string)"/>).
/// </summary>
/// <param name="context">The RemoteHooking context</param>
/// <param name="channelName">The name of the IPC channel</param>
public InjectionEntryPoint(
EasyHook.RemoteHooking.IContext context,
string channelName)
{
// Connect to server object using provided channel name
_server = EasyHook.RemoteHooking.IpcConnectClient<ServerInterface>(channelName);
// If Ping fails then the Run method will be not be called
_server.Ping();
}
/// <summary>
/// The main entry point for our logic once injected within the target process.
/// This is where the hooks will be created, and a loop will be entered until host process exits.
/// EasyHook requires a matching Run method for the constructor
/// </summary>
/// <param name="context">The RemoteHooking context</param>
/// <param name="channelName">The name of the IPC channel</param>
public void Run(
EasyHook.RemoteHooking.IContext context,
string channelName)
{
// Injection is now complete and the server interface is connected
_server.IsInstalled(EasyHook.RemoteHooking.GetCurrentProcessId());
// Install hooks
// CreateFile https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
var createFileHook = EasyHook.LocalHook.Create(
EasyHook.LocalHook.GetProcAddress("kernel32.dll", "CreateFileW"),
new CreateFile_Delegate(CreateFile_Hook),
this);
// ReadFile https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx
var readFileHook = EasyHook.LocalHook.Create(
EasyHook.LocalHook.GetProcAddress("kernel32.dll", "ReadFile"),
new ReadFile_Delegate(ReadFile_Hook),
this);
// WriteFile https://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx
var writeFileHook = EasyHook.LocalHook.Create(
EasyHook.LocalHook.GetProcAddress("kernel32.dll", "WriteFile"),
new WriteFile_Delegate(WriteFile_Hook),
this);
// Activate hooks on all threads except the current thread
createFileHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
readFileHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
writeFileHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
_server.ReportMessage("CreateFile, ReadFile and WriteFile hooks installed");
// Wake up the process (required if using RemoteHooking.CreateAndInject)
EasyHook.RemoteHooking.WakeUpProcess();
try
{
// Loop until FileMonitor closes (i.e. IPC fails)
while (true)
{
System.Threading.Thread.Sleep(500);
string[] queued = null;
lock (_messageQueue)
{
queued = _messageQueue.ToArray();
_messageQueue.Clear();
}
// Send newly monitored file accesses to FileMonitor
if (queued != null && queued.Length > 0)
{
_server.ReportMessages(queued);
}
else
{
_server.Ping();
}
}
}
catch
{
// Ping() or ReportMessages() will raise an exception if host is unreachable
}
// Remove hooks
createFileHook.Dispose();
readFileHook.Dispose();
writeFileHook.Dispose();
// Finalise cleanup of hooks
EasyHook.LocalHook.Release();
}
/// <summary>
/// P/Invoke to determine the filename from a file handle
/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364962(v=vs.85).aspx
/// </summary>
/// <param name="hFile"></param>
/// <param name="lpszFilePath"></param>
/// <param name="cchFilePath"></param>
/// <param name="dwFlags"></param>
/// <returns></returns>
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags);
#region CreateFileW Hook
/// <summary>
/// The CreateFile delegate, this is needed to create a delegate of our hook function <see cref="CreateFile_Hook(string, uint, uint, IntPtr, uint, uint, IntPtr)"/>.
/// </summary>
/// <param name="filename"></param>
/// <param name="desiredAccess"></param>
/// <param name="shareMode"></param>
/// <param name="securityAttributes"></param>
/// <param name="creationDisposition"></param>
/// <param name="flagsAndAttributes"></param>
/// <param name="templateFile"></param>
/// <returns></returns>
[UnmanagedFunctionPointer(CallingConvention.StdCall,
CharSet = CharSet.Unicode,
SetLastError = true)]
delegate IntPtr CreateFile_Delegate(
String filename,
UInt32 desiredAccess,
UInt32 shareMode,
IntPtr securityAttributes,
UInt32 creationDisposition,
UInt32 flagsAndAttributes,
IntPtr templateFile);
/// <summary>
/// Using P/Invoke to call original method.
/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
/// </summary>
/// <param name="filename"></param>
/// <param name="desiredAccess"></param>
/// <param name="shareMode"></param>
/// <param name="securityAttributes"></param>
/// <param name="creationDisposition"></param>
/// <param name="flagsAndAttributes"></param>
/// <param name="templateFile"></param>
/// <returns></returns>
[DllImport("kernel32.dll",
CharSet = CharSet.Unicode,
SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern IntPtr CreateFileW(
String filename,
UInt32 desiredAccess,
UInt32 shareMode,
IntPtr securityAttributes,
UInt32 creationDisposition,
UInt32 flagsAndAttributes,
IntPtr templateFile);
/// <summary>
/// The CreateFile hook function. This will be called instead of the original CreateFile once hooked.
/// </summary>
/// <param name="filename"></param>
/// <param name="desiredAccess"></param>
/// <param name="shareMode"></param>
/// <param name="securityAttributes"></param>
/// <param name="creationDisposition"></param>
/// <param name="flagsAndAttributes"></param>
/// <param name="templateFile"></param>
/// <returns></returns>
IntPtr CreateFile_Hook(
String filename,
UInt32 desiredAccess,
UInt32 shareMode,
IntPtr securityAttributes,
UInt32 creationDisposition,
UInt32 flagsAndAttributes,
IntPtr templateFile)
{
try
{
lock (this._messageQueue)
{
if (this._messageQueue.Count < 1000)
{
string mode = string.Empty;
switch (creationDisposition)
{
case 1:
mode = "CREATE_NEW";
break;
case 2:
mode = "CREATE_ALWAYS";
break;
case 3:
mode = "OPEN_ALWAYS";
break;
case 4:
mode = "OPEN_EXISTING";
break;
case 5:
mode = "TRUNCATE_EXISTING";
break;
}
// Add message to send to FileMonitor
this._messageQueue.Enqueue(
string.Format("[{0}:{1}]: CREATE ({2}) \"{3}\"",
EasyHook.RemoteHooking.GetCurrentProcessId(), EasyHook.RemoteHooking.GetCurrentThreadId()
, mode, filename));
}
}
}
catch
{
// swallow exceptions so that any issues caused by this code do not crash target process
}
// now call the original API...
return CreateFileW(
filename,
desiredAccess,
shareMode,
securityAttributes,
creationDisposition,
flagsAndAttributes,
templateFile);
}
#endregion
#region ReadFile Hook
/// <summary>
/// The ReadFile delegate, this is needed to create a delegate of our hook function <see cref="ReadFile_Hook(IntPtr, IntPtr, uint, out uint, IntPtr)"/>.
/// </summary>
/// <param name="hFile"></param>
/// <param name="lpBuffer"></param>
/// <param name="nNumberOfBytesToRead"></param>
/// <param name="lpNumberOfBytesRead"></param>
/// <param name="lpOverlapped"></param>
/// <returns></returns>
[UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)]
delegate bool ReadFile_Delegate(
IntPtr hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToRead,
out uint lpNumberOfBytesRead,
IntPtr lpOverlapped);
/// <summary>
/// Using P/Invoke to call the orginal function
/// </summary>
/// <param name="hFile"></param>
/// <param name="lpBuffer"></param>
/// <param name="nNumberOfBytesToRead"></param>
/// <param name="lpNumberOfBytesRead"></param>
/// <param name="lpOverlapped"></param>
/// <returns></returns>
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern bool ReadFile(
IntPtr hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToRead,
out uint lpNumberOfBytesRead,
IntPtr lpOverlapped);
/// <summary>
/// The ReadFile hook function. This will be called instead of the original ReadFile once hooked.
/// </summary>
/// <param name="hFile"></param>
/// <param name="lpBuffer"></param>
/// <param name="nNumberOfBytesToRead"></param>
/// <param name="lpNumberOfBytesRead"></param>
/// <param name="lpOverlapped"></param>
/// <returns></returns>
bool ReadFile_Hook(
IntPtr hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToRead,
out uint lpNumberOfBytesRead,
IntPtr lpOverlapped)
{
bool result = false;
lpNumberOfBytesRead = 0;
// Call original first so we have a value for lpNumberOfBytesRead
result = ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, out lpNumberOfBytesRead, lpOverlapped);
try
{
lock (this._messageQueue)
{
if (this._messageQueue.Count < 1000)
{
// Retrieve filename from the file handle
StringBuilder filename = new StringBuilder(255);
GetFinalPathNameByHandle(hFile, filename, 255, 0);
// Add message to send to FileMonitor
this._messageQueue.Enqueue(
string.Format("[{0}:{1}]: READ ({2} bytes) \"{3}\"",
EasyHook.RemoteHooking.GetCurrentProcessId(), EasyHook.RemoteHooking.GetCurrentThreadId()
, lpNumberOfBytesRead, filename));
}
}
}
catch
{
// swallow exceptions so that any issues caused by this code do not crash target process
}
return result;
}
#endregion
#region WriteFile Hook
/// <summary>
/// The WriteFile delegate, this is needed to create a delegate of our hook function <see cref="WriteFile_Hook(IntPtr, IntPtr, uint, out uint, IntPtr)"/>.
/// </summary>
/// <param name="hFile"></param>
/// <param name="lpBuffer"></param>
/// <param name="nNumberOfBytesToWrite"></param>
/// <param name="lpNumberOfBytesWritten"></param>
/// <param name="lpOverlapped"></param>
/// <returns></returns>
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
delegate bool WriteFile_Delegate(
IntPtr hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToWrite,
out uint lpNumberOfBytesWritten,
IntPtr lpOverlapped);
/// <summary>
/// Using P/Invoke to call original WriteFile method
/// </summary>
/// <param name="hFile"></param>
/// <param name="lpBuffer"></param>
/// <param name="nNumberOfBytesToWrite"></param>
/// <param name="lpNumberOfBytesWritten"></param>
/// <param name="lpOverlapped"></param>
/// <returns></returns>
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool WriteFile(
IntPtr hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToWrite,
out uint lpNumberOfBytesWritten,
IntPtr lpOverlapped);
/// <summary>
/// The WriteFile hook function. This will be called instead of the original WriteFile once hooked.
/// </summary>
/// <param name="hFile"></param>
/// <param name="lpBuffer"></param>
/// <param name="nNumberOfBytesToWrite"></param>
/// <param name="lpNumberOfBytesWritten"></param>
/// <param name="lpOverlapped"></param>
/// <returns></returns>
bool WriteFile_Hook(
IntPtr hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToWrite,
out uint lpNumberOfBytesWritten,
IntPtr lpOverlapped)
{
bool result = false;
// Call original first so we get lpNumberOfBytesWritten
result = WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, out lpNumberOfBytesWritten, lpOverlapped);
try
{
lock (this._messageQueue)
{
if (this._messageQueue.Count < 1000)
{
// Retrieve filename from the file handle
StringBuilder filename = new StringBuilder(255);
GetFinalPathNameByHandle(hFile, filename, 255, 0);
// Add message to send to FileMonitor
this._messageQueue.Enqueue(
string.Format("[{0}:{1}]: WRITE ({2} bytes) \"{3}\"",
EasyHook.RemoteHooking.GetCurrentProcessId(), EasyHook.RemoteHooking.GetCurrentThreadId()
, lpNumberOfBytesWritten, filename));
}
}
}
catch
{
// swallow exceptions so that any issues caused by this code do not crash target process
}
return result;
}
#endregion
}
}
I don't have mutch experience in hook in general, can someone let me known, if is possible fix this ?

Exception when event is raised with GC'd handler [duplicate]

I have been using this key hook script i found but I continue to get an error after a few seconds of using it in my program. The error says.. A call has been made on a garbage collected delegate 'keylogger!Utilities.globalKeyboardHook+keyboardHookProc::Invoke'.
How can I fix this?
namespace Utilities
{
/// <summary>
/// A class that manages a global low level keyboard hook
/// </summary>
class globalKeyboardHook
{
#region Constant, Structure and Delegate Definitions
/// <summary>
/// defines the callback type for the hook
/// </summary>
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
public struct keyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_SYSKEYDOWN = 0x104;
const int WM_SYSKEYUP = 0x105;
#endregion
#region Instance Variables
/// <summary>
/// The collections of keys to watch for
/// </summary>
public List<Keys> HookedKeys = new List<Keys>();
/// <summary>
/// Handle to the hook, need this to unhook and call the next hook
/// </summary>
IntPtr hhook = IntPtr.Zero;
#endregion
#region Events
/// <summary>
/// Occurs when one of the hooked keys is pressed
/// </summary>
public event KeyEventHandler KeyDown;
/// <summary>
/// Occurs when one of the hooked keys is released
/// </summary>
public event KeyEventHandler KeyUp;
#endregion
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the <see cref="globalKeyboardHook"/> class and installs the keyboard hook.
/// </summary>
public globalKeyboardHook()
{
hook();
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="globalKeyboardHook"/> is reclaimed by garbage collection and uninstalls the keyboard hook.
/// </summary>
~globalKeyboardHook()
{
unhook();
}
#endregion
#region Public Methods
/// <summary>
/// Installs the global hook
/// </summary>
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}
/// <summary>
/// Uninstalls the global hook
/// </summary>
public void unhook()
{
UnhookWindowsHookEx(hhook);
}
/// <summary>
/// The callback for the keyboard hook
/// </summary>
/// <param name="code">The hook code, if it isn't >= 0, the function shouldn't do anyting</param>
/// <param name="wParam">The event type</param>
/// <param name="lParam">The keyhook event information</param>
/// <returns></returns>
public int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
{
if (code >= 0)
{
Keys key = (Keys)lParam.vkCode;
if (HookedKeys.Contains(key))
{
KeyEventArgs kea = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
{
KeyDown(this, kea);
}
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
{
KeyUp(this, kea);
}
if (kea.Handled)
return 1;
}
}
return CallNextHookEx(hhook, code, wParam, ref lParam);
}
#endregion
#region DLL imports
/// <summary>
/// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
/// </summary>
/// <param name="idHook">The id of the event you want to hook</param>
/// <param name="callback">The callback.</param>
/// <param name="hInstance">The handle you want to attach the event to, can be null</param>
/// <param name="threadId">The thread you want to attach the event to, can be null</param>
/// <returns>a handle to the desired hook</returns>
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
/// <summary>
/// Unhooks the windows hook.
/// </summary>
/// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
/// <returns>True if successful, false otherwise</returns>
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
/// <summary>
/// Calls the next hook.
/// </summary>
/// <param name="idHook">The hook id</param>
/// <param name="nCode">The hook code</param>
/// <param name="wParam">The wparam.</param>
/// <param name="lParam">The lparam.</param>
/// <returns></returns>
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
/// <summary>
/// Loads the library.
/// </summary>
/// <param name="lpFileName">Name of the library</param>
/// <returns>A handle to the library</returns>
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
#endregion
}
}
globalKeyboardHook class :
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.IO;
namespace Utilities
{
/// <summary>
/// A class that manages a global low level keyboard hook
/// </summary>
class globalKeyboardHook : IDisposable
{
private bool _disposed;
#region Constant, Structure and Delegate Definitions
/// <summary>
/// defines the callback type for the hook
/// </summary>
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
public struct keyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_SYSKEYDOWN = 0x104;
const int WM_SYSKEYUP = 0x105;
#endregion
#region Instance Variables
/// <summary>
/// The collections of keys to watch for
/// </summary>
public List<Keys> HookedKeys = new List<Keys>();
/// <summary>
/// Handle to the hook, need this to unhook and call the next hook
/// </summary>
IntPtr hhook = IntPtr.Zero;
#endregion
#region Events
/// <summary>
/// Occurs when one of the hooked keys is pressed
/// </summary>
public event KeyEventHandler KeyDown;
/// <summary>
/// Occurs when one of the hooked keys is released
/// </summary>
public event KeyEventHandler KeyUp;
#endregion
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the <see cref="globalKeyboardHook"/> class and installs the keyboard hook.
/// </summary>
public globalKeyboardHook()
{
hook();
_disposed = false;
}
public void Dispose()
{
Dispose(true);
// Use SupressFinalize in case a subclass
// of this type implements a finalizer.
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// If you need thread safety, use a lock around these
// operations, as well as in your methods that use the resource.
if (!_disposed)
{
if (disposing)
{
unhook();
}
// Indicate that the instance has been disposed.
_disposed = true;
}
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="globalKeyboardHook"/> is reclaimed by garbage collection and uninstalls the keyboard hook.
/// </summary>
~globalKeyboardHook()
{
Dispose();
}
#endregion
#region Public Methods
/// <summary>
/// Installs the global hook
/// </summary>
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, new keyboardHookProc(hookProc), hInstance, 0);
}
/// <summary>
/// Uninstalls the global hook
/// </summary>
public void unhook()
{
UnhookWindowsHookEx(hhook);
}
/// <summary>
/// The callback for the keyboard hook
/// </summary>
/// <param name="code">The hook code, if it isn't >= 0, the function shouldn't do anyting</param>
/// <param name="wParam">The event type</param>
/// <param name="lParam">The keyhook event information</param>
/// <returns></returns>
public int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
{
if (code >= 0)
{
Keys key = (Keys)lParam.vkCode;
if (HookedKeys.Contains(key))
{
KeyEventArgs kea = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
{
KeyDown(this, kea);
}
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
{
KeyUp(this, kea);
}
if (kea.Handled)
return 1;
}
}
return CallNextHookEx(hhook, code, wParam, ref lParam);
}
#endregion
#region DLL imports
/// <summary>
/// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
/// </summary>
/// <param name="idHook">The id of the event you want to hook</param>
/// <param name="callback">The callback.</param>
/// <param name="hInstance">The handle you want to attach the event to, can be null</param>
/// <param name="threadId">The thread you want to attach the event to, can be null</param>
/// <returns>a handle to the desired hook</returns>
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
/// <summary>
/// Unhooks the windows hook.
/// </summary>
/// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
/// <returns>True if successful, false otherwise</returns>
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
/// <summary>
/// Calls the next hook.
/// </summary>
/// <param name="idHook">The hook id</param>
/// <param name="nCode">The hook code</param>
/// <param name="wParam">The wparam.</param>
/// <param name="lParam">The lparam.</param>
/// <returns></returns>
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
/// <summary>
/// Loads the library.
/// </summary>
/// <param name="lpFileName">Name of the library</param>
/// <returns>A handle to the library</returns>
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
#endregion
}
}
I updated the code with IDisposable. I am probably horribly off on what I am supposed to do but its still not working
The problem is that:
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
is just syntactic sugar for:
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, new keyboardHookProc(hookProc), hInstance, 0);
and so the keyboardHookProc object is just local and will get disposed of since SetWindowsHookEx doesn't do anything to actually hold onto it in the managed world.
To fix this, up at the top where you define your member variables, add one more like this:
IntPtr hhook = IntPtr.Zero
private keyboardHookProc hookProcDelegate;
then change your constructor to be:
public globalKeyboardHook()
{
hookProcDelegate = hookProc;
hook();
}
and then change your hook() method to be:
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProcDelegate, hInstance, 0);
}
That way you're using a delegate that is stored as a member variable and will be alive as long as your globalKeyboardHook object is alive.
Sounds to me like you are instantiating a globalKeyboardHook then letting it get garbage collected. I'm guessing you do something like this:
public void InstallHook()
{
var hook = new globalKeyboardHook();
}
You need to keep a reference to the globalKeyboardHook() around to prevent it from being garbage collected.
globalKeyboardHook hook;
public void InstallHook()
{
hook = new globalKeyboardHook();
}
I'd like to add this, for future reference, as it may help understanding Tim's answer, and maybe debugging what's going on, if you have complex code:
callbackOnCollectedDelegate MDA
https://learn.microsoft.com/en-us/dotnet/framework/debug-trace-profile/callbackoncollecteddelegate-mda
even though with the new code I am still getting the mentioned error, as a solution I just kept an instance of the delegate at class scope, now the error does not come up anymore.
//do not forget to declare kbhproc class var
this.kbhProc = new keyboardHookProc(hookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, this.kbhProc /*new keyboardHookProc(hookProc)*/, hInstance, 0);
the above code is based on the code of the question.

How do I write Powershell Wrapper in C#?

I'm trying to write general interactive shell wrapper class from c#/.Net using CreateProcess() for Dos, Powershell, Plink, etc, and I've found the article Why does StandardOutput.Read() block when StartInfo.RedirectStandardInput is set to true? to write wrapper classes which redirect child process's stdin/stdout for getting powershell command execution output. As he directed, I got his source code from http://sixfeetsix.blogspot.com/2012/08/interacting-with-sub-processed-shell-in.html. However, any command line execution such as plink.exe, cmd.exe works great for redirection of stdin/stdout but powershell.exe doesn't get me the input/output correctly. It looks like powershell.exe doesn't inherit from parant process and thread for pipe. Would you give me any feedbacks for getting retValue as of "get-help" powershell command successfully?
When I run "dir" command from DosShell, it returns the output of dos command "dir" successfully. However, Powershell doesn't return "get-help" command from powershell prompt.
using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Diagnostics;
namespace PowershellWrapperPOC
{
class Program
{
static void Main(string[] args)
{
StringBuilder testOutput = new StringBuilder();
string retValue = null;
DosShell ds = new DosShell();
retValue = ds.Start(#"C:\Windows\System32\cmd.exe /k", #"C:\Windows\System32\");
testOutput.Append(retValue.ToString());
retValue = ds.SendAndReceive("dir");
testOutput.Append(retValue.ToString());
ds.Terminate();
Debug.Print(testOutput.ToString());
testOutput = new StringBuilder();
PowerShell ps = new PowerShell();
retValue = ps.Start(#"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe", #"C:\Windows\System32\");
testOutput.Append(retValue.ToString());
retValue = ps.SendAndReceive("get-help");
testOutput.Append(retValue.ToString());
ps.Terminate();
Debug.Print(testOutput.ToString());
}
}
public class PowerShell : CommonShell
{
public PowerShell()
{
base.SetPrintSendCommand(false);
base.SetEncoding("utf8");
base.SetExitCommand("exit");
base.SetPrompts("> ; ");
}
}
public class DosShell : CommonShell
{
public DosShell()
{
base.SetPrintSendCommand(false);
base.SetEncoding("utf8");
base.SetExitCommand("exit");
base.SetPrompts(">");
}
}
public class CommonShell : ShellProcess
{
private StringBuilder _strOutput;
private StringBuilder _strLastOutput;
private static string _escapeCharsPattern = "[\\[|\\(][0-9;?]*[^0-9;]";
private int _timeout;
public CommonShell()
{
_strOutput = new StringBuilder();
_strLastOutput = new StringBuilder();
}
public bool RemoveEscChars
{
get;
set;
}
protected override string Prompt
{
get;
set;
}
protected override string ExitCommand
{
get;
set;
}
protected override Encoding Encoding
{
get;
set;
}
public bool PrintSendCommand
{
get;
set;
}
new public string Start(string applicationName, string workDirectory)
{
if (PrintSendCommand == true)
{
_strOutput.Append(applicationName + "\r\n");
_strLastOutput.Append(applicationName + "\r\n");
}
var results = base.Start(applicationName, workDirectory);
// if remove esc chars?
if (RemoveEscChars == true)
{
string str = results.Item3;
str = Regex.Replace(str, _escapeCharsPattern, "");
_strOutput.Append(str);
_strLastOutput.Append(str);
}
else
{
_strOutput.Append(results.Item3);
_strLastOutput.Append(results.Item3);
}
return _strLastOutput.ToString();
}
new public string SendAndReceive(string toSend)
{
_strLastOutput = new StringBuilder();
if (PrintSendCommand == true)
{
_strLastOutput.Append(toSend + "\r\n");
_strOutput.Append(toSend + "\r\n");
}
// Wait forever till getting the expected prompt
var results = base.SendAndReceive(toSend + "\r\n");
// if remove esc chars?
if (RemoveEscChars == true)
{
string str = results.Item3;
str = Regex.Replace(str, _escapeCharsPattern, "");
_strOutput.Append(str);
_strLastOutput.Append(str);
}
else
{
_strOutput.Append(results.Item3);
_strLastOutput.Append(results.Item3);
}
return _strLastOutput.ToString();
}
public void SetPrompts(string prompt)
{
var enc = this.Encoding;
byte[] utfBytes = enc.GetBytes(prompt);
Prompt = enc.GetString(utfBytes);
}
public void SetEncoding(string enc)
{
string l_enc = enc.ToLower();
switch (l_enc)
{
case "utf8":
this.Encoding = Encoding.UTF8;
break;
case "utf7":
this.Encoding = Encoding.UTF7;
break;
case "utf32":
this.Encoding = Encoding.UTF32;
break;
case "ascii":
this.Encoding = Encoding.ASCII;
break;
default:
return;
}
}
public void SetExitCommand(string command)
{
ExitCommand = command;
}
public void SetPrintSendCommand(bool bPrintSendCommand)
{
PrintSendCommand = bPrintSendCommand;
}
public void SetRemoveEscChars(bool bRemove)
{
RemoveEscChars = bRemove;
}
public void SetTimeout(int timeout)
{
this._timeout = timeout;
}
public void Flush()
{
_strOutput = new StringBuilder();
_strLastOutput = new StringBuilder();
}
public string GetLastOutput()
{
return _strLastOutput.ToString();
}
public string GetOutput()
{
return _strLastOutput.ToString();
}
}
/// <summary>
/// Kernel32 Marshaling
/// </summary>
public static class Kernel32
{
/// <summary>
/// HANDLE_FLAG_INHERIT
///
/// If this flag is set, a child process created with the bInheritHandles parameter of CreateProcess set to TRUE will inherit the object handle.
///
/// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724935(v=vs.85).aspx
/// </summary>
public const int HANDLE_FLAG_INHERIT = 1;
/// <summary>
/// STARTF_USESTDHANDLES
///
/// The hStdInput, hStdOutput, and hStdError members contain additional information.
/// If this flag is specified when calling one of the process creation functions, the handles must be inheritable and the function's
/// bInheritHandles parameter must be set to TRUE. For more information, see Handle Inheritance.
///
/// If this flag is specified when calling the GetStartupInfo function, these members are either the handle value specified during
/// process creation or INVALID_HANDLE_VALUE.
///
/// Handles must be closed with CloseHandle when they are no longer needed.
/// This flag cannot be used with STARTF_USEHOTKEY.
///
/// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
/// </summary>
public const UInt32 STARTF_USESTDHANDLES = 0x00000100;
/// <summary>
/// STARTF_USESHOWWINDOW
///
/// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
/// </summary>
public const UInt32 STARTF_USESHOWWINDOW = 0x00000001;
/// <summary>
/// SECURITY_ATTRIBUTES
/// </summary>
public struct SECURITY_ATTRIBUTES
{
/// <summary>
/// The size, in bytes, of this structure. Set this value to the size of the SECURITY_ATTRIBUTES structure.
/// </summary>
public int length;
/// <summary>
/// A pointer to a SECURITY_DESCRIPTOR structure that controls access to the object.
/// If the value of this member is NULL, the object is assigned the default security descriptor associated with the access token of the calling process.
/// This is not the same as granting access to everyone by assigning a NULL discretionary access control list (DACL).
/// By default, the default DACL in the access token of a process allows access only to the user represented by the access token.
///
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa379560(v=vs.85).aspx
/// </summary>
public IntPtr lpSecurityDescriptor;
/// <summary>
/// A Boolean value that specifies whether the returned handle is inherited when a new process is created.
/// If this member is TRUE, the new process inherits the handle.
/// </summary>
[MarshalAs(UnmanagedType.Bool)]
public bool bInheritHandle;
}
/// <summary>
/// STARTUPINFO
/// </summary>
public struct STARTUPINFO
{
/// <summary>
/// The size of the structure, in bytes.
/// </summary>
public uint cb;
/// <summary>
/// Reserved; must be NULL.
/// </summary>
public string lpReserved;
/// <summary>
/// The name of the desktop, or the name of both the desktop and window station for this process.
/// A backslash in the string indicates that the string includes both the desktop and window station names.
/// </summary>
public string lpDesktop;
/// <summary>
/// For console processes, this is the title displayed in the title bar if a new console window is created.
/// If NULL, the name of the executable file is used as the window title instead.
/// This parameter must be NULL for GUI or console processes that do not create a new console window.
/// </summary>
public string lpTitle;
/// <summary>
/// If dwFlags specifies STARTF_USEPOSITION, this member is the x offset of the upper left corner of a window if a new window is created, in pixels.
/// Otherwise, this member is ignored.
///
/// The offset is from the upper left corner of the screen. For GUI processes, the specified position is used the first time the new process calls
/// CreateWindow to create an overlapped window if the x parameter of CreateWindow is CW_USEDEFAULT.
/// </summary>
public uint dwX;
/// <summary>
/// If dwFlags specifies STARTF_USEPOSITION, this member is the y offset of the upper left corner of a window if a new window is created, in pixels.
/// Otherwise, this member is ignored.
///
/// The offset is from the upper left corner of the screen. For GUI processes, the specified position is used the first time the new process calls
/// CreateWindow to create an overlapped window if the y parameter of CreateWindow is CW_USEDEFAULT.
/// </summary>
public uint dwY;
/// <summary>
/// If dwFlags specifies STARTF_USESIZE, this member is the width of the window if a new window is created, in pixels.
/// Otherwise, this member is ignored.
///
/// For GUI processes, this is used only the first time the new process calls CreateWindow to create an overlapped window
/// if the nWidth parameter of CreateWindow is CW_USEDEFAULT.
/// </summary>
public uint dwXSize;
/// <summary>
/// If dwFlags specifies STARTF_USESIZE, this member is the height of the window if a new window is created, in pixels.
/// Otherwise, this member is ignored.
///
/// For GUI processes, this is used only the first time the new process calls CreateWindow to create an overlapped window
/// if the nHeight parameter of CreateWindow is CW_USEDEFAULT.
/// </summary>
public uint dwYSize;
/// <summary>
/// If dwFlags specifies STARTF_USECOUNTCHARS, if a new console window is created in a console process,
/// this member specifies the screen buffer width, in character columns. Otherwise, this member is ignored.
/// </summary>
public uint dwXCountChars;
/// <summary>
/// If dwFlags specifies STARTF_USECOUNTCHARS, if a new console window is created in a console process,
/// this member specifies the screen buffer height, in character rows. Otherwise, this member is ignored.
/// </summary>
public uint dwYCountChars;
/// <summary>
/// If dwFlags specifies STARTF_USEFILLATTRIBUTE, this member is the initial text and background colors
/// if a new console window is created in a console application. Otherwise, this member is ignored.
///
/// This value can be any combination of the following values:
/// FOREGROUND_BLUE, FOREGROUND_GREEN, FOREGROUND_RED, FOREGROUND_INTENSITY, BACKGROUND_BLUE, BACKGROUND_GREEN,
/// BACKGROUND_RED, and BACKGROUND_INTENSITY. For example, the following combination of values produces red text on a white background:
///
/// FOREGROUND_RED| BACKGROUND_RED| BACKGROUND_GREEN| BACKGROUND_BLUE
/// </summary>
public uint dwFillAttribute;
/// <summary>
/// A bitfield that determines whether certain STARTUPINFO members are used when the process creates a window.
/// This member can be one or more of the following values.
///
/// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
/// </summary>
public uint dwFlags;
/// <summary>
/// If dwFlags specifies STARTF_USESHOWWINDOW, this member can be any of the values that can be specified in the nCmdShow parameter for the
/// ShowWindow function, except for SW_SHOWDEFAULT. Otherwise, this member is ignored.
///
/// For GUI processes, the first time ShowWindow is called, its nCmdShow parameter is ignored wShowWindow specifies the default value.
/// In subsequent calls to ShowWindow, the wShowWindow member is used if the nCmdShow parameter of ShowWindow is set to SW_SHOWDEFAULT.
/// </summary>
public short wShowWindow;
/// <summary>
/// Reserved for use by the C Run-time; must be zero.
/// </summary>
public short cbReserved2;
/// <summary>
/// Reserved for use by the C Run-time; must be NULL.
/// </summary>
public IntPtr lpReserved2;
/// <summary>
/// If dwFlags specifies STARTF_USESTDHANDLES, this member is the standard input handle for the process.
/// If STARTF_USESTDHANDLES is not specified, the default for standard input is the keyboard buffer.
///
/// If dwFlags specifies STARTF_USEHOTKEY, this member specifies a hotkey value that is sent as the wParam parameter of a
/// WM_SETHOTKEY message to the first eligible top-level window created by the application that owns the process.
/// If the window is created with the WS_POPUP window style, it is not eligible unless the WS_EX_APPWINDOW extended window style is also set.
///
/// For more information, see CreateWindowEx.
///
/// Otherwise, this member is ignored.
/// </summary>
public IntPtr hStdInput;
/// <summary>
/// If dwFlags specifies STARTF_USESTDHANDLES, this member is the standard output handle for the process.
/// Otherwise, this member is ignored and the default for standard output is the console window's buffer.
///
/// If a process is launched from the taskbar or jump list, the system sets hStdOutput to a handle to the monitor
/// that contains the taskbar or jump list used to launch the process. For more information, see Remarks.
///
/// Windows 7, Windows Server 2008 R2, Windows Vista, Windows Server 2008, Windows XP, and Windows Server 2003:
/// This behavior was introduced in Windows 8 and Windows Server 2012.
/// </summary>
public IntPtr hStdOutput;
/// <summary>
/// If dwFlags specifies STARTF_USESTDHANDLES, this member is the standard error handle for the process.
/// Otherwise, this member is ignored and the default for standard error is the console window's buffer.
/// </summary>
public IntPtr hStdError;
}
/// <summary>
/// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684873(v=vs.85).aspx
/// </summary>
public struct PROCESS_INFORMATION
{
/// <summary>
/// A handle to the newly created process. The handle is used to specify the process in all functions that perform operations on the process object.
/// </summary>
public IntPtr hProcess;
/// <summary>
/// A handle to the primary thread of the newly created process. The handle is used to specify the thread in all functions that perform operations on the thread object.
/// </summary>
public IntPtr hThread;
/// <summary>
/// A value that can be used to identify a process. The value is valid from the time the process is created until all handles to the process are closed and the process object is freed; at this point, the identifier may be reused.
/// </summary>
public uint dwProcessId;
/// <summary>
/// A value that can be used to identify a thread. The value is valid from the time the thread is created until all handles to the thread are closed and the thread object is freed; at this point, the identifier may be reused.
/// </summary>
public uint dwThreadId;
}
/// <summary>
/// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
/// </summary>
/// <param name="lpApplicationName"></param>
/// <param name="lpCommandLine"></param>
/// <param name="lpProcessAttributes"></param>
/// <param name="lpThreadAttributes"></param>
/// <param name="bInheritHandles"></param>
/// <param name="dwCreationFlags"></param>
/// <param name="lpEnvironment"></param>
/// <param name="lpCurrentDirectory"></param>
/// <param name="lpStartupInfo"></param>
/// <param name="lpProcessInformation"></param>
/// <returns></returns>
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CreateProcess(string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
/// <summary>
/// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx
/// </summary>
/// <param name="hObject"></param>
/// <returns></returns>
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
/// <summary>
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365152(v=vs.85).aspx
/// </summary>
/// <param name="hReadPipe"></param>
/// <param name="hWritePipe"></param>
/// <param name="lpPipeAttributes"></param>
/// <param name="nSize"></param>
/// <returns></returns>
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CreatePipe(out IntPtr hReadPipe,
out IntPtr hWritePipe,
ref SECURITY_ATTRIBUTES lpPipeAttributes,
uint nSize);
/// <summary>
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365779(v=vs.85).aspx
/// </summary>
/// <param name="hNamedPipe"></param>
/// <param name="pBuffer"></param>
/// <param name="nBufferSize"></param>
/// <param name="lpBytesRead"></param>
/// <param name="lpTotalBytesAvail"></param>
/// <param name="lpBytesLeftThisMessage"></param>
/// <returns></returns>
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern unsafe bool PeekNamedPipe(IntPtr hNamedPipe,
IntPtr pBuffer,
int nBufferSize,
IntPtr lpBytesRead,
int* lpTotalBytesAvail,
IntPtr lpBytesLeftThisMessage);
/// <summary>
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx
/// </summary>
/// <param name="hFile"></param>
/// <param name="pBuffer"></param>
/// <param name="nNumberOfBytesToRead"></param>
/// <param name="lpNumberOfBytesRead"></param>
/// <param name="lpOverlapped"></param>
/// <returns></returns>
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern unsafe bool ReadFile(IntPtr hFile,
void* pBuffer,
int nNumberOfBytesToRead,
int* lpNumberOfBytesRead,
IntPtr lpOverlapped);
/// <summary>
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx
/// </summary>
/// <param name="hFile"></param>
/// <param name="pBuffer"></param>
/// <param name="nNumberOfBytesToWrite"></param>
/// <param name="lpNumberOfBytesWritten"></param>
/// <param name="lpOverlapped"></param>
/// <returns></returns>
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern unsafe bool WriteFile(IntPtr hFile,
void* pBuffer,
int nNumberOfBytesToWrite,
int* lpNumberOfBytesWritten,
IntPtr lpOverlapped);
/// <summary>
/// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724935(v=vs.85).aspx
/// </summary>
/// <param name="hObject"></param>
/// <param name="dwMask"></param>
/// <param name="dwFlags"></param>
/// <returns></returns>
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetHandleInformation(IntPtr hObject, int dwMask, uint dwFlags);
}
public abstract class ShellProcess
{
IntPtr _hChildStdoutR, _hChildStdoutW, _hChildStderrR, _hChildStderrW, _hChildStdinR, _hChildStdinW;
Kernel32.SECURITY_ATTRIBUTES _sa, _sa_process, _sa_thread;
Kernel32.STARTUPINFO _si;
Kernel32.PROCESS_INFORMATION _pi;
string _applicationName;
protected abstract string Prompt { get; set; }
protected abstract string ExitCommand { get; set; }
protected abstract Encoding Encoding { get; set; }
static unsafe int Write(IntPtr h, byte[] buffer, int index, int count)
{
int n = 0;
fixed (byte* p = buffer)
{
if (!Kernel32.WriteFile(h, p + index, count, &n, IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return n;
}
static unsafe int Peek(IntPtr h)
{
int n = 0;
if (!Kernel32.PeekNamedPipe(h, IntPtr.Zero, 0, IntPtr.Zero, &n, IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
return n;
}
static unsafe int Read(IntPtr h, byte[] buffer, int index, int count)
{
int n = 0;
fixed (byte* p = buffer)
{
if (!Kernel32.ReadFile(h, p + index, count, &n, IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return n;
}
public virtual void SendCommand(string s)
{
byte[] bytesToWrite = Encoding.GetBytes(s);
Write(_hChildStdinW, bytesToWrite, 0, bytesToWrite.Length);
}
Tuple<string, string, string> ReadToPrompt()
{
StringBuilder strOutput = new StringBuilder();
const int bufferLength = 128;
byte[] buffer = new byte[bufferLength];
int bytesReadCount;
var stdOut = new StringBuilder(4096);
var stdErr = new StringBuilder();
string[] prompts = Prompt.Split(';');
bool foundPrompt = false;
while (!foundPrompt)
{
while (Peek(_hChildStdoutR) > 0)
{
bytesReadCount = Read(_hChildStdoutR, buffer, 0, bufferLength);
stdOut.Append(Encoding.GetString(buffer, 0, bytesReadCount));
strOutput.Append(Encoding.GetString(buffer, 0, bytesReadCount));
}
foreach (string prompt in prompts)
{
if (stdOut.ToString().Contains(prompt))
{
foundPrompt = true;
break;
}
}
//strOutput.Append(stdOut);
while (Peek(_hChildStderrR) > 0)
{
bytesReadCount = Read(_hChildStderrR, buffer, 0, bufferLength);
stdErr.Append(Encoding.GetString(buffer, 0, bytesReadCount));
strOutput.Append(Encoding.GetString(buffer, 0, bytesReadCount));
}
foreach (string prompt in prompts)
{
if (stdErr.ToString().Contains(prompt))
{
foundPrompt = true;
break;
}
}
//strOutput.Append(stdErr);
Thread.Sleep(20);
}
while (Peek(_hChildStderrR) > 0)
{
bytesReadCount = Read(_hChildStderrR, buffer, 0, bufferLength);
stdErr.Append(Encoding.GetString(buffer, 0, bytesReadCount));
strOutput.Append(Encoding.GetString(buffer, 0, bytesReadCount));
}
return new Tuple<string, string, string>(stdOut.ToString(), stdErr.ToString(), strOutput.ToString());
}
public virtual Tuple<string, string, string> SendAndReceive(string toSend)
{
SendCommand(toSend);
return ReadToPrompt();
}
public virtual Tuple<string, string, string> Start(string applicationName, string workDirectory)
{
_sa = new Kernel32.SECURITY_ATTRIBUTES
{
bInheritHandle = true,
lpSecurityDescriptor = IntPtr.Zero,
length = Marshal.SizeOf(typeof(Kernel32.SECURITY_ATTRIBUTES))
};
_sa.lpSecurityDescriptor = IntPtr.Zero;
_sa_process = new Kernel32.SECURITY_ATTRIBUTES
{
bInheritHandle = true,
lpSecurityDescriptor = IntPtr.Zero,
length = Marshal.SizeOf(typeof(Kernel32.SECURITY_ATTRIBUTES))
};
_sa_process.lpSecurityDescriptor = IntPtr.Zero;
_sa_thread = new Kernel32.SECURITY_ATTRIBUTES
{
bInheritHandle = true,
lpSecurityDescriptor = IntPtr.Zero,
length = Marshal.SizeOf(typeof(Kernel32.SECURITY_ATTRIBUTES))
};
_sa_thread.lpSecurityDescriptor = IntPtr.Zero;
if (!Kernel32.CreatePipe(out _hChildStdoutR, out _hChildStdoutW, ref _sa, 0))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Kernel32.CreatePipe(out _hChildStderrR, out _hChildStderrW, ref _sa, 0))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Kernel32.CreatePipe(out _hChildStdinR, out _hChildStdinW, ref _sa, 0))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Kernel32.SetHandleInformation(_hChildStdoutR, Kernel32.HANDLE_FLAG_INHERIT, 0))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Kernel32.SetHandleInformation(_hChildStderrR, Kernel32.HANDLE_FLAG_INHERIT, 0))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Kernel32.SetHandleInformation(_hChildStdinW, Kernel32.HANDLE_FLAG_INHERIT, 0))
throw new Win32Exception(Marshal.GetLastWin32Error());
_si = new Kernel32.STARTUPINFO
{
wShowWindow = 0,
dwFlags = Kernel32.STARTF_USESTDHANDLES | Kernel32.STARTF_USESHOWWINDOW,
hStdOutput = _hChildStdoutW,
hStdError = _hChildStderrW,
hStdInput = _hChildStdinR
};
_si.cb = (uint)Marshal.SizeOf(_si);
_pi = new Kernel32.PROCESS_INFORMATION();
if (!Kernel32.CreateProcess(null, applicationName, IntPtr.Zero, IntPtr.Zero, true, 0, IntPtr.Zero, workDirectory, ref _si, out _pi))
throw new Win32Exception(Marshal.GetLastWin32Error());
_applicationName = applicationName;
return ReadToPrompt();
}
public void Terminate()
{
SendCommand(ExitCommand);
if (!Kernel32.CloseHandle(_hChildStderrW))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Kernel32.CloseHandle(_hChildStdoutW))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Kernel32.CloseHandle(_hChildStdinW))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Kernel32.CloseHandle(_pi.hProcess))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Kernel32.CloseHandle(_pi.hThread))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
powershell is different. I think you can not simple redirect its input and output. that's why we need to use runspace to get the output object. search "powershell c# example", lots code. here is a simple one:
Calling PowerShell From C#
depends on the powershell you use (exchange powers shell, sharepoint power shell..), you will need to add the correct snapin.
It sounds like you don't want to just call powershell commands from C#, but to act as a powershell host. Being a powershell host allows you to receive the output from commands like Write-Host and Out-String and just have a stream of strings as input and output. Examples of hosts are powershell.exe and powershell_ise.exe, but it can be a lot of work and won't make processing the results of powershell commands easier, only harder. The power of powershell is that the input and output of commands are real objects, not just strings. You can read more about it here.
If you just want to execute some powershell commands and receive their output you would be better of with this here and work with the objects the powershell commands are returning instead of wanting them to be strings. This means you have to stop using commands like Write-Host, Out-String, Format-Table, etc. Those are for formatting, not for processing results.

Low level keyboard hook on a timer

I have a keyboard hook that intercepts the keys and outputs a random letter. What I want to do is set up a timer and have the keyboard unhook after one minute then in another minute hook itself back up. So the first part works, it hooks up on start and after one minute it unhooks, but then never hooks up again. How could I get it to re-hook after being unhooked?
Here's is the hook code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Utilities {
/// <summary>
/// A class that manages a global low level keyboard hook
/// </summary>
class globalKeyboardHook {
#region Constant, Structure and Delegate Definitions
/// <summary>
/// defines the callback type for the hook
/// </summary>
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
public struct keyboardHookStruct {
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_SYSKEYDOWN = 0x104;
const int WM_SYSKEYUP = 0x105;
#endregion
#region Instance Variables
/// <summary>
/// The collections of keys to watch for
/// </summary>
public List<Keys> HookedKeys = new List<Keys>();
/// <summary>
/// Handle to the hook, need this to unhook and call the next hook
/// </summary>
IntPtr hhook = IntPtr.Zero;
#endregion
#region Events
/// <summary>
/// Occurs when one of the hooked keys is pressed
/// </summary>
public event KeyEventHandler KeyDown;
/// <summary>
/// Occurs when one of the hooked keys is released
/// </summary>
public event KeyEventHandler KeyUp;
#endregion
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the <see cref="globalKeyboardHook"/> class and installs the keyboard hook.
/// </summary>
public globalKeyboardHook() {
}
/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="globalKeyboardHook"/> is reclaimed by garbage collection and uninstalls the keyboard hook.
/// </summary>
~globalKeyboardHook() {
unhook();
}
#endregion
#region Public Methods
/// <summary>
/// Installs the global hook
/// </summary>
public void hook() {
_hookProc = new keyboardHookProc(hookProc);
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}
/// <summary>
/// Uninstalls the global hook
/// </summary>
public void unhook() {
UnhookWindowsHookEx(hhook);
hhook = IntPtr.Zero;
}
/// <summary>
/// The callback for the keyboard hook
/// </summary>
/// <param name="code">The hook code, if it isn't >= 0, the function shouldn't do anyting</param>
/// <param name="wParam">The event type</param>
/// <param name="lParam">The keyhook event information</param>
/// <returns></returns>
public int hookProc(int code, int wParam, ref keyboardHookStruct lParam) {
if (code >= 0) {
Keys key = (Keys)lParam.vkCode;
if (HookedKeys.Contains(key)) {
KeyEventArgs kea = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null)) {
KeyDown(this, kea) ;
} else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null)) {
KeyUp(this, kea);
}
if (kea.Handled)
return 1;
}
}
return CallNextHookEx(hhook, code, wParam, ref lParam);
}
#endregion
#region DLL imports
/// <summary>
/// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
/// </summary>
/// <param name="idHook">The id of the event you want to hook</param>
/// <param name="callback">The callback.</param>
/// <param name="hInstance">The handle you want to attach the event to, can be null</param>
/// <param name="threadId">The thread you want to attach the event to, can be null</param>
/// <returns>a handle to the desired hook</returns>
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
/// <summary>
/// Unhooks the windows hook.
/// </summary>
/// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
/// <returns>True if successful, false otherwise</returns>
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
/// <summary>
/// Calls the next hook.
/// </summary>
/// <param name="idHook">The hook id</param>
/// <param name="nCode">The hook code</param>
/// <param name="wParam">The wparam.</param>
/// <param name="lParam">The lparam.</param>
/// <returns></returns>
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
/// <summary>
/// Loads the library.
/// </summary>
/// <param name="lpFileName">Name of the library</param>
/// <returns>A handle to the library</returns>
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
keyboardHookProc _hookProc;
#endregion
}
}
Here is the entry point to the application:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Utilities;
using System.Timers;
namespace KeyRemapWindowsForm
{
static class Program
{
static bool _isHookActive = true;
static globalKeyboardHook gkh = new globalKeyboardHook();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
System.Timers.Timer HookTimer = new System.Timers.Timer(60000);
HookTimer.Elapsed += new ElapsedEventHandler(HookTimer_Elapsed);
HookTimer.Start();
Begin();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run();
GC.KeepAlive(HookTimer);
}
// Specify what you want to happen when the Elapsed event is
// raised.
static void HookTimer_Elapsed(object source, ElapsedEventArgs e)
{
if (_isHookActive)
{
End();
}
else
{
Begin();
}
}
static void Begin()
{
gkh = new globalKeyboardHook();
gkh.hook();
gkh.HookedKeys.Add(Keys.A);
gkh.HookedKeys.Add(Keys.B);
gkh.KeyDown += new KeyEventHandler(gkh_KeyDown);
gkh.KeyUp += new KeyEventHandler(gkh_KeyUp);
_isHookActive = true;
}
static void End()
{
gkh.HookedKeys.Clear();
gkh.KeyDown -= new KeyEventHandler(gkh_KeyDown);
gkh.KeyUp -= new KeyEventHandler(gkh_KeyUp);
gkh.unhook();
_isHookActive = false;
}
static void gkh_KeyUp(object sender, KeyEventArgs e)
{
SendKeys.Send(((KeyboardKeys)GetRandomKeyCode()).ToString());
e.Handled = true;
}
static void gkh_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
}
static int GetRandomKeyCode()
{
int RandomNum = 0;
while(RandomNum == 0)
{
Random RanNum = new Random();
RandomNum = RanNum.Next(65, 90);
switch(RandomNum)
{
case 68:
case 69:
case 86:
RandomNum = 0;
break;
default:
break;
}
}
return RandomNum;
}
}
public enum KeyboardKeys
{
/// <summary>
/// The A key.
/// </summary>
A = 65,
/// <summary>
/// The B key.
/// </summary>
B = 66,
/// <summary>
/// The C key.
/// </summary>
C = 67,
/// <summary>
/// The D key.
/// </summary>
D = 68,
/// <summary>
/// The E key.
/// </summary>
E = 69,
/// <summary>
/// The F key.
/// </summary>
F = 70,
/// <summary>
/// The G key.
/// </summary>
G = 71,
/// <summary>
/// The H key.
/// </summary>
H = 72,
/// <summary>
/// The I key.
/// </summary>
I = 73,
/// <summary>
/// The J key.
/// </summary>
J = 74,
/// <summary>
/// The K key.
/// </summary>
K = 75,
/// <summary>
/// The L key.
/// </summary>
L = 76,
/// <summary>
/// The M key.
/// </summary>
M = 77,
/// <summary>
/// The N key.
/// </summary>
N = 78,
/// <summary>
/// The O key.
/// </summary>
O = 79,
/// <summary>
/// The P key.
/// </summary>
P = 80,
/// <summary>
/// The Q key.
/// </summary>
Q = 81,
/// <summary>
/// The R key.
/// </summary>
R = 82,
/// <summary>
/// The S key.
/// </summary>
S = 83,
/// <summary>
/// The T key.
/// </summary>
T = 84,
/// <summary>
/// The U key.
/// </summary>
U = 85,
/// <summary>
/// The V key.
/// </summary>
V = 86,
/// <summary>
/// The W key.
/// </summary>
W = 87,
/// <summary>
/// The X key.
/// </summary>
X = 88,
/// <summary>
/// The Y key.
/// </summary>
Y = 89,
/// <summary>
/// The Z key.
/// </summary>
Z = 90
}
}
EDIT: So I took Jonathan.Peppers advice and put the check for isActive in the keydown event and that worked as far as faking like it was on and off. Now I have run into a new problem. After I have typed for awhile I get an InvalidOperationException with the reason being "Queue Empty" and not sure why it gets emptied when it's running just fine while I'm typing. I left the keyboard hook code alone but updated the application entry point code to the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Utilities;
using System.Timers;
namespace KeyRemapWindowsForm
{
static class Program
{
static bool _isHookActive = true;
static globalKeyboardHook gkh = new globalKeyboardHook();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
System.Timers.Timer HookTimer = new System.Timers.Timer(10000);
HookTimer.Elapsed += new ElapsedEventHandler(HookTimer_Elapsed);
HookTimer.Start();
Application.ApplicationExit += new EventHandler(OnApplicationExit);
gkh.hook();
gkh.HookedKeys.Add(Keys.S);
gkh.HookedKeys.Add(Keys.E);
gkh.KeyDown += new KeyEventHandler(gkh_KeyDown);
gkh.KeyUp += new KeyEventHandler(gkh_KeyUp);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run();
GC.KeepAlive(HookTimer);
}
static void OnApplicationExit(object sender, EventArgs e)
{
gkh.unhook();
}
static void HookTimer_Elapsed(object source, ElapsedEventArgs e)
{
if (_isHookActive)
{
_isHookActive = false;
}
else
{
_isHookActive = true;
}
}
static void gkh_KeyUp(object sender, KeyEventArgs e)
{
try
{
if (_isHookActive)
{
e.Handled = true;
}
}
catch
{
gkh.unhook();
Application.Exit();
}
}
static void gkh_KeyDown(object sender, KeyEventArgs e)
{
try
{
if (_isHookActive)
{
SendKeys.Send(((Keys)new Random().Next(65, 90)).ToString());
e.Handled = true;
}
}
catch
{
gkh.unhook();
Application.Exit();
}
}
}
}
EDIT: Stack trace I get from the above code after typing for awhile.
at System.Collections.Queue.Dequeue()
at System.Windows.Forms.SendKeys.SendInput(Byte[] oldKeyboardState, Queue previousEvents)
at System.Windows.Forms.SendKeys.Send(String keys, Control control, Boolean wait)
at System.Windows.Forms.SendKeys.Send(String keys)
at KeyRemapWindowsForm.Program.gkh_KeyDown(Object sender, KeyEventArgs e) in C:\Demos\KeyRemapWindowsForm\Program.cs:line 79
I've just been wrangling a very similar issue so I'm adding what I have found out to help anyone else having the problem.
The problem is the thread the hook is created on.
The System.Timers.Timer class will by default create a thread on the thread pool, not the main UI thread. If this thread goes, so does your hook.
You need to ensure the timer event it called on a thread that doesn't disappear.
You can either:
use the .SynchronizingObject property to ensure the call is made in the same thread as the object you specify.
Or you can marshal it yourself if you put something like this at top of your timer event:
this.InvokeCatchDisposedException(new MethodInvoker(() => HookTimer_Elapsed(sender, e)));
return;
Here's a link to describe the differences between the .net timers I found helpful.
https://msdn.microsoft.com/en-us/magazine/cc164015.aspx
Why don't you leave the hook in place continuously?
You can toggle your hook to not modify the keypress with your timer. Put an if (_hookEnabled) in your gkh_KeyUp and gkh_KeyDown methods.
I would think that setting up the hook would be an expensive operation anyways.

How to make Taskbar Flash on Lost Focus

I stumbled on this code below and tried to implement it in my WinForm App to help my users as many are very NOT tech-savy.
Unfortunately, it does nothing. It does not generate any errors or anything. It just doesn't make it Flash.
Can anyone offer any insight? I have tried it on Win 7(x64) & Win XP (x86) with the same results on both.
I am calling it like so --> TaskbarFlasher.FlashWindow(this); From my Main Form.
[DllImport("user32.dll")]
private extern static bool FlashWindow(IntPtr hwnd, bool bInvert);
[DllImport("user32.dll")]
private extern static IntPtr GetForegroundWindow();
/// <summary>
/// Notifies the user that the application requests attention
/// by flashing the taskbar if the form is not the current window.
/// </summary>
/// <param name="myForm">The form in question.</param>
public static void FlashWindow(Form myForm)
{
// if the current foreground window isn't this window,
// flash this window in task bar once every 1 second
if (GetForegroundWindow() != myForm.Handle)
{
FlashWindow(myForm.Handle, true);
}
}
Nevermind, I figured it out with the Following Links Help --> http://pietschsoft.com/post/2009/01/26/CSharp-Flash-Window-in-Taskbar-via-Win32-FlashWindowEx.aspx
Thanks Chris Pietschmann from a fellow SO Wisconsinite!!
public static class FlashWindow
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FlashWindowEx(ref FLASHWINFO pwfi);
[StructLayout(LayoutKind.Sequential)]
private struct FLASHWINFO
{
/// <summary>
/// The size of the structure in bytes.
/// </summary>
public uint cbSize;
/// <summary>
/// A Handle to the Window to be Flashed. The window can be either opened or minimized.
/// </summary>
public IntPtr hwnd;
/// <summary>
/// The Flash Status.
/// </summary>
public uint dwFlags;
/// <summary>
/// The number of times to Flash the window.
/// </summary>
public uint uCount;
/// <summary>
/// The rate at which the Window is to be flashed, in milliseconds. If Zero, the function uses the default cursor blink rate.
/// </summary>
public uint dwTimeout;
}
/// <summary>
/// Stop flashing. The system restores the window to its original stae.
/// </summary>
public const uint FLASHW_STOP = 0;
/// <summary>
/// Flash the window caption.
/// </summary>
public const uint FLASHW_CAPTION = 1;
/// <summary>
/// Flash the taskbar button.
/// </summary>
public const uint FLASHW_TRAY = 2;
/// <summary>
/// Flash both the window caption and taskbar button.
/// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
/// </summary>
public const uint FLASHW_ALL = 3;
/// <summary>
/// Flash continuously, until the FLASHW_STOP flag is set.
/// </summary>
public const uint FLASHW_TIMER = 4;
/// <summary>
/// Flash continuously until the window comes to the foreground.
/// </summary>
public const uint FLASHW_TIMERNOFG = 12;
/// <summary>
/// Flash the spacified Window (Form) until it recieves focus.
/// </summary>
/// <param name="form">The Form (Window) to Flash.</param>
/// <returns></returns>
public static bool Flash(System.Windows.Forms.Form form)
{
// Make sure we're running under Windows 2000 or later
if (Win2000OrLater)
{
FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL | FLASHW_TIMERNOFG, uint.MaxValue, 0);
return FlashWindowEx(ref fi);
}
return false;
}
private static FLASHWINFO Create_FLASHWINFO(IntPtr handle, uint flags, uint count, uint timeout)
{
FLASHWINFO fi = new FLASHWINFO();
fi.cbSize = Convert.ToUInt32(Marshal.SizeOf(fi));
fi.hwnd = handle;
fi.dwFlags = flags;
fi.uCount = count;
fi.dwTimeout = timeout;
return fi;
}
/// <summary>
/// Flash the specified Window (form) for the specified number of times
/// </summary>
/// <param name="form">The Form (Window) to Flash.</param>
/// <param name="count">The number of times to Flash.</param>
/// <returns></returns>
public static bool Flash(System.Windows.Forms.Form form, uint count)
{
if (Win2000OrLater)
{
FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL, count, 0);
return FlashWindowEx(ref fi);
}
return false;
}
/// <summary>
/// Start Flashing the specified Window (form)
/// </summary>
/// <param name="form">The Form (Window) to Flash.</param>
/// <returns></returns>
public static bool Start(System.Windows.Forms.Form form)
{
if (Win2000OrLater)
{
FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL, uint.MaxValue, 0);
return FlashWindowEx(ref fi);
}
return false;
}
/// <summary>
/// Stop Flashing the specified Window (form)
/// </summary>
/// <param name="form"></param>
/// <returns></returns>
public static bool Stop(System.Windows.Forms.Form form)
{
if (Win2000OrLater)
{
FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_STOP, uint.MaxValue, 0);
return FlashWindowEx(ref fi);
}
return false;
}
/// <summary>
/// A boolean value indicating whether the application is running on Windows 2000 or later.
/// </summary>
private static bool Win2000OrLater
{
get { return System.Environment.OSVersion.Version.Major >= 5; }
}
}

Categories

Resources