繁体   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