【问题标题】:How do I convert a stdole.StdPicture to a different Type?如何将 stdole.StdPicture 转换为不同的类型?
【发布时间】:2010-12-03 04:54:31
【问题描述】:

要获得赏金,请提供带有工作代码的答案。谢谢。

我有一个类型为 vbPicTypeIcon 的 stdole.StdPicture 对象。我需要将其转换为类型 vbPicTypeBitmap。由于项目限制,我需要能够使用 Win32 或 VBA 来执行此操作。我正在尝试将文件的图标加载到命令栏按钮。这是我到目前为止所拥有的。它会产生一个可爱的黑色方块:)我对图形领域真的很陌生,所以如果这是一个基本问题,请原谅我。

Option Explicit

Private Const vbPicTypeBitmap As Long = 1
Private Const vbPicTypeIcon As Long = 3

Private Const SHGFI_ICON As Long = &H100&
Private Const SHGFI_SMALLICON As Long = &H1&

Private Type PICTDESC
    cbSize As Long
    pictType As Long
    hIcon As Long
    hPal As Long
End Type

Private Type typSHFILEINFO
  hIcon As Long
  iIcon As Long
  dwAttributes As Long
  szDisplayName As String * 260
  szTypeName As String * 80
End Type

Private Declare Function DestroyIcon Lib "user32" (ByVal hIcon As Long) As Long
Private Declare Function SHGetFileInfoA Lib "shell32.dll" (ByVal pszPath As String, ByVal dwFileAttributes As Long, psfi As typSHFILEINFO, ByVal cbSizeFileInfo As Long, ByVal uFlags As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (lpPictDesc As PICTDESC, riid As Any, ByVal fOwn As Long, ipic As stdole.IPictureDisp) As Long

Public Sub Test()
    Dim btn As Office.CommandBarButton
    Dim lngRslt As Long
    Dim lngAppInstc As Long
    Dim strFilePath As String
    Dim tFI As typSHFILEINFO
    Dim pic As stdole.IPictureDisp
    Set btn = TestEnv.GetTestButton
    lngAppInstc = Excel.Application.Hinstance
    strFilePath = TestEnv.GetTestFile
    If LenB(strFilePath) = 0& Then
        Err.Raise 70&
    End If
    SHGetFileInfoA strFilePath, 0&, tFI, LenB(tFI), SHGFI_ICON Or SHGFI_SMALLICON
    Set pic = IconToPicture(tFI.hIcon)
    btn.Picture = pic
Exit_Proc:
    On Error Resume Next
    If tFI.hIcon Then
        lngRslt = DestroyIcon(tFI.hIcon)
    End If
    Exit Sub
Err_Hnd:
    MsgBox Err.Description, vbCritical Or vbMsgBoxHelpButton, Err.Number, Err.HelpFile, Err.HelpContext
    Resume Exit_Proc
    Resume
End Sub

Private Function IconToPicture(ByVal hIcon As Long) As stdole.IPictureDisp
    'Modified from code by Francesco Balena on DevX
    Dim pic As PICTDESC
    Dim guid(0 To 3) As Long
    Dim pRtnVal As stdole.IPictureDisp
    pic.cbSize = LenB(pic)
    'pic.pictType = vbPicTypeBitmap
    pic.pictType = vbPicTypeIcon
    pic.hIcon = hIcon
    ' this is the IPicture GUID {7BF80980-BF32-101A-8BBB-00AA00300CAB}
    ' we use an array of Long to initialize it faster
    guid(0) = &H7BF80980
    guid(1) = &H101ABF32
    guid(2) = &HAA00BB8B
    guid(3) = &HAB0C3000
    ' create the picture,
    ' return an object reference right into the function result
    OleCreatePictureIndirect pic, guid(0), True, pRtnVal
    Set IconToPicture = pRtnVal
End Function

【问题讨论】:

  • 我下面的代码虽然需要清理,但只使用 API 并且完全符合您的要求。示例中有一个表单和一个图片框,仅用于演示它的工作原理。
  • 嗨 Beaner,有很多编辑,所以你可能错过了。但是解决方案必须在 VBA 中工作。 VBA 没有优化校准。不幸的是,这就是我问这个问题的原因:) 很抱歉有任何困惑。
  • 您可能错过了它,但图片框不再是转换的一部分。我把它留在那里以在示例中显示转换后的图像。就像我说的我没有时间清理代码。现在它已经被清理了一些,我完全删除了图片框以消除混乱。转换为 ALL WinAPI
  • 嗨 Beaner,我调整了您最近发布的代码,将图片从 PictureFromBitmap 加载到 CommandBarButton 的 Picture 属性,但我仍然遇到同样的问题。它看起来像一个类型的图片(位图),但它仍然加载为黑色矩形。这很令人沮丧。
  • 我知道您不想使用剪贴板,但似乎微软推荐将透明图像粘贴到按钮上的方法。 support.microsoft.com/kb/288771

标签: vba com vb6 winapi ole


【解决方案1】:

在 vbAccelerator.com 上给 this post 一个机会。

编辑:我发现的最接近 VBA 的是 officeblogs.net 上的这篇文章。该代码采用图标而不是图标句柄。

【讨论】:

  • 嗨 C 磅。也许我应该提到我也需要这个解决方案才能在 VBA 中工作。这就是造成问题的原因。不幸的是,VBA 无法访问 Picture 对象。这就是我特意尝试使用 stdole.IPictureDisp 的原因。是否可以使您引用的代码生成正确的对象?
  • 很抱歉仅提供 VB 链接。我添加了一个可能适用于 VBA 的编辑。
  • 嗨 CPG,再一次,我们都解决了这个问题,但并不完全在那里。这是 VB.Net 代码。我可以对其进行编码和编译,但随后我将分发一个附加文件(如前所述,我试图避免)。它还带回了 Icon 类型的 StdPicture 。经过深入研究,我意识到为什么对图片属性的分配不起作用是因为 StdPicture 对象需要是位图类型。据我所知,问题归结为试图弄清楚如何转换所述图标。我最终使用 SHGetFileInfoA 到达正确的 16X16 图标句柄,然后
  • ... OleCreatePictureIndirect 以获取 StdPicture 对象。但它的类型不对。这就是我卡住的地方。如果您尝试将 StdPicture 应用于 CommandBarButton,您只会有一个黑色方块。
【解决方案2】:

好的,我已经清理了代码。 ExtractAssociatedIcon 方法返回一个 64x64 的图标,所以对于这个例子,我刚刚硬编码了这个大小。图片框已被删除,并且图像已分配给表单的图片属性以避免混淆。

示例:将代码复制到新表单并运行

Option Explicit

Private Declare Function ExtractAssociatedIcon Lib "shell32.dll" Alias "ExtractAssociatedIconA" (ByVal hInst As Long, ByVal lpIconPath As String, lpiIcon As Long) As Long
Private Declare Function DestroyIcon Lib "user32" (ByVal hIcon As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function FillRect Lib "user32" (ByVal hdc As Long, lpRect As RECT, ByVal hBrush As Long) As Long
Private Declare Function DrawIconEx Lib "user32" (ByVal hdc As Long, ByVal xLeft As Long, ByVal yTop As Long, ByVal hIcon As Long, ByVal cxWidth As Long, ByVal cyWidth As Long, ByVal istepIfAniCur As Long, ByVal hbrFlickerFreeDraw As Long, ByVal diFlags As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (ByRef PicDesc As PICTDESC_BMP, ByRef RefIID As GUID, ByVal fPictureOwnsHandle As Long, ByRef IPic As IPicture) As Long

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

Private Type PICTDESC_BMP
    Size As Long
    Type As Long
    hBmp As Long
    hPal As Long
    Reserved As Long
End Type

Const DI_MASK = &H1
Const DI_IMAGE = &H2
Const DI_NORMAL = DI_MASK Or DI_IMAGE

Private Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
End Type

Private Sub Form_Load()

   Call GetIcon("C:\Program Files\Internet Explorer\iexplore.exe")

End Sub

Private Sub GetIcon(ByVal sFileName As String)
   Dim hIcon As Long
   Dim hAssocIcon As Long
   Dim sAssocFile As String * 260
   Dim sCommand As String
   Dim lDC As Long
   Dim lBmp As Long
   Dim R As RECT
   Dim OldBMP As Long

   Me.AutoRedraw = True
   hIcon = ExtractAssociatedIcon(App.hInstance, sFileName, hAssocIcon)
   If hIcon <> 0 Then 'no icons found - use icon generic icon resource
      'Create a device context, compatible with the screen
      lDC = CreateCompatibleDC(GetDC(0&))
      'Create a bitmap, compatible with the screen
      lBmp = CreateCompatibleBitmap(GetDC(0&), 64, 64)
      'Select the bitmap into the device context
      OldBMP = SelectObject(lDC, lBmp)
      ' Set the rectangles' values
      R.Left = 0
      R.Top = 0
      R.Right = 64
      R.Bottom = 64
      ' Fill the rect with white
      FillRect lDC, R, 0
      ' Draw the icon
      Call DrawIconEx(lDC, 0, 0, hIcon, 64, 64, 0, 0, DI_NORMAL)
      Me.Picture = PictureFromBitmap(lBmp, 0&)
      DestroyIcon (hIcon)
   End If
   Call SelectObject(lDC, OldBMP)
   Call DeleteObject(lDC)

End Sub

Private Function PictureFromBitmap(ByVal hBmp As Long, ByVal hPal As Long) As StdPicture
    Dim IPictureIID As GUID
    Dim IPic As IPicture
    Dim tagPic As PICTDESC_BMP
    Dim lpGUID As Long

    ' Fill in the IPicture GUID
    ' {7BF80980-BF32-101A-8BBB-00AA00300CAB}
    With IPictureIID
        .Data1 = &H7BF80980
        .Data2 = &HBF32
        .Data3 = &H101A
        .Data4(0) = &H8B
        .Data4(1) = &HBB
        .Data4(2) = &H0
        .Data4(3) = &HAA
        .Data4(4) = &H0
        .Data4(5) = &H30
        .Data4(6) = &HC
        .Data4(7) = &HAB
    End With

    ' Set the properties on the picture object
    With tagPic
        .Size = Len(tagPic)
        .Type = vbPicTypeBitmap
        .hBmp = hBmp
        .hPal = hPal
    End With

    ' Create a picture that will delete it's bitmap when it is finished with it
    Call OleCreatePictureIndirect(tagPic, IPictureIID, 1, IPic)

    ' Return the picture to the caller
    Set PictureFromBitmap = IPic
End Function

【讨论】:

  • 您好,感谢您的回复。这是我想要的方向,但我没有将图标加载到图片控件中,而是尝试将其加载到 Office.CommandBarButton.Picture 中,这是一个 IPictureDisp 对象。
  • 我只是建议你使用这个方法,到一个隐藏的Picturebox来加载你的图标,然后使用“Clipboard.Clear”、Clipboard.SetData(Picture1.Picture, vbCFBitmap)和CommandBarControl.PasteFace来把图标放到你的按钮上。
  • 嗨 Beaner,问题的限制之一是它也必须在 VBA 中工作。遗憾的是 VBA 无法访问 Picture 对象。这就是为什么我要专门将图标句柄强制转换为 stdole.IPictureDisp 的原因。
【解决方案3】:

在 Google 网上论坛中搜索标题为 Convert StdPicture from Icon to Bitmap 的主题。

更新

不,我也不能让它工作。

但是当我尝试它时,我得到了一种似曾相识的可怕感觉......然后记得我几年前确实这样做过,即在运行时向 Excel CommandBarButtons 添加带有掩码的图标,不知道它是哪个版本的 Excel正在打开。遗憾的是我找不到代码(不在源代码管理中,所以没有发布?我几乎可以肯定我得到了它的工作)。

我认为我从这些文章中大量借鉴了:

How To Create a Transparent Picture For Office CommandBar Buttons

How To Set the Mask and Picture Properties for Office XP CommandBars

而且由于 Excel 没有剪贴板,我似乎记得借用了 Stephen Bullen 的 PastePicture.zip

希望这不会让你白费心机:)

【讨论】:

  • 这看起来很有希望,但我无法将其转化为一个工作示例。虽然我确实想出了一种让 Excel 始终如一地崩溃到桌面的方法:) 你能提供一个工作示例吗?
  • 我绝对想避开剪贴板。劫持它是可怕的做法。我考虑的另一种方法是尝试将图标保存到临时文件夹,然后使用加载图片。但它仍然必须保存为位图才能正确加载。然而,由于这将是另一个研究问题,我想我会把它留到 B 计划中。
【解决方案4】:

LoadPicture 它返回一个支持 IPictureDisp 的对象。虽然它可能不是 vbPicTypeBitmap。不确定是否可以在 VBA 中调用 GdipCreateBitmapFromFile。

【讨论】:

  • 您好,LoadPicture 仅适用于以下文件类型:bmp、ico、cur、rle、wmf、emf、gif 和 jpg。当我从 Exes 中提取图标时,我需要有一种方法来从图标句柄中保存文件。如果你知道如何解决这个问题,我会认为这是一个解决方案。
  • 试试 GdipCreateBitmapFromHICON/GdipSaveImageToFile
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-28
  • 1970-01-01
  • 2019-01-27
  • 1970-01-01
  • 1970-01-01
  • 2020-01-17
  • 2019-02-01
相关资源
最近更新 更多