WinApi: Get Control Name of COM Forms - c#

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,
}
}

Related

PInvoke ReadProcessMemory - Reading PEB with ReadProcessMemory: "Invalid handle"

I am trying to read the value of ImageBaseAddress in the PEB of a process I have created using Win32 API calls in C# with P/Invoke. However the call to ReadProcessMemory returns false, indicating it has failed. Inspecting with Visual Studio debugger I see the bytes array passed to the function is populated with zeroes. However a Win32Exception isn't raised, printing Win32Exception(Marshal.GetLastWin32Error()).Message gives The operation completed successfully.
Edit:
After addressing issues highlighted in the comments that I wasn't actually retrieving the latest Win32 error, I now see that the error I am getting is Invalid handle
Below is my code, and two screenshots to help illustrate my point.
using System.ComponentModel;
namespace ReadProcess
{
class Program
{
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
internal struct STARTUPINFO
{
uint cb;
IntPtr lpReserved;
IntPtr lpDesktop;
IntPtr lpTitle;
uint dwX;
uint dwY;
uint dwXSize;
uint dwYSize;
uint dwXCountChars;
uint dwYCountChars;
uint dwFillAttributes;
uint dwFlags;
ushort wShowWindow;
ushort cbReserved;
IntPtr lpReserved2;
IntPtr hStdInput;
IntPtr hStdOutput;
IntPtr hStdErr;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_BASIC_INFORMATION
{
public IntPtr ExitStatus;
public IntPtr PebAddress;
public IntPtr AffinityMask;
public IntPtr BasePriority;
public IntPtr UniquePID;
public IntPtr InheritedFromUniqueProcessId;
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
[DllImport("ntdll.dll", SetLastError = true)]
static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, IntPtr processInformation, uint processInformationLength, IntPtr returnLength);
[DllImport("kernel32.dll")]
static extern bool CloseHandle(IntPtr hProcess);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CreateProcess(IntPtr lpApplicationName, string lpCommandLine, IntPtr lpProcAttribs, IntPtr lpThreadAttribs, bool bInheritHandles, uint dwCreateFlags, IntPtr lpEnvironment, IntPtr lpCurrentDir, [In] ref STARTUPINFO lpStartinfo, out PROCESS_INFORMATION lpProcInformation);
public static IntPtr GetPEBAddress(IntPtr hProcess)
{
//Allocate memory for a new PROCESS_BASIC_INFORMATION structure
IntPtr pbi = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)));
//Allocate memory for a long
IntPtr outLong = Marshal.AllocHGlobal(sizeof(long));
IntPtr outPtr = IntPtr.Zero;
int queryStatus = 0;
//Store API call success in a boolean
queryStatus = NtQueryInformationProcess(hProcess, 0, pbi, (uint)Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)), outLong);
//Close handle and free allocated memory
CloseHandle(hProcess);
Marshal.FreeHGlobal(outLong);
//STATUS_SUCCESS = 0, so if API call was successful querySuccess should contain 0 ergo we reverse the check.
if (queryStatus == 0)
outPtr = Marshal.PtrToStructure<PROCESS_BASIC_INFORMATION>(pbi).PebAddress;
//Free allocated space
Marshal.FreeHGlobal(pbi);
//Return pointer to PEB base address
return outPtr;
}
static void Main(string[] args)
{
STARTUPINFO startInfo = new STARTUPINFO();
PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();
CreateProcess((IntPtr)0, "notepad", (IntPtr)0, (IntPtr)0, false, 0x00000004, (IntPtr)0, (IntPtr)0, ref startInfo, out procInfo);
byte[] ImageBaseAddress = new byte[IntPtr.Size];
IntPtr lpNumberOfBytesRead;
IntPtr pPEB = GetPEBAddress(procInfo.hProcess);
ReadProcessMemory(procInfo.hProcess, pPEB + 16, ImageBaseAddress, 8, out lpNumberOfBytesRead);
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
Console.WriteLine("File handle: 0x{0:X16}", procInfo.hProcess);
Console.WriteLine("PEB base address: 0x{0:X16}", pPEB);
Console.WriteLine("Last Win32 Error: {0}", errorMessage);
}
}
}
Here is the output of the program compared with the output of !peb in a WinDBG session that is attached to the notepad process.
Here is a screenshot of Visual Studio debugger, the breakpoint is on the Console.WriteLine call, after ReadProcessMemory has been called. (edit: Win32 error is now outdated but this screenshot still highlights that I am correctly identifying the PEB base address).
You have many glaring errors in your PInvoke code.
Your primary issue: You cannot see the error code, as you are not checking GetLastWin32Error immediately after the call. It cannot wait as it gets overwritten quickly by other calls.
Furthermore, NtQueryInformationProcess doesn't use SetLastError at all, it returns a NTSTATUS. You can find the full enum list here.
Ideally you should force Unicode using CreateProcessW and CharSet.Unicode. Also use SafeFileHandle for the process and thread handles, and use using rather than CloseHandle to dispose them.
But in this case I recommend you just create the process using Process.Create. Then you don't need to manage that handle, you just need to put the Process into a using.
The call to ReadProcessMemory should use IntPtr.Size to match the buffer array.
Do not attempt manual marshalling unless you know what you are doing. You have memory leaks all over, and none of it is necessary as you can just pass in string and ref parameters where necessary.
Some IntPtr and uint types are wrong also.
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_BASIC_INFORMATION
{
public int ExitStatus;
public IntPtr PebAddress;
public IntPtr AffinityMask;
public int BasePriority;
public IntPtr UniquePID;
public IntPtr InheritedFromUniqueProcessId;
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory (
IntPtr hProcess,
IntPtr lpBaseAddress,
[Out] byte[] lpBuffer,
int dwSize,
out IntPtr lpNumberOfBytesRead);
[DllImport("ntdll.dll", SetLastError = false)]
static extern NtStatus NtQueryInformationProcess(
IntPtr processHandle,
int processInformationClass,
out PROCESS_BASIC_INFORMATION processInformation,
int processInformationLength,
out int returnLength);
public static IntPtr GetPEBAddress(SafeFileHandle hProcess)
{
var result = NtQueryInformationProcess(hProcess, 0, out var pbi, Marshal.SizeOf<PROCESS_BASIC_INFORMATION>(), out var outLong);
if(result != NtStatus.Success)
throw new Exception(result.ToString());
return pbi.PebAddress;
}
static void Main(string[] args)
{
try
{
using (var proc = Process.Start("notepad.exe"))
{
byte[] ImageBaseAddress = new byte[IntPtr.Size];
IntPtr pPEB = GetPEBAddress(proc.Handle);
if(!ReadProcessMemory(proc.Handle, pPEB + 16, ImageBaseAddress, IntPtr.Size, out var lpNumberOfBytesRead))
throw new Win32Exception(Marshal.GetLastWin32Error());
Console.WriteLine("PEB base address: 0x{0:X16}", pPEB);
// use ImageBaseAddress here ??
}
}
catch(Exception ex)
{
Console.WriteLine("Last Win32 Error: {0}", ex.Message);
}
}
I must say, this whole thing seems rather suspect, as you appear to be trying to read the PEB block. But the PEB block can and does change between different versions of Windows. In particular, offset 16 does not appear to line up with anything either in x64 or x86.

Getting all processes in desktop

I have created a new desktop using CreateDesktop and would like to be able to enumerate all processes within that desktop.
I have tried this:
foreach (Process process in Process.GetProcesses())
{
foreach (ProcessThread processThread in process.Threads)
{
IntPtr hDesk = GetThreadDesktop((uint)processThread.Id);
if (hDesk == desk)
{
// Do something
}
}
}
However, I get Access is denied exception. I can also use
EnumDesktopWindows
However, this doesn't work for command line applications. (I am trying to prevent keyloggers)
Is there any way to get all processes within a desktop?
Thanks
I spent the last hour playing with this and I have it working fine from a console window. The code is messy, but you should be able to make your way through it:
class Program
{
private static class Win32Native
{
[Flags]
public enum CreateDesktopFlags : uint
{
DF_NONE = 0,
DF_ALLOWOTHERACCOUNTHOOK = 1
}
[Flags]
public enum CreateWindowAccessMask : uint
{
DESKTOP_READOBJECTS = 0x0001,
DESKTOP_CREATEWINDOW = 0x0002,
DESKTOP_CREATEMENU = 0x0004,
DESKTOP_HOOKCONTROL = 0x0008,
DESKTOP_JOURNALRECORD = 0x0010,
DESKTOP_JOURNALPLAYBACK = 0x0020,
DESKTOP_ENUMERATE = 0x0040,
DESKTOP_WRITEOBJECTS = 0x0080,
DESKTOP_SWITCHDESKTOP = 0x0100,
DESKTOP_ALL_ACCESS = 0x01FF
}
[Flags]
public enum CreateProcessFlags : uint
{
CREATE_NEW_CONSOLE = 0x00000010,
CREATE_NEW_PROCESS_GROUP = 0x00000200
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[DllImport("user32.dll")]
public static extern IntPtr GetProcessWindowStation();
[return: MarshalAs(UnmanagedType.Bool)]
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[return: MarshalAs(UnmanagedType.Bool)]
public delegate bool EnumDesktopProc([MarshalAs(UnmanagedType.LPWStr)] string lpszDesktop, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumWindowsProc lpfn, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "GetWindowTextW", CharSet = CharSet.Unicode)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", EntryPoint = "GetClassNameW", CharSet = CharSet.Unicode)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, EntryPoint = "CreateDesktopW", CharSet = CharSet.Unicode)]
public static extern IntPtr CreateDesktop(
string lpszDesktop, IntPtr lpszDevice,
IntPtr pDevMode, CreateDesktopFlags dwFlags,
CreateWindowAccessMask dwDesiredAccess,
IntPtr lpsa);
[DllImport("user32.dll", SetLastError = true, EntryPoint = "EnumDesktopsW", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumDesktops(IntPtr hwinsta, EnumDesktopProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseDesktop(IntPtr hDesktop);
[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "CreateProcessW", CharSet = CharSet.Unicode)]
[return:MarshalAs(UnmanagedType.Bool)]
public static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
CreateProcessFlags dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
[return:MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
[return:MarshalAs(UnmanagedType.Bool)]
public static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
}
static int Main(string[] args)
{
StringBuilder sbWndText = new StringBuilder(512),
sbWndClass = new StringBuilder(512);
Console.WriteLine("Trying current desktop:");
if(!Win32Native.EnumDesktopWindows(IntPtr.Zero, (hWnd, lParam) =>
{
Win32Native.GetWindowText(hWnd, sbWndText, sbWndText.Capacity);
Win32Native.GetClassName(hWnd, sbWndClass, sbWndClass.Capacity);
Console.WriteLine($"Found Window: {hWnd} with title \"{sbWndText}\" and class name \"{sbWndClass}\"");
return true;
}, IntPtr.Zero))
{
var error = Marshal.GetLastWin32Error();
Console.WriteLine($"EnumDesktopWindows for current desktop failed with error {error}");
}
Console.WriteLine("Current desktops: ");
Win32Native.EnumDesktops(Win32Native.GetProcessWindowStation(), (desktopName, lParam) =>
{
Console.WriteLine($"Found desktop: {desktopName}");
return true;
}, IntPtr.Zero);
Console.WriteLine("Trying new desktop:");
const string DesktopName = "ANDY DESKTOP NEATO 2";
var hDesktop = Win32Native.CreateDesktop(
DesktopName, IntPtr.Zero, IntPtr.Zero,
Win32Native.CreateDesktopFlags.DF_ALLOWOTHERACCOUNTHOOK,
Win32Native.CreateWindowAccessMask.DESKTOP_ALL_ACCESS,
IntPtr.Zero);
if(hDesktop != IntPtr.Zero)
{
Win32Native.EnumDesktops(Win32Native.GetProcessWindowStation(), (desktopName, lParam) =>
{
Console.WriteLine($"Found desktop: {desktopName}");
return true;
}, IntPtr.Zero);
var si = new Win32Native.STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = DesktopName;
var pi = new Win32Native.PROCESS_INFORMATION();
if(!Win32Native.CreateProcess(
null, "cmd.exe", IntPtr.Zero, IntPtr.Zero, false,
Win32Native.CreateProcessFlags.CREATE_NEW_CONSOLE |
Win32Native.CreateProcessFlags.CREATE_NEW_PROCESS_GROUP,
IntPtr.Zero, null, ref si, out pi))
{
var error = Marshal.GetLastWin32Error();
Console.WriteLine($"Unable to create process on new desktop: {error}");
}
Console.WriteLine("WAITING 2 SECONDS FOR PROCESS TO START...");
Thread.Sleep(2000); // breath so the process starts
if (!Win32Native.EnumDesktopWindows(hDesktop, (hWnd, lParam) =>
{
Win32Native.GetWindowText(hWnd, sbWndText, sbWndText.Capacity);
Win32Native.GetClassName(hWnd, sbWndClass, sbWndClass.Capacity);
Console.WriteLine($"Found Window: {hWnd} with title \"{sbWndText}\" and class name \"{sbWndClass}\"");
return true;
}, IntPtr.Zero))
{
var error = Marshal.GetLastWin32Error();
Console.WriteLine($"EnumDesktopWindows for new desktop failed with error {error}");
}
// IMPORTANT: close the processes you start, otherwise you desktop won't self-destruct.
Win32Native.TerminateProcess(pi.hProcess, 42);
Win32Native.CloseHandle(pi.hProcess);
Win32Native.CloseHandle(pi.hThread);
Win32Native.CloseDesktop(hDesktop);
}
else
{
Console.WriteLine($"Unable to create desktop: {Marshal.GetLastWin32Error()}");
}
return 0;
}
}
So the issue I think you were having with EnumDesktopWindows is that when you called CreateDesktop, you didn't ask for sufficient privileges. I set all privileges on (value of 0x1FF) and then EnumDesktopWindows worked for me.
Keep in mind if you call EnumDesktopWindows and there are no windows to enumerate, it will return false with a value of 0 when you call GetLastError.
So what I did to prove that it actually is working is I created a process (cmd.exe) in the new desktop, then called EnumDesktopWindows.
Also keep in mind if you don't destroy all the processes in your new desktop, the desktop will not "self-destruct" and it will be alive until all the processes are destroyed or you logoff/reboot.
I also ran this as a normal user. I didn't need to elevate to administrator to make this work.
However, this doesn't work for command line applications. (I am trying
to prevent keyloggers)
Assume that you want to prevent from hooking keyboard input of the desktop.
Since hook need a message loop to receive keyboard messages, which requires to create a window. So restrict other users to create window application associate to your desktop can prevent them from logging keyboard inputs.
If you want to check the desktop name of a given process to see if it is same with your desktop's name (applied for window application) you can follow these steps:
Call OpenProcess() to get a HANDLE from the target process ID.
Call NtQueryInformationProcess() to retrieve the address of the process's PEB structure.
Call ReadProcessMemory() to read the PEB. It's ProcessParams.DesktopName field contains the name of the workstation/desktop currently associated with the process (there are many more fields available in the PEB.ProcessParams then what MSDN shows).
Refer to "How to get window station for a given process?"

getting listview item from other windows application always return empty string

I'm making winform application which needs to get content of a listview from other windows application which has several tabs, each tab contains a listview, like Windows Task Manager.
I was following this Q&A Get ListView items from other windows and this guide Crossing the process boundry with .NET.
Unluckily, I'm really a newbie in win32api. I always get empty string returned from local buffer.
Below is my code so far.
public partial class Form1 : Form
{
private const int LVM_FIRST = 0x1000;
private const int LVM_GETITEMCOUNT = LVM_FIRST + 4;
private const int LVM_GETITEM = LVM_FIRST + 75;
private const int LVIF_TEXT = 0x0001;
private const int LVM_SETITEM = 0x1006;
private const uint PROCESS_ALL_ACCESS = (uint)(0x000F0000L | 0x00100000L | 0xFFF);
private const uint MEM_COMMIT = 0x1000;
private const uint MEM_RELEASE = 0x8000;
private const uint PAGE_READWRITE = 0x04;
public Form1()
{
InitializeComponent();
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
[DllImport("user32.dll")]
static extern bool SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32")]
static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out int lpwdProcessID);
[DllImport("kernel32")]
static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32")]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32")]
static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint dwFreeType);
[DllImport("kernel32")]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref LV_ITEM buffer, int dwSize, IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32")]
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("kernel32")]
static extern bool CloseHandle(IntPtr hObject);
[StructLayout(LayoutKind.Sequential)]
public struct LV_ITEM
{
public uint mask;
public int iItem;
public int iSubItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
}
public static string ReadListViewItem(IntPtr hWnd, int item)
{
const int dwBufferSize = 1024;
int dwProcessID;
LV_ITEM lvItem;
string retval;
bool bSuccess;
IntPtr hProcess = IntPtr.Zero;
IntPtr lpRemoteBuffer = IntPtr.Zero;
IntPtr lpLocalBuffer = IntPtr.Zero;
IntPtr threadId = IntPtr.Zero;
try
{
lvItem = new LV_ITEM();
lpLocalBuffer = Marshal.AllocHGlobal(dwBufferSize);
// Get the process id owning the window
threadId = GetWindowThreadProcessId(hWnd, out dwProcessID);
if ((threadId == IntPtr.Zero) || (dwProcessID == 0))
throw new ArgumentException("Invalid hWnd");
// Open the process with all access
hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
if (hProcess == IntPtr.Zero)
throw new ApplicationException("Failed to access process");
// Allocate a buffer in the remote process
lpRemoteBuffer = VirtualAllocEx(hProcess, IntPtr.Zero, dwBufferSize, MEM_COMMIT, PAGE_READWRITE);
if (lpRemoteBuffer == IntPtr.Zero)
throw new SystemException("Failed to allocate memory in remote process");
// Fill in the LVITEM struct, this is in your own process
// Set the pszText member to somewhere in the remote buffer,
// For the example I used the address imediately following the LVITEM stuct
lvItem.mask = LVIF_TEXT;
lvItem.iItem = item;
lvItem.pszText = (IntPtr)(lpRemoteBuffer.ToInt32() + Marshal.SizeOf(typeof(LV_ITEM)));
lvItem.cchTextMax = 50;
// Copy the local LVITEM to the remote buffer
bSuccess = WriteProcessMemory(hProcess, lpRemoteBuffer, ref lvItem, Marshal.SizeOf(typeof(LV_ITEM)), IntPtr.Zero);
if (!bSuccess)
throw new SystemException("Failed to write to process memory");
// Send the message to the remote window with the address of the remote buffer
SendMessage(hWnd, LVM_GETITEM, 0, lpRemoteBuffer);
// Read the struct back from the remote process into local buffer
bSuccess = ReadProcessMemory(hProcess, lpRemoteBuffer, lpLocalBuffer, dwBufferSize, IntPtr.Zero);
if (!bSuccess)
throw new SystemException("Failed to read from process memory");
// At this point the lpLocalBuffer contains the returned LV_ITEM structure
// the next line extracts the text from the buffer into a managed string
retval = Marshal.PtrToStringAuto((IntPtr (lpLocalBuffer.ToInt32() + Marshal.SizeOf(typeof(LV_ITEM))));
}
finally
{
if (lpLocalBuffer != IntPtr.Zero)
Marshal.FreeHGlobal(lpLocalBuffer);
if (lpRemoteBuffer != IntPtr.Zero)
VirtualFreeEx(hProcess, lpRemoteBuffer, 0, MEM_RELEASE);
if (hProcess != IntPtr.Zero)
CloseHandle(hProcess);
}
return retval;
}
private void button1_Click(object sender, EventArgs e)
{
var mainWnd = FindWindow(null, "Windows Task Manager");
var childWnd = FindWindowEx(mainWnd, IntPtr.Zero, null, "Processes");
IntPtr count = SendMessage(mainWnd, LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero);
Console.WriteLine(count);
Console.WriteLine(ReadListViewItem(mainWnd, 1));
}
}
Please, somebody gives me suggestion!!
Thanks a lot!

Detect Key Sequence C# WPF

I was develop a application en C# WPF. In a new requirement, i need capture a Key combination, of letters and numbers only. For Example, AC, T2 etc...
I have implemented a lower level listener hook, but i don't have a idea for implement the logger for the combination.
Any ideas?
This is the hook:
internal class InterceptKeys
{
#region Delegates
/// <summary>
/// Delegate from keypress
/// </summary>
/// <param name="nCode">Code of Key</param>
/// <param name="wParam">wParam</param>
/// <param name="lParam">lParam</param>
/// <returns>return next Call</returns>
public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
#endregion
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
public static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}

Change defaut text on a save button

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

Categories