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


Office Graphic Filter あれこれ

Windowsが32bitか、64bitかによってOffice Graphic Filterのインストール場所が違う
おまけに、JPEGのQuality設定のレジストリの場所も、メインのキーから違ったりする。
どちらのWindowsでも動くコードが実現できないかとおもってやってみた。
おまけ1:WSHを用いてレジストリを書き換えJPEG画質を変更
おまけ2:ユーザー環境変数を設定しOffice Graphic Filterにパスを通す


Option Explicit

Private Type FLTIMAGE
StructSize As Integer
Type As Byte
Reserved1(0 To 8) As Byte
hImage As Long
Reserved3(0 To 19) As Byte
End Type
Private Type FLTFILE
Reserved1 As Integer
Ext As String * 4
Reserved2 As Integer
Path As String * 260
Reserved3 As Currency
End Type

'32bit OS
Private Declare Function GetFilterInfo64 Lib _
"C:\Program Files (x86)\Common Files\microsoft shared\GRPHFLT\JPEGIM32.FLT" _
Alias "GetFilterInfo" (ByVal Ver As Integer, ByVal Reserved As Long, _
phMem As Long, ByVal flags As Long) As Long
'64bit OS
Private Declare Function GetFilterInfo32 Lib _
"C:\Program Files\Common Files\Microsoft Shared\Grphflt\JPEGIM32.FLT" _
Alias "GetFilterInfo" (ByVal Ver As Integer, ByVal Reserved As Long, _
phMem As Long, ByVal flags As Long) As Long

Private Declare Function ExportGr Lib "JPEGIM32.FLT" _
(ff As FLTFILE, fi As FLTIMAGE, ByVal hMem As Long) As Long
Private Const SaveExt As String = "Jpeg保存,*.Jpg"

Private Declare Function OpenClipboard Lib "user32" _
(ByVal hWndNewOwner As Long) As Long
Private Declare Function GetClipboardData Lib "user32" _
(ByVal uFormat As Long) As Long
Private Declare Function CloseClipboard Lib "user32" () As Long
Const CF_ENHMETAFILE = 14
Private Declare Function CopyEnhMetaFile Lib "gdi32" _
Alias "CopyEnhMetaFileA" _
(ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
Private Declare Function DeleteEnhMetaFile Lib "gdi32" _
(ByVal hemf As Long) As Long
Private Declare Function GlobalFree Lib "kernel32" _
(ByVal hMem As Long) As Long

'============================================================
'☆選択したオートシェープ、セル等をJPEGで保存
'DLLの宣言のAliasを活用して、OSのbit数により別々の場所から読み込んでいる

Sub saveSelectionAsJpeg()
  Dim myPath
  
  myPath = GetDesktopPath & "\fltTest.jpg"
  Call Selection.CopyPicture(xlPrinter, xlPicture)
  If SaveCBPictureAs(myPath) Then
  MsgBox "保存しました", vbInformation, Dir(myPath)
  Else
  MsgBox "失敗しました"
  End If
End Sub

Private Function SaveCBPictureAs(ByVal SavePath As String) As Boolean
  Dim fi As FLTIMAGE
  Dim ff As FLTFILE
  Dim hemf As Long
  Dim hMem As Long
  
  If OpenClipboard(0) Then
    hemf = CopyEnhMetaFile( _
    GetClipboardData(CF_ENHMETAFILE), vbNullString)
    CloseClipboard
  End If
  If hemf = 0 Then
    Exit Function
  End If
  ff.Path = SavePath & vbNullChar
  With fi
    .StructSize = LenB(fi)
    .Type = 1
    .hImage = hemf
  End With
  ' フィルタ呼び出し
  Select Case CheckOSbitCount
    Case 32
      If GetFilterInfo32(3, 0, hMem, &H10000) And &H10 Then
        If ExportGr(ff, fi, hMem) = 0 Then
        SaveCBPictureAs = True
        End If
      End If
    Case 64
      If GetFilterInfo64(3, 0, hMem, &H10000) And &H10 Then
        If ExportGr(ff, fi, hMem) = 0 Then
        SaveCBPictureAs = True
        End If
      End If
  End Select
  If hMem Then GlobalFree hMem
  DeleteEnhMetaFile hemf
End Function

'Windowsのbit数を求める
Private Function CheckOSbitCount() As Long
  'WMIにて使用する各種オブジェクトを定義・生成する。
  Dim oClassSet
  Dim oClass
  Dim oLocator
  Dim oService
  Dim sMesStr
  
  'ローカルコンピュータに接続する。
  Set oLocator = CreateObject("WbemScripting.SWbemLocator")
  Set oService = oLocator.ConnectServer
  'クエリー条件をWQLにて指定する。
  Set oClassSet = oService.ExecQuery("Select * From Win32_OperatingSystem")
  
  'コレクションを解析する。
  For Each oClass In oClassSet
    '最初の一個で打ち切って良いか不明だが...
    '元コードは複数取得して繋いでいた
    sMesStr = CStr(oClass.OSArchitecture)
    Exit For
  Next
  '32ビットといった文字列で取得される
  CheckOSbitCount = Val(sMesStr)
  
  '使用した各種オブジェクトを後片付けする。
  Set oClassSet = Nothing
  Set oClass = Nothing
  Set oService = Nothing
  Set oLocator = Nothing
End Function

'============================================================
'☆おまけ1 Office Graphic Filter のJPEG画質設定
Sub jpegQualityTest()
  setGraphicFilterJpegQuality 90
End Sub

'Office Graphic Filterの画質設定
Private Sub setGraphicFilterJpegQuality(jpegQuality As Long)
  Dim WshShell As Object
  Dim strNew As String
  Dim strRtn As String
  
'regedit.exeで確認するとQualityはREG_SZ:文字列とされている
  Const defaultValue As String = "0x0000004b"
  Const location32 As String = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Graphics Filters\Export\JPEG\Options\Quality"
  Const location64 As String = "HKEY_CURRENT_USER\Software\Microsoft\Shared Tools\Graphics Filters\Export\JPEG\Options\Quality"
  
  If jpegQuality < 10 Or jpegQuality > 95 Then
    MsgBox "Invalid Jpeg Quality"
     Exit Sub
  End If
  strNew = defaultValue
  Set WshShell = CreateObject("WScript.Shell")
  Mid(strNew, 9, 2) = LCase(Hex(jpegQuality))
  On Error GoTo errHandle
  Select Case CheckOSbitCount
    Case 32
       strRtn = WshShell.RegRead(location32)
      WshShell.RegWrite location32, strNew, "REG_SZ"
    Case 64
       strRtn = WshShell.RegRead(location64)
      WshShell.RegWrite location64, strNew, "REG_SZ"
  End Select
'  Debug.Print strRtn
  
errHandle:
  If Err.Number <> 0 Then MsgBox "Read error of registry"
  Set WshShell = Nothing
End Sub

'============================================================
'☆おまけ2 Office Graphic Filter DLLへのパスをユーザー環境変数に設定
'ユーザー環境変数のPathに設定することで、DllのDeclareのパスを外して動作する事を確認した

Sub setJpegFlt2EnvPath()
  Dim rtnName As String
  
  Const filePath32 As String = "C:\Program Files\Common Files\Microsoft Shared\Grphflt\JPEGIM32.FLT"
  Const filePath64 As String = "C:\Program Files (x86)\Common Files\microsoft shared\GRPHFLT\JPEGIM32.FLT"
  
  rtnName = Dir(filePath32)
  If rtnName = "" Then
    rtnName = Dir(filePath64)
    If rtnName = "" Then
      MsgBox "JPEG Filter Not Found"
      Exit Sub
    Else
      setUserPathEnv filePath64
    End If
    setUserPathEnv filePath32
  End If
End Sub

Private Sub setUserPathEnv(strNewPath As String)
  Dim objShell, strSystemPath, strUserPath, rCode ', strNewPath
  
  Set objShell = CreateObject("wscript.shell")
  On Error Resume Next
  strUserPath = objShell.RegRead("HKCU\Environment\Path")
  Select Case Err.Number
    '正常に取得できた場合は追記する 重複チェック
    Case 0
      If InStr(strUserPath, strNewPath) = 0 Then
        rCode = objShell.RegWrite("HKCU\Environment\Path", strUserPath & ";" & strNewPath, "REG_SZ")
      End If
    '存在しないとエラーになるので新規生成
'  -2147024894   レジストリ キー hoge  を開いて読み取ることができません。
    Case -2147024894
      rCode = objShell.RegWrite("HKCU\Environment\Path", strNewPath, "REG_SZ")
    Case Else
      MsgBox Err.Number & vbCrLf & Err.Description & vbCrLf & Err.Source
      Exit Sub
  End Select
End Sub

Private Function GetDesktopPath() As String
Dim wScriptHost As Object, strInitDir As String
Set wScriptHost = CreateObject("Wscript.Shell")
GetDesktopPath = wScriptHost.SpecialFolders("Desktop")
Set wScriptHost = Nothing
End Function