DOBON.NET DOBON.NETプログラミング掲示板過去ログ

IPersistFileのクラスを作成使用するには?

環境/言語:[XP,7,VB2005express]
分類:[.NET]

2012/08/26(Sun) 21:20:17 編集(投稿者)
2012/08/26(Sun) 19:57:29 編集(投稿者)

ブラウザを起動せずにパラメータ送信必要なWebソースの取得をしようとしてます
次のように使用するつもりです
text(0) = doc.all(100).innertext
ExcelVBAで稼働していたものをVB2005expressで作る必要に迫られ困っています

MSXML2.XMLHTTP30ClassによるResponsetextの取得まで問題ありません
(responsexmlは取得されません)
********************************************************************************
大きく分けて2点教えていただきたいです

1.既出の”「WebBrowser」でソースコードを取得するには?”
http://hpcgi1.nifty.com/MADIA/VBBBS/wwwlng.cgi?print+200310/03100017.txt
を参考に(以下、原本と記載)クラスモジュールを以下のように改変しましたが、
SetObject()の実行時に、メモリアドレスの取得に失敗しますので正しいコードを指導願います

2.IPersistFileクラスが完成したとして、SetObject(ByVal Source As Object)
の引数に入れるべきobjectがわかりません
(1に記載した”SetObject()の実行時”は仕方なしにResponsetextを引数としました)
同様のことをExcel VBAで行なっていたときは
-----------------------------
Dim stm As New PersistStreamInit  'yu-tang氏作成のClass(公開)
Set doc = New MSHTML.HTMLDocument
...(略).....
stm = doc
stm.Load Http.ResponseStream
text(0) = doc.all(100).innertext
-------------------------------
としていましたが、docにあたるものをVBで作ることができません

'1.クラスモジュールIPersistFile.vbのコード
Option Explicit On
Imports System.Runtime.InteropServices

Public Class Persistfile

Private Structure UUID
Public Data1 As Long
Public Data2 As Integer
Public Data3 As Integer
Public Data4() As Byte
End Structure


Private mudtIPersistFile As UUID

Private Const autCCStdCall = 4
Private Declare Function DispCallFunc Lib "oleaut32" _
(ByVal pvInstance As Long, _
ByVal oVft As Long, _
ByVal CallConv As Integer, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByRef prgvt As Integer, _
ByRef prgpvarg As Long, _
ByRef pvargResult As Object _
) As Long

Private mobjSource As Object
Private mlpPersistFile As Long


Private Const comIUnknown_QueryInterface = 0
Private Const comIUnknown_Release = 8
Private Const comIPersistFile_Save = 24

Public Sub New()
ReDim mudtIPersistFile.Data4(7)
'{0000010b-0000-0000-C000-000000000046}
With mudtIPersistFile
.Data1 = &H10B
.Data4(0) = &HC0
.Data4(7) = &H46
End With
End Sub

Public Sub SetObject(ByVal Source As Object)
'******以下宣言部でError"オブジェクトに、プリミティブでないか、
' または blittable でないデータが含まれています。"**********
Dim gch1 As GCHandle = GCHandle.Alloc(Source, GCHandleType.Pinned)
Dim Adr1 As Integer = gch1.AddrOfPinnedObject().ToInt32()

Dim gch2 As GCHandle = GCHandle.Alloc(mudtIPersistFile, GCHandleType.Pinned)
Dim Adr2 As Integer = gch2.AddrOfPinnedObject().ToInt32()

Dim gch3 As GCHandle = GCHandle.Alloc(mlpPersistFile, GCHandleType.Pinned)
Dim Adr3 As Integer = gch3.AddrOfPinnedObject().ToInt32()
'***************************************************************************
mobjSource = Source
If Source Is Nothing Then
mlpPersistFile = 0
Else
Dim hResult As Long

If mlpPersistFile <> 0 Then
Call Invoke_(mlpPersistFile, comIUnknown_Release)
mlpPersistFile = 0
End If

hResult = Invoke_(Adr1, comIUnknown_QueryInterface, _
Adr2, Adr3)
If hResult < 0 Then
mlpPersistFile = 0
Err.Raise(hResult)
End If
End If
gch1.Free()
gch2.Free()
gch3.Free()

End Sub

Public Sub Save(ByVal pszFileName As String, ByVal fRemember As Boolean)
Dim bytFileName() As Byte
Dim hResult As Long
Dim lngBool As Long
ReDim bytFileName(0) '*************(*)1
Dim gch4 As GCHandle = GCHandle.Alloc(bytFileName(0), GCHandleType.Pinned)
Dim Adr4 As Integer = gch4.AddrOfPinnedObject().ToInt32()

If mlpPersistFile = 0 Then

Err.Raise(91)
Exit Sub
End If

bytFileName(0) = pszFileName & vbNullChar '*************(*)2
lngBool = IIf(fRemember, 1&, 0&)

hResult = Invoke_(mlpPersistFile, comIPersistFile_Save, _
Adr4, lngBool)
If hResult <> 0 Then
Err.Raise(hResult)
End If
gch4.Free()

End Sub

Private Function Invoke_(ByVal lpObject As Long, ByVal VtblOffset As Long, _
ByVal ParamArray Args() As Object) As Long
Dim lngPtArgs() As Long
Dim intVtArgs() As Integer
Dim varResult As Object
Dim lngArgs As Long
Dim n As Long

Dim gch5 As GCHandle = GCHandle.Alloc(Args(n), GCHandleType.Pinned)
Dim Adr5 As Integer = gch5.AddrOfPinnedObject().ToInt32()

If lpObject = 0 Then
Exit Function
End If

lngArgs = UBound(Args) - LBound(Args) + 1
If lngArgs = 0 Then
ReDim lngPtArgs(0), intVtArgs(0)
Else
ReDim lngPtArgs(lngArgs - 1), intVtArgs(lngArgs - 1)
For n = 0 To lngArgs - 1
intVtArgs(n) = VarType(Args(n))
lngPtArgs(n) = Adr5
Next
End If

n = 0
n = DispCallFunc(lpObject, VtblOffset, autCCStdCall, _
vbLong, lngArgs, intVtArgs(0), lngPtArgs(0), varResult)
If n >= 0 Then
Invoke_ = CLng(varResult)
End If
gch5.Free()
End Function

Protected Overrides Sub finalize()
Call Invoke_(mlpPersistFile, comIUnknown_Release)
mlpPersistFile = 0
mobjSource = Nothing
End Sub
End Class

--------------------------
(*)1は原本にはないものだが(*)2の原本において配列のErrorが出るため追加
(*)2の原本は bytFileName = pszFileName & vbNullChar

********************************************************************************
長くなり申し訳ありませんが、ご指導願います
■No30853に返信(ミソモさんの記事)
> ExcelVBAで稼働していたものをVB2005expressで作る必要に迫られ困っています
VBA には VBA の、.NET には .NET の流儀があるので…そのまま置き換えても駄目ですよ。


> MSXML2.XMLHTTP30ClassによるResponsetextの取得まで問題ありません
MSXML3 は OOM コンポーネントですので、VB2005 から利用するのであれば、
WebRequest / WebResponse クラスに差し替えた方が良いでしょう。


> SetObject()の実行時に、メモリアドレスの取得に失敗しますので正しいコードを指導願います
API の宣言が間違っているのが原因でしょうね…。

それと、その引数に渡すべきオブジェクトは、ActiveX 版の WebBrowser ならば
VBA の場合と同様に AxWebBrowser1.Document で良いと思いますが、
マネージの System.Windows.Forms.WebBrowset を使っているなら、
WebBrowser1.Document を渡すのではなく、
WebBrowser1.Document.DomDocument を渡す必要があります。


> 2.IPersistFileクラスが完成したとして、SetObject(ByVal Source As Object)
> の引数に入れるべきobjectがわかりません
使いたいのは IPersistStreamInit ではなく、IPersistFile なのですね?
.NET の場合は、標準で IPersistFile インターフェイスが用意されているため、
わざわざ自分で {0000010b-0000-0000-C000-000000000046} な
インターフェイスを用意せずとも

 Dim p As System.Runtime.InteropServices.ComTypes.IPersistFile
 p = WebBrowser1.Document.DomDocument
 p.Save("C:\temp\test.html",True)
 System.Runtime.InteropServices.Marshal.ReleaseComObject(p)

だけで保存できますよ。
# .NET 1.x では System.Runtime.InteropServices.UCOMIPersistFile で代用。


> Dim stm  As New PersistStreamInit  'yu-tang氏作成のClass(公開)
IPersistStreamInit を使いたいなら、IID は
 {7FD52380-4E07-101B-AE2D-08002B2EC713} です。
一応、GAC から Microsoft.VisualStudio.OLE.Interop.dll を参照設定すれば、
Microsoft.VisualStudio.OLE.Interop.IPersistStreamInit を利用できますが…。


>     Private Structure UUID
この UUID というのは、.NET でいうところの System.Guid 構造体のことなので
わざわざ再定義する必要は無かったりします。

まぁ、再定義したいのであればそれでも構いませんが、見た感じ、
VBA から .NET へのデータ型の置き換えに失敗しているようですね。

VBA の Integer  型 は、VB.NET では System.Int16 構造体(すなわち、Short  型)に相当します。
VBA の Long     型 は、VB.NET では System.Int32 構造体(すなわち、Integer型)に相当します。
VBA の LongLong 型 は、VB.NET では System.Int64 構造体(すなわち、Long   型)に相当します。
VBA の LongPtr  型 は、VB.NET では System.IntPtr構造体に相当します。

参考にしているコードは x86 版の VBA/VB6 でしょうから、LongLong / LongPtr 型は無かったかもしれませんが、
いずれにせよ Integer だった箇所は Short に置き換えるべきですし、Long も Long のままでは NG です。
(VBA で Long だった箇所は、文脈によって Integer に置き換える場合と、IntPtr に置き換える場合とがあります。


>         ReDim mudtIPersistFile.Data4(7)
>         '{0000010b-0000-0000-C000-000000000046}
>         With mudtIPersistFile
>             .Data1 = &H10B
>             .Data4(0) = &HC0
>             .Data4(7) = &H46
>         End With
これも、本来は
 Private IID_IPersistFile As New Guid("{0000010b-0000-0000-C000-000000000046}")
という一行で済ませられますよ。

とはいえ今回のケースにおいては IPersistFile 自体がすでに
System.Runtime.InteropServices.ComTypes 名前空間にて
提供されているので、そもそもこのクラスを作る意味すらないのですけれどね。
冗長な質問にも関わらず、丁寧な説明いただき感謝しきりです
ご忠告に従い、まずはWebRequest/WebResponseを使用する事から始めました
http://dobon.net/vb/dotnet/internet/webrequest.html
の丸写しですので当然ですが無事ソースを取得できました
********************************************************************************
     sub test
        
        Dim enc As System.Text.Encoding = _
            System.Text.Encoding.GetEncoding("shift_jis")

        Dim postData As String = "xxxxxxx"
            
        Dim postDataBytes As Byte() = _
            System.Text.Encoding.ASCII.GetBytes(postData)

        Dim req As System.Net.WebRequest = _
            System.Net.WebRequest.Create("http://xxx")

        req.Method = "POST"
        req.ContentType = "application/x-www-form-urlencoded"
        req.ContentLength = postDataBytes.Length

        Dim reqStream As System.IO.Stream = req.GetRequestStream()
        reqStream.Write(postDataBytes, 0, postDataBytes.Length)
        reqStream.Close()

        Dim res As System.Net.WebResponse = req.GetResponse()
        Dim resStream As System.IO.Stream = res.GetResponseStream()
        Dim sr As New System.IO.StreamReader(resStream, enc)

        Debug.Print(sr.ReadToEnd())
        sr.Close()

    end sub

文字化けしますので、これから文字コードの変換方法を調べます
********************************************************************************

 > Dim p As System.Runtime.InteropServices.ComTypes.IPersistFile
 > p = WebBrowser1.Document.DomDocument
 > p.Save("C:\temp\test.html",True)
 > System.Runtime.InteropServices.Marshal.ReleaseComObject(p)

今の私ではWebBrowser1をブラウザ立ち上げずに使用する方法がわかりませんが
IPersistFileをこうして使用出来ることがわかり、諸々調べる道筋がはっきりしました
参照先としてしか調べていませんでしたので、msdnの情報の活かし方が全くわからなかった次第です

ひとつひとつが、今後の勉強の指針となりました
非力につき最終的な解決までは日数かかりますので、
追って結果を報告申し上げたいと思います
まずは魔界の仮面弁士さんへ重ねて御礼申し上げます、ありがとうございました
■No30856に返信(ミソモさんの記事)
> 冗長な質問にも関わらず、丁寧な説明いただき感謝しきりです
> ご忠告に従い、まずはWebRequest/WebResponseを使用する事から始めました
> http://dobon.net/vb/dotnet/internet/webrequest.html
> の丸写しですので当然ですが無事ソースを取得できました

その処理は HTML の内容を String として得るだけのものなので、
本当にやりたいことは、その先なのでしょうけれどね。

もしも HTMLソースの解析だけが目的なら、HAP を使う手もあります。
http://news.mynavi.jp/articles/2009/12/15/htmlagility/index.html

あるいは、SGMLReader とか。
http://developer.mindtouch.com/en/docs/SgmlReader


解析だけでなく、JavaScript の実行なども必要な場合には、
WebBrowser 経由の方が良いのかも知れませんが。
■No30857に返信(魔界の仮面弁士さんの記事)
> もしも HTMLソースの解析だけが目的なら、HAP を使う手もあります。
> http://news.mynavi.jp/articles/2009/12/15/htmlagility/index.html

> あるいは、SGMLReader とか。
> http://developer.mindtouch.com/en/docs/SgmlReader

> 解析だけでなく、JavaScript の実行なども必要な場合には、
> WebBrowser 経由の方が良いのかも知れませんが。

無事に当座の目的を達することができました、ありがとうございます
HAPの方がすっきりして、使いよかったです
後々Java Scriptの実行要請が来そうなので、勉強はWebBrowserで進めます

---WebRequest について
全く同じURL、Parameterを渡してもWebRequestで接続すると
LogIn画面への接続すらできずアクセスエラーが帰ってくるイントラサイト
(IEでしか動かないサイト)があるので(VBAのときはWinHttp.WinHttpRequestで
同じエラー)、そこだけはやむを得ず、MSXML2.XMLHTTP30Classで
Html取得することとしました

---GAC
ClassLibrary、どこに何があるのかわからなかったのですが
存在すら知らなかったGACを覗いて
非常に興味がわきましたので(あまり実用的ではないのでしょうが)
基本構造を知るために勉強しています

------------------------------------------------------------------
目的達成に加え、勉強の出発点も見つけられて、本当に感謝しております

データベース検索ツールを他社委託して経費に苦しむ部署があるので
今回を起点に勉強を進め、VB Expressで自主制作しようと思います

魔界の仮面弁士さん、ありがとうございました
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板