Help us understand the problem. What is going on with this article?

Windows10でBASIC認証画面にIDとパスワードを自動入力するソフトを作った

この記事の内容は、ライブラリやソフトのダウンロードなしで使えます。

やれること

下のようなWindowがでているときに本記事のプログラムを実行すると、指定のIDとパスワードを自動で入力します。

image.png

C#のコンパイル

このあたりが参考になるかと。
https://qiita.com/yuki451/items/c9c6a2b79ce96ae0d252

今回のソースに関しては、下記のようにアセンブリのパスを指定する(環境に依存するかも)。

csc /r:C:\Windows\Microsoft.NET\assembly\GAC_MSIL\UIAutomationClient\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationClient.dll ^
/r:C:\Windows\Microsoft.NET\assembly\GAC_MSIL\UIAutomationTypes\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationTypes.dll ^
/r:C:\Windows\Microsoft.NET\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll ^
Test.cs

ソースコード

Test.cs
using System;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using System.Windows.Automation;


public static class NativeMethods
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpszClass, string lpszWindow);

    public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public extern static bool EnumWindows(EnumWindowsDelegate lpEnumFunc,   IntPtr lparam);
}

public class Class1
{
    static AutomationElement FindCredentialWindow()
    {
        IntPtr hWnd = NativeMethods.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Credential Dialog Xaml Host", "Windows セキュリティ");

        if ( hWnd == IntPtr.Zero ) {
            return null;
        }

        return AutomationElement.FromHandle(hWnd);
    }

    static string FindTextBlockMatches(AutomationElement aeForm, Regex r)
    {
        var elems = FindElementsByClassName(aeForm, "TextBlock");
        foreach ( AutomationElement elem in elems ) {
            var elemInfo = elem.Current;
            if ( r.IsMatch(elemInfo.Name) ) {
                return elemInfo.Name;
            }
        }
        return null;
    }

    static void InputToCredentialWindow(AutomationElement aeForm, string userId, string password)
    {
        AutomationElement aeUserId   = FindElementById(aeForm, "EditField_0");
        AutomationElement aePassword = FindElementById(aeForm, "PasswordField_1");
        AutomationElement aeOkButton = FindElementById(aeForm, "OkButton");

        if ( aeUserId != null && aePassword != null && aeOkButton != null ) {
            var vpUserId   = (ValuePattern)aeUserId.GetCurrentPattern(ValuePattern.Pattern);
            var vpPassword = (ValuePattern)aePassword.GetCurrentPattern(ValuePattern.Pattern);
            vpUserId.SetValue(userId);
            vpPassword.SetValue(password);

//  OKも自動で押す場合はこれ
//            var ipOkButton = (InvokePattern)aeOkButton.GetCurrentPattern(InvokePattern.Pattern);
//            ipOkButton.Invoke();
        }
    }

    static AutomationElement FindElementById(AutomationElement rootElement, string automationId)
    {
        var cond = new PropertyCondition(AutomationElement.AutomationIdProperty, automationId);
        return rootElement.FindFirst(TreeScope.Descendants, cond);
    }

    static AutomationElementCollection FindElementsByClassName(AutomationElement rootElement, string className)
    {
        var cond = new PropertyCondition(AutomationElement.ClassNameProperty, className);
        return rootElement.FindAll(TreeScope.Descendants, cond);
    }

    [STAThread]
    static void Main()
    {
        AutomationElement aeForm = FindCredentialWindow();
        if ( aeForm != null ) {

            // 対象サイト(?)のメッセージに応じて適宜変更. 改行とか合わせるのが面倒なので正規表現で探すようにした
            string text = FindTextBlockMatches(aeForm, new Regex("^サーバー(.*)が"));

            if ( text != null ) {
                Console.WriteLine(text);

                // 下記は ID, PASSWORD に応じて適宜変更。機密管理に注意すること。
                //  やらかし例:ソースにパスワード埋め込んだままGitHubなどでうっかり公開したりとか
                InputToCredentialWindow(aeForm, "userid", "password");
            }
            else {
                Console.WriteLine("Not found Credential window with the description.");
            }
        }
        else {
            Console.WriteLine("Not found Credential window.");
        }
    }
}

事前調査

BASIC認証の画面の構成要素を下記のツールで調べた。
https://qiita.com/kob58im/items/3587d8e595e655e9391d

参考サイト

https://www.atmarkit.co.jp/fdotnet/special/uiautomation/uiautomation_03.html

その後

EditField_0PasswordField_1の数字が違う認証画面が出るケースに遭遇した。
Windows仕様が分からん…

kob58im
趣味でC#で色々試してます。 置いてるほとんどのC#サンプルコードは、Windows7以降デフォで入ってる環境でコンパイルできます。 最近はCodePen使ってJavaScriptも書いてます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away