I have the following powershell code to dump the process memory from a given process. In this case, cmd.exe.
It opens the given process with PROCESS_ALL_ACCESS and uses VirtualQueryEx to list all the memory regions. For each memory region it finds, it calls ReadProcessMemory and writes the bytes read to a file.
$typeDefinition = #"
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
public static partial class MemApi
{
[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_BASIC_INFORMATION64
{
public ulong BaseAddress;
public ulong AllocationBase;
public int AllocationProtect;
public int __alignment1;
public ulong RegionSize;
public int State;
public int Protect;
public int Type;
public int __alignment2;
}
[Flags]
public enum ProcessAccessFlags : uint
{
VM_READ = 0x00000010,
PROCESS_ALL_ACCESS = 0x001F0FFF
}
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr OpenProcess(
ProcessAccessFlags dwAccess,
[MarshalAs(UnmanagedType.Bool)]
bool bInheritHandle,
int dwProcId
);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern int VirtualQueryEx(
IntPtr hProc,
IntPtr lpAddr,
out MEMORY_BASIC_INFORMATION64 lpBuf,
int dwLen
);
public static List<MEMORY_BASIC_INFORMATION64> ListMemoryRegions(
IntPtr hProc
)
{
long maxAddr = 0x00007FFFFFFFFFFF;
long addr = 0;
List<MEMORY_BASIC_INFORMATION64> regions = new List<MEMORY_BASIC_INFORMATION64>();
do
{
MEMORY_BASIC_INFORMATION64 m;
int result = VirtualQueryEx(hProc, (IntPtr)addr, out m, (int)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION64)));
if (addr == (long)m.BaseAddress + (long)m.RegionSize)
break;
addr = (long)m.BaseAddress + (long)m.RegionSize;
regions.Add(m);
} while (addr <= maxAddr);
return regions;
}
[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern Boolean ReadProcessMemory(
IntPtr hProc,
IntPtr lpBaseAddr,
byte[] lpBuf,
UInt32 nSize,
ref UInt32 lpNBytes
);
}
"#
Add-Type -TypeDefinition $typeDefinition -Language CSharp
$cmd = Get-Process -Name cmd
$handle = [MemApi]::OpenProcess(0x1F0FFF, $false, $cmd.Id)
$regions = [MemApi]::ListMemoryRegions($handle)
$fileStream = [System.IO.File]::OpenWrite("$env:TEMP\cmd.DMP")
Write-Host "$($regions.Count) memory segments have been found"
for ($i = 0; $i -lt $regions.Count; $i++)
{
Write-Host "Exporting memory region $("{0:d4}/{1:d4}" -f $i, ($regions.Count + 1)) ($("0x{0:X16} - 0x{1:X16}" -f $regions[$i].BaseAddress, ($regions[$i].BaseAddress + $regions[$i].RegionSize)))..."
try
{
$read = 0
$buff = New-Object byte[] $regions[$i].RegionSize
if ([MemApi]::ReadProcessMemory($handle, [System.IntPtr]::new($regions[$i].BaseAddress), $buff, $buff.Length, [ref]$read) -eq $true)
{
$fileStream.Write($buff, 0, $buff.Length)
}
} catch { } finally { }
}
$fileStream.Close()
I would like to avoid using dbghelp.dll to do this, but is there a way I can create a MiniDump file out of this raw memory? Would I need to change the code at all or is there an external tool that can convert this?
I've tried to take a look into the MiniDumpWriteDump function to see if I can replicate it at all but I've had no luck.
I've also tried volatility's raw2dmp function, but this has not worked unfortunately.
Apologies if this is completely on the wrong tracks!
Any help would be much appreciated - Merry Christmas!
I'm trying to pull folder icons for a listview in a winform. I'm using [Drawing.Icon]::ExtractAssociatedIcon for file icons, but it doesn't work on folders.
I found this question here on stack which said to use SHGetStockIconInfo, but I can't wrap my head around how to load this function and use it in Powershell (I have no C programming experience). I managed to pull this from the linked question, but I'm stuck:
Add-Type -NameSpace WinAPI -Name DefaultIcons -MemberDefinition #'
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHSTOCKICONINFO {
public uint cbSize;
public IntPtr hIcon;
public int iSysIconIndex;
public int iIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szPath;
}
[DllImport("shell32.dll")]
public static extern int SHGetStockIconInfo(uint siid, uint uFlags, ref SHSTOCKICONINFO psii);
[DllImport("user32.dll")]
public static extern bool DestroyIcon(IntPtr handle);
private const uint SHSIID_FOLDER = 0x3;
private const uint SHGSI_ICON = 0x100;
private const uint SHGSI_LARGEICON = 0x0;
private const uint SHGSI_SMALLICON = 0x1;
'#
This runs without error, but that's as far as I can get and I don't fully understand what's going on.
I know I need to call the function SHGetStockIconInfo with three parameters:
uint to identify what icon I want
uint flag for the size of icon I want
a reference to a SHSTOCKICONINFO structure
The question I got all this from includes a GetStockIcon function definition, but I keep getting errors trying to include it saying The type or namespace name 'Icon' could not be found (are you missing a using directive or an assembly reference?)
With a few adjustments I got the code from that C# question to work under both PowerShell 5.1 and 7.2.6.
$refAsm = if( $PSVersionTable.PSVersion.Major -le 5 ) {'System.Drawing'} else {'System.Drawing.Common'}
Add-Type -ReferencedAssemblies $refAsm -TypeDefinition #'
using System;
using System.Runtime.InteropServices;
using System.Drawing;
public static class DefaultIcons
{
public static Icon GetStockIcon(uint type, uint size)
{
var info = new SHSTOCKICONINFO();
info.cbSize = (uint)Marshal.SizeOf(info);
SHGetStockIconInfo(type, SHGSI_ICON | size, ref info);
var icon = (Icon)Icon.FromHandle(info.hIcon).Clone(); // Get a copy that does not use the original handle
DestroyIcon(info.hIcon); // Clean up native icon to prevent resource leak
return icon;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHSTOCKICONINFO
{
public uint cbSize;
public IntPtr hIcon;
public int iSysIconIndex;
public int iIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szPath;
}
[DllImport("shell32.dll")]
public static extern int SHGetStockIconInfo(uint siid, uint uFlags, ref SHSTOCKICONINFO psii);
[DllImport("user32.dll")]
public static extern bool DestroyIcon(IntPtr handle);
public const uint SHSIID_FOLDER = 0x3;
public const uint SHGSI_ICON = 0x100;
public const uint SHGSI_LARGEICON = 0x0;
public const uint SHGSI_SMALLICON = 0x1;
}
'#
Use it like this:
$folderIconLarge = [DefaultIcons]::GetStockIcon( [DefaultIcons]::SHSIID_FOLDER, [DefaultIcons]::SHGSI_LARGEICON )
$folderIconSmall = [DefaultIcons]::GetStockIcon( [DefaultIcons]::SHSIID_FOLDER, [DefaultIcons]::SHGSI_SMALLICON )
I'm in the process of building a little utility using powershell and have spent a good deal of time teaching myself about building windows forms and controls using powershell. In order to keep my deployment to a small footprint I have packaged all of my graphics icons (.ico) and images (.bmp) in a dynamically linked library (*.dll) using a third party utilty (greenfish icon editor pro, which I highly recommend).
The problem that I have now is that I can't load the assembly because the resulting *.dll is missing a manifest. I've searched all over but was unable to find any concise answer on how to access the resource without using the add-type cmdlet to load the *.dll.
I was able to find a way to load icons from a dll in an answer to this post from Kazun. Then utilizing technet I was able to find a corresponding entry point in the dll for a method to extract a bitmap. See my kludged together script below which combines both the IconExtractor and my newly created BitmapExtractor classes.
$code = #"
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace System {
public class IconExtractor { #Credit to Kazun from https://social.technet.microsoft.com/Forums/windows/en-US/16444c7a-ad61-44a7-8c6f-b8d619381a27/using-icons-in-powershell-scripts?forum=winserverpowershell
public static Icon Extract(string file, int number, bool largeIcon) {
IntPtr large;
IntPtr small;
ExtractIconEx(file, number, out large, out small, 1);
try {
return Icon.FromHandle(largeIcon ? large : small);
} catch {
return null;
}
}
[DllImport("Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons);
}
public class BitmapExtractor { #Credit to Chad Estes (chadwestes#gmail.com)
public static Bitmap Extract(string file, string resourceID) {
IntPtr hModule = LoadLibraryEx(file,IntPtr.Zero,0x00000020);
Bitmap bmp = null;
IntPtr hBitmap = IntPtr.Zero;
try {
if (hModule != IntPtr.Zero) {
hBitmap = LoadBitmap(hModule, resourceID);
if (hBitmap != IntPtr.Zero) {
bmp = Bitmap.FromHbitmap(hBitmap);
}
}
} catch {
bmp = null;
} finally {
DeleteObject(hBitmap);
FreeLibrary(hModule);
}
return bmp;
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, uint dwFlags);
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool FreeLibrary(IntPtr hModule);
[DllImport("user32.dll")]
static extern IntPtr LoadBitmap(IntPtr hInstance, string lpBitmapName);
[DllImport("gdi32.dll", EntryPoint="DeleteObject", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool DeleteObject(IntPtr hBitmap);
}
}
"#
Add-Type -TypeDefinition $code -ReferencedAssemblies System.Drawing
$myImage = [System.BitmapExtractor]::Extract("C:\SomePath\Some.dll", "LOGO")
$myIcon = [System.IconExtractor]::Extract("C:\SomePath\Some.dll", 1, $true)
This question is very closely related to How to get the “KeyPress” event from a Word 2010 Addin (developed in C#)? (and in fact includes the sample code from the answer to that question), but this is specifically about developing in Visual Studio (Professional) 2015 for Word 2016 running in Windows 10.
I’m trying to detect when text changes in a Word document from a VSTO add-in. I understand from
How to get the “KeyPress” event from a Word 2010 Addin (developed in C#)? (Nov 14, 2011)
Capturing keydown event of MS Word using C# (Oct 21, 2012)
How to raise an event on MS word Keypress (Oct 24, 2012)
How to trap keypress event in MSword using VSTO? (Nov 5, 2012)
that there’s no event-driven way to do this. Word simply does not send events when text changes.
I’ve seen two workarounds discussed:
Use the WindowSelectionChange event. Unfortunately, this event appears to be sent when the selection is changed by pressing arrow keys, using the mouse, performing undo or redo, and probably other actions, but not when typing or deleting.
Use a low-level keydown event hook. This has been discussed in several of those StackOverflow questions, and was also called a “widely spread technique” in a thread on a Visual Studio forum in February 2014.
I’m trying to use the code in the answer to How to get the “KeyPress” event from a Word 2010 Addin (developed in C#)?, and it seems to observe every keydown event except those sent to Word 2016.
Here’s the code I’m using, for ease of reference.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace KeydownWordAddIn
{
public partial class ThisAddIn
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static IntPtr hookId = IntPtr.Zero;
private delegate IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam);
private static HookProcedure procedure = HookCallback;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProcedure lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr SetHook(HookProcedure procedure)
{
using (Process process = Process.GetCurrentProcess())
using (ProcessModule module = process.MainModule)
return SetWindowsHookEx(WH_KEYBOARD_LL, procedure, GetModuleHandle(module.ModuleName), 0);
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int pointerCode = Marshal.ReadInt32(lParam);
string pressedKey = ((Keys)pointerCode).ToString();
// Do some sort of processing on key press.
var thread = new Thread(() => {
Debug.WriteLine(pressedKey);
});
thread.Start();
}
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
private void ThisAddIn_Startup(object sender, EventArgs e)
{
hookId = SetHook(procedure);
}
private void ThisAddIn_Shutdown(object sender, EventArgs e)
{
UnhookWindowsHookEx(hookId);
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
When I run Word 2016 with this add-in, I see keydown events sent to the Edge browser and even Visual Studio, but not to Word itself.
Are keydown hooks somehow prevented in Word 2016, or am I doing something wrong?
Everthing should work fine if you don't use a low-level hook in your VSTO add-in.
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetCurrentThreadId();
const int WH_KEYBOARD = 2;
private static IntPtr SetHook(HookProcedure procedure)
{
var threadId = (uint)SafeNativeMethods.GetCurrentThreadId();
return SetWindowsHookEx(WH_KEYBOARD, procedure, IntPtr.Zero, threadId);
}
Please note that you probably also need to create a hook to intercept mouse messages as it is possible to modify the text of a document solely by mouse interactions (e.g. copy and paste via Ribbon or context menu).
VSTO Sample
Here is a complete working VSTO sample including keyboard and mouse hooks:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Office = Microsoft.Office.Core;
namespace SampleAddinWithKeyboardHook
{
public partial class ThisAddIn
{
// NOTE: We need a backing field to prevent the delegate being garbage collected
private SafeNativeMethods.HookProc _mouseProc;
private SafeNativeMethods.HookProc _keyboardProc;
private IntPtr _hookIdMouse;
private IntPtr _hookIdKeyboard;
private void ThisAddIn_Startup(object sender, EventArgs e)
{
_mouseProc = MouseHookCallback;
_keyboardProc = KeyboardHookCallback;
SetWindowsHooks();
}
private void ThisAddIn_Shutdown(object sender, EventArgs e)
{
UnhookWindowsHooks();
}
private void SetWindowsHooks()
{
uint threadId = (uint)SafeNativeMethods.GetCurrentThreadId();
_hookIdMouse =
SafeNativeMethods.SetWindowsHookEx(
(int)SafeNativeMethods.HookType.WH_MOUSE,
_mouseProc,
IntPtr.Zero,
threadId);
_hookIdKeyboard =
SafeNativeMethods.SetWindowsHookEx(
(int)SafeNativeMethods.HookType.WH_KEYBOARD,
_keyboardProc,
IntPtr.Zero,
threadId);
}
private void UnhookWindowsHooks()
{
SafeNativeMethods.UnhookWindowsHookEx(_hookIdKeyboard);
SafeNativeMethods.UnhookWindowsHookEx(_hookIdMouse);
}
private IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
var mouseHookStruct =
(SafeNativeMethods.MouseHookStructEx)
Marshal.PtrToStructure(lParam, typeof(SafeNativeMethods.MouseHookStructEx));
// handle mouse message here
var message = (SafeNativeMethods.WindowMessages)wParam;
Debug.WriteLine(
"{0} event detected at position {1} - {2}",
message,
mouseHookStruct.pt.X,
mouseHookStruct.pt.Y);
}
return SafeNativeMethods.CallNextHookEx(
_hookIdKeyboard,
nCode,
wParam,
lParam);
}
private IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
// handle key message here
Debug.WriteLine("Key event detected.");
}
return SafeNativeMethods.CallNextHookEx(
_hookIdKeyboard,
nCode,
wParam,
lParam);
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support.
/// </summary>
private void InternalStartup()
{
Startup += ThisAddIn_Startup;
Shutdown += ThisAddIn_Shutdown;
}
#endregion
}
internal static class SafeNativeMethods
{
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
public enum HookType
{
WH_KEYBOARD = 2,
WH_MOUSE = 7
}
public enum WindowMessages : uint
{
WM_KEYDOWN = 0x0100,
WM_KEYFIRST = 0x0100,
WM_KEYLAST = 0x0108,
WM_KEYUP = 0x0101,
WM_LBUTTONDBLCLK = 0x0203,
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MBUTTONDBLCLK = 0x0209,
WM_MBUTTONDOWN = 0x0207,
WM_MBUTTONUP = 0x0208,
WM_MOUSEACTIVATE = 0x0021,
WM_MOUSEFIRST = 0x0200,
WM_MOUSEHOVER = 0x02A1,
WM_MOUSELAST = 0x020D,
WM_MOUSELEAVE = 0x02A3,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_MOUSEHWHEEL = 0x020E,
WM_RBUTTONDBLCLK = 0x0206,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205,
WM_SYSDEADCHAR = 0x0107,
WM_SYSKEYDOWN = 0x0104,
WM_SYSKEYUP = 0x0105
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(
int idHook,
HookProc lpfn,
IntPtr hMod,
uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(
IntPtr hhk,
int nCode,
IntPtr wParam,
IntPtr lParam);
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetCurrentThreadId();
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
public static implicit operator System.Drawing.Point(Point p)
{
return new System.Drawing.Point(p.X, p.Y);
}
public static implicit operator Point(System.Drawing.Point p)
{
return new Point(p.X, p.Y);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct MouseHookStructEx
{
public Point pt;
public IntPtr hwnd;
public uint wHitTestCode;
public IntPtr dwExtraInfo;
public int MouseData;
}
}
}
VBE Add-in Sample
And here is a working sample for the VBA editor (VBE add-in):
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Extensibility;
namespace VbeAddin
{
[ComVisible(true)]
[ProgId("VbeAddin.Connect")]
[Guid("95840C70-5A1A-4EDB-B436-40E8BF030469")]
public class Connect : StandardOleMarshalObject, IDTExtensibility2
{
// NOTE: We need a backing field to prevent the delegate being garbage collected
private SafeNativeMethods.HookProc _mouseProc;
private SafeNativeMethods.HookProc _keyboardProc;
private IntPtr _hookIdMouse;
private IntPtr _hookIdKeyboard;
#region IDTExtensibility2 Members
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
_mouseProc = MouseHookCallback;
_keyboardProc = KeyboardHookCallback;
SetWindowsHooks();
}
public void OnDisconnection(ext_DisconnectMode removeMode, ref Array custom)
{
UnhookWindowsHooks();
}
public void OnAddInsUpdate(ref Array custom)
{
}
public void OnStartupComplete(ref Array custom)
{
}
public void OnBeginShutdown(ref Array custom)
{
}
#endregion
private void SetWindowsHooks()
{
uint threadId = (uint)SafeNativeMethods.GetCurrentThreadId();
_hookIdMouse =
SafeNativeMethods.SetWindowsHookEx(
(int)SafeNativeMethods.HookType.WH_MOUSE,
_mouseProc,
IntPtr.Zero,
threadId);
_hookIdKeyboard =
SafeNativeMethods.SetWindowsHookEx(
(int)SafeNativeMethods.HookType.WH_KEYBOARD,
_keyboardProc,
IntPtr.Zero,
threadId);
}
private void UnhookWindowsHooks()
{
SafeNativeMethods.UnhookWindowsHookEx(_hookIdKeyboard);
SafeNativeMethods.UnhookWindowsHookEx(_hookIdMouse);
}
private IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
var mouseHookStruct =
(SafeNativeMethods.MouseHookStructEx)
Marshal.PtrToStructure(
lParam,
typeof(SafeNativeMethods.MouseHookStructEx));
// handle mouse message here
var message = (SafeNativeMethods.WindowMessages)wParam;
Debug.WriteLine(
"{0} event detected at position {1} - {2}",
message,
mouseHookStruct.pt.X,
mouseHookStruct.pt.Y);
}
return SafeNativeMethods.CallNextHookEx(
_hookIdKeyboard,
nCode,
wParam,
lParam);
}
private IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
// handle key message here
Debug.WriteLine("Key event detected.");
}
return SafeNativeMethods.CallNextHookEx(
_hookIdKeyboard,
nCode,
wParam,
lParam);
}
}
internal static class SafeNativeMethods
{
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
public enum HookType
{
WH_KEYBOARD = 2,
WH_MOUSE = 7
}
public enum WindowMessages : uint
{
WM_KEYDOWN = 0x0100,
WM_KEYFIRST = 0x0100,
WM_KEYLAST = 0x0108,
WM_KEYUP = 0x0101,
WM_LBUTTONDBLCLK = 0x0203,
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MBUTTONDBLCLK = 0x0209,
WM_MBUTTONDOWN = 0x0207,
WM_MBUTTONUP = 0x0208,
WM_MOUSEACTIVATE = 0x0021,
WM_MOUSEFIRST = 0x0200,
WM_MOUSEHOVER = 0x02A1,
WM_MOUSELAST = 0x020D,
WM_MOUSELEAVE = 0x02A3,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_MOUSEHWHEEL = 0x020E,
WM_RBUTTONDBLCLK = 0x0206,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205,
WM_SYSDEADCHAR = 0x0107,
WM_SYSKEYDOWN = 0x0104,
WM_SYSKEYUP = 0x0105
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(
int idHook,
HookProc lpfn,
IntPtr hMod,
uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(
IntPtr hhk,
int nCode,
IntPtr wParam,
IntPtr lParam);
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetCurrentThreadId();
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential)]
public struct MouseHookStructEx
{
public Point pt;
public IntPtr hwnd;
public uint wHitTestCode;
public IntPtr dwExtraInfo;
public int MouseData;
}
}
}
I've experienced this same exact issue in Word 2013 and had to come up with a somewhat "creative" solution. It uses diffplex to monitor changes in the text of the active document and fires events when it changes. It's less than ideal but we do what we have to do to make things work.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Word = Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Word;
using System.ComponentModel;
namespace WordUtils {
public class TextChangeDetector {
public Word.Application Application;
private BackgroundWorker bg;
public delegate void TextChangeHandler(object sender, TextChangedEventArgs e);
public event TextChangeHandler OnTextChanged;
public TextChangeDetector(Word.Application app) {
this.Application = app;
}
public void Start() {
bg = new BackgroundWorker();
bg.WorkerReportsProgress = true;
bg.WorkerSupportsCancellation = true;
bg.ProgressChanged += bg_ProgressChanged;
bg.DoWork += bg_DoWork;
bg.RunWorkerAsync(this.Application);
}
private void bg_ProgressChanged(object sender, ProgressChangedEventArgs e) {
switch (e.ProgressPercentage) {
case 50: //change
if (OnTextChanged != null) {
OnTextChanged(this, new TextChangedEventArgs((char)e.UserState));
}
break;
}
}
private void bg_DoWork(object sender, DoWorkEventArgs e) {
Word.Application wordApp = e.Argument as Word.Application;
BackgroundWorker bg = sender as BackgroundWorker;
string lastPage = string.Empty;
while (true) {
try {
if (Application.Documents.Count > 0) {
if (Application.ActiveDocument.Words.Count > 0) {
var currentPage = Application.ActiveDocument.Bookmarks["\\Page"].Range.Text;
if (currentPage != null && currentPage != lastPage) {
var differ = new DiffPlex.Differ();
var builder = new DiffPlex.DiffBuilder.InlineDiffBuilder(differ);
var difference = builder.BuildDiffModel(lastPage, currentPage);
var change = from d in difference.Lines where d.Type != DiffPlex.DiffBuilder.Model.ChangeType.Unchanged select d;
if (change.Any()) {
bg.ReportProgress(50, change.Last().Text.Last());
}
lastPage = currentPage;
}
}
}
} catch (Exception) {
}
if (bg.CancellationPending) {
break;
}
System.Threading.Thread.Sleep(100);
}
}
public void Stop() {
if (bg != null && !bg.CancellationPending) {
bg.CancelAsync();
}
}
}
public class TextChangedEventArgs : EventArgs {
public char Letter;
public TextChangedEventArgs(char letter) {
this.Letter = letter;
}
}
}
Usage:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Word = Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Word;
using WordUtils;
namespace WordAddIn1 {
public partial class ThisAddIn {
TextChangeDetector detector;
private void ThisAddIn_Startup(object sender, System.EventArgs e) {
detector = new TextChangeDetector(Application);
detector.OnTextChanged += detector_OnTextChanged;
detector.Start();
}
void detector_OnTextChanged(object sender, TextChangedEventArgs e) {
Console.WriteLine(e.Letter);
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e) {
detector.Stop();
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup() {
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
I'm trying to call CreateProcess from PowerShell. The API call indicates that it succeeded, but no new application or process is visible in Task Manager.
Add-Type -TypeDefinition #"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public STARTF dwFlags;
public ShowWindow wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[Flags]
public enum CreationFlags : int
{
NONE = 0,
DEBUG_PROCESS = 0x00000001,
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
CREATE_SUSPENDED = 0x00000004,
DETACHED_PROCESS = 0x00000008,
CREATE_NEW_CONSOLE = 0x00000010,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
CREATE_SEPARATE_WOW_VDM = 0x00000800,
CREATE_SHARED_WOW_VDM = 0x00001000,
CREATE_PROTECTED_PROCESS = 0x00040000,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
CREATE_NO_WINDOW = 0x08000000,
}
[Flags]
public enum STARTF : uint
{
STARTF_USESHOWWINDOW = 0x00000001,
STARTF_USESIZE = 0x00000002,
STARTF_USEPOSITION = 0x00000004,
STARTF_USECOUNTCHARS = 0x00000008,
STARTF_USEFILLATTRIBUTE = 0x00000010,
STARTF_RUNFULLSCREEN = 0x00000020, // ignored for non-x86 platforms
STARTF_FORCEONFEEDBACK = 0x00000040,
STARTF_FORCEOFFFEEDBACK = 0x00000080,
STARTF_USESTDHANDLES = 0x00000100,
}
public enum ShowWindow : short
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_FORCEMINIMIZE = 11,
SW_MAX = 11
}
public static class Kernel32
{
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
CreationFlags dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
}
"#
$si = New-Object STARTUPINFO
$pi = New-Object PROCESS_INFORMATION
$si.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($si)
$si.wShowWindow = [ShowWindow]::SW_SHOW
$pSec = New-Object SECURITY_ATTRIBUTES
$tSec = New-Object SECURITY_ATTRIBUTES
$pSec.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($pSec)
$tSec.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($tSec)
[Kernel32]::CreateProcess("c:\windows\notepad.exe", $null, [ref] $pSec, [ref] $tSec, $false, [CreationFlags]::NONE, [IntPtr]::Zero, $null, [ref] $si, [ref] $pi)
[System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
$pi
Try using a real CurrentDirectory instead of $null:
[Kernel32]::CreateProcess("c:\windows\notepad.exe", $null, [ref] $pSec, [ref] $tSec, $false, [CreationFlags]::NONE, [IntPtr]::Zero, "c:", [ref] $si, [ref] $pi)