Make a wallpaper window clickable - c#

I created a library which lets you move any window behind the desktop icons using the user32.dll, but the problem is that the window doesn't react neither to clicks or key presses (even KeyboardDevice didn't seem to capture anything)'
One thing to note though is that after I make any window run on that wallpaper layer, it disappears from the task bar.
This is the code:
public static void MoveBehindIcons(IntPtr windowHandle)
{
IntPtr window = W32.FindWindow("Progman", (string) null);
IntPtr result = IntPtr.Zero;
int num1 = 1324;
IntPtr wParam = new IntPtr(0);
IntPtr zero = IntPtr.Zero;
int num2 = 0;
int num3 = 1000;
W32.SendMessageTimeout(window, (uint) num1, wParam, zero, (W32.SendMessageTimeoutFlags) num2, (uint) num3, out result);
IntPtr workerw = IntPtr.Zero;
W32.EnumWindows((W32.EnumWindowsProc) ((tophandle, topparamhandle) =>
{
if (W32.FindWindowEx(tophandle, IntPtr.Zero, "SHELLDLL_DefView", IntPtr.Zero) != IntPtr.Zero)
workerw = W32.FindWindowEx(IntPtr.Zero, tophandle, "WorkerW", IntPtr.Zero);
return true;
}), IntPtr.Zero);
W32.SetParent(windowHandle, workerw);
}
the W32 class: https://pastebin.com/Tw72Lbw6
WallpaperEngine can do it somehow..
Is it possible to make it work?

Related

How to set custom position of OpenFileDialog in extended monitor uisng C# winforms?

I am using Extended monitor screen. How to set the custom popup position of the predefined windows of Microsoft (for Example: OpenFileDialog) in windows forms using C#. This dialog should open to the center of the parent.
There is an other question answered by Dima, who wrote the following:
Open file dialog is a system dialog. It means that your application
has no control over it. It is controlled by user. If user wants to
change its size in one application, it will gain new size in all
applications, since this size is user's preference.
Purpose of common dialogs is providing standard UI parts to user.
Imagine how uncomfortable for users it would be to search for file to
open if every application would use its own OpenFileDialog.
However you can build your own dialog.
But if you want to open your custom form you can use this:
SomeForm form = new SomeForm();
form.StartPosition = FormStartPosition.CenterParent;
form.ShowDialog(this);
Sadly it will not work this way if you use this method with simple Show() maybe because than it will use another thread and cant access to the parent, or I don't really know.
I hope it helped.
EDIT:
If your problem is that the dialog isn't opening on the same screen your application runs maybe there is a solution here:
private void button1_Click(object sender, EventArgs e)
{
Point myNewLocation = Location;
myNewLocation.Offset(20, 20);
OpenFileDialog dlg = new OpenFileDialog();
dlg.Title = "Open";
MoveDialogWhenOpened(dlg.Title, myNewLocation);
dlg.ShowDialog(this);
}
private void MoveDialogWhenOpened(String windowCaption, Point location)
{
Object[] argument = new Object[] { windowCaption, location };
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += new DoWorkEventHandler(MoveDialogThread);
backgroundWorker.RunWorkerAsync(argument);
}
private void MoveDialogThread(Object sender, DoWorkEventArgs e)
{
const String DialogWindowClass = "#32770";
String windowCaption = (String)(((Object[])e.Argument)[0]);
Point location = (Point)(((Object[])e.Argument)[1]);
// try for a maximum of 4 seconds (sleepTime * maxAttempts)
Int32 sleepTime = 10; // milliseconds
Int32 maxAttempts = 400;
for (Int32 i = 0; i < maxAttempts; ++i)
{
// find the handle to the dialog
IntPtr handle = Win32Api.FindWindow(DialogWindowClass, windowCaption);
// if the handle was found and the dialog is visible
if ((Int32)handle > 0 && Win32Api.IsWindowVisible(handle) > 0)
{
// move it
Win32Api.SetWindowPos(handle, (IntPtr)0, location.X, location.Y,
0, 0, Win32Api.SWP_NOSIZE | Win32Api.SWP_NOZORDER);
break;
}
// if not found wait a brief sec and try again
Thread.Sleep(sleepTime);
}
}
public class Win32Api
{
public const Int32 SWP_NOSIZE = 0x1;
public const Int32 SWP_NOZORDER = 0x4;
[DllImport("user32")]
public static extern IntPtr FindWindow(String lpClassName,
String lpWindowName);
[DllImport("user32")]
public static extern Int32 IsWindowVisible(IntPtr hwnd);
[DllImport("user32")]
public static extern Int32 SetWindowPos(IntPtr hwnd,
IntPtr hwndInsertAfter,
Int32 x,
Int32 y,
Int32 cx,
Int32 cy,
Int32 wFlags);
}
GetOpenFilename() allows you specify a hook procedure via the OFN_ENABLEHOOK flag,
and through this, you can create a hook procedure that receives a window handle to a child window of the open file dialog. By calling GetParent() from the hook procedure, you can subclass the window procedure of the GetOpenFilename() dialog and control where it locates itself when it is shown.
This method works well for me.
HWND hMainWnd = NULL; // Designated window handle assigned before GetOpenFilename() call.
TCHAR szFilepath[512] = _T(""); // Filename returned from GetOpenFile().
const UINT WM_OPENSAVE_DIALOG = RegisterWindowMessage(_T("OPENSAVE"));
bool bTriggerMove = false; // Move open file dialog only once, on WM_INITDIALOG.
WNDPROC pOldDlgProc = NULL; // Pointer to original window procedur for open file dialog
// Forward declarations.
BOOL CALLBACK NewOpenFileProc( HWND hDlg, UINT uMsg,
WPARAM wParam, LPARAM lParam );
UINT_PTR CALLBACK OpenFileHookProc( HWND hDlg, UINT uMsg,
WPARAM wParam, LPARAM lParam );
// Define a hook procedure for the GetOpenFilename() call
//---------------------------------------------------------------------------
UINT_PTR CALLBACK OpenFileHookProc( HWND hDlg, UINT uMsg,
WPARAM wParam, LPARAM lParam )
{
//==========================================================================
// This is the publicly exposed hook procedure for the open file dialog.
// From here, we subclass the parent window to get the *actual* OpenFile
// dialog box procedure, hook it, and set our trigger
if( uMsg == WM_INITDIALOG )
{
//The hook procedure handle is a child window of the actual dialog.
HWND hDlgParent = GetParent( hDlg );
pOldDlgProc = (WNDPROC)GetWindowLong( hDlgParent, GWL_WNDPROC );
bTriggerMove = true;
SetWindowLong( hDlgParent, GWL_WNDPROC, (LONG) NewOpenFileProc );
}
return 0;
}
// Define a window procedure subclass for the GetOpenFilename() call
//---------------------------------------------------------------------------
BOOL CALLBACK NewOpenFileProc( HWND hDlg, UINT uMsg,
WPARAM wParam, LPARAM lParam )
{
BOOL bResult = FALSE;
//========================================================================
// This is a window procedure subclass that centers a file open dialog
// over a designated window when GetOpenFilename() is called and refers
// to the OPENFILENAME structure defined in the function MyFileOpenDialog(),
// below. The dialog is centered over it's designated window regardless of
// what monitor the designated window is on.
if( pOldDlgProc != NULL )
{
if( uMsg == WM_NCDESTROY )
{
// Destroying open dialog window so remove window subclass.
SetWindowLong( hDlg, GWL_WNDPROC,(LONG)pOldDlgProc );
// Falls through and calls the old dialog procedure.
}
else
if( uMsg == WM_SHOWWINDOW )
{
// Showing or hide the open dialog window
if( wParam == TRUE && bTriggerMove == true )
{
// Showing the window for the first time.
bTriggerMove = false;
SendMessage( hDlg, WM_OPENSAVE_DIALOG, 0, 0 );
}
// Falls through and calls the old dialog procedure.
}
else
if( uMsg == WM_OPENSAVE_DIALOG )
{
// Center the dialog over hMainWnd (the designated window).
RECT rcDlg; int cxDlg, cyDlg;
RECT rcWnd; int cxWnd, cyWnd;
int iLeft, iTop;
// main window metrics
GetWindowRect( hMainWnd, &rcWnd );
cxWnd= rcWnd.right - rcWnd.left;
cyWnd= rcWnd.bottom - rcWnd.top;
// OpenFilename dialog metrics
GetWindowRect( hDlg, &rcDlg );
cxDlg= rcDlg.right - rcWnd.left;
cyDlg= rcDlg.bottom - rcDlg.top;
iLeft = rcWnd.left + ( ( cxWnd - cxDlg ) / 2);
iTop = rcWnd.top + ( ( cyWnd - cyDlg ) / 2);
SetWindowPos( hDlg, NULL, iLeft, iTop , 0, 0, SWP_NOSIZE );
//Don't call old window procedure for this custom message.
return FALSE;
}
bResult = CallWindowProc( pOldDlgProc, hDlg, uMsg, wParam, lParam );
}
return bResult;
}
// Define a function to incorporate necessary business logic, assign
// handle of window to center the dialog on, then call GetOpenFilename().
//---------------------------------------------------------------------------
void MyOpenFileDialog( void )
{
OPENFILENAME ofn;
// Initialize OPENFILENAME
memset( &ofn, 0, sizeof(ofn) );
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hMainWnd;
ofn.lpstrFile = szFilepath;
ofn.nMaxFile = RTL_NUMBER_OF(szFilepath);
ofn.lpstrFilter = _T("Excel Workbook\0*.xls\0");
ofn.nFilterIndex = 0;
ofn.lpstrTitle = _T("Import from Excel...");
ofn.lpfnHook = OFNHookProc;
ofn.Flags = OFN_PATHMUSTEXIST |
OFN_FILEMUSTEXIST |
OFN_ENABLEHOOK |
OFN_EXPLORER |
OFN_ENABLESIZING |
0;
// Display the Open dialog box.
if( GetOpenFileName( &ofn ) != TRUE )
{
return;
}
}

How to fix memory leak in Word Add-In

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);
}

Change Window's Sticky Notes (StikyNot.exe) location (multiple instances)

It seems that all notes under StikyNot.exe are single exes instead of multiple. Also that means the coordinates of its location are always 0, 0, 0, 0. Is there a way to move it around? I tried using Win32's MoveWindow function without success.
Here's an example of how to iterate through all the Sticky Note windows and move each of them. (Error checking has been removed for brevity. Also, be sure to read the note at the end for some comments on this implementation.)
First, we have to define the RECT struct.
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public RECT(int l, int t, int r, int b)
{
Left = l;
Top = t;
Right = r;
Bottom = b;
}
public int Left;
public int Top;
public int Right;
public int Bottom;
}
Then some key p/Invokes. We'll need FindWindowExW to locate the window with the correct window class for a sticky note. We also need GetWindowRect, so we can figure out the size of the window, so we only move it, rather than a move and resize. Finally, we need SetWindowPos which is pretty self-explanatory.
[DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr FindWindowExW(IntPtr hWndParent, IntPtr hWndAfter,
string lpszClass, string lpszWindow);
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
Finally our algorithm.
IntPtr hWnd = FindWindowExW(IntPtr.Zero, IntPtr.Zero, "Sticky_Notes_Note_Window", null);
if (hWnd == IntPtr.Zero)
{
int error = Marshal.GetLastWin32Error();
if (error > 0) throw new Win32Exception(error);
else return;
}
IntPtr first = hWnd;
int currentX = 0;
while (hWnd != IntPtr.Zero)
{
RECT r;
bool result = GetWindowRect(hWnd, out r);
if (!result)
{
int error = Marshal.GetLastWin32Error();
if (error > 0) throw new Win32Exception(error);
else return;
}
result = SetWindowPos(hWnd,
IntPtr.Zero,
currentX,
0,
r.Right - r.Left,
r.Bottom - r.Top,
0);
if (!result)
{
int error = Marshal.GetLastWin32Error();
if (error > 0) throw new Win32Exception(error);
else return;
}
currentX += r.Right - r.Left;
hWnd = FindWindowExW(IntPtr.Zero, hWnd, "Sticky_Notes_Note_Window", null);
if (hWnd == IntPtr.Zero)
{
int error = Marshal.GetLastWin32Error();
if (error > 0) throw new Win32Exception(error);
else return;
}
if (hWnd == first) hWnd = IntPtr.Zero;
}
How does it work? First, using a tool like Spy++, I found the window class. From the window's property sheet, we can see that the window's class name is Sticky_Notes_Note_Window.
With the information from Spy++, the first window handle is obtained using FindWindowExW. This value is cached so that it can be determined when we've finished iterating all the windows. Inside the loop, we move the window, then use FindWindowEx to again locate the next window with the same class, if none are found, hWnd will be IntPtr.Zero aka NULL. We also have to check whether we are back to the start of our iteration. (If the notes are wider than the screen, they will spill off to the right. Wrapping the notes to another row is left as an exercise)
The issue with this implementation is that, if the first sticky note is closed, before we have iterated through all of them, then the program will never terminate. It would be better to keep track of all the windows that have been seen, and if any is seen again, then all have been enumerated.
An alternative method would be to use EnumWindows and inside the callback call GetClassName to see if it's a Sticky_Notes_Note_Window, and then act appropriately. The method above required less work, so it's the method I chose.
References:
RECT Structure
FindWindowEx(W)
GetWindowRect
SetWindowPos
Edit: Added error checking based on #DavidHeffernan's comment. Also added clarification about how I found the Window class name.

Send mouse click to minimized window. I don't want to maximize or move the mouse

I want to simulate a mouseclick at a specific position inside a window that is hidden. So no maximizing the window or moving the mouse. I just want to send the right message.
Now I tried something like this
DllImport("user32.dll")]
public static extern int PostMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
static int WM_LBUTTONDOWN = 0x0201;
static int WM_LBUTTONUP = 0x0202;
private static IntPtr MakeLParam(int LoWord, int HiWord)
{
return (IntPtr)((HiWord << 16) | (LoWord & 0xffff));
}
public static void test()
{
int windowPtr = FindWindow(null, "Calculator");
Console.WriteLine("pointer: " + windowPtr);
int x = 260;
int y = 180;
IntPtr lParam = MakeLParam(x, y);
IntPtr windowPointer = new IntPtr(windowPtr);
PostMessage(windowPointer, WM_LBUTTONDOWN, IntPtr.Zero, lParam);
PostMessage(windowPointer, WM_LBUTTONUP, IntPtr.Zero, lParam);
}
I expected this program to click the 8 on the calculator ( if you set it to scientific ), but it doesn't do that.
How can I simulate that click? Please don't suggest finding the windows-control-element for the "8"-Button, this is not what I am trying to do here.

How to select an item in a TreeView using Win32 API

I am trying to automate a sequence of user inputs to a compiled application in C# using Win32 API. I do not have any source code for the application I am trying to control and it is running while I am trying to control it. In my code, I have a single button that, when clicked, needs to make a sequence of 3 inputs to the application I am trying to control:
Select an item in a treeview
Click a button
Click another button
The way it works is the button in step 2 performs an action depending on the item selected in the treeview in step 1. I am able to get the button clicks to work just fine by simply sending a message, but I cannot figure out how to select the TreeView item I want. The TreeView is static, so the items and layout will never change. It has the following layout:
-itemsA
-itemsB
--itemB1
-itemsC
Where itemB1 is the item that needs to be selected in order for the button clicks in steps 2 and 3 to work. By default ItemsB is collapsed, so I probably need to expand it before I can select ItemB1? Here is my code. I really appreciate any help!!
//Find Window API
[DllImport("User32.dll")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
//Find WindowEx API
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
//Send Message API
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(int hWnd, int msg, int wParam, IntPtr lParam);
private const int BN_CLICKED = 245;
//Method called by button click
public static void Start()
{
int hwnd = 0;
int prod = 0;
IntPtr hwndChild = IntPtr.Zero;
IntPtr treeChild = IntPtr.Zero;
IntPtr prodChild = IntPtr.Zero;
hwnd = FindWindow(null, "Application");
if (hwnd > 0)
{
//Get Handle for TreeView, THIS IS WHERE I AM STUCK!!
treeChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "AfxMDIFrame80", null);
treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "AfxMDIFrame80", null);
treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "SysTreeView32", null);
//Need to Add code to select item in TreeView ???
//Click First Button
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "AfxMDIFrame80", null);
hwndChild = FindWindowEx((IntPtr)hwndChild, IntPtr.Zero, "AfxMDIFrame80", null);
hwndChild = FindWindowEx((IntPtr)hwndChild, IntPtr.Zero, "#32770", null);
IntPtr scanBtn = FindWindowEx((IntPtr)hwndChild, IntPtr.Zero, "Button", "&Scan");
SendMessage((int)scanBtn, BN_CLICKED, 0, IntPtr.Zero);
//Click Second Button
prod = FindWindow("#32770", "Product: WPC");
prodChild = FindWindowEx((IntPtr)prod, IntPtr.Zero, "Button", "&Collect");
SendMessage((int)prodChild, BN_CLICKED, 0, IntPtr.Zero);
}
}//END Start
Hans,
Can you give me an example of how I would do this? The problem I am really having is finding the handle for the treeview item I want to select. If I use Spy++ to find the current handle and hardcode it into my method, it works fine, like this:
SendMessage((int)treeChild, TV_SELECTITEM, TVGN_CARET, (IntPtr)0x092DCB30);
If I use SendMessage and send TVGN_ROOT to the TreeView Handle, will it return an IntPtr with the handle for the item to select in the treeview, or how does that work? I am also experimenting with AutoIt, but I was hoping to keep all of my code in one application.
I figured it out, so I'll post for anyone else who is interested, I have had a hard time finding documentation on this. Here is the majority of my code:
//Define TreeView Flags and Messages
private const int BN_CLICKED = 0xF5;
private const int TV_FIRST = 0x1100;
private const int TVGN_ROOT = 0x0;
private const int TVGN_NEXT = 0x1;
private const int TVGN_CHILD = 0x4;
private const int TVGN_FIRSTVISIBLE = 0x5;
private const int TVGN_NEXTVISIBLE = 0x6;
private const int TVGN_CARET = 0x9;
private const int TVM_SELECTITEM = (TV_FIRST + 11);
private const int TVM_GETNEXTITEM = (TV_FIRST + 10);
private const int TVM_GETITEM = (TV_FIRST + 12);
//Find Window API
[DllImport("User32.dll")]
public static extern Int32 FindWindow(String lpClassName, String lpWindowName);
//Find WindowEx API
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
//Send Message API
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(int hWnd, int msg, int wParam, IntPtr lParam);
public static void Start()
{
//Handle variables
int treeItem = 0;
IntPtr treeChild = IntPtr.Zero;
int hwnd = FindWindow(null, "Application"); //Handle for the application to be controlled
if (hwnd > 0)
{
//Select TreeView Item
treeChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "AfxMDIFrame80", null);
treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "AfxMDIFrame80", null);
treeChild = FindWindowEx((IntPtr)treeChild, IntPtr.Zero, "SysTreeView32", null);
treeItem = SendMessage((int)treeChild, TVM_GETNEXTITEM, TVGN_ROOT, IntPtr.Zero);
treeItem = SendMessage((int)treeChild, TVM_GETNEXTITEM, TVGN_NEXT, (IntPtr)treeItem);
treeItem = SendMessage((int)treeChild, TVM_GETNEXTITEM, TVGN_CHILD, (IntPtr)treeItem);
SendMessage((int)treeChild, TVM_SELECTITEM, TVGN_CARET, (IntPtr)treeItem);
// ...Continue with my automation...
}
}
I may still not understand this 100%, but hopefully this helps. The SendMessage returns value will depend on what message you are sending, in this case, it was an int containing the handle of a TreeView item. The first argument is the handle to the TreeView itself. The second argument is the Message you want to send. The 3rd and 4th arguments are flags. The 3rd specifies the type of item, the 4th is the handle of the current TreeView item.
Thanks for the help Hans! Anyone else, please feel free to elaborate.
You'll need to walk the nodes with TVM_GETNEXTITEM, starting at TVGN_ROOT. Then select it with TVM_SELECTITEM. Pass the TVGN_FIRSTVISIBLE to ensure it is visible, shouldn't be necessary if you just automate it.
Take a look at AutoIt to avoid writing grungy code like this.
I know this is quite late in coming, but if you are having a similar issue (as am I). You might take a look at AutoHotKey, especially if you are familiar with SendMessage. This would save you the need to compile and a lot of complexity, but to your point it would be possible to navigate through the structure using arrow key presses.

Categories

Resources