VB6でGDI+を使用し、モジュール(DLL, EXE)内のリソースからビットマップを表示しようとしています。
現状は、GdipCreateBitmapFromResource関数の戻り値が2(InvalidParameter)で失敗します。
恐らく、引き数の指定に誤りがあると思うのですが、うまくいきません。
以下は、現状のテストコードです。
解決策についてアドバイスお願いします。
Option Explicit
Private Declare Function LoadLibraryEx Lib "kernel32" _
Alias "LoadLibraryExA" (ByVal lpLibFileName As String, _
ByVal hFile As Long, ByVal dwFlags As Long) As Long
Private Const LOAD_LIBRARY_AS_DATAFILE As Long = 2
Private Declare Function FreeLibrary Lib "kernel32" ( _
ByVal hLibModule As Long) As Long
Private m_lToken As Long
Private Type GdiplusStartupInput
GdiplusVersion As Long
DebugEventCallback As Long
SuppressBackgroundThread As Long
SuppressExternalCodecs As Long
End Type
Private Declare Function GdiplusStartup Lib "gdiplus" ( _
token As Long, inputbuf As GdiplusStartupInput, _
Optional ByVal outputbuf As Long = 0) As Long
Private Declare Function GdiplusShutdown Lib "gdiplus" ( _
ByVal token As Long) As Long
Private Declare Function GdipCreateBitmapFromResource Lib "gdiplus" ( _
ByVal hInstance As Long, ByVal lpBitmapName As Long, _
bitmap As Long) As Long
Private Declare Function GdipCreateFromHDC Lib "gdiplus" ( _
ByVal hdc As Long, graphics As Long) As Long
Private Declare Function GdipGetImageWidth Lib "gdiplus" ( _
ByVal image As Long, Width As Long) As Long
Private Declare Function GdipGetImageHeight Lib "gdiplus" ( _
ByVal image As Long, Height As Long) As Long
Private Declare Function GdipDrawImageRectI Lib "gdiplus" ( _
ByVal graphics As Long, ByVal image As Long, _
ByVal X As Long, ByVal Y As Long, _
ByVal Width As Long, ByVal Height As Long) As Long
Private Declare Function GdipDeleteGraphics Lib "gdiplus" ( _
ByVal graphics As Long) As Long
Private Declare Function GdipDisposeImage Lib "gdiplus" ( _
ByVal image As Long) As Long
Private Sub Command1_Click()
Dim sModuleName As String
Dim sName As String
Dim hModule As Long
Dim lBitmap As Long
Dim lStatus As Long
Dim lHeight As Long
Dim lWidth As Long
Dim lGraphics As Long
sModuleName = "C:\Windows\Explorer.exe"
sName = "#150"
hModule = LoadLibraryEx(sModuleName, 0, LOAD_LIBRARY_AS_DATAFILE)
If (hModule = 0) Then Exit Sub
lStatus = GdipCreateBitmapFromResource(hModule, StrPtr(sName), lBitmap)
If (lStatus = 0) Then
lStatus = GdipGetImageHeight(lBitmap, lHeight)
lStatus = GdipGetImageWidth(lBitmap, lWidth)
If (lStatus = 0) Then
lStatus = GdipCreateFromHDC(Picture1.hdc, lGraphics)
If (lStatus = 0) Then
lStatus = GdipDrawImageRectI(lGraphics, lBitmap, 0, 0, lWidth, lHeight)
Call GdipDeleteGraphics(lGraphics)
End If
End If
Call GdipDisposeImage(lBitmap)
End If
Call FreeLibrary(hModule)
If (lStatus <> 0) Then MsgBox "GDI+ Error " & lStatus
End Sub
Private Sub Form_Load()
Dim gsi As GdiplusStartupInput
gsi.GdiplusVersion = 1
Call GdiplusStartup(m_lToken, gsi)
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
Call GdiplusShutdown(m_lToken)
End Sub
最初に確認しておきたいのですが、お使いの環境の
「C:\Windows\Explorer.exe」には、#150のBitmapリソースが
含まれているのでしょうか?
もし含まれていなければ、InvalidParameter(=2)のエラーが
発生した時に、Err.LastDllError の値が、1814
(指定されたリソース名がイメージ ファイルに見つかりません。)
になっているかと思います。
説明不足で申し訳ありません。
>「C:\Windows\Explorer.exe」には、#150のBitmapリソースが
> 含まれているのでしょうか?
存在します。Resource Hacker で確認しました。
> もし含まれていなければ、InvalidParameter(=2)のエラーが
> 発生した時に、Err.LastDllError の値が、1814
> (指定されたリソース名がイメージ ファイルに見つかりません。)
> になっているかと思います。
GdipCreateBitmapFromResource関数コール直後の
Err.LastDllError の値は、0 でした。
尚、他のEXEやDLLでも全て同じエラー(InvalidParameter)で失敗します。
宜しくお願いします。
>>「C:\Windows\Explorer.exe」には、#150のBitmapリソースが
>> 含まれているのでしょうか?
> 存在します。Resource Hacker で確認しました。
あら、そうですか。MAKEINTRESOURCE を利用して、
sName = "#150"
lStatus = GdipCreateBitmapFromResource(hModule, StrPtr(sName), lBitmap)
ではなく、
lStatus = GdipCreateBitmapFromResource(hModule, 150, lBitmap)
ならばいけるかと思いましたが……関係無さそうですね。
> GdipCreateBitmapFromResource関数コール直後の
> Err.LastDllError の値は、0 でした。
LastDllErrorも無しですか…ちょっと検討がつきません。
こちらでは同じコードで、Win2000機/WinXP機、どちらも表示されたのですが…。
(Win2000機の方には、セキュリティ対策済みのGDI+再頒布モジュールをインストールしました)
魔界の仮面弁士 さん レスありがとうございます。
> こちらでは同じコードで、Win2000機/WinXP機、どちらも表示されたのですが…。
当方の環境を書き忘れていました。Win98です。
...ということは、環境依存の現象ということですね。残念!
>> 他のEXEやDLLでも全て同じエラー(InvalidParameter)で失敗します。
これは、誤りでしたので、訂正します。
いろいろ試した結果、LoadLibraryEx()関数の第3引き数(dwFlags)の指定を変更してやると、成功したり、失敗したりする様です。例えば、
1.LoadLibraryEx(sModuleName, 0, LOAD_LIBRARY_AS_DATAFILE) では、
"Shell32.dll", "#130" の場合、表示される。
"C:\Windows\Explorer.exe", "#150" の場合、表示されない。
但し、他のDLL, EXEでもLoadLibraryEx関数の戻り値は非ゼロです。
2.LoadLibraryEx(sModuleName, 0, 0) では、
上記DLL, EXE 共に、表示される。しかし、他のEXEでは、LoadLibraryEx関数
の戻り値がゼロ(hModule=0)で、表示されない場合が多い。
それと、hModule<>0 で、表示される場合と表示されない場合に、
GdipCreateBitmapFromResource関数のGDI+ StatusとErr.LastDllErrorを調べてみると、妙な結果になりました。
・表示される場合
GDI+ Status = 0 (Ok)
Err.LastDllError = 87 (パラメータが正しくありません。)
・表示されない場合
GDI+ Status = 2 (InvalidParameter)
Err.LastDllError = 0 (操作は正常に終了しました。)
他の方法を検討してみたいと思います。
解決したら、報告します。
いろいろとありがとうございました。
ども、何とか解決に近づいたので、現状報告と疑問点について質問させて下さい。
GdipCreateBitmapFromResource関数を諦め、以下の様にしました。
FindResource()、LoadResource()、LockResource()を使用し、メモリにリソースをロードする関数を作りました。
これで、LoadLibraryEx(szModuleName, 0, LOAD_LIBRARY_AS_DATAFILE)で得たモジュールのハンドルを使用しても失敗無くビットマップを表示できる様です。
この方法は、PNGやTIFF等のリソースも表示できるので、メモリにストリームを作ってGdipCreateBitmapFromStream関数に渡す様にしました。
ここからが質問です。
下記の自作関数CreateStreamOnResourceで作成したストリームをCommand1_Click()内の Set oStream = Nothing にてグローバルメモリを開放してるつもりです。
MSDNによると、CreateStreamOnHGlobal関数の第2引き数(fDeleteOnRelease)をTRUE(1)にした場合、ストリームを開放すると、自動的にグローバルメモリが開放される旨が書かれています。
この場合のストリームの開放とは、Set oStream = Nothing でいいでしょうか?
宜しくお願いします。
Option Explicit
Private m_lToken As Long
Private Type GdiplusStartupInput
GdiplusVersion As Long
DebugEventCallback As Long
SuppressBackgroundThread As Long
SuppressExternalCodecs As Long
End Type
Private Declare Function GdiplusStartup Lib "gdiplus" ( _
token As Long, inputbuf As GdiplusStartupInput, _
Optional ByVal outputbuf As Long = 0) As Long
Private Declare Function GdiplusShutdown Lib "gdiplus" ( _
ByVal token As Long) As Long
Private Declare Function GdipCreateFromHDC Lib "gdiplus" ( _
ByVal hdc As Long, graphics As Long) As Long
Private Declare Function GdipGetImageWidth Lib "gdiplus" ( _
ByVal image As Long, Width As Long) As Long
Private Declare Function GdipGetImageHeight Lib "gdiplus" ( _
ByVal image As Long, Height As Long) As Long
Private Declare Function GdipDrawImageRectI Lib "gdiplus" ( _
ByVal graphics As Long, ByVal image As Long, _
ByVal X As Long, ByVal Y As Long, _
ByVal Width As Long, ByVal Height As Long) As Long
Private Declare Function GdipDeleteGraphics Lib "gdiplus" ( _
ByVal graphics As Long) As Long
Private Declare Function GdipDisposeImage Lib "gdiplus" ( _
ByVal image As Long) As Long
' Private Declare Function GdipLoadImageFromStream Lib "gdiplus" _
(ByVal stream As Long, image As Long) As Long
Private Declare Function GdipCreateBitmapFromStream Lib "gdiplus" ( _
ByVal stream As Long, bitmap As Long) As Long
Private Declare Function LoadLibraryEx Lib "kernel32" _
Alias "LoadLibraryExA" (ByVal lpLibFileName As String, _
ByVal hFile As Long, ByVal dwFlags As Long) As Long
Private Const LOAD_LIBRARY_AS_DATAFILE As Long = 2
' Private Const LOAD_WITH_ALTERED_SEARCH_PATH As Long = 8
Private Declare Function FreeLibrary Lib "kernel32" ( _
ByVal hLibModule As Long) As Long
Private Declare Function FindResource Lib "kernel32" _
Alias "FindResourceA" (ByVal hModule As Long, _
ByVal lpName As String, ByVal lpType As String) As Long
Private Declare Function LoadResource Lib "kernel32" ( _
ByVal hModule As Long, ByVal hResInfo As Long) As Long
Private Declare Function SizeofResource Lib "kernel32" ( _
ByVal hModule As Long, ByVal hResInfo As Long) As Long
Private Declare Function LockResource Lib "kernel32" ( _
ByVal hResData As Long) As Long
Private Declare Function FreeResource Lib "kernel32" ( _
ByVal hResData As Long) As Long
Private Declare Function GlobalAlloc Lib "kernel32" ( _
ByVal uFlags As Long, ByVal dwBytes As Long) As Long
Private Const GMEM_MOVEABLE As Long = &H2
Private Const GMEM_ZEROINIT As Long = &H40
Private Const GHND = (GMEM_MOVEABLE Or GMEM_ZEROINIT)
Private Declare Function GlobalLock Lib "kernel32" ( _
ByVal hMem As Long) As Long
Private Declare Function GlobalUnlock Lib "kernel32" ( _
ByVal hMem As Long) As Long
' Private Declare Function GlobalFree Lib "kernel32.dll" ( _
ByVal hMem As Long) As Long
Private Declare Sub MoveMemory Lib "kernel32" _
Alias "RtlMoveMemory" (Destination As Any, _
Source As Any, ByVal Length As Long)
Private Declare Function CreateStreamOnHGlobal Lib "ole32.dll" ( _
ByVal hGlobal As Long, ByVal fDeleteOnRelease As Long, _
ppstm As Any) As Long
Private Function CreateStreamOnResource(ByVal sModuleName As String, _
ByVal vName As Variant, _
ByVal vType As Variant, _
oStream As IUnknown) As Long
Dim hModule As Long
Dim hResource As Long
Dim lResSize As Long
Dim hResData As Long
Dim pResData As Long
Dim hGlobal As Long
Dim pGlobal As Long
Dim hr As Long
CreateStreamOnResource = 1
hModule = LoadLibraryEx(sModuleName, 0, LOAD_LIBRARY_AS_DATAFILE)
If hModule = 0 Then Exit Function
If IsNumeric(vType) Then vType = "#" & CStr(vType)
If IsNumeric(vName) Then vName = "#" & CStr(vName)
hResource = FindResource(hModule, vName, vType)
If hResource <> 0 Then
lResSize = SizeofResource(hModule, hResource)
hResData = LoadResource(hModule, hResource)
If lResSize > 0 And hResData <> 0 Then
pResData = LockResource(hResData)
If pResData <> 0 Then
Select Case vType
Case "#2" ' BITMAP
hGlobal = GlobalAlloc(GHND, lResSize + 14)
Case Else
hGlobal = GlobalAlloc(GHND, lResSize)
End Select
If hGlobal <> 0 Then
pGlobal = GlobalLock(hGlobal)
If pGlobal <> 0 Then
Select Case vType
Case "#2" ' BITMAP
Dim nBitCount As Integer
Dim lCompression As Long
Dim lClrUsed As Long
Dim lOffBits As Long
Call MoveMemory(nBitCount, ByVal pResData + 14, 2) ' biBitCount
Call MoveMemory(lCompression, ByVal pResData + 16, 4) ' biCompression
Call MoveMemory(lClrUsed, ByVal pResData + 32, 4) ' biClrUsed
If lClrUsed = 0 Then
Select Case nBitCount
Case 1, 4, 8
lOffBits = 4 * (2 ^ nBitCount)
Case 24
lOffBits = 0
Case 16, 32
lOffBits = IIf(lCompression = 0, 12, 0)
End Select
Else
lOffBits = lClrUsed * 4
End If
lOffBits = lOffBits + 14 + 40
Call MoveMemory(ByVal pGlobal, &H4D42, 2) ' bfType: "BM"
Call MoveMemory(ByVal pGlobal + 2, lResSize + 14, 4) ' bfSize
Call MoveMemory(ByVal pGlobal + 10, lOffBits, 4) ' bfOffBits
Call MoveMemory(ByVal pGlobal + 14, ByVal pResData, lResSize)
Case Else
Call MoveMemory(ByVal pGlobal, ByVal pResData, lResSize)
End Select
Call GlobalUnlock(hGlobal)
hr = CreateStreamOnHGlobal(hGlobal, 1, oStream)
CreateStreamOnResource = hr
End If
End If
End If
Call FreeResource(hResData)
End If
End If
Call FreeLibrary(hModule)
End Function
Private Sub Command1_Click()
Const RT_BITMAP As Long = 2
Dim sModuleName As String
Dim vName As Variant
Dim vType As Variant
Dim hModule As Long
Dim oStream As IUnknown
Dim lBitmap As Long
Dim lStatus As Long
Dim lHeight As Long
Dim lWidth As Long
Dim lGraphics As Long
sModuleName = "C:\Windows\Explorer.exe"
vName = 150
vType = RT_BITMAP
hModule = LoadLibraryEx(sModuleName, 0, LOAD_LIBRARY_AS_DATAFILE)
If (hModule = 0) Then Exit Sub
If (CreateStreamOnResource(sModuleName, vName, vType, oStream) = 0) Then
lStatus = GdipCreateBitmapFromStream(ObjPtr(oStream), lBitmap)
If (lStatus = 0) Then
lStatus = GdipGetImageHeight(lBitmap, lHeight)
lStatus = GdipGetImageWidth(lBitmap, lWidth)
If (lStatus = 0) Then
lStatus = GdipCreateFromHDC(Picture1.hdc, lGraphics)
If (lStatus = 0) Then
lStatus = GdipDrawImageRectI(lGraphics, lBitmap, 0, 0, lWidth, lHeight)
Call GdipDeleteGraphics(lGraphics)
End If
End If
Call GdipDisposeImage(lBitmap)
End If
Set oStream = Nothing ' これで、いいのかな?
End If
Call FreeLibrary(hModule)
If (lStatus <> 0) Then MsgBox "GDI+ Error " & lStatus
End Sub
Private Sub Form_Load()
Dim gsi As GdiplusStartupInput
gsi.GdiplusVersion = 1
Call GdiplusStartup(m_lToken, gsi)
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
Call GdiplusShutdown(m_lToken)
End Sub
使用上問題ない様なので、解決とします。
ありがとうございました。