Пользовательский командлет PSHost, Get-Credential в C#

Я пытаюсь реализовать пользовательский класс PSHost с целью создания графического интерфейса для сценариев Power Shell. Я взял этот код за основу и начал адаптировать его для проектов с графическим интерфейсом https://msdn.microsoft.com/en-us/library/ee706557%28v=vs.85%29.aspx

Все было хорошо, пока я не попытался выполнить командлет Get-Credential и не получил ошибку, что метод не реализован (ooouuuu)... Исходный код был этими двумя методами:

public override PSCredential PromptForCredential(
                                                 string caption, 
                                                 string message, 
                                                 string userName, 
                                                 string targetName)
{
  throw new NotImplementedException(
                       "The method or operation is not implemented.");
}

public override PSCredential PromptForCredential(
                                   string caption, 
                                   string message, 
                                   string userName, 
                                   string targetName, 
                                   PSCredentialTypes allowedCredentialTypes, 
                                   PSCredentialUIOptions options)
{
  throw new NotImplementedException(
                          "The method or operation is not implemented.");
}

Поэтому после некоторого исследования я реализовал диалог следующим образом:

    [DllImport("ole32.dll")]
    public static extern void CoTaskMemFree(IntPtr ptr);

    [DllImport("credui.dll", CharSet = CharSet.Auto)]
    private static extern int CredUIPromptForWindowsCredentials(ref CREDUI_INFO notUsedHere, int authError, ref uint authPackage, IntPtr InAuthBuffer, uint InAuthBufferSize, out IntPtr refOutAuthBuffer, out uint refOutAuthBufferSize, ref bool fSave, int flags);

    [DllImport("credui.dll", CharSet = CharSet.Auto)]
    private static extern bool CredUnPackAuthenticationBuffer(int dwFlags, IntPtr pAuthBuffer, uint cbAuthBuffer, StringBuilder pszUserName, ref int pcchMaxUserName, StringBuilder pszDomainName, ref int pcchMaxDomainame, StringBuilder pszPassword, ref int pcchMaxPassword);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct CREDUI_INFO
    {
        public int cbSize;
        public IntPtr hwndParent;
        public string pszMessageText;
        public string pszCaptionText;
        public IntPtr hbmBanner;
    }

    private enum PromptForWindowsCredentialsFlags
    {
        ...
    }

    public override PSCredential PromptForCredential(
                                       string caption,
                                       string message,
                                       string userName,
                                       string targetName,
                                       PSCredentialTypes allowedCredentialTypes,
                                       PSCredentialUIOptions options)
    {
        CREDUI_INFO credui = new CREDUI_INFO();
        credui.pszCaptionText = "Please enter the credentails";
        credui.pszMessageText = "DisplayedMessage";
        credui.cbSize = Marshal.SizeOf(credui);
        uint authPackage = 0;
        IntPtr outCredBuffer = new IntPtr();
        uint outCredSize;
        bool save = false;
        int result = CredUIPromptForWindowsCredentials(ref credui, 0, ref authPackage, IntPtr.Zero, 0, out outCredBuffer, out outCredSize, ref save, 1 /* Generic */);

        var usernameBuf = new StringBuilder(100);
        var passwordBuf = new StringBuilder(100);
        var domainBuf = new StringBuilder(100);

        int maxUserName = 100;
        int maxDomain = 100;
        int maxPassword = 100;
        if (result == 0)
        {
            if (CredUnPackAuthenticationBuffer(0, outCredBuffer, outCredSize, usernameBuf, ref maxUserName, domainBuf, ref maxDomain, passwordBuf, ref maxPassword))
            {
                //clear the memory allocated by CredUIPromptForWindowsCredentials
                CoTaskMemFree(outCredBuffer);
                SecureString secureString = new SecureString();
                foreach (char c in passwordBuf.ToString())
                {
                    secureString.AppendChar(c);
                }
                return new PSCredential(usernameBuf.ToString(), secureString);
            }
        }
        return null;
    }

Это работает нормально, но есть одна загвоздка: при запуске командлета Get-Credential он запрашивает значение для параметра Credential, независимо от того, вводится ли после нажатия кнопки return, всплывает диалоговое окно и все работает, как и ожидалось. Делая то же самое в ISE, диалоговое окно выскакивает непосредственно без любой подсказки. Я думал, что это было связано с аргументами метода, но сделать их необязательными путем определения значения по умолчанию не имеет значения. Это может быть связано с тем, как командлет определен в среде PS, поэтому у меня такой вопрос: как я могу сделать так, чтобы при запуске командлета переходил прямо к диалогу? Можно ли определить значение по умолчанию для параметра Credential для привязки? Я предполагаю, что это то, как ISE обходит это, или я что-то упустил?

1 ответ

Решение

Я потратил некоторое время и экспериментировал, но обнаружил, что обязательная привязка параметров обрабатывается public override Dictionary<string, PSObject> Prompt(string caption, string message, Collection<FieldDescription> descriptions) метод в интерфейсе PSHostUserInterface до PromptForCredential называется.

Другие вопросы по тегам