PowerShellから各種ブラウザで開いているURLを取得する (Windows10+Chrome編)

概要

Chrome編です。Chromeは開いているページのURL全部取れないこともないっぽいんですけど、一個あたり1〜2秒くらいかかるので用途によっては厳しい。なんとか減らせないかな?ってTreeWalkerでやってみたけど最短経路で到達しても同じくらいの時間がかかる…。

適当に取れる範囲絞ってとるといいと思う。

ターゲットは「アドレス検索バー」。今回は、ControlTypePropertyで取ってみる。

f:id:harupu:20210322093509p:plain

コード

$chorome_csharp = @"
using System;
using System.Windows.Automation;
using System.Diagnostics;
using System.Collections.Generic;
namespace UIAutomationChrome {
  public class Program {
    public static List<String> GetURLList() {
      List<String> urlList = new List<String>();
      AutomationElementCollection tests = AutomationElement.RootElement.FindAll(TreeScope.Element | TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Pane));
      foreach (AutomationElement testElement in tests){
        AutomationElement test1 = testElement.FindFirst(TreeScope.Element | TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1"));
        if (test1 == null) {continue;}
        AutomationElement test2 = test1.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
        if (test2 == null ) {continue;}
        ValuePattern v = (ValuePattern)test2.GetCurrentPattern(ValuePattern.Pattern);
        if (v.Current.Value != null && v.Current.Value != "") {
          Console.WriteLine("URL:" + v.Current.Value);
          urlList.Add(v.Current.Value);
        }
      }
      return urlList;
    }
  }
}
"@

Add-Type -TypeDefinition $chorome_csharp -ReferencedAssemblies("UIAutomationClient", "UIAutomationTypes");

foreach ($url in [UIAutomationChromes.Program]::GetURLList()) {
  $url
}

f:id:harupu:20210322093711p:plain

PowerShellから各種ブラウザで開いているURLを取得する (Windows10+Microsoft Edge編)

概要

AuntomationのConditionで絞って特定の属性を持つノードをInspectorでとってくる(前の記事参照)。

悲しいことにMSEdgeはActiveになっていないタブのURLは取れない。ので、アドレスバーからとる。タブにフォーカスを順に当ててけばできると思うけど、ここでは秘密裏にユーザ操作に影響を与えず取得することを目的とするため、そういうことはせず、一個とるだけで我慢する。

目標はURL入ってるこれ。ControlTypeでも取れると思うが、Nameの「アドレスと検索バー」からとる。当然、言語が違えば表現が変わるので注意。

f:id:harupu:20210318100437p:plain

コード

$edge_csharp = @"
using System;
using System.Windows.Automation;
using System.Diagnostics;
namespace UIAutomationEdge {
  public class Program {
    public static string GetURL(Process process) {
      AutomationElementCollection roots = AutomationElement.RootElement.FindAll(TreeScope.Element | TreeScope.Children, new AndCondition(new PropertyCondition(AutomationElement.ProcessIdProperty, process.Id), new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")));
      foreach (AutomationElement rootElement in roots){
        AutomationElement address = rootElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "アドレスと検索バー"));
        if (address == null) {continue;}
        ValuePattern v = (ValuePattern)address.GetCurrentPattern(ValuePattern.Pattern);
        if (v.Current.Value != null && v.Current.Value != "") {
          Console.WriteLine("URL:" + v.Current.Value);
          return v.Current.Value;
        }
      }
      return "";
    }
  }
}
"@
Add-Type -TypeDefinition $edge_csharp -ReferencedAssemblies("UIAutomationClient", "UIAutomationTypes");

$msedges = (Get-Process -name msedge)
foreach ($msedge in $msedges) {
  $url = [UIAutomationEdge.Program]::GetURL($msedge);
  if ($url) {
    $url
  }
}

f:id:harupu:20210318100930p:plain

PowerShellから各種ブラウザで開いているURLを取得する (Windows10+FireFox編)

概要

色々Webを調べたんだけど、うまくばしっと取れるやつがなかったので自分で調べて作った。困ってる人もいそうな気がしなくもないので?書いておく。あとでIE編(超簡単)、Chrome編、MSEdge編も書く。

まず、AuntomationのConditionで絞って特定の属性を持つノードをとってくる、というのがわかってないと何をやってるのかさっぱりわからないので、とりあえずインストールしましょう。とりあえず、気軽に取れるのかな?って思って調べ始めたときは、「これで取れます!」って言っているコードが何を元にプロパティ名・値を指定しているのかさっぱりわからず迷宮入りした。

C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64 とかそういう感じのところに入ってます。

Windows SDK アーカイブ - Windows アプリ開発

こんな感じ。URLを持ってそうなやつがいますね。

f:id:harupu:20210317091308p:plain
Inspect

あとは、愚直に取ります。TreeScope.Descendants+FindAllするとコード的には簡単に取れますが、20秒くらい時間かかりました。開いてるタブ多いともっとかかりそう。あと、LocalizedControlTypeはLocalizedなので環境によって違う可能性があるので、利用する環境に合わせてInspectorでとってきた方が良さそう。

コード

$firefox_csharp = @"
using System;
using System.Windows.Automation;
using System.Diagnostics;
using System.Collections.Generic;
namespace UIAutomationFirefox {
  public class Program {
    public static List<String> GetURLList(Process process) {
      AutomationElementCollection roots = AutomationElement.RootElement.FindAll(TreeScope.Element | TreeScope.Children, new AndCondition(new PropertyCondition(AutomationElement.ProcessIdProperty, process.Id), new PropertyCondition(AutomationElement.ClassNameProperty, "MozillaWindowClass")));
      List<String> urlList = new List<String>();
      foreach (AutomationElement rootElement in roots){
        AutomationElementCollection groupElements = rootElement.FindAll(TreeScope.Element | TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
        foreach (AutomationElement groupElement in groupElements){
          if (groupElement.Current.ControlType.LocalizedControlType == "メニュー") {continue;}
          AutomationElementCollection children1 = groupElement.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
          foreach (AutomationElement child1 in children1){
            AutomationElementCollection children2 = child1.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
            foreach (AutomationElement child2 in children2){
              AutomationElementCollection docElements = child2.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.LocalizedControlTypeProperty, "ドキュメント"));
              foreach (AutomationElement docElement in docElements){
                ValuePattern v = (ValuePattern)docElement.GetCurrentPattern(ValuePattern.Pattern);
                Console.WriteLine("URL:" + v.Current.Value);
                urlList.Add(v.Current.Value);
              }
            }
          }
        }
      }
      return urlList;
    } 
  }
}
"@
Add-Type -TypeDefinition $firefox_csharp -ReferencedAssemblies("UIAutomationClient", "UIAutomationTypes");

$firefoxes = (Get-Process -name firefox)
foreach ($firefox in $firefoxes) {
  foreach ($url in [UIAutomationFirefox.Program]::GetURLList($firefox)) {
    $url
  }
}

f:id:harupu:20210317092129p:plain
結果

ひさびさに 見に来てみたら 2年前

最後に書いたの2年前くらいだった。
はてなに出てくる広告でも(というかGoogleAdがいけないのか)「Download」とかの偽装バナーのやつがいるのですね。あれ、AdWareとかうっかりダウンロードする人がいるので悪質だからやめてほしい。。。

そして、また、広告を消したいので書くべ、って思ったら、消えない説??
出てもいいけど、悪質な奴は出ないでほしい。

そして、消せたけどこれ怒られるパターンかな?今時はどこに移住するのが流行なのだろうか。