web-dev-qa-db-ja.com

Googleから現在のタブのURLを取得するChrome C#を使用して

GoogleからアクティブなタブのURLを取得する方法がありましたChromeを使用してFindWindowExSendMessage呼び出しと組み合わせて、現在のテキストを取得しますomn​​ibox。Chromeは現在すべてをすべてレンダリングしているようです。(Spy ++、AHK Window SpyまたはWindow Detectiveで確認できます)

FirefoxとOperaで現在のURLを取得するには、DDEとWWW_GetWindowInfo。これはChrome(もう?)では不可能だと思われます。

この質問 は、それがどのように機能していたかについての詳細を含む回答を持っています。これはこのコードです(説明したように、もう機能しません-hAddressBox0):

var hAddressBox = FindWindowEx(
    intPtr,
    IntPtr.Zero,
    "Chrome_OmniboxView",
    IntPtr.Zero);

var sb = new StringBuilder(256);
SendMessage(hAddressBox, 0x000D, (IntPtr)256, sb);
temp = sb.ToString();

だから私の質問は:new現在フォーカスされているタブのURLを取得する方法はありますか? (タイトルだけでは不十分です)

41
Codecat

Edit:ここでの私の答えのコードはもう機能しないようです(AutomationElementを使用するというアイデアはまだ機能しますが) Chromeバージョンなので、異なるバージョンについては他の回答をご覧ください。たとえば、Chrome 54: https:// stackoverflow。 com/a/40638519/377618

次のコードは機能しているようです(icemanindのコメントのおかげです)が、リソースを大量に消費します。 elmUrlBarを見つけるのに約350msかかります...少し遅いです。

言うまでもなく、同時に実行されている複数のchromeプロセスで作業する問題があることは言うまでもありません。

// there are always multiple chrome processes, so we have to loop through all of them to find the
// process with a Window Handle and an automation element of name "Address and search bar"
Process[] procsChrome = Process.GetProcessesByName("chrome");
foreach (Process chrome in procsChrome) {
  // the chrome process must have a window
  if (chrome.MainWindowHandle == IntPtr.Zero) {
    continue;
  }

  // find the automation element
  AutomationElement Elm = AutomationElement.FromHandle(chrome.MainWindowHandle);
  AutomationElement elmUrlBar = Elm.FindFirst(TreeScope.Descendants,
    new PropertyCondition(AutomationElement.NameProperty, "Address and search bar"));

  // if it can be found, get the value from the URL bar
  if (elmUrlBar != null) {
    AutomationPattern[] patterns = elmUrlBar.GetSupportedPatterns();
    if (patterns.Length > 0) {
      ValuePattern val = (ValuePattern)elmUrlBar.GetCurrentPattern(patterns[0]);
      Console.WriteLine("Chrome URL found: " + val.Current.Value);
    }
  }
}

編集:上記の遅い方法に満足できなかったので、より速く(現在は50ミリ秒)にし、URL検証を追加して確認しましたユーザーがウェブ上で検索しているものの代わりに正しいURL、またはURLを入力するのに忙しい。コードは次のとおりです。

// there are always multiple chrome processes, so we have to loop through all of them to find the
// process with a Window Handle and an automation element of name "Address and search bar"
Process[] procsChrome = Process.GetProcessesByName("chrome");
foreach (Process chrome in procsChrome) {
  // the chrome process must have a window
  if (chrome.MainWindowHandle == IntPtr.Zero) {
    continue;
  }

  // find the automation element
  AutomationElement Elm = AutomationElement.FromHandle(chrome.MainWindowHandle);

  // manually walk through the tree, searching using TreeScope.Descendants is too slow (even if it's more reliable)
  AutomationElement elmUrlBar = null;
  try {
    // walking path found using inspect.exe (Windows SDK) for Chrome 31.0.1650.63 m (currently the latest stable)
    var Elm1 = Elm.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
    if (Elm1 == null) { continue; } // not the right chrome.exe
    // here, you can optionally check if Incognito is enabled:
    //bool bIncognito = TreeWalker.RawViewWalker.GetFirstChild(TreeWalker.RawViewWalker.GetFirstChild(Elm1)) != null;
    var Elm2 = TreeWalker.RawViewWalker.GetLastChild(Elm1); // I don't know a Condition for this for finding :(
    var Elm3 = Elm2.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
    var Elm4 = Elm3.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ToolBar));
    elmUrlBar = Elm4.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));
  } catch {
    // Chrome has probably changed something, and above walking needs to be modified. :(
    // put an assertion here or something to make sure you don't miss it
    continue;
  }

  // make sure it's valid
  if (elmUrlBar == null) {
    // it's not..
    continue;
  }

  // elmUrlBar is now the URL bar element. we have to make sure that it's out of keyboard focus if we want to get a valid URL
  if ((bool)elmUrlBar.GetCurrentPropertyValue(AutomationElement.HasKeyboardFocusProperty)) {
    continue;
  }

  // there might not be a valid pattern to use, so we have to make sure we have one
  AutomationPattern[] patterns = elmUrlBar.GetSupportedPatterns();
  if (patterns.Length == 1) {
    string ret = "";
    try {
      ret = ((ValuePattern)elmUrlBar.GetCurrentPattern(patterns[0])).Current.Value;
    } catch { }
    if (ret != "") {
      // must match a domain name (and possibly "https://" in front)
      if (Regex.IsMatch(ret, @"^(https:\/\/)?[a-zA-Z0-9\-\.]+(\.[a-zA-Z]{2,4}).*$")) {
        // prepend http:// to the url, because Chrome hides it if it's not SSL
        if (!ret.StartsWith("http")) {
          ret = "http://" + ret;
        }
        Console.WriteLine("Open Chrome URL found: '" + ret + "'");
      }
    }
    continue;
  }
}
35
Codecat

Chrome 54の時点で、次のコードが機能しています。

public static string GetActiveTabUrl()
{
  Process[] procsChrome = Process.GetProcessesByName("chrome");

  if (procsChrome.Length <= 0)
    return null;

  foreach (Process proc in procsChrome)
  {
    // the chrome process must have a window 
    if (proc.MainWindowHandle == IntPtr.Zero)
      continue;

    // to find the tabs we first need to locate something reliable - the 'New Tab' button 
    AutomationElement root = AutomationElement.FromHandle(proc.MainWindowHandle);
    var SearchBar = root.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Address and search bar"));
    if (SearchBar != null)
      return (string)SearchBar.GetCurrentPropertyValue(ValuePatternIdentifiers.ValueProperty);
  }

  return null;
}
9
dotNET

上記のすべての方法は、Chrome V53以上で失敗しています。

動作しているのは次のとおりです。

Process[] procsChrome = Process.GetProcessesByName("chrome");
foreach (Process chrome in procsChrome)
{
    if (chrome.MainWindowHandle == IntPtr.Zero)
        continue;

    AutomationElement element = AutomationElement.FromHandle(chrome.MainWindowHandle);
    if (element == null)
        return null;
    Condition conditions = new AndCondition(
        new PropertyCondition(AutomationElement.ProcessIdProperty, chrome.Id),
        new PropertyCondition(AutomationElement.IsControlElementProperty, true),
        new PropertyCondition(AutomationElement.IsContentElementProperty, true),
        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));

    AutomationElement elementx = element.FindFirst(TreeScope.Descendants, conditions);
    return ((ValuePattern)elementx.GetCurrentPattern(ValuePattern.Pattern)).Current.Value as string;
}

ここで見つけました:

https://social.msdn.Microsoft.com/Forums/vstudio/en-US/93001bf5-440b-4a3a-ad6c-478a4f618e32/how-can-i-get-urls-of-open-pages- from-chrome-and-firefox?forum = csharpgeneral

8
Randall Deetz

次のコードでChrome 38.0.2125.10の結果が得られました(「try」ブロック内のコードはこれに置き換える必要があります)

var Elm1 = Elm.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
if (Elm1 == null) { continue; }  // not the right chrome.exe
var Elm2 = TreeWalker.RawViewWalker.GetLastChild(Elm1);
var Elm3 = Elm2.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.HelpTextProperty, "TopContainerView"));
var Elm4 = Elm3.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ToolBar));
var Elm5 = Elm4.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.HelpTextProperty, "LocationBarView"));
elmUrlBar = Elm5.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
7
Paul3k

私はアンジェロのソリューションを取り、それを少しきれいにしました...私はLINQに固執しています:)

これが主な方法です。いくつかの拡張メソッドを使用します。

public IEnumerable<string> GetTabs()
{
  // there are always multiple chrome processes, so we have to loop through all of them to find the
  // process with a Window Handle and an automation element of name "Address and search bar"
  var processes = Process.GetProcessesByName("chrome");
  var automationElements = from chrome in processes
                           where chrome.MainWindowHandle != IntPtr.Zero
                           select AutomationElement.FromHandle(chrome.MainWindowHandle);

  return from element in automationElements
         select element.GetUrlBar()
         into elmUrlBar
         where elmUrlBar != null
         where !((bool) elmUrlBar.GetCurrentPropertyValue(AutomationElement.HasKeyboardFocusProperty))
         let patterns = elmUrlBar.GetSupportedPatterns()
         where patterns.Length == 1
         select elmUrlBar.TryGetValue(patterns)
         into ret
         where ret != ""
         where Regex.IsMatch(ret, @"^(https:\/\/)?[a-zA-Z0-9\-\.]+(\.[a-zA-Z]{2,4}).*$")
         select ret.StartsWith("http") ? ret : "http://" + ret;
}

コメントは傾向があるため、コメントは誤解を招きやすいことに注意してください。実際には、単一のAutomationElementを参照しません。アンジェロのコードがそれを持っているので、私はそこに残しました。

拡張クラスは次のとおりです。

public static class AutomationElementExtensions
{
  public static AutomationElement GetUrlBar(this AutomationElement element)
  {
    try
    {
      return InternalGetUrlBar(element);
    }
    catch
    {
      // Chrome has probably changed something, and above walking needs to be modified. :(
      // put an assertion here or something to make sure you don't miss it
      return null;
    }
  }

  public static string TryGetValue(this AutomationElement urlBar, AutomationPattern[] patterns)
  {
    try
    {
      return ((ValuePattern) urlBar.GetCurrentPattern(patterns[0])).Current.Value;
    }
    catch
    {
      return "";
    }
  }

  //

  private static AutomationElement InternalGetUrlBar(AutomationElement element)
  {
    // walking path found using inspect.exe (Windows SDK) for Chrome 29.0.1547.76 m (currently the latest stable)
    var Elm1 = element.FindFirst(TreeScope.Children,
      new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
    var Elm2 = TreeWalker.RawViewWalker.GetLastChild(Elm1); // I don't know a Condition for this for finding :(
    var Elm3 = Elm2.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
    var Elm4 = Elm3.FindFirst(TreeScope.Children,
      new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ToolBar));
    var result = Elm4.FindFirst(TreeScope.Children,
      new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));

    return result;
  }
}
5
Marcel Popescu

Angelo Geelsのソリューションを参照すると、バージョン35のパッチがあります。「try」ブロック内のコードはこれに置き換える必要があります。

var Elm1 = Elm.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
if (Elm1 == null) { continue; } // not the right chrome.exe
var Elm2 = TreeWalker.RawViewWalker.GetLastChild(Elm1); // I don't know a Condition for this for finding
var Elm3 = Elm2.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
var Elm4 = TreeWalker.RawViewWalker.GetNextSibling(Elm3); // I don't know a Condition for this for finding
var Elm7 = Elm4.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ToolBar));
elmUrlBar = Elm7.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));  

私はここからそれを取りました: http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/dotNet/System_CodeDom+Grab~URL~from~Chrome.txt

4

私の場合、アクティブなchromeウィンドウにはMainWindowHandleがあります。すべてのウィンドウでchrome windowsを探し、代わりにこれらのハンドルを使用します。例:

    public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);

    [DllImport("user32.dll")]
    protected static extern bool EnumWindows(Win32Callback enumProc, IntPtr lParam); 

    private static bool EnumWindow(IntPtr handle, IntPtr pointer)
    {
        List<IntPtr> pointers = GCHandle.FromIntPtr(pointer).Target as List<IntPtr>;
        pointers.Add(handle);
        return true;
    }

    private static List<IntPtr> GetAllWindows()
    {
        Win32Callback enumCallback = new Win32Callback(EnumWindow);
        List<IntPtr> pointers = new List<IntPtr>();
        GCHandle listHandle = GCHandle.Alloc(pointers);
        try
        {
            EnumWindows(enumCallback, GCHandle.ToIntPtr(listHandle));
        }
        finally
        {
            if (listHandle.IsAllocated) listHandle.Free();
        }
        return pointers;
    }

そして、すべてのchromeウィンドウを取得するには:

    [DllImport("User32", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int GetWindowText(IntPtr windowHandle, StringBuilder stringBuilder, int nMaxCount);

    [DllImport("user32.dll", EntryPoint = "GetWindowTextLength", SetLastError = true)]
    internal static extern int GetWindowTextLength(IntPtr hwnd);
    private static string GetTitle(IntPtr handle)
    {
        int length = GetWindowTextLength(handle);
        StringBuilder sb = new StringBuilder(length + 1);
        GetWindowText(handle, sb, sb.Capacity);
        return sb.ToString();
    }

そして最後に:

GetAllWindows()
    .Select(GetTitle)
    .Where(x => x.Contains("Google Chrome"))
    .ToList()
    .ForEach(Console.WriteLine);

うまくいけば、すべてのchromeウィンドウのハンドルを実際に取得する方法を理解するのに他の誰かが時間を節約できることを願っています。

3
yeerk

私はこの投稿を発見し、これらのメソッドを使用してC#でchromeからURLを正常にプルできました。皆さん、ありがとうございます!

残念ながら最近のChrome 69の更新により、AutomationElementツリーのトラバーサルが壊れました再び。

Microsoftのこの記事を見つけました: TreeWalkerを使用したUIオートメーション要素間の移動

そして、それを使用して、探している"edit"制御型でAutomationElementを検索する単純な関数を作成しました。は、ツリー階層をトラバースする代わりには常に変更されます、そこからそのAutomationElementからURL値を抽出します。

これをすべてまとめるシンプルなクラスを作成しました: Google-Chrome-URL-Check-C-Sharp

READMEは、その使用方法について少し説明しています。

全体として、それはほんの少し信頼性が高いかもしれませんし、あなたの何人かがそれが役に立つと思うことを望みます。

3
progietheus

バージョン53.0.2785の場合、これで動作します:

var Elm1 = Elm.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Google Chrome"));
                if (Elm1 == null) { continue; } // not the right chrome.exe
                var Elm2 = Elm1.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""))[1];
                var Elm3 = Elm2.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""))[1];
                var Elm4 = Elm3.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "principal"));
                var Elm5 = Elm4.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, ""));
                elmUrlBar = Elm5.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
1
Yind