colinardo
Goto Top

Powershell: Windows 10 Modern Apps an Startmenü anheften oder entfernen (Pin oder Unpin)

Microsoft lässt leider eine Funktion vermissen die es einem als Admin ermöglicht einzelne Modern Apps per Skript an das Startmenü anzupinnen oder aus diesem zu entfernen ohne die App gleich deinstallieren zu müssen. Zwar gibt es die Möglichkeit ein vordefiniertes Layout an die User per GPO zu verteilen, doch manchmal ist das zu viel des Guten.

Aus diesem Grund habe ich eine Powershell-Funktion entwickelt die diesen Missstand behebt.

back-to-topPowershell-Funktion Pin-App

function Pin-App {
    param(
        [parameter(mandatory=$true)][ValidateNotNullOrEmpty()][string[]]$appname,
        [switch]$unpin
    )
    $actionstring = @{$true='Von "Start" lösen|Unpin from Start';$false='An "Start" anheften|Pin to Start'}[$unpin.IsPresent]  
    $action = @{$true='unpinned from';$false='pinned to'}[$unpin.IsPresent]  
    $apps = (New-Object -Com Shell.Application).NameSpace('shell:::{4234d49b-0245-4df3-b780-3893943456e1}').Items() | ?{$_.Name -in $appname}  
    
    if($apps){
        $notfound = compare $appname $apps.Name -PassThru
        if ($notfound){write-error "These App(s) were not found: $($notfound -join ",")"}  

        foreach ($app in $apps){
            $appaction = $app.Verbs() | ?{$_.Name.replace('&','') -match $actionstring}  
            if ($appaction){
                $appaction | %{$_.DoIt(); return "App '$($app.Name)' $action Start"}  
            }else{
                write-error "App '$($app.Name)' is already pinned/unpinned to/from start or action not supported."  
            }
        }
    }else{
        write-error "App(s) not found: $($appname -join ",")"  
    }
}

Die Funktion kennt zwei Parameter:
Pin-App [NAME DER APP(s) <string[]>] [-Unpin <switch>]
Im ersten wird der Name der App angegeben (es können auch mehrere Apps als String-Array übergeben werden). Der zweite dient dazu die App(s) aus dem Startmenü zu entfernen.
Anzugeben ist jeweils der lokalisierte Name der App wie er auch im Startmenü erscheint.
back-to-topApp an Startmenü anpinnen
Pin-App "Mail"
back-to-topApp aus Startmenü entfernen
Pin-App "Mail" -unpin
back-to-topMehrere Apps auf einmal anpinnen
Pin-App "Rechner","Kamera"
Das gleiche kann auch für das Entfernen mehrerer Apps mit dem Switch -unpin benutzt werden.

back-to-topHinweise
Die Funktion ist auf deutsche und englische Sprachversionen von Windows 10 angepasst. Soll diese für andere Sprachen erweitert werden sind die Strings in Zeile 6 zum anpinnen und "de"-pinnen mit einem Pipe-Symbol hinzuzufügen (Achtung: Hier gilt die Regular Expression-Syntax).

Die Apps die mit dieser Methode gepinnt werden können müssen zwingend in diesem System-Ordner gelistet sein:
shell:::{4234d49b-0245-4df3-b780-3893943456e1}
a1c3086a1d77932a2ed9f549ec294fc6

Viel Spaß damit
Grüße Uwe

Falls euch der Beitrag gefällt, könnt ich euch auch gerne jederzeit mit einer kleinen Spende erkenntlich zeigen.

back-to-topUpdates
DatumÄnderung
17.02.2016Grundlegende Überarbeitung der Funktion und Unterstützung für mehrere Apps auf einmal
13.06.2019 Zur Info: Funktion unter Windows 10 1903 nicht funktionsfähig

Content-ID: 287368

Url: https://administrator.de/contentid/287368

Ausgedruckt am: 24.11.2024 um 04:11 Uhr

teslacoil
teslacoil 03.11.2015 um 21:02:42 Uhr
Goto Top
Die Idee ist gar nicht mal so schlecht ne
DerWoWusste
DerWoWusste 17.11.2015 aktualisiert um 15:33:44 Uhr
Goto Top
Hallo Uwe!

Schöne Sache, sagte ich ja schon an anderer Stelle. Einen Haken sehe ich noch: Seit win10 threshold2 kommen neue Kacheln daher, die, sofern man keinen Internetzugang erlaubt, keine sinnvolle Beschriftung erhalten, sondern schlicht "Ausstehend", siehe Bild.
57f5977b4b5e3e53c4644997798a36a1
also quasi Platzhalter für weiteren Krempel, den MS dort abladen möchte.
Ich würde gerne alles abpinnen und dann selektiv Dinge wieder anpinnen. Kann man alles abpinnen?
Oder kann man die "Ausstehend"-Items ebenso programmatisch entfernen?
colinardo
colinardo 17.11.2015 aktualisiert um 16:08:26 Uhr
Goto Top
Hallo DWW,
das sind so wie es aussieht nur Links in den MS Store, da werde ich mich mal wieder in die untiefen des Dateibaumes begeben müssen und diese "MS-Krankheiten" zu lokalisieren ... face-wink

See you soon with a working solution
DerWoWusste
DerWoWusste 17.11.2015 um 16:10:55 Uhr
Goto Top
See you soon with a working solution
see you soon with another MS illness face-wink
colinardo
colinardo 17.11.2015, aktualisiert am 18.11.2015 um 17:16:19 Uhr
Goto Top
Also folgenden erfolgreichen Zwischenstand kann ich vermelden:

Ich konnte herausfinden das ein Registry-Eintrag für die zusätzlichen Links (welche übrigens die Spiele Candy-Crush/Minecraft, die Newsapp Flipboard etc. sind) im Startmenü verantwortlich sind.

Um zu erreichen das diese Links bei neuen Userprofilen nicht mehr im Startmenü auftauchen geht man folgendermaßen vor:
Man startet Regedit, markiert den Knoten HKEY_LOCAL_MACHINE, und lädt dann über Datei > Struktur laden das Default-Userprofil (C:\Users\Default\ntuser.dat) in einen Schlüssel den man z.B. temp_default_profile nennt, dort ändern man dann folgenden Key:
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\temp_default_profile\SOFTWARE\Microsoft\Windows\CurrentVersion\ContentDeliveryManager]
"PreInstalledAppsEnabled"=dword:00000000  
Danach markiert man die importierte Struktur temp_default_profile und geht auf Datei > Struktur entfernen

Als Admin-Batch lässt sich das ganze Prozedere so abkürzen:
@echo off
reg load "HKLM\temp_default_profile" "C:\Users\Default\ntuser.dat"  
reg add "HKLM\temp_default_profile\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v PreinstalledAppsEnabled /d 0 /t "REG_DWORD" /f  
reg unload "HKLM\temp_default_profile"  

Man beachte: Der Key wirkt nur wenn das Profil neu erstellt wird, ein nachträgliches Ändern des Keys nachdem das Profil erstellt wurde ändert nichts an den Links !!

Grüße Uwe

p.s. In dem Key wo ich den Wert gefunden habe sind auch ein paar andere Interessante Werte (SystemPaneSuggestionsEnabled) mit denen sich vermutlich die Werbung im Startmenü abschalten lässt.
DerWoWusste
DerWoWusste 18.11.2015 um 08:36:44 Uhr
Goto Top
Besten Dank, läuft! Regmounting kann man ja auch skripten, also ist es in jedem Fall ein Weg.
JohnDorian
JohnDorian 11.12.2015 um 09:30:25 Uhr
Goto Top
Gelobet seiest du colinardo!

Danke, das hilft mir ungemein!

Grüße, JD
Highend01
Highend01 03.02.2016 um 10:23:14 Uhr
Goto Top
Danke Uwe (das nächste Mal, wenn eine Zahlung auf meinem Paypal-Konto eingeht, leite ich eine Spende weiter...)!

Eine Möglichkeit, bereits existierende Verknüpfungen zu entfernen, die selber keine installierten Apps darstellen (Flipboard, Minecraft und ähnlicher Sch) konntest du (leider) nicht mehr finden? Also für einen bereits existierenden Benutzeraccount...

LG,
Highend
colinardo
colinardo 03.02.2016 aktualisiert um 15:20:51 Uhr
Goto Top
Zitat von @Highend01:
Danke Uwe (das nächste Mal, wenn eine Zahlung auf meinem Paypal-Konto eingeht, leite ich eine Spende weiter...)!
Danke face-smile
Eine Möglichkeit, bereits existierende Verknüpfungen zu entfernen, die selber keine installierten Apps darstellen (Flipboard, Minecraft und ähnlicher Sch) konntest du (leider) nicht mehr finden? Also für einen bereits existierenden Benutzeraccount...
Leider nein, das Startmenü selber wird von MS in binären Files mit proprietärem dynamischen Inhalt verwaltet. Verteile dein Startmenü über eine XML damit kannst du das Startmenü auch noch nachträglich anpassen.

Grüße Uwe
Bommi1961
Bommi1961 16.02.2016 um 14:57:33 Uhr
Goto Top
Hallo Uwe, Dein Tool ist einfach klasse und hat mir sehr geholfen. Läuft auf Notebooks erstklassig. Aber auf Tablet z.B. Surface 3 oder 4, hier wird der Gleich Instal-Jjob genommen wie bei den NB's, löscht er nur die nicht gewünschten Kacheln, erstellt aber nicht die neuen. Wie gesagt bei NB's läuft es Fehler frei. Und es wird immer der gleich Job genommen. Hast Du eine Idee woran das liegen könnte?

VG Thilo
colinardo
colinardo 16.02.2016 aktualisiert um 16:19:44 Uhr
Goto Top
Zitat von @Bommi1961:

Hallo Uwe, Dein Tool ist einfach klasse und hat mir sehr geholfen. Läuft auf Notebooks erstklassig. Aber auf Tablet z.B. Surface 3 oder 4, hier wird der Gleich Instal-Jjob genommen wie bei den NB's, löscht er nur die nicht gewünschten Kacheln, erstellt aber nicht die neuen. Wie gesagt bei NB's läuft es Fehler frei. Und es wird immer der gleich Job genommen. Hast Du eine Idee woran das liegen könnte?

Hallo Thilo,
von was für "Jobs" sprichst du ? Hellsehen kann ich leider noch nicht face-confused
Bau dir halt ein Logging ein oder poste mal "was" bei dir gepinnt wird.
Mangels Surface kann ich hier aber leider keine Tests anstellen. Hab hier nur ein altes first Gen. Surface was ich hier mal als Preis gewonnen habe.

Vielleicht lauten die Strings zum Pinnen der Apps auf dem Surface anderst. Kannst du ja mal nachsehen ob diese im Kontextmenü gleich lauten wie auf dem Desktop.

Grüße Uwe
Bommi1961
Bommi1961 17.02.2016 um 07:59:50 Uhr
Goto Top
Hallo Uwe,

leider bin ich totaler Anfänger was Powershell angeht. Wie kann ich denn ein Login bei Deinem Skrip einschalten?

Hab Dir Gestern eine Spende überwiesen. face-smile

VG Thilo
colinardo
colinardo 17.02.2016 aktualisiert um 13:56:58 Uhr
Goto Top
Zitat von @Bommi1961:

leider bin ich totaler Anfänger was Powershell angeht. Wie kann ich denn ein Login bei Deinem Skrip einschalten?
Du könntest mal die Zeile 11 nur zum Debugging mal so abändern
((New-Object -Com Shell.Application).NameSpace('shell:::{4234d49b-0245-4df3-b780-3893943456e1}').Items() | ?{$_.Name -eq $appname}).Verbs()  

Und dir dabei den Output in der Konsole ansehen, dort sollte ein Eintrag vorhanden sein der dem Text zum Pinnen einer App entspricht. Du kannst die Ausgabe auch gerne hier posten, dann kann ich sie dir bei Bedarf anpassen. Z.b. Wenn es sich um ein Tablet in einer anderen GUI-Sprache als Englisch oder Deutsch handelt.
Hab Dir Gestern eine Spende überwiesen. face-smile
TOP! Herzlichen Dank face-smile

Grüße Uwe
Bommi1961
Bommi1961 17.02.2016 um 10:17:12 Uhr
Goto Top
Wenn ich die Zeile einfüge kommt diese Meldung:
Es ist nicht möglich, eine Methode für einen Ausdruck aufzurufen, der den NULL hat.
In C:\ProgrammDaten\WIN10AP\PIN-APP1.PS1:11 Zeichen:2

back-to-top((New-Object -Com Shell.Application).NameSpace('shell:::{4234d49b-02 ...

back-to-top~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : InvalidOperation: (face-smile , RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

Wenn ich die Zeile nicht drin haben, läuft es ohne Fehler durch:
App 'Store' unpinned from Start
App 'Einstellungen' unpinned from Start
Die beiden Befehle werden gemacht!!

App 'PCInfo' pinned to Start
App 'IT-Shop' pinned to Start
App 'Internet Explorer' pinned to Start
App 'Word 2016' pinned to Start
App 'Excel 2016' pinned to Start
App 'OneNote 2016' pinned to Start
App 'Computer Sperren' pinned to Start
App 'Logoff' pinned to Start
App 'Computer Ausschalten' pinned to Start
Das alles wird ignoriert bei den Tablets

VG Thilo
colinardo
colinardo 17.02.2016 aktualisiert um 10:23:47 Uhr
Goto Top
Sorry Typo....bin am Tablet, ist korrigiert.

Ich hatte gemeint das du das mal manuell für eine einzelne App machst die du pinnen willst. Damit werden dann die möglichen Aktionen gelistet die mit dieser App möglich sind, wenn in diesen Aktionen der String zum Pinnen nicht enthalten ist, kann das Skript die App auch nicht anpinnen.

Ansonsten bitte weitere Kommentare via PM. Danke!
Bommi1961
Bommi1961 17.02.2016 um 11:19:17 Uhr
Goto Top
muss wieder fragen...
was hast Du geändert und wo?
und was meinst Du mit PM?
colinardo
colinardo 17.02.2016 um 11:40:40 Uhr
Goto Top
Zitat von @Bommi1961:

muss wieder fragen...
was hast Du geändert und wo?
Den Code im letzten Post.
und was meinst Du mit PM?
PM
Damit der Thread hier nicht unnötig voll läuft und die anderen nicht "gestört" werden.
loonydeluxe
loonydeluxe 05.11.2017 um 15:11:18 Uhr
Goto Top
Der Thread ist zwar schon etwas älter, scheint aber der passendste zu sein

Hat einer von euch eine Möglichkeit gefunden, die Apps auf der Taskbar zu enumerieren, zu pinnen/entpinnen?

Scheint nur Anleitungen für das Startmenü zu geben, um einzelne Apps zu bearbeiten und für die Taskbar die "Lösung", per Registry alle gepinnten Apps wegzuwerfen.

Die hier kenne ich schon, hilft aber nix:
https://pinto10blog.wordpress.com/
http://alexweinberger.com/main/pinning-network-program-taskbar-programm ...
https://www.tenforums.com/customization/21002-how-automatically-cmd-powe ...

Das hier könnte funktionieren, nützt aber nix für Einzelplatzinstallationen:
https://www.itnator.net/taskleiste-windows-10-anpassen-per-gpo/

(und ich hätte gerne am Ende die alles erschlagende Lösung per Skript, nicht wieder für jede Umgebung einen eigenen Workaround)
134464
134464 05.11.2017 aktualisiert um 16:07:25 Uhr
Goto Top
Wieso hilft https://pinto10blog.wordpress.com nicht?? Geht doch einwandfrei auch für die Taskbar, und Source Code ist sogar mit dabei. Kannst du dir also nach Belieben in deine präferierte Skriptspache übersetzen.
Das hier könnte funktionieren, nützt aber nix für Einzelplatzinstallationen:
https://www.itnator.net/taskleiste-windows-10-anpassen-per-gpo/
Doch, einfach die XML statt per GPO mit Import-Startlayout importieren, GPO ist dann nicht nötig!!
und ich hätte gerne am Ende die alles erschlagende Lösung per Skript, nicht wieder für jede Umgebung einen eigenen Workaround
Wohin sollen wir die Rechnung schicken?
loonydeluxe
loonydeluxe 05.11.2017 um 18:59:12 Uhr
Goto Top
Zitat von @134464:

Wieso hilft https://pinto10blog.wordpress.com nicht?? Geht doch einwandfrei auch für die Taskbar, und Source Code ist sogar mit dabei. Kannst du dir also nach Belieben in deine präferierte Skriptspache übersetzen.
Kennst du einen allgemeingültigen Pfad zur .exe der integrierten Mail-App, so dass ich diesen als absoluten Pfad übergeben kann? --> Mein Ziel wäre ja, die Objekte der Taskbar enumerieren zu können und diese dann entsprechend zu löschen, statt die absoluten Pfade der "eventuell" gepinnten Programme zu kennen. Für das Startmenü gibts da schon was, jedoch nicht für die Taskbar.

Das hier könnte funktionieren, nützt aber nix für Einzelplatzinstallationen:
https://www.itnator.net/taskleiste-windows-10-anpassen-per-gpo/
Doch, einfach die XML statt per GPO mit Import-Startlayout importieren, GPO ist dann nicht nötig!!
Wie gut funktioniert das denn für (verschiedene) neu erzeugte Benutzerprofile einer Einzelplatzinstallation ohne dass ich jedes Mal wieder ran muss? Wie stehts um die "Home Edition"?

und ich hätte gerne am Ende die alles erschlagende Lösung per Skript, nicht wieder für jede Umgebung einen eigenen Workaround
Wohin sollen wir die Rechnung schicken?
Ich helfe ja auch immer gern.
Winfried-HH
Winfried-HH 12.07.2018 um 00:15:07 Uhr
Goto Top
Zitat von @colinardo:

Also folgenden erfolgreichen Zwischenstand kann ich vermelden:

Ich konnte herausfinden das ein Registry-Eintrag für die zusätzlichen Links (welche übrigens die Spiele Candy-Crush/Minecraft, die Newsapp Flipboard etc. sind) im Startmenü verantwortlich sind.

Meinst Du hier mit "Startmenü" nur den rechten Teil mit den "an Start angehefteten" Kacheln oder auch die Liste links mit allen Apps? Wenn letzteres, dann scheint es unter Version 1803 nicht mehr zu funktionieren. Kennst Du schon einen neuen Lösungsansatz? Bzw. wie kann ich die vorinstallierten Apps (z.B. XBox) auch dort ausblenden.
colinardo
colinardo 12.07.2018 aktualisiert um 06:55:25 Uhr
Goto Top
@Winfried-HH
Hier geht es nur um angepinnte Apps auf der rechten Seite!

Den Consumer-Blödsinn entfernt man seit einiger Zeit hiermit
https://gpsearch.azurewebsites.net/#13329
Bzw. wie kann ich die vorinstallierten Apps (z.B. XBox) auch dort ausblenden.
Die musst du aus dem Provisioning entfernen => Remove-AppxProvisionedPackage
Die linke Liste zeigt alle installierten Apps, damit dort etwas verschwindet musst du es deinstallieren. Die obige Policy verhindert das CandyCrush&Co überhaupt erst auf die Kiste landen.
Winfried-HH
Winfried-HH 12.07.2018 aktualisiert um 07:05:37 Uhr
Goto Top
Zitat von @colinardo:

Den Consumer-Blödsinn entfernt man seit einiger Zeit hiermit
https://gpsearch.azurewebsites.net/#13329
Consumer Experience nennen die das? Diesen Nervkram mit "Experience" schönzureden grenzt schon an einen Euphemismus face-confused-alt

Die linke Liste zeigt alle installierten Apps, damit dort etwas verschwindet musst du es deinstallieren.
Danke Microsoft, für die Bevormundung. Wo ist bloß das alte Win7-Startmenü geblieben, wo ich alles rauswerfen konnte, was effizientes Arbeiten behindert face-sad

Für die rechte Seite habe ich mich dann allerdings für ein anderes Verfahren entschieden, nämlich das Vorlegen einer einmalig optimierten Kachelanordnung für alle Benutzer:
https://www.zdnet.de/88276943/windows-10-einheitliches-startmenue-fuer-a ...
colinardo
colinardo 12.07.2018 aktualisiert um 07:10:21 Uhr
Goto Top
einmalig optimierten Kachelanordnung für alle Benutzer
Jap allseits bekannt. Hier ging es zwar auch um einen anderen Nutzen aber ok.

Gut dann können wir ja jetzt das Beschweren hier beenden. Das richtest du bitte besser direkt an Microsoft selber, hier bringt das nichts. Merci.
Winfried-HH
Winfried-HH 12.07.2018 aktualisiert um 07:25:13 Uhr
Goto Top
Zitat von @colinardo:

Gut dann können wir ja jetzt das Beschweren hier beenden. Das richtest du bitte besser direkt an Microsoft selber, hier bringt das nichts. Merci.

Ja sorry, bin nur gerade etwas gefrustet, weil die Einrichtung des Win10-SchülerPC-Musters so unendlich aufwändiger ist als damals die des Win7-Rechners. Und der größte Teil der Arbeit entfällt darauf, irgendwelchen überflüssigen, aber vorinstallierten Kram zu deinstallieren/deaktivieren, um eine vernünftige Benutzeroberfläche für die Schüler zu bekommen. Sofern das überhaupt möglich ist (siehe Win10-Hintergrundbild Anmeldung soll nicht Bild des Sperrbildschirms sein (per Registry))
honeybee
honeybee 24.08.2018 um 11:28:06 Uhr
Goto Top
Hallo,

ist das Powershell-Skript noch aktuell? Bei mir sieht es so aus:

screenshot
colinardo
colinardo 24.08.2018 aktualisiert um 11:38:16 Uhr
Goto Top
Moin.
Zitat von @honeybee:
ist das Powershell-Skript noch aktuell? Bei mir sieht es so aus:

screenshot
Jepp, ist aktuell und funktioniert weiterhin einwandfrei!
Wenn du diese Meldung bekommst, ist die APP nicht am Startmenü angepinnt, hab da nur im Kommentar-Text das zusätzliche 'unpinned' nicht eingetragen ist also nur ein Fehlermeldungs-Missverständnis, das ich oben jetzt durch Korrektur der Fehlermeldung ausgeräumt haben sollte face-smile.

Grüße Uwe
honeybee
honeybee 24.08.2018 um 12:09:20 Uhr
Goto Top
Sie ist angepinnt und trotzdem nicht weg...

unbenannt
colinardo
colinardo 24.08.2018 aktualisiert um 12:14:54 Uhr
Goto Top
Dann befindest du dich mit deiner Powershell-Konsole nicht im aktuellen Userkontext. Wurde hier mit der aktuellsten 1803 getestet und funktioniert problemlos. Du futelst da einfach falsch face-smile, oder dein Startmenü is out of sync mit dem o.g. Ordner denn du natürlich hoffentlich mal aufgemacht hast.
honeybee
honeybee 24.08.2018 aktualisiert um 12:30:11 Uhr
Goto Top
Ich verwende die aktuelle Windows-Version.

bild1

Der Ordner war geöffnet... oder wie soll ich das Startmenü mit dem Ordner synchroniseren?

bild2

Und trotzdem sieht es so aus:

bild4

Habe das sogar auf einem frisch installierten Windows ausprobiert. Es geht nix.
colinardo
colinardo 24.08.2018 aktualisiert um 12:39:25 Uhr
Goto Top
Das das nur für ein Deutsches oder Englisches System gedacht ist hast du beachtet (Regex im Skript in Zeile 6 muss angepasst werden wenn es eine andere Sprache ist)?

Nöp, cleanes System Windows 10 Enterprise (aktuellstes Build):

screenshot

Muss also an deinen Kisten liegen.
honeybee
honeybee 24.08.2018 um 12:43:28 Uhr
Goto Top
Ich habe es rausgefunden:

Mit der normalen Powershell geht es nicht.
Mit der Powershell ISE geht es.

Danke!
colinardo
colinardo 24.08.2018 aktualisiert um 12:49:19 Uhr
Goto Top
Zitat von @honeybee:

Ich habe es rausgefunden:

Mit der normalen Powershell geht es nicht.
Mit der Powershell ISE geht es.
Nöp., es gibt keine "normale" Powershell und unnormale Powershell, beide sind gleich, ist nur eine GUI drum herum.
Hier in einer ganz normalen Konsole.
screenshot
Wie schon gesagt deine Konsole in der du es ausführst hat zu 99,9% den falschen User-Kontext.
loonydeluxe
loonydeluxe 19.11.2018 aktualisiert um 18:33:51 Uhr
Goto Top
Um nochmal drauf zurückzukommen... in Windows 10 1809 funktioniert das (mal wieder) nicht richtig.

Für die vorherigen Versionen (ich meine, ich hatte das mit Windows 10 1709 geschrieben, edit: aus verschiedenen Quellen zusammengetragen face-wink) hat das Folgende als DLL mit PowerShell-Aufruf gut funktioniert. In Windows 10 1809 kann ich damit immerhin noch Symbole von der Taskleiste unpinnen sowie ans Startmenü pinnen sowie entpinnen, sowohl für Modern Apps als auch Win32. Was nicht funktioniert, ist an die Taskleiste anzupinnen.

Der "Hack", dass sich die PowerShell als Explorer.exe ausgibt, um die Verben des Kontextmenüs zu sehen, ist mit drin. Die neuen Verben fürs Windows 8-Startmenü und höher sind ebenfalls mit drin.

Vielleicht hat einer ne Idee, wie sich das wieder hinbiegen lässt?

Erstmal der Aufruf (Pfad zur DLL und Name der Verknüpfung ggf. anpassen). Parameter 1: Name der Verknüpfung, Parameter 2: pinnen ($true) bzw. unpinnen ($false), Paramter 3: Taskbar ($true) oder Startmenü ($false).
Add-Type -Path "D:\PinTo10DLL.dll"  
$p = [Pinto10DLL.Utils]::PinUnpinTaskbar("Microsoft Edge", $false, $true);  
$p = [Pinto10DLL.Utils]::PinUnpinTaskbar("Outlook 2013", $false, $true);  

Code für die DLL:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Pinto10DLL
{
    static public class Utils
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]  
        internal static extern IntPtr LoadLibrary(string lpLibFileName);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]  
        internal static extern int LoadString(IntPtr hInstance, uint wID, StringBuilder lpBuffer, int nBufferMax);

        public static bool PinUnpinTaskbar(string filePath, bool pin, bool taskbar=true)
        {
            bool success = false;
            try
            {
                ChangeImagePathName("explorer.exe");  

                bool isModern = !(filePath.StartsWith(@"\\") || filePath.Substring(1).StartsWith(@":"));  



                if (!isModern && !File.Exists(filePath)) throw new FileNotFoundException(filePath);
                int MAX_PATH = 255;
                // 5386 is the DLL index for"Pin to Tas&kbar", ref. http://www.win7dll.info/shell32_dll.html 
                //uncomment the following line to pin to start instead
                //actionIndex = pin ? 51201 : 51394;
                var actionIndex = 0;
                if (taskbar) { actionIndex = pin ? 5386 : 5387; }
                
                Version os = Environment.OSVersion.Version;
                if (!taskbar && (os.Major == 6 && os.Minor == 1))
                {
                    actionIndex = pin ? 5381 : 5382;
                }
                else if (!taskbar)
                {
                    actionIndex = pin ? 51201 : 51394;
                }


                StringBuilder szPinToStartLocalized = new StringBuilder(MAX_PATH);
                IntPtr hShell32 = LoadLibrary("Shell32.dll");  
                LoadString(hShell32, (uint)actionIndex, szPinToStartLocalized, MAX_PATH);
                string localizedVerb = szPinToStartLocalized.ToString();

                string path = isModern ? "shell:AppsFolder" : Path.GetDirectoryName(filePath);  
                string fileName = isModern ? filePath.Trim().ToUpper() : Path.GetFileName(filePath);

                //Console.WriteLine(filePath + ": " + isModern.ToString()); 
//                Console.WriteLine(path + "\\" + fileName); 
//                Console.ReadLine();

                // create the shell application object
                dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));  
                dynamic directory = shellApplication.NameSpace(path);
                dynamic link = null; // directory.ParseName(fileName);



                if (!isModern)
                    link = directory.ParseName(fileName);
                else
                {
                    //// modern apps ////
                    var items = directory.Items;
                    foreach (var item in items)
                    {
                        if (item.Name.ToUpper() == fileName.ToUpper())
                        {
                            link = item;
                            break;
                        }
                    }
                    //// modern apps ////
                }


                dynamic verbs = link.Verbs();
                string verbstring = "";  
                for (int i = 0; i < verbs.Count(); i++)
                {
                    dynamic verb = verbs.Item(i);
                    var name = verb.Name;
                    if (verb.Name.Equals(localizedVerb))
                    {
//                        verbstring += "!!!"; 
                        verb.DoIt();
                        success = true;
                    }

                    verbstring += name + Environment.NewLine;
                    

                }
//                Console.WriteLine(verbstring);
//                Console.ReadLine();

            }
            catch
            {
                success=false;
            }
            finally {
                RestoreImagePathName();
            }
            return success;
        }

        static string originalImagePathName;
        static int unicodeSize = IntPtr.Size * 2;
        static bool changed;
        static void GetPointers(out IntPtr imageOffset, out IntPtr imageBuffer)
        {
            IntPtr pebBaseAddress = GetBasicInformation().PebBaseAddress;
            var processParameters = Marshal.ReadIntPtr(pebBaseAddress, 4 * IntPtr.Size);
            imageOffset = processParameters.Increment(4 * 4 + 5 * IntPtr.Size + unicodeSize + IntPtr.Size + unicodeSize);
            imageBuffer = Marshal.ReadIntPtr(imageOffset, IntPtr.Size);
        }
        internal static void ChangeImagePathName(string newFileName)
        {
            IntPtr imageOffset, imageBuffer;
            GetPointers(out imageOffset, out imageBuffer);

            //Read original data
            var imageLen = Marshal.ReadInt16(imageOffset);
            originalImagePathName = Marshal.PtrToStringUni(imageBuffer, imageLen / 2);

            var newImagePathName = Path.Combine(Path.GetDirectoryName(originalImagePathName), newFileName);
            if (newImagePathName.Length > originalImagePathName.Length) throw new Exception("new ImagePathName cannot be longer than the original one");  

            //Write the string, char by char
            var ptr = imageBuffer;
            foreach (var unicodeChar in newImagePathName)
            {
                Marshal.WriteInt16(ptr, unicodeChar);
                ptr = ptr.Increment(2);
            }
            changed = true;
            Marshal.WriteInt16(ptr, 0);

            //Write the new length
            Marshal.WriteInt16(imageOffset, (short)(newImagePathName.Length * 2));
        }
        internal static void RestoreImagePathName()
        {
            if (!changed) return;
            IntPtr imageOffset, ptr;
            GetPointers(out imageOffset, out ptr);

            foreach (var unicodeChar in originalImagePathName)
            {
                Marshal.WriteInt16(ptr, unicodeChar);
                ptr = ptr.Increment(2);
            }
            Marshal.WriteInt16(ptr, 0);
            Marshal.WriteInt16(imageOffset, (short)(originalImagePathName.Length * 2));
        }
        public static ProcessBasicInformation GetBasicInformation()
        {
            uint status;
            ProcessBasicInformation pbi;
            int retLen;
            var handle = System.Diagnostics.Process.GetCurrentProcess().Handle;
            if ((status = NtQueryInformationProcess(handle, 0,
                out pbi, Marshal.SizeOf(typeof(ProcessBasicInformation)), out retLen)) >= 0xc0000000)
                throw new Exception("Windows exception. status=" + status);  
            return pbi;
        }
        public static IntPtr Increment(this IntPtr ptr, int value)
        {
            unchecked
            {
                if (IntPtr.Size == sizeof(Int32))
                    return new IntPtr(ptr.ToInt32() + value);
                else
                    return new IntPtr(ptr.ToInt64() + value);
            }
        }
        [DllImport("ntdll.dll")] public static extern uint NtQueryInformationProcess([In] IntPtr ProcessHandle, [In] int ProcessInformationClass, [Out] out ProcessBasicInformation ProcessInformation, [In] int ProcessInformationLength, [Out] [Optional] out int ReturnLength);  
        [StructLayout(LayoutKind.Sequential)] public struct ProcessBasicInformation
        {
            public uint ExitStatus;
            public IntPtr PebBaseAddress;
            public IntPtr AffinityMask;
            public int BasePriority;
            public IntPtr UniqueProcessId;
            public IntPtr InheritedFromUniqueProcessId;
        }
    }
}
pcgh09
pcgh09 13.06.2019 um 07:32:02 Uhr
Goto Top
Hallo Uwe,

leider funktioniert diese PowerShell Funktion unter Windows 10 Pro 1903 nicht mehr. Wir haben diese Funktion bisher bei jeder unserer Windows 10 Installation genutzt, weil es genau unsere Zwecke erfüllt dem User ein paar spezifische Apps für die Arbeitszwecke anzupinnen

Der Systemordner shell:::{4234d49b-0245-4df3-b780-3893943456e1} ist noch vorhanden.

PowerShell gibt als Fehler 0x8007005 (E_ACCESSDENIED) in Zeile 17 bei "$_.DoIt()" zurück.

Wäre toll wenn hier eine Anpassung möglich wäre um diese tolle Tool weiter zu benutzen.

Andere haben auch das Problem https://www.tenforums.com/customization/133814-unpin-pin-ps1-does-not-wo ...
colinardo
colinardo 13.06.2019, aktualisiert am 27.06.2021 um 11:52:27 Uhr
Goto Top
Servus,
seit Windows 10 1903, gibt es einige tiefgreifende Änderungen für das Startmenü. So ist z.B. das Startmenü jetzt eine eigene "App" mit eigenem Prozess(StartMenuExperienceHost.exe), wohingegen es früher ein Subprocess der ShellExperienceHost.exe war. Hier hat es wohl grundlegende Berechtigungsänderungen gegeben so das der Pin-Vorgang nicht mehr aus jedem beliebigen Prozess aus getriggert werden kann.
Die nötigen Maßnahmen dafür konnte ich noch nicht evaluieren, das wird etwas länger dauern und erfordert grundlegenden Umbau und im Moment habe ich für dieses Katz und Maus Spiel von MS nicht die Zeit.
Für den Übergang kannst du dich aber an diesem Kommandozeilen-Tool bedienen(auf eigene Gefahr), das die Änderungen wohl schon in Code umgesetzt hat:
http://www.technosys.net/products/utils/pintotaskbar#s1
Und noch etwas mit Quellcode
https://github.com/0x546F6D/pttb_-_Pin_To_TaskBar
Die noch funktionierende Methode baut auf PE-Code-Injection in den Progman-Prozess auf, da MS die vorherigen Methoden nach und nach raus gepatcht hat um zu verhindern das jemand per Kommandozeile Programme anpinnt.

Grüße Uwe
DerWoWusste
DerWoWusste 13.06.2019 aktualisiert um 18:51:11 Uhr
Goto Top
Hallo.

Ich hatte die pintotaskbar (syspin.exe) in älterer Version mal irgendwann angeschaut. Auch die war bei Virustotal.com auffällig. Die jetzige gibt zwar noch kein eindeutiges Bild ab (2 Erkennungen), aber Vorsicht ist geboten: https://www.virustotal.com/gui/file/b666a7cc06be4ab39bdc930d26aed0e164bc ...
Dort sind auch Sandbox-Begutachtungen abgelegt.