UIA question (extremely bizarre lol)

Joined
Jul 20, 2020
Messages
11
Programming Experience
5-10
Hey there,

I've been working on a project that automates the "Save As" dialog as part of an automated download process, and I thought it would be best to use UIA (a.k.a. "Microsoft UI Automation", in the System.Windows.Automation namespace) as opposed to simulating keystrokes AutoHotKey-style. I had done it that way before (in C/Win32) and it worked, but I thought UIA might be more reliable; at the same time, the requirements changed so I'm using C# now (lol). Anyway, I've been fighting hard against a bizarre bug that has no logical explanation (based on my little mortal brain's limited understanding of the inner workings of .NET, lol). My program works perfectly when run from Visual Studio (VS 2017, on Windows 10), and works well part of the way when run from PowerShell/CMD. But when not using VS, the program crashes with no stack trace. Debug vs. release mode doesn't seem to matter., but it only works (in both modes) in Visual Studio. When it crashes (in either mode), there is no "the program has stopped working", no text is printed to stdout or stderr, and there is absolutely no indication whatsoever as to what causes the problem. Try/catch blocks don't catch it, piping the output to a file doesn't show any new info, and I haven't been able to dig up any usable info on the interwebz.

But the random doesn't stop there. When I run it from PowerShell, then bring up the Save As dialog in Notepad, it not only crashes my program, but also PowerShell and Notepad! Returning to Visual Studio, if I try to re-compile my code, I get an error:

Severity: Error
Description: Unable to copy file "obj\Debug\download.exe" to "bin\Debug\download.exe". Access to the path 'bin\Debug\download.exe' is denied.
Project: download
All other fields are blank/empty.

Here's the code, with all my comments as I've tried different ways out of this nightmare:
Makes me love Linux that much more lol:
/// <summary>
    /// Automates the "Save As" dialog
    /// </summary>
    /// <param name="path">The path</param>
    /// <param name="file">The file name</param>
    static void AutomateSaveAsDialog(string path, string file)
    {
        // Wait for the "Save As" dialog to appear
        AutomationElement start;
        while (true)
        {
            start = AutomationElement.FocusedElement;
            if (start.Current.Name.Equals("File name:")) break;
            Thread.Sleep(100);
        }

        // Set the file name
        ValuePattern textBox = start.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
        if (textBox == null)
        {
            Console.WriteLine("Error: Can't set the name");
            return;
        }
        textBox.SetValue(path + "\\" + file);
        
        // Get a reference to the dialog.
        // "Luke TreeWalker" lol
        TreeWalker luke = TreeWalker.ControlViewWalker;
        AutomationElement dialog = luke.GetParent(luke.GetParent(luke.GetParent(start)));
        
        // Use that to find the "Save" button
        Condition[] conditions = {
            new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button),
            new PropertyCondition(AutomationElement.NameProperty, "Save")
        };

        // Using this "cave man debugging" (as one Stack Overflow post cleverly put it)
        // I found that the cause of the crashing is something somewhere after this line:
        Console.WriteLine("Conditions set");

        AutomationElement button = dialog.FindFirst(TreeScope.Descendants, new AndCondition(conditions));

        // And this line doesn't run, so I think it IS line 123... but why?
        Console.WriteLine("It's not line 123");
        
        /*
        // Gonna try commenting out this block - maybe something in here causes it??????
        if (button == null)
        {
            Console.WriteLine("Error: couldn't find the save button");
            return;
        }
        */

        Console.WriteLine("button is not null");
        
        // Push the button
        InvokePattern save = button.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
        Console.WriteLine("It's not line 138");

        /*
        // We know it won't be null, so Lord willing we can comment this out safely.
        if (save == null)
        {
            Console.WriteLine("Error: Can't set the name");
            Console.ReadKey();
            return;
        }
        */
        save.Invoke();
        Console.WriteLine("It's not line 151");
        
        // Check to make sure it worked
        Thread.Sleep(1000);  // just to play it safe
        if (!FileExistsAnyExtension(path, file))
            Console.WriteLine("Error: File not saved");
    }

Any info, ideas, questions, suggestions, or other thoughts on the subject would be greatly appreciated. For now, I can go back to simulating keystrokes and get this project done; but UIA is something I may need to use again, so if this is a super-common recurring problem or something I'll eventually need to understand it. My best guess is that Visual Studio jumps through some unknown hoops, dynamically inserting some mysterious missing component that doesn't get packaged with the EXEs... I'm 99.99999% sure it's not my code, cuz my code works in Visual Studio. lol idk.
 
Right after your line 39, make sure that dialog is not null, by adding something like:
C#:
if (dialog == null)
    Console.WriteLine("dialog not found.");
else
    Console.WriteLine("searching dialog to find the Save button");
 
But the random doesn't stop there. When I run it from PowerShell, then bring up the Save As dialog in Notepad, it not only crashes my program, but also PowerShell and Notepad! Returning to Visual Studio, if I try to re-compile my code, I get an error:
That's usually and indication that the program is still running... or that the file is currently locked by your antivirus software or some other nanny ware.

Make sure that you are stopping/terminating the app.

Try disabling your antivirus and other nannyware when running your program.
 
Hey Skydiver,
  • I had a null-check like yours in place and deleted it sometime yesterday. Sorry I forgot to add it here (lol). Re-adding it didn't really make a difference. Good guess tho! :)
  • I thought of what you said too, about the program still being running. Every obscure Stack Overflow question I found about the error suggested that too, but it wasn't showing up in Task Manager.
  • This leaves your third idea, and I suspect this is the culprit; it's the only thing that makes any sense. We use BitDefender, and this wouldn't be the first time it's jabbed its monkey-wrenches into my work. Unfortunately devs are not admins here, and I'm not sure they'd be cool with disabling our "nannyware" (btw love that expression - never heard that one before) because they want the finished software to run on a server. So I guess I'm stuck with Win32/Pinvoke, and that's okay. At least these insane glitches finally have an explanation.
Anyway, thanks for the quick reply on this. Have an awesome day! :)

PS: I also got a kick out of that quote in your signature, about destroying planets. :D
 
Back
Top Bottom