P/Invoking SetWindowLong and CallWindowProc in managed code (compact framework) - c#

I am trying to override the window procedure for the winmobile taskbar (in order to catch and block pressed buttons) by using SetWindowLong. I have created a class with one method for overriding and one for restoring the window procedure. The MessageReceived method is the one I use to replace the taskbar window procedure with. My class looks the following way:
class ButtonBlocker
{
public delegate IntPtr WindowProc(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam);
public static WindowProc newWindowDeleg;
private static IntPtr oldWindowProc;
[DllImport("coredll.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("coredll.dll")]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("coredll.dll")]
static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("coredll.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
private static IntPtr MessageReceived(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam)
{
Debug.WriteLine("Message received");
return CallWindowProc(oldWindowProc, hwnd, uMsg, wParam, lParam);
}
public static void OverrideWindowProc()
{
newWindowDeleg = MessageReceived;
IntPtr taskBarHandle = FindWindow("HHTaskBar", null);
int newWndProc = Marshal.GetFunctionPointerForDelegate(newWindowDeleg).ToInt32();
int result = SetWindowLong(taskBarHandle, -4, newWndProc);
oldWindowProc = (IntPtr)result;
if (result == 0)
{
MessageBox.Show("Failed to SetWindowLong");
}
}
public static void RestoreWindowProc()
{
IntPtr taskBarHandle = FindWindow("HHTaskBar", null);
int result = SetWindowLong(taskBarHandle, -4, oldWindowProc.ToInt32());
}
}
The behavior in the mobile emulator is the following - after I press a button, "Message received" is displayed in the Debug output, but the program crashes and offers to send a crash report to Microsoft. The visual studio debugger hangs and doesn't react to the stop command. It unfreezes only after an emulator reset.
The problem seems to be with the CallWindowProc at the end of the MessageReceived method. Most probably, there is some issue with the old windowproc address.
If I try to execute the RestoreWindowProc code immediately after the OverrideWindowProc code (with no messages being intercepted by my function), the application exits okay and the debugger does not freeze.
Any ideas on how to get this to work would be appreciated.
I am using Visual Studio 2008 SP1. The project targets .NET framework v3.5, Windows Mobile 6 Professional.

You cannot replace the window procedure of a window that is owned by another process. The address of the replacement function is only valid in your process. It causes an immediate bomb in the other process. You would have to inject a DLL into the target process to work around this problem. You can't inject DLLs written in a managed language, the CLR isn't initialized.

Related

Window drag event c#

I am trying to hook into the window drag event of every active/visible windows using C# in a System tray app. I have the code to find and move all the windows figured out using PInvoke, but am unable to figure out how to hook into the drag event.
My goal is to run the program in the background and while dragging a window be able to snap it into preset locations (showing preview windows while the drag is occurring and snapping when released).
How would I hook into the window drag event?
You could try to set a CBT hook and handling the case when a window is about to be moved or sized, though this hook monitors much more than just this case:
class YourClassName
{
const int WH_CBT = 5;
private delegate IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam);
private IntPtr hHook;
private HookCallback hookCallback = new HookCallback(Callback);
[DllImport("user32")]
private static extern IntPtr SetWindowsHookEx(int idHook, HookCallback lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32")]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
public void SetHook()
{
using (Process p = Process.GetCurrentProcess()
{
using (var mainMod = p.MainModule)
{
hHook = SetWindowsHookEx(WH_CBT, hookCallback, GetModuleHandle(mainMod.ModuleName, 0);
}
}
}
public bool Unhook()
{
return UnhookWindowsHookEx(hHook);
}
public IntPtr Callback(int nCode, IntPtr wParam, IntPtr lParam
{
if (nCode < 0)
return CallNextHookEx(hHook, nCode, wParam, lParam);
switch (nCode)
{
case 0:
// Handle window move/size here
// wParam is the handle of the window being moved or sized
// other cases...
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
}
Don't forget to call Unhook() before terminating to free system resources.
For more information on SetWindowsHookEx see the msdn page.

C# Win32 Interop Crashes when Enumerating Window Handles

I have a C# wrapper for some Win32 operations involving window handles, but I am experiencing an unexpected crash, with no details, when I call a Win32 function.
Interestingly, this whole code sample works fine when the class is constructed (at application init time), but later fails when the same buildCache() method is called.
Here is the relevant code:
public delegate bool CallBack(int hWnd, int lParam);
public class Win32Interop {
private Dictionary<int, string> windowCache = new Dictionary<int, string>();
public Win32Interop() {
buildCache();
}
public void buildCache() {
windowCache.Clear();
CallBack hWndCacher = new CallBack(saveHWndHandler);
EnumWindows(hWndCacher, 0);
}
public void doThings(string title, uint message, bool rebuildCache = false) {
//Use the window title to get its handle
int hWnd = titleToHWnd(title, rebuildCache);
SendMessage(hWnd, message, 0, 0);
}
private bool saveHWndHandler(int hWnd, int lParam) {
if(IsWindow(hWnd) != 0) { / ***** CRASHES HERE ***** /
int length = GetWindowTextLength(hWnd);
StringBuilder title = new StringBuilder(length + 1);
GetWindowText(hWnd, title, title.Capacity);
string formatted = title.ToString().Trim();
windowCache.Add(hWnd, formatted);
}
return true;
}
private int titleToHWnd(string title, bool rebuildCache = false) {
if(rebuildCache)
buildCache();
if(windowCache.ContainsValue(title)) {
return windowCache.FirstOrDefault(x => x.Value.Contains(title)).Key;
} else {
throw new KeyNotFoundException(string.Format("\"{0}\" is not a window title which is available in the cache.", title));
}
}
#region Win32 API Functions
[DllImport("user32.dll")]
private static extern int EnumWindows(CallBack lpEnumFunc, int lParam);
[DllImport("user32.dll")]
private static extern int GetWindowText(int hWnd, StringBuilder lpString, int maxCount);
[DllImport("user32.dll")]
private static extern int GetWindowTextLength(int hWnd);
[DllImport("user32.dll")]
private static extern int IsWindow(int hWnd);
[DllImport("user32.lib")]
private static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
#endregion
}
Inside of the saveHWndHandler() method, I've marked the line were the debugger shows execution stops. Interestingly, there are usually ~300 window handles returned by EnumWindows(), and it always crashes on iteration number 45 or 46. The window handle it crashes on is a reasonable value, such as 12345.
According to MSDN, IsWindow() should return 0, if a window was not associated with the handle, not crash the thread.
Does anyone know why this is happening? There isn't a thrown exception, or any details in the Windows event log.
Thank you.
For those who don't want to figure out the buildCache() process works:
(1.) When buildCache() is called, the dictionary of <HWnd, Title> values is cleared.
(2.) The Win32 function EnumWindows() is called, which calls the saveHWndHandler() method for each window handle.
(3.) saveHWndHandler() will check if the current window handle still exists, the call another Win32 to function to get the window title from the handle.
(4.) The title and window handle are added to the dictionary.
I can't reproduce your issue, but a likely problem is that all of your P/Invoke signatures are wrong. HWND, LPARAM, and WPARAM data types need to at least be mapped to IntPtr in P/Invoke code.
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString,
int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam,
IntPtr lParam);
You'll need to change your corresponding instance method signatures and usages to match these correct signatures.

FindWindow return 0 in Windows 7

I have a C# program. The program creates an Adobe Reader process and prints a PDF document. It works fine in Windows XP, but does not work in Windows 7. I have checked that the AcroRd32.exe path is correct in Windows 7. The FindWindow method always returns 0 in Windows 7.
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("User32.dll")]
public static extern IntPtr FindWindow(string ClassN, string WindN);
[DllImport("user32.dll", EntryPoint = "SendMessageA")]
private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("shell32.dll ")]
public static extern int ShellExecute(IntPtr hwnd, string lpszOp, string lpszFile, string lpszParams, string lpszDir, int FsShowCmd);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern uint WinExec(string lpCmdLine, uint uCmdShow);
public bool isAcrobatExsists(string acrobatLoc)
{
IntPtr currentHandle = getWindowHandlerByClass("AcrobatSDIWindow");
if (currentHandle != IntPtr.Zero)
{
return true;
}
return false;
}
private static IntPtr getWindowHandlerByClass(string className)
{
IntPtr currentHandle = FindWindow(className, null);
return currentHandle;
}
private static IntPtr getWindowHandlerByName(string appName)
{
IntPtr currentHandle = FindWindow(null, appName);
return currentHandle;
}
Findwindow can depend a great deal on how you are running the application. It sounds like you might be running it as a scheduled task or a windows service. These run in a different session than what the user's desktop window is in, so it won't see or be able to interact with them.
They introduced this change in Windows Vista, so applications that do this will work just fine in XP but fail in Windows Vista or later.
Here's a link from msdn about it: Application Compatibility: Session 0 isolation

CoInternetSetFeatureEnabled with FEATURE_BLOCK_INPUT_PROMPTS does not work

I am trying to use CoInternetSetFeatureEnabled API for blocking prompt dialog as it show below in application that hosts WebBrowser control, but it does not work. Updating registry does not work either, following MSDN - Internet Feature Controls (I..L). I am using IE9 on Win7 64 bit.
FEATURE_BLOCK_INPUT_PROMPTS Internet Explorer 7. Enable or disable the
pop-up blocker to show input prompt dialog boxes. Used pop-up blocker
to mitigate the risk of spoofing.
private const int FEATURE_BLOCK_INPUT_PROMPTS = 27;
[DllImport("urlmon.dll")]
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
public static extern int CoInternetSetFeatureEnabled(
int FeatureEntry,
[MarshalAs(UnmanagedType.U4)] int dwFlags,
bool fEnable);
public static int disableInputPrompts(bool state)
{
return CoInternetSetFeatureEnabled(
FEATURE_BLOCK_INPUT_PROMPTS, SET_FEATURE_ON_PROCESS, state);
}
Update Just have tried in WinXP with IE8 and this feature is working. Does it depends on something else?
Update According to msdn this feature is by default enabled in IE9 but it does not work. Clicking on "Try It" button in w3schools sample initiates the prompt dialog, is that a security bug?
By default, this feature is enabled for Internet Explorer and disabled for
applications hosting the WebBrowser Control.
Any working alternative on how to block prompt dialog in WebBrowser control?
Can this be done by implementing custom security manager IInternetSecurityManager?
The only option I have for now is to use SetWindowsHookEx, and is based on Suppressing Hosted WebBrowser Control Dialogs sample.
internal static class WindowsInterop
{
private const Int32 WM_COMMAND = 0x0111;
private const Int32 WM_INITDIALOG = 0x0110;
private const Int32 WM_SYSCOMMAND = 0x0112;
private const Int32 SC_CLOSE = 0xF060;
private static IntPtr _pWH_CALLWNDPROCRET = IntPtr.Zero;
private static HookProcedureDelegate _WH_CALLWNDPROCRET_PROC =
new HookProcedureDelegate(WindowsInterop.WH_CALLWNDPROCRET_PROC);
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(Int32 hooktype,
HookProcedureDelegate callback, IntPtr hMod, UInt32 dwThreadId);
[DllImport("user32.dll")]
private static extern IntPtr UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll")]
private static extern Int32 CallNextHookEx(IntPtr hhk, Int32 nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern Int32 GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern Int32 GetWindowText(IntPtr hWnd,
StringBuilder text, Int32 maxLength);
[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg,
IntPtr wParam, IntPtr lParam);
// Hook Types
private const Int32 WH_CALLWNDPROCRET = 12;
[StructLayout(LayoutKind.Sequential)]
private struct CWPRETSTRUCT
{
public IntPtr lResult;
public IntPtr lParam;
public IntPtr wParam;
public UInt32 message;
public IntPtr hwnd;
};
// Delegate for a WH_ hook procedure
private delegate Int32 HookProcedureDelegate(Int32 iCode,
IntPtr pWParam, IntPtr pLParam);
// Delegate for the EnumChildWindows method
private delegate Boolean EnumerateWindowDelegate(IntPtr pHwnd, IntPtr pParam);
// Add a Hook into the CALLWNDPROCRET notification chain
internal static void Hook()
{
if (WindowsInterop._pWH_CALLWNDPROCRET == IntPtr.Zero)
{
WindowsInterop._pWH_CALLWNDPROCRET = SetWindowsHookEx(
WH_CALLWNDPROCRET,
WindowsInterop._WH_CALLWNDPROCRET_PROC,
IntPtr.Zero,
(uint)AppDomain.GetCurrentThreadId());
}
}
// Remove the Hook into the CALLWNDPROCRET notification chain
internal static void Unhook()
{
if (WindowsInterop._pWH_CALLWNDPROCRET != IntPtr.Zero)
{
UnhookWindowsHookEx(WindowsInterop._pWH_CALLWNDPROCRET);
}
}
// Hook proceedure called by the OS when a message has been processed by the target Window
private static Int32 WH_CALLWNDPROCRET_PROC(Int32 iCode,
IntPtr pWParam, IntPtr pLParam)
{
if (iCode < 0)
return CallNextHookEx(WindowsInterop._pWH_CALLWNDPROCRET,
iCode, pWParam, pLParam);
CWPRETSTRUCT cwp = (CWPRETSTRUCT)
Marshal.PtrToStructure(pLParam, typeof(CWPRETSTRUCT));
Console.WriteLine(cwp.message);
if (cwp.message == WM_INITDIALOG)
{
// A dialog was initialised, find out what sort it was via it's Caption text
Int32 iLength = GetWindowTextLength(cwp.hwnd);
StringBuilder sb = new StringBuilder(iLength + 1);
GetWindowText(cwp.hwnd, sb, sb.Capacity);
var title = sb.ToString();
if (String.IsNullOrEmpty(title) == false &&
title.IndexOf("prompt", StringComparison.OrdinalIgnoreCase) >= 0)
{
// just close it
SendMessage(cwp.hwnd, WM_SYSCOMMAND, new IntPtr(SC_CLOSE), IntPtr.Zero);
return 1;
}
}
// Call the next hook in the chain
return CallNextHookEx(WindowsInterop._pWH_CALLWNDPROCRET, iCode, pWParam, pLParam);
}
}

How to send text to Notepad in C#/Win32?

I'm trying to use SendMessage to Notepad, so that I can insert written text without making Notepad the active window.
I have done something like this in the past using SendText, but that required giving Notepad focus.
Now, first I'm retrieving the Windows handle:
Process[] processes = Process.GetProcessesByName("notepad");
Console.WriteLine(processes[0].MainWindowHandle.ToString());
I've confirmed it's the right handle for Notepad, the same shown within Windows Task Manager.
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);
From here, I haven't been able to get SendMessage to work in all my experimentation. Am I going in the wrong direction?
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
private void button1_Click(object sender, EventArgs e)
{
Process [] notepads=Process.GetProcessesByName("notepad");
if(notepads.Length==0)return;
if (notepads[0] != null)
{
IntPtr child= FindWindowEx(notepads[0].MainWindowHandle, new IntPtr(0), "Edit", null);
SendMessage(child, 0x000C, 0, textBox1.Text);
}
}
WM_SETTEXT=0x000c
You first have to find the child window where the text is entered. You can do this by finding the child window with the window class "Edit".
Once you have that window handle, use WM_GETTEXT to get the text which is already entered, then modify that text (e.g., add your own), then use WM_SETTEXT to send the modified text back.
using System.Diagnostics;
using System.Runtime.InteropServices;
static class Notepad
{
#region Imports
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]
private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
//this is a constant indicating the window that we want to send a text message
const int WM_SETTEXT = 0X000C;
#endregion
public static void SendText(string text)
{
Process notepad = Process.Start(#"notepad.exe");
System.Threading.Thread.Sleep(50);
IntPtr notepadTextbox = FindWindowEx(notepad.MainWindowHandle, IntPtr.Zero, "Edit", null);
SendMessage(notepadTextbox, WM_SETTEXT, 0, text);
}
}

Categories