Hatena::Groupmoz-addon

Ci.nsIZIGOROu

2008-02-08イケスカナイ

WebBrowser Controlには後からConnectObject()出来ない

| 03:41 |  WebBrowser Controlには後からConnectObject()出来ない - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  WebBrowser Controlには後からConnectObject()出来ない - Ci.nsIZIGOROu

IEってひょっとして生成前から仕込まないとダメなのかな?

var browser = WScript.CreateObject("InternetExplorer.Application");
WScript.ConnectObject(browser, "browser_");

これは接続出来ないって言われる。

WebBrowser Control, MSHTMLのイベント発火とJS側の状態

| 02:49 |  WebBrowser Control, MSHTMLのイベント発火とJS側の状態 - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  WebBrowser Control, MSHTMLのイベント発火とJS側の状態 - Ci.nsIZIGOROu

どの時点でIEのスクリプトに横槍出来るか試してみた。

はじめに

実験はIE7でやってます。IE6は捨ての方向で。

またWSHでやってます。ソース見れば分かると思うけど。

ソース


var browser = WScript.CreateObject("InternetExplorer.Application", "browser_");

var console = {
  log: function(msg) {
    WScript.StdErr.Write(msg + "\n");
  }
};

function browser_BeforeNavigate2(browser, url, flags, target, postdata, headers, cancel) {
  console.log("[event] BeforeNavigate2");
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);

  with({ window: browser.Document.parentWindow }) {
    console.log("window -> " + typeof window);
    console.log("eval -> " + typeof window.eval);
  }
}

function browser_DownloadBegin() {
  console.log("[event] DownloadBegin");
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);

  if (browser.ReadyState > 0 && typeof browser.Document == "object") {
    with({ window: browser.Document.parentWindow }) {
      console.log("window -> " + typeof window);
      console.log("eval -> " + typeof window.eval);
    }
  }
}

function browser_DownloadComplete() {
  console.log("[event] DownloadComplete");
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);

  with({ window: browser.Document.parentWindow }) {
    console.log("window -> " + typeof window);
    console.log("eval -> " + typeof window.eval);

    if (browser.ReadyState == 1 && typeof window == "object") {
      window.console = console;

      window.document.attachEvent("onreadystatechange", function(evt) {
        console.log("[event] onreadystatechange");
        console.log("ReadyState : " + browser.ReadyState);
        console.log("document -> " + typeof browser.Document);
        console.log("window -> " + typeof window);
        console.log("eval -> " + typeof window.eval);
      });

      window.attachEvent("onload", function(evt) {
        console.log("[event] onload");
        console.log("ReadyState : " + browser.ReadyState);
        console.log("document -> " + typeof browser.Document);
        console.log("window -> " + typeof window);
        console.log("eval -> " + typeof window.eval);
      });
    }
  }
}

function browser_WindowStateChanged(flags, validFlags) {
  console.log("[event] WindowStateChanged : " + flags);
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);
  
  if (browser.ReadyState > 0 && typeof browser.Document == "object") {
    with({ window: browser.Document.parentWindow }) {
      console.log("window -> " + typeof window);
      console.log("eval -> " + typeof window.eval);
    }
  }
}

function browser_NavigateComplete2(ie, url) {
  console.log("[event] NavigateComplete2");
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);

  with({ window: browser.Document.parentWindow }) {
    console.log("window -> " + typeof window);
    console.log("eval -> " + typeof window.eval);
  }
}

function browser_DocumentComplete(ie, url) {
  console.log("[event] DocumentComplete");
  console.log("ReadyState : " + browser.ReadyState);
  console.log("document -> " + typeof browser.Document);

  with({ window: browser.Document.parentWindow }) {
    console.log("window -> " + typeof window);
    console.log("eval -> " + typeof window.eval);
  }
}

function browser_OnQuit() {
  console.log("[event] OnQuit");
  browser = null;
}

browser.Visible = true;
browser.Navigate2("http://sample.art-code.org/ie.html");

while (true) {
  if (!browser) {
    break;
  }
  WScript.Sleep(1000);
}

console.log("End");

結果

実際の実行結果は貼り付けると縦長になるので、表組みにしてみた。

object event ReadyState Document window eval
WebBrowser DownloadBegin 1 unknown -- --
WebBrowser WindowStateChanged 1 unknown -- --
WebBrowser DownloadComplete 1 object object undefined
WebBrowser DownloadBegin 1 object object undefined
WebBrowser NavigateComplete2 1 object object undefined
WebBrowser WindowStateChanged 1 object object undefined
HTMLDocument onreadystatechange 3 object object undefined
WebBrowser DownloadComplate 3 object object object
HTMLDocument onreadystatechange 4 object object object
WebBrowser DocumentComplate 4 object object object
HTMLWindow onload 4 object object object

こんな感じの結果となる。見分けやすいように変更点があるところをボールドにしてみた。

IE7の場合は最も早くwindowオブジェクトが生成されるのが、

  • DownloadComplateイベントの発火時
  • ReadyStateが1の時

が成り立つ時のようだ。

それ以前はdocumentオブジェクト相当がunknownとなりアクセス出来ない。またevalは当分実行出来なくて、やはりDownloadCompleteイベントの発火時で、ReadyStateが3の時に実行出来る。これじゃ全然意味が無い。*1

まとめ

  • とりあえずInternetExplorer.Applicationをダイレクトで生成すればイベントに接続出来る
  • 肝はDownloadCompleteイベント
  • evalは当分使えない
  • Document, ReadyStateの値が状態をはかるのに参考となる

って所かな。

ちなみにこれが出来るとGreasemonkey的な枠組みを作ろうと思えば出来る*2し、予め任意のオブジェクトを用意しておくことも可能です。

但しvar宣言はeval相当*3が使えないとやれないので、d:id:amachangdocumentオブジェクトを差し替えるhackを予め仕込むとかは出来ない。

但し、DownloadComplete, ReadyState = 3のときに差し込めば、実際のDOMアクセス処理が走る前には適用出来ると思いますけど。

これは画面遷移が挟まったとしても有効です。

*1:但しsetTimeoutの時間指定を0とかにしたら実行出来るかもしれない

*2:但し、WSHの権限をもろに使えるようになるのでやることによっては相当危険

*3:setTimeout, execScriptも同等の事が出来るけど

2008-01-03あけますたおめでたう

Getter/Setterの公開、JScriptの関数呼び出し

| 02:55 |  Getter/Setterの公開、JScriptの関数呼び出し - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  Getter/Setterの公開、JScriptの関数呼び出し - Ci.nsIZIGOROu

あけましておめでとうございます。新年の初エントリはいきなりニッチなネタです><

とりあえず前提はすっ飛ばす。私的なメモです。

Getter/Setter

Private foo As Object

Public Sub Set_Foo(ByRef aFoo As Object)
    foo = aFoo
End Sub

Public Function Get_Foo() As Object
    Return foo
End Function

とかでもまぁ別にいいんだけども、Get_, Set_とかダサいよねって事で、色々調べてたら、

Private fooObj As Object

Public Property Foo() As Object
    Get
        Return fooObj
    End Get
    Set(ByVal value As Object)
        fooObj = value
    End Set
End Property

こういう書き方が出来る。(参考: Property ステートメント - Visual Basic 言語リファレンス)

これはCOMにした場合、idl的には、

[id(0x00000001), propget]
VARIANT Foo();
[id(0x00000001), propputref]
void Foo([in] VARIANT rhs);

のようになる。

JScriptのFunctionオブジェクト

jscriptで自作COMからイベントを発生させたいにおける、Microsoft MVP for Visual C++社本さん*1のフォローですが、

あと、JScript独自のIDispatchExとか。

なお、関数オブジェクトは、DISPIDが0(DISPID_VALUE)で、名前が"anonymous"というメソッドを持ったCOMオブジェクトのようです。 MSDN内でもまとまった記述はなく、色々と苦労して調査しましたよ。。

jscriptで自作COMからイベントを発生させたい

と仰ってますので、IDispatch経由の遅延バインディングでJScriptのFunctionオブジェクトの呼び出しが出来るはず。これってVisual Basic 2005でも出来るのかなと色々やってたら出来ました。

Public Function Do_Callback() As Object
    Return callbackValue.GetType.InvokeMember("[DispID=0]", Reflection.BindingFlags.InvokeMethod, Nothing, Callback, Nothing, Nothing, Nothing, Nothing)
End Function

Object.GetType.InvokeMember()で出来る。

今の所は引数なしの関数オブジェクトを呼び出してるけど、InvokeMemberで引数リストを渡す事が出来るから、型変換さえ正しく出来れば問題なく任意の関数を呼び出せる。

サンプルコード

一応書いてみたコード。

<ComClass(JScriptFunction.ClassId, JScriptFunction.InterfaceId, JScriptFunction.EventsId)> _
Public Class JScriptFunction
#Region "COM GUIDs"
    ' These  GUIDs provide the COM identity for this class 
    ' and its COM interfaces. If you change them, existing 
    ' clients will no longer be able to access the class.
    Public Const ClassId As String = "e1df88cf-648b-48ec-bf30-5b92576364c2"
    Public Const InterfaceId As String = "3b8b64f8-c707-4f94-945d-447c23df7da1"
    Public Const EventsId As String = "a1299ef6-d8ea-4a6c-9b75-3cf9c6d11402"
#End Region

    ' A creatable COM class must have a Public Sub New() 
    ' with no parameters, otherwise, the class will not be 
    ' registered in the COM registry and cannot be created 
    ' via CreateObject.
    Public Sub New()
        MyBase.New()
    End Sub

    Private callbackValue As Object

    Public Property Callback() As Object
        Get
            Return callbackValue
        End Get
        Set(ByVal value As Object)
            callbackValue = value
        End Set
    End Property

    Public Function Do_Callback() As Object
        Return callbackValue.GetType.InvokeMember("[DispID=0]", Reflection.BindingFlags.InvokeMethod, Nothing, Callback, Nothing, Nothing, Nothing, Nothing)
    End Function
End Class

で動作確認はWSHだったら、

var jsfunc = new ActiveXObject("VBComTest.JSFunction");
jsfunc.Callback = function() { WScript.Echo("hogehoge"); }
jsfunc.Do_Callback();

僕の環境ではプロジェクト名がVBComTestになっているのでProgIDにもprefixとして適用されてる。

*1:「しゃもと」と読むらしい。珍しい苗字ですね。

2007-12-27似た物同士なんだなと思った

Visual Studio 2005でCOMオブジェクトを書く

| 02:50 |  Visual Studio 2005でCOMオブジェクトを書く - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  Visual Studio 2005でCOMオブジェクトを書く - Ci.nsIZIGOROu

と言うネタを今度書く。

はじめに

COMオブジェクトとの連携の「3.VB6から.NETのアセンブリを呼び出す」を一読して、Visual Basic 2005 Express EditionでもCOM連携機能が使えるようになります。

これによってVB2005 Express EditionでもCOMオブジェクトを作る事が出来ます。

実装する

プロパティの定義 (あとで書く)
メソッドの定義 (あとで書く)
イベントの定義 (あとで書く)
VBのインターフェースとCOMの実装について (あとで書く)

参考リンク

2007-12-06

Microsoft SOAP Toolkitを使ってJScriptでSOAP - 低レベルAPI (1) SoapConnector, SoapReader

| 19:48 |  Microsoft SOAP Toolkitを使ってJScriptでSOAP - 低レベルAPI (1) SoapConnector, SoapReader - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  Microsoft SOAP Toolkitを使ってJScriptでSOAP - 低レベルAPI (1) SoapConnector, SoapReader - Ci.nsIZIGOROu

低レベルAPIを使ったクライアント作成の流れ

低レベルAPIを使って自前でSoap Clientになりたい場合の流れは。

  1. HttpConnectorでInput/Outputストリームを準備する
    1. EndPointURLを明示的に指定
    2. SoapActionも明示的に指定
  2. HttpConnector.BeginMessage()
  3. SoapSerializerでリクエストデータを作る
    1. 最初にHttpConnector.InputStreamでSerializerを初期化
    2. 構造体は配列も自分で頑張って作らなきゃいけない *1
  4. HttpConnector.EndMessage() - この時点でリクエスト完了
  5. HttpConnector.OutputStreamでSoapReader.Init()する
  6. レスポンスデータはDOMで何とかする

って流れになる。

サンプル

var soapConnector = new ActiveXObject("MSSOAP.HttpConnector");
var soapSerializer = new ActiveXObject("MSSOAP.SoapSerializer");
var soapReader = new ActiveXObject("MSSOAP.SoapReader");

soapConnector.Property("EndPointURL") = "http://btonic.est.co.jp/Netdic/Netdicv10.asmx";
soapConnector.Property("SoapAction") = "http://btonic.est.co.jp/NetDic/NetDicV09/GetDicList";
soapConnector.Property("ProxyServer") = "localhost";
soapConnector.Property("ProxyPort") = 8000;

soapConnector.BeginMessage();
soapSerializer.Init(soapConnector.InputStream);

soapSerializer.startEnvelope("", "STANDARD");
soapSerializer.startBody();
soapSerializer.startElement("GetDicList", "http://schemas.xmlsoap.org/soap/envelope/", "", "SOAPSDK1");
soapSerializer.startElement("AuthTicket");
soapSerializer.endElement();
soapSerializer.endElement();
soapSerializer.endBody();
soapSerializer.endEnvelope();

soapConnector.EndMessage();

soapReader.Load(soapConnector.OutputStream);

var stream = new ActiveXObject("ADODB.Stream");
stream.Type = 2;
stream.Charset = "UTF-8";

stream.Open();
stream.WriteText(soapReader.DOM.xml);
stream.SaveToFile("result.xml");
stream.Close();

ADODB.StreamのSaveToFileは既にファイルが存在すると作ってくれないので、適宜消す事w

あと結果が見づらい場合はcygwinユーザーならば、

$ xmllint --noent --format result.xml

みたいにする。

これでとりあえずリクエストとレスポンスを低レベルで行う事は出来た。

まとめ

どう考えてもWSDL見て良しなにやってくれるwrapperが欲しい。

が作る気はしないw

*1:激しく面倒ww

smellmansmellman2007/12/07 20:58meadow付属のtelnetを勧めてる俺ってマニアックだよね><

ZIGOROuZIGOROu2007/12/09 01:17んなこたーないですw
ってありましたね、そういうの。

Emacs連携もあるですよ>mozrepl

2007-12-05本日3回目のアレですよ

Microsoft SOAP Toolkitを使ってJScriptでSOAP - 低レベルAPI (1) SoapSerializer

| 16:12 |  Microsoft SOAP Toolkitを使ってJScriptでSOAP -  低レベルAPI (1) SoapSerializer - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  Microsoft SOAP Toolkitを使ってJScriptでSOAP -  低レベルAPI (1) SoapSerializer - Ci.nsIZIGOROu

やっとここまで辿り付けた。。。

複合型を使う場合はどうしても低レベルAPIを使わなくてはならないので、その際リクエストデータを作る際に使うSoapSerializerの練習をしたかったのでした。

SoapSerializerはInit()メソッドでIStreamを実装したオブジェクトが必要で、通常SoapConnectorオブジェクトを使うんですが、ここでは単にどういうXMLを生成しているか確認する為にADODB.Streamを使います。

var soapSerializer = new ActiveXObject("MSSOAP.SoapSerializer");

var stream = new ActiveXObject("ADODB.Stream");
stream.Charset = "UTF-8";
stream.Open();

soapSerializer.Init(stream);
soapSerializer.startEnvelope("", "STANDARD");
soapSerializer.startBody();
soapSerializer.startElement("WS_Square_root", "http://www.kanetaka.net/namespace/default", "", "SOAPSDK1");
soapSerializer.startElement("MyInput", "", "", "");
soapSerializer.writeString(3);
soapSerializer.endElement();
soapSerializer.endElement();
soapSerializer.endBody();
soapSerializer.endEnvelope();
stream.Position = 0

WScript.Echo(stream.ReadText(-1));

出力結果は、

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    <SOAPSDK1:WS_Square_root xmlns:SOAPSDK1="http://www.kanetaka.net/namespace/default" SOAP-ENV:encodingStyle="">
      <MyInput SOAP-ENV:encodingStyle="">3</MyInput>
    </SOAPSDK1:WS_Square_root>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

なるほど。

とりあえず今日はここまで。

追記

  • 一部間違えてたので、ソースコード修正。 (2007-12-06T16:17:23+09:00)

ADODB.Streamを使った文字コードの取り扱い (2)

| 18:48 |  ADODB.Streamを使った文字コードの取り扱い (2) - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  ADODB.Streamを使った文字コードの取り扱い (2) - Ci.nsIZIGOROu

で次は文字コード変換。

これは簡単で、二つストリームを用意して、変換元文字コード、変換先文字コードをそれぞれ指定してから、CopyTo()メソッドで移せばOK。

var sjisStream = new ActiveXObject("ADODB.Stream");
var utf8Stream = new ActiveXObject("ADODB.Stream");

sjisStream.Charset = "Shift_JIS";
utf8Stream.Charset = "UTF-8";

sjisStream.Open();
sjisStream.LoadFromFile("C:\\path\\to\\sjis.txt");

utf8Stream.Open();
sjisStream.CopyTo(utf8Stream);
sjisStream.Close();

utf8Stream.SaveToFile("C:\\path\\to\\utf8.txt");
utf8Stream.Close();

こんな感じ。


ADODB.Streamを使った文字コードの取り扱い (1)

| 18:41 |  ADODB.Streamを使った文字コードの取り扱い (1) - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  ADODB.Streamを使った文字コードの取り扱い (1) - Ci.nsIZIGOROu

先人が激しく既出してる訳ですが、まだ未経験なので一からやる。

まず適当なディレクトリにutf-8, shift_jisなファイルをそれぞれ用意しておく。

utf-8なファイルはBOMはつけない事。

Shift_JIS, UTF-8なファイルをそれぞれCharsetを指定して読み込み標準出力へ書き込む

var s1 = new ActiveXObject("ADODB.Stream");
var s2 = new ActiveXObject("ADODB.Stream");

s1.Charset = "UTF-8";
s2.Charset = "Shift_JIS"

s1.Open();
s1.LoadFromFile("C:\\path\\to\\utf8.txt");
WScript.Echo(s1.ReadText(-1));
s1.Close();

s2.Open();
s2.LoadFromFile("C:\\path\\to\\sjis.txt");
WScript.Echo(s2.ReadText(-1));
s2.Close();

こんな感じでOK。

元の文字コードが何であれ、正しい文字コードで読み込んだ場合はWSHの場合はShift_JISで動作してるけど問題なく出力できる。

誤ったCharsetで読み込み標準出力に書き込む

var s1 = new ActiveXObject("ADODB.Stream");

s1.Charset = "UTF-8";

s1.Open();
s1.LoadFromFile("C:\\path\\to\\sjis.txt");
WScript.Echo(s1.ReadText(-1));
s1.Close();

これは何も出力されない。文字コード指定が間違えているから。


Microsoft SOAP Toolkitを使ってJScriptでSOAP - 基本編

| 16:14 |  Microsoft SOAP Toolkitを使ってJScriptでSOAP - 基本編 - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  Microsoft SOAP Toolkitを使ってJScriptでSOAP - 基本編 - Ci.nsIZIGOROu

ちょっとやりたい事があったから調べてみたよ。

ちなみにProgIDでMSSOAP.SoapClientとかがもし入ってない環境だったら、

からSDKをダウンロードしてインストールして下さい。

準備

とりあえず通信内容も見たいので、以下のようなPerlプログラムを作っておきました。

どんな物かはソース見て各自把握しよう。(ぇ

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dump qw(dump);
use HTTP::Proxy;
use HTTP::Proxy::BodyFilter::simple;

my $proxy = HTTP::Proxy->new(port => 1981);

my $body_filter = HTTP::Proxy::BodyFilter::simple->new(
    filter => sub {
        my ($self, $dataref, $message, $protocol, $buffer) = @_;

        print STDERR dump({
            data => $$dataref,
            message => $message,
            protocol => $protocol,
            buffer => $buffer
        });
    }
);

$proxy->push_filter(request => $body_filter, response => $body_filter);

$proxy->start;

これでよしなに本文はリクエスト・レスポンス共にdumpしてくれます。

今回試して見るサービス

にあるWSH版の奴を試す。

var soapClient = new ActiveXObject("MSSOAP.SoapClient");
// WSDLファイルで初期化する
soapClient.mssoapinit("http://ws.kanetaka.net/4dwsdl");
soapClient.ConnectorProperty("ProxyServer") = "localhost";
soapClient.ConnectorProperty("ProxyPort") = 1981; // 今、流行の年代らしいですw

WScript.Echo(soapClient.WS_Square_root(3));

事前にproxyサーバーを立ち上げておこう。

通信内容

リクエスト
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    <SOAPSDK1:WS_Square_root xmlns:SOAPSDK1="http://www.kanetaka.net/namespace/default">
      <MyInput>3</MyInput>
    </SOAPSDK1:WS_Square_root>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
レスポンス
<?xml version="1.0" encoding="UTF-8" ?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SOAP-ENV:Body>
    <ns1:WS_Square_rootResponse xmlns:ns1="http://www.kanetaka.net/namespace/default" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
      <Result xsi:type="xsd:float">1.73205080756887</Result>
    </ns1:WS_Square_rootResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

まとめ

とりあえず複合型が送受信データに含まれなければSoapClientだけで凄い簡単に出来る。

次回は複合型の場合について考える予定。

参考

RodriguezRodriguez2012/09/28 17:27That's not even 10 mnitues well spent!

mfojlkmfojlk2012/09/30 01:111vBeq3 , [url=http://uetxqsgbcept.com/]uetxqsgbcept[/url], [link=http://rrpikmmoyapg.com/]rrpikmmoyapg[/link], http://fdkbadzfioma.com/

mqrdvonxwkfmqrdvonxwkf2012/09/30 11:345z9xtm <a href="http://isztxmrmkykw.com/">isztxmrmkykw</a>

ysvqtuzfysvqtuzf2012/10/02 02:52jxjtOm , [url=http://yprzkgwjuvdg.com/]yprzkgwjuvdg[/url], [link=http://glnqmogpaeqw.com/]glnqmogpaeqw[/link], http://ahmjujlffyqc.com/