I am working on a project in which I have to first split and then re size the the windows on desktop. I am able to split the desktop screen by using pinvoke service TileWindows. But i am stuck in the other part, to resize the windows on desktop. Because first I have to catch the event that some window is re sized on the desktop. To catch this event I have used this code answered in SO.
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventProc lpfnWinEventProc, int idProcess, int idThread, uint dwflags);
[DllImport("user32.dll")]
internal static extern int UnhookWinEvent(IntPtr hWinEventHook);
internal delegate void WinEventProc(IntPtr hWinEventHook, uint iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime);
const uint WINEVENT_OUTOFCONTEXT = 0;
const uint EVENT_SYSTEM_FOREGROUND = 3;
private IntPtr winHook;
private WinEventProc listener;
public void StartListeningForWindowChanges()
{
listener = new WinEventProc(EventCallback);
//setting the window hook
winHook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, listener, 0, 0, WINEVENT_OUTOFCONTEXT);
}
public void StopListeningForWindowChanges()
{
UnhookWinEvent(winHook);
}
private static void EventCallback(IntPtr hWinEventHook, uint iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime)
{
MessageBox.Show("I am here");
}
the problem I am getting is the event is fired even when i have not re sized any window on screen. And as you can see I have put a messageBox in the eventCallback function and it pops up but interestingly it shows nothing on its ok button.
As #HansPassant already said you should use the
EVENT_SYSTEM_MOVESIZEEND = 0x000B constant that indicates that: The movement or resizing of a window has finished.
Event Constants
you are also receiving events from ANY window. If you look at the signature of SetWinEventHook function:
HWINEVENTHOOK WINAPI SetWinEventHook(
_In_ UINT eventMin,
_In_ UINT eventMax,
_In_ HMODULE hmodWinEventProc,
_In_ WINEVENTPROC lpfnWinEventProc,
_In_ DWORD idProcess,
_In_ DWORD idThread,
_In_ UINT dwflags
);
you'll find that:
idProcess - Specifies the ID of the process from which the hook function receives events. Specify zero (0) to receive events from all processes on the current desktop.
however in your code you have spacified a 0 there:
winHook = SetWinEventHook(
EVENT_SYSTEM_FOREGROUND,
EVENT_SYSTEM_FOREGROUND,
IntPtr.Zero,
listener,
0, // <--- idProcess
0,
WINEVENT_OUTOFCONTEXT);
You need to get your process' ID to listen only for events about your window. For that you can use the Process.Id Property. I think this should work (it works for me):
const uint EVENT_SYSTEM_MOVESIZEEND = 0x000B;
Process currentProcess = Process.GetCurrentProcess();
winHook = SetWinEventHook(
EVENT_SYSTEM_MOVESIZEEND,
EVENT_SYSTEM_MOVESIZEEND,
IntPtr.Zero,
listener,
currentProcess.Id,
0,
WINEVENT_OUTOFCONTEXT);
Related
I am trying to make a program that writes the active window title, whenever it changes to the console window.
Here's my code, It does work in a winform application but not in a console application, I couldn't figure out what's wrong.
Any help would be appreciated.
class Program
{
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
private const uint WINEVENT_OUTOFCONTEXT = 0;
private const uint EVENT_SYSTEM_FOREGROUND = 3;
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private string GetActiveWindowTitle()
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Console.WriteLine(GetActiveWindowTitle() + "\r\n");
}
static void Main(string[] args)
{
WinEventDelegate dele = null;
Program a = new Program();
dele = new WinEventDelegate(a.WinEventProc);
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
Console.ReadKey();
}
}
"The client thread that calls SetWinEventHook must have a message loop in order to receive events." Threads in a console application don't have message loops. Unless you build one:
using System.ComponentModel;
using System.Windows.Forms;
...
[DllImport("user32.dll", SetLastError = true)]
static extern int GetMessage(out Message lpMsg, IntPtr hwnd, int wMsgFilterMin, int wMsgFilterMax);
[DllImport("user32.dll")]
static extern int TranslateMessage(Message lpMsg);
[DllImport("user32.dll")]
static extern int DispatchMessage(Message lpMsg);
Then replace Console.ReadKey() with
Message msg;
while (true) {
int result = GetMessage(out msg, IntPtr.Zero, 0, 0);
if (result == 0) break;
if (result == -1) throw new Win32Exception();
TranslateMessage(msg);
DispatchMessage(msg);
}
I'm being lazy and taking the Message structure from System.Windows.Forms. You could add the definition of that separately if you don't want to take the dependency.
Since this thread is now occupied with processing messages, you probably want to do this on a separate dedicated thread if you want to do other processing.
I am currently developing a program that will send a "key press" (the letter A or 0x41 in virtual key codes) to another program (notepad) every X seconds.
The problem is that for it to work I need the other program (notepad) to be in the FOREGROUND, example :
Process[] processes = Process.GetProcessesByName("notepad");
foreach (Process proc in processes) {
SetForegroundWindow(proc.MainWindowHandle);
// Do Whatever
}
Thread.Sleep(1000);
Is there a way to do that WITHOUT notepad having to be in the foreground ?
Like something that could run in the background ?
You could do it via winApi. Try to use SendMessage
According to this link you can do following:
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public static void sendKeystroke(ushort k)
{
const uint WM_KEYDOWN = 0x100;
const uint WM_SYSCOMMAND = 0x018;
const uint SC_CLOSE = 0x053;
IntPtr WindowToFind = FindWindow(null, "Untitled1 - Notepad++");
IntPtr result3 = SendMessage(WindowToFind, WM_KEYDOWN, ((IntPtr)k), (IntPtr)0);
//IntPtr result3 = SendMessage(WindowToFind, WM_KEYUP, ((IntPtr)c), (IntPtr)0);
}
I am trying to click on 'OK' button on a message box of C# windows form using winapi. Below is the code that I am working on.
private const int WM_CLOSE = 16;
private const int BN_CLICKED = 245;
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(int hWnd, int msg, int wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
//this works
hwnd = FindWindow(null, "Message");
if(hwnd!=0)
SendMessage(hwnd, WM_CLOSE, 0, IntPtr.Zero);
//this doesn't work.
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", "ok");
SendMessage((int)hwndChild, BN_CLICKED, 0, IntPtr.Zero);
Though i get a value in hwndChild, it is not recognising BN_CLICKED.
I am not sure what am I missing. any help?
I am trying to close the message box button of another application and this is what I am doing. But, I m still missing something.
IntPtr hwndChild = IntPtr.Zero;
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero,' '"Button", "OK");
SendMessage((int)hwndChild, WM_COMMAND, (BN_CLICKED '<<16) | IDOK, hwndChild);
BN_CLICKED is not a message. You need to send a WM_COMMAND message containing the BN_CLICKED notification and the button ID in the wParam and the button handle in lParam.
The parent window of the button receives this notification code
through the WM_COMMAND message.
private const uint WM_COMMAND = 0x0111;
private const int BN_CLICKED = 245;
private const int IDOK = 1;
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
SendMessage(hwndChild, WM_COMMAND, (BN_CLICKED << 16) | IDOK, hwndChild);
Finallu, this works for me.
First click probably activates the window and second click clicks the button.
SendMessage(btnHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(btnHandle, WM_LBUTTONUP, 0, 0);
SendMessage(btnHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(btnHandle, WM_LBUTTONUP, 0, 0);
I want to get all tray icons on windows7(64bit) with C#,but when i used the windows api "ReadProcessMemory" ,the tray button Text can't be recognied.
codes below
IntPtr pid = IntPtr.Zero;
IntPtr ipHandle = IntPtr.Zero;
IntPtr lTextAdr = IntPtr.Zero;
IntPtr ipTray = TrayToolbarWindow32();
WinApiHelper.GetWindowThreadProcessId(ipTray, ref pid);
if (pid.Equals(0))
return iconList;
IntPtr hProcess = WinApiHelper.OpenProcess(WinApiHelper.PROCESS_ALL_ACCESS | WinApiHelper.PROCESS_VM_OPERATION | WinApiHelper.PROCESS_VM_READ | WinApiHelper.PROCESS_VM_WRITE, IntPtr.Zero, pid);
IntPtr lAddress = WinApiHelper.VirtualAllocEx(hProcess, 0, 4096, WinApiHelper.MEM_COMMIT, WinApiHelper.PAGE_READWRITE);
int lButton = WinApiHelper.SendMessage(ipTray, WinApiHelper.TB_BUTTONCOUNT, 0, 0);
for (int i = 0; i < lButton; i++)
{
WinApiHelper.SendMessage(ipTray, WinApiHelper.TB_GETBUTTON, i, lAddress);
WinApiHelper.ReadProcessMemory(hProcess, (IntPtr)(lAddress.ToInt32() + 16), ref lTextAdr, 4, 0);
if (!lTextAdr.Equals(-1))
{
byte[] buff = new byte[ 1024 ];
WinApiHelper.ReadProcessMemory(hProcess, lTextAdr, buff, 1024, 0);
string title = System.Text.ASCIIEncoding.Unicode.GetString(buff);
and api declaration
[DllImport("kernel32", EntryPoint = "ReadProcessMemory")]
public static extern int ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref IntPtr lpBuffer, int nSize, int lpNumberOfBytesWritten);
[DllImport("kernel32.dll", EntryPoint = "ReadProcessMemory")]
public static extern bool ReadProcessMemoryEx(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, IntPtr size, out IntPtr lpNumberOfBytesRead);
[DllImport("kernel32", EntryPoint = "ReadProcessMemory")]
public static extern int ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] lpBuffer, int nSize, int lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, UIntPtr nSize, IntPtr lpNumberOfBytesRead);
the problem is here
string title = System.Text.ASCIIEncoding.Unicode.GetString(buff);
when converted , the string "title" can't be recognized,it maybe like
ǎ\0\0\0\0Д\0\0à\0\0ƿ\r\0\0\0\0\0\0\0\0\0\0D:\\Tools\\ESET Smart Security\\egui.exe\0\0\0\0\0\0\0\0\0\0\0\0\0\
i don't know why, help.
You may want to consider what you are doing. ReadProcessMemory is a debug function designed for debuggers which requires SeDebugPrivilege, so I hope you are writing a debugger. Ignoring the queasiness I get from considering use of these functions in a non-debug capacity, you are leaking the buffer you allocated and requiring that your application run as administrator.
If this application is only for your own purposes and for no-one else, then check out the related question Systray Access, as it seems that there is a separate TB_GETBUTTONTEXT message. I imagine that you are actually receiving the button data, rather than text in the copied memory, causing issues.
I am using CDB (Microsoft Console Debugger,) and WinDbg to try to force a break when heap corruption occurs by P/Invoke into ReadFile. I read many more bytes from a text file than what I've allocated to the chBuf array. The debugger does not see the access violation until after GC.Collect, which is too late for me. Prior to running my program, I run
gflags -p /enable testheap.exe /unaligned
The effect seems useless. I wrote this little test program to apply what I find to debugging a much larger commercial program that is having heap corruption issues.
I have also tried DebugDiag with Application Verifier and MDA callbackOnCollectedDelegate without success. Isn't my use of gflags supposed to detect heap corruption immediately after ReadFile?
The code:
namespace TestHeap
public partial class Form1 : Form
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadFile(SafeFileHandle hFile, [Out] byte[] lpBuffer,
uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
string fileName = "testHeap.txt";
const uint GENERIC_READ = 0x80000000;
const uint OPEN_EXISTING = 3;
SafeFileHandle sh;
byte[] chBuf = new byte[8];
public Form1()
{
InitializeComponent();
}
private void testBtn_Click(object sender, EventArgs e)
{
bool nStat;
uint bytesToRead = 1025;
uint bytesRead = 0;
if (!(nStat = ReadFile( sh, chBuf, bytesToRead, out bytesRead, IntPtr.Zero)))
Debug.Print("testBtn_Click error in ReadFile, nStat = {0}", nStat);
MessageBox.Show(string.Format("After ReadFile, bytesToRead = {0},\n bytes read = {1}", bytesToRead, bytesRead));
GC.Collect();
MessageBox.Show("testBtn_Click end, after GC.Collect");
}
private void Form1_Load(object sender, EventArgs e)
{
sh = CreateFile(fileName, GENERIC_READ, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
}
}
}
Just a guess but I believe the unexpected gflags behaviour is caused by this line:
byte[] chBuf = new byte[8];
Since chBuf is managed by the CLR, gflags is not able to put the fill pattern after it to detect buffer overruns. Try changing that to:
IntPtr chBuf = Marshal.AllocHGlobal(8);
So that you will be allocating in the unmanaged heap. Gflags should be able to work with that. Also, you may need to change the signature of ReadFile for that to work:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadFile(SafeFileHandle hFile, [Out] IntPtr lpBuffer,
uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);