2008-02-08イケスカナイ
WebBrowser Controlには後からConnectObject()出来ない
IEってひょっとして生成前から仕込まないとダメなのかな?
var browser = WScript.CreateObject("InternetExplorer.Application"); WScript.ConnectObject(browser, "browser_");
これは接続出来ないって言われる。
WebBrowser Control, MSHTMLのイベント発火とJS側の状態
どの時点で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:amachangのdocumentオブジェクトを差し替えるhackを予め仕込むとかは出来ない。
但し、DownloadComplete, ReadyState = 3のときに差し込めば、実際のDOMアクセス処理が走る前には適用出来ると思いますけど。
これは画面遷移が挟まったとしても有効です。
2008-01-03あけますたおめでたう
Getter/Setterの公開、JScriptの関数呼び出し
あけましておめでとうございます。新年の初エントリはいきなりニッチなネタです><
とりあえず前提はすっ飛ばす。私的なメモです。
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オブジェクトを書く
と言うネタを今度書く。
はじめに
COMオブジェクトとの連携の「3.VB6から.NETのアセンブリを呼び出す」を一読して、Visual Basic 2005 Express EditionでもCOM連携機能が使えるようになります。
これによってVB2005 Express EditionでもCOMオブジェクトを作る事が出来ます。
実装する
プロパティの定義 (あとで書く)
メソッドの定義 (あとで書く)
イベントの定義 (あとで書く)
VBのインターフェースとCOMの実装について (あとで書く)
参考リンク
- COMオブジェクトとの連携
- http://hyons.hp.infoseek.co.jp/ref/type.shtml
- http://msdn2.microsoft.com/ja-jp/library/x66s8zcd.aspx
- http://msdn2.microsoft.com/ja-jp/library/2044hysa(VS.80).aspx
- http://msdn2.microsoft.com/ja-jp/library/sd032a17.aspx
- http://msdn2.microsoft.com/ja-jp/library/y4wy1d42.aspx
- http://msdn2.microsoft.com/ja-jp/library/6bw51z5z.aspx
- http://msdn2.microsoft.com/ja-jp/library/8877bdk6.aspx
- http://msdn2.microsoft.com/ja-jp/library/ms172890.aspx
2007-12-06
Microsoft SOAP Toolkitを使ってJScriptでSOAP - 低レベルAPI (1) SoapConnector, SoapReader
COM, ActiveX, SOAP, JScript, JavaScript | |
低レベルAPIを使ったクライアント作成の流れ
低レベルAPIを使って自前でSoap Clientになりたい場合の流れは。
- HttpConnectorでInput/Outputストリームを準備する
- EndPointURLを明示的に指定
- SoapActionも明示的に指定
- HttpConnector.BeginMessage()
- SoapSerializerでリクエストデータを作る
- HttpConnector.EndMessage() - この時点でリクエスト完了
- HttpConnector.OutputStreamでSoapReader.Init()する
- レスポンスデータは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
2007-12-05本日3回目のアレですよ
Microsoft SOAP Toolkitを使ってJScriptでSOAP - 低レベルAPI (1) SoapSerializer
COM, ActiveX, SOAP, JScript, JavaScript | |
やっとここまで辿り付けた。。。
複合型を使う場合はどうしても低レベル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)
COM, ActiveX, JScript, JavaScript, ADODB | |
で次は文字コード変換。
これは簡単で、二つストリームを用意して、変換元文字コード、変換先文字コードをそれぞれ指定してから、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)
COM, ActiveX, JScript, JavaScript, ADODB | |
先人が激しく既出してる訳ですが、まだ未経験なので一からやる。
まず適当なディレクトリに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 - 基本編
COM, ActiveX, SOAP, JScript, JavaScript | |
ちょっとやりたい事があったから調べてみたよ。
ちなみに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だけで凄い簡単に出来る。
次回は複合型の場合について考える予定。
参考
RodriguezThat's not even 10 mnitues well spent!
mfojlk1vBeq3 , [url=http://uetxqsgbcept.com/]uetxqsgbcept[/url], [link=http://rrpikmmoyapg.com/]rrpikmmoyapg[/link], http://fdkbadzfioma.com/
mqrdvonxwkf5z9xtm <a href="http://isztxmrmkykw.com/">isztxmrmkykw</a>
ysvqtuzfjxjtOm , [url=http://yprzkgwjuvdg.com/]yprzkgwjuvdg[/url], [link=http://glnqmogpaeqw.com/]glnqmogpaeqw[/link], http://ahmjujlffyqc.com/
ってありましたね、そういうの。
Emacs連携もあるですよ>mozrepl
<a href=" http://temresults2018.com/ ">bbcode</a>
<a href="http://temresults2018.com/">html</a>
http://temresults2018.com/ simple