powershell com object windows installer - powershell

I would like to leverage the following
$wi=new-object -com WindowsInstaller.Installer
If I do a $wi |gm I do not see the method I want "Products".
I would like to iterate Products and show a list of all items installed on the system.
So I thought... let me do a $wi.gettype().invokemember
Not really sure what to do $wi.gettype().invokemember("Products","InvokeMethod")
or something yields cannot find an overload...
But now I am lost. I have looked elsewhere but I don't want to create a whole XML file. I should be able to access the com objects methods.

If you are trying to get a list of installed programs in Windows, there is a native Powershell way, which is actually using WMI behind the scenes:
Get-WmiObject Win32_Product
Here's a related article from Microsoft Scripting Guys.
It appears that this approach has some issues, so better be avoided.
When you query this class, the way the provider works is that it
actually performs a Windows Installer “reconfiguration” on every MSI
package on the system as its performing the query!
I tried my best to find a solution that involves WindowsInstaller com object, but all of them point to an article that no longer exists. Here is one on stackoverflow.
An alternative solution is to give a try to psmsi on CodePlex.

Here my code-snippet for this:
cls
$msi = New-Object -ComObject WindowsInstaller.Installer
$prodList = foreach ($p in $msi.Products()) {
try {
$name = $msi.ProductInfo($p, 'ProductName')
$ver = $msi.ProductInfo($p, 'VersionString')
$guid = $p
[tuple]::Create($name, $ver, $guid)
} catch {}
}
$prodlist

Related

creating comobject for AccessObjects

I am trying to create a powershell 'AccessObject' comobject for my MS Access app. Basically, I will trying to create a powershell script that gets queries in a database and the tables and/or queries a particular query depends on. To do that i will need to have an instance of the MS Access 'AccessObject' and 'DependencyInfo' classes in my powershell script. I have attached a snippet of the function i intend to use. This is not the complete function, please note. All i want is to know how to create an instance of the DependencyInfo and AccessObjects in powershell.
function getQueries([string] $database)
{
$dbEng = New-Object -ComObject DAO.DBEngine.120
$AccessApp= New-Object -ComObject Access.Application
$Dependency = $AccessApp.DependencyInfo
$AccessObject=$AccessApp.AccessObjects
...
}
All i want is to know how to create an instance of the DependencyInfo
and AccessObjects in powershell.
The following creates a new Access process, opens a local accdb file, and retrieves the dependencies for a given form:
$db = new-object -ComObject 'Access.Application'
$db.OpenCurrentDatabase('C:\temp\deezNutz.accdb')
$dependency_info = $db.Application.CurrentProject().AllForms('frm_person').GetDependencyInfo()
foreach ($dependency in $dependency_info.Dependencies) { $dependency.FullName }
$db.CloseCurrentDatabase()
$db.Application.DoCmd.Quit()
If you're trying to pro grammatically manipulate the objects in a Microsoft Access database e.g., forms, reports, queries, etc. Your best bet is to search for solutions using VBA then convert those to Powershell. For this example, I first wrote the solution in VBA then converted it to Powershell.
Thanks #Lord Adam. This was really helpful. In my case i had to modify the logic a little bit:
$AccessApp= New-Object -ComObject 'Access.Application'
$AccessApp.OpenCurrentDatabase($database)
$AccessApp.Application.SetOption("Track Name AutoCorrect Info", $true)
$QryDependency = $AccessApp.Application.CurrentData.AllQueries.Item($query.Name).GetDependencyInfo()
ForEach($di in $QryDependency.Dependencies)
{
$QryObjects= $QryObjects + $di.Name +","
}

Connect to TFS via Powershell with different user credentials

I've written a Powershell script which connects to our TFS server, creates a workspace, downloads the latest source and performs a nightly build and release.
The issue I have is that it always connects with my own credentials, and from what I've read this is because I'm logged onto the machine as me. I've had a new domain user account created and we've given this admin permissions within TFS, however I'm having trouble making the script use these credentials.
Here's the part of the script which deals with the initial connection and workspace creation as it currently stands:
$subfolder = [System.Guid]::NewGuid().ToString()
$tfsServer = "http://tfsserver:8080/tfs/xyz"
$tfsCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($tfsServer)
$tfsVersionCtrlType = [Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]
$tfsVersionCtrl = $tfsCollection.GetService([type] $tfsVersionCtrlType)
$tfsWorkspace = $tfsVersionCtrl.CreateWorkspace($subfolder, $tfsVersionCtrl.AuthenticatedUser)
For completeness, here's the rest of the "Get" logic:
$tfsWorkspace.Map($serverLocation, $localLocation)
$recursion = [Microsoft.TeamFoundation.VersionControl.Client.RecursionType]::Full
$versionSpec = [Microsoft.TeamFoundation.VersionControl.Client.VersionSpec]::Latest
$itemSpec = New-Object Microsoft.TeamFoundation.VersionControl.Client.ItemSpec($serverLocation, $recursion)
$fileRequest = New-Object Microsoft.TeamFoundation.VersionControl.Client.GetRequest($itemSpec, $versionSpec)
$getStatus = $tfsWorkspace.Get($fileRequest, [Microsoft.TeamFoundation.VersionControl.Client.GetOptions]::Overwrite)
The main problem I have when working with TFS via Powershell is the fact Microsoft keep changing their implementation, in some cases quite radically, and what we're left with is scraps of code posts and documentation littered around the internet (including here on SO) which refer to old addins and other now obsolete references which take you down a myriad of wrong paths.
Anyway, so I've had a play around with trying to create Windows credentials, PSCredentials, and the like (which don't seem to be accepted by anything), the old methods to supply an ICredential are now obsolete and I'm really not sure where to turn.
Basically, I just want to create a workspace, check out items, update files and check them back in - all as our new "tfsService" user account. Please help...
UPDATE:
Based on the answer from #Nick, I needed to make the following changes. Note the use of [System.Uri] which was required to get this working for me (not sure if that's a quirk of my setup as others didn't seem to require this). Also, I needed to put the constructor call for TfsTeamProjectCollection all on one line, as splitting it onto separate lines, as per Nick's example, wouldn't work for me either.
$cred = New-Object System.Net.NetworkCredential("username", "password", "domain")
$tfsCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection([System.Uri]$tfsServer, $cred)
#$tfsCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($tfsServer)
$tfsVersionCtrlType = [Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]
$tfsVersionCtrl = $tfsCollection.GetService([type] $tfsVersionCtrlType)
$tfsWorkspace = $tfsVersionCtrl.CreateWorkspace($subfolder, $tfsVersionCtrl.AuthenticatedUser)
I cannot test this effectively but this seems to be a common answer on SO for this issue.
$cred = New-Object NetworkCredential("myuser", "myPassword", "mydomain")
$tfsCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection
(
$tfsServer,
$cred
)

Using querySelectorAll on an mshtml.HTMLDocumentClass object in PowerShell causes a crash

I'm trying to do some web-scraping via PowerShell, as I've recently discovered it is possible to do so without too much trouble.
A good starting point is to just fetch the HTML, use Get-Member, and see what I can do from there, like so:
$html = Invoke-WebRequest "https://www.google.com"
$html.ParsedHtml | Get-Member
The methods available to me for fetching specific elements appear to be the following:
getElementById()
getElementsByName()
getElementsByTagName()
For example I can get the first IMG tag in the document like so:
$html.ParsedHtml.getElementsByTagName("img")[0]
However after doing some more research in to whether I could use CSS Selectors or XPath I discovered that there are unlisted methods available, since we are just using the HTML Document object documented here:
querySelector()
querySelectorAll()
So instead of doing:
$html.ParsedHtml.getElementsByTagName("img")[0]
I can do:
$html.ParsedHtml.querySelector("img")
So I was expecting to be able to do:
$html.ParsedHtml.querySelectorAll("img")
...in order to get all of the IMG elements. All the documentation I've found and googling I've done supports this. However, in all my testing this function crashes the calling process and reports a heap corruption exception code in the Event Log (0xc0000374).
I'm using PowerShell 5 on Windows 10 x64. I've tried it in a Win10 x64 VM that is a clean build and just patched up. I've also tried it in Win7 x64 upgraded to PowerShell 5. I haven't tried it on anything prior to PowerShell 5 as all our systems here are upgraded, but I probably will once I have time to spool a new vanilla VM for testing.
Has anyone run in to this issue before? All my research so far is a dead end. Are there alternatives to querySelectorAll? I need to scrape pages that will have predictable sets of tags inside unpredictable layouts and potentially no IDs or classes assigned to the tags, so I want to be able to use selectors that allow structure/nesting/wildcards.
P.S. I've also tried using the InternetExplorer.Application COM object in PowerShell, the result is the same, except instead of PowerShell crashing Internet Explorer crashes. This was actually my original approach, here's the code:
# create browser object
$ie = New-Object -ComObject InternetExplorer.Application
# make browser visible for debugging, otherwise this isn't necessary for function
$ie.Visible = $true
# browse to page
$ie.Navigate("https://www.google.com")
# wait till browser is not busy
Do { Start-Sleep -m 100 } Until (!$ie.Busy)
# this works
$ie.document.getElementsByTagName("img")[0]
# this works as well
$ie.document.querySelector("img")
# blow it up
$ie.document.querySelectorAll("img")
# we wanna quit the process, but since we blew it up we don't really make it here
$ie.Quit()
Hope I'm not breaking any rules and this post makes sense and is relevant, thanks.
UPDATE
I tested earlier PowerShell versions. v2-v4 crash using the InternetExplorer.Application COM method. v3-4 crash using the Invoke-WebRequest method, v2 doesn't support it.
I ran into this problem, too, and posted about it on reddit. I believe the problem happens when Powershell tries to enumerate the HTML DOM NodeList object returned by querySelectorAll(). The same object is returned by childNodes() which can be enumerated by PS, so I'm guessing there's some glue code written for .ParsedHtml.childNodes but not .ParsedHtml.querySelectorAll(). The crash can be triggered by Intellisense trying to get tab-complete help for the object, too.
I found a way around it, though! Just access the native DOM methods .item() and .length directly and emit the node objects into a PowerShell array. The following code pulls the newest page of posts from /r/Powershell, gets the post list anchors via querySelectorAll() then manually enumerates them using the native DOM methods into a Powershell-native array.
$Result = Invoke-WebRequest -Uri "https://www.reddit.com/r/PowerShell/new/"
$NodeList = $Result.ParsedHtml.querySelectorAll("#siteTable div div p.title a")
$PsNodeList = #()
for ($i = 0; $i -lt $NodeList.Length; $i++) {
$PsNodeList += $NodeList.item($i)
}
$PsNodeList | ForEach-Object {
$_.InnerHtml
}
Edit .Length seems to work capitalized or lower-case. I would have expected the DOM to be case-sensitive, so either there's some things going on to help translate or I'm misunderstanding something. Also, the CSS selector is grabbing the source links (self.PowerShell mostly), but that it my CSS selector logic error, not a problem with querySelectorAll(). Note that the results of querySelectorAll() are not live, so modifying them won't modify the original DOM. And I haven't tried modifying them or using their methods yet, but clearly we can grab at the very least .InnerHtml.
Edit 2: Here is a more-generalized wrapper function:
function Get-FixedQuerySelectorAll {
param (
$HtmlWro,
$CssSelector
)
# After assignment, $NodeList will crash powershell if enumerated in any way including Intellisense-completion while coding!
$NodeList = $HtmlWro.ParsedHtml.querySelectorAll($CssSelector)
for ($i = 0; $i -lt $NodeList.length; $i++) {
Write-Output $NodeList.item($i)
}
}
$HtmlWro is an HTML Web Response Object, the output of Invoke-WebReqest. I originally tried to pass .ParsedHtml but then it would crash on assignment. Doing it this way returns the nodes in a Powershell array.
The #midnightfreddie's solution worked fine for me before, but now it throws Exception from HRESULT: 0x80020101 when calling $NodeList.item($i).
I found the following workaround:
function Invoke-QuerySelectorAll($node, [string] $selector)
{
$nodeList = $node.querySelectorAll($selector)
$nodeListType = $nodeList.GetType()
$result = #()
for ($i = 0; $i -lt $nodeList.length; $i++)
{
$result += $nodeListType.InvokeMember("item", [System.Reflection.BindingFlags]::InvokeMethod, $null, $nodeList, $i)
}
return $result
}
This one works for New-Object -ComObject InternetExplorer.Application as well.

Getting result of .Net object asynchronous method in powershell

I'm trying to call an async method on a .Net object instantiated in Powershell :
Add-Type -Path 'my.dll'
$myobj = new-object mynamespace.MyObj()
$res = $myobj.MyAsyncMethod("arg").Result
Write-Host "Result : " $res
When executing the script, the shell doesn't seem to wait for MyAsyncMethod().Result and displays nothing, although inspecting the return value indicates it is the correct type (Task<T>). Various other attempts, such as intermediary variables, Wait(), etc. gave no results.
Most of the stuff I found on the web is about asynchronously calling a Powershell script from C#. I want the reverse, but nobody seems to be interested in doing that. Is that even possible and if not, why ?
I know this is a very old thread, but it might be that you were actually getting an error from the async method but it was being swallowed because you were using .Result.
Try using .GetAwaiter().GetResult() instead of .Result and that will cause any exceptions to be bubbled up.
For long running methods, use the PSRunspacedDelegate module, which will enable you to run the task asynchronously:
$task = $myobj.MyAsyncMethod("arg");
$continuation = New-RunspacedDelegate ( [Action[System.Threading.Tasks.Task[object]]] {
param($t)
# do something with $t.Result here
} );
$task.ContinueWith($continuation);
See documentation on GitHub. (Disclaimer: I wrote it).
This works for me.
Add-Type -AssemblyName 'System.Net.Http'
$myobj = new-object System.Net.Http.HttpClient
$res = $myobj.GetStringAsync("https://google.com").Result
Write-Host "Result : " $res
Perhaps check that PowerShell is configured to use .NET 4:
How can I run PowerShell with the .NET 4 runtime?

Register dll in COMPLUS application using powershell

I have a COMPLUS apllication of activation type "1".Now i want to register few dlls under this application,that will get list down under components tab of the application.suppose the dll name is test1.dll.
How can i do this.I came to know about ICOMAdminCatalog::InstallComponent,but not getting a syntax to use it.Please help :)
Try this:
$comAdmin = New-Object -comobject COMAdmin.COMAdminCatalog;
$comAdmin.InstallComponent("MyApplication", "MyLibrary.dll", $null, $null);
Note: If you're adding a new application, make sure you $apps.SaveChanges() first.