How to load assembly [Systems.Collections.Specialized.StringCollection] into Powershell - 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;

Related

How to create a MSI extractor with Powershell? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 months ago.
Improve this question
A few months back I made a simple MSI extractor script, I improved it some today by adding a popup dialog using WScript.Shell, however I also wanted to improve on the first part of the script, the extractor. Originaly users would have to go in and manually edit the input file (.msi) and the output directory. I used the old friend google to see what the file popup syntax was. Tried to encorperate it into the script, with no luck.
Current code:
msiexec /n /a $FileBrowser /qb TARGETDIR=$Out # This uses the built in Windows tool to extract the MSI
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{
InitialDirectory = [Environment]::GetFolderPath('Desktop')
Filter = 'Windows Packages (*.msi)|*.msi'
}
$Out = $FileBrowser.ShowDialog()
$Shell = New-Object -ComObject "WScript.Shell"
$Button = $Shell.Popup("Once you install the MSI using this PowerShell script, please add any programs that run from a shell (i.e. CMD, PowerShell) be added to Path.
To add a program to path, search for Control Panel in Windows Search, and open it. Once in Control Panel,
select User Accounts, then User Accounts again. On the side bar, select Change my Enviorment Variables.
Select the Path variable, and then Edit. Select a unfilled box, and type the path to the program (for most, it can be just the root folder, some may need to be bin) and then Ok, and Ok again.
You WILL need to restart any open shells.", 0, "Thank you for using MSI-Extractor", 0)
Any help is greatly apprecitaed,
James
I tried adding vars, with no luck.
EDIT: I got some outside help, and I somewhat fixed it.
#Use Windows Forms to open a file select dialog
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{
InitialDirectory = [Environment]::GetFolderPath('Desktop')
Filter = 'Windows Packages (*.msi)|*.msi'
}
$Out = $FileBrowser.ShowDialog() #Display the dialog
#Select output directory
$FolderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog -Property #{
Description = 'Output'
}
$Out = $FolderBrowser.ShowDialog() #Display the dialog
$FolderBrowser.SelectedPath #Variable stuff
msiexec /a $FileBrowser.FileName /qb TARGETDIR=$($FolderBrowser.SelectedPath) # This uses the built in Windows tool to extract the MSI
#A helpful message
$Shell = New-Object -ComObject "WScript.Shell"
$Button = $Shell.Popup("Once you install the MSI using this PowerShell script, please add any programs that run from a shell (i.e. CMD, PowerShell) be added to Path.
To add a program to path, search for Control Panel in Windows Search, and open it. Once in Control Panel,
select User Accounts, then User Accounts again. On the side bar, select Change my Enviorment Variables.
Select the Path variable, and then Edit. Select a unfilled box, and type the path to the program (for most, it can be just the root folder, some may need to be bin) and then Ok, and Ok again.
You WILL need to restart any open shells.", 0, "Thank you for using MSI-Extractor", 0)
Running this in Windows Powershell ISE works, but in Powershell directly it errors out
New-Object : Cannot find type [System.Windows.Forms.OpenFileDialog]: verify that the assembly containing this type is
loaded.
At C:\Users\693982\Downloads\MSI-extractor.ps1:4 char:16
+ ... leBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
You cannot call a method on a null-valued expression.
At C:\Users\693982\Downloads\MSI-extractor.ps1:9 char:1
+ $Out = $FileBrowser.ShowDialog() #Display the dialog
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
New-Object : Cannot find type [System.Windows.Forms.FolderBrowserDialog]: verify that the assembly containing this
type is loaded.
At C:\Users\693982\Downloads\MSI-extractor.ps1:13 char:18
+ ... erBrowser = New-Object System.Windows.Forms.FolderBrowserDialog -Prop ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
You cannot call a method on a null-valued expression.
At C:\Users\693982\Downloads\MSI-extractor.ps1:17 char:1
+ $Out = $FolderBrowser.ShowDialog() #Display the dialog
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Running this in Windows Powershell ISE works, but in Powershell directly it errors out
The reason this works in the ISE is that the ISE will autoload needed libraries/modules. The Powershell consoles (powershell.exe/pwsh.exe) will not.
You need to put stuff like this at the top of your code for the shells to load for use in GUI-based scripts.
Add-Type -AssemblyName System.Drawing,
PresentationCore,
PresentationFramework,
System.Windows.Forms,
microsoft.VisualBasic
[System.Windows.Forms.Application]::EnableVisualStyles()
Point of Note:
You don't need all of those for what you are doing, but I put them here for your awareness/research of them. Sure, you can still use this old COM...
New-Object -ComObject "WScript.Shell"
... for popups; however, you should be using the modern .Net namespaces above.
Example details:
[enum]::GetNames([System.Windows.MessageBoxImage])
# Results
<#
None
Hand
Error
Stop
Question
Exclamation
Warning
Asterisk
Information
#>
[System.Windows.MessageBox]::Show('Do you want to proceed?', 'Confirm', 'YesNoCancel','Error')
Example message boxes using .Net.
[System.Windows.MessageBox]::Show(
"
General Info
`n Some other Info
`n Username
`n Password,
", 'Dialog Title'
)
[System.Windows.Forms.MessageBox]::Show(
"
General Info
`n Some other Info
`n Username
`n Password
", 'Dialog Title'
)
Here's a refactor of your popup message for readability.
[System.Windows.MessageBox]::Show(
"1. Once you install the MSI using this PowerShell script,
please add any programs that run from a shell (i.e. CMD, PowerShell)
be added to Path.
`n2. To add a program to path,
`t->search for Control Panel in Windows Search, and open it.
`n3. Once in Control Panel,
`t->select User Accounts,
`t->then User Accounts again.
`n4. On the side bar, select Change my Enviorment Variables.
`tSelect the Path variable, and then Edit.
`t`Select a unfilled box, and type the path to the program
`t->for most, it can be just the root folder,
`t->some may need to be bin
`t->and then Ok, and Ok again.
`n5. You WILL need to restart any open shells.", "Thank you for using MSI-Extractor"
)

Merge PowerPoint files Using PowerShell and PowerPoint application object

I need a simple PowerShell script to merge n number of pptx files into a single pptx file. Seems like MergeWithBaseline off the presentation object should work, but I continue to get an error.
Add-type -AssemblyName office
add-type -assembly microsoft.office.interop.powerpoint
$strwithPresentation = ".\one.pptx"
$strbaselinePresentation = ".\two.pptx"
$Application = New-Object -ComObject powerpoint.application
$presentation = $application.Presentations.add()
$presentation.MergeWithBaseline($strwithPresentation, $presentation.FullName)
I have tried many options but continue to get this error:
PS C:\Users\ltsharpe\Documents\scripts> C:\Users\ltsharpe\Documents\scripts\MergePPTX.ps1
Error HRESULT E_FAIL has been returned from a call to a COM component.
At C:\Users\ltsharpe\Documents\scripts\MergePPTX.ps1:10 char:1
$presentation.MergeWithBaseline($strwithPresentation, $presentation.F ...
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException
What is the best way to programmatically merge pptx files into a single one? Few hits on searches and most are 10+ years old. Any suggestions are greatly appreciated as I do not want to cut/paste 20+ pptx files into one on a monthly basis :(.

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

How can I use the unmanaged UI Automation API from 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

reference assembly in Powershell script

In Visual Studio I'm able to simply add a reference to an assembly, for example: Interop.ADODB.
How do I do this in powershell? The following is exploding
[Reflection.Assembly]::LoadWithPartialName("Interop.ADODB")
$conn = New-Object ADODB.Connection
And here is the error message
New-Object : Constructor not found. Cannot find an appropriate constructor for type ADODB.Connection.
At C:\Users\michaelr\Desktop\jet-project\PS\test.ps1:3 char:19
+ $conn = New-Object <<<< ADODB.Connection
+ CategoryInfo : ObjectNotFound: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : CannotFindAppropriateCtor,Microsoft.PowerShell.Commands.NewObjectCommand
I'm on Windows 7 x64.
Use this line instead:
$conn = New-Object -comobject ADODB.Connection
Powershell understand com object natively (well to the extent it's possible for objects that don't always have a lot of metadata) and does all the interop for you. You just need to tell powershell that it's a COM object. You don't need to reference the interop assemblies explicitly.
Having said that, depending on your task you might be better off using a native .net provider instead of ADODB.
You're looking for the Add-Type cmdlet, which lets you either compile .NET code into an in-memory assembly, or load an assembly from disk.
Check out the -AssemblyName parameter if you want to load an assembly from the GAC, or the -Path parameter if you know exactly where the DLL you want to load is.
The answer's right in front of you:
Cannot find an appropriate constructor for type ADODB.Connection
You need to pass the connection string to the constructor like this:
$c = new-object ADODB.connection "server=foo;user id=bar ..."
However, I don't understand why you want to use 1998-era ADODB in .NET when you can use System.Data instead. Check out this MSDN article on accessing databases with PowerShell:
http://technet.microsoft.com/en-us/magazine/hh855069.aspx