Pinning Metro Apps To Taskbar Windows 10 Powershell - c#

The following code will pin a metro app to start given an AUMID
If you change
-match 'Pin To Start'
Unfortunately changing the match to 'Pin To Taskbar' does not work. What is going on here?
function Pin-Taskbar { param(
[string]$aumid,
[switch]$unpin
)
try{
if ($unpin.IsPresent){
((New-Object -Com Shell.Application).NameSpace('shell:::{4234d49b-0245-4df3-b780-3893943456e1}').Items() | ?{$_.Path -eq $aumid}).Verbs() | ?{$_.Name.replace('&','') -match 'Unpin from Taskbar'} | %{$_.DoIt()}
return "App '$aumid' unpinned from Taskbar"
}else{
((New-Object -Com Shell.Application).NameSpace('shell:::{4234d49b-0245-4df3-b780-3893943456e1}').Items() | ?{$_.Path -eq $aumid}).Verbs() | ?{$_.Name.replace('&','') -match 'Pin to Taskbar'} | %{$_.DoIt()}
return "App '$aumid' pinned to Taskbar"
}
}catch{
Write-Error "Error Pinning/Unpinning App! (App-Name correct?)"
}
}
Pin-Taskbar king.com.CandyCrushSaga_kgqvnymyfvs32!App

I assume you are a good citizen and do not want to spam the taskbar of the users.
In previous versions of Windows, you were able to use the verb Pintotaskbar to programmatically un-/pin programs to your taskbar.
$shell = new-object -com "Shell.Application" 
$folder = $shell.Namespace((Join-Path $env:SystemRoot System32\WindowsPowerShell\v1.0))
$item = $folder.Parsename('powershell_ise.exe')
$item.invokeverb('taskbarpin');
This no longer works in Windows 10. The verb Pintotaskbar simply is not exist accessible anymore.
Hence, an unapproved 3rd-party command prompt utility
called SysPin was brought to life - that allows you to do exactly what you want (though it does not work with each and every app, especially UWP/Metro apps).
However, since Windows 10 version 1607 there is a new official way: adding a <TaskbarLayout>
See: Customize Pinned Items on Taskbar in Windows 10 1607 during OSD with ConfigMgr

Related

How to enumerate the real Windows File Explorer windows

I'm trying to enumerates all open File Explorer windows in a PowerShell script.
I have already found on other posts how to enumerate all explorer.exe windows instances, for example using the Shell.Application COM API:
(New-Object -com "Shell.Application").windows()
But this actually returns more than I want:
I want only the "real" File Explorer windows showing actual files on my disk or network, not the "fake" explorer.exe instances that are just containers for various Control Panel windows, etc.
So basically the list of instances shown when hovering the mouse over the File Explorer icon on the Taskbar.
How can this be done reliably, and preferably in a way that works in Windows 7 to 11?
Comparing the window title to known strings like "Control Panel" or "Windows Update" has limited value. This would only eliminate the most common cases, and on English versions of Windows only.
I tried looking at the File Explorer window class, but it's "CabinetWClass" in all cases, even for Control Panels.
I noticed that real instances have a child window of class "UIRibbonWorkPane", whereas the Control Panel does not. But the ribbon can be disabled, so this is not a reliable marker.
My script already contains C# declarations encapsulating WIN32 API calls, so C# code snippets would also do.
2021-10-10 update:
The best algorithm I've found so far, building on #simon-mourier's answer, can summarized this way:
$self = $window.Document.Folder.Self
$ClassID = $Self.ExtendedProperty("System.NamespaceCLSID")
$BaseClassID = $Self.Path.Substring(2,38) # With proper tests to clear it if it's not a UUID
$FileExplorerIDs = ( # The few known types which are file systems, but don't set $Self.IsFileSystem
# Windows 10
"f02c1a0d-be21-4350-88b0-7367fc96ef3c", # Network
"679f85cb-0220-4080-b29b-5540cc05aab6", # Quick Access
"20d04fe0-3aea-1069-a2d8-08002b30309d", # This PC
# Windows 7
"031e4825-7b94-4dc3-b131-e946b44c8dd5" # Libraries
)
if ($Self.IsFileSystem) {
$AppType = "File Explorer"
} elseif ($FileExplorerIDs -contains "$ClassID") {
$AppType = "File Explorer"
} elseif ($BaseClassID -eq "{26EE0668-A00A-44D7-9371-BEB064C98683}") {
$AppType = "Control Panel"
} elseif ("{$ClassID}" -eq "{D20EA4E1-3957-11D2-A40B-0C5020524153}") {
$AppType = "Control Panel" # Windows 7 Administrative Tools
} elseif ($Self.Name -eq $Self.Path) { # TODO: Improve this test, which is very weak
$AppType = "Search Results" # Ex: "Search Results in Indexed Locations"
} else {
$AppType = "Unknown"
}
The full algorithm, with the proper precautions to eliminate undefined fields, or invalid values, etc, is implemented in this script:
https://github.com/JFLarvoire/SysToolsLib/blob/master/PowerShell/ShellApp.ps1
One solution is to test whether the Shell Folder (IShellFolder) beneath the Shell View that Windows sends back is handled by the Windows file system or by some custom folder.
For that, you can use the System.NamespaceCLSID Windows property. If the folder associated with the view is handled by the file system, this property value will be the ShellFSFolder GUID value which equal to f3364ba0-65b9-11ce-a9ba-00aa004ae837 (from Windows SDK shobjidl_core.h).
You can test it with something like this in PowerShell:
$ShellFSFolder = [System.Guid]::New("f3364ba0-65b9-11ce-a9ba-00aa004ae837")
foreach($win in (New-Object -com "Shell.Application").Windows()) {
$clsid = $win.Document.Folder.Self.ExtendedProperty("System.NamespaceCLSID")
if ($clsid -ne $null) {
$clsid = [System.Guid]::New($clsid)
if ($clsid -eq $ShellFSFolder) {
Write-Host $win.Document.Folder.Self.Path
}
}
}
And like this in C#:
var ShellFSFolder = new Guid("f3364ba0-65b9-11ce-a9ba-00aa004ae837");
dynamic shell = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
foreach (var win in shell.Windows)
{
var clsid = win.Document.Folder.Self.ExtendedProperty("System.NamespaceCLSID");
if (clsid != null)
{
Guid guid;
if (clsid is byte[] bytes)
{
guid = new Guid(bytes);
}
else
{
guid = new Guid((string)clsid);
}
if (guid == ShellFSFolder)
{
Console.WriteLine(win.Document.Folder.Title); // for example
}
}
}
It seems that only file-path-based File Explorer windows have a non-$null .LocationUrl property value, so you can filter by that:
Caveat: Jean-François reports that this approach doesn't work for Explorer windows that are open to a file-system folder located on a connected smartphone, in which case .LocationUrl is apparently $null too.
$explorerWinsWithFilePaths =
(New-Object -com "Shell.Application").Windows() | Where-Object LocationUrl
To extract the file paths that these windows are displaying (the technique also works with non-file locations such as Quick Access, which translate into ::-prefixed GUIDs):
$explorerWinsWithFilePaths.Document.Folder.Self.Path
See Jean-François' comment below for examples of what windows showing folders on a connected smartphone report.

Jenkins build blocks because of a windows form

We have a .net application where it checks whether the build is on release mode and open up a simple windows form to input the version as a pre build event. I made this form to automatically close in 10 seconds if the user does not give an input. But unfortunately, in Jenkins, the build gets stuck on this step without going forward. So my guess was since Jenkins runs on command line it waits until the user input for continue. But even when I add automatically close the form it does not continue. Is there a way to build this job without UI blocking Jenkins?
You are not using Jenkins in the optimal way. Here are a few tips to help you out:
Get rid of your windows form to increment version
Add a CommonAssemblyInfo.cs in your visual studio solution with an initial version number
Force Jenkins to increment the version automatically [described below]
Commit the file by jenkins using git publisher or using svn.exe with commit flag
Reading Version number using powershell:
param([string]$assemblyInfoPath, [string]$workSpace)
$contents = [System.IO.File]::ReadAllText($assemblyInfoPath)
$versionString = [RegEx]::Match($contents,"(AssemblyFileVersion\("")(?:\d+\.\d+\.\d+\.\d+)(""\))")
Write-Host ("AssemblyFileVersion: " +$versionString)
$version = gc $assemblyInfoPath | select-string -pattern "AssemblyVersion"
$version -match '^\[assembly: AssemblyVersion\(\"(?<major>[0-9]+)\.(?<minor>[0-9]+)\.(?<revision>[0-9]+)\.(?<build>[0-9]+)\"\)\]'
$BuildVersionNumber = $matches["major"]+"."+$matches["minor"]+"."+$matches["revision"]+"."+$matches["build"]
Write-Host ("WorkSpace: " + $env:WORKSPACE.ToString()+"\version.txt")
#[Environment]::SetEnvironmentVariable("BUILD_NUMBER", $BuildVersionNumber, "Machine")
$path = $env:WORKSPACE.ToString() + "\version.txt"
$BuildVersionNumber | out-file -encoding ASCII -filepath $path
Increment Version using powershell:
#
# This script will increment the build number in an AssemblyInfo.cs file
#
param([string]$assemblyInfoPath, [string]$workSpace)
$contents = [System.IO.File]::ReadAllText($assemblyInfoPath)
$versionString = [RegEx]::Match($contents,"(AssemblyFileVersion\("")(?:\d+\.\d+\.\d+\.\d+)(""\))")
Write-Host ("AssemblyFileVersion: " +$versionString)
#Parse out the current build number from the AssemblyFileVersion
$currentBuild = [RegEx]::Match($versionString,"(\.)(\d+)(""\))").Groups[2]
Write-Host ("Current Build: " + $currentBuild.Value)
#Increment the build number
$newBuild= [int]$currentBuild.Value + 1
Write-Host ("New Build: " + $newBuild)
#update AssemblyFileVersion and AssemblyVersion, then write to file
Write-Host ("Setting version in assembly info file ")
$contents = [RegEx]::Replace($contents, "(AssemblyVersion\(""\d+\.\d+\.\d+\.)(?:\d+)(""\))", ("`${1}" + $newBuild.ToString() + "`${2}"))
$contents = [RegEx]::Replace($contents, "(AssemblyFileVersion\(""\d+\.\d+\.\d+\.)(?:\d+)(""\))", ("`${1}" + $newBuild.ToString() + "`${2}"))
[System.IO.File]::WriteAllText($assemblyInfoPath, $contents)
$version = gc $assemblyInfoPath | select-string -pattern "AssemblyVersion"
$version -match '^\[assembly: AssemblyVersion\(\"(?<major>[0-9]+)\.(?<minor>[0-9]+)\.(?<revision>[0-9]+)\.(?<build>[0-9]+)\"\)\]'
$BuildVersionNumber = $matches["major"]+"."+$matches["minor"]+"."+$matches["revision"]+"."+$matches["build"]
Write-Host ("WorkSpace: " + $env:WORKSPACE.ToString()+"\version.txt")
#[Environment]::SetEnvironmentVariable("BUILD_NUMBER", $BuildVersionNumber, "Machine")
$path = $env:WORKSPACE.ToString() + "\version.txt"
$BuildVersionNumber | out-file -encoding ASCII -filepath $path
Usage in Jenkins:
Version Format in CommonAssembly: 1.0.0.0
After incrementing: 1.0.0.1
As Tom mentioned in the comments, you should look add the option to start your forms application with a parameter that indicates that the main form should not be shown and some magic should happen. For example, check for a "/s" and let the application run silently if it is present:
MyWinformsApplication.exe /s
Also, as Tom mentioned, a console application can still open a window and this is really useful to have in Jenkins as you can then write insightful messages to the console which will be logged by Jenkins. You can always use these at a later stage to check if something went wrong.
As an additional note - if you add Console.WriteLine() to your WinForms application, Jenkins will pick the string up and add it to the console log.

how to set Set-VMFirmware on a single line in powershell

i am trying to make a small c# application to create my Hyper V VM's but i has got a problem with powershell in c# so i just running every powershell cmdlet using cmd.exe
but now i cant get how to set dvddrive as firstbootdevice in generation 2 virtual machine in just one line
for powershell script i use
$VMNAME= "SQL3"
$VMDVD = Get-VMDvdDrive -VMName $VMNAME
Set-VMFirmware -VMName $VMNAME -FirstBootDevice $VMDVD
but how can i do it in a single line
As a general rule, any variable reference can be substituted by a subexpression ($()):
Set-VMFirmware -VMName "SQL3" -FirstBootDevice $(Get-VMDvdDrive -VMName "SQL3")
If you have multiple DVD drives and know the ISO file
$DvdBootDrive = Get-VMDvdDrive -VMName $VmName | where {$_.Path -Like "*AutoInstall.iso*" }
Set-VMFirmware -VMName $VmName -FirstBootDevice $DvdBootDrive

Powershell change taskbar icon of running console application

I'm using code from this post over at Microsoft's TechNet to change the icon of my running PowerShell application. This works great for the icon that is displayed in the Powershell window itself, but it doesn't change the Taskbar's icon. I changed the function a bit an hoped that it would also change the icon displayed in the Taskbar.
# Set the icon of the current console window to the specified icon.
#
# AUTHOR: Aaron Lerch <http://www.aaronlerch.com/blog>
# COPYRIGHT: © 2009 Aaron Lerch
# LINK: http://gallery.technet.microsoft.com/scriptcenter/9d476461-899f-4c98-9d63-03b99596c2c3
#
# PARAM:
# -IconFile
# Absolute path to the icon file.
# RETURN:
# $null
function Set-ConsoleIcon {
Param(
[parameter(Mandatory = $true)] [string] $IconFile
)
[System.Reflection.Assembly]::LoadWithPartialName('System.Drawing') | Out-Null
# Verify the file exists
if ([System.IO.File]::Exists($iconFile) -eq $true) {
$ch = Invoke-Win32 'kernel32' ([IntPtr]) 'GetConsoleWindow'
$i = 0;
$size = 16;
while ($i -ne 4) {
$ico = New-Object System.Drawing.Icon($iconFile, $size, $size)
if ($ico -ne $null) {
Send-Message $ch 0x80 $i $ico.Handle | Out-Null
}
if ($i -eq 4) {
break
}
$i += 1
$size += 16
}
}
else {
Write-Host 'Icon file not found' -ForegroundColor 'Red'
}
}
I'm providing the icon in the sizes 16 (wParam 1), 32 (wParam 2), 48 (wParam 3), and 64 (wParam 4).
I also tried to change the Icon from my launching C# application (based on this Stackoverflow discussion) but that didn't work at all.
If you'd like to see the complete code have a look at the following:
the C# class that starts the Powershell process
the Powershell PS1 script
This might not be possible. Here are more details about the "group icon" in the taskbar:
Change icon of group in taskbar (Win7)
Update:
You can change the application ID of your window. Since the icon primarily comes from the application ID, by changing it Explorer doesn't know the default icon anymore and will use the actual window icon. This also ungroups the window from other CMD windows to make the individual icon visible at all. (There's a taskbar animation like for closed/new windows when you do this in an existing console window.) There's an MSDN article, look for "application ID" in it:
https://msdn.microsoft.com/en-us/magazine/dd942846.aspx
Here's the relevant code from it (C++):
#define WINVER 0x601
#define _WIN32_WINNT 0x601
#include <Propvarutil.h>
#include <propkey.h>
#include <Shellapi.h>
PROPVARIANT pv;
InitPropVariantFromString(L"MyAppID", &pv);
IPropertyStore* pps;
VERIFY(SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps)));
VERIFY(pps->SetValue(PKEY_AppUserModel_ID, pv));
VERIFY(pps->Commit());
(Linked libs: shlwapi.lib)
The Windows API Code Pack should also have managed wrapper code for this. Didn't look it up because I currently use this function in a C++ application. But I found other questions about it here.
For your PowerShell script that probably won't help much either. Since it's all native code wrapped with more complex managed code, I think your best bet would be a little native helper tool. I am currently integrating this function into my FlashConsoleWindow tool that can do some more things to console windows like flashing or displaying a taskbar progress state.
An easy alternative is to create a shortcut to the powershell exe. Change the icon of the shortcut to whatever you want.
Then whenever you call your script, use the shortcut instead of the PS exe. So instead of
powershell.exe -ExecutionPolicy Bypass -File D:\scripts\whatever.ps1
Use
D:\scripts\powershell.lnk -ExecutionPolicy Bypass -File D:\scripts\whatever.ps1
The shortcut powershell.lnk can be "C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe" or just powershell.exe without the full path (as long as PATH var is set properly for PS on the system)
You can put the PS shortcut anywhere really, but I would recommend you put it with the script for portability.
Now when you launch the script via the shortcut, the taskbar icon will be whatever you defined it as via the shortcut file.
The only caveat is that your window settings in the shortcut will override the PS window, so you should define how you want it to look in the shortcut properties.

Adding bookmark to web browser from external applications

Does anyone know if it is possible to add bookmark to web browsers (Safari, IE, FF, Chrome, Opera) from external applications ?
For IE :
You need to create a link file here :
c#
Environment.GetFolderPath(Environment.SpecialFolder.Favorites)
Powershell
[Environment]::GetFolderPath( [System.Environment+SpecialFolder]::Favorites)
Chrome:
You need to add entry in json format file bookmarks (with no extension):
on Win7 is
C:\Users\<YOURUSERNAME>\AppData\Local\Google\Chrome\User Data\Default\
Firefox:
The bookmarks are stored in a SQLite:
../Application Data/Mozilla/Firefox/Profiles/{your firefox profile}/places.sqlite
Using System.Data.SQLite you can try to add link, but I can't help you more.
Can't help you for Safari and Opera
In Powershell V2 ISE (x86), this code will list all the Special Folders on the system or even this -
$objShell = New-Object -com "Wscript.Shell"
$objShell.SpecialFolders | WHERE {$_.ToString() -match "Fav"}
You can then access & manipulate the C:\Users\username\Favorites folder. I don't know if this will extend to all browsers [except IE]
Here is the solution I came up with for adding bookmarks to Chrome from PowerShell:
$fileBookmarks="$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Bookmarks"
$dataBookmarks=Get-Content $fileBookmarks -Encoding UTF8| Out-String |ConvertFrom-Json
function createNewChromeBookmark ($Bookmarks, [string]$BookmarkName, [string]$BookmarkURL) {
function getBookmarkIDs($object){
$object | ForEach-Object{
"{0:0000}" -f [int]($_.id);
if([bool]($_.PSobject.Properties.name -match "children")){
GetBookmarkIDs($_.children);
}
}
};
$nextBookmarkID = [int](getBookmarkIDs -object $Bookmarks.roots.bookmark_bar|Sort-Object -Descending|Select-Object -First 1) + 1
$currentChomeTime=[System.DateTimeOffset]::Now.ToUnixTimeMilliseconds()*10000;
$newBookmark= [PSCustomObject]#{
date_added=$currentChomeTime
guid=[guid]::NewGuid()
id=$nextBookMarkID
name="$BookmarkName"
type="url"
url="$BookmarkURL"
}
return $newBookmark;
}
$newBookmark = createNewChromeBookmark -Bookmarks $dataBookmarks -BookmarkName "Your Bookmark Name" -BookmarkURL "https://[Your URL Here]";
$dataBookmarks.roots.bookmark_bar.children += $newBookmark;
$dataBookmarksJSON = ConvertTo-Json -InputObject $dataBookmarks -Depth 200
Set-Content -Path $fileBookmarks -Value $dataBookmarksJSON -Encoding UTF8

Categories

Resources