ISAXXMLReader::Parse

解決


熊谷隆史  2010-08-15 09:16:51  No: 102511  IP: [192.*.*.*]

こんにちは。
環境 Windows 7/Excel 2010

    ISAXXMLReader* pRdr;
    HRESULT hr = CoCreateInstance( CLSID_SAXXMLReader60,
                                NULL,
                                CLSCTX_ALL,
                                IID_ISAXXMLReader,(void**)&pRdr);
    if(FAILED(hr)) return;

上記の様なC++と等価な呼び出しを行いたくて、タイプライブラリを作成して、
(規定インターフェースをISAXXMLReaderに設定)
VBAでインスタンス化しています。
(引数のdocは、MSXML2.DOMDocument)

IStreamはきちんと得られているのですが
> rdr.Parse doc
で、
---
実行時エラー '5':

プロシージャの呼び出し、または引数が不正です。
---
となります。
どなたか、お分かりになる方がいらっしゃいましたら、アドバイスを頂けませんでしょうか。


Sub Indentation(strPath, doc)

    Dim hr As Long
    Dim stm As IUnknown
    Dim wrt As Object
    Dim rdr As hoge.ISAXXMLReader
    Set stm = hoge.API_ole32.CreateStreamOnHGlobal(0, 1)

    'STGM_READWRITE Or STGM_SHARE_DENY_WRITE Or STGM_CREATE
    hr = hoge.API_shlwapi.SHCreateStreamOnFileW(strPath, 4130&, stm)
    If hr < 0 Then Err.Raise hr

    If stm Is Nothing Then Err.Raise 7
        Set wrt = CreateObject("MSXML2.MXXMLWriter.6.0")

        wrt.Version = "1.0"
        wrt.Encoding = "utf-8"
        wrt.indent = True
        wrt.output = stm

        Set rdr = New hoge.SAXXMLReader60
        rdr.putContentHandler wrt
        rdr.Parse doc
End Sub

長いのですが。
hoge.idl
[
    uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),
    version(1.0),
    helpstring ("hoge")
]
library hoge
{
    importlib("stdole2.tlb");

     interface ISAXXMLReader;
     
    [
      odl,
      uuid(A4F96ED0-F829-476E-81C0-CDC7BD2A0802),
      helpstring ("ISAXXMLReader interface")
    ]
    interface ISAXXMLReader : IUnknown {
        HRESULT _stdcall getFeature(
                        [in] unsigned short* pwchName,
                        [out, retval] VARIANT_BOOL* pvfValue);
        HRESULT _stdcall putFeature(
                        [in] unsigned short* pwchName,
                        [in] VARIANT_BOOL vfValue);
        HRESULT _stdcall getProperty(
                        [in] unsigned short* pwchName,
                        [out, retval] VARIANT* pvarValue);
        HRESULT _stdcall putProperty(
                        [in] unsigned short* pwchName,
                        [in] VARIANT varValue);
        HRESULT _stdcall getEntityResolver([out, retval] long** ppResolver);
        HRESULT _stdcall putEntityResolver([in] long* pResolver);
        HRESULT _stdcall getContentHandler([out, retval] IUnknown** ppHandler);
        HRESULT _stdcall putContentHandler([in] IUnknown* pHandler);
        HRESULT _stdcall getDTDHandler([out, retval] long** ppHandler);
        HRESULT _stdcall putDTDHandler([in] long* pHandler);
        HRESULT _stdcall getErrorHandler([out, retval] long** ppHandler);
        HRESULT _stdcall putErrorHandler([in] long* pHandler);
        HRESULT _stdcall getBaseURL([out, retval] unsigned short** ppwchBaseUrl);
        HRESULT _stdcall putBaseURL([in] unsigned short* pwchBaseUrl);
        HRESULT _stdcall getSecureBaseURL([out, retval] unsigned short** ppwchSecureBaseUrl);
        HRESULT _stdcall putSecureBaseURL([in] unsigned short* pwchSecureBaseUrl);
        HRESULT _stdcall parse([in, optional] VARIANT varInput);
        HRESULT _stdcall parseURL([in] unsigned short* pwchUrl);
    };
    [
        uuid(88d96a0c-f192-11d4-a65f-0040963251e5),
        helpstring ("SAX XML Reader 6.0")
    ]
    coclass SAXXMLReader60
    {
        [default] interface ISAXXMLReader;
    };
    
    [
      dllname ("Shlwapi.dll")
    ]
    module API_shlwapi {
        [entry("SHCreateStreamOnFileW")]
        long _stdcall SHCreateStreamOnFileW(
                [in] LPWSTR pszFile,
                [in] long grfMode,
                [out] IUnknown **ppstm);
    };
    
    [
      dllname ("ole32.dll")
    ]
    module API_ole32 {
        [entry("CreateStreamOnHGlobal")]
        HRESULT _stdcall CreateStreamOnHGlobal(
                [in] long hGlobal,
                [in] long fDeleteOnRelease,
                [out, retval] IUnknown **ppstm );
    };
};
参考)
http://web.archive.org/web/20060721221948/http://forums.belution.com/ja/xml/000/000/52s.shtml

編集 削除
魔界の仮面弁士  2010-08-16 12:47:51  No: 102512  IP: [192.*.*.*]

タイプライブラリを作成しているとの事ですが、
MSXML6.DLL では都合が悪いのでしょうか?

> 環境 Windows 7/Excel 2010
同じ環境が無いので、とりあえず WinXP + Excel 2007 で動作確認。


Option Explicit
'参照設定
'[Microsoft ActiveX Data Objects 2.8 Library]
'[Microsoft XML, v6.0]
Public Sub Test()
    Dim stm As ADODB.Stream
    Set stm = New ADODB.Stream
    stm.Type = adTypeBinary
    stm.Open

    Dim wrt As MSXML2.MXXMLWriter60
    Set wrt = New MSXML2.MXXMLWriter60
    wrt.Version = "1.0"
    wrt.Encoding = "UTF-8"
    wrt.indent = True
    wrt.standalone = True
    wrt.omitXMLDeclaration = False
    wrt.output = stm
    wrt.flush

    Dim doc As MSXML2.DOMDocument60
    Set doc = New MSXML2.DOMDocument60
    doc.async = False
    doc.preserveWhiteSpace = False
    
    Dim root As MSXML2.IXMLDOMElement
    Set root = doc.createElement("root")

    Dim element As IXMLDOMElement
    Set element = doc.createElement("ParentElement")
    element.setAttribute "atr1", "1"
    element.setAttribute "atr2", "xxxxx"

    root.appendChild element
    doc.appendChild root

    Dim irdr As MSXML2.ISAXXMLReader
    Dim ordr As MSXML2.SAXXMLReader60
    Set ordr = New MSXML2.SAXXMLReader60
    Set irdr = ordr
    irdr.putContentHandler wrt
    ordr.Parse doc

    stm.SaveToFile "C:\result.xml", adSaveCreateOverWrite
    stm.Close
End Sub

編集 削除
Abyss  2010-08-16 16:54:33  No: 102513  IP: [192.*.*.*]

熊谷隆史さん、こんにちは。
この件は別の場所でも拝見いたしました。
xmlには無知ですが、今回の件で少し興味を持ち、
上記のidlにてTlbファイルを作って参照設定してみました。
(引数の型指定問題は論外にします。)

(環境)WindowXP(SP3)、Excel2003(SP3)

しかし、 Set rdr = New hoge.SAXXMLReader60

の直後、msgbox typename(rdr)
で確認したら、ISAXXMLReaderではなく、「IVBSAXXMLReader」
インターフェイスですね。
IVBSAXXMLReaderはIDispatch Interfaceを継承しているので
折角作ったTypLibのVTableがおかしくなっているかと思います。
Windows7の環境がないので、検証は出来ませんが、当方の
環境では下記のような方法で問題ありませんでした。

Private Declare Function SHCreateStreamOnFileW& Lib "Shlwapi" _
  (ByVal pszFile&, ByVal grfMode&, ByVal ppstm&)

Sub Indentation2(strPath, doc)

    Dim hr As Long
    Dim stm As IUnknown 'IStream
    Dim wrt As Object
    Dim rdr As Object   'IVBSAXXMLReader

    'STGM_READWRITE Or STGM_SHARE_DENY_WRITE Or STGM_CREATE
    hr = SHCreateStreamOnFileW(StrPtr(strPath), 4130&, VarPtr(stm))
    If hr < 0 Then Err.Raise hr

    If stm Is Nothing Then Err.Raise 7
    Set wrt = CreateObject("MSXML2.MXXMLWriter")

    wrt.Version = "1.0"
    wrt.Encoding = "utf-8"
    wrt.indent = True
    wrt.output = stm

    Set rdr = CreateObject("MSXML2.SAXXMLReader") 'New SAXXMLReader60
    Set rdr.contentHandler = wrt
    rdr.Parse doc
End Sub

編集 削除
熊谷隆史  2010-08-17 08:17:56  No: 102514  IP: [192.*.*.*]

魔界の仮面弁士さん、回答ありがとうございます。
載せて頂きましたコードでバッチリです。
> ADODB.Stream
QueryInterfaceを呼び出して、IStreamを取り出さなくてもいいのですね。
(IDispatchとIStreamを実装したオブジェクトでないとダメと言う意味なのか、
IUnknown型だとvtメンバがきちんと指定されないのかとも思いました)
何か色々、脱帽です。
しっかりと勉強させて頂きます。

Abyssさん、回答ありがとうございます。
こちらでは、
>     rdr.Parse doc
おなじみの実行時エラーになります。
実行時エラー '-1072898019 (c00ce01d)':

XPとWindows 7では動作の振る舞いに違いがあると認識しました。

> 上記のidlにてTlbファイルを作って参照設定してみました。
お手数掛けます。

   //importlib("stdole2.tlb");
     importlib("msxml6.dll");
とすると参照設定するのと変わらなかったので
あらためて自前でISAXXMLReaderを定義したのですが、
結局、私の考え違いでした。

> しかし、 Set rdr = New hoge.SAXXMLReader60

> の直後、msgbox typename(rdr)
> で確認したら、ISAXXMLReaderではなく、「IVBSAXXMLReader」
> インターフェイスですね。
確認不足で失礼しました。

>     Set rdr = CreateObject("MSXML2.SAXXMLReader") 'New SAXXMLReader60
XPにはもう随分、まともに触れていないのでよく分かりませんが、
XPのCreateObject関数では、上位のバージョンから参照されるのでしょうか。

これは何故かと言うと今回、Parseメソッドが
それぞれ参照してるライブラリのバージョンによってちょっとずつ、
違うエラーメッセージを返して来ることから分かりました。
(こちらでは下位のバージョンが参照されています)

私が載せたコード中で、
>         Set wrt = CreateObject("MSXML2.MXXMLWriter.6.0")
などとしてるのもきちんと意味があります。

編集 削除
魔界の仮面弁士  2010-08-17 11:02:47  No: 102515  IP: [192.*.*.*]

> QueryInterfaceを呼び出して、IStreamを取り出さなくてもいいのですね。
IStream を実装したオブジェクトであれば OK です。
ADO の Stream でも ASP の Response でも MSXML2 の DOMDocument でも。
もちろん、SHCreateStreamOnFileW や CreateStreamOnHGlobal も可です。

それ以外では文字列(or Empty)も指定できますね。
文字列を使った場合には encoding 指定が出力されませんけれども…。

Dim wrt As Object
Set wrt = CreateObject("MSXML2.MXXMLWriter.6.0")
wrt.Version = "1.0"
wrt.Encoding = "UTF-8"
wrt.indent = True
wrt.standalone = True
wrt.omitXMLDeclaration = False
wrt.output = Empty
wrt.flush

Dim rdr As Object
Set rdr = CreateObject("MSXML2.SAXXMLReader.6.0")
Set rdr.ContentHandler = wrt
rdr.Parse doc

Debug.Print wrt.output


なお、ContentHandler を指定する場合には、ISAXXMLReader.putContentHandler メソッドを
使うよりも、SAXXMLReader.ContentHandler プロパティの方が便利かもしれません。
(参照設定無しですむので、VBA のみならず VBScript からでも利用できますし)


> これは何故かと言うと今回、Parseメソッドが
> それぞれ参照してるライブラリのバージョンによってちょっとずつ、
> 違うエラーメッセージを返して来ることから分かりました。
これらは通常、サイドバイサイドモードにてインストールされる仕様です。
http://support.microsoft.com/kb/303207/ja

バージョンによってサポートしている "動的プロパティ" が異なったりもしますから、
これらをレイトバインドで使う場合には、バージョン指定の ProgID を
利用した方が安全ですね。もしくは参照設定。

編集 削除
Abyss  2010-08-17 13:06:13  No: 102516  IP: [192.*.*.*]

> XPのCreateObject関数では、上位のバージョンから参照されるのでしょうか。

すみません。これは一個人の癖です。下位優先志向なので。
現在のXPでVersionIndependenceClsidが見てるのがmsxml3.dllですから、
当然、Window7ではそれ以上だと思ったので。

> それぞれ参照してるライブラリのバージョンによってちょっとずつ、
> 違うエラーメッセージを返して来ることから分かりました。

MSXMLには詳しくないので、そこまで頭が回りませんでした。

編集 削除
熊谷隆史  2010-08-19 08:50:18  No: 102517  IP: [192.*.*.*]

魔界の仮面弁士さん、Abyssさん、回答ありがとうございます。

Abyssさんの載せられてるコードでバッチリでした。m(_ _)m 
原因はXMLファイルにありました。orz
> VersionIndependenceClsid
初聞きの言葉です。調べてみます。

> それ以外では文字列(or Empty)も指定できますね。
> 文字列を使った場合には encoding 指定が出力されませんけれども…。
追加情報ありがとうございます。同時に載せて頂いたコードも動作しました。

> これらは通常、サイドバイサイドモードにてインストールされる仕様です。
http://support.microsoft.com/kb/303207/ja
参考リンク先のご紹介、ありがとうございます。
件のオブジェクトにXPとWindows 7で、動作の違いはありません。取り下げます。
魔界の仮面弁士さんには当初から、そこまで見透かされていた様です。
あらためて感謝します。

編集 削除
Abyss  2010-08-19 12:53:22  No: 102518  IP: [192.*.*.*]

> VersionIndependenceClsid
> 初聞きの言葉です。調べてみます。

VersionIndependentProgIDでした。

編集 削除
熊谷隆史  2010-08-21 06:48:47  No: 102519  IP: [192.*.*.*]

Abyssさん、ありがとうございます。
> VersionIndependentProgID
http://msdn.microsoft.com/en-us/library/dd542717(VS.85).aspx

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{079AA557-4A18-424A-8EEE-E39F0A8D41B9}\VersionIndependentProgID\
を参照したら、
Msxml2.SAXXMLReader
が取れました。

編集 削除