It's my first message here and I'm not a native English speaker so thank you in advance for not minding to much about my mistakes.
My problem is that I want to change the default text of a OpenFileDialog and SaveFileDialog because in my application there is an option to change the language settings.
The buttons texts are "Save" and "Open".
I can do it for other buttons thanks to the MessageBoxManager, however I need to do it also for the button "Save" and for the button "Open".
Those buttons have to be handled by other means, because I can't find their "ID" (ID_ok = 1, ID_cancel = 2) and I can't find "Save".
So if you have any idea for a solution I will be very grateful.
#pragma warning disable 0618
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Permissions;
[assembly: SecurityPermission(SecurityAction.RequestMinimum, UnmanagedCode = true)]
namespace System.Windows.Forms {
public class MessageBoxManager {
private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private delegate bool EnumChildProc(IntPtr hWnd, IntPtr lParam);
private const int WH_CALLWNDPROCRET = 12;
private const int WM_DESTROY = 0x0002;
private const int WM_INITDIALOG = 0x0110;
private const int WM_TIMER = 0x0113;
private const int WM_USER = 0x400;
private const int DM_GETDEFID = WM_USER + 0;
private const int MBOK = 1;
private const int MBCancel = 2;
private const int MBAbort = 3;
private const int MBRetry = 4;
private const int MBIgnore = 5;
private const int MBYes = 6;
private const int MBNo = 7;
private const int MBSave = 8;
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll")]
private static extern int UnhookWindowsHookEx(IntPtr idHook);
[DllImport("user32.dll")]
private static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "GetWindowTextLengthW", CharSet = CharSet.Unicode)]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "GetWindowTextW", CharSet = CharSet.Unicode)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int maxLength);
[DllImport("user32.dll")]
private static extern int EndDialog(IntPtr hDlg, IntPtr nResult);
[DllImport("user32.dll")]
private static extern bool EnumChildWindows(IntPtr hWndParent, EnumChildProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "GetClassNameW", CharSet = CharSet.Unicode)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll")]
private static extern int GetDlgCtrlID(IntPtr hwndCtl);
[DllImport("user32.dll")]
private static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
[DllImport("user32.dll", EntryPoint = "SetWindowTextW", CharSet = CharSet.Unicode)]
private static extern bool SetWindowText(IntPtr hWnd, string lpString);
[StructLayout(LayoutKind.Sequential)]
public struct CWPRETSTRUCT
{
public IntPtr lResult;
public IntPtr lParam;
public IntPtr wParam;
public uint message;
public IntPtr hwnd;
};
private static HookProc hookProc;
private static EnumChildProc enumProc;
[ThreadStatic]
private static IntPtr hHook;
[ThreadStatic]
private static int nButton;
/// <summary>
/// OK text
/// </summary>
public static string OK = "&OK";
/// <summary>
/// Cancel text
/// </summary>
public static string Cancel = "&Cancel";
/// <summary>
/// Abort text
/// </summary>
public static string Abort = "&Abort";
/// <summary>
/// Retry text
/// </summary>
public static string Retry = "&Retry";
/// <summary>
/// Ignore text
/// </summary>
public static string Ignore = "&Ignore";
/// <summary>
/// Yes text
/// </summary>
public static string Yes = "&Yes";
/// <summary>
/// No text
/// </summary>
public static string No = "&No";
/// No text
/// </summary>
public static string Save = "&Save";
static MessageBoxManager()
{
hookProc = new HookProc(MessageBoxHookProc);
enumProc = new EnumChildProc(MessageBoxEnumProc);
hHook = IntPtr.Zero;
}
/// <summary>
/// Enables MessageBoxManager functionality
/// </summary>
/// <remarks>
/// MessageBoxManager functionality is enabled on current thread only.
/// Each thread that needs MessageBoxManager functionality has to call this method.
/// </remarks>
public static void Register()
{
if (hHook != IntPtr.Zero)
throw new NotSupportedException("One hook per thread allowed.");
hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, hookProc, IntPtr.Zero, AppDomain.GetCurrentThreadId());
}
/// <summary>
/// Disables MessageBoxManager functionality
/// </summary>
/// <remarks>
/// Disables MessageBoxManager functionality on current thread only.
/// </remarks>
public static void Unregister()
{
if (hHook != IntPtr.Zero)
{
UnhookWindowsHookEx(hHook);
hHook = IntPtr.Zero;
}
}
private static IntPtr MessageBoxHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
return CallNextHookEx(hHook, nCode, wParam, lParam);
CWPRETSTRUCT msg = (CWPRETSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPRETSTRUCT));
IntPtr hook = hHook;
if (msg.message == WM_INITDIALOG)
{
int nLength = GetWindowTextLength(msg.hwnd);
StringBuilder className = new StringBuilder(10);
GetClassName(msg.hwnd, className, className.Capacity);
if (className.ToString() == "#32770")
{
nButton = 0;
EnumChildWindows(msg.hwnd, enumProc, IntPtr.Zero);
if (nButton == 1)
{
IntPtr hButton = GetDlgItem(msg.hwnd, MBCancel);
if (hButton != IntPtr.Zero)
SetWindowText(hButton, OK);
}
}
}
return CallNextHookEx(hook, nCode, wParam, lParam);
}
private static bool MessageBoxEnumProc(IntPtr hWnd, IntPtr lParam)
{
StringBuilder className = new StringBuilder(10);
GetClassName(hWnd, className, className.Capacity);
if (className.ToString() == "Button")
{
int ctlId = GetDlgCtrlID(hWnd);
switch (ctlId)
{
case MBOK:
SetWindowText(hWnd, OK);
break;
case MBCancel:
SetWindowText(hWnd, Cancel);
break;
case MBAbort:
SetWindowText(hWnd, Abort);
break;
case MBRetry:
SetWindowText(hWnd, Retry);
break;
case MBIgnore:
SetWindowText(hWnd, Ignore);
break;
case MBYes:
SetWindowText(hWnd, Yes);
break;
case MBNo:
SetWindowText(hWnd, No);
break;
case MBSave:
SetWindowText(hWnd, Save);
break;
case 1038:
SetWindowText(hWnd, Retry);
break;
}
nButton++;
}
return true;
}
}
Edit:
First, thank to correct my mistakes (many of them) ^^.... The reason, I want translate the button is simple. My program have to be translate in 5 languages that can be selected by the menu. I don't have "window ultimate" to change the setting language like i want.
But anyway, I don't want change the language of the OS at every click. i had only two solutions create all messageBox and openDialog by myself and change the 1500 times where the program call messageBox.show by my new form (what will spend many time...) or translate the buttons correctly for messageBox and open/save dialogBox. The computer used is dedied for this program, no any others programs should be used.
Currently I'm able to translate all messageBox correctly, But my problem is for the Buttons "save", "open"(this text change like my button "ok") if you want i can tell you where you can dowload the project to have a better idea of the fonction realized
Related
Here (below sample program) if the notepad is resized--> maximized and then minized. the window position (maximize) is not retained.
I want to retain the window position as it is before minimizing.
i have tried out the below code. Pls help.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private readonly IntPtr HWND_TOPMOST = new IntPtr(0);
private void button1_Click(object sender, EventArgs e)
{
Process[] Processes = Process.GetProcessesByName("notepad");
string processString = string.Format("Untitled - Notepad");
IntPtr res;
foreach (Process theprocess in Processes)
{
if (theprocess.MainWindowTitle.Contains(processString))
{
if (theprocess.MainWindowHandle == IntPtr.Zero)
{
continue;
}
WindowsNativeCalls.SetActiveWindow(theprocess.MainWindowHandle);
if (theprocess.MainWindowHandle != IntPtr.Zero)
{
if (theprocess.MainWindowTitle.Contains(processString))
{
WindowsNativeCalls.WINDOWPLACEMENT placement = new WindowsNativeCalls.WINDOWPLACEMENT();
WindowsNativeCalls.GetWindowPlacement(theprocess.MainWindowHandle, ref placement);
switch (placement.showCmd)
{
case 1:// SW_NORMAL
case 3:// SW_MAXIMIZE does the job of bring to front
WindowsNativeCalls.SetWindowPos(theprocess.MainWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, WindowsNativeCalls.SWP_NOMOVE | WindowsNativeCalls.SWP_NOSIZE | WindowsNativeCalls.SWP_SHOWWINDOW);
break;
case 2: // SW_SHOWMINIMIZED
placement.showCmd = 9;
WindowsNativeCalls.SetWindowPlacement(theprocess.MainWindowHandle, ref placement);
//res = WindowsNativeCalls.SendMessage(theprocess.MainWindowHandle, WindowsNativeCalls.WM_SYSCOMMAND, (IntPtr)WindowsNativeCalls.SW_SHOWNORMAL, IntPtr.Zero);
break;
}
}
}
}
}
}
}
public class WindowsNativeCalls
{
#region Fields
/// <summary>
/// Hide Window
/// </summary>
public const int SW_HIDE = 0;
/// <summary>
/// Show Normal
/// </summary>
public const int SW_SHOWNORMAL = 1;
/// <summary>
/// Minimize Window
/// </summary>
public const int SW_SHOWMINIMIZED = 2;
/// <summary>
/// Maxiize Window
/// </summary>
public const int SW_SHOWMAXIMIZED = 3;
/// <summary>
/// Activate Window
/// </summary>
public const int SW_SHOWNOACTIVATE = 4;
/// <summary>
/// Restore Window
/// </summary>
public const int SW_RESTORE = 9;
/// <summary>
/// SW_SHOW
/// </summary>
public const int SW_SHOW = 5;
/// <summary>
/// Default Window
/// </summary>
public const int SW_SHOWDEFAULT = 10;
/// <summary>
/// Close Windows message
/// </summary>
public const uint WM_CLOSE = 0x10;
/// <summary>
/// Screen Window Position to No Size Change
/// </summary>
public const UInt32 SWP_NOSIZE = 0x0001;
/// <summary>
/// Screen Window Position to No Move Change
/// </summary>
public const UInt32 SWP_NOMOVE = 0x0002;
/// <summary>
/// Screen Window Position to Show Window
/// </summary>
public const UInt32 SWP_SHOWWINDOW = 0x0040;
/// <summary>
/// Maximize Window in Graphics mode
/// </summary>
public const int SC_MAXIMIZE = 0xF030;
/// <summary>
/// Resize Windows message
/// </summary>
public const uint WM_SYSCOMMAND = 0x0112;
public struct POINTAPI
{
public int x;
public int y;
}
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
public struct WINDOWPLACEMENT
{
public int length;
public int flags;
public int showCmd;
public POINTAPI ptMinPosition;
public POINTAPI ptMaxPosition;
public RECT rcNormalPosition;
}
#endregion
#region DllImport Functions
/// <summary>
/// Sets the show state of a window created.
/// </summary>
/// <param name="hWnd">Window Handle</param>
/// <param name="nCmdShow">Show state</param>
/// <returns>Returns True if successful</returns>
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
/// <summary>
/// Sets window in Forefront and activates it
/// </summary>
/// <param name="hWnd">Window Handle</param>
/// <returns>Returns True if successfu</returns>
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
/// <summary>
/// Sets the show state of a window created.
/// </summary>
/// <param name="hWnd">Window Handle</param>
/// <param name="nCmdShow">Show state</param>
/// <returns>Returns True if successful</returns>
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern bool SetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder text, int count);
[DllImport("user32.dll")]
public static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumWindows(EnumedWindow lpEnumFunc, ArrayList lParam);
public delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumedWindow callback, ArrayList lParam);
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetActiveWindow(IntPtr hWnd);
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWINFO
{
public uint cbSize;
public RECT rcWindow;
public RECT rcClient;
public uint dwStyle;
public uint dwExStyle;
public uint dwWindowStatus;
public uint cxWindowBorders;
public uint cyWindowBorders;
public ushort atomWindowType;
public ushort wCreatorVersion;
public WINDOWINFO(Boolean? filler)
: this()
{
cbSize = (UInt32)(Marshal.SizeOf(typeof(WINDOWINFO)));
}
}
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetWindowInfo(IntPtr hwnd, ref WINDOWINFO pwi);
#endregion
}
If you want the window to use its previous position, then don't overwrite the window position information by calling SetWindowPos().
Instead, just minimize the window by calling ShowWindow() with the appropriate command (i.e. SW_MINIMIZE). Then when you restore it, its previous position information will still be there.
<aside>
By the way, any particular reason your code is checking both the window handle for null and the window title twice?
</aside>
switch (placement.showCmd)
{
case 2: // Activates and displays the window -- This case only when the window is minimized.
WindowsNativeCalls.ShowWindow(theprocess.MainWindowHandle, WindowsNativeCalls.SW_RESTORE);
break;
default: // Bring to Front - For all other cases
WindowsNativeCalls.SetWindowPos(theprocess.MainWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, WindowsNativeCalls.SWP_NOMOVE | WindowsNativeCalls.SWP_NOSIZE | WindowsNativeCalls.SWP_SHOWWINDOW);
break;
}
i am working on hide task bar of window this code working fine on visual studo 2010 but not working on IIS 7.5 and not give any error .Please tell me why this code not working on iis7.5 (window 7)
This is my code
public static class Taskbar
{
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool EnumThreadWindows(int threadId, EnumThreadProc pfnEnum, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern System.IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
[DllImport("user32.dll")]
private static extern IntPtr FindWindowEx(IntPtr parentHwnd, IntPtr childAfterHwnd, IntPtr className, string windowText);
[DllImport("user32.dll")]
private static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hwnd, out int lpdwProcessId);
private const int SW_HIDE = 0;
private const int SW_SHOW = 5;
private const string VistaStartMenuCaption = "Start";
private static IntPtr vistaStartMenuWnd = IntPtr.Zero;
private delegate bool EnumThreadProc(IntPtr hwnd, IntPtr lParam);
/// <summary>
/// Show the taskbar.
/// </summary>
public static void Show()
{
SetVisibility(true);
}
/// <summary>
/// Hide the taskbar.
/// </summary>
public static void Hide()
{
SetVisibility(false);
}
/// <summary>
/// Sets the visibility of the taskbar.
/// </summary>
public static bool Visible
{
set { SetVisibility(value); }
}
/// <summary>
/// Hide or show the Windows taskbar and startmenu.
/// </summary>
/// <param name="show">true to show, false to hide</param>
private static void SetVisibility(bool show)
{
// get taskbar window
IntPtr taskBarWnd = FindWindow("Shell_TrayWnd", null);
// try it the WinXP way first...
IntPtr startWnd = FindWindowEx(taskBarWnd, IntPtr.Zero, "Button", "Start");
if (startWnd == IntPtr.Zero)
{
// try an alternate way, as mentioned on CodeProject by Earl Waylon Flinn
startWnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, (IntPtr)0xC017, "Start");
}
if (startWnd == IntPtr.Zero)
{
// ok, let's try the Vista easy way...
startWnd = FindWindow("Button", null);
if (startWnd == IntPtr.Zero)
{
// no chance, we need to to it the hard way...
startWnd = GetVistaStartMenuWnd(taskBarWnd);
}
}
ShowWindow(taskBarWnd, show ? SW_SHOW : SW_HIDE);
ShowWindow(startWnd, show ? SW_SHOW : SW_HIDE);
}
/// <summary>
/// Returns the window handle of the Vista start menu orb.
/// </summary>
/// <param name="taskBarWnd">windo handle of taskbar</param>
/// <returns>window handle of start menu</returns>
private static IntPtr GetVistaStartMenuWnd(IntPtr taskBarWnd)
{
// get process that owns the taskbar window
int procId;
GetWindowThreadProcessId(taskBarWnd, out procId);
Process p = Process.GetProcessById(procId);
if (p != null)
{
// enumerate all threads of that process...
foreach (ProcessThread t in p.Threads)
{
EnumThreadWindows(t.Id, MyEnumThreadWindowsProc, IntPtr.Zero);
}
}
return vistaStartMenuWnd;
}
/// <summary>
/// Callback method that is called from 'EnumThreadWindows' in 'GetVistaStartMenuWnd'.
/// </summary>
/// <param name="hWnd">window handle</param>
/// <param name="lParam">parameter</param>
/// <returns>true to continue enumeration, false to stop it</returns>
private static bool MyEnumThreadWindowsProc(IntPtr hWnd, IntPtr lParam)
{
StringBuilder buffer = new StringBuilder(256);
if (GetWindowText(hWnd, buffer, buffer.Capacity) > 0)
{
Console.WriteLine(buffer);
if (buffer.ToString() == VistaStartMenuCaption)
{
vistaStartMenuWnd = hWnd;
return false;
}
}
return true;
}
}
It doesn't work because IIS runs as a Windows service and is therefore prohibited from interacting with the desktop.
Did you expect this to happen on the client? If your code did work, it would happen on the server, not the client. But it doesn't, so give up.
I want to get the name of the printer chosen in Acrobat PrintDialog using SendMessage Windows API.
This is sample code.
static string GetWindowText( hwnd_printDialog_in_Acrobat )
{
int comboBoxCount = 0;
int HWND_PRINTER_NAME = 1 ;
List<IntPtr> ChildPtrList = GetChildWindows(hwnd_printDialog_in_Acrobat);
for( i=0 ; i < ChildPtrList.Size(); i++) {
GetClassName( ChildPtrList[i], sClass, 100);
if (sClass.ToString() == "ComboBox") {
comboBoxCount++;
if (comboBoxCount == HWND_PRINTER_NAME ) {
hwnd = ChildPtrList[i];
break;
}
}
}
ChildPtrList.Clear();
int sSize ;
sSize = SendMessageW( hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero )+1;
StringBuilder sbTitle = new StringBuilder( sSize );
SendMessageW( hwn, WM_GETTEXT, (IntPtr)sSize, sbTitle);
return (sbTitle.ToString());
}
The return value of sSize is 4;
The value of sbTitle.ToString() is "?-" etc.
The expected resu
What is wrong?
Here are my current guesses to your problem:
HWND_PRINTER_NAME is not 1
The class name you are looking for is not "ComboBox"
Problem in the code not listed:
Grabbing the wrong parent window or treating the handle incorrectly
SendMessageW DllImport is wrong, or parameters are treated incorrectly
You do have some bugs in the code listed so, my code isn't exactly the same, but here is the code I used to try to figure your problem. I could not get it to behave the way you describe. My code simply never finds a Child Window with the Class name "ComboBox".
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace PrinterChoosenInAcrobat
{
class Program
{
public const uint WM_GETTEXTLENGTH = 0x000E;
private const uint WM_GETTEXT = 0x000D;
// External OS calls
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
public static extern bool SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam,
StringBuilder lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wparam,
IntPtr lparam);
[DllImport("User32.dll")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
[DllImport("user32.dll")]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName,
int nMaxCount);
static void Main(string[] args)
{
try
{
IntPtr windowHandle = (IntPtr)FindWindow("AcrobatSDIWindow", null);
string text = GetWindowText(windowHandle);
Console.WriteLine(text);
}
catch (Exception ex)
{
Console.Out.WriteLine(ex.Message);
}
// Don't close before I get to read the results
Console.WriteLine();
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
static string GetWindowText(IntPtr hwnd_printDialog_in_Acrobat)
{
int comboBoxCount = 0;
int HWND_PRINTER_NAME = 1;
List<IntPtr> ChildPtrList = GetChildWindows(hwnd_printDialog_in_Acrobat);
IntPtr hwnd = IntPtr.Zero;
for (int i = 0; i < ChildPtrList.Count; i++)
{
StringBuilder sClass = new StringBuilder();
GetClassName(ChildPtrList[i], sClass, 100);
if (sClass.ToString() == "ComboBox")
{
comboBoxCount++;
if (comboBoxCount == HWND_PRINTER_NAME)
{
hwnd = ChildPtrList[i];
break;
}
}
}
ChildPtrList.Clear();
int sSize;
sSize = (int)SendMessage(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero) + 1;
StringBuilder sbTitle = new StringBuilder(sSize);
SendMessage(hwnd, WM_GETTEXT, (IntPtr)sSize, sbTitle);
return (sbTitle.ToString());
}
#region Code from http://pinvoke.net/default.aspx/user32/EnumChildWindows.html
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
/// <summary>
/// Returns a list of child windows
/// </summary>
/// <param name="parent">Parent of the windows to return</param>
/// <returns>List of child windows</returns>
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
/// <summary>
/// Callback method to be used when enumerating windows.
/// </summary>
/// <param name="handle">Handle of the next window</param>
/// <param name="pointer">Pointer to a GCHandle that holds a reference to the list to fill</param>
/// <returns>True to continue the enumeration, false to bail</returns>
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
// You can modify this to check to see if you want to cancel the operation, then return a null here
return true;
}
/// <summary>
/// Delegate for the EnumChildWindows method
/// </summary>
/// <param name="hWnd">Window handle</param>
/// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
/// <returns>True to continue enumerating, false to bail.</returns>
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
#endregion
}
}
I'm using a global keyboard hook class. This class allows to check if keyboard key pressed anywhere. And after some time I'm having an error:
**CallbackOnCollectedDelegate was detected**
A callback was made on a garbage collected delegate of type 'Browser!Utilities.globalKeyboardHook+keyboardHookProc::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
Here is globalkeyboardHook class:
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;
public List<Keys> HookedKeys = new List<Keys>();
IntPtr hhook = IntPtr.Zero;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
public globalKeyboardHook()
{
hook();
}
~globalKeyboardHook()
{
unhook();
}
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}
public void unhook()
{
UnhookWindowsHookEx(hhook);
}
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);
}
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
#endregion
Any ideas how to fix it? The program works well, but after some time the program freezes ant I get this error.
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
There's your problem. You are relying on C# syntax sugar to have it automatically create a delegate object to hookProc. Actual code generation look like this:
keyboardHookProc $temp = new keyboardHookProc(hookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, $temp, hInstance, 0);
There's only one reference to the delegate object, $temp. But it is local variable and disappears as soon as your hook() method stops executing and returns. The garbage collector is otherwise powerless to see that Windows has a 'reference' to it as well, it cannot probe unmanaged code for references. So the next time the garbage collector runs, the delegate object gets destroyed. And that's a kaboom when Windows makes the hook callback. The built-in MDA detects the problem and generates the helpful diagnostic before the program crashes with an AccessViolation.
You will need to create an additional reference to the delegate object that survives long enough. You could use GCHandle for example. Or easier, just store a reference yourself so the garbage collector can always see the reference. Add a field to your class. Making it static is a sure-fire way to ensure the object can't be collected:
private static keyboardHookProc callbackDelegate;
public void hook()
{
if (callbackDelegate != null) throw new InvalidOperationException("Can't hook more than once");
IntPtr hInstance = LoadLibrary("User32");
callbackDelegate = new keyboardHookProc(hookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
if (hhook == IntPtr.Zero) throw new Win32Exception();
}
public void unhook()
{
if (callbackDelegate == null) return;
bool ok = UnhookWindowsHookEx(hhook);
if (!ok) throw new Win32Exception();
callbackDelegate = null;
}
No need to pinvoke FreeLibrary, user32.dll is always loaded until your program terminates.
It didn't take too long to get it done!
Here's an all good working implementation with latest fix (definitions & implementations) following Hans Passant's answer and a GitHub project.
//file Win32Api.cs
using System;
using System.Runtime.InteropServices;
using YourProjectNamespace.Hooks;
namespace YourProjectNamespace
{
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
/// <summary>
/// Pcursor info
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct PCURSORINFO
{
public Int32 Size;
public Int32 Flags;
public IntPtr Cursor;
public POINTAPI ScreenPos;
}
/// <summary>
/// Point
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct POINTAPI
{
public int x;
public int y;
}
/// <summary>
/// keyboard hook struct
/// </summary>
public struct keyboardHookStruct
{
public int dwExtraInfo;
public int flags;
public int scanCode;
public int time;
public int vkCode;
}
/// <summary>
/// Wrapper for windows 32 calls.
/// </summary>
public class Win32Api
{
public const Int32 CURSOR_SHOWING = 0x00000001;
public const int WH_KEYBOARD_LL = 13;
public const int WM_KEYDOWN = 0x100;
public const int WM_KEYUP = 0x101;
public const int WM_SYSKEYDOWN = 0x104;
public const int WM_SYSKEYUP = 0x105;
[DllImport("user32.dll")]
public static extern bool GetCursorInfo(out PCURSORINFO cinfo);
[DllImport("user32.dll")]
public static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon);
[DllImport("winmm.dll")]
private static extern int mciSendString(string MciComando, string MciRetorno, int MciRetornoLeng, int CallBack);
[DllImport("user32")]
private static extern int GetKeyboardState(byte[] pbKeyState);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern short GetKeyState(int vKey);
[DllImport("user32")]
public static extern int ToAscii(
int uVirtKey,
int uScanCode,
byte[] lpbKeyState,
byte[] lpwTransKey,
int fuState);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(
IntPtr idHook,
int nCode,
int wParam,
ref keyboardHookStruct lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern int UnhookWindowsHookEx(IntPtr idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(
int idHook,
keyboardHookProc lpfn,
IntPtr hMod,
int dwThreadId);
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
//Constants
} // class Win32
**File GlobalKeyboardHook.cs**
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using log4net;
namespace ScreenRecorder.Hooks
{
public class GlobalKeyboardHook
{
private static readonly ILog log = LogManager.GetLogger(typeof (GlobalKeyboardHook).Name);
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;
private static keyboardHookProc callbackDelegate;
public List<Keys> HookedKeys = new List<Keys>();
private IntPtr keyboardHook = IntPtr.Zero;
/// <summary>
public GlobalKeyboardHook()
{
Hook();
}
~GlobalKeyboardHook() {
Unhook();
}
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
public int HookProc(int nCode, int wParam, ref keyboardHookStruct lParam)
{
if (nCode >= 0)
{
var key = (Keys) lParam.vkCode;
if (HookedKeys.Contains(key))
{
var kArgs = new KeyEventArgs(key);
if ((wParam == Win32Api.WM_KEYDOWN || wParam == Win32Api.WM_SYSKEYDOWN) && (KeyDown != null))
{
KeyDown(this, kArgs);
}
else if ((wParam == Win32Api.WM_KEYUP || wParam == Win32Api.WM_SYSKEYUP) && (KeyUp != null))
{
KeyUp(this, kArgs);
}
if (kArgs.Handled)
return 1;
}
}
return Win32Api.CallNextHookEx(keyboardHook, nCode, wParam, ref lParam);
}
public void Hook()
{
// Create an instance of HookProc.
//if (callbackDelegate != null) throw new InvalidOperationException("Multiple hooks are not allowed!");
IntPtr hInstance = Win32Api.LoadLibrary("User32");
callbackDelegate = new keyboardHookProc(HookProc);
//install hook
keyboardHook = Win32Api.SetWindowsHookEx( Win32Api.WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
//If SetWindowsHookEx fails.
if (keyboardHook == IntPtr.Zero)
{
//Returns the error code returned by the last unmanaged function called using platform invoke that has the DllImportAttribute.SetLastError flag set.
var errorCode = Marshal.GetLastWin32Error();
log.Error("Unable to install keyboard hook.", new Win32Exception(errorCode));
}
}
/// <summary>
/// Unsubscribe for keyboard hook
/// </summary>
public void Unhook()
{
if (callbackDelegate == null) return;
if (keyboardHook != IntPtr.Zero)
{
//uninstall hook
var retKeyboard = Win32Api.UnhookWindowsHookEx(keyboardHook);
//reset invalid handle
keyboardHook = IntPtr.Zero;
//if failed and exception must be thrown
if (retKeyboard == 0)
{
//Returns the error code returned by the last unmanaged function called using platform invoke that has the DllImportAttribute.SetLastError flag set.
var errorCode = Marshal.GetLastWin32Error();
//Initializes and throws a new instance of the Win32Exception class with the specified error.
log.Error("Error while uninstalling keyboard hook", new Win32Exception(errorCode));
}
}
callbackDelegate = null;
}
}
}
I want to replace my current UI automation tool (QTP) with .Net framework.
I need to test VB6 (COM) application.
One of the fundamentals of framework is using the form name.
So far I failed to find a way to get this data using Win API.
There is only one constrain for the solution and its that the solution MUST rely on .Net code - meaning: no commercials tools allowed.
Does anyone is familiar with this subject?
These links are my major references:
http://msdn.microsoft.com/en-us/library/ms996405(d=printer).aspx
http://blogs.msdn.com/b/brianmcm/archive/2006/01/17/getting-the-winforms-id-of-a-control.aspx
http://blogs.msdn.com/b/brianmcm/archive/2006/01/23/516418.aspx
http://bytes.com/topic/c-sharp/answers/558930-really-need-help-sendmessage-wm_getcontrolname
All of them suggest to use SendMessage in order to retrieve the form's data, which I failed to do.
I will be happy to for any idea for this issue.
Thank you very much.
The C# code
public static class VbAdapter : IAdapter
{
/// <summary>
/// Gets form internal name (design-time name).
/// </summary>
/// <param name="hWnd">Form handle</param>
/// <returns>string. Form's internal name.</returns>
public static string GetFormInternalName(IntPtr hWnd)
{
int _ctrlNameMsg = 0;
//_ctrlNameMsg = NativeMethods.RegisterWindowMessage("WM_GETCONTROLNAME"); //For .Net forms
_ctrlNameMsg = NativeMethods.RegisterWindowMessage("Get_CONTROLNAME"); //for vb6 forms
return GetControlName(hWnd, _ctrlNameMsg);
}
/// <summary>
/// Get control internal name using its handle.
/// </summary>
/// <param name="hWnd">Control handle</param>
/// <param name="msg">Control Name Message</param>
/// <returns>string.</returns>
private static string GetControlName(IntPtr hWnd, int msg)
{
//vars
uint size = 65536; //size of memory to be allocated
byte[] byteArray = new byte[size]; //win form internal name buffer
IntPtr bufferMem = IntPtr.Zero; //pointer to memory buffer contain the internal name
IntPtr written = IntPtr.Zero; //number of bytes written so far
IntPtr retHandle = IntPtr.Zero; //returned handle
IntPtr hProcess = IntPtr.Zero; //Process handle
IntPtr fileHandle = IntPtr.Zero; //File handle
bool retVal = false;
//in case non Win32Nt OS version - throw exception
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
throw new Win32Exception("Oprating System is not supportted for this module.\nThis module is supportted on Win32Nt OS only.");
try
{
uint procId = GetProcessIdFromHWnd(hWnd);
//get process deatails
hProcess = NativeMethods.OpenProcess(
WindowsConsts.PROCESS_VM_OPERATION |
WindowsConsts.PROCESS_VM_READ |
WindowsConsts.PROCESS_VM_WRITE,
false,
procId);
//Todo: Export to OpenProcess Method in native class
if (hProcess.ToInt64() == 0) throw new Win32Exception();
bufferMem = NativeMethods.VirtualAllocEx(hProcess,
IntPtr.Zero,
new UIntPtr(size),
WindowsConsts.MEM_RESERVE | WindowsConsts.MEM_COMMIT,
NativeMethods.PageProtection.ReadWrite);
//Todo: Export to OpenProcess Method in native class
if (hProcess.ToInt64() == 0) throw new Win32Exception();
//Send message to the control requesting it's name
retHandle = NativeMethods.SendMessage(hWnd, msg, new IntPtr(size), bufferMem);
//Get TVITEM from shared memory
if (!NativeMethods.ReadProcessMemory(hProcess, bufferMem, byteArray, new UIntPtr(size), written))
throw new Win32Exception();
}
catch (Exception)
{
throw new Win32Exception();
}
return ByteArrayToString(byteArray);
}
/// <summary>
/// Converts byte array to string.
/// </summary>
/// <param name="byteArray">The byte array.</param>
/// <returns>string.</returns>
private static string ByteArrayToString(byte[] byteArray)
{
return Encoding.Unicode.GetString(byteArray).TrimEnd('\0');
}
/// <summary>
/// Get the process id using its handle.
/// </summary>
/// <param name="hWnd">The handle</param>
/// <returns>uint. The process Id.</returns>
private static uint GetProcessIdFromHWnd(IntPtr hWnd)
{
uint pId;
NativeMethods.GetWindowThreadProcessId(hWnd, out pId);
return pId;
}
}
internal class NativeMethods
{
[DllImport("kernel32.dll")]
internal static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle,
uint dwProcessId);
[DllImport("kernel32.dll")]
internal static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
UIntPtr dwSize, uint flAllocationType, PageProtection flProtect);
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("kernel32.dll")]
internal static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress,
UIntPtr dwSize, uint dwFreeType);
[DllImport("kernel32.dll")]
internal static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
internal static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint
dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow,
UIntPtr dwNumberOfBytesToMap);
[DllImport("kernel32.dll")]
internal static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr CreateFileMapping(IntPtr hFile,
IntPtr lpFileMappingAttributes, PageProtection flProtect, int dwMaximumSizeHigh,
int dwMaximumSizeLow, string lpName);
[DllImport("user32.dll")]
internal static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
internal static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
[Out] byte[] lpBuffer, UIntPtr nSize, IntPtr lpNumberOfBytesRead);
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
internal static extern void MoveMemoryFromByte(IntPtr dest, ref byte src, int size);
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
internal static extern void MoveMemoryToByte(ref byte dest, IntPtr src, int size);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern int RegisterWindowMessage(string lpString);
[Flags]
internal enum PageProtection : uint
{
NoAccess = 0x01,
Readonly = 0x02,
ReadWrite = 0x04,
WriteCopy = 0x08,
Execute = 0x10,
ExecuteRead = 0x20,
ExecuteReadWrite = 0x40,
ExecuteWriteCopy = 0x80,
Guard = 0x100,
NoCache = 0x200,
WriteCombine = 0x400,
}
}