簡體   English   中英

從 C# 中運行完整的 powershell 腳本

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM