HeavyBommer@blog
時事ネタ、コンピュータ、家電、プログラミング等々、思ったこと/考えたこと/提案/使用レポ/Tipsなどを思いついたときに書いてゆきます
スポンサーサイト
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
CHtmlViewによるIEコアブラウザの作成
Windows(IE4以上を入れているかWin98/Win2K以降のOS)では、IEコアを使って簡単にオリジナルWebブラウザが作れます。
フリーのタブブラウザなども、多くのものはこの機能を利用しています。

さらに、VC++でMFCを使うなら、サンプルのmfcieってのを流用すればさらに簡単です。

でも、このIEコンポーネントが結構曲者で、ちょっと込み入ったことをやろうとするとすぐ壁にぶち当たります。
今回は、とあるアプリのブラウザ部分のエミュレートの為に、オリジナルのリクエストヘッダを付与するような仕組みを入れ込んだミニブラウザを作ったのですが、結構いろいろ壁がありました…
備忘録を兼ねて、それらについてメモっておきます。
まずはじめの一歩。
ブラウザコアをMFCで使うにはCHtmlView継承クラスを使うのですが、このCHtmlViewにバグがあります。
何はなくとも、この対策をはじめに行っておく必要がありますね。

MSDNのサンプルが新しいものなら最初から入っているかもしれませんが、VC++6.0付属のMSDN(大分古いですが…)のサンプルには入っていませんでした。
情報はMSの技術情報に有りますので、その通りにバグフィックス用の中間クラスを作りましょう。

次に、任意のリクエストヘッダを挿入する方法ですが、実はその手段は用意されていません
多少強引な方法を採らないと実現できません。

具体的な手順ですが、CHtmlViewはURLを読み込む「前」にOnBeforeNavigate2(Navigate2でURLを読み込む場合。サンプルはこうなってます)を呼び出します。
ここで、lpszHeadersの中身をチェックし、自分が必要なヘッダが含まれて居ない場合はヘッダ情報を付加した上でNavigate2を呼びなおす必要があります。
#ちゃんとヘッダが入っているかどうかをチェックしないと無限ループになりますので注意

OnBeforeNavigate2の中から直接Navigate2を呼んでしまっても普通に動作するようですが、私の場合はURLやヘッダを退避して、自分自身にユーザーメッセージを投げています。
ヘッダ不足の場合のOnBeforeNavigate2はpbCancelをTRUEにセットして動作をキャンセルし、ユーザーメッセージの処理の方で退避したURLとヘッダを使ってNavigate2を再発行します。

とりあえず、これでオリジナルのリクエストヘッダは付加できます。
が、細かい問題が結構あります。
以下に問題と対策を。

・値のないヘッダを入れると全ヘッダが無視される
例えば「X-Option:abc」とかいったヘッダを付加したいとしましょう。
このとき、「X-Option:」と言うように、値無しのヘッダを設定してしまうと、IEコンポーネントの方で全部のヘッダが削除されてしまいます。
サーバに届くのはUAなんかの標準のヘッダだけになります。
なので、値がない場合はエントリ自体を入れないように注意してください。

・更新(Reflesh)だと全ヘッダが無視される
RefleshでもReflesh2でも同じですが、これらの動作ではOnBeforeNavigate2自体が呼ばれません。
ですから、そもそもヘッダが付加できません。
対策としては、更新の場合にもReflesh系を使わないで、同じURLをNavigate2するしかありません。
が、その方法だと次の問題が発生します…

・キャッシュを無効にできない
Navigate2のnFlagsでキャッシュ機能を無効化できるように一見見えます。(古いMSDNではキャッシュ制御系のフラグが無効だとは書いていない)
ですが、(新しいMSDNを見ている人には自明ですが)これらのフラグは実際には機能しません。
いろいろ調べてみたのですが、結局キャッシュを無効化する方法は(IE自体のキャッシュ設定を変えない限り)無いようです。
でも、リフレッシュが機能しないし、同じURLを見に行くとCGIは動かないしで問題ありまくりです。

この対策は少々厄介で、不完全な方法しかありません。
その方法とは「余分なCGIパラメータ(GETパラメータ)をURLに付加する」方法です。
GetTickCountなんかを使って、ユニークなダミーパラメータを作り、それをURLのお尻に追加します。
ただ、GETパラメータは「URLとパラメータの区切りは'?'」「パラメータ同士の区切りは'&'」なので、既にURLにGETパラメータがある場合とない場合で処理を変えないとなりません。

また、誤動作を防止するなら、パラメータ名が正規のものと被らないようにする必要もあります。
私の場合は、限定されたサーバが対象だったので、ダミーパラメータ名は決め撃ちでやってしまいました。

・上記の対策を採ると更新する度にヒストリに追加されてしまう
これは簡単で、更新の場合のNavigate2にnavNoHistoryフラグ(ヒストリに残さない)を追加すれば良いだけです。

・上記の対策を採るとURL入力欄にダミーパラメータ付きのURLが表示されてしまう
サンプルのmfcieの場合はOnDocumentCompleteでURLを表示しているので、そこの部分を弄ってダミーパラメータを取っ払う処理を追加します。

・URL入力欄に表示桁数以上の入力が出来ない
サンプルのmfcieではアドレス入力欄のコンボボックスをリソースファイルを使わずに作っていますので、コンボボックスの初期化コードを弄ってスタイルを追加すればOKです。
具体的には、CMainFrame::OnCreateでm_wndAddress.Createを呼んでいる部分にCBS_AUTOHSCROLLを追加します。

・デバッグ実行するとAssertion Failureが発生する
なぜかこれが出ますね。
理由は良くわかりませんが、リリースでは問題ないですし、MFC内ですので放置で良いでしょう。
デバッグする時は(ウザいですが)「無視」を押せば先に進みます。
まあ、起動時に3回ほどだけなので…

最後に、ユーザ定義メッセージの処理方法を書いておきます。
ユーザ定義メッセージはクラスウィザードなんかでは設定できないので、自前で書かないとなりません。
まず、メッセージを処理するクラスのインプリメントの方(.cppの方)の頭で以下のような定義をします。

static UINT WM_NAVIGATE = ::RegisterWindowMessage(_T("WM_NAVIGATE"));
まあ、絶対に被らないメッセージ番号が解るのなら、RegisterWindowMessageを使わずにdefine定義でも良いのですが、WM_USERの範疇でもMFCが使っているものとかありますからね…
これを使っといたほうが安全でしょう。

で、次に同じインプリメントの中で、END_MESSAGE_MAP()を探します。
これの直前に
ON_REGISTERED_MESSAGE(WM_NAVIGATE, OnNavigate)
見たいな感じにメッセージ名と処理するメソッド名を書きます。

メッセージを処理するコールバックメソッドは以下のような感じにヘッダファイルに定義します。
afx_msg void OnNavigate(WPARAM wParam, LPARAM lParam);
場所はDECLARE_MESSAGE_MAP()の直前です。

これで準備は完了です。
あとは、定義したメッセージコールバックメソッドの実態をインプリメントファイルに書くだけです。
スポンサーサイト

テーマ:プログラミング - ジャンル:コンピュータ

コメント
この記事へのコメント
コメントを投稿する
URL:
Comment:
Pass:
秘密: 管理者にだけ表示を許可する
 
トラックバック
この記事のトラックバックURL
この記事へのトラックバック
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。