C++でFrame分割されたページを取得するには

解決


boo  2006-06-29 11:08:13  No: 62357

初心者です。よろしくお願いいたします。
フレーム分割されたページを取得してテキストボックスに
入力したりボタンをクリックする処理をC++で実行できる
用にしたいと考えています。

mshtml.tlbを使用することで実現できると考えて
おりますが、フレーム分割されたページを取得する
プログラムの記述がわかりません。

また、これからコーディングを進めていく上でこの手の問題に
直面したときの調査の仕方を教示していただけたら幸いです。

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

開発環境は、Windows Xp, VC++.net2002です。


επιστημη  2006-06-29 19:04:54  No: 62358

<frameset>タグ内に書かれたURLに対し、再度HTTP-GETすりゃええのでは?


boo  2006-06-30 07:26:05  No: 62359

επιστημη さん
HTTP-GETを調べてみましたが、勉強不足のためよくわかりません。
もうしわけありません。

VBAでよくやられている下記のような処理を考えています。

    'ドキュメントオブジェクトの代入
    Set objFRAME = objIE.document.frames
    Set objDOC0 = objFRAME(0).document
    
    'リンクのアンカーオブジェクトをクリックする
    Dim ret As Variant
    
    For n = 0 To objDOC0.Links.Length - 1
        'Debug.Print objDOC0.Links(n).href 'デバックで表示する
        ret = InStr(1, objDOC0.Links(n).href, "FraStkOrder.jsp", vbBinaryCompare)
                
        If ret <> 0 And IsNull(ret) = False Then
            objDOC0.Links(n).Click
            Exit For
        End If
    Next n

VBAではできることをC++で実現しようと思っています。
よろしくお願いいたします。


Blue  2006-06-30 09:18:10  No: 62360

以下のサンプルとVBAのソースを見比べて、どのように対応しているか
確認してみてください。

私は基本的には、VCのインテリセンス機能を使って、メンバ変数やメソッドがあるか確認しました。
IHTMLDocumentPtrやらIHTMLDocument2PtrやらIHTMLDocument3Ptrの使い分けはメンバがあるかないかでしかないです。

サンプル)
// この掲示板のTop→Visual C++のページに移動する
#include <windows.h>
#include <comdef.h>

#import "shdocvw.dll" rename( "FindText", "FindTextIE" )
#import "mshtml.tlb" rename( "TranslateAccelerator", "TranslateAcceleratorHTML" )

HRESULT Test()
{
    HRESULT hr;

    hr = ::CoInitialize( NULL );
    if ( FAILED( hr ) ) return hr;

    try
    {
        SHDocVw::IWebBrowser2Ptr pBrowser;

        // IEを立ち上げ、Programming Library を表示
        hr = pBrowser.CreateInstance( __uuidof( SHDocVw::InternetExplorer ) );
        if ( FAILED( hr ) ) _com_issue_error( hr );
        pBrowser->Visible = VARIANT_TRUE;
        pBrowser->Navigate( _bstr_t( L"http://madia.world.coocan.jp/" ) );

        // ページが表示されるまで待つ
        while ( pBrowser->Busy == VARIANT_TRUE ||
                pBrowser->ReadyState != READYSTATE_COMPLETE )
            ::Sleep( 100 );

        MSHTML::IHTMLDocument2Ptr pDocument( pBrowser->Document );

        // すべてのフレームをチェック
        MSHTML::IHTMLFramesCollection2Ptr pFrameCollection( pDocument->frames );
        for ( long i = 0L; i < pFrameCollection->length; i++ )
        {
            MSHTML::IHTMLWindow2Ptr   pFrame( pFrameCollection->item( &_variant_t( i ) ) );
            MSHTML::IHTMLDocument2Ptr pDoc( pFrame->document );
            // すべてのリンクをチェック
            MSHTML::IHTMLElementCollectionPtr pCollection( pDoc->links );
            for ( long j = 0L; j < pCollection->length; j++ )
            {
                // ※DispHTMLLinkElementPtrだと、hrefがうまく取れないので、Anchorにしてある
                MSHTML::DispHTMLAnchorElementPtr pAnchor( pCollection->item( _variant_t( j ) ) );
                // 該当の URL か?
                if ( _bstr_t( pAnchor->href ) == _bstr_t( L"http://madia.world.coocan.jp/vc/index.html" ) )
                {
                    pAnchor->click();
                    // ページが表示されるまで待つ
                    while ( pBrowser->Busy == VARIANT_TRUE || 
                            pBrowser->ReadyState != READYSTATE_COMPLETE )
                        ::Sleep( 100 );

                    break;
                }
            }
        }
    }
    catch ( _com_error& e )
    {
        hr = e.Error();
    }

    ::CoUninitialize();

    return hr;
}

int main()
{
    Test();
    return 0;
}

> 'デバックで表示する
デバッグです。

VCでどうしてもかけないのであれば、VBAではなくVBSで該当の動作をするコードを書き
それをVCから起動させるのもありかなぁと。


Blue  2006-06-30 18:10:32  No: 62361

> if ( _bstr_t( pAnchor->href ) == _bstr_t( L"http://madia.world.coocan.jp/vc/index.html" ) )
ここは pAnchor->href の戻り値は _bstr_t型なんで、単に
if ( pAnchor->href == _bstr_t( L"http://madia.world.coocan.jp/vc/index.html" ) )
でよかったです。

それと、VBAを使っているのであれば、ローカルウィンドウを十分に活用しましょう。
例えば

' VC++掲示板のリンク一覧をイミディエイトウィンドウに列挙
Sub test()
    Dim objIE     As Object
    Dim objAnchor As Object
    Set objIE = CreateObject("InternetExplorer.Application")
    objIE.Visible = True
    objIE.Navigate "http://madia.world.coocan.jp/cgi-bin/Vcbbs/wwwlng.cgi"
    While objIE.busy
        DoEvents
    Wend
    For Each objAnchor In objIE.Document.links
        Debug.Print objAnchor.href ' ※
    Next
    Set objIE = Nothing
End Sub

というのをステップ実行して、※のところにブレイクポイントを置き、
ローカルウィンドウで objAnchor の型を確認してみると

Object/HTMLAnchorElement

となっているのが確認できます。
そこから、目星をつけてMSHTML:: で探します。
で、MSHTML::IHTMLAnchorElementPtrでは使いたいメソッドやメンバ変数があ
るかなければ、MSHTML::IHTMLAnchorElement2Ptrではどうかとうチェックしていきます。
ただ、今回の場合は結構わかりやすいオブジェクト名になっているので、
いちいちVBAで確認しなくても
> Set objFRAME = objIE.document.frames

MSHTML::IHTMLFramesなんたらPtr かなとわかります。


boo  2006-07-19 11:54:07  No: 62362

Blueさん
ご教授いただきましてありがとうございます。
少し時間がたってしまいすいません。

サンプル通り、フレームを取得しリンクページに遷移することが
出来ました。

MSHTML::IHTMLDocument2Ptrを使用してフレームを取得しテキスト
ボックスに入力する際、メンバにgetElementsByNameがないため
使用することができないのですが、他の方法でテキストボックスに
入力する方法はありますか。


Bllue  2006-07-19 20:21:56  No: 62363

とりあえず、VBAレベルのソースを考えてください。
そこからVCのソースにするにはどうすればよいかは前のレスで説明済み。

半月レスがない上に、自分で考えた形跡もないのでは、、、回答者としてはカナリ萎えますよ。


boo  2006-07-22 06:49:13  No: 62364

VBAでは下記のような処理になります。

Dim objFRAME As FramesCollection
Dim objDOC0 As IHTMLDocument

Set objFRAME = objIE.document.frames
Set objDOC0 = objFRAME(0).document

objDOC0.all.dscrCDsearch.Value = Range("A1")

教えていただきましたサンプルからフレームのドキュメントを取得する
は実現することができます。
前回、リンクを取得する型が変わらなかったのと同様に、テキストボックス
を取得する型がわからずにいます。

Debug文で型を知ることができるとわかりましたが、
今のVBAからどう記述すればわかるのかわからず。

getElementsByName( TagName )で取得はできますが、IHTMLDocument2Ptrには
メンバになく使うことができません。

どうしたら良いかご教授ください。
宜しくお願いいたします。


Blue  2006-07-22 10:11:45  No: 62365

IHTMLDocument3PtrにgetElementsByTagNameってのがあるので、そこからタグをinput限定しIHTMLElementCollectionPtrを取得し、
IHTMLElementCollectionPtrからすべてのIHTMLElementPtrをループしgetAttributeで text であるものを取得するとか。


Blue  2006-07-22 10:14:48  No: 62366

あれ?
> getElementsByName( TagName )で取得はできますが、IHTMLDocument2Ptrには
> メンバになく使うことができません。

getElementsByNameがあるんだったら、それ用にIHTMLDocument3Ptrの変数を用意して取得するだけなのでは?

// 用途によって、利用を切り分ける
MSHTML::IHTMLDocument2Ptr pDocument2( pBrowser->Document );
MSHTML::IHTMLDocument3Ptr pDocument( pBrowser->Document );


Blue  2006-07-22 10:32:45  No: 62367

ちなみに
> 2006/06/30(金) 00:18:10
のサンプルでは、Frameを取得するためにトップのDocumentはIHTMLDocument2Ptrにしてありますが、
for文の中の
>  MSHTML::IHTMLDocument2Ptr pDoc( pFrame->document );
は links プロパティを使いたいがために IHTMLDocument2Ptr にしました。
結局 Aタグ の要素を取得するのであるならば、

MSHTML::IHTMLDocument3Ptr pDoc( pFrame->document );
// すべてのリンクをチェック
MSHTML::IHTMLElementCollectionPtr pCollection( pDoc->getElementsByTagName( _bstr_t( L"A" ) ) );

で同様の動きはできると思います。


boo  2006-07-24 09:43:44  No: 62368

ご教授ありがとうございます。
INPUTタグを抽出し目的の名前があった時、
処理をすることで実現することができました。
ありがとうございます。

// すべてのINPUTタグをチェック
MSHTML::IHTMLElementCollectionPtr pCollection( pDoc->getElementsByTagName( _bstr_t( L"INPUT" ) ) );

for ( long j = 0L; j < pCollection->length; j++ )
{
        MSHTML::DispHTMLInputElementPtr pInText( pCollection->item( _variant_t( j ) ) );

        //INPUTタグにはname要素がない時がある
        if ((LPCTSTR)pInText->name != (NULL))
        {
                 if ( _bstr_t( pAnchor->href ) == _bstr_t( L"http" ) )
                {
                        処理内容
                }
        }
}


Blue  2006-07-24 10:13:26  No: 62369

> //INPUTタグにはname要素がない時がある
> if ((LPCTSTR)pInText->name != (NULL))
ここは、個人的には

if ( pInText->name.length() == 0 )

です。
ちなみに、テキストボックスかどうかはtypeもみないといけないです。

if ( _wcsicmp( pInText->type, L"text" ) == 0 )
// if ( pInText->type == _bstr_t( L"text" ) )

見たいなので、確認するとよいでしょう。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加