Table of Contents

Desktop Automation Patterns

This guide provides comprehensive examples for automating desktop applications across different UI frameworks including WinForms, WPF, Win32, and native Windows applications.

🖥️ Technology Coverage

Framework Description Common Use Cases
WinForms Traditional .NET Windows Forms Legacy applications, Simple desktop apps
WPF Windows Presentation Foundation Modern .NET desktop applications
Win32 Native Windows API applications System utilities, Legacy software
Office Applications Microsoft Office automation Document processing, Reporting
System Dialogs Windows system dialogs File operations, Security prompts

WinForms Patterns

Basic WinForms Automation

using Ranorex;
using Ranorex.Core;
using System;
using System.Linq;

public class WinFormsAutomation
{
    public void AutomateCalculator()
    {
        Host.Initialize();
        
        try
        {
            // Launch Windows Calculator
            System.Diagnostics.Process.Start("calc.exe");
            
            // Wait for calculator window
            var calculatorWindow = "/form[@processname='Calculator']";
            Host.Local.FindSingle<Form>(calculatorWindow, 10000);
            
            // Perform calculation: 123 + 456 = 579
            PerformCalculation("123", "+", "456");
            
            // Verify result
            var resultDisplay = Host.Local.FindSingle<Text>($"{calculatorWindow}//text[@automationid='CalculatorResults']");
            var result = resultDisplay.Text.Replace("Display is ", "").Trim();
            
            if (result == "579")
            {
                Report.Success("Calculator", "Calculation result is correct: 579");
            }
            else
            {
                Report.Failure("Calculator", $"Expected 579, but got: {result}");
            }
            
            // Close calculator
            var closeButton = Host.Local.FindSingle<Button>($"{calculatorWindow}//button[@name='Close']");
            closeButton.Click();
        }
        catch (Exception ex)
        {
            Report.Failure("WinForms Automation", $"Failed: {ex.Message}");
            ScreenshotHelper.CaptureOnFailure("WinFormsCalculator", ex);
            throw;
        }
        finally
        {
            Host.Shutdown();
        }
    }
    
    private void PerformCalculation(string num1, string operation, string num2)
    {
        var calculatorWindow = "/form[@processname='Calculator']";
        
        // Enter first number
        foreach (char digit in num1)
        {
            var buttonPath = $"{calculatorWindow}//button[@name='{digit}']";
            var button = Host.Local.FindSingle<Button>(buttonPath);
            button.Click();
            Delay.Milliseconds(100);
        }
        
        // Click operation
        var operationButton = Host.Local.FindSingle<Button>($"{calculatorWindow}//button[@name='{operation}']");
        operationButton.Click();
        
        // Enter second number
        foreach (char digit in num2)
        {
            var buttonPath = $"{calculatorWindow}//button[@name='{digit}']";
            var button = Host.Local.FindSingle<Button>(buttonPath);
            button.Click();
            Delay.Milliseconds(100);
        }
        
        // Click equals
        var equalsButton = Host.Local.FindSingle<Button>($"{calculatorWindow}//button[@name='Equals']");
        equalsButton.Click();
    }
}

Advanced WinForms Data Grid Automation

using Ranorex;
using System;
using System.Collections.Generic;
using System.Linq;

public class DataGridAutomation
{
    /// <summary>
    /// Extracts all data from a WinForms DataGridView
    /// </summary>
    public List<Dictionary<string, string>> ExtractDataGridData(string gridPath)
    {
        var results = new List<Dictionary<string, string>>();
        
        try
        {
            var dataGrid = Host.Local.FindSingle<Table>(gridPath);
            
            // Get column headers
            var headers = dataGrid.Find<Cell>("./row[1]/cell")
                                 .Select(cell => cell.Text?.Trim())
                                 .ToList();
            
            // Get data rows (skip header row)
            var dataRows = dataGrid.Find<Row>("./row").Skip(1);
            
            foreach (var row in dataRows)
            {
                var cells = row.Find<Cell>("./cell");
                var rowData = new Dictionary<string, string>();
                
                for (int i = 0; i < Math.Min(headers.Count, cells.Count); i++)
                {
                    var headerName = headers[i] ?? $"Column{i + 1}";
                    var cellValue = cells[i].Text?.Trim() ?? "";
                    rowData[headerName] = cellValue;
                }
                
                results.Add(rowData);
            }
            
            Report.Info("DataGrid", $"Extracted {results.Count} rows with {headers.Count} columns");
            return results;
        }
        catch (Exception ex)
        {
            Report.Error("DataGrid", $"Failed to extract data: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// Searches and filters data in a WinForms DataGridView
    /// </summary>
    public void SearchAndFilterGrid(string gridPath, string searchColumn, string searchValue)
    {
        try
        {
            var dataGrid = Host.Local.FindSingle<Table>(gridPath);
            
            // Right-click on column header to access filter
            var headers = dataGrid.Find<Cell>("./row[1]/cell");
            var targetHeader = headers.FirstOrDefault(h => h.Text?.Contains(searchColumn) == true);
            
            if (targetHeader != null)
            {
                targetHeader.Click(Location.CenterRight);
                
                // Look for filter option in context menu
                var filterOption = Host.Local.FindSingle<MenuItem>("//menuitem[contains(@text, 'Filter')]", 2000);
                filterOption.Click();
                
                // Enter search value in filter dialog
                var filterDialog = Host.Local.FindSingle<Form>("//form[contains(@title, 'Filter')]", 3000);
                var searchBox = filterDialog.FindSingle<Text>("//text[@name='SearchBox' or @name='FilterValue']");
                
                searchBox.Click();
                searchBox.PressKeys("{Ctrl}a");
                searchBox.PressKeys(searchValue);
                
                // Apply filter
                var applyButton = filterDialog.FindSingle<Button>("//button[@text='Apply' or @text='OK']");
                applyButton.Click();
                
                Report.Success("DataGrid Filter", $"Applied filter: {searchColumn} = {searchValue}");
            }
            else
            {
                throw new ElementNotFoundException($"Column '{searchColumn}' not found");
            }
        }
        catch (Exception ex)
        {
            Report.Failure("DataGrid Filter", $"Failed to apply filter: {ex.Message}");
            throw;
        }
    }
}

WPF Patterns

WPF Application Automation

using Ranorex;
using System;
using System.Collections.Generic;

public class WpfAutomation
{
    /// <summary>
    /// Automates WPF TreeView navigation
    /// </summary>
    public void NavigateWpfTreeView(string treeViewPath, string[] nodePath)
    {
        try
        {
            var treeView = Host.Local.FindSingle<Tree>(treeViewPath);
            
            string currentPath = treeViewPath;
            
            foreach (string nodeName in nodePath)
            {
                // Find the tree item by name
                var treeItem = Host.Local.FindSingle<TreeItem>($"{currentPath}//treeitem[@text='{nodeName}']");
                
                // Expand if it has children
                if (treeItem.Expanded == false)
                {
                    var expandButton = treeItem.FindSingle<Button>("./button[@name='Expander']", 1000);
                    if (expandButton.Exists)
                    {
                        expandButton.Click();
                        Delay.Milliseconds(500); // Wait for expansion
                    }
                }
                
                // Update current path for next iteration
                currentPath = treeItem.GetPath().ToString();
                
                Report.Info("TreeView", $"Navigated to node: {nodeName}");
            }
            
            // Final selection
            var finalNode = Host.Local.FindSingle<TreeItem>(currentPath);
            finalNode.Click();
            
            Report.Success("TreeView Navigation", "Successfully navigated to target node");
        }
        catch (Exception ex)
        {
            Report.Failure("TreeView Navigation", $"Failed: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// Handles WPF ComboBox with complex data
    /// </summary>
    public void SelectFromWpfComboBox(string comboBoxPath, string displayText)
    {
        try
        {
            var comboBox = Host.Local.FindSingle<ComboBox>(comboBoxPath);
            
            // Open dropdown
            comboBox.Click();
            Delay.Milliseconds(300);
            
            // Try to find item by display text
            var dropdownItems = comboBox.Find<ListItem>("./listitem");
            var targetItem = dropdownItems.FirstOrDefault(item => 
                item.Text?.Contains(displayText) == true ||
                item.GetAttributeValue("DisplayText")?.Contains(displayText) == true);
            
            if (targetItem != null)
            {
                targetItem.Click();
                Report.Success("ComboBox", $"Selected item: {displayText}");
            }
            else
            {
                // Try typing to search
                comboBox.PressKeys(displayText);
                comboBox.PressKeys("{Return}");
                Report.Info("ComboBox", $"Typed selection: {displayText}");
            }
        }
        catch (Exception ex)
        {
            Report.Failure("ComboBox Selection", $"Failed to select '{displayText}': {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// Automates WPF TabControl navigation
    /// </summary>
    public void NavigateWpfTabs(string tabControlPath, string tabName)
    {
        try
        {
            var tabControl = Host.Local.FindSingle<TabControl>(tabControlPath);
            
            // Find tab by name or text
            var tabItem = tabControl.FindSingle<TabItem>($"./tabitem[@text='{tabName}' or @name='{tabName}']");
            
            if (!tabItem.Selected)
            {
                tabItem.Click();
                Delay.Milliseconds(500); // Wait for tab content to load
            }
            
            // Verify tab is active
            if (tabItem.Selected)
            {
                Report.Success("Tab Navigation", $"Successfully switched to tab: {tabName}");
            }
            else
            {
                throw new Exception($"Failed to activate tab: {tabName}");
            }
        }
        catch (Exception ex)
        {
            Report.Failure("Tab Navigation", $"Failed: {ex.Message}");
            throw;
        }
    }
}

Win32 Patterns

Win32 Application Automation

using Ranorex;
using System;
using System.Runtime.InteropServices;

public class Win32Automation
{
    // Win32 API declarations
    [DllImport("user32.dll")]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
    [DllImport("user32.dll")]
    static extern bool SetForegroundWindow(IntPtr hWnd);
    
    [DllImport("user32.dll")]
    static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    
    const int SW_RESTORE = 9;
    
    /// <summary>
    /// Automates legacy Win32 applications using window handles
    /// </summary>
    public void AutomateLegacyApplication(string windowTitle)
    {
        try
        {
            // Find window by title
            IntPtr windowHandle = FindWindow(null, windowTitle);
            
            if (windowHandle == IntPtr.Zero)
            {
                throw new Exception($"Window '{windowTitle}' not found");
            }
            
            // Bring window to foreground
            ShowWindow(windowHandle, SW_RESTORE);
            SetForegroundWindow(windowHandle);
            
            // Convert to Ranorex element
            var windowElement = Host.Local.FindSingle<Form>($"/form[@title='{windowTitle}']");
            
            // Automate using Ranorex from here
            AutomateWindowContent(windowElement);
            
            Report.Success("Win32 Automation", $"Successfully automated window: {windowTitle}");
        }
        catch (Exception ex)
        {
            Report.Failure("Win32 Automation", $"Failed: {ex.Message}");
            throw;
        }
    }
    
    private void AutomateWindowContent(Form window)
    {
        // Generic window content automation
        var buttons = window.Find<Button>("//button");
        foreach (var button in buttons)
        {
            if (button.Text.Contains("OK") || button.Text.Contains("Apply"))
            {
                Report.Info("Win32", $"Found actionable button: {button.Text}");
            }
        }
        
        var textFields = window.Find<Text>("//text[@enabled='True']");
        foreach (var field in textFields)
        {
            if (string.IsNullOrEmpty(field.Text))
            {
                Report.Info("Win32", "Found empty text field for potential input");
            }
        }
    }
    
    /// <summary>
    /// Handles Win32 message boxes and dialogs
    /// </summary>
    public bool HandleMessageBox(string expectedTitle, string buttonToClick = "OK")
    {
        try
        {
            var messageBox = Host.Local.FindSingle<Form>($"/form[@title='{expectedTitle}']", 5000);
            
            if (messageBox.Exists)
            {
                var messageText = messageBox.FindSingle<Text>("//text", 1000);
                if (messageText.Exists)
                {
                    Report.Info("Message Box", $"Message: {messageText.Text}");
                }
                
                var button = messageBox.FindSingle<Button>($"//button[@text='{buttonToClick}']");
                button.Click();
                
                Report.Success("Message Box", $"Handled message box: {expectedTitle}");
                return true;
            }
            
            return false;
        }
        catch (Exception ex)
        {
            Report.Warn("Message Box", $"Failed to handle message box: {ex.Message}");
            return false;
        }
    }
}

Office Automation

Microsoft Office Automation

using Ranorex;
using System;
using System.IO;

public class OfficeAutomation
{
    /// <summary>
    /// Automates Microsoft Excel operations
    /// </summary>
    public void AutomateExcel(string filePath)
    {
        try
        {
            // Launch Excel with specific file
            System.Diagnostics.Process.Start("excel.exe", $"\"{filePath}\"");
            
            // Wait for Excel window
            var excelWindow = "/form[@processname='EXCEL']";
            Host.Local.FindSingle<Form>(excelWindow, 15000);
            
            // Navigate to specific cell
            var cellA1 = Host.Local.FindSingle<Cell>($"{excelWindow}//cell[@name='A1']");
            cellA1.Click();
            
            // Enter data
            cellA1.PressKeys("Test Data");
            cellA1.PressKeys("{Return}");
            
            // Format cell (make it bold)
            cellA1.Click();
            Host.Local.FindSingle<Form>(excelWindow).PressKeys("{Ctrl}b");
            
            // Save file
            Host.Local.FindSingle<Form>(excelWindow).PressKeys("{Ctrl}s");
            
            // Close Excel
            Host.Local.FindSingle<Form>(excelWindow).PressKeys("{Alt}{F4}");
            
            Report.Success("Excel Automation", "Successfully automated Excel operations");
        }
        catch (Exception ex)
        {
            Report.Failure("Excel Automation", $"Failed: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// Automates Microsoft Word document creation
    /// </summary>
    public void AutomateWord(string documentTitle)
    {
        try
        {
            // Launch Word
            System.Diagnostics.Process.Start("winword.exe");
            
            // Wait for Word window
            var wordWindow = "/form[@processname='WINWORD']";
            Host.Local.FindSingle<Form>(wordWindow, 15000);
            
            // Click in document area
            var documentArea = Host.Local.FindSingle<Document>($"{wordWindow}//document");
            documentArea.Click();
            
            // Type document content
            documentArea.PressKeys($"Title: {documentTitle}");
            documentArea.PressKeys("{Return}{Return}");
            documentArea.PressKeys("This is automated content created by Ranorex.");
            documentArea.PressKeys("{Return}");
            documentArea.PressKeys($"Generated on: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
            
            // Format title (select first line and make it bold)
            documentArea.PressKeys("{Ctrl}{Home}");
            documentArea.PressKeys("{Shift}{End}");
            documentArea.PressKeys("{Ctrl}b");
            
            // Move to end
            documentArea.PressKeys("{Ctrl}{End}");
            
            Report.Success("Word Automation", "Successfully created Word document");
        }
        catch (Exception ex)
        {
            Report.Failure("Word Automation", $"Failed: {ex.Message}");
            throw;
        }
    }
}

System Dialogs

Windows System Dialog Automation

using Ranorex;
using System;
using System.IO;

public class SystemDialogAutomation
{
    /// <summary>
    /// Handles Windows File Open dialog
    /// </summary>
    public void HandleFileOpenDialog(string filePath)
    {
        try
        {
            // Wait for File Open dialog
            var openDialog = Host.Local.FindSingle<Form>("/form[@title='Open' or @title='Open File']", 10000);
            
            // Enter file path in filename field
            var filenameField = openDialog.FindSingle<Text>("//text[@name='File name:' or @automationid='1001']");
            filenameField.Click();
            filenameField.PressKeys("{Ctrl}a");
            filenameField.PressKeys(filePath);
            
            // Click Open button
            var openButton = openDialog.FindSingle<Button>("//button[@text='Open' or @text='&Open']");
            openButton.Click();
            
            Report.Success("File Dialog", $"Successfully opened file: {filePath}");
        }
        catch (Exception ex)
        {
            Report.Failure("File Dialog", $"Failed to handle file open dialog: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// Handles Windows File Save dialog
    /// </summary>
    public void HandleFileSaveDialog(string filePath, string fileName)
    {
        try
        {
            // Wait for File Save dialog
            var saveDialog = Host.Local.FindSingle<Form>("/form[@title='Save As' or @title='Save']", 10000);
            
            // Navigate to directory if needed
            var directory = Path.GetDirectoryName(filePath);
            if (!string.IsNullOrEmpty(directory))
            {
                var addressBar = saveDialog.FindSingle<Text>("//text[@name='Address' or @automationid='1000']");
                addressBar.Click();
                addressBar.PressKeys("{Ctrl}a");
                addressBar.PressKeys(directory);
                addressBar.PressKeys("{Return}");
                
                WaitHelper.WaitForCondition(() => !saveDialog.FindSingle<Text>("//text[contains(@text, 'Loading')]", 1000).Exists,
                    "directory navigation to complete");
            }
            
            // Enter filename
            var filenameField = saveDialog.FindSingle<Text>("//text[@name='File name:']");
            filenameField.Click();
            filenameField.PressKeys("{Ctrl}a");
            filenameField.PressKeys(fileName);
            
            // Click Save button
            var saveButton = saveDialog.FindSingle<Button>("//button[@text='Save' or @text='&Save']");
            saveButton.Click();
            
            // Handle overwrite confirmation if it appears
            try
            {
                var confirmDialog = Host.Local.FindSingle<Form>("/form[contains(@title, 'Confirm')]", 3000);
                var yesButton = confirmDialog.FindSingle<Button>("//button[@text='Yes' or @text='&Yes']");
                yesButton.Click();
            }
            catch (RanorexException)
            {
                // No confirmation dialog appeared
            }
            
            Report.Success("File Dialog", $"Successfully saved file: {fileName}");
        }
        catch (Exception ex)
        {
            Report.Failure("File Dialog", $"Failed to handle file save dialog: {ex.Message}");
            throw;
        }
    }
    
    /// <summary>
    /// Handles Windows User Account Control (UAC) dialogs
    /// </summary>
    public bool HandleUacDialog(TimeSpan timeout)
    {
        try
        {
            var endTime = DateTime.Now.Add(timeout);
            
            while (DateTime.Now < endTime)
            {
                // Look for UAC dialog
                if (Host.Local.TryFindSingle("/form[@title='User Account Control']", out Form uacDialog))
                {
                    // Click Yes button
                    var yesButton = uacDialog.FindSingle<Button>("//button[@text='Yes' or @text='&Yes']");
                    yesButton.Click();
                    
                    Report.Success("UAC Dialog", "Successfully handled UAC prompt");
                    return true;
                }
                
                System.Threading.Thread.Sleep(500);
            }
            
            return false;
        }
        catch (Exception ex)
        {
            Report.Warn("UAC Dialog", $"Failed to handle UAC dialog: {ex.Message}");
            return false;
        }
    }
}

Best Practices for Desktop Automation

1. Application Lifecycle Management

  • Always initialize and shutdown Ranorex properly
  • Handle application startup timeouts gracefully
  • Implement proper cleanup in finally blocks

2. Element Identification Strategies

  • Use multiple identification strategies (name, automationid, text)
  • Implement retry logic for element finding
  • Use relative paths when possible for maintainability

3. Timing and Synchronization

  • Use explicit waits instead of fixed delays
  • Wait for application state changes
  • Handle loading indicators and progress bars

4. Error Handling

  • Capture screenshots on failures
  • Implement graceful error recovery
  • Log detailed error information

5. Cross-Framework Compatibility

  • Test automation across different OS versions
  • Handle framework-specific behaviors
  • Use accessibility properties when available

Next Steps

For more advanced desktop automation scenarios:

  1. Advanced Integration - Database and API integration
  2. Code Cookbook - Reusable utility methods
  3. Web Patterns - Browser automation techniques

This desktop automation framework provides the foundation for robust, maintainable automation of Windows desktop applications across all major UI frameworks.