VBAからGDI+を使う資料集
inet上の達人の方々から御教示いただいたコードを少しアレンジさせてもらっています(切り貼りとも言います)。
  1. ホーム
  2. GDI+
  3. CBのbitmapをリサイズしてCBに書き戻す(その2)


クリップボードのbitmapをリサイズしてクリップボードに書き戻す

どうも不安定なので、再度Pictureを生成してからClipboardに貼り付ける方法に変更しました。
冗長だとは思いますが。


Public Enum InterpolationMode   ' 補間方法
    InterpolationModeInvalid = -1
    InterpolationModeDefault = 0
    InterpolationModeLowQuality = 1
    InterpolationModeHighQuality = 2
    InterpolationModeBilinear = 3
    InterpolationModeBicubic = 4
    InterpolationModeNearestNeighbor = 5
    InterpolationModeHighQualityBilinear = 6
    InterpolationModeHighQualityBicubic = 7
End Enum

Public Enum GDIPlusStatusConstants
    Ok = 0
    GenericError = 1
    InvalidParameter = 2
    OutOfMemory = 3
    ObjectBusy = 4
    InsufficientBuffer = 5
    NotImplemented = 6
    Win32Error = 7
    WrongState = 8
    Aborted = 9
    FileNotFound = 10
    ValueOverflow = 11
    AccessDenied = 12
    UnknownImageFormat = 13
    FontFamilyNotFound = 14
    FontStyleNotFound = 15
    NotTrueTypeFont = 16
    UnsupportedGdiplusVersion = 17
    GdiplusNotInitialized = 18
    PropertyNotFound = 19
    PropertyNotSupported = 20
    ProfileNotFound = 21
End Enum

Private Type Guid
        Data1          As Long
        Data2          As Integer
        Data3          As Integer
        Data4(7)       As Byte
End Type

Private Type GdiplusStartupInput
    GdiplusVersion              As Long
    DebugEventCallback          As Long
    SuppressBackgroundThread    As Long
    SuppressExternalCodecs      As Long
End Type

Private Type EncoderParameter
    Guid           As Guid
    NumberOfValues As Long
    Type           As Long
    Value          As Long
End Type

Private Type EncoderParameters
    Count         As Long
    Parameter(15) As EncoderParameter
End Type

Private Type PICTDESC
        cbSizeofstruct As Long
        picType        As Long
        hImage         As Long
        Option1        As Long
        Option2        As Long
End Type

Const PICTYPE_BITMAP = 1

' // Declareations --------------------------------------------------
'Clipboard
Private Declare Function IsClipboardFormatAvailable Lib "user32.dll" ( _
        ByVal wFormat As Long) As Long
Private Declare Function OpenClipboard Lib "user32.dll" ( _
        ByVal hWnd As Long) As Long
Private Declare Function GetClipboardData Lib "user32.dll" ( _
        ByVal wFormat As Long) As Long
Private Declare Function CloseClipboard Lib "user32.dll" () As Long
Private Declare Function CopyImage Lib "user32.dll" _
    (ByVal hImage As Long, ByVal uType As Long, ByVal cxDesired As Long, _
    ByVal cyDesired As Long, ByVal fuFlags As Long) As Long
Private Declare Function EmptyClipboard Lib "user32" () As Long
Private Declare Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hMem As Long) As Long

Private Const IMAGE_BITMAP = 0
Private Const LR_COPYRETURNORG = &H4
Private Const CF_BITMAP      As Long = 2
Private Const CF_PALETTE     As Long = 9

'GDI+
'CreateBitmapFromHBITMAP
Private Declare Function GdipCreateBitmapFromHBITMAP Lib "gdiplus.dll" _
    (ByVal hbm As Long, ByVal hpal As Long, ByRef bitmap As Long) As GDIPlusStatusConstants
'CreateBitmapFromGraphics
Private Declare Function GdipCreateBitmapFromGraphics Lib "gdiplus" _
        (ByVal Width As Long, ByVal Height As Long, _
        ByVal Target As Long, bitmap As Long) As Long
'CreatHBITMAPFromBitmap
Private Declare Function GdipCreateHBITMAPFromBitmap Lib "gdiplus" _
        (ByVal bitmap As Long, hbmReturn As Long, _
        ByVal background As Long) As Long
'DeleteGraphics
Private Declare Function GdipDeleteGraphics Lib "gdiplus" _
        (ByVal graphics As Long) As Long
'DisposeImage
Private Declare Function GdipDisposeImage Lib "gdiplus.dll" _
    (ByVal image As Long) As GDIPlusStatusConstants
'DrawImageRectI
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
'GetImageHeight
Private Declare Function GdipGetImageHeight Lib "gdiplus" _
        (ByVal image As Long, Height As Long) As Long
'GetImageWidth
Private Declare Function GdipGetImageWidth Lib "gdiplus" _
        (ByVal image As Long, Width As Long) As Long
'GetImageGraphicsContext
Private Declare Function GdipGetImageGraphicsContext Lib "gdiplus" _
        (ByVal image As Long, graphics As Long) As Long
'SetInterpolationMode 補間モード設定
Private Declare Function GdipSetInterpolationMode Lib "gdiplus" _
        (ByVal graphics As Long, _
        ByVal nInterpolationMode As InterpolationMode) As Long
'SaveImageToFile
Private Declare Function GdipSaveImageToFile Lib "gdiplus.dll" _
    (ByVal image As Long, ByVal filename As Long, ByRef clsidEncoder As Guid, ByVal encoderParams As Long) As GDIPlusStatusConstants
'Startup
Private Declare Function GdiplusStartup Lib "gdiplus.dll" _
    (ByRef token As Long, ByRef inputBuf As GdiplusStartupInput, ByVal outputBuf As Long) As GDIPlusStatusConstants
'Shutdown
Private Declare Sub GdiplusShutdown Lib "gdiplus.dll" (ByVal token As Long)

'その他
Private Declare Function CLSIDFromString Lib "ole32" _
    (ByVal lpszCLSID As Long, ByRef pclsid As Guid) As Long
Private Declare Function DeleteObject Lib "gdi32" _
        (ByVal hObject As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" ( _
        ByRef lpPictDesc As PICTDESC, _
        ByRef RefIID As Guid, _
        ByVal fPictureOwnsHandle As Long, _
        ByRef IPic As IPicture) As Long

'使用例 Vix等でトリミングした画像を、サイズ指定でエクセルのシートに貼り付けたかったのが
'発端です。
Sub test()
    Dim myStdPicture As StdPicture
    Dim gdipRet As GDIPlusStatusConstants
    
    Set myStdPicture = CreatePictureFromClipboard
    gdipRet = cb2cb_resized(myStdPicture, Range("A2").Value, InterpolationModeHighQualityBicubic)
    If gdipRet = GDIPlusStatusConstants.Ok Then ActiveSheet.Paste
End Sub

Public Function cb2cb_resized( _
              ByVal PicObj As IPictureDisp, _
              Optional ByVal outputHeight As Long = 200, _
              Optional ByVal InterpolationMode As InterpolationMode _
                           = InterpolationModeHighQualityBicubic _
              ) As GDIPlusStatusConstants
    
    Dim GdiPStartupInput    As GdiplusStartupInput
    Dim Ret                 As GDIPlusStatusConstants
    Dim GDIPToken           As Long, lngStatus As Long
    Dim GdipBmpHdl          As Long
    Dim lngHeight As Long, lngWidth As Long
    Dim pGraphics As Long, pDstBmp As Long
    Dim EncodParameters     As EncoderParameters
    Dim hBmp As Long
    Dim aspectRatio As Double
    
    Dim uPictDesc As PICTDESC
    Dim uGUID     As Guid
    Dim objPicture As StdPicture
    
    ' ピクチャーオブジェクトが無い
    If PicObj Is Nothing Then
        cb2cb_resized = GDIPlusStatusConstants.UnknownImageFormat
        Exit Function
    End If
    ' GDIスタートアップ構造体初期化
    GdiPStartupInput.GdiplusVersion = 1
    ' GDI+ライブラリ初期化して失敗なら終了
    If GdiplusStartup(GDIPToken, GdiPStartupInput, 0&) <> 0 Then Exit Function
    ' ピクチャーからGDI+BITMAPを作成
    Ret = GdipCreateBitmapFromHBITMAP(PicObj.Handle, 0&, GdipBmpHdl)
    ' 変換成功
    If Ret <> GDIPlusStatusConstants.Ok Then GoTo terminate
    ' 元画像サイズの取得 'OK
    GdipGetImageWidth GdipBmpHdl, lngWidth
    GdipGetImageHeight GdipBmpHdl, lngHeight
    aspectRatio = lngWidth / lngHeight
    ' サイズの変更
    lngWidth = CLng(outputHeight * aspectRatio)
    lngHeight = outputHeight
    If GdipGetImageGraphicsContext(GdipBmpHdl, pGraphics) = 0 Then
        ' コピー先Bitmap作成
        lngStatus = GdipCreateBitmapFromGraphics( _
                        lngWidth, lngHeight, pGraphics, pDstBmp)
        GdipDeleteGraphics pGraphics
        If lngStatus = 0 Then
            ' コピー用Graphics作成
            If GdipGetImageGraphicsContext(pDstBmp, pGraphics) = 0 Then
                ' 補間方法の設定
                InterpolationMode = InterpolationModeBicubic
                GdipSetInterpolationMode pGraphics, InterpolationMode
                ' イメージのコピー
                GdipDrawImageRectI pGraphics, GdipBmpHdl, 0, 0, lngWidth, lngHeight
                'bitmapオブジェクトのハンドルを取得
                GdipCreateHBITMAPFromBitmap pDstBmp, hBmp, 0&
            End If
            GdipDisposeImage pDstBmp
        End If
    End If
 ' GDI+BITMAPを廃棄
    GdipDisposeImage GdipBmpHdl
    
     ' 以降はOLEのPictureオブジェクト作成処理
    With uGUID
        .Data1 = &H20400
        .Data4(0) = &HC0
        .Data4(7) = &H46
    End With
    With uPictDesc
        .cbSizeofstruct = Len(uPictDesc)
        .picType = PICTYPE_BITMAP
        .hImage = hBmp
    End With
    
    If OleCreatePictureIndirect(uPictDesc, uGUID, 1, objPicture) < 0 Then DeleteObject hBmp
    Call CopyBitmapPictureToCB(ByVal objPicture)
    ' GDI+ライブラリ開放
terminate:
    GdiplusShutdown GDIPToken
    
    '下記のコードだと安定して貼り付けられない(pDstBmpを削除しないでも)
'    If OpenClipboard(0&) <> 0 Then
'        EmptyClipboard
'        SetClipboardData CF_BITMAP, hBmp
'        CloseClipboard
'        cb2cb_resized = GDIPlusStatusConstants.Ok
'    Else
'        cb2cb_resized = GDIPlusStatusConstants.Aborted
'    End If
End Function

' // クリップボードのビットマップデータから Picture オブジェクトを作成
'http://okwave.jp/qa/q2885043.html
'KenKen_SPさん
Public Function CreatePictureFromClipboard() As StdPicture
    Dim hImg      As Long
    Dim hPalette As Long
    Dim uPictDesc As PICTDESC
    Dim uGUID     As Guid

    Set CreatePictureFromClipboard = Nothing
    ' 終了条件:: クリップボードに該当データが無い
    If IsClipboardFormatAvailable(CF_BITMAP) = 0 Then Exit Function
    ' 終了条件:: クリップボードからイメージハンドルが取得できない
    If OpenClipboard(0&) <> 0 Then
        hImg = GetClipboardData(CF_BITMAP)
        hPalette = GetClipboardData(CF_PALETTE)
        Call CloseClipboard
    Else
        MsgBox "cannot get bitmap from CB"
        Exit Function
    End If
    If hImg = 0 Then
        MsgBox "cannot get bitmap from CB"
        Exit Function
    End If
    With uPictDesc
        .cbSizeofstruct = Len(uPictDesc)
        .picType = PICTYPE_BITMAP
        .hImage = hImg
        .Option1 = hPalette
    End With
    With uGUID
        .Data1 = &H20400
        .Data4(0) = &HC0
        .Data4(7) = &H46
    End With
    Call OleCreatePictureIndirect(uPictDesc, _
                                  uGUID, _
                                  0&, _
                                  CreatePictureFromClipboard)
End Function

Private Function ConvCLSID(ByVal sGuid As String) As Guid
    CLSIDFromString StrPtr(sGuid), ConvCLSID
End Function

' ビットマップ形式のPictureをクリップボードにコピー
Private Function CopyBitmapPictureToCB(ByVal pic As Object) As Boolean
    Dim hBmp As Long
    If pic Is Nothing Then Exit Function
    If pic.Type <> PICTYPE_BITMAP Then Exit Function
    hBmp = pic.Handle
    hBmp = CopyImage(hBmp, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)
    If hBmp = 0 Then Exit Function
    If OpenClipboard(0) Then
        EmptyClipboard
        If SetClipboardData(CF_BITMAP, hBmp) Then
            hBmp = 0
            CopyBitmapPictureToCB = True
        End If
        CloseClipboard
    End If
    If hBmp Then DeleteObject hBmp
End Function