- ホーム
- EMF
- embededobjecttext
Excelのワークシートの埋め込みオブジェクトのファイルパスをアイコンから読む
QAサイトのお題から、Office Open XMLファイルの中味を覗いてみたところ、埋め込まれたオブジェクトのファイル本体は内部的な名前に改名されており、アイコンがemf形式で収納されていた。
このemfを昔MSのサイトでみつけたCのソースから生成したemf viewerで見てみたところ、パスの切れて表示されない部分も内部的には含まれている事が分かった。
emfというのはDCへの描画をAPIで行う手順をそのまま記述した様なものなので、上記のツールでemf recordを表示させてみると、EMR_EXTTEXTOUTWがテキスト表示部分であり、
その中にはテキストが含まれている事が期待できる。バイナリエディタで確認したところ、パスを示す文字列の断片が確認できた。
という訳で、昔どこかで見つけてきたBitmap取り出しの記事を参考に、テキスト取り出しにトライしてみた。
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type METAFILEPICT
mm As Long
xExt As Long
yExt As Long
hmf As Long
End Type
Private Type RECTL
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type SIZEL
cx As Long
cy As Long
End Type
Public Type POINTL
x As Long
y As Long
End Type
Private Type ENHMETAHEADER
iType As Long
nSize As Long
rclBounds As RECTL
rclFrame As RECTL '0.01mm単位
dSignature As Long
nVersion As Long
nBytes As Long
nRecords As Long
nHandles As Integer
sReserved As Integer
nDescription As Long
offDescription As Long
nPalEntries As Long
szlDevice As SIZEL
szlMillimeters As SIZEL
End Type
Type emr
iType As Long
nSize As Long
End Type
Type ENHMETARECORD
iType As Long
nSize As Long
dParm(1) As Long
End Type
Type EMRSETSTRETCHBLTMODE
pEmr As emr
iMode As Long
End Type
Public Type emrtext
ptlReference As POINTL
nchars As Long 'Stringの文字数
offString As Long 'これは所属するemfrecord先頭から文字列へのオフセット
fOptions As Long
rcl As RECT
offDx As Long
'ここにtextが存在する
End Type
'EMR_EXTTEXTOUTW Exampleの内容と合致する
Public Type EMREXTTEXTOUT
pEmr As emr 'Type & Size
rclBounds As RECT 'これはBounds:0x00.....values are not used.のlong4個分を便宜上表している?
iGraphicsMode As Long
exScale As Single ' and not Double as in the winapi vb declaration!
eyScale As Single ' and not Double as in the winapi vb declaration!
emrtext As emrtext
End Type
'====== 定数 ======
Private Const PICTYPE_BITMAP = 1 'pictdescに与えるpictureのタイプ
Private Const DIB_RGB_COLORS = 0&
Const PICTYPE_ENHMETAFILE = 4
Const CF_ENHMETAFILE = 14
Const BI_RGB = 0&
Const EMR_HEADER = 1
Const EMR_POLYBEZIER = 2
Const EMR_POLYGON = 3
Const EMR_POLYLINE = 4
Const EMR_POLYBEZIERTO = 5
Const EMR_POLYLINETO = 6
Const EMR_POLYPOLYLINE = 7
Const EMR_POLYPOLYGON = 8
Const EMR_SETWINDOWEXTEX = 9
Const EMR_SETWINDOWORGEX = 10
Const EMR_SETVIEWPORTEXTEX = 11
Const EMR_SETVIEWPORTORGEX = 12
Const EMR_SETBRUSHORGEX = 13
Const EMR_EOF = 14
Const EMR_SETPIXELV = 15
Const EMR_SETMAPPERFLAGS = 16
Const EMR_SETMAPMODE = 17
Const EMR_SETBKMODE = 18
Const EMR_SETPOLYFILLMODE = 19
Const EMR_SETROP2 = 20
Const EMR_SETSTRETCHBLTMODE = 21
Const EMR_SETTEXTALIGN = 22
Const EMR_SETCOLORADJUSTMENT = 23
Const EMR_SETTEXTCOLOR = 24
Const EMR_SETBKCOLOR = 25
Const EMR_OFFSETCLIPRGN = 26
Const EMR_MOVETOEX = 27
Const EMR_SETMETARGN = 28
Const EMR_EXCLUDECLIPRECT = 29
Const EMR_INTERSECTCLIPRECT = 30
Const EMR_SCALEVIEWPORTEXTEX = 31
Const EMR_SCALEWINDOWEXTEX = 32
Const EMR_SAVEDC = 33
Const EMR_RESTOREDC = 34
Const EMR_SETWORLDTRANSFORM = 35
Const EMR_MODIFYWORLDTRANSFORM = 36
Const EMR_SELECTOBJECT = 37
Const EMR_CREATEPEN = 38
Const EMR_CREATEBRUSHINDIRECT = 39
Const EMR_DELETEOBJECT = 40
Const EMR_ANGLEARC = 41
Const EMR_ELLIPSE = 42
Const EMR_RECTANGLE = 43
Const EMR_ROUNDRECT = 44
Const EMR_ARC = 45
Const EMR_CHORD = 46
Const EMR_PIE = 47
Const EMR_SELECTPALETTE = 48
Const EMR_CREATEPALETTE = 49
Const EMR_SETPALETTEENTRIES = 50
Const EMR_RESIZEPALETTE = 51
Const EMR_REALIZEPALETTE = 52
Const EMR_EXTFLOODFILL = 53
Const EMR_LINETO = 54
Const EMR_ARCTO = 55
Const EMR_POLYDRAW = 56
Const EMR_SETARCDIRECTION = 57
Const EMR_SETMITERLIMIT = 58
Const EMR_BEGINPATH = 59
Const EMR_ENDPATH = 60
Const EMR_CLOSEFIGURE = 61
Const EMR_FILLPATH = 62
Const EMR_STROKEANDFILLPATH = 63
Const EMR_STROKEPATH = 64
Const EMR_FLATTENPATH = 65
Const EMR_WIDENPATH = 66
Const EMR_SELECTCLIPPATH = 67
Const EMR_ABORTPATH = 68
Const EMR_GDICOMMENT = 70
Const EMR_FILLRGN = 71
Const EMR_FRAMERGN = 72
Const EMR_INVERTRGN = 73
Const EMR_PAINTRGN = 74
Const EMR_EXTSELECTCLIPRGN = 75
Const EMR_BITBLT = 76
Const EMR_STRETCHBLT = 77
Const EMR_MASKBLT = 78
Const EMR_PLGBLT = 79
Const EMR_SETDIBITSTODEVICE = 80
Const EMR_STRETCHDIBITS = 81
Const EMR_EXTCREATEFONTINDIRECTW = 82
Const EMR_EXTTEXTOUTA = 83
Const EMR_EXTTEXTOUTW = 84
Const EMR_POLYBEZIER16 = 85
Const EMR_POLYGON16 = 86
Const EMR_POLYLINE16 = 87
Const EMR_POLYBEZIERTO16 = 88
Const EMR_POLYLINETO16 = 89
Const EMR_POLYPOLYLINE16 = 90
Const EMR_POLYPOLYGON16 = 91
Const EMR_POLYDRAW16 = 92
Const EMR_CREATEMONOBRUSH = 93
Const EMR_CREATEDIBPATTERNBRUSHPT = 94
Const EMR_EXTCREATEPEN = 95
Const EMR_POLYTEXTOUTA = 96
Const EMR_POLYTEXTOUTW = 97
Const EMR_MIN = 1
Const EMR_MAX = 97
'==============================================================
'API関数の宣言
Private Declare Function GetActiveWindow Lib "user32.dll" () As Long
Private Declare Function GetLastError Lib "kernel32" () As Long
Private Declare Function OpenClipboard Lib "user32" (ByVal hWndNewOwner As Long) As Long
Private Declare Function CloseClipboard Lib "user32" () As Long
Private Declare Function GetClipboardData Lib "user32" (ByVal uFormat As Long) As Long
Private Declare Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
Private Declare Function EnumEnhMetaFile Lib "gdi32" (ByVal hdc As Long, ByVal hemf As Long, ByVal lpEnhMetaFunc As Long, lpData As Any, lpRect As RECT) As Long
Private Declare Function PlayEnhMetaFileRecord Lib "gdi32" (ByVal hdc As Long, ByVal pHandles As Long, ByVal pRecord As Long, ByVal HandleNum As Long) As Long
Private Declare Function RtlMoveMemory Lib "kernel32" (ByVal pDest As Long, ByVal pSrc As Long, ByVal l As Long) As Long
Private Declare Function GetEnhMetaFileBits Lib "gdi32" (ByVal hemf As Long, ByVal cbBuffer As Long, lpbBuffer As Byte) As Long
Private Declare Function GetEnhMetaFile Lib "gdi32" Alias "GetEnhMetaFileA" (ByVal lpszMetaFile As String) As Long
Private Declare Function GetMetaFile Lib "gdi32" Alias "GetMetaFileA" (ByVal lpFileName As String) As Long
Private Declare Function GetMetaFileBitsEx Lib "gdi32" (ByVal hmf As Long, ByVal nSize As Long, lpvData As Any) As Long
Private Declare Function SetWinMetaFileBits Lib "gdi32" (ByVal cbBuffer As Long, lpbBuffer As Byte, ByVal hdcRef As Long, lpmfp As METAFILEPICT) As Long
Private Declare Function PlayEnhMetaFile Lib "gdi32" (ByVal hdc As Long, ByVal hemf As Long, lpRect As RECT) As Long
Private Declare Function SetWinMetaFileBitsByNull Lib "gdi32" Alias "SetWinMetaFileBits" (ByVal cbBuffer As Long, lpbBuffer As Byte, ByVal hdcRef As Long, lpmfp As Long) As Long
Private Declare Function DeleteEnhMetaFile Lib "gdi32" (ByVal hemf As Long) As Long
Private Declare Function GetEnhMetaFileHeader Lib "gdi32" (ByVal hemf As Long, ByVal MetaHeaderSize As Long, ByRef MetaHeader As ENHMETAHEADER) As Long
Private Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Dest As Any, ByRef Src As Any, ByVal Length As Long)
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, _
ByRef Source As Long, _
ByVal Length As Long)
Sub getEmbededFilePath()
Dim SrcData() As Byte '元のメタファイルの内容を格納するバッファ
Dim SrcIdx As Long 'メタファイルからデータを取り出す位置を示す
Dim hSrcMetaFile As Long '複製元メタファイルのハンドル
Dim BufSize As Long 'SrcDataに格納されたメタファイルのサイズ
Dim RecordHeader As emr 'メタファイルレコードのヘッダ
'EMR_EXTTEXTOUTWの個数だけ宣言する必要あり。使い回し不可。とりあえず大きめに宣言。
Dim emfTextRecord(10) As EMREXTTEXTOUT
Dim emfText As emrtext
Dim topEMREXTTEXTOUT As Long
Dim extEmfText As String
Dim byteEmfText() As Byte
Dim i As Long
Dim filePath As String
'複製元メタファイルをクリップボードから取得
Selection.Copy
If OpenClipboard(0) Then
hSrcMetaFile = GetClipboardData(CF_ENHMETAFILE)
hSrcMetaFile = CopyEnhMetaFile(hSrcMetaFile, vbNullString)
CloseClipboard
End If
If hSrcMetaFile = 0 Then
MsgBox "emf取得に失敗"
Exit Sub ' 失敗
End If
'メタファイルの内容を取得
BufSize = GetEnhMetaFileBits(hSrcMetaFile, ByVal 0, ByVal 0) '最終引数はCのNULL
ReDim SrcData(BufSize)
BufSize = GetEnhMetaFileBits(hSrcMetaFile, BufSize, SrcData(0))
If BufSize = 0 Then
MsgBox "GetEnhMetaFileBits failed!"
Exit Sub
End If
SrcIdx = 0
i = 0
Do While SrcIdx < BufSize
extEmfText = Space(255)
'レコードのヘッダを取得
MoveMemory RecordHeader, SrcData(SrcIdx), Len(RecordHeader)
If RecordHeader.iType = EMR_EXTTEXTOUTW Then 'レコードの場合
'EMR_EXTTEXTOUTWの内容を、構造体emfTextRecord(AS EMREXTTEXTOUT )に取り込む
MoveMemory emfTextRecord(i), SrcData(SrcIdx), RecordHeader.nSize
'文字のレコードの先頭位置を保存
topEMREXTTEXTOUT = SrcIdx
emfText = emfTextRecord(i).emrtext
ReDim byteEmfText(emfText.nchars * 2)
'EMREXTTEXTOUTの入れ子の構造体emrtextは、親構造体(EMF RECORD)の先頭からの文字列の位置を
'.Offstringとして保持している。
MoveMemory byteEmfText(0), SrcData(topEMREXTTEXTOUT + emfText.offString), emfText.nchars * 2
'StrPtr(extEmfText)やVarPtr(extEmfText)だとハングアップする
'byte配列から文字列への変換(VBAが勝手にやってくれる)
extEmfText = byteEmfText
'合成したとき文字化けするので安直な対策。なおvbNullCharが入って居るためではなさそう。
filePath = filePath & Application.WorksheetFunction.Clean(extEmfText)
i = i + 1
End If
SrcIdx = SrcIdx + RecordHeader.nSize
Loop
DeleteEnhMetaFile hSrcMetaFile
MsgBox filePath
End Sub
' メタファイルレコード列挙コールバック
Public Function EnumFunc(ByVal hdc As Long, ByVal pHandles As Long, ByVal pRecord As Long, _
ByVal HandleNum As Long, ByVal pData As Long) As Long
Dim eh As ENHMETARECORD 'EMR 'レコードのヘッダ
'レコードのヘッダをehに格納
RtlMoveMemory VarPtr(eh), pRecord, Len(eh)
EnumFunc = 1
End Function<