未知错误 - cmdlet Invoke-SCScript

时间:2015-01-13 11:32:23

标签: c# powershell wsh cmdlet

所以这就是我所拥有的(完整的加载项代码):

using Microsoft.SystemCenter.VirtualMachineManager;
using Microsoft.SystemCenter.VirtualMachineManager.UIAddIns;
using Microsoft.SystemCenter.VirtualMachineManager.UIAddIns.ContextTypes;
using Microsoft.VirtualManager.Remoting;
using System;
using System.AddIn;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Forms;

namespace Microsoft.VirtualManager.UI.AddIns.BackupAddIn
{
[AddIn("Make Backup")]
public class BackupAddIn : ActionAddInBase
{
    protected const string PowershellPath = "%WINDIR%\\System32\\WindowsPowershell\\v1.0\\powershell.exe";
    protected const string DefaultDirectory = "C:\\ClusterStorage\\Volume2";
    protected const string WildcardVMName = "{{VMName}}";
    protected const string WildcardError = "{{Error}}";
    protected const string BackupDirectoryBase = "{{Base}}";
    protected const string BackupDirectoryName = "{{Name}}";
    protected const string BackupDirectoryDate = "{{Date}}";

    public enum JobState { Initialized, Success, Fail };

    protected const string BackupDirectoryTemplate = BackupDirectoryBase + "\\" + BackupDirectoryName + "\\" + BackupDirectoryDate + "\\";
    protected static readonly ReadOnlyCollection<string> AllowedBackupStates = new ReadOnlyCollection<string>(new string[] { "PowerOff", "Paused"/*, "Saved"*/});

    public override bool CheckIfEnabledFor(IList<ContextObject> contextObjects)
    {
        if (contextObjects != null && contextObjects.Count > 0)
        {
            foreach (var host in contextObjects.OfType<HostContext>())
            {
                if (host.ComputerState != ComputerState.Responding)
                {
                    return false;
                }
            }
            return true;
        }

        return false;
    }

    public override void PerformAction(IList<ContextObject> contextObjects)
    {
        if (contextObjects != null)
        {
            // check if we have VMs selected
            var VMs = contextObjects.OfType<VMContext>();
            if (VMs != null && VMs.Count() > 0)
            {
                // check if VMs are in a good state
                var badVMs = VMs.Where(vm => AllowedBackupStates.Contains(vm.Status.ToString()) == false).ToArray();
                if (badVMs != null && badVMs.Length > 0)
                {
                    MessageBox.Show("Backup not possible!\r\nThe following VMs are still running:\r\n\r\n" + string.Join(", ", badVMs.Select(vm => vm.Name)));
                }
                else
                {
                    // ask for backup directory
                    string backupDir = Microsoft.VisualBasic.Interaction.InputBox("Enter a path on the host to export the selected virtual machine(s) to.", "Export path", DefaultDirectory);
                    if (string.IsNullOrEmpty(backupDir) == false)
                    {
                        if (backupDir.EndsWith("\\"))
                        {
                            backupDir = backupDir.Substring(0, backupDir.Length - 1);
                        }

                        // go
                        /*foreach (var vm in VMs)
                        {
                            exportVM(vm, backupDir);
                        }*/

                        // testing to export multiple vms in one invoke
                        exportVMs(VMs, backupDir);
                    }
                }
            }
        }
    }

    public string getDate()
    {
        var date = DateTime.Now;
        return date.Year.ToString()
            + (date.Month < 10 ? "0" : "") + date.Month.ToString()
            + (date.Day < 10 ? "0" : "") + date.Day.ToString()
            + "_"
            + (date.Hour < 10 ? "0" : "") + date.Hour.ToString()
            + (date.Minute < 10 ? "0" : "") + date.Minute.ToString();
    }

    public void ManageJob(string name, JobState state, string message = null)
    {
        string command;
        if (state == JobState.Initialized)
        {
            command = string.Format("New-SCExternalJob -Name \"{0}\"", name);
        }
        else if (state == JobState.Success)
        {
            command = string.Format("Set-SCExternalJob -Job (Get-SCJob -Name \"{0}\")[0] -Complete -InfoMessage \"" + (string.IsNullOrEmpty(message) ? "Backup successfully started." : message.Replace("\"", "'")) + "\"", name);
        }
        else
        {
            command = string.Format("Set-SCExternalJob -Job (Get-SCJob -Name \"{0}\")[0] -Failed -InfoMessage \"" + (string.IsNullOrEmpty(message) ? "Backup FAILED." : message.Replace("\"", "'")) + "\"", name);
        }

        //MessageBox.Show(command);

        PowerShellContext.ExecuteScript<Host>(
            command,
            (profiles, error) =>
            {
                if (error != null)
                {
                    MessageBox.Show("Cannot modify job state\r\nError: " + error.Problem);
                }
            }
        );
    }

    public void exportVMs(IEnumerable<VMContext> VMs, string backupDir)
    {
        string date = getDate();
        string VMS = "";
        string fullBackupDirS = BackupDirectoryTemplate.Replace(BackupDirectoryBase, backupDir).Replace(BackupDirectoryName, "_VMBackups").Replace(BackupDirectoryDate, date);
        VMS = "'" + string.Join("', '", VMs.Select(vm => vm.Name).ToArray()) + "'";
        string command = string.Format("Export-VM -Name {0} -Path '{1}'", VMS, fullBackupDirS);
        MessageBox.Show(command);

        // We need to manager jobs in another thread probably --------------------------------------------------------------!!!
        string jobname = "Starting_backup_of_multiple_machines";

        mkShortcuts(backupDir, date, VMs.Select(vm => vm.Name).ToArray(), VMs.First());
        //!            execPSScript(jobname, scvmmPsCommand(command, VMs.First()), VMs.First(), WildcardVMName + ": Backup successful.", WildcardVMName + ": Backup FAILED!\r\nError: " + WildcardError, backupDir, date, VMs.Select(vm => vm.Name).ToArray());
    }

    public String scvmmPsCommand(string command, VMContext vm, string appPath = PowershellPath)
    {
        return string.Format("Invoke-SCScriptCommand -Executable {0} -VMHost (Get-SCVMHost -ID \"{1}\") -CommandParameters \"{2}\" -RunAsynchronous -TimeoutSeconds 360000", appPath, vm.VMHostId.ToString(), command);
    }

    // Make a shortcut from the machines backup directory to the backup in the "_VMBackups"-folder
    public void mkShortcuts(string path, string date, string[] names, VMContext vm)
    {
        string command = "$shell = New-Object -ComObject WScript.Shell;";
        foreach (var n in names)
        {
            command = command + string.Format(" $shortc = $shell.CreateShortcut('{0}\\{1}\\{2}.lnk'); $shortc.TargetPath = '{0}\\_VMBackup\\{2}\\{1}'; $shortc.Save();", path, n, date);
        }
        string fullCommand = scvmmPsCommand(command, vm);
        MessageBox.Show(fullCommand);
        execPSScript("Create_ShortcutS", fullCommand, vm, "Shortcut(s) created.", "FAILED to create Shortcut(s)!");
    }

    public void execPSScript(string jobname, string command, VMContext vm, string successMessage, string errorMessage, string path = "", string date = "", string[] names = null)
    {
        ManageJob(jobname, JobState.Initialized);
        PowerShellContext.ExecuteScript<Host>(
            command,
            (vms, error) =>
            {
                if (error != null)
                {
                    ManageJob(jobname, JobState.Fail, errorMessage.Replace(WildcardVMName, vm.Name).Replace(WildcardError, error.Problem));
                }
                else
                {
                    ManageJob(jobname, JobState.Success, successMessage.Replace(WildcardVMName, vm.Name));
                    if (string.IsNullOrEmpty(path) == false)
                    {
                        //mkShortcuts(path, date, names, vm);
                    }
                }
            }
        );
    }
}
}

当我运行插件时,我得到一个错误框,表示如下:&#34;未知脚本错误。表达未关闭 - &#34;)&#34;丢失。

+ ... andard'; .Save();
+                    ~
An expression was expected after '('.
+ CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordEx 
   ception
+ FullyQualifiedErrorId : ExpectedExpression". Weitere Informationen finden Sie im Standardfehlerprotokoll "C:\Windows\TEMP\gce_stderrord07b04547c74493caa6bdba9087df444.log".

但我无法在我的代码中找到错误,因为它在手动输入到主机上的powershell时起作用,而.Save()不带参数。你有什么想法吗?

1 个答案:

答案 0 :(得分:0)

好的,而不是

command2 = command2 + string.Format(" ${1} = $shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); ${1}.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; ${1}.Save();", backupDir, vm.Name, date);

尝试

command2 = command2 + string.Format(" $shortc = $shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); $shortc.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; $shortc.Save();", backupDir, vm.Name, date);
command2 = "'" + command2 + "'"

这不起作用,也没有做很多其他事情。 基本上,问题是命令字符串传递给PowerShell执行的方式。 PowerShell将检查sting并尝试枚举所有变量。这不是理想的行为。试图将整个字符串用单引号括起来,但这并没有帮助。 最后,解决方案是使用PowerShell转义字符“`”来掩盖所有变量名称。

command2 = command2 + string.Format(" `$shortc = `$shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); `$shortc.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; `$shortc.Save();", backupDir, vm.Name, date);