none
ブラウザのスクロールバーを動作させたい RRS feed

  • 質問

  • 自作アプリ(VS Express 2013、C#)から
    SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
    にてhwndを指定し、他アプリのスクロールバーを動かそうとしています。

    ウインドウズエクスプローラーやFirefoxは上記で動くのですが、
    IEやChromeのスクロールバーが動作してくれません。

    これらのバーは仕組みが異なるのでしょうか?
    外部から動作させるにはどのような方法があるかヒントをいただけないでしょうか?

    ヴァージョンについてこちらでは
    IEはIE11.0.96600.17914
    Chromeは44.0.2403.125を使用しました。

    hwndは"WinID"ツールにて動かしたいスクロールバーにフォーカスを当てて
    "HWND"の値を使用しました。

    何卒よろしくお願いいたします。

    2015年8月4日 18:23

すべての返信

  • 直接の回答ではありませんが、IEであればSendMessageを使わなくてもスクロールさせることができます。

    (参考)
    生成したスレッドからのIE操作でエラーが発生してしまいます。
    http://bbs.wankuma.com/index.cgi?mode=al2&namber=26218&KLOG=48


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2015年8月5日 2:04
    モデレータ
  • スクロールだけならWM_MOUSEWHEELで代用するという手も

    using System;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                IntPtr hwnd = new IntPtr(***);//SPI++でしらべたHWND
    
                System.Text.StringBuilder sb = new StringBuilder(100);
                GetClassName(hwnd, sb, 100);
                bool isIE = sb.ToString() == "Internet Explorer_Server";
    
                var style = GetWindowLong(hwnd, GWL_STYLE);
                if (isIE)
                {
                    RECT rect = new RECT();
                    GetWindowRect(hwnd, out rect);
                    int x = rect.Right - 1;
                    int y = rect.Top;
    
                    int delta = -WHEEL_DELTA * 5;
    
                    IntPtr w = new IntPtr(delta << 16);
                    IntPtr l = new IntPtr(((y & 0xFFFF) << 16) | (x & 0xFFFF));
    
                    SendMessage(hwnd, WM_MOUSEWHEEL, w, l);
                }
                else if ((style & WS_VSCROLL) == WS_VSCROLL)
                {//スクロールバーがある
                    //WM_VSCROLL
                }
            }
    
            public const int WHEEL_DELTA = 120;
            public const int GWL_STYLE = -16;
            public const int WS_VSCROLL = 0x00200000;
            public const int WS_HSCROLL = 0x00100000;
            public const int WM_MOUSEWHEEL = 0x20A;
    
            [StructLayout(LayoutKind.Sequential)]
            public struct RECT
            {
                public int Left;
                public int Top;
                public int Right;
                public int Bottom;
            }
    
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
    
            [DllImport("user32.dll")]
            public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
    
            [DllImport("user32.dll")]
            static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
    
            [DllImport("user32.dll")]
            static extern IntPtr PostMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    
            [DllImport("user32.dll")]
            static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    
        }
    }
    #Chromeは試してないです

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2015年8月5日 3:48
  • 早速のご回答ありがとうございます。
    trapemiyaさんとgekkaさんの情報を拝見しました。(gekkaさんにつきましてはサンプルまでいただいてありがとうございます。)

    IEがもっているスクロールバーはIE固有のものでWM_VSCROLLでは動かせない。という理解でよいのでしょうか?
    にしてもWM_MOUSEWHELLは何故に受け取るのか理解できずにいます。

    一瞬 WM_MOUSEWHEELLで統一してしまおうと思いましたが、水平スクロールも制御したいと考えておりましてその場合は都合が悪いです。。。
    WM_MOUSEWHELLを水平スクロールに向けるのは不可能と認識しています。

    今のところは、垂直スクロールはMM_MOUSEWHEELで統一。水平スクロールはWM_VSCROLL、IEの場合は制限にしようかな。と考えていますが、もし他に"~固有スクロール"があると潰しがきかなくなります。
    (そういうものはありそうでしょうか?)

    さらによりよいやり方はないでしょうか・・・
    2015年8月5日 6:37
  • すみませんが追加でご教授をいただきたくお願いいたします。
    trapemiyaさんの提示先サンプルを試しているのですが、

    > myWindow = (mshtml.DispHTMLWindow2)myDocument.parentWindow; //<-{"指定されたキャストは有効ではありません。"}
    まさしく上記の箇所で例外が発生してしまいました。

    型'System.InvalidCastException'のハンドルされていない例外がmscorlib.dllで発生しました
    追加情報:型 'mshtml.HTMLDocumentClass'のCOMオブジェクトをインターフェイス型'mshtml.DispHTMLDocument'
    にキャストできません。(以降続く・・・)

    と表示されます。
    提示先ではスレッドのアパートモデルをSTAにすると解決されたようですが、
    こちらでも
            [System.STAThreadAttribute()]
            static void Main()
    としたり
    navigateWaitThread.SetApartmentState(System.Threading.ApartmentState.STA);
    を加えてみましたが結果は変わりませんでした。(Mainはもともと[STAThread]が記述されていましたが)

    COMのMicrosoft HTML Object LibraryとMicrosoft Internet Controls
    の参照設定は問題なく出来ていると思います。
    m_IE.Navigate(""http://www.yahoo.com"ref dummy, ref dummy, ref dummy, ref dummy);
    とやってみると起動済みIEにはyahooは表示されるのでSHDocVwはとれているかと思います。

    お手数ですがどこに不備がありそうかご指摘いただけないでしょうか?

    2015年8月11日 18:18
  • DispHTMLWindow2ではなく、IHTMLWindow2にキャストしてみては。
    2015年8月11日 20:44
  • >Hongliangさん

    動きました!ご指摘大変ありがとうございます。

    2015年8月12日 0:52
  • たびたびすみません。
    表示されているIEのスクロールバーの現在のつまみの位置を取得したいのですが
    どのようにすればよいでしょうか?

    http://akibakokoubou.jp/blog/?p=3577
    上記のページにて
    DOM経由で"scrollTop"や"scrollleft"を取得する・・・のようなことを見つけたのですが、
    SHDocVwとして取得したオブジェクトをmshtml.IHTMLDocument3にキャストしていっても

    DomDocumentが見つからずいきづまっています。

    どこかの時点でForms.Webbrowserに変換しないといけないようなのですが、
    どのオブジェクトを何の型に変換していけばよろしいでしょうか?

    2015年8月12日 4:28
  • DOM経由で"scrollTop"や"scrollleft"を取得する・・・のようなことを見つけたのですが、SHDocVwとして取得したオブジェクトをmshtml.IHTMLDocument3にキャストしていってもDomDocumentが見つからずいきづまっています。

    IHTMLWindow3::documentが返すオブジェクトが、直接にIHTMLDocument3を公開しています(キャスト可能です)。DomDocumentはWinForm用のWebBrowserコントロールを使っている際に、生のCOMオブジェクトとして扱うためのプロパティであり、外部のIEを扱う際には全く関係ありません。

    どこかの時点でForms.Webbrowserに変換しないといけないようなのですが、どのオブジェクトを何の型に変換していけばよろしいでしょうか?

    不要ですし不可能です。このリンク先のは、WebBrowserコントロールを使っている自作のアプリにおいて、スクロール位置を取得するためのコードです。

    // まあその場合はmshtmlを直接扱わずとも、HtmlDocument/HtmlElementのみで解決できるのですが。

    2015年8月12日 4:43
  • アドバイスありがとうございます。

    >IHTMLWindow3::documentが返すオブジェクトが、直接にIHTMLDocument3を公開しています(キャスト可能です)。

    を試しているのですが、どうにもうまくいきません・・・

                mshtml.IHTMLDocument2 myDocument = null;
                mshtml.IHTMLWindow2 myWindow = null;
                mshtml.IHTMLWindow3 myWindow3 = null;
                mshtml.IHTMLDocument3 myDocument3 = null;

                myDocument = (mshtml.IHTMLDocument2)m_IE.Document;           
                myWindow  = (mshtml.IHTMLWindow2)myDocument.parentWindow; // ここは解決
                myWindow.scrollTo(0, 100);

                myWindow3 = (mshtml.IHTMLWindow3)myWindow;                // これだと通るが※1で例外
                //myWindow3 = (mshtml.IHTMLWindow3)myWindow.document;       // これだとここで例外発生
                //myWindow3 = (mshtml.IHTMLWindow2)myDocument.parentWindow; // ここで例外発生
                //myWindow3 = (mshtml.IHTMLWindow3)m_IE.Document;            // ここで例外発生


                myDocument3 = (mshtml.IHTMLDocument3)myWindow3;           // ※1 ここで例外
                mshtml.IHTMLElement2 elm = (mshtml.IHTMLElement2)myDocument3.documentElement;
                Point scrollpos = new Point(elm.scrollLeft,elm.scrollTop);//WebBrowserコントロールのスクロール位置

    大変お手数ですが具体的にどのようなコードを書けばよいかご提示いただけないでしょうか?
    2015年8月12日 6:03
  • windowとdocumentは(普通は)別物です。互いにキャストはできません。

    IEが公開しているdocumentはIHTMLDocument2~IHTMLDocument5ぐらいまではカバーしているので、必要に応じてdocumentオブジェクトをキャストして扱ってください。

    // あと、myWindow3 = (mshtml.IHTMLWindow2)myDocument.parentWindowは例外じゃなくてコンパイルエラーじゃないかなぁ。

    2015年8月12日 6:41
  • すみません。だいぶ混乱してきました。

    >windowとdocumentは(普通は)別物です。互いにキャストはできません。

                myDocument = (mshtml.IHTMLDocument2)m_IE.Document;           
                myWindow  = (mshtml.IHTMLWindow2)myDocument.parentWindow; // ここは解決

    このコードはIHTMLDocument2からIHTMLWindow2にキャストしていますがこれは普通ではないのでしょうか。

    >IEが公開しているdocumentはIHTMLDocument2~IHTMLDocument5ぐらいまではカバーしているので、必要に応じてdocumentオブジェクトをキャストして扱ってください。

    具体的にはどのようなコードになりますでしょうか? documentオブジェクトのとりだしがよくわかっていません。

    //myWindow3 = (mshtml.IHTMLWindow3)m_IE.Document;

    はすでに試しているのですが駄目でした。

    2015年8月12日 7:29
  • myWindow  = (mshtml.IHTMLWindow2)myDocument.parentWindow;

    このコードはIHTMLDocument2からIHTMLWindow2にキャストしていますがこれは普通ではないのでしょうか。

    キャストしているのはmyDocument.parentWindowであって、IHTMLDocument2であるmyDocumentではないですよね。myDocument.parentWindowは、文字通りwindowを公開しているプロパティです。windowなので、当然ながらIHTMLWindow2にキャストできます。

    取り出しも何も、もう持ってますよね。m_IEのDocumentプロパティから取得済みです。

    2015年8月12日 7:41
  •   連レスですが

             myDocument3 = (mshtml.IHTMLDocument3)m_IE.Document;

    とすることでうまくいきました!ありがとうございます。

    ただ、いだいたコメントの

    >windowとdocumentは(普通は)別物です。互いにキャストはできません。

    >IHTMLWindow3::documentが返すオブジェクトが、直接にIHTMLDocument3を公開しています(キャスト可能です)。

    というのが相変わらず理解できていません。たとえばIHTMLWindow3::documentが返すオブジェクトを

    IHTMLDocument3にキャストするといったコードはどのようになりますでしょうか?

    2015年8月12日 7:52
  • コメントがかぶってしまったようですみません。

    ご説明ありがとうございます。理解できました。

    2015年8月12日 9:42
  • 今度はChromeのスクロールバーを操作したいと考えています。
    Webkit.NETを参照設定するところまで終えたのですが、
    その後どのようにしてChromeを探しだして、操作オブジェクトとして引っ張ってくるのか分かっておりません。

    IEの場合はからクラス名"TabWindowClass"や"Internet Explorer_Server"の
    ウインドウハンドル元にObjectFromLresultを呼んでIHTMLDocument2等のオブジェクトを
    とってきました。

    Chromeの場合はどのような手順になりますでしょうか?

    2015年8月14日 2:28
  • あれから調査したのですが現行のChromeで操作オブジェクトを利用する方法が見つかりませんでした。

    (過去verではWebkitなどで操作できたようですが・・・)

    別の方法がないかと思い「かざぐるまうす」を見つけたのですが、これはブラウザやエクスプローラなどの

    ウインドウの種類にかかわらず全てのスクロールバーをジェスチャによって操作しているように見えます。

    どなたかこのアプリはどのようにして実現しているかご存知でしょうか?

    2015年10月10日 22:45
  • 質問の背景も提示していただけると回答が付きやすいかもしれません。

    当初IEのスクロールバー、追加でスクロール位置、さらに追加でChromeとのことですが、もしかしてWebブラウザー全般の操作全般を目的とされているのでしょうか?

    そうであれば、Webブラウザーの(テスト目的で)自動化ツールがあります。Selenium WebDriverを試されてはどうでしょうか?

    2015年10月11日 0:22
  • ブラウザに限らずできれば全てのウインドウ(フォルダ、エクスプローラ、ブラウザ、etc)のスクロールを一律に動かしたいのです。

    画面最手前の任意位置にアイコンを表示させ、それをクリック&ドラッグすることで現在アクティブになっているウインドウの

    スクロールバーをドラッグした方向へ同期的に動かそうとしています。

    Surface Pro3を購入したのですが付属のタッチペンはマウスの動きをシミュレートしており、

    指ではとうぜん画面をスライドできるのですがタッチペンで同じ動きをさせると

    単なるクリックドラッグになってしまいます(例えばブラウザ上でそれを行うと文字選択になってしまいます)。

    タッチペンでわざわざ端っこのスクロールバーを掴むのが煩わしいと感じており、このようなアプリを作成したいと考えておりました。

    (当フォーラムのおかげでフォルダとFirefoxは動かせましたが、Chrome派なのであと一歩です)

    ご提示のSeleniumも検討していたのですが、感触としてクライアントとしてブラウザにかぶせるようなので

    連続でスクロールのメッセージを投げるような本件の動きだと挙動が遅くなるのでは考えております。

    2015年10月11日 7:46
  • 回答ではないのですが、

    タッチとペンとで確かに挙動が異なりますが、ペン入力には独自にFlicks Gesturesという機能があります。(というかペンはタッチより歴史が長いです。)

    結構素早く操作をする必要はありますが、タッチと同じく上下フリックでスクロールをサポートしています。この機能は既定で有効化されています。コントロールパネルの「ペンとタッチ」から設定でき、また「フリックの使い方練習」も用意されていますので確認してください。

    2015年10月11日 11:23
  • ご返信ありがとうございます。ご提示の機能について存じ上げているのですが、

    ブラウザのジェスチャー機能(Gesture for chrome等)と併用しておりますので、ペンのフリック機能とブッキンして使いづらいのです。

    また、ペンのフリックを行う際に画面を傷つけないよう力加減を意識してしまいますのであまり素早く上下には動かせません。

    フリックのように目的のページ箇所がはっきりしてそこにジャンプさせたいのではなく、

    巻物を観るように「ぐりぐり」っといった感じで上下に任意スクロールさせたいのです。

    2015年10月11日 22:29