Is it possible to set the height of a window using the window handle or process handle?
I have the following so far, assume the application in question is notepad.
Process[] processes = Process.GetProcessesByName("notepad");
foreach (Process p in processes)
{
if (p.MainWindowTitle == title)
{
handle = p.MainWindowHandle;
while ((handle = p.MainWindowHandle) == IntPtr.Zero)
{
Thread.Sleep(1000);
p.Refresh();
}
break;
}
}
Can I make use of handle or p to set the height of the window?
This is how I would do it:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowRect(IntPtr hWnd, ref RECT Rect);
[DllImport("user32.dll", SetLastError = true)]
static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int Width, int Height, bool Repaint);
static void Main(string[] args)
{
Process[] processes = Process.GetProcessesByName("notepad");
foreach (Process p in processes)
{
IntPtr handle = p.MainWindowHandle;
RECT Rect = new RECT();
if (GetWindowRect(handle, ref Rect))
MoveWindow(handle, Rect.left, Rect.right, Rect.right-Rect.left, Rect.bottom-Rect.top + 50, true);
}
}
}
}
While you can do it with SetWindowPos, and SetWindowPos is the newer and more capable API, MoveWindow is just easier to call.
You should be able to use the Win32 SetWindowPos function (use for both position and size). Here's a link for how to do it in C#.
Here's a quick sample. This will move notepad to (10,10) on the screen, and resize it to (450,450):
class Program
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
static void Main(string[] args)
{
Console.WriteLine("Start notepad and hit any key...");
Console.ReadKey(true);
Process[] processes = Process.GetProcessesByName("notepad");
foreach (Process p in processes)
{
var handle = p.MainWindowHandle;
SetWindowPos(handle, new IntPtr(SpecialWindowHandles.HWND_TOP), 10,10,450,450,SetWindowPosFlags.SWP_SHOWWINDOW);
break;
}
}
}
public enum SpecialWindowHandles
{
HWND_TOP = 0,
HWND_BOTTOM = 1,
HWND_TOPMOST = -1,
HWND_NOTOPMOST = -2
}
[Flags]
public enum SetWindowPosFlags : uint
{
SWP_ASYNCWINDOWPOS = 0x4000,
SWP_DEFERERASE = 0x2000,
SWP_DRAWFRAME = 0x0020,
SWP_FRAMECHANGED = 0x0020,
SWP_HIDEWINDOW = 0x0080,
SWP_NOACTIVATE = 0x0010,
SWP_NOCOPYBITS = 0x0100,
SWP_NOMOVE = 0x0002,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOREDRAW = 0x0008,
SWP_NOREPOSITION = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_NOSIZE = 0x0001,
SWP_NOZORDER = 0x0004,
SWP_SHOWWINDOW = 0x0040,
}
Related
I have this code that works perfectly in cmd and PowerShell, but does nothing in Windows Terminal.
internal class TaskbarProgress : IDisposable
{
private IntPtr consoleWindowHandle = IntPtr.Zero;
[DllImport("kernel32.dll")]
private static extern IntPtr GetConsoleWindow();
internal TaskbarProgress()
{
consoleWindowHandle = GetConsoleWindow();
if (consoleWindowHandle != IntPtr.Zero)
{
TaskbarProgressCom.SetState(consoleWindowHandle, TaskbarProgressState.Normal);
}
}
internal void SetProgress(ulong currentValue, ulong maximumValue)
{
if (consoleWindowHandle != IntPtr.Zero)
{
TaskbarProgressCom.SetValue(consoleWindowHandle, currentValue, maximumValue);
}
}
public void Dispose()
{
if (consoleWindowHandle != IntPtr.Zero)
{
TaskbarProgressCom.SetState(consoleWindowHandle, TaskbarProgressState.NoProgress);
consoleWindowHandle = IntPtr.Zero;
}
}
}
internal enum TaskbarProgressState
{
NoProgress = 0,
Indeterminate = 0x1,
Normal = 0x2,
Error = 0x4,
Paused = 0x8
}
internal static class TaskbarProgressCom
{
... // Removed for StackOverflow complaint of too much code, but basically the same as https://www.nuget.org/packages/Microsoft-WindowsAPICodePack-Shell
}
I thought maybe the console window is childed, so grab the root window:
[DllImport("user32.dll", ExactSpelling = true)]
private static extern IntPtr GetAncestor(IntPtr hwnd, GetAncestorFlags flags);
// ...
IntPtr rootOwnerHandle = GetAncestor(consoleWindowHandle, GetAncestorFlags.RootOwner);
if (rootOwnerHandle != IntPtr.Zero)
{
consoleWindowHandle = rootOwnerHandle;
}
But that didn't change anything. What am I missing?
Extra context: https://github.com/dotnet/BenchmarkDotNet/pull/2158
Thanks to folks on the Windows Terminal repo, I got the answer:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModes lpMode);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, ConsoleModes dwMode);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
const int STD_OUTPUT_HANDLE = -11;
[Flags]
private enum ConsoleModes : uint
{
ENABLE_PROCESSED_INPUT = 0x0001,
ENABLE_LINE_INPUT = 0x0002,
ENABLE_ECHO_INPUT = 0x0004,
ENABLE_WINDOW_INPUT = 0x0008,
ENABLE_MOUSE_INPUT = 0x0010,
ENABLE_INSERT_MODE = 0x0020,
ENABLE_QUICK_EDIT_MODE = 0x0040,
ENABLE_EXTENDED_FLAGS = 0x0080,
ENABLE_AUTO_POSITION = 0x0100,
ENABLE_PROCESSED_OUTPUT = 0x0001,
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002,
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004,
DISABLE_NEWLINE_AUTO_RETURN = 0x0008,
ENABLE_LVB_GRID_WORLDWIDE = 0x0010
}
static void Main(string[] args)
{
IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE);
ConsoleModes previousConsoleMode;
GetConsoleMode(handle, out previousConsoleMode);
SetConsoleMode(handle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT);
for (uint i = 0; i < 100; ++i)
{
// Set progress value (0-100).
Console.Write($"\x1b]9;4;1;{i}\x1b\\");
Thread.Sleep(100);
}
// Set state to no progress.
Console.Write($"\x1b]9;4;0;0\x1b\\");
SetConsoleMode(handle, previousConsoleMode);
}
The supported sequences are:
ESC ] 9 ; 4 ; st ; pr ST
Set progress state on Windows taskbar and tab. When `st` is:
0: remove progress.
1: set progress value to pr (number, 0-100).
2: set the taskbar to the "Error" state
3: set the taskbar to the "Indeterminate" state
4: set the taskbar to the "Warning" state
https://github.com/microsoft/terminal/issues/6700
private async void WrongPassword_Load(object sender, EventArgs e)
{
IntPtr NEWDESKTOP = CreateDesktop("dew", IntPtr.Zero, IntPtr.Zero, 0, (uint) DESKTOP_ACCESS.GENERIC_ALL,IntPtr.Zero);
IntPtr OLDDESKTOP = GetThreadDesktop(GetCurrentThreadId());
SwitchDesktop(NEWDESKTOP);
SetThreadDesktop(NEWDESKTOP);
Thread.CurrentThread.TrySetApartmentState(ApartmentState.STA);
ShowIcon = false;
Text = "";
TopMost = true;
ControlBox = false;
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
button1.Enabled = false;
MessageCycle();
var i = 60;
Task.Factory.StartNew(() =>
{
SoundPlayer dewd = new SoundPlayer(Resources.WrongPassword);
dewd.PlaySync();
});
PlayMusic();
while (i > 0)
{
CountDownLabel.Text = "Please wait " + i +
" seconds before you can dismiss to prevent \n DDOS, BRUTE FORCE ATTACKS";
i = i - 1;
Task.Delay(1000);
}
SetThreadDesktop(OLDDESKTOP);
CloseDesktop(NEWDESKTOP);
SwitchDesktop(OLDDESKTOP);
CountDownLabel.Text = "We are not responsible if you lose your password and files";
button1.Enabled = true;
}
When I put SetThreadDesktop(NEWDESKTOP), my form does not transfer from my current desktop to NEWDESKTOP, I have also put TrySetApartmentState(ApartmentState.STA) and it still does not work, can someone give me a solution to how to transfer a form from your current desktop to NEWDESKTOP?
Comments the [STAThread] and try to activate STA manually:
Also make sure that Don't use anything which will prevent you switching desktop before calling SetThreadDesktop
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
namespace WindowsFormsApp1
{
static class Program
{
[Flags]
internal enum ACCESS_MASK : uint
{
DESKTOP_NONE = 0,
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,
GENERIC_ALL = 0x10000000,
}
[DllImport("user32.dll", EntryPoint = "CreateDesktop", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateDesktop(
[MarshalAs(UnmanagedType.LPWStr)] string desktopName,
[MarshalAs(UnmanagedType.LPWStr)] string device, // must be null.
[MarshalAs(UnmanagedType.LPWStr)] string deviceMode, // must be null,
[MarshalAs(UnmanagedType.U4)] int flags, // use 0
[MarshalAs(UnmanagedType.U4)] ACCESS_MASK accessMask,
IntPtr attributes);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern uint GetCurrentThreadId();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetThreadDesktop(uint dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
public static extern int SetThreadDesktop(IntPtr hDesktop);
[DllImport("user32.dll", SetLastError = true)]
public static extern int SwitchDesktop(IntPtr hDesktop);
[DllImport("user32.dll", SetLastError = true)]
public static extern int CloseDesktop(IntPtr hDesktop);
/// <summary>
/// The main entry point for the application.
/// </summary>
//[STAThread]
static void Main()
{
int ret = 0;
IntPtr NEWDESKTOP = CreateDesktop("dew", null, null, 0, ACCESS_MASK.GENERIC_ALL, IntPtr.Zero);
IntPtr OLDDESKTOP = GetThreadDesktop(GetCurrentThreadId());
ret = SetThreadDesktop(NEWDESKTOP);
ret = SwitchDesktop(NEWDESKTOP);
Thread.CurrentThread.TrySetApartmentState(ApartmentState.STA);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
ret = SwitchDesktop(OLDDESKTOP);
CloseDesktop(NEWDESKTOP);
}
}
}
I have a MS Word Application Add-in written with VSTO. It contains a button used to create new Letter documents. When pressed a document is instantiated, a WPF dialog is displayed to capture information and then the information is inserted into the document.
On one of my test machines I get the following exception when approximately 40 letters are created in a single Word session:
The disk is full. Free some space on this drive, or save the document
on another disk.
Try one or more of the following:
Close any unneeded documents, programs or windows.
Save the document to another disk.
So I monitored the Winword.exe process using Task Manager:
Memory starts at 97,000k
Memory steadily increases with each letter document until the error is seen at approximately 1,000,000k
If I then close all the documents the memory only drops down to 500,000k
Any tips on how I can troubleshoot the memory leak?
I've gone through my code and ensured that event handlers are unregistered and that i'm disposing objects that need disposing.
Any reference articles that I should be reading?
-- Edit --
Malick, I use unmanaged code to make the WPF window look like an Office dialog. Is there a better way of doing this? I'll try removing it. (edit, there wasn't a change. I'll try the memory monitoring tools)
public class OfficeDialog : Window
{
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
const int GWL_EXSTYLE = -20;
const int WS_EX_DLGMODALFRAME = 0x0001;
const int SWP_NOSIZE = 0x0001;
const int SWP_NOMOVE = 0x0002;
const int SWP_NOZORDER = 0x0004;
const int SWP_FRAMECHANGED = 0x0020;
const uint WM_SETICON = 0x0080;
const int ICON_SMALL = 0;
const int ICON_BIG = 1;
public OfficeDialog()
{
this.ShowInTaskbar = false;
//this.Topmost = true;
}
public new void ShowDialog()
{
try
{
var helper = new WindowInteropHelper(this);
using (Process currentProcess = Process.GetCurrentProcess())
helper.Owner = currentProcess.MainWindowHandle;
base.ShowDialog();
}
catch (System.ComponentModel.Win32Exception ex)
{
Message.LogWarning(ex);
//this.Topmost = true;
var helper = new WindowInteropHelper(this);
using (Process currentProcess = Process.GetCurrentProcess())
helper.Owner = currentProcess.MainWindowHandle;
base.ShowDialog();
}
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
RemoveIcon(this);
HideMinimizeAndMaximizeButtons(this);
//using (Process currentProcess = Process.GetCurrentProcess())
// SetCentering(this, currentProcess.MainWindowHandle);
}
public static void HideMinimizeAndMaximizeButtons(Window window)
{
const int GWL_STYLE = -16;
IntPtr hwnd = new WindowInteropHelper(window).Handle;
long value = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, (int)(value & -131073 & -65537));
}
public static void RemoveIcon(Window w)
{
// Get this window's handle
IntPtr hwnd = new WindowInteropHelper(w).Handle;
// Change the extended window style to not show a window icon
int extendedStyle = OfficeDialog.GetWindowLong(hwnd, GWL_EXSTYLE);
OfficeDialog.SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
// reset the icon, both calls important
OfficeDialog.SendMessage(hwnd, WM_SETICON, (IntPtr)ICON_SMALL, IntPtr.Zero);
OfficeDialog.SendMessage(hwnd, WM_SETICON, (IntPtr)ICON_BIG, IntPtr.Zero);
// Update the window's non-client area to reflect the changes
OfficeDialog.SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
static void SetCentering(Window win, IntPtr ownerHandle)
{
bool isWindow = IsWindow(ownerHandle);
if (!isWindow) //Don't try and centre the window if the ownerHandle is invalid. To resolve issue with invalid window handle error
{
//Message.LogInfo(string.Format("ownerHandle IsWindow: {0}", isWindow));
return;
}
//Show in center of owner if win form.
if (ownerHandle.ToInt32() != 0)
{
var helper = new WindowInteropHelper(win);
helper.Owner = ownerHandle;
win.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
else
win.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindow(IntPtr hWnd);
}
I am trying to access a TreeView running in another process. Many of the tree view messages work correctly. However, trying to use the TVM_GETITEM causes that other process to crash.
The code below is a simple program that illustrates the crash. To get it to run, you will need some CHM help file. I'm using a CHM help file because hh.exe uses a TreeView control for the table of contents.
The goal is to be able to get the a tree node's text.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace TreeViewTest {
public class TVForm : Form {
String chmFile = #"C:\temp\QuickGuide.chm"; // change this to some help file on local computer
Button btnOpenFile = new Button { Text = "Open File", AutoSize = true };
Button btnDeleteSelected = new Button { Text = "Delete Selected", AutoSize = true };
Button btnGetCount = new Button { Text = "Get Count", AutoSize = true };
Button btnGetText = new Button { Text = "Get Text", AutoSize = true }; // causes hh.exe process to crash
Button btnSelectNext = new Button { Text = "Select Next", AutoSize = true };
Process process = null;
IntPtr ptrTreeView = IntPtr.Zero;
public TVForm() {
FlowLayoutPanel p = new FlowLayoutPanel { FlowDirection = System.Windows.Forms.FlowDirection.TopDown, Dock = DockStyle.Fill };
p.Controls.Add(btnOpenFile);
p.Controls.Add(btnGetCount);
p.Controls.Add(btnDeleteSelected);
p.Controls.Add(btnGetText);
p.Controls.Add(btnSelectNext);
Controls.Add(p);
btnOpenFile.Click += buttonClicked; // works fine
btnDeleteSelected.Click += buttonClicked; // works fine
btnGetCount.Click += buttonClicked;
btnGetText.Click += buttonClicked;
btnSelectNext.Click += buttonClicked;
}
void buttonClicked(object sender, EventArgs e) {
if (!File.Exists(chmFile)) {
MessageBox.Show("Cannot find CHM file: " + chmFile);
return;
}
IntPtr hwndTreeView = GetTreeViewHandle();
if (hwndTreeView == IntPtr.Zero) {
MessageBox.Show("Cannot find TreeView handle.");
return;
}
if (sender == btnDeleteSelected) {
// get the handle to the selected node in the tree view
IntPtr hwndItem = SendMessage(hwndTreeView, (int) TVM.TVM_GETNEXTITEM, (int) TVGN.TVGN_CARET, 0);
if (hwndItem == IntPtr.Zero)
MessageBox.Show("no item selected");
else
SendMessage(hwndTreeView, (int) TVM.TVM_DELETEITEM, IntPtr.Zero, hwndItem);
}
else if (sender == btnGetCount) {
IntPtr count = SendMessage(hwndTreeView, (int) TVM.TVM_GETCOUNT, 0, 0);
MessageBox.Show(String.Format("There are {0} nodes in the tree view.", count.ToInt32()));
}
else if (sender == btnSelectNext) {
IntPtr hwndItem = SendMessage(hwndTreeView, (int) TVM.TVM_GETNEXTITEM, (int) TVGN.TVGN_CARET, 0);
if (hwndItem == IntPtr.Zero) {
MessageBox.Show("no item selected");
return;
}
hwndItem = SendMessage(hwndTreeView, (int) TVM.TVM_GETNEXTITEM, new IntPtr((int) TVGN.TVGN_NEXT), hwndItem);
if (hwndItem == IntPtr.Zero)
MessageBox.Show("there is no next item");
else
SendMessage(hwndTreeView, (int) TVM.TVM_SELECTITEM, new IntPtr((int) TVGN.TVGN_CARET), hwndItem);
}
else if (sender == btnGetText) { // crashes hh.exe
IntPtr hwndItem = SendMessage(hwndTreeView, (int) TVM.TVM_GETNEXTITEM, (int) TVGN.TVGN_CARET, 0);
if (hwndItem == IntPtr.Zero) {
MessageBox.Show("no item selected");
return;
}
var item = new TVITEMEX(); // have tried both TVITEM and TVITEMEX
item.hItem = hwndItem;
item.mask = (int) (TVIF.TVIF_TEXT | TVIF.TVIF_HANDLE);
item.cchTextMax = 260;
item.pszText = Marshal.AllocHGlobal(item.cchTextMax);
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(item));
Marshal.StructureToPtr(item, ptr, false);
// have tried TVM_GETITEM and TVM_GETITEMA
SendMessage(hwndTreeView, (int) TVM.TVM_GETITEM, IntPtr.Zero, ptr); // this line crashes hh.exe
String text = Marshal.PtrToStringAuto(item.pszText);
MessageBox.Show(text);
Marshal.FreeHGlobal(item.pszText);
Marshal.FreeHGlobal(ptr);
}
}
public Process GetProcess() {
if (process != null && !process.HasExited)
return process;
ptrTreeView = IntPtr.Zero;
process = Process.Start(chmFile);
Thread.Sleep(1000); // sleep a little to give the process time to open (otherwise cannot find the TreeView control)
return process;
}
public IntPtr GetTreeViewHandle() {
if (ptrTreeView != IntPtr.Zero && !process.HasExited)
return ptrTreeView;
var list = new List<IntPtr>();
Process p = GetProcess();
FindSysTreeView(p.MainWindowHandle, new Hashtable(), list);
if (list.Count == 0) {
return IntPtr.Zero;
}
ptrTreeView = list[0];
return ptrTreeView;
}
private static void FindSysTreeView(IntPtr p, Hashtable ht, List<IntPtr> list) {
var cn = GetClassName(p);
//var txt = GetWindowText(p);
if (cn == "SysTreeView32") {
if (!list.Contains(p))
list.Add(p);
}
if (ht[p] == null) {
ht[p] = p;
foreach (var c in GetChildWindows(p))
FindSysTreeView(c, ht, list);
}
}
public static String GetClassName(IntPtr hWnd) {
StringBuilder sb = new StringBuilder(256);
GetClassName(hWnd, sb, sb.Capacity);
return sb.ToString();
}
public static List<IntPtr> GetChildWindows(IntPtr parent) {
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try {
Win32Callback childProc = new Win32Callback(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
} finally {
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
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);
return true;
}
[DllImport("user32.dll")]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.Dll")]
private static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam);
private delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
private const int TV_FIRST = 0x1100;
public enum TVM {
TVM_GETNEXTITEM = (TV_FIRST + 10),
TVM_GETITEMA = (TV_FIRST + 12),
TVM_GETITEM = (TV_FIRST + 62),
TVM_GETCOUNT = (TV_FIRST + 5),
TVM_SELECTITEM = (TV_FIRST + 11),
TVM_DELETEITEM = (TV_FIRST + 1),
TVM_EXPAND = (TV_FIRST + 2),
TVM_GETITEMRECT = (TV_FIRST + 4),
TVM_GETINDENT = (TV_FIRST + 6),
TVM_SETINDENT = (TV_FIRST + 7),
TVM_GETIMAGELIST = (TV_FIRST + 8),
TVM_SETIMAGELIST = (TV_FIRST + 9),
TVM_GETISEARCHSTRING = (TV_FIRST + 64),
TVM_HITTEST = (TV_FIRST + 17),
}
public enum TVGN {
TVGN_ROOT = 0x0,
TVGN_NEXT = 0x1,
TVGN_PREVIOUS = 0x2,
TVGN_PARENT = 0x3,
TVGN_CHILD = 0x4,
TVGN_FIRSTVISIBLE = 0x5,
TVGN_NEXTVISIBLE = 0x6,
TVGN_PREVIOUSVISIBLE = 0x7,
TVGN_DROPHILITE = 0x8,
TVGN_CARET = 0x9,
TVGN_LASTVISIBLE = 0xA
}
[Flags]
public enum TVIF {
TVIF_TEXT = 1,
TVIF_IMAGE = 2,
TVIF_PARAM = 4,
TVIF_STATE = 8,
TVIF_HANDLE = 16,
TVIF_SELECTEDIMAGE = 32,
TVIF_CHILDREN = 64,
TVIF_INTEGRAL = 0x0080,
TVIF_DI_SETITEM = 0x1000
}
[StructLayout(LayoutKind.Sequential)]
public struct TVITEMEX {
public uint mask;
public IntPtr hItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public IntPtr lParam;
public int iIntegral;
public uint uStateEx;
public IntPtr hwnd;
public int iExpandedImage;
public int iReserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct TVITEM {
public uint mask;
public IntPtr hItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public IntPtr lParam;
}
}
}
Here is the code used to access the node text from a TreeView running in another process:
[DllImport("kernel32.dll")]
static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint dwFreeType);
// privileges
const int PROCESS_CREATE_THREAD = 0x0002;
const int PROCESS_QUERY_INFORMATION = 0x0400;
const int PROCESS_VM_OPERATION = 0x0008;
const int PROCESS_VM_WRITE = 0x0020;
const int PROCESS_VM_READ = 0x0010;
// used for memory allocation
const uint MEM_COMMIT = 0x00001000;
const int MEM_DECOMMIT = 0x4000;
const uint MEM_RESERVE = 0x00002000;
const uint PAGE_READWRITE = 4;
///<summary>Returns the tree node information from another process.</summary>
///<param name="hwndItem">Handle to a tree node item.</param>
///<param name="hwndTreeView">Handle to a tree view control.</param>
///<param name="process">Process hosting the tree view control.</param>
private static NodeData AllocTest(Process process, IntPtr hwndTreeView, IntPtr hwndItem) {
// code based on article posted here: http://www.codingvision.net/miscellaneous/c-inject-a-dll-into-a-process-w-createremotethread
// handle of the process with the required privileges
IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, process.Id);
// Write TVITEM to memory
// Invoke TVM_GETITEM
// Read TVITEM from memory
var item = new TVITEMEX();
item.hItem = hwndItem;
item.mask = (int) (TVIF.TVIF_HANDLE | TVIF.TVIF_CHILDREN | TVIF.TVIF_TEXT);
item.cchTextMax = 1024;
item.pszText = VirtualAllocEx(procHandle, IntPtr.Zero, (uint) item.cchTextMax, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // node text pointer
byte[] data = getBytes(item);
uint dwSize = (uint) data.Length;
IntPtr allocMemAddress = VirtualAllocEx(procHandle, IntPtr.Zero, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // TVITEM pointer
uint nSize = dwSize;
UIntPtr bytesWritten;
bool successWrite = WriteProcessMemory(procHandle, allocMemAddress, data, nSize, out bytesWritten);
var sm = SendMessage(hwndTreeView, (int) TVM.TVM_GETITEM, IntPtr.Zero, allocMemAddress);
UIntPtr bytesRead;
bool successRead = ReadProcessMemory(procHandle, allocMemAddress, data, nSize, out bytesRead);
UIntPtr bytesReadText;
byte[] nodeText = new byte[item.cchTextMax];
bool successReadText = ReadProcessMemory(procHandle, item.pszText, nodeText, (uint) item.cchTextMax, out bytesReadText);
bool success1 = VirtualFreeEx(procHandle, allocMemAddress, dwSize, MEM_DECOMMIT);
bool success2 = VirtualFreeEx(procHandle, item.pszText, (uint) item.cchTextMax, MEM_DECOMMIT);
var item2 = fromBytes<TVITEMEX>(data);
String name = Encoding.Unicode.GetString(nodeText);
int x = name.IndexOf('\0');
if (x >= 0)
name = name.Substring(0, x);
NodeData node = new NodeData();
node.Text = name;
node.HasChildren = (item2.cChildren == 1);
return node;
}
public class NodeData {
public String Text { get; set; }
public bool HasChildren { get; set; }
}
private static byte[] getBytes(Object item) {
int size = Marshal.SizeOf(item);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(item, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
private static T fromBytes<T>(byte[] arr) {
T item = default(T);
int size = Marshal.SizeOf(item);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(arr, 0, ptr, size);
item = (T) Marshal.PtrToStructure(ptr, typeof(T));
Marshal.FreeHGlobal(ptr);
return item;
}
// Note: different layouts required depending on OS versions.
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb773459%28v=vs.85%29.aspx
[StructLayout(LayoutKind.Sequential)]
public struct TVITEMEX {
public uint mask;
public IntPtr hItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public IntPtr lParam;
public int iIntegral;
public uint uStateEx;
public IntPtr hwnd;
public int iExpandedImage;
public int iReserved;
}
I am trying to move winamps main window, with this code:
[DllImport("user32.dll")]
static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
static void resize()
{
Process w = new Process();
w.StartInfo = new ProcessStartInfo("winamp.exe");
w.Start();
Thread.Sleep(5000);
IntPtr hWnd = GetForegroundWindow();
RECT rect;
GetWindowRect(hWnd, out rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
MoveWindow(hWnd, 0, 0, width, height, true);
}
This code snippet works with all processes that I tested, except Winamp. When I use the mainWindowHandle of the process it moves another window.
Anyone an idea how to get it working with Winamp?
With the following code I can confirm changing WinAmp's main window size does not work via the Win32 API's.
Changing other winamp window sizes via the Win32 API's did work, but is not a solution:
public partial class Form1 : Form
{
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
[DllImport("user32.dll")]
static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
private System.IntPtr hWnd;
private void button1_Click(object sender, EventArgs e)
{
Process p = Process.Start(#"C:\Program Files\Winamp\winamp.exe");
try
{
do
{
p.Refresh();
}
while (p.MainWindowHandle.ToInt32() == 0);
hWnd = new IntPtr(p.MainWindowHandle.ToInt32());
}
catch (Exception ex)
{
//Do some stuff...
throw;
}
}
private void button2_Click(object sender, EventArgs e)
{
//Make sure we have a handle to the shelled exe
if (hWnd == new IntPtr(0)) return;
ResizeExternalExeChildWindows(hWnd);
}
private void ResizeExternalExeChildWindows(IntPtr parent)
{
List<IntPtr> childWindows = GetChildWindows(hWnd);
foreach (IntPtr childWindow in childWindows)
{
RECT rect;
GetWindowRect(childWindow, out rect);
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
MoveWindow(hWnd, 0, 0, width, height, true);
}
}
// <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);
}
}
Cause
Because WinAmp is skinnable it supports resizing via config files (not via external apps using win32 API's).
Solution
Open the file C:\Users[username]\AppData\Roaming\Winamp\studio.xnf and edit the following values:
<entry name="Bento_nomax_h" value="492" />
<entry name="Bento_nomax_w" value="633" />
<entry name="Bento_nomax_x" value="27" />
<entry name="Bento_nomax_y" value="16" />
I am using the Bento Skin. If you open the WinAmp.ini file you can detect the skin being used:
skin=Bento
The solution to set the initial size for WinAmp's main window is to edit the config file before shelling WinAmp with Process.Start.
I have tried your code, it is not working always, and it is not re-sizing windows, it was moving it only,
Actually I found a problem in the 2000 ms sleep, which you can change to a while loop checking if the handle is zero then proceed with the code,
while (p.MainWindowHandle == 0)
{
}
IntPtr hWnd = p.MainWindowHandle;
this may help if the winamp takes too much time to build its own window
This worked for me using the latest version, please give it a try:
internal struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, ExactSpelling = true, SetLastError = true)]
internal static extern bool GetWindowRect(IntPtr hWnd, ref RECT rect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
public static class HWND
{
public static IntPtr
NoTopMost = new IntPtr(-2),
TopMost = new IntPtr(-1),
Top = new IntPtr(0),
Bottom = new IntPtr(1);
}
[Flags]
public enum SetWindowPosFlags : uint
{
SWP_ASYNCWINDOWPOS = 0x4000,
SWP_DEFERERASE = 0x2000,
SWP_DRAWFRAME = 0x0020,
SWP_FRAMECHANGED = 0x0020,
SWP_HIDEWINDOW = 0x0080,
SWP_NOACTIVATE = 0x0010,
SWP_NOCOPYBITS = 0x0100,
SWP_NOMOVE = 0x0002,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOREDRAW = 0x0008,
SWP_NOREPOSITION = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_NOSIZE = 0x0001,
SWP_NOZORDER = 0x0004,
SWP_SHOWWINDOW = 0x0040,
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
static void Resize()
{
IntPtr winampMainWindow = IntPtr.Zero;
while (true)
{
winampMainWindow = FindWindow("BaseWindow_RootWnd", "Main Window");
if (winampMainWindow != IntPtr.Zero)
{
RECT rect = new RECT();
GetWindowRect(winampMainWindow, ref rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
SetWindowPos(winampMainWindow,
HWND.Top,
0,
0,
width,
height,
SetWindowPosFlags.SWP_SHOWWINDOW | SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOSENDCHANGING | SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOCOPYBITS);
break;
}
Thread.Sleep(1000);
}
}
[STAThread]
static void Main()
{
Process.Start("winamp.exe");
Resize();
}
Absolute nightmare!
However, right clicking in the "song window" DID open up the docking menu - I think the undock-(something) worked. One of these options allowed me to move the god-damned thing. It was a fluke, but something opened up there that worked.