GoogleからアクティブなタブのURLを取得する方法がありましたChromeを使用してFindWindowEx
をSendMessage
呼び出しと組み合わせて、現在のテキストを取得しますomnibox。Chromeは現在すべてをすべてレンダリングしているようです。(Spy ++、AHK Window SpyまたはWindow Detectiveで確認できます)
FirefoxとOperaで現在のURLを取得するには、DDEとWWW_GetWindowInfo
。これはChrome(もう?)では不可能だと思われます。
この質問 は、それがどのように機能していたかについての詳細を含む回答を持っています。これはこのコードです(説明したように、もう機能しません-hAddressBox
は0
):
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を取得する方法はありますか? (タイトルだけでは不十分です)
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;
}
}
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;
}
上記のすべての方法は、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;
}
ここで見つけました:
次のコードで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));
私はアンジェロのソリューションを取り、それを少しきれいにしました...私は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;
}
}
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
私の場合、アクティブな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ウィンドウのハンドルを実際に取得する方法を理解するのに他の誰かが時間を節約できることを願っています。
私はこの投稿を発見し、これらのメソッドを使用してC#でchromeからURLを正常にプルできました。皆さん、ありがとうございます!
残念ながら最近のChrome 69の更新により、AutomationElement
ツリーのトラバーサルが壊れました再び。
Microsoftのこの記事を見つけました: TreeWalkerを使用したUIオートメーション要素間の移動
そして、それを使用して、探している"edit"
制御型でAutomationElement
を検索する単純な関数を作成しました。は、ツリー階層をトラバースする代わりには常に変更されます、そこからそのAutomationElement
からURL値を抽出します。
これをすべてまとめるシンプルなクラスを作成しました: Google-Chrome-URL-Check-C-Sharp 。
READMEは、その使用方法について少し説明しています。
全体として、それはほんの少し信頼性が高いかもしれませんし、あなたの何人かがそれが役に立つと思うことを望みます。
バージョン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));