UI automation with accessibility object and win 32 API
In my earlier blogs (Fun with UIAutomation and calc, Fun with UIAutomation and calc 2 (event handling)) I have been talking about how to user uiautomation in .NET. Now it time to dig down bellow uiautomation and go directly to the source WIN 32 API. I have tried to translate the first calculator project to use win 32 API and accessibility object instead. The first thing that we need to-do is to create DllImport statement for user32.dll used for handling more or less all windows UI and oleacc.dll used for accessing the accessibility object. You will find my complete solution in the end of the blog.
An example of DllImport is EnumChildWindows that gives use the possibility to get all child windows to a specific window.
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam); |
How to determine if we are running on 64 or 32 bit?
In my work I need to be able to pick different files depending on if I run in 64 bit or 32 bit system. The first thing I did try was to use the size of IntPtr which is 4 for 32 bit and 8 for 64 bit. But since I was using the visual studio unit test framework it did not work that god. The unit test framework run in a 32 bit process but the application I’m testing is running in 64 bit, so decided to use P/Invoke to call GetNativeSystemInfo.
The structure that you get is according to
[StructLayout(LayoutKind.Sequential)] private struct SystemInfoNative { internal ushort ProcessorArchitecture; internal ushort Reserved; internal uint PageSize; internal IntPtr MinimumApplicationAddress; internal IntPtr MaximumApplicationAddress; internal IntPtr ActiveProcessorMask; internal uint NumberOfProcessors; internal uint ProcessorType; internal uint AllocationGranularity; internal ushort ProcessorLevel; internal ushort ProcessorRevision; } |
Out from this you can check the processor architecture.
And the complete source code
Read more
Fun with UIAutomation and calc 2 (event handling)
Last week, I wrote about UIAutomation and calc. Now it’s time to revisit or little example program and replace the automatic start of calc and replace it a method that will wait for any calc to start and take control over that session. To abstract away the waiting code, I did create a new class called WindowOpenWaiter. Let’s take a look how we can use or new class. The full code to this blog can be found in the bottom.
The first thing we need to-do is to create a new instance of WindowOpenWaiter and give the title to the window that it should look for. Next thing is to call the wait methods with a timeout time in mille seconds so that we will not wait forever. The last thing we do is to get the element that the waiter has found for us and save it in some variable.
AutomationElement calc = null; using (WindowOpenWaiter waiter = new WindowOpenWaiter("Calculator")) { try { waiter.Wait(1000 * 10); } catch (TimeoutException e) { Console.WriteLine(e.Message); return; } calc = waiter.Element; } |
Fun with UIAutomation and calc
Last night I got a cool idea how to show some basic ideas of UIAutomation. I did decide to-do a UIAutomation example with calc as my test subject. The idea is to give a mathematical expression to the program, which will then use calc to solve the problem and show you the result. The main thing is to show how UIAutomation is working and not to calculate.
The first thing that we need is a method that can find an AutomationElement for us. An AutomationElement is anything that you can see on the screen as buttons and input fields. The method take a root AutomationElement that define where to start to search, a name and a class name for the object that we are looking for. We then create two property conditions based on the name and class name. These two conditions are then combined in an AndCondition. We also have OrCondition and NotCondition to use. The last thing we do is to search for the first AutomationElement that match the condition.
private static AutomationElement GetElement(AutomationElement root, string name, string className) { PropertyCondition condName = new PropertyCondition(AutomationElement.NameProperty, name); PropertyCondition condClassName = new PropertyCondition(AutomationElement.ClassNameProperty, className); AndCondition cond = new AndCondition(condName, condClassName); return root.FindFirst(TreeScope.Descendants, cond); } |
Next method that we need is one that can return a invoke pattern. Invoke pattern is used for example to click on a button. This will be used to be able to click on the different buttons in calc
private static InvokePattern GetInvokePattern(AutomationElement element) { return element.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern; } |
Zipping files in C#
For a while ago I tried to create zip files to be used by windows built in zip functionality. Did try to use GZipStream but I found out that I needed a GZip expander installed to be able to use it. I don’t understand why we have an inbuilt function in VS to create a compressed format that Windows can’t open natively. Anyway after a lot of search I found DotNetZip. It has a really easy to use API and no dependencies to other DLL’s.
It supports the following scenarios
-creating a zip archive, adding files or directories into the archive
-extracting files from an existing archive
-modifying an existing archive – removing entries from an archive or adding new entries to an archive
-password protecting entries in the archive
-getting entry input from a file or a stream
-reading a zip file from a file or a stream
-extracting an entry into a file or a stream
Example Usage
try { using (ZipFile zip = new ZipFile("MyZipFile.zip") { zip.AddFile("c:\photos\personal\7440-N49th.png"); zip.AddFile("c:\Desktop\2005_Annual_Report.pdf"); zip.AddFile("ReadMe.txt"); zip.Save(); } } catch (System.Exception ex1) { System.Console.Error.WriteLine("exception: " + ex1); } |
Working with GAC from code
For some day ago I was working on testing an installer that did add some files to the GAC during installation. To ensure that the installer was working correct I needed some code that could check if some files did exist, add files and remove files from the GAC. I did need this for mainly 3 test cases; files already exist in GAC, files are installed correct and repair scenario when some files are deleted. I did find a good API wrapper on Junfena Zhana’s blog that did wrap the needed COM interactions. Based on this wrapper I did create a small class that did solve my problem.
Install assembly example
public static void InstallAssembly(string assemblyPath) { if (String.IsNullOrEmpty(assemblyPath)) { throw new ArgumentNullException("assemblyPath"); } AssemblyCache.InstallAssembly(assemblyPath, null, AssemblyCommitFlags.Force); } |
Working with windows firewall from code
Quite recently I did some automation for an installation program that was changing the firewalls rules. So to be able to test that the installation was working as expected I need a way to access information about the firewall from code and also a way to change the information to verify that the installer was able to repair the rules. I found a lot of smaller code snippets mostly in VB on the net with random quality. Since I’m big fan of C# I did decide to create my one snippet’s in C#
The basic functionality is found in the COM object hnetcfg.dll. So the first thing to-do is to get a firewall manager.
private const string CLSIDFireWallManager = "{304CE942-6E39-40D8-943A-B913C40C9CD4}"; private static NetFwTypeLib.INetFwMgr GetFirewallManager() { Type objectType = Type.GetTypeFromCLSID(new Guid(CLSIDFireWallManager)); INetFwMgr manager = Activator.CreateInstance(objectType) as NetFwTypeLib.INetFwMgr; if (manager == null) { throw new NotSupportedException("Could not load firewall manager"); } return manager; } |
Getting the current profile The profile is used for all firewall rules interactions.
private static INetFwProfile GetCurrentProfile() { INetFwProfile profile; try { profile = GetFirewallManager().LocalPolicy.CurrentProfile; } catch (System.Runtime.InteropServices.COMException e) { throw new NotSupportedException("Could not get the current profile (COMException)", e); } catch (System.Runtime.InteropServices.InvalidComObjectException e) { throw new NotSupportedException("Could not get the current profile (InvalidComObjectException)", e); } return profile; } |
Is firewall on or of?
public static bool IsWindowsFirewallOn { get { return GetCurrentProfile().FirewallEnabled; } set { GetCurrentProfile().FirewallEnabled = value; } } |