How can I use the unmanaged UI Automation API from PowerShell - powershell

The UI Automation API for Windows is available from two DLLs.
One is a managed DLL, which is C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\UIAutomationClient.dll.
The other is an unmanaged DLL, which is C:\Windows\System32\UIAutomationCore.dll.
According to this post, the unmanaged API is superior to the managed API in terms of the number of visible elements, so I would like to use the unmanaged API.
I have tried three approaches, but all of them failed.
Would you tell me the correct approach?
Approach #1: New-Object -ComObject
$uia = New-Object -ComObject <ProgID of CUIAutomation>
$root = $uia.GetRootElement()
Failed because New-Object requires ProgID but CUIAutomation does not have ProgID.
Approach #2: Instantiation from CLSID
The CLSID of CUIAutomation is ff48dba4-60ef-4201-aa87-54103eef594e, then,
$type = [Type]::GetTypeFromCLSID("ff48dba4-60ef-4201-aa87-54103eef594e")
$uia = [Activator]::CreateInstance($type)
$root = $uia.GetRootElement()
but failed with the following error message.
I still do not know why.
Method invocation failed because [System.__ComObject] does not contain a method named 'GetRootElement'.
At line:1 char:1
+ $root = $uia.GetRootElement()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Approach #3: Add-Type
Add-Type -Path "C:\Windows\System32\UIAutomationCore.dll"
$uia = New-Object UIAutomationClient.CUIAutomation
$root = $uia.GetRootElement()
Failed because Add-Type expects managed DLLs.
Error message:
Add-Type : Could not load file or assembly 'file:///C:\Windows\System32\UIAutomationCore.dll' or one of its dependencies. The module was expected to contain an assembly manifest. At line:1 char:1
+ Add-Type -Path "C:\Windows\System32\UIAutomationCore.dll"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Add-Type], BadImageFormatException
+ FullyQualifiedErrorId : System.BadImageFormatException,Microsoft.PowerShell.Commands.AddTypeCommand
Edit (2018-06-12)
I tried another approach. (and failed)
Approach #4: Interop DLL
I do not really understand what exactly the Interop DLL is, but this post says the Interop DLL helped OP anyway.
I installed Visual Studio and generated Interop.UIAutomationClient.dll by following the procedures of the post.
Add-Type -Path "Interop.UIAutomationClient.dll"
$uia = New-Object UIAutomationClient.CUIAutomationClass
$root = $uia.GetRootElement()
$children = $root.FindAll([UIAutomationClient.TreeScope]::TreeScope_Children, $uia.CreateTrueCondition())
I succeeded in obtaining $root, but failed at the line of $children with the following error message.
Method invocation failed because [System.__ComObject] does not contain a method named 'FindAll'.
At line:1 char:1
+ $children = $root.FindAll([UIAutomationClient.TreeScope]::TreeScope_C ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
I still do not know why.

What about this?:
Add-Type -AssemblyName 'UIAutomationClient'
$ae = [System.Windows.Automation.AutomationElement]
$cTrue = [System.Windows.Automation.PropertyCondition]::TrueCondition
$root = $ae::RootElement
$winNames = $root.FindAll("Children", $cTrue).current.name

I have not yet resolved the problem, but finally found an alternative, that is C# Interactive.
I will leave this question for PowerShell users, but, if you can use C# Interactive as an alternative of PowerShell, the following section may help you.
Approach #5: C# Interactive
Install Visual Studio.
Generate Interop.UIAutomationClient.dll by following the procedures of this post.
Run the following script on csi.exe.
#r "Interop.UIAutomationClient.dll"
var uia = new UIAutomationClient.CUIAutomation();
var root = uia.GetRootElement();
var children = root.FindAll(UIAutomationClient.TreeScope.TreeScope_Children, uia.CreateTrueCondition());
FYI, C# Interactive works if only the following files exist in the same folder (i.e., you can use C# Interactive anywhere just by bringing the following files from the development environment).
C:\Program Files (x86)\MSBuild\14.0\Bin\csi.exe
C:\Program Files (x86)\MSBuild\14.0\Bin\csi.rsp
C:\Program Files (x86)\MSBuild\14.0\Bin\Microsoft.CodeAnalysis.CSharp.dll
C:\Program Files (x86)\MSBuild\14.0\Bin\Microsoft.CodeAnalysis.CSharp.Scripting.dll
C:\Program Files (x86)\MSBuild\14.0\Bin\Microsoft.CodeAnalysis.dll
C:\Program Files (x86)\MSBuild\14.0\Bin\Microsoft.CodeAnalysis.Scripting.dll
C:\Program Files (x86)\MSBuild\14.0\Bin\System.AppContext.dll
C:\Program Files (x86)\MSBuild\14.0\Bin\System.Collections.Immutable.dll
C:\Program Files (x86)\MSBuild\14.0\Bin\System.Diagnostics.StackTrace.dll
C:\Program Files (x86)\MSBuild\14.0\Bin\System.IO.FileSystem.dll
C:\Program Files (x86)\MSBuild\14.0\Bin\System.Reflection.Metadata.dll

Approach 2 you should get to the interface of
IID_IUIAutomation = "{30CBE57D-D9D0-452A-AB13-7AC5AC4825EE}"
CLSID_UIAutomationClient = "{944DE083-8FB8-45CF-BCB7-C477ACB2F897}"
;CoClasses
CLSID_CUIAutomation = "{FF48DBA4-60EF-4201-AA87-54103EEF594E}"
MS Doc states
Remarks
Every UI Automation client application must obtain this interface to a CUIAutomation object in order to gain access to the functionality of UI Automation.
The following example function creates a CUIAutomation object and obtains the
IUIAutomation interface.
IUIAutomation *g_pAutomation;
BOOL InitializeUIAutomation()
{
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IUIAutomation), (void**)&g_pAutomation);
return (SUCCEEDED(hr));
}
I was not able to get it working in PS but maybe this answers helps partly in the right direction (I have it working in AutoIt but that works differently, you can find it with AutoIt IUIAutomation on google)
$objCUI=[System.Runtime.InteropServices.Marshal]::GetTypeFromCLSID("30CBE57D-D9D0-452A-AB13-7AC5AC4825EE")
or
$Type = [Type]::GetTypeFromCLSID('30CBE57D-D9D0-452A-AB13-7AC5AC4825EE')
$objCUI = [System.Activator]::CreateInstance($Type)
both run but when I come to
$rootEl = $objCUI.GetType().InvokeMember(
"GetRootElement",
"InvokeMethod",
$Null,
$objCUI,
#()
)
I get errors

Related

Unable to find type System.Web.MimeMapping error - Powershell on Azure DevOps Pipeline

I'm trying to run a Powershell script as a part of my build pipeline on Azure DevOps, but I keep getting the following error:
Unable to find type [System.Web.MimeMapping].
At D:\a\1\s\upload.ps1:31 char:15
+ $sourceMime = [System.Web.MimeMapping]::GetMimeMapping($sourceItem.Fu …
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Web.MimeMapping:TypeName) [], ParentContainsErrorRecordException
FullyQualifiedErrorId : TypeNotFound
The related bit of code in my Powershell script that's throwing the error:
# Get the source file contents and details, encode in base64
$sourceItem = Get-Item $sourceFile
$sourceBase64 = [Convert]::ToBase64String([IO.File]::ReadAllBytes($sourceItem.FullName))
$sourceMime = [System.Web.MimeMapping]::GetMimeMapping($sourceItem.FullName)
Any Pipelines gurus out there that can help?
Note: I've already tried using Add-Type -AssemblyName System.Web to no avail. This seems to be something related to the hosted agent (windows-latest) since the script runs fine on my local machine.
Thanks in advance.

Issue with QR Decoding using Zxing.Net in Powershell - Cannot find an overload for "Decode" and the argument count: "1"

I am trying to decode an QR Code in PowerShell with using Zxing.net (https://github.com/micjahn/ZXing.Net)
There is a HowTo at this page but I am not able to use it that way:
https://github.com/micjahn/ZXing.Net/wiki/Using-ZXing.Net-with-Powershell
Everytime I run the script I receive the following error message:
Cannot find an overload for "Decode" and the argument count: "1".
At C:\Users\ww\Desktop\reader.ps1:13 char:1
+ $result = $reader.Decode($bitmap)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
My whole script is:
Add-Type -Path "C:\Users\ww\Desktop\zxing.net\lib\net45\zxing.dll"
$reader = New-Object -TypeName ZXing.BarcodeReader
$reader.Options.TryHarder=1
# set TryHarder option to true, other options can be set the same way
$bitmap = [System.Drawing.Bitmap]::FromFile("C:\Users\ww\Desktop\abc.bmp")
$result = $reader.Decode($bitmap)
$bitmap.Dispose()
$result.Text
I only know "overload" from Java, where I can use different parameters for the same function, but in this case it makes no sense to me (I am not a very experienced programmer).
Is it possible that I am using the wrong .dll? I downloaded the Nuget-Package from https://www.nuget.org/packages/ZXing.Net/0.16.5, then extracted it via 7-Zip. I have tried various .dll (net40, netcoreapp3.0, portable, net20,...) but the result was always the same.
I also found this Github thread, where it seems rather easy to get everything working.
Could anyone give me a hint what is wrong with my implementation?
Thank you!
The script as-is should work fine. Make sure you are using the .dll for the appropriate version of .NET installed on your system. For example, I have .NET version 4.8, so I used the .dll for the highest version available, 4.7. My file-path is C:\...\ZXing.Net.0.16.5.0\net4.7\zxing.dll .
Add-Type -Path "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Zxing\zxing.dll"
$reader = New-Object -TypeName ZXing.BarcodeReader
$reader.Options.TryHarder = 1 # set TryHarder option to true, other options can be set the same way
$bitmap = [System.Drawing.Bitmap]::FromFile("C:\Users\testUser\Pictures\QR.png")
$result = $reader.Decode($bitmap)
$bitmap.Dispose()
$result.Text
Console Output: https://www.youtube.com/watch?v=oHg5SJYRHA0

Why can't I instance a chromeoptions object with selenium in powershell?

I'm trying to use Powershell v5.1 on a Windows 10 machine to automate chrome tasks with Selenium chromedriver, and I'm having trouble creating a ChromeOptions object in PS after importing all the relevant dlls. Every source I've found says the correct object instantiation is done thus:
ChromeOptions options = new Chromeoptions();
But that results in this error:
At line:1 char:43
+ chromeoptions options = new chromeoptions();
+ ~
An expression was expected after '('.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ExpectedExpression
This is after I've imported these:
add-type -path "D:\\selenium\selenium.webdriverbackedselenium.dll"
add-type -path "D:\\selenium\thoughtworks.selenium.core.dll"
add-type -path "D:\\selenium\webdriver.support.dll"
add-type -path "D:\\selenium\webdriver.support.dll"
Am I missing any dlls required to instantiate chromeoptions objects? I can open chrome fine with chromedriver and navigate normally, but I need to configure some things before it opens or it's essentially useless to me. Thanks for any and all help!
I literally started playing with Selenium today because i want to try and scrape dynamic loaded data (javascript). Anyway
You are copying C# code and expecting it to work. You need to convert it to PS code (or run is as C# code in PS)
ChromeOptions options = new Chromeoptions();
becomes
$chromeOptions = New-Object OpenQA.Selenium.Chrome.ChromeOptions
$chromeOptions now has methods and properties that you can access, such as AddArgument for instance.
Once you created your ChromeOptions-object, you need to add it to the driver.
$driver = New-Object OpenQA.Selenium.Chrome.ChromeDriver($chromeOptions)

How to load assembly [Systems.Collections.Specialized.StringCollection] into Powershell

I semi understand what I'm asking and have tried to research this as best as I'm able.
I'm trying to use an object of type [Systems.Collections.Specialized.StringCollection]:
$newDBFiles = new-object Systems.Collections.Specialized.StringCollection;
I get the error:
new-object : Cannot find type [Systems.Collections.Specialized.StringCollection]: make sure the assembly containing this type is loaded.
At line:1 char:15
+ $newDBFiles = new-object Systems.Collections.Specialized.StringCollection;
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
I've tried loading the assembly a few different ways without success:
[System.Reflection.Assembly]::LoadWithPartialName("System.Collections.Specialized") | Out-Null;
add-type -AssemblyName systems;
add-type -AssemblyName systems.collections;
The documentation says that Add-Type will accept a location to the .dll. Looking at the documentation for the StringCollection Class the .dll I want is System.dll. That sounds like it should already be present on my system.
When I look at the assemblies already loaded, I see this one, which appears to me be correct:
[appdomain]::currentdomain.getassemblies() | sort -property fullname | format-table fullname
System.Collections, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
There's numerous posts about using the type but I couldn't find any on how to load the specific assembly. Do I need a special .dll file?
To expand on the comment from #PetSerAl: You have an s after System that shouldn't be there. Do this instead
$newDBFiles = new-object System.Collections.Specialized.StringCollection;

extracting zip files with powershell

I am trying to extract a zip file to then be downloaded by remote people. Once extracted I will have a script that installs the package but I need to use a compressed folder and the remote users don't have 7zip or anything like it.
I have this but I keep getting an error -
$shell = new-object -com shell.application
$zip = $shell.NameSpace(“C:\name.zip”)
foreach($item in $zip.items())
{
$shell.Namespace(“C:\temp\name”).copyhere($item)
}
The error I am getting is below -
You cannot call a method on a null-valued expression.
At line:5 char:43
+ $shell.Namespace(“C:\name”).copyhere <<<< ($item)
+ CategoryInfo : InvalidOperation: (copyhere:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
There are other issues with the code but this is a work in progress, I will fix the hard coding of values once I can get it to extract.
Does your extract path exist (“C:\temp\name”)? To be clear, both of the items in that path should be directories.
That should be a path that already exists. If it doesn't you get that error.