C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[10]: 複数のIEを操作するには


(過去ログ 146 を表示中)

[トピック内 14 記事 (1 - 14 表示)]  << 0 >>

■85362 / inTopicNo.1)  複数のIEを操作するには
  
□投稿者/ のり (1回)-(2017/10/13(Fri) 17:35:03)

分類:[VB.NET/VB2005 以降] 

皆さま、お世話になります。
vb.net 2015
Windowsフォームアプリケーションです。

IE操作(webbrowserではない)で、ひとつのフォームアプリで複数のIEを操作したいのですが

今までIEをひとつだけ使用していた時は問題なくできていた

        For Each tags In Ie1.Document.getElementsByClassName ("b_searchbox")

            tags.innerText = "天気"
            Exit For
        Next
のようなクラス検索が、IEを2個で2個目を操作しようとしたときは以下のようなエラーになります。

型 'System.Runtime.InteropServices.COMException' のハンドルされていない例外が Microsoft.VisualBasic.dll で発生しました
追加情報:HRESULT からの例外:0x80020101


複数のIEを操作するときに必要なことが抜けているんだと思いますが私の知識では解決できません。
アドバイスをいただけれるととても助かります。

プログラムはボタンを4個使用します。
[button 1] 1つ目のIEを起動
[button 2] 1つ目のIEに対してテキスト入力

[button 3] 2つ目のIEを起動
[button 4] 2つ目のIEに対してテキスト入力

ボタン1→ボタン2→ボタン3 ボタン4でエラーになります。

ただし立ち上げ後
ボタン3→ボタン4 は問題なくテキスト入力できます。


ボタン1〜4までエラーなく完了できるようしたいです。
どうかよろしくお願いいたします。



以下、プログラム。
'-------------------------------------------------------------------
'vb.net Windowsフォームアプリケーション
'参照設定:Microsoft Internet Controls
'ボタンを4個使用しています。(button1〜4)

Public Class Form1

    'Ie1の宣言
    Public Ie1 As SHDocVw.InternetExplorer
    'Ie2の宣言
    Public Ie2 As SHDocVw.InternetExplorer

    'Ie1起動
    Private Sub Button1_Click(sender As Object, e As EventArgs) _
            Handles Button1.Click

        Ie1 = CreateObject("InternetExplorer.Application")
        Ie1.Visible = True
        Ie1.Navigate("https://www.google.co.jp/")

    End Sub

    'Ie1にテキスト入力
    Private Sub Button2_Click(sender As Object, e As EventArgs) _
    Handles Button2.Click

        For Each tags In Ie1.Document.getElementsByClassName _
        ("gsfi")

            tags.innerText = "グルメ"

            Exit For
        Next

    End Sub

    'Ie2起動
    Private Sub Button3_Click(sender As Object, e As EventArgs) _
    Handles Button3.Click

        Ie2 = CreateObject("InternetExplorer.Application")
        Ie2.Visible = True
        Ie2.Navigate("https://www.bing.com/")

    End Sub

    'Ie2にテキスト入力
    Private Sub Button4_Click(sender As Object, e As EventArgs) _
    Handles Button4.Click

        For Each tags In Ie2.Document.getElementsByClassName _
        ("b_searchbox")

            tags.innerText = "天気"

            Exit For
        Next
    End Sub

End Class

引用返信 編集キー/
■85382 / inTopicNo.2)  Re[1]: 複数のIEを操作するには
□投稿者/ 魔界の仮面弁士 (1425回)-(2017/10/16(Mon) 19:20:23)
No85362 (のり さん) に返信
> tags.innerText = "天気"
相手が input 要素なら、本来は innerText ではなく value だった気も。


> 型 'System.Runtime.InteropServices.COMException' のハンドルされていない例外が Microsoft.VisualBasic.dll で発生しました

本来は小文字始まりの getElementsByClassName なのですが、
あえて大文字始まりの GetElementsByClassName にしてみたところ、
呼び出せることもありました。難読化されていることもあり、原因箇所までは特定できませんでしたが。

For Each tags In CallByName(Ie2.Document, "GetElementsByClassName", CallType.Method, "b_searchbox")
For Each tags In Ie2.Document.GetElementsByClassName("b_searchbox")



> ボタン1→ボタン2→ボタン3 ボタン4でエラーになります。

ボタン5 として、こんな物を書いてみました。

Dim S1 = CallByName(CallByName(Ie1.Document, "getElementsByClassName", CallType.Get), "toString", CallType.Method)
Dim S2 = CallByName(CallByName(Ie2.Document, "getElementsByClassName", CallType.Get), "toString", CallType.Method)
Dim S3 = CallByName(CallByName(Ie2.Document, "GetElementsByClassName", CallType.Get), "toString", CallType.Method)

上記を実行すると、
 S1 = "◆function getElementsByClassName() {◆ [native code]◆}◆"
 S2 = "◆function replaceNode() {◆ [native code]◆}◆"
が得られました。◆ の箇所は vbLf です。


S3 については、S1 と同じ文字列の場合と、S2 と同じ文字列になる場合がありました。
何がトリガーになって切り替わるのかまでは特定できていませんが…。
引用返信 編集キー/
■85385 / inTopicNo.3)  Re[2]: 複数のIEを操作するには
□投稿者/ のり (2回)-(2017/10/16(Mon) 21:40:04)
No85382 (魔界の仮面弁士 さん) に返信
> ■No85362 (のり さん) に返信

返信ありがとうございます。とてもありがたいです。

>>tags.innerText = "天気"
> 相手が input 要素なら、本来は innerText ではなく value だった気も。

これは知りませんでした。
取り入れてみたいと思います。ありがとうございます。


>>型 'System.Runtime.InteropServices.COMException' のハンドルされていない例外が Microsoft.VisualBasic.dll で発生しました
>
> 本来は小文字始まりの getElementsByClassName なのですが、
> あえて大文字始まりの GetElementsByClassName にしてみたところ、
> 呼び出せることもありました。難読化されていることもあり、原因箇所までは特定できませんでしたが。

すみません、自分では大文字にしてみてもボタン4ではエラーになってしまいました。


> For Each tags In CallByName(Ie2.Document, "GetElementsByClassName", CallType.Method, "b_searchbox")
> For Each tags In Ie2.Document.GetElementsByClassName("b_searchbox")
>
>
>
>>ボタン1→ボタン2→ボタン3 ボタン4でエラーになります。
>
> ボタン5 として、こんな物を書いてみました。
>
> Dim S1 = CallByName(CallByName(Ie1.Document, "getElementsByClassName", CallType.Get), "toString", CallType.Method)
> Dim S2 = CallByName(CallByName(Ie2.Document, "getElementsByClassName", CallType.Get), "toString", CallType.Method)
> Dim S3 = CallByName(CallByName(Ie2.Document, "GetElementsByClassName", CallType.Get), "toString", CallType.Method)
>
> 上記を実行すると、
>  S1 = "◆function getElementsByClassName() {◆ [native code]◆}◆"
>  S2 = "◆function replaceNode() {◆ [native code]◆}◆"
> が得られました。◆ の箇所は vbLf です。


返信していただき、ありがとうございます。
やはり自分の知識では解決できなさそうですが
返信いただけたことはかなり勇気づけられました。

もっと自分でも調べてみたいと思います。


引用返信 編集キー/
■85387 / inTopicNo.4)  Re[3]: 複数のIEを操作するには
□投稿者/ 魔界の仮面弁士 (1426回)-(2017/10/16(Mon) 23:14:20)
No85385 (のり さん) に返信
>>本来は小文字始まりの getElementsByClassName なのですが、
>>あえて大文字始まりの GetElementsByClassName にしてみたところ、
>>呼び出せることもありました。難読化されていることもあり、原因箇所までは特定できませんでしたが。
>
> すみません、自分では大文字にしてみてもボタン4ではエラーになってしまいました。

レイトバインドで呼び出すのではなく、mshtml を参照設定して、
mshtml.HTMLDocument や mshtml.HTMLInputElement などに
キャストして取り出してみた場合はどうでしょうか。
引用返信 編集キー/
■85388 / inTopicNo.5)  Re[4]: 複数のIEを操作するには
□投稿者/ のり (3回)-(2017/10/17(Tue) 00:36:49)
No85387 (魔界の仮面弁士 さん) に返信
> ■No85385 (のり さん) に返信

> レイトバインドで呼び出すのではなく、mshtml を参照設定して、
> mshtml.HTMLDocument や mshtml.HTMLInputElement などに
> キャストして取り出してみた場合はどうでしょうか。

返信ありがとうございます。

参照設定に「Microsoft HTML Object Library」を追加して
以下のように書いてみましが、キャストとはこんな感じで良いでしょうか。
(ちょっと待たないとエラーになりそうなので、読み込み待ちを追加しています)

   'Ie1の宣言
    Public Ie1 As SHDocVw.InternetExplorer
    'キャストの宣言
    Public htmlDoc As mshtml.HTMLDocument

    'Ie1起動
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        Ie1 = CreateObject("InternetExplorer.Application")
        Ie1.Visible = True
        Ie1.Navigate("https://www.google.co.jp/")


        Do While Ie1.Busy = True Or Ie1.ReadyState <> 4   '読み込み待ちを追加
            Application.DoEvents()
        Loop

        'キャストする
        htmlDoc = Ie1.Document

    End Sub


    'Ie1にテキスト入力
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click

        For Each tags In htmlDoc.Document.GetElementsByClassName("gsfi")

            tags.value = "グルメ"

            Exit For
        Next

    End Sub


これですと Button1 で開いたIEブラウザが、ずっとグルグルしていて次に進まない状態です。
作り方が間違っているでしょうか。
(基本的なことがわかっていなくてすみません)



引用返信 編集キー/
■85391 / inTopicNo.6)  Re[5]: 複数のIEを操作するには
□投稿者/ 魔界の仮面弁士 (1428回)-(2017/10/17(Tue) 11:05:21)
No85388 (のり さん) に返信
> (ちょっと待たないとエラーになりそうなので、読み込み待ちを追加しています)

UI スレッドでループ待機するのは、あまり望ましい処理とは言えません。
イベント処理(Timer 等による定期監視なども含む)に置き換えた方が良いでしょう。

「CheckBox1 がチェックされたときに、Label1 の文字を赤くする」という処理のために
  Do Until CheckBox1.Checked
    Application.DoEvents()
  Loop
  Label1.ForeColor = Color.Red
などというコードを書いたりはしないですよね。それと同じことです。



> Do While Ie1.Busy = True Or Ie1.ReadyState <> 4 '読み込み待ちを追加
Or ではなく、OrElse を使う癖を付けた方が良いですよ。

また、「4」のようなマジックナンバーに頼るのではなく、
SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE などの列挙定数が良いと思います。

記述が長くなる分、冗長的ではあるのですが、マジックナンバーのままですと
「4」という値が何を意味するものなのか、意図が伝わりづらいので。


> For Each tags In htmlDoc.Document.GetElementsByClassName("gsfi")
htmlDoc 自体が「document オブジェクト」を表しますので、
そこからさらに「Document プロパティ」にアクセスする必要はありません。

そもそも documet オブジェクトに Document プロパティはありません。
(documentElement プロパティならありますが)


とはいえ、GAC に登録されている MSHTML は、古い実装のままのようです。
手元にある Microsoft.mshtml.dll は、バージョン 7.0.3300.0 の PIA でしたが、
これの HTMLDocument は IHTMLDocument7 を実装していませんでした。
(getElementsByClassName メソッドを実装しているのは IHTMLDocument7 インターフェイスです)


現状、getElementsByClassName を使うとなると、
 (案1)諦めてレイトバインドにする。
 (案2)自前でインターフェイスを明示実装する
 (案3)GAC のものを使わず、mshtml.tlb を tlbimp して取り込みなおす
という選択肢になってくると思います。

もしくは getElementsByClassName メソッドは使わず、PIA でも使える
getElementById / getElementsByName / getElementsByTagName を用いるとか。



ひとまず案3で実装してみました。
ついでに、Button の Enabled を切り替えるコードも追加しています。

とはいえ手抜きコードなので、ReleaseComObject の呼び出しや、
IE 側のスレッドとの同期制御などは省略しています。


Option Strict On
Imports mshtml = Interop.mshtml

Public Class Form1
  Inherits System.Windows.Forms.Form

  Friend togetherClose As Boolean = True

  Public WithEvents Ie1 As SHDocVw.InternetExplorer
  Public WithEvents Ie2 As SHDocVw.InternetExplorer
  Public htmlDoc1 As mshtml.HTMLDocument
  Public htmlDoc2 As mshtml.HTMLDocument

  Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Button1.Enabled = True
    Button2.Enabled = False
    Button3.Enabled = True
    Button4.Enabled = False
  End Sub

  Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Ie1 = New SHDocVw.InternetExplorer()
    Ie1.Visible = True
    Ie1.Navigate("https://www.google.co.jp/")
    Button1.Enabled = False
    Button2.Enabled = True
  End Sub
  Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    Dim elm = htmlDoc1.getElementsByClassName("gsfi").OfType(Of mshtml.HTMLInputElement).FirstOrDefault()
    If elm IsNot Nothing Then
      elm.value = "グルメ"
    End If
  End Sub
  Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    Ie2 = New SHDocVw.InternetExplorer()
    Ie2.Visible = True
    Ie2.Navigate("https://www.bing.com/")
    Button3.Enabled = False
    Button4.Enabled = True
  End Sub
  Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
    Dim elm = htmlDoc2.getElementsByClassName("b_searchbox").OfType(Of mshtml.HTMLInputElement).FirstOrDefault()
    If elm IsNot Nothing Then
      elm.value = "天気"
    End If
  End Sub

  Private Sub Ie1_DocumentComplete(pDisp As Object, ByRef URL As Object) Handles Ie1.DocumentComplete
    htmlDoc1 = DirectCast(Ie1.Document, mshtml.HTMLDocument)
    BeginInvoke(Sub() Button2.Enabled = True)
  End Sub
  Private Sub Ie2_DocumentComplete(pDisp As Object, ByRef URL As Object) Handles Ie2.DocumentComplete
    htmlDoc2 = DirectCast(Ie2.Document, mshtml.HTMLDocument)
    BeginInvoke(Sub() Button4.Enabled = True)
  End Sub

  Private Sub Ie1_OnQuit() Handles Ie1.OnQuit
    htmlDoc1 = Nothing
    Ie1 = Nothing
    BeginInvoke( _
      Sub()
        Button1.Enabled = True
        Button2.Enabled = False
      End Sub)
  End Sub
  Private Sub Ie2_OnQuit() Handles Ie2.OnQuit
    htmlDoc2 = Nothing
    Ie2 = Nothing
    BeginInvoke( _
      Sub()
        Button3.Enabled = True
        Button4.Enabled = False
      End Sub)
  End Sub
  Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
    If Ie1 IsNot Nothing Then
      htmlDoc1 = Nothing
      If togetherClose Then
        Ie1.Quit()
      End If
      Ie1 = Nothing
    End If
    If Ie2 IsNot Nothing Then
      htmlDoc2 = Nothing
      If togetherClose Then
        Ie2.Quit()
      End If
      Ie2 = Nothing
    End If
  End Sub
End Class
引用返信 編集キー/
■85392 / inTopicNo.7)  Re[6]: 複数のIEを操作するには
□投稿者/ 魔界の仮面弁士 (1429回)-(2017/10/17(Tue) 11:19:56)
No85391 (魔界の仮面弁士) に追記
> もしくは getElementsByClassName メソッドは使わず、PIA でも使える
> getElementById / getElementsByName / getElementsByTagName を用いるとか。

わざわざタイプライブラリから取り込みなおすのも手間なので、
PIA のままで使えるようにしてみました。

Option Strict On
Partial Public Class Form1
  Inherits System.Windows.Forms.Form
  Friend togetherClose As Boolean = True
  Public WithEvents Ie1 As SHDocVw.InternetExplorer
  Public WithEvents Ie2 As SHDocVw.InternetExplorer
  Public htmlDoc1 As mshtml.HTMLDocument
  Public htmlDoc2 As mshtml.HTMLDocument
  Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Ie1 = New SHDocVw.InternetExplorer()
    Ie1.Visible = True
    Ie1.Navigate("https://www.google.co.jp/")
  End Sub
  Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    Dim elm = htmlDoc1.getElementById("lst-ib")
    If elm IsNot Nothing Then
      DirectCast(elm, mshtml.IHTMLInputElement).value = "グルメ"
    End If
  End Sub
  Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    Ie2 = New SHDocVw.InternetExplorer()
    Ie2.Visible = True
    Ie2.Navigate("https://www.bing.com/")
  End Sub
  Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
    Dim elm = htmlDoc2.getElementById("sb_form_q")
    If elm IsNot Nothing Then
      DirectCast(elm, mshtml.IHTMLInputElement).value = "天気"
    End If
  End Sub
  '
  '以下、 No85391 と同様のコードが続くので省略。
  '
End Class
引用返信 編集キー/
■85400 / inTopicNo.8)  Re[7]: 複数のIEを操作するには
□投稿者/ のり (4回)-(2017/10/18(Wed) 01:16:26)
No85392 (魔界の仮面弁士 さん) に返信
> ■No85391 (魔界の仮面弁士) に追記

魔界の仮面弁士 様
お世話になります。貴重な時間を割いて返信いただき本当に感謝します。

頂いた内容で
「ループ待機」、「Or → OrElse」の件は勉強になります。
プログラムは自己流で、すべてネットを見て作成していますので基本が抜けているのが現状です。
正直申しますと、教えて頂いたことを全部は理解できていません。ご迷惑おかけしてすみません。
何とかついていけるようにします。


教えて頂いたプログラムですが
No85391 は   (●〜〜● まではエラーで下線が出て実行できませんでした )

何か私が設定を間違えているのだと思いますがわかりませんでした。

参照設定 :Microsoft Object Library
     :Microsoft Internet Controls
--------------------------------------------------------------------------
Option Strict On
Imports ●mshtml = Interop.mshtml●

Public Class Form1
    Inherits System.Windows.Forms.Form

    Friend togetherClose As Boolean = True

    Public WithEvents Ie1 As SHDocVw.InternetExplorer
    Public WithEvents Ie2 As SHDocVw.InternetExplorer
    Public htmlDoc1 As mshtml.HTMLDocument
    Public htmlDoc2 As mshtml.HTMLDocument

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Button1.Enabled = True
        Button2.Enabled = False
        Button3.Enabled = True
        Button4.Enabled = False
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Ie1 = New SHDocVw.InternetExplorer()
        Ie1.Visible = True
        Ie1.Navigate("https://www.google.co.jp/")
        Button1.Enabled = False
        Button2.Enabled = True
    End Sub
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Dim elm = ●htmlDoc1.getElementsByClassName●("gsfi").OfType(Of mshtml.HTMLInputElement).FirstOrDefault()
        If elm IsNot Nothing Then
            elm.value = "グルメ"
        End If
    End Sub
    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        Ie2 = New SHDocVw.InternetExplorer()
        Ie2.Visible = True
        Ie2.Navigate("https://www.bing.com/")
        Button3.Enabled = False
        Button4.Enabled = True
    End Sub
    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        Dim elm = ●htmlDoc2.getElementsByClassName●("b_searchbox").OfType(Of mshtml.HTMLInputElement).FirstOrDefault()
        If elm IsNot Nothing Then
            elm.value = "天気"
        End If
    End Sub

    Private Sub Ie1_DocumentComplete(pDisp As Object, ByRef URL As Object) Handles Ie1.DocumentComplete
        htmlDoc1 = DirectCast(Ie1.Document, mshtml.HTMLDocument)
        BeginInvoke(Sub() Button2.Enabled = True)
    End Sub
    Private Sub Ie2_DocumentComplete(pDisp As Object, ByRef URL As Object) Handles Ie2.DocumentComplete
        htmlDoc2 = DirectCast(Ie2.Document, mshtml.HTMLDocument)
        BeginInvoke(Sub() Button4.Enabled = True)
    End Sub

    Private Sub Ie1_OnQuit() Handles Ie1.OnQuit
        htmlDoc1 = Nothing
        Ie1 = Nothing
        BeginInvoke(
        Sub()
            Button1.Enabled = True
            Button2.Enabled = False
        End Sub)
    End Sub
    Private Sub Ie2_OnQuit() Handles Ie2.OnQuit
        htmlDoc2 = Nothing
        Ie2 = Nothing
        BeginInvoke(
        Sub()
            Button3.Enabled = True
            Button4.Enabled = False
        End Sub)
    End Sub
    Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
        If Ie1 IsNot Nothing Then
            htmlDoc1 = Nothing
            If togetherClose Then
                Ie1.Quit()
            End If
            Ie1 = Nothing
        End If
        If Ie2 IsNot Nothing Then
            htmlDoc2 = Nothing
            If togetherClose Then
                Ie2.Quit()
            End If
            Ie2 = Nothing
        End If
    End Sub
End Class
--------------------------------------------------------------------------


続いて、2番目のプログラム
No85392 は問題なく button1 → button4 までクリックすることが出来ました。
今まで何をやっても button4 でエラーになっていたのでクリックできたときは感動しました!

しかも getElementById でいけるように、名前を調べなおしていただき感謝します。

ただ、残念なことに getElementsByClassName は使えなくなるのでしょうか。

私が実現したいことは、1つのWindowsフォームアプリケーションから3つのIEでWebページを
操作して、それぞれ値の取得やWeb上のボタンをクリックすることです。
(今回のGoogleなどのWebページは例という感じで実際は別のページです)

3つのWebページを操作するプログラムは、それぞれ単独(Windowsフォームから1つのみWebページを開く)では
動作できるようになり、あとは3つ同時にWebページを開ければ目的達成なのですが。。


その際に使用している Element名 ですが、以下の方法で値の取得、ボタンのクリック
などを行っています。

@
For Each tags In IE2.Document.getElementsByClassName("expanded")
       RichTextBox1.Text = tags.innerText
       Exit For
Next

A
For Each tags In IE2.Document.getElementsByClassName("tableIcon Open")
	tags.click
	Exit For
Next

B
For Each tags In IE1.Document.getElementsByTagName("div")       
	If tags.innerText = "options" Then 
		tags.Click 
                Exit For
	End If
Next

C
RichTextBox2.Text = IE1.Document.getElementById("Header").outerHTML

D
IE2.Document.getElementById("loginbutton").Click

E
For Each tags In IE3.Document.getElementsByName("text")      
        tags.innerText = "10"
        Exit For
Next


教えて頂いたプログラム
No85392 で実現できれば最高なのですが、getElementsByClassName 等を使わずに getElementById に
置き換えられるかを F12キーで出てくるIEの DOM Explorer を使って調べていたのですが、

どうしても getElementsByClassName じゃないとダメな箇所があり、No85392 のプログラムでは
残念ながら実現できなさそうです。教えて頂いたのにすみません。



現状はそのような感じです。
自分で解決できれば良いのですが、いまの自分にはお手上げに近い状態です。

ただ、今まで一人でやってきまして、周りに質問できる人もいないので
今回こうしてアドバイスを頂けるのはとても心強いです。

解決できるまで続けてみたいと思います。

引用返信 編集キー/
■85403 / inTopicNo.9)  Re[8]: 複数のIEを操作するには
□投稿者/ BBQ (1回)-(2017/10/18(Wed) 12:36:15)
「getElementsByClassName じゃないとダメ」な箇所って、どんなタグなのでしょうか?

試してはいませんが、getElementsByTagNameで対象タグのコレクションを取得し、
そのタグのclass属性をgetAttribute("ClassName")のようにして、
指定したクラス名であるかどうかを判断する方法はいかがでしょうか?
※例えば、対象タグのvalue属性を知りたい場合は、getAttribute("value")で得られますが、
 Class属性の場合は例外で、getAttribute("Class")ではなく、getAttribute("ClassName")と
 しなくてはならなかったような気がします。
引用返信 編集キー/
■85405 / inTopicNo.10)  Re[8]: 複数のIEを操作するには
□投稿者/ 魔界の仮面弁士 (1431回)-(2017/10/18(Wed) 14:56:15)
No85400 (のり さん) に返信
> ただ、残念なことに getElementsByClassName は使えなくなるのでしょうか。

No85362 の最初のコードに対して、ディスパッチID 指定での呼び出しに
切り替えてみたところ、当方では getElementsByClassName を呼び出せました。
そちらの環境で動くかどうかは分かりませんが。

For Each tags In CallByName(Ie2.Document, "[DispId=1120]", CallType.Method, "b_searchbox")
  tags.value = "天気"
  Exit For
Next

No85362 [2017/10/13 17:35:03] のコードの参照設定を外して
 'Public Ie2 As SHDocVw.InternetExplorer
 Public Ie2 As Object
にしていた場合も、上記の DispId 指定での呼び出しは成功しました。



> 今まで何をやっても button4 でエラーになっていたのでクリックできたときは感動しました!

No85392 の回答のように、そもそも getElementsByClassName を使うのを
辞めてしまえば、標準の Microsoft.mshtml.dll を参照するだけですみます。


あるいは getElementsByClassName での直接的な探索にせず、
getElementsByTagName で探してから class 属性の値を調べるとか。

class 属性は、className プロパティで調べられますので、
こんな感じの拡張メソッドを用意しておくと便利かと思います。

Module mshtmlExtensions
  <System.Runtime.CompilerServices.Extension()>
  Public Function getElementsByTagAndClassNames( _
    doc As mshtml.HTMLDocument, _
    tagName As String, _
    ParamArray className As String()) As mshtml.IHTMLElement()
    Return doc.getElementsByTagName(tagName).OfType(Of mshtml.IHTMLElement).Where( _
     Function(tag) className.All( _
      Function(c) If(tag.className, String.Empty).Split(" "c).Contains(c) _
     ) _
    ).ToArray()
  End Function
End Module


使うときはこんな感じ。

'==== Button2_Click ====
Dim doc = DirectCast(Ie1.Document, mshtml.HTMLDocument)
Dim tag = doc.getElementsByTagAndClassNames("INPUT", "gsfi").FirstOrDefault()
If tag IsNot Nothing Then
  tag.setAttribute("value", "グルメ")
End If

'==== Button4_Click ====
Dim doc = DirectCast(Ie2.Document, mshtml.HTMLDocument)
Dim tag = doc.getElementsByTagAndClassNames("INPUT", "b_searchbox").FirstOrDefault()
If tag IsNot Nothing Then
  tag.setAttribute("value", "天気")
End If

'==== class に複数の値がセットされている場合 ====
.getElementsByTagAndClassNames("INPUT", "tableIcon Open")



> No85391 は(●〜〜● まではエラーで下線が出て実行できませんでした )

一応こちらも答えておきますと:


> 参照設定 :Microsoft Object Library

"Microsoft Object Library" というのは
"Microsoft HTML Object Library" のことですね。


> Imports ●mshtml = Interop.mshtml●

これを使う場合は、参照設定から "Microsoft HTML Object Library" を外す必要があります。

GAC に登録されている Microsoft.mshtml.dll のインターフェイス定義は古いので、
>> (案3)GAC のものを使わず、mshtml.tlb を tlbimp して取り込みなおす
という作業が必要になります。これは先に述べていますね。

なお tlbimp で名前空間の指定を行わなかった場合、既定では
mshtml ではなく Interop.mshtml という名前空間に割り当てられます。


> Public htmlDoc1 As mshtml.HTMLDocument
> Dim elm = ●htmlDoc1.getElementsByClassName●("gsfi").OfType(Of mshtml.HTMLInputElement).FirstOrDefault()

標準の Microsoft.mshtml.dll の「mshtml.HTMLDocument」では
getElementsByClassName メソッドが公開されていません。

tlbimp を通じて生成した DLL の「Interop.mshtml.HTMLDocument」であれば、
getElementsByClassName メソッドを使えるようになります。


名前空間が変わってしまっているため、mshtml.HTMLInputElement も
Interop.mshtml.HTMLInputElement にしなければなりません。これでは面倒ですよね。

そこで、名前空間に対して
 Imports mshtml = Interop.mshtml
という別名設定を施すことで、
"mshtml.HTMLInputElement" や "Interop.mshtml.HTMLDocument" という表記が
"Interop.mshtml.HTMLInputElement" や "mshtml.HTMLDocument" として
扱われるようにしていた、ということです。

※ "Interop.mshtml" と "mshtml" が両方参照されていると都合が悪いので、
 この方法を使う場合、"Microsoft HTML Object Library" の参照設定は外す必要があります。
引用返信 編集キー/
■85410 / inTopicNo.11)  Re[9]: 複数のIEを操作するには
□投稿者/ のり (5回)-(2017/10/19(Thu) 03:12:19)
No85403 (BBQ さん) に返信

返信ありがとうございます。また返信おくれましてすみません。

> 「getElementsByClassName じゃないとダメ」な箇所って、どんなタグなのでしょうか?

うまく答えられなくて申し訳ないのですが、私の今の知識では id名からは私の必要としている値以外も
取得してしまうので、それでclass以下の値を取得してから必要な値を文字列の処理で取得しています。

それと class名が変化する箇所があり、目的の class名がいくつあるかで処理を分岐させているところが
ありまして、その処理は私には class名を使った処理しか思いつかなかったので今回は class名も
使用したいと思いました。

> ※例えば、対象タグのvalue属性を知りたい場合は、getAttribute("value")で得られますが、
>  Class属性の場合は例外で、getAttribute("Class")ではなく、getAttribute("ClassName")と
>  しなくてはならなかったような気がします。

すみません、お恥ずかしい限りですが getAttribute("value")というものを使ったことがないのでどのように
使ったらよいかわからないです。

引用返信 編集キー/
■85411 / inTopicNo.12)  Re[9]: 複数のIEを操作するには
□投稿者/ のり (6回)-(2017/10/19(Thu) 05:07:39)
No85405 (魔界の仮面弁士 さん) に返信
> ■No85400 (のり さん) に返信

返信ありがとうございます。
すみません、私の知識が追いつかずアドバイスいただいたこと全てを確かめられていません。

>>ただ、残念なことに getElementsByClassName は使えなくなるのでしょうか。
> 
> No85362 の最初のコードに対して、ディスパッチID 指定での呼び出しに
> 切り替えてみたところ、当方では getElementsByClassName を呼び出せました。
> そちらの環境で動くかどうかは分かりませんが。
> 
> For Each tags In CallByName(Ie2.Document, "[DispId=1120]", CallType.Method, "b_searchbox")
>   tags.value = "天気"
>   Exit For
> Next

上記ですが、現在作成しているIE単体起動のみなら動作するプログラムを書き換えたところ、
複数IEを立ち上げても単体IEのときと同じ動作することができました。感謝します。(以下のような感じです)

        'For Each tags2 In IE2.Document.getElementsByClassName("login-form-text-input text")
        For Each tags2 In CallByName(IE2.Document, "[DispId=1120]", CallType.Method, "login-form-text-input text")

            If i = 1 Then
                tags2.value = LoginID
            ElseIf i = 2 Then  
                tags2.value = LoginPass
                Exit For
            End If

            i = i + 1
        Next
    

        'For Each tags In IE2.Document.getElementsByClassName("button button-blue login-button")
        For Each tags In CallByName(IE2.Document, "[DispId=1120]", CallType.Method, "button button-blue login-button")
            tags.click
            Exit For
        Next

すみません、ディスパッチID というものは今まで聞いたことが無かったのですが、[DispId=1120] とはどのような
数値を意味しているのでしょうか。

本来の正しいIE操作方法とは異なるのかもしれませんが、この ディスパッチID 方式でいけるならこのまま使わせて
いただきたいと考えています。

そこで ディスパッチID 方式では以下のことは可能でしょうか。

タグ名検索
        For Each tags In IE1.Document.getElementsByTagName("a") 
        Next

名前検索
        For Each tags In IE3.Document.getElementsByName("text")       
        Next

ID入力
        IE2.Document.getElementById("size-input").value= "10"

ボタンクリック
    IE1.Document.getElementById("PlaceButton").Click

アクティブ
        Dim appName As String = IE2.LocationName & " - " & IE2.Name     'ページのtitleとieの名前を取得
        AppActivate(appName)     
    SendKeys.Send("{ENTER}")        'エンターキーを送信


可能であれば ディスパッチ方式に置き換えた例を教えていただきたいです。
お手数おかけしますがアドバイス頂けると助かります。

拡張メソッド と Interop.mshtml 方式は今日以降に試させていただきます。
せっかく頂いたアドバイスに追いつけてなくて申し訳ないです。

貴重な時間を割いていただきありがとうございます。

引用返信 編集キー/
■85439 / inTopicNo.13)  Re[9]: 複数のIEを操作するには
□投稿者/ のり (7回)-(2017/10/20(Fri) 17:17:09)
No85405 (魔界の仮面弁士 さん) に返信

お世話になります。
No85391 のコードについて

>>参照設定 :Microsoft Object Library
> 
> "Microsoft Object Library" というのは
> "Microsoft HTML Object Library" のことですね。

すみません間違えていました。 "Microsoft HTML Object Library" のことです。

>>Imports ●mshtml = Interop.mshtml●

> ※ "Interop.mshtml" と "mshtml" が両方参照されていると都合が悪いので、
>  この方法を使う場合、"Microsoft HTML Object Library" の参照設定は外す必要があります。

参照設定をはずすの了解です。 実際に参照設定をはずてみたのですが、
以下のようにエラーを表す下線の場所が変化しました。

これは何か私の設定が足りないところがあるでしょうか。
質問ばかりですみません。


-----------------------------------------------------------------------------
参照設定:Microsoft Internet Controls  のみ
                     (●〜〜● まではエラーで下線が出て実行できませんでした )

Option Strict On
Imports mshtml = ●Interop.mshtml●

Public Class Form1
    Inherits System.Windows.Forms.Form

    Friend togetherClose As Boolean = True

    Public WithEvents Ie1 As SHDocVw.InternetExplorer
    Public WithEvents Ie2 As SHDocVw.InternetExplorer
    Public htmlDoc1 As ●mshtml.HTMLDocument●
    Public htmlDoc2 As ●mshtml.HTMLDocument●

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Button1.Enabled = True
        Button2.Enabled = False
        Button3.Enabled = True
        Button4.Enabled = False
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Ie1 = New SHDocVw.InternetExplorer()
        Ie1.Visible = True
        Ie1.Navigate("https://www.google.co.jp/")
        Button1.Enabled = False
        Button2.Enabled = True
    End Sub
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Dim elm = htmlDoc1.getElementsByClassName("gsfi").OfType(Of mshtml.HTMLInputElement).FirstOrDefault()
        If elm IsNot Nothing Then
            elm.value = "グルメ"
        End If
    End Sub
    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        Ie2 = New SHDocVw.InternetExplorer()
        Ie2.Visible = True
        Ie2.Navigate("https://www.bing.com/")
        Button3.Enabled = False
        Button4.Enabled = True
    End Sub
    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        Dim elm = htmlDoc2.getElementsByClassName("b_searchbox").OfType(Of mshtml.HTMLInputElement).FirstOrDefault()
        If elm IsNot Nothing Then
            elm.value = "天気"
        End If
    End Sub

    Private Sub Ie1_DocumentComplete(pDisp As Object, ByRef URL As Object) Handles Ie1.DocumentComplete
        htmlDoc1 = DirectCast(Ie1.Document, ●mshtml.HTMLDocument●)
        BeginInvoke(Sub() Button2.Enabled = True)
    End Sub
    Private Sub Ie2_DocumentComplete(pDisp As Object, ByRef URL As Object) Handles Ie2.DocumentComplete
        htmlDoc2 = DirectCast(Ie2.Document, ●mshtml.HTMLDocument●)
        BeginInvoke(Sub() Button4.Enabled = True)
    End Sub

    Private Sub Ie1_OnQuit() Handles Ie1.OnQuit
        htmlDoc1 = Nothing
        Ie1 = Nothing
        BeginInvoke(
        Sub()
            Button1.Enabled = True
            Button2.Enabled = False
        End Sub)
    End Sub
    Private Sub Ie2_OnQuit() Handles Ie2.OnQuit
        htmlDoc2 = Nothing
        Ie2 = Nothing
        BeginInvoke(
        Sub()
            Button3.Enabled = True
            Button4.Enabled = False
        End Sub)
    End Sub
    Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
        If Ie1 IsNot Nothing Then
            htmlDoc1 = Nothing
            If togetherClose Then
                Ie1.Quit()
            End If
            Ie1 = Nothing
        End If
        If Ie2 IsNot Nothing Then
            htmlDoc2 = Nothing
            If togetherClose Then
                Ie2.Quit()
            End If
            Ie2 = Nothing
        End If
    End Sub
End Class

-----------------------------------------------------------------------------



引用返信 編集キー/
■85440 / inTopicNo.14)  Re[10]: 複数のIEを操作するには
□投稿者/ 魔界の仮面弁士 (1433回)-(2017/10/20(Fri) 19:37:11)
2017/10/20(Fri) 19:37:47 編集(投稿者)

No85439 (のり さん) に返信
> 参照設定をはずすの了解です。 実際に参照設定をはずてみたのですが、
> 以下のようにエラーを表す下線の場所が変化しました。

標準の mshtml への参照設定を外した上で、
tlbimp.exe で生成した Interop.mshtml への
参照設定に差し替える、ということです。
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -