プロが教えるわが家の防犯対策術!

VBA(Excel)で、IStream を使いたいのですが、どうしたらよいのでしょうか?
Variant(IUnknown?)を IStream にして OleLoadPicture() を通して IPictureDisp に戻したいです。
こんな感じです:
Public Sub foo(v As Variant)
  Dim s As IStream  ' ← ×
  Dim p As IPictureDisp
  Dim r As Long
  Set s = v
  r = OleLoadPicture(s, 0, True, IID_IPictureDisp, p)
  Debug.Assert r = 0
  ...

A 回答 (7件)

OleLoadPictureを使用するに当たって、全てポインタで操作ですよね?


OleLoadPictureの宣言を見てみないことには、回答不能です。

それにIStreamで宣言してますが、、、型は実際はLongになると思うのですが、、、

画像関係をVBではなく、VBAでするにあたっても、OfficeのDLLとVBのDLLは違うため、表示であれば困難かと思われ。。。
VBAのピクチャボックスは、ハンドルを得ることが難しいです。

http://www.galliver.co.jp/writing/vbm_tokushu/sp/
であるように、OfficeまたはIEなどの環境にも依存されるOleLoadPictureを使用して、何がしたいのかが判れば、多少は糸口がつかめるかも?

この回答への補足

ご回答、ありがとうございます。

目的:
foo の続きは、例えば、
  Set Sheet1.Label1.Picture = p
などです(つまり表示です)。

OleLoadPicture() の宣言:
Set s = v は、v.QueryInterface(IID_IStream, s) のつもりなんです。
OleLoadPicture() の引数の宣言は、Long でも構わないのですが、v には IUnknown として入っているので、IStream に QueryInterface(IID_IStream) しないと OleLoadPicture() に渡せないと思います。
Set s = v で QueryInterface(IID_IStream) するには Dim s As IStream が必要で、IStream があるのならば OleLoadPicture() の宣言は、結局、Long ではなく、
Private Declare Function OleLoadPicture Lib "olepro32" ( _
  ByVal s As IStream, ByVal z As Long, ByVal r as Long, _
  g As GUID, p As IPictureDisp) As Long
などとできると思います。

補足日時:2003/03/29 02:52
    • good
    • 0

>MSDN は間違っています


ちょっと・・・間違っているって・・・
少なくともOleLoadPictureについては、間違っていないように思うのですが?


>__declspec(dllexport) int __stdcall bar(IPictureDisp *);  
ポインタで受けてますよね?

IPictureDispとIStreamも、どちらもオブジェクトですが、前回言った
>『IStreamの型がないので、IStreamオブジェクトのポインタを操作しなければなりません。言語仕様を理解してください。 』
と言った通りです。


[VB仕様として理解して欲しいこと1]
IPictureDispはVBにもCにも、同様の型(タイプ)を持っています。
何度も言います。IStream という型はVBにありません。

[VB仕様として理解して欲しいこと2]
As IStreamは通りません。

ByValかByRefかというのが大事なのではありません。
受けがポインタを要しているのであれば、ポインタを
値を要しているのであれば、値を渡すだけです。


そちらで作成したものを引用して、
Private Declare Function bar Lib "foo.dll" (ByVal p As Long) As Long
Private Declare Function bar Lib "foo.dll" (ByRef p As Long) As Long
Private Declare Function bar Lib "foo.dll" (ByVal p As Any) As Long
Private Declare Function bar Lib "foo.dll" (ByRef p As Any) As Long
の何でもいいです。
MsgBox bar(ByVal ObjPtr(p))
で通るはずです。

Cの受けが(IPictureDisp *)となっているので、pのポインタ値を渡せば何でもよいのです。
IPictureDispの宣言がVBもCもできますが、何度も言いますがIStream型はVBにはありません。
VBでは、同様な手法でLong(ポインタとアドレス値)を応用したやり方で行います。


>「VBA-実行環境は、インターフェイスポインタを As Long で扱うと Release しない」ようです。
おそらく、アドレスのポインタではなくアドレスのポインタのポインタを渡しているからでは?
これは他のメーリングリストで、CのDLLを使用する人が、よくこのような間違い行っています。
※VBとCとの連携の基本はアドレスを扱うのが基本です。
※VBではアドレスをLongで扱います。


締め切りはして欲しいけど、納得しないうちは締め切って欲しくないです。
私はここと、他の二つのメーリングリストに発言しております。
プログラミングは業務ではプロトタイプしかやってませんが、こうやって発言することで言語スキルを落とさないように勤ようとしています。また、特定の人の発言を読むことによって、結構しらない事をいっぱい学べるので、大変役に立っています。ですので、ポイントはどっちでもよいです。

昨日より、しばらくホテル住まいのため、レスが遅れます。
ご容赦ください。

この回答への補足

3:56 2003/04/03

> 納得しないうちは締め切って欲しくないです。
>
そうですね。全員が納得してから、閉じることにします。

MSDN には、COM についても記されているかと思います。それらをお読みになった帰結が
>
> ※VBとCとの連携の基本はアドレスを扱うのが基本です。
> ※VBではアドレスをLongで扱います。
>
だと仰るのならば、MSDN は間違っています。

C/C++ で、IPictureDisp p; は--抽象だからという理由で--違法ですが、記述はできます。
しかし、VB/VBA では、IPictureDisp p; に対応する記法自体がありません。VB/VBA での Dim p As IPictureDisp は合法で、C/C++ での IPictureDisp *p; に対応します。
インターフェイスポインタ型に関して、C/C++ と VB/VBA では、参照の深さ(C/C++ での * の数)が1つずれているように見えます。

だから、もし IPictureDisp が(そのようなタイプライブラリ中に)存在すれば--そして存在するので、現実に、
 bar(ByVal p As IPictureDisp)
が正しく、
 bar(ByRef p As IPictureDisp)
は間違いです。
もちろん、p は [in] なので As Long でも問題ないでしょう(c.f. 回答 No.3 への補足)。

上の記述を bar → OleLoadPicture、IPictureDisp → IStream に置換して読んでみてください。
→ もし IStream が存在すれば…

しかし、一般に As Long で良いかというと違います。特に、[out] void ** などには注意してください。
例を挙げます。次の Excel VBA をご覧ください:

Private Const CLSCTX_LOCAL_SERVER As Long = 4

Private Type GUID
  Data1 As Long
  Data2 As Integer
  Data3 As Integer
  Data4(0 To 7) As Byte
End Type

Private Declare Function CoCreateInstance Lib "ole32" ( _
  ByRef rclsid As GUID, ByVal pUnkOuter As Long, _
  ByVal dwClsContext As Long, ByRef riid As GUID, _
  ByRef ppv As Application) As Long

Private Function CLSID_Excel() As GUID
' {00024500-0000-0000-C000-000000000046}
  With CLSID_Excel
    .Data1 = &H24500
    .Data2 = &H0
    .Data3 = &H0
    .Data4(0) = &HC0
    .Data4(1) = &H0
    .Data4(2) = &H0
    .Data4(3) = &H0
    .Data4(4) = &H0
    .Data4(5) = &H0
    .Data4(6) = &H0
    .Data4(7) = &H46
  End With
End Function

Private Function IID_App() As GUID
' {000208D5-0000-0000-C000-000000000046}
  With IID_App
    .Data1 = &H208D5
    .Data2 = &H0
    .Data3 = &H0
    .Data4(0) = &HC0
    .Data4(1) = &H0
    .Data4(2) = &H0
    .Data4(3) = &H0
    .Data4(4) = &H0
    .Data4(5) = &H0
    .Data4(6) = &H0
    .Data4(7) = &H46
  End With
End Function

Private Sub ExecExcel()
  Dim hr As Long
  Dim app As Application
  hr = CoCreateInstance(CLSID_Excel, 0, CLSCTX_LOCAL_SERVER, IID_App, app)
  'Debug.Print "CoCreateInstance", hr
  'app.Visible = True
End Sub

Public Sub ExecExcel10()
  Dim i As Long
  For i = 0 To 9
    Call ExecExcel
  Next i
End Sub

ここで CoCreateInstance() は、新しい Excel を起動します。
早速、ExecExcel10() を実行してください。そして、タスクマネージャなどで Excel.exe の数を数えてください。→1個です(この VBA を実行した Excel)。
次に、CoCreateInstance() の ppv を ByRef As Long に、ExecExcel() の Dim app As Application を Long に変更して ExecExcel10() を実行後、再び Excel の数を確認してください。→11個です。
As Object ではどうでしょう。→1個です。
As Long のときの10個は VBA から到達不可能なのでリークです。
VB/VBA では、IUnknown-メソッド AddRef/Release などは暗黙的・自動的に呼び出されます。
これを抑止すること(インターフェイスポインタを As Long で扱うなど)をするとリークの危険があります。
As Object は1個でした。このことは、As Object が IUnknown や IDispatch などに対応することを暗示しています。

> [VB仕様として理解して欲しいこと1]
> IPictureDispはVBにもCにも、同様の型(タイプ)を持っています。
> 何度も言います。IStream という型はVBにありません。
>
> [VB仕様として理解して欲しいこと2]
> As IStreamは通りません。
>
IStream …ないですね。どこに(*.olb, *.tlb, *.dll)あるのかしら?

補足日時:2003/04/03 03:53
    • good
    • 0

>C/C++ で、


>・・・
>参照の深さ(* の数)を単純に計算すると、このようになります。

・・・MSDN見ましょうよ。。。

何も特別なことを言っているわけではありません。
IStream * pStream //Pointer to the stream that contains picture's data
直訳:ピクチャデータのStreamポインタ

もしVBがIStreamと言うオブジェクトの型を持っているのであれば、APIの宣言は
byref pStream as IStream
VB側の渡しは
OleLoadPicture(s,・・・)
でよいですが、IStreamの型がないので、IStreamオブジェクトのポインタを操作しなければなりません。言語仕様を理解してください。
 
ましてや
byval pStream as IStream
OleLoadPicture(s,・・・)
とする値渡しで、VBのオブジェクト本体を渡すのは不可能です。

何度も言うようですが、[As Stream]の宣言は通りませんので、こだわるのはもうそろそろ・・・


APIはVBではそのまま使えるものと、VB用にいろいろと考慮する必要があるものがあります。
*や&が何個付いているからというのではなく、関数仕様を理解してください。
パラメータに何が必要かはきちんとMSDNに書いてあるはずです。

VB独自のオブジェクト仕様を理解しないで、Cの感覚を持ち込むと、できません。

>COM-インターフェイスポインタを As Long で扱うと VBA-実行環境が、AddRef/Release できなくてメモリリーク
クリークが起こる場合、Longだからということはありません。PGの設計しだいです。
このLongにはアドレスの先頭のポインタが入っているのですよ?C使いなら、私よりむしろ得意分野ではないですか?
不安なら、Pictureの複製を作るなり、グローバルメモリを確保すりゃいいだけのように思うのですが?

この回答への補足

16:33 2003/04/01

何度も、ありがとうございます。
一応、私の知るところを述べておきます。

MSDN は間違っています--もし、そのように書かれているのならば。
これらは、実際に調べてみれば、即座に分かります。

ByVal/ByRef について調べました。
例えば、VC++6 で次のようにして foo.dll を作ります:
==============================
foo.cpp
------------------------------
#include <windows.h>
#include <ocidl.h>
__declspec(dllexport) int __stdcall bar(IPictureDisp *);  // ※1
BOOL __stdcall DllMain(HINSTANCE, DWORD, void *) { return TRUE; }
int __stdcall bar(IPictureDisp *d) {
  IPicture *p = 0;
  OLE_HANDLE h;
  d->QueryInterface(__uuidof(p), reinterpret_cast<void **>(&p));
  p->get_Handle(&h);
  p->Release();
  return (int)h;
}
==============================
foo.def
------------------------------
LIBRARY  foo
EXPORTS
  bar  @1
==============================

これを、VBA で
 Private Declare Function bar Lib "foo.dll" (ByRef p As IPictureDisp) As Long
 ' Sheet1.Label1.Picture には予め有効な絵を入れておきます。
 Private Sub baz()
   Dim p As IPictureDisp
   Set p = Sheet1.Label1.Picture
   MsgBox p.Handle
   MsgBox bar(p) ' ※2
 End Sub
とすると、※2で壊れます。
※1(IPictureDisp *)に対応する VBA の宣言は、ByVal にしなければなりません。
 Private Declare Function bar Lib "foo.dll" (ByVal p As IPictureDisp) As Long
とすれば bar() の戻り(直前の p.Handle と同じ値)が表示されます。

AddRef/Release についても調べました。
例えば、やや乱暴ですが Release に { *(char *)NULL = '\0' /* wh@? */ ; … } などと書いておいて、クラッシュするかどうかを調べます(私は Delphi で作成しました)。
結論だけ言うと「VBA-実行環境は、インターフェイスポインタを As Long で扱うと Release しない」ようです。これは、とても重要なことです。
例えば、今の私の場合 IStream が Release されないと、もとの HGLOBAL も解放されないです。

……などとやってるうちに、解決策が1つ見つかりました:
 void MyLoadPicture(IUnknown *u, IPictureDisp **p) {
   IStream *s = 0;
   u->QueryInterface(__uuidof(s), reinterpret_cast<void **>(&s));
   ::OleLoadPicture(s, 0, TRUE, __uuidof(*p), reinterpret_cast<void **>(p));
   s->Release();
 }
とうい小さな DLL を作ろうかと考えています。
しかし、VBA からハミ出てしまうのは、とても残念です…。
というわけで、今すぐにポイントを差し上げたいのですが、念のため、もう少しクローズしないでおきます。

補足日時:2003/04/01 16:31
    • good
    • 0

ちょっとだけ訂正



>VBではポインタは全てロングの型になるのが鉄則なのです。



VBでは「オブジェクト」のポインタは全てロングの型になるのが鉄則なのです。

と訂正します。

またオブジェクトではない変数の場合は、その都度適した宣言を行います。
(これは言うまでもないか ^^;)

この回答への補足

19:32 2003/03/31

> VBでは「オブジェクト」のポインタは全てロングの型になるのが鉄則なのです。
>
C/C++ で、
  int n;
  IPictureDisp *p;
という宣言は、VB/VBA では、
  Dim n As Long
  Dim p As IPictureDisp
(n はそのまま、p の方だけ * が1つ除去される)になりますよね。
同様に、C/C++ で、
  void bar(int n, int *m, IPictureDisp **p);
は、VB/VBA では、
  Sub bar(ByVal n As Long, ByRef m As Long, ByRef p As IPictureDisp)
(n は ByVal がつく、m は * が1つ除去されて ByRef がつく、p は * が2つ除去されて ByRef が付く)ですよね。
ならば、C/C++ で、
  OleLoadPicture(IStream *s, ...)
は、VB/VBA では、
  OleLoadPicture(ByVal s As IStream, ...)
(* が1つ除去されて ByVal がつく)ではないのですか?
参照の深さ(* の数)を単純に計算すると、このようになります。

それと、COM-インターフェイスポインタを As Long で扱うと VBA-実行環境が、AddRef/Release できなくてメモリリークとかしそうですが、大丈夫なんでしょうか?

補足日時:2003/03/31 19:32
    • good
    • 0

ですので、、、


IStream *s ← 思いっきりポインタです。
VBではポインタは全てロングの型になるのが鉄則なのです。

>> それ以前にAPIの宣言の段階で「ユーザ定義型は定義されていません。」
>>と
>今は、IStream が無いのでそうなります。
違います。型が無いと言われているのです。オブジェクトが無いといわれているわけではありません。
Cで「Variant *v」と書いて通りませんよね?それと同じ意味です。


さらにCからVBを呼び出す方法ですが、IDispatchImpl::Invokeはよくわかりませんが、そもそもVBのVariantに正常なパラメータで入ってきていますか?
そこら辺は、私は経験ありませんのでコメントができませんが、、、
グローバルメモリを確保しないでも、やり取りができるのですね?知りませんでした。

もしVariantでfoo関数に値(オブジェクト)が入ってきているなら、ObjPtr(S)でもよいのですが、その際の宣言は[s as Object]です。そうでなければLong以外はあり得ないと思います。


>VBA(Sheet1.Label1.Picture)と画像をやり取りできれば、OKです。
オブジェクトの絵が取れれば何でもよいというのであれば、ピクチャハンドルから、ピクチャを起こしたらよいのでは?

VBA(VB)で全て書いてますが、これをそのままコンバートしてあげたらできると思います。

Option Explicit

Private Declare Function GetObjectType Lib "gdi32" (ByVal hGdiObject As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (lpPictDesc As TPictDesc, RefIID As TGUID, ByVal fOwn As Long, ByRef lpvObj As Picture) As Long

Private Enum ePicTypeConst
  ePicTypeNone = 0
  ePicTypeBitmap = 1
  ePicTypeMetafile = 2
  ePicTypeIcon = 3
  ePicTypeEMetafile = 4
End Enum
Private Enum NGdiObhectType
  GdiObjPen = 1
  GdiObjBrush = 2
  GdiObjDC = 3
  GdiObjMetaDC = 4
  GdiObjPalette = 5
  GdiObjFont = 6
  GdiObjBitmap = 7
  GdiObjRegion = 8
  GdiObjMetafile = 9
  GdiObjMemDC = 10
  GdiObjExtPen = 11
  GdiObjEnhMetaDC = 12
  GdiObjEnhMetafile = 13
End Enum


Private Type TPictDesc
  'この構造体のサイズです。
  cbSizeofStruct As Long
  'ピクチャーのタイプを指定。
  picType As ePicTypeConst
  'イメージのハンドル。
  hImage As Long
  'ビットマップの場合は、パレットのハンドル。
  'メタファイルの場合は、幅。
  Option1 As Long
  'メタファイルの場合は、高さ。
  Option2 As Long
End Type
Private Type TGUID
  Data1 As Long
  Data2 As Integer
  Data3 As Integer
  Data4(1 To 8) As Byte
End Type

Sub Main()
  Dim picWk  As Picture
  Dim lngWk  As Long
  
  lngWk = (Sheet1.Label1.Picture.Handle)
  Set picWk = CreatePictureFromHandle(lngWk, ePicTypeBitmap)
End Sub


Private Function CreatePictureFromHandle _
( _
  hImage As Long, _
  Optional PictureType As ePicTypeConst = ePicTypeNone, _
  Optional hPalette As Long = 0, _
  Optional Width As Long = 0, _
  Optional Height As Long = 0, _
  Optional AutoDelete As Boolean = True _
) As Picture

  'この関数はハンドルからピクチャーオブジェクトを返します。

  '関数に渡すためのPictDesc構造体。
  Dim tdsPicture As TPictDesc
  'IPictureインターフェースを入れる変数。
  Dim tgdPicture As TGUID
  
  '戻ってきた値を受ける変数。
  Dim lngAPIResult As Long
  
  'インターフェースへのポインタを受ける変数。
  Dim lngBuffer As Long
  
  'Pictureを入れる変数。
  Dim objPicture As Picture

  'あらかじめNULLにセット。
  Set CreatePictureFromHandle = Nothing
  
  'ハンドルがNULLならば、抜ける。
  If hImage = 0 Then Exit Function
  
  '構造体の初期化。
  With tdsPicture
    .cbSizeofStruct = Len(tdsPicture)
    .hImage = hImage
    .Option1 = 0
    .Option2 = 0
  End With
  
  'イメージのタイプを調べる。
  Select Case PictureType
    '指定されている場合。
    Case ePicTypeBitmap To ePicTypeEMetafile
    
    '指定されていない場合。
    Case Else
      lngAPIResult = GetObjectType(hImage)
      
      Select Case lngAPIResult
      
        'ビットマップの場合。
        Case GdiObjBitmap
          PictureType = ePicTypeBitmap
          
        'メタファイルの場合。
        Case GdiObjMetafile
          PictureType = ePicTypeMetafile
          
        '拡張メタファイルの場合。
        Case GdiObjEnhMetafile
          PictureType = ePicTypeEMetafile
          
        'GDIオブジェクトじゃない場合は
        'アイコンとみなす。
        Case 0
          PictureType = ePicTypeIcon
          
        '他のGDIオブジェクトの場合は抜ける。
        Case Else
          Exit Function
    
      End Select
  End Select

  tdsPicture.picType = PictureType
  
  If PictureType = ePicTypeBitmap Then
    tdsPicture.Option1 = hPalette
  End If
  
  If PictureType = ePicTypeMetafile Then
    tdsPicture.Option1 = Width
    tdsPicture.Option2 = Height
  End If
  
  'IIDのセット。IID_IDispatchにします。
  '今までIID_IPictureにしていたので苦労していました。
  
  With tgdPicture
    .Data1 = &H20400  ' &H7BF80980
    .Data2 = &H0 '&HBF32
    .Data3 = &H0 ' &H101A
    .Data4(1) = &HC0 ' &H8B
    .Data4(2) = &H0 '&HBB
    .Data4(3) = &H0
    .Data4(4) = &H0 '&HAA
    .Data4(5) = &H0
    .Data4(6) = &H0 '&H30
    .Data4(7) = &H0 '&HC
    .Data4(8) = &H46 '&HAB
  End With
  
  '肝心の関数を呼出す。
  lngAPIResult = OleCreatePictureIndirect(tdsPicture, tgdPicture, 1, objPicture)

  'エラートラップを許可。
  On Error Resume Next
  
  Set CreatePictureFromHandle = objPicture

  Set objPicture = Nothing
  
  Err.Clear
  
End Function

この回答への補足

ありがとうございます。

> オブジェクトの絵が取れれば何でもよいというのであれば、ピクチャハンドルから、
> ピクチャを起こしたらよいのでは?
>
IStream の代わりに HENHMETAFILE などのハンドルを渡すという意味でしょうか?
  Public Sub foo(ByVal hEnhMetafile As Long)
    Dim p As IPictureDisp
    Dim pd As TPictDesc
    Dim hr As Long
    With pd
      .cbSizeofStruct = Len(pd)
      .picType = 4
      .hImage = hEnhMetafile
      .Option1 = 0
      .Option2 = 0
    End With
    hr = OleCreatePictureIndirect(pd, IID_IPictureDisp, True, p)
    Sheet1.Label1.Picture = p
  End Sub
ハンドルは通じない(プロセス間で)と思いこんで、やって無かったです。
今、一番容易な icon をやってみたら、できたみたいです。
残りも、今日やってみます。

補足日時:2003/03/31 04:17
    • good
    • 0
この回答へのお礼

03-03-28 19:30
ありがとうございます。

HENHMETAFILE で実験しました。
#define STRICT
#include <windows.h>
#include <stdio.h>
int main (int argc, char **argv) {
  HENHMETAFILE hEmf;
  int nEmf;
  char c;
  hEmf = GetEnhMetaFile (argv[1]);
  nEmf = (int) hEmf;
  printf("%d, %x\n", nEmf, nEmf);
  scanf("%c", &c);  /* 時間稼ぎ */
  DeleteEnhMetaFile (hEmf);
}
を書いて、HENHMETAFILE を表示して、(「時間稼ぎ」の間に)この値で VBA で OleCreatePictureIndirect() してみましたが、画像は得られませんでした(HRESULT は 0 なのに表示されません)。プロセスを越えたら、HENHMETAFILE は無効みたいですが、やり方がマズかったでしょうか。

お礼日時:2003/03/31 19:32

私はC++はよくわかりませんが、Cはわかります。



>CreateStreamOnHGlobal() で IStream
もしかして、その部分はCでやっているわけではありませんよね?もしCなら、そのアドレスをCから渡していまるのですか?
そうじゃなくてVBでやっているなら、その部分はうまくいってますか?3番目の引数に返ってくるものもポインタなので、VBではLongで返ってきていますが・・・
スクリプトでIStremオブジェクトを取得するのであれば話は別ですが、、、

Cでデバッグしてもらったらわかるように、IStremの変数をウォッチウインドウのところで見ると、アドレスが入っているはずです。
VBとCとのやりとりは全てアドレスで行います。
逆も同じです。

>Function OleLoadPicture (ByVal s As IStream, ByVal z As Long, _
   ByVal r As Long, ByRef g As GUID, ByRef p As IPictureDisp) As Long
なので、
Function OleLoadPicture ( s As Long, ByVal z As Long, _
   ByVal r As Long, ByRef g As GUID, ByRef p As IPictureDisp) As Long
になります。

それ以前にAPIの宣言の段階で「ユーザ定義型は定義されていません。」とVBAから言われませんか?


それと、、、GUIDの宣言はメーリングリストによって、宣言がバラバラですので、ある意味全てが独自です。

この回答への補足

> もしかして、その部分はCでやっているわけではありませんよね?
> もしCなら、そのアドレスをCから渡していまるのですか
>
「外部」は Visual C++ や Delphi で作ってます。
インターフェイスポインタを渡しています。

> それ以前にAPIの宣言の段階で「ユーザ定義型は定義されていません。」と
>
今は、IStream が無いのでそうなります。

勿論、宣言では s の型など捨ててしまっても良いです:
 Function OleLoadPicture (ByVal s As Long, ...) As Long
その場合、
 Call OleLoadPicture(ObjPtr(s), ...)
と呼び出すことになりますが、これは、本質ではないです。

もとの質問:
foo() を C/C++ 風に書くと
void foo(IUnknown *v) {
  IStream *s = 0;
  IPictureDisp *p = 0;
  long r;
  r = v->QueryInterface(__uuidof(s), reinterpret_cast<void **>(&s)); // ※1
  if (r != 0) throw r;
  r = OleLoadPicture(s, 0, TRUE, __uuidof(p), reinterpret_cast<void **>(&p));
  s->Release(); // ※2
  s = 0;
  if (r != 0) throw r;
  ...
}
などとなります。
※1 (int)v と (int)s は異なる値になります。この s の値を得たいのです。
※2 s は、Release() もしなければならないです。
これを VBA では、どのように書くのでしょうか?
それは、分かります:
  Dim s As IStream
  Set s = v
です。
だから、残る問題は「VBA(Excel)で、IStream を使いたいのですが、どうしたらよいのでしょうか?」になります。

当初、↑というつもりだったのですが
「外部」も書き換え可能なので、これも本当の問題ではなくて、
VBA(Sheet1.Label1.Picture)と画像をやり取りできれば、OKです。
ファイルやクリップボードを汚さずに。

補足日時:2003/03/30 22:50
    • good
    • 0

> Set Sheet1.Label1.Picture = p


ですか?
このままでは無理だと思います。

APIがオブジェクト本体を返すことはありません。
オブジェクトハンドルやIDから、オブジェクトを創生する必要があります。
それが可能かどうかも未知ですが、、、



APIを普段からご利用されているのでしょうか?

>Private Declare Function OleLoadPicture Lib "olepro32" ( _
>  ByVal s As IStream, ByVal z As Long, ByVal r as Long, _
>  g As GUID, p As IPictureDisp) As Long

APIでオブジェクト本体を返すことはできなく、フラグ値や、オプション値以外は全てアドレスで渡します。
「ByVal s As IStream」
で渡したいのであれば、IStremのアドレスを渡さなければなりません。
しかもIStreamはストリームオブジェクトなので、ObjPtr(IStream の変数)などとして、オブジェクトハンドルを得て、それを値渡しする必要があります。
オブジェクト本体の値渡しなど、不可能です。

ストリームオブジェクトをどのように取得しているのかも知りたいところですね。
それ以外にも、一般に知られていないライブラリを参照しているなら、それも知りたいし、独自で型を宣言しているのであれば、それも知りたいです。
私の知っている限りと、あなたの使用としていることとに大きく開きがあるように思えます。

この回答への補足

ありがとうございます。

OleLoadPicture () の宣言:
すみません、C/C++ の書き方と、VBA が混在して読みにくかったと思います。
C/C++ の宣言
 long OleLoadPicture (IStream *, long, int, REFIID, void **);
を書き換えると、
 Function OleLoadPicture (ByVal s As IStream, ByVal z As Long, _
   ByVal r As Long, ByRef g As GUID, ByRef p As IPictureDisp) As Long
で正しいと思います。
ここで、p が As IPictureDisp なのは、C++ での呼び出し:
 OleLoadPicture (s, 0, TRUE, __uuidof(p), reinterpret_cast<void **>(&p));
における __uuidof(p), reinterpret_cast<void **>(&p) の代替です(g には、
IID_IPictureDisp の GUID(下記)を入れるつもりなので、これで良いです)。
型 GUID は(C での定義に倣って)、
 Private Type GUID
   Data1 As Long
   Data2 As Integer
   Data3 As Integer
   Data4(0 To 7) As Byte
 End Type
などと(独自?に宣言)しています(この定義で、他の API は呼び出せます)。

> APIを普段からご利用されているのでしょうか?
>
いいえ。知らないというわけではないですが。

IStream の取得:
foo() は、IDispatch::Invoke() を用いて外部から呼び出されます。
foo() を呼び出す側は、画像を CreateStreamOnHGlobal() で IStream にして、
引数 v As Variant として添えます。

参照ライブラリについて:
「参照設定」は、次の5つをチェックしています。
 Visual Basic For Applications
 Microsoft Excel 9.0 Object Library
 OLE Automation (stdole2.tlb)
 Microsoft Office 9.0 Object Library
 Microsoft Forms 2.0 Object Library

しようとしてること:
外部から Excel/VBA と画像をやり取りしようとしています。

補足日時:2003/03/29 23:45
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!