[英]Running a complete powershell script from within C#
我目前正在用 C# 構建一個小型應用程序,以在暫存后協助配置我們公司的計算機。 這是為了自動執行我們目前無緣無故手動執行的一些任務。
我想要自動化的步驟之一是將項目固定到 Windows 10 中的任務欄。我們目前使用以下 powershell 腳本,它可以執行此任務:
Param($Target)
$KeyPath1 = "HKCU:\SOFTWARE\Classes"
$KeyPath2 = "*"
$KeyPath3 = "shell"
$KeyPath4 = "{:}"
$ValueName = "ExplorerCommandHandler"
$ValueData = (Get-ItemProperty("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\" +
"Explorer\CommandStore\shell\Windows.taskbarpin")).ExplorerCommandHandler
$Key2 = (Get-Item $KeyPath1).OpenSubKey($KeyPath2, $true)
$Key3 = $Key2.CreateSubKey($KeyPath3, $true)
$Key4 = $Key3.CreateSubKey($KeyPath4, $true)
$Key4.SetValue($ValueName, $ValueData)
$Shell = New-Object -ComObject "Shell.Application"
$Folder = $Shell.Namespace((Get-Item $Target).DirectoryName)
$Item = $Folder.ParseName((Get-Item $Target).Name)
$Item.InvokeVerb("{:}")
$Key3.DeleteSubKey($KeyPath4)
if ($Key3.SubKeyCount -eq 0 -and $Key3.ValueCount -eq 0) {
$Key2.DeleteSubKey($KeyPath3)
}
我試圖在 C# 中導入這個 powershellscript,但似乎無法從函數中調用它。 當我嘗試時,windows 會拋出各種錯誤,並且項目沒有固定到任務欄。
我已經完成了完整的白痴路徑並嘗試使用字符串輸入它,然后使用帶有這些字符串的 Powershell.Create() 來復制腳本的作用,但無濟於事。
有沒有人看到某種方法可以將此腳本的功能轉換為 C#? 或者知道在 Windows 10 中將項目固定到任務欄的替代方法嗎?
注意:我希望應用程序保持獨立 - 所以我無法調用網絡文件。 如果可能,我還希望將其作為便攜式 exe(使用 Costura.Fody NuGet 包),因此我希望避免將腳本放在同一文件夾中並從 C# 運行它,或者使用安裝程序。
編輯:找到了一個可行的,盡管相當愚蠢的解決方案。 我創建了一個包含上述腳本的 string[],然后使用 streamwriter 創建一個包含整個腳本的 .ps1,並使用 Powershell.create 執行它。 然后我使用另一個 powershell.create 來刪除該文件。 笨重,但可行。
Pintotaskbar.cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Management.Automation;
using System.Text;
namespace ConfiguratorLibrary
{
public class PinToTaskBar
{
private string itempath;
public string pinitem
{
get { return itempath; }
set { itempath = value; }
}
public string Pin()
{
string[] psscript = { "$Target = ("+"\u0022" + $"{itempath}" + "\u0022" + ")" , "\n\r" , "$KeyPath1 = " + "\"HKCU:\\SOFTWARE\\Classes\"", "$KeyPath2 = " +"\"*\"", "$KeyPath3 = " + "\"shell\"",
"$KeyPath4 = " + "\"{:}\"", "$ValueName = " + "\"ExplorerCommandHandler\"", "$ValueData = (Get-ItemProperty(" + "\"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\\"" + " + " + "\"Explorer\\CommandStore\\shell\\Windows.taskbarpin\"" + ")).ExplorerCommandHandler",
"\r\n", "$Key2 = (Get-Item $KeyPath1).OpenSubKey($KeyPath2, $true)", "$Key3 = $Key2.CreateSubKey($KeyPath3, $true)","$Key4 = $Key3.CreateSubKey($KeyPath4, $true)","$Key4.SetValue($ValueName, $ValueData)", "\r\n",
"$Shell = New-Object -ComObject " + "\"Shell.Application\"", "$Folder = $Shell.Namespace((Get-Item $Target).DirectoryName)","$Item = $Folder.ParseName((Get-Item $Target).Name)",
"$Item.InvokeVerb("+ "\"{:}\"" + ")", "\r\n", "$Key3.DeleteSubKey($KeyPath4)", "if ($Key3.SubKeyCount -eq 0 -and $Key3.ValueCount -eq 0) {","$Key2.DeleteSubKey($KeyPath3)",
"}"};
StringBuilder psout = new StringBuilder();
using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\pinitemstotaskbar.ps1"))
{
foreach (string line in psscript)
{
file.WriteLine(line);
}
}
using (PowerShell ps = PowerShell.Create())
{
ps.AddScript(@"cd C:\");
ps.AddScript("Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force ");
ps.AddScript(@".\pinitemstotaskbar.ps1");
ps.AddScript("rm pinitemstotaskbar.ps1");
Collection<PSObject> scriptoutput = ps.Invoke();
foreach (PSObject psoutp in scriptoutput)
{
if(psoutp != null)
{
psout.Append(psoutp);
}
else
{
psout.Append("Error");
}
}
String output = psout.ToString();
return output;
}
}
}
}
忘記提及:此方法采用您希望作為字符串輸入的應用程序的 .exe 的完整路徑。
例如,輸入字符串@"C:\\Windows\\system32\\Notepad.exe" 作為屬性 Pinitem 然后運行該方法會將記事本固定到任務欄。 我目前正在使用它來將默認用戶應用程序固定到任務欄,例如 Outlook、Word 和 Excel - 以及一些公司特定的應用程序。
以下解決方案與您發布的 PowerShell 代碼段完全不同。
概述:功能存在於類 PinToTaskBar 中並從類程序調用
注意:如聊天中所述,尚未經過測試
PS:當我訪問 VS 時,我會嘗試將您的 PS 轉換為 C#
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Management.Automation;
using System.Text;
namespace ConfiguratorLibrary
{
public class PinToTaskBar
{
[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)
{
if (!File.Exists(filePath)) throw new FileNotFoundException(filePath);
int MAX_PATH = 255;
var actionIndex = pin ? 5386 : 5387; // 5386 is the DLL index for"Pin to Tas&kbar", ref. win7dll.info/shell32_dll.html
//uncomment the following line to pin to start instead
//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 = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
// create the shell application object
dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
dynamic directory = shellApplication.NameSpace(path);
dynamic link = directory.ParseName(fileName);
dynamic verbs = link.Verbs();
for (int i = 0; i < verbs.Count(); i++)
{
dynamic verb = verbs.Item(i);
if (verb.Name.Equals(localizedVerb))
{
verb.DoIt();
return true;
}
}
return false;
}
static string originalImagePathName;
static int unicodeSize = IntPtr.Size * 2;
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);
}
Marshal.WriteInt16(ptr, 0);
//Write the new length
Marshal.WriteInt16(imageOffset, (short) (newImagePathName.Length * 2));
}
internal static void RestoreImagePathName()
{
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;
}
[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
);
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);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct ProcessBasicInformation
{
public uint ExitStatus;
public IntPtr PebBaseAddress;
public IntPtr AffinityMask;
public int BasePriority;
public IntPtr UniqueProcessId;
public IntPtr InheritedFromUniqueProcessId;
}
}
class TestClass
{
static void Main(string[] args)
{
var pin = new PinToTaskBar();
try
{
pin.ChangeImagePathName("explorer.exe");
pin.PinUnpinTaskbar(tempFilePath, true);
}
Catch(Exception ex)
{
Console.WriteLine(ex.Message + ex.InnerException);
}
finally
{
pin.RestoreImagePathName();
}
}
}
}
編輯:我發現參考了一篇解釋所用功能的文章
http://alexweinberger.com/main/pinning-network-program-taskbar-programmatically-windows-10/
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.