Jaafar Tribak

Well-known Member
Joined
Dec 5, 2002
Messages
9,806
Office Version
  1. 2016
Platform
  1. Windows
Hi,

For a static icon, sending the WM_SETICON to the UserForm window is the standard way and it is straightforward. However, for an animated icon, we would need to use multiple images\frames and periodically alternate them in a timer procedure or in a loop. This can become quite tedious and has a performance hit.

Here, I am using an animated gif alternative... Animated gif files are easy to obtain and share and are ready to use.

Download Example





1- Class code: (Class name; IconClass)
VBA Code:
Option Explicit

Private WithEvents UfEvents As MSForms.UserForm

Private Type POINTAPI
    x As Long
    y As Long
End Type

Private Type Size
    cx As Long
    cy As Long
End Type

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

Private Const LF_FACESIZE = 32
Private Type LOGFONT
    lfHeight As Long
    lfWidth As Long
    lfEscapement As Long
    lfOrientation As Long
    lfWeight As Long
    lfItalic As Byte
    lfUnderline As Byte
    lfStrikeOut As Byte
    lfCharSet As Byte
    lfOutPrecision As Byte
    lfClipPrecision As Byte
    lfQuality As Byte
    lfPitchAndFamily As Byte
    lfFaceName(1 To LF_FACESIZE) As Byte
End Type

Private Type NONCLIENTMETRICS
    cbSize As Long
    iBorderWidth As Long
    iScrollWidth As Long
    iScrollHeight As Long
    iCaptionWidth As Long
    iCaptionHeight As Long
    lfCaptionFont As LOGFONT
    iSMCaptionWidth As Long
    iSMCaptionHeight As Long
    lfSMCaptionFont As LOGFONT
    iMenuWidth As Long
    iMenuHeight As Long
    lfMenuFont As LOGFONT
    lfStatusFont As LOGFONT
    lfMessageFont As LOGFONT
    iPaddedBorderWidth As Long
End Type

#If VBA7 Then
    #If Win64 Then
        Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
    #Else
        Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As LongPtr
    #End If
    Private Declare PtrSafe Function AtlAxWinInit Lib "Atl.dll" () As Long
    Private Declare PtrSafe Function AtlAxGetControl Lib "atl" (ByVal hwnd As LongPtr, Unk As stdole.IUnknown) As Long
    Private Declare PtrSafe Function CreateWindowEx Lib "user32.dll" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hwndParent As LongPtr, ByVal hMenu As LongPtr, ByVal hInstance As LongPtr, lpParam As Any) As LongPtr
    Private Declare PtrSafe Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String, ByVal hData As LongPtr) As Long
    Private Declare PtrSafe Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String) As LongPtr
    Private Declare PtrSafe Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As LongPtr
    Private Declare PtrSafe Function GetWindowRect Lib "user32" (ByVal hwnd As LongPtr, lpRect As RECT) As Long
    Private Declare PtrSafe Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hwnd As LongPtr, ByVal wFlag As Long) As LongPtr
    Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hwnd As LongPtr, ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
    Private Declare PtrSafe Function SystemParametersInfo Lib "user32" Alias "SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam As Long, ByRef lpvParam As Any, ByVal fuWinIni As Long) As Long
    Private Declare PtrSafe Function GetTextExtentPoint32 Lib "gdi32" Alias "GetTextExtentPoint32A" (ByVal hDc As LongPtr, ByVal lpsz As String, ByVal cbString As Long, lpSize As Size) As Long
    Private Declare PtrSafe Function GetDC Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
    Private Declare PtrSafe Function ReleaseDC Lib "user32" (ByVal hwnd As LongPtr, ByVal hDc As LongPtr) As Long
    Private Declare PtrSafe Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As LongPtr) As Long
#Else
    Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
    Private Declare Function AtlAxWinInit Lib "Atl.dll" () As Long
    Private Declare Function AtlAxGetControl Lib "atl" (ByVal hwnd As Long, Unk As stdole.IUnknown) As Long
    Private Declare Function CreateWindowEx Lib "user32.dll" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hwndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, lpParam As Any) As Long
    Private Declare Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
    Private Declare Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As Long, ByVal lpString As String) As Long
    Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
    Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
    Private Declare Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hwnd As Long, ByVal wFlag As Long) As Long
    Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
    Private Declare Function SystemParametersInfo Lib "user32" Alias "SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam As Long, ByRef lpvParam As Any, ByVal fuWinIni As Long) As Long
    Private Declare Function GetTextExtentPoint32 Lib "gdi32" Alias "GetTextExtentPoint32A" (ByVal hDc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As Size) As Long
    Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hDc As Long) As Long
    Private Declare Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As Long) As Long
#End If


Public Function AddAnimatedIcon(ByVal Form As Object, ByVal IconURL As String) As Boolean
    Set UfEvents = Form
    AddAnimatedIcon = CreateIcon(Form, IconURL)
End Function

Private Sub UfEvents_Layout()
    Call SetIconPos(UfEvents)
End Sub


Private Function CreateIcon(ByVal Form As Object, ByVal url As String) As Boolean

    Const GWL_HWNDPARENT = (-8)
    Const WS_VISIBLE = &H10000000
    Const WS_POPUP = &H80000000
    Const WS_DISABLED = &H8000000
    Const WS_EX_TOOLWINDOW = &H80

    #If Win64 Then
        Dim hWebCtrl As LongLong, hParent As LongLong, hDc As LongLong
    #Else
        Dim hWebCtrl As Long, hParent As Long, hDc As Long
    #End If

    Dim oWbrowser  As Object, Unk As stdole.IUnknown
    Dim tTextSize As Size, sCaption  As String, lStyle As Long

    Call IUnknown_GetWindow(Form, VarPtr(hParent))
    hDc = GetDC(hParent)
    sCaption = Form.Caption
    Call GetTextExtentPoint32(hDc, sCaption, Len(sCaption), tTextSize)
    Call ReleaseDC(hParent, hDc)
    Form.Caption = Space(1 + tTextSize.cx / Len(sCaption)) & sCaption
    Call AtlAxWinInit
    lStyle = WS_POPUP Or WS_VISIBLE Or WS_DISABLED Or WS_EX_TOOLWINDOW
    hWebCtrl = CreateWindowEx(0, "AtlAxWin", "about:blank", lStyle, _
    0, 0, 0, 0, 0, 0, GetModuleHandle(vbNullString), ByVal 0)
    Call SetWindowLong(hWebCtrl, GWL_HWNDPARENT, hParent)
    If hWebCtrl Then
        Call SetProp(hParent, "hWebCtrl", hWebCtrl)
        Call AtlAxGetControl(hWebCtrl, Unk)
        Set oWbrowser = Unk
        With oWbrowser
            .Silent = True
            DoEvents
            .Document.Body.innerHTML = "<img style=""position:absolute;top:0px;left:0px;width:" & Fix(.Width) & _
            "px;height:" & Fix(.Height) & "px"" src=""" & url & "?" & ObjPtr(Form) & """/>"
            CreateIcon = CBool(Len(Dir(url))) And CBool(hWebCtrl)
        End With
    End If
   
End Function


Private Sub SetIconPos(ByVal Form As Object)

    Const SWP_SHOWWINDOW = &H40
    Const SWP_NOACTIVATE = &H10
    Const SPI_GETNONCLIENTMETRICS = 41
    Const GW_CHILD = 5

    #If Win64 Then
        Dim hParent As LongLong, hwnd As LongLong
    #Else
        Dim hParent As Long, hwnd As Long
    #End If

    Dim tParentRect As RECT, tChildRect As RECT
    Dim tNCM As NONCLIENTMETRICS

    Call IUnknown_GetWindow(Form, VarPtr(hParent))
    hwnd = GetProp(hParent, "hWebCtrl")
    Call GetWindowRect(GetNextWindow(hParent, GW_CHILD), tChildRect)
    Call GetWindowRect(hParent, tParentRect)
    tNCM.cbSize = Len(tNCM)
    If SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, tNCM, 0) Then
        With tParentRect
            Call SetWindowPos(hwnd, 0, tChildRect.Left + 5, tChildRect.Top - tNCM.iCaptionHeight - tNCM.iPaddedBorderWidth, _
            tNCM.iCaptionHeight, tNCM.iCaptionHeight, SWP_NOACTIVATE + SWP_SHOWWINDOW)
        End With
    End If

End Sub




2- This is how to use the above class for setting the gif pseudo-icon: on the UserForm.
UserForm Module:
VBA Code:
Option Explicit

Private IconClassInstance As IconClass

Private Sub UserForm_Initialize()
    Set IconClassInstance = New IconClass
    IconClassInstance.AddAnimatedIcon Me, ThisWorkbook.Path & "\1.gif"
End Sub

For better looking icons, try using transparent gifs.

Tested on excel 2016 64bit , Windows 10 64Bit
 

Excel Facts

Return population for a City
If you have a list of cities in A2:A100, use Data, Geography. Then =A2.Population and copy down.
Excel 64-bit / Windows 10 (64-bit).

Thank you for this, Jaafar - it's given me some food for thought!


1632690597797.png
 
Upvote 0
Ok- This my final attempt at getting the animated gif background to blend with the current windows theme color so the ugly white background doesn't show up and interfere.

This is a bit hacky, and as a result, the code may be less portable to Windows versions prior to Windows 10.

Download Example


Preview with colored title bars and window borders:




1- IconClass code:
VBA Code:
Option Explicit

Private WithEvents UfEvents As MSForms.UserForm
Private WithEvents CMBRSEvents  As CommandBars

Private Type POINTAPI
    x As Long
    y As Long
End Type

Private Type Size
    cx As Long
    cy As Long
End Type

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

Private Type RGB
    R As Long
    G As Long
    B As Long
End Type

Private Const LF_FACESIZE = 32
Private Type LOGFONT
    lfHeight As Long
    lfWidth As Long
    lfEscapement As Long
    lfOrientation As Long
    lfWeight As Long
    lfItalic As Byte
    lfUnderline As Byte
    lfStrikeOut As Byte
    lfCharSet As Byte
    lfOutPrecision As Byte
    lfClipPrecision As Byte
    lfQuality As Byte
    lfPitchAndFamily As Byte
    lfFaceName(1 To LF_FACESIZE) As Byte
End Type

Private Type NONCLIENTMETRICS
    cbSize As Long
    iBorderWidth As Long
    iScrollWidth As Long
    iScrollHeight As Long
    iCaptionWidth As Long
    iCaptionHeight As Long
    lfCaptionFont As LOGFONT
    iSMCaptionWidth As Long
    iSMCaptionHeight As Long
    lfSMCaptionFont As LOGFONT
    iMenuWidth As Long
    iMenuHeight As Long
    lfMenuFont As LOGFONT
    lfStatusFont As LOGFONT
    lfMessageFont As LOGFONT
    iPaddedBorderWidth As Long
End Type


#If VBA7 Then
    #If Win64 Then
        Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
    #Else
        Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As LongPtr
    #End If
    Private Declare PtrSafe Function AtlAxWinInit Lib "Atl.dll" () As Long
    Private Declare PtrSafe Function AtlAxGetControl Lib "atl" (ByVal hwnd As LongPtr, Unk As stdole.IUnknown) As Long
    Private Declare PtrSafe Function CreateWindowEx Lib "user32.dll" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hwndParent As LongPtr, ByVal hMenu As LongPtr, ByVal hInstance As LongPtr, lpParam As Any) As LongPtr
    Private Declare PtrSafe Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String, ByVal hData As LongPtr) As Long
    Private Declare PtrSafe Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String) As LongPtr
    Private Declare PtrSafe Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As LongPtr
    Private Declare PtrSafe Function GetWindowRect Lib "user32" (ByVal hwnd As LongPtr, lpRect As Rect) As Long
    Private Declare PtrSafe Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hwnd As LongPtr, ByVal wFlag As Long) As LongPtr
    Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hwnd As LongPtr, ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
    Private Declare PtrSafe Function ShowWindow Lib "user32" (ByVal hwnd As LongPtr, ByVal nCmdShow As Long) As Long
    Private Declare PtrSafe Function SystemParametersInfo Lib "user32" Alias "SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam As Long, ByRef lpvParam As Any, ByVal fuWinIni As Long) As Long
    Private Declare PtrSafe Function GetTextExtentPoint32 Lib "gdi32" Alias "GetTextExtentPoint32A" (ByVal hDC As LongPtr, ByVal lpsz As String, ByVal cbString As Long, lpSize As Size) As Long
    Private Declare PtrSafe Function GetDC Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
    Private Declare PtrSafe Function ReleaseDC Lib "user32" (ByVal hwnd As LongPtr, ByVal hDC As LongPtr) As Long
    Private Declare PtrSafe Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As LongPtr) As Long
    Private Declare PtrSafe Function ColorHLSToRGB Lib "shlwapi.dll" (ByVal wHue As Integer, ByVal wLuminance As Integer, ByVal wSaturation As Integer) As Long
    Private Declare PtrSafe Function ColorRGBToHLS Lib "shlwapi" (ByVal clrRGB As Long, ByRef wHue As Integer, ByRef wLuminance As Integer, ByRef wSaturation As Integer) As Long
    Private Declare PtrSafe Function GetPixel Lib "gdi32" (ByVal hDC As LongPtr, ByVal x As Long, ByVal y As Long) As Long
    Private Declare PtrSafe Function DestroyWindow Lib "user32" (ByVal hwnd As LongPtr) As Long
    Private Declare PtrSafe Function GetForegroundWindow Lib "user32" () As LongPtr
#Else
    Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
    Private Declare Function AtlAxWinInit Lib "Atl.dll" () As Long
    Private Declare Function AtlAxGetControl Lib "atl" (ByVal hwnd As Long, Unk As stdole.IUnknown) As Long
    Private Declare Function CreateWindowEx Lib "user32.dll" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hwndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, lpParam As Any) As Long
    Private Declare Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
    Private Declare Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As Long, ByVal lpString As String) As Long
    Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
    Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
    Private Declare Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hwnd As Long, ByVal wFlag As Long) As Long
    Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
    Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
    Private Declare Function SystemParametersInfo Lib "user32" Alias "SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam As Long, ByRef lpvParam As Any, ByVal fuWinIni As Long) As Long
    Private Declare Function GetTextExtentPoint32 Lib "gdi32" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As Size) As Long
    Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long
    Private Declare Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As Long) As Long
    Private Declare Function ColorHLSToRGB Lib "shlwapi.dll" (ByVal wHue As Integer, ByVal wLuminance As Integer, ByVal wSaturation As Integer) As Long
    Private Declare Function ColorRGBToHLS Lib "shlwapi" (ByVal clrRGB As Long, ByRef wHue As Integer, ByRef wLuminance As Integer, ByRef wSaturation As Integer) As Long
    Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) As Long
    Private Declare Function DestroyWindow Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function GetForegroundWindow Lib "user32" () As Long
#End If

Private oWebBrowser As Object
Private lShiftLeftPix As Long, lShiftTopPix As Long



Public Function AddAnimatedIcon( _
        ByVal Form As Object, _
        ByVal IconURL As String, _
        Optional ByVal ShiftLeftPix As Long, _
        Optional ByVal ShiftTopPix As Long _
    ) As Boolean

    Set UfEvents = Form
    Set CMBRSEvents = Application.CommandBars
    lShiftLeftPix = ShiftLeftPix: lShiftTopPix = ShiftTopPix
    AddAnimatedIcon = CreateIcon(Form, IconURL)
End Function

Public Sub RefreshIcon(Optional ByVal bActive As Boolean = True, Optional ByVal bRefresh As Boolean = True)

    #If Win64 Then
        Dim hwnd As LongLong
    #Else
        Dim hwnd As Long
    #End If

    Call IUnknown_GetWindow(UfEvents, VarPtr(hwnd))
    With oWebBrowser.Document.body
        If bActive Then
        If bRefresh Then _
            Call SetIconPos(UfEvents)
            .bgcolor = GetHLS(hwnd)
        Else
            .bgcolor = "#FFFFFF"
        End If
    End With

End Sub


Private Sub UfEvents_Layout()
    Call SetIconPos(UfEvents)
End Sub

Private Function CreateIcon(ByVal Form As Object, ByVal url As String) As Boolean

    Const GWL_HWNDPARENT = (-8)
    Const WS_VISIBLE = &H10000000
    Const WS_POPUP = &H80000000
    Const WS_DISABLED = &H8000000
    Const WS_EX_TOOLWINDOW = &H80

    #If Win64 Then
        Dim hWebCtrl As LongLong, hwnd As LongLong, hDC As LongLong
    #Else
        Dim hWebCtrl As Long, hwnd As Long, hDC As Long
    #End If
    Dim oWbrowser  As Object, Unk As stdole.IUnknown
    Dim tTextSize As Size, sCaption  As String, lStyle As Long

    Call IUnknown_GetWindow(Form, VarPtr(hwnd))
    hDC = GetDC(hwnd)
    sCaption = Form.Caption
    Call GetTextExtentPoint32(hDC, sCaption, Len(sCaption), tTextSize)
    Call ReleaseDC(hwnd, hDC)
    Form.Caption = Space(tTextSize.cx / Len(sCaption)) & sCaption
    Call AtlAxWinInit
    lStyle = WS_POPUP Or WS_VISIBLE Or WS_DISABLED Or WS_EX_TOOLWINDOW
    hWebCtrl = CreateWindowEx(0, "AtlAxWin", "about:blank", lStyle, _
    0, 0, 0, 0, 0, 0, GetModuleHandle(vbNullString), ByVal 0)
    Call SetWindowLong(hWebCtrl, GWL_HWNDPARENT, hwnd)
    If hWebCtrl Then
        Call SetProp(hwnd, "hWebCtrl", hWebCtrl)
        Call AtlAxGetControl(hWebCtrl, Unk)
        Set oWbrowser = Unk
        With oWbrowser
            Set oWebBrowser = oWbrowser
            .Silent = True
            .FullScreen = True
            DoEvents
            .Document.body.innerHTML = "<img style=""position:absolute;top:0px;left:0px;width:" & Fix(.Width) & _
            "px;height:" & Fix(.Height) & "px"" src=""" & url & "?" & ObjPtr(Form) & """/>"
             ShowWindow hWebCtrl, 0
            CreateIcon = CBool(Len(Dir(url))) And CBool(hWebCtrl)
        End With
    End If
 
End Function


Private Sub SetIconPos(ByVal Form As Object)

    Const SWP_SHOWWINDOW = &H40
    Const SWP_NOACTIVATE = &H10
    Const SPI_GETNONCLIENTMETRICS = 41
    Const GW_CHILD = 5

    #If Win64 Then
        Dim hParent As LongLong, hwnd As LongLong
    #Else
        Dim hParent As Long, hwnd As Long
    #End If
    Dim tParentRect As Rect, tChildRect As Rect
    Dim tNCM As NONCLIENTMETRICS
    Dim x As Long, y As Long
               
    Call IUnknown_GetWindow(Form, VarPtr(hParent))
    hwnd = GetProp(hParent, "hWebCtrl")
    Call GetWindowRect(GetNextWindow(hParent, GW_CHILD), tChildRect)
    Call GetWindowRect(hParent, tParentRect)
    tNCM.cbSize = Len(tNCM)
    If SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, tNCM, 0) Then
        If lShiftLeftPix < 0 Then lShiftLeftPix = 0
        If lShiftLeftPix >= 5 Then lShiftLeftPix = 5
        If lShiftTopPix < -5 Then lShiftTopPix = -5
        If lShiftTopPix >= 5 Then lShiftTopPix = 5
        With tChildRect
            x = .Left + lShiftLeftPix + 2
            y = .Top - tNCM.iCaptionHeight - tNCM.iPaddedBorderWidth + lShiftTopPix
        End With
        Call SetWindowPos(hwnd, 0, x, y, tNCM.iCaptionHeight - 2, tNCM.iCaptionHeight - 2, SWP_NOACTIVATE + SWP_SHOWWINDOW)
    End If
End Sub


#If Win64 Then
    Private Function GetHLS(ByVal hwnd As LongLong) As String
        Dim hDC As LongLong
#Else
    Private Function GetHLS(ByVal hwnd As Long) As String
        Dim hDC As Long
#End If
    Dim sHLS As String, tARGB As RGB, tWinRect As Rect
    Dim R As Byte, G As Byte, B As Byte
    Dim iHu As Integer, iLu As Integer, iSa As Integer
    Dim lTempCol As Long
 
    hDC = GetDC(0)
    Call GetWindowRect(hwnd, tWinRect)
    lTempCol = GetPixel(hDC, tWinRect.Left + 50, tWinRect.Top + 5)
    Call ReleaseDC(0, hDC)
    Call ColorRGBToHLS(lTempCol, iHu, iLu, iSa)
    tARGB = ColorToRGB(ColorHLSToRGB(iHu, iLu, iSa))
    sHLS = "#" & Right("0" & Hex(tARGB.R), 2) & Right("0" & Hex(tARGB.G), 2) & Right("0" & Hex(tARGB.B), 2)
    GetHLS = sHLS

End Function

Private Function ColorToRGB(ByVal Col As Long) As RGB
    ColorToRGB.R = &HFF& And Col
    ColorToRGB.G = (&HFF00& And Col) \ 256
    ColorToRGB.B = (&HFF0000 And Col) \ 65536
End Function

Private Sub CMBRSEvents_OnUpdate()
    With Application.CommandBars
        .FindControl(Id:=2040).Enabled = Not .FindControl(Id:=2040).Enabled
    End With
    RefreshIcon GetForegroundWindow <> Application.hwnd, False
End Sub



2- UserForm code
( Added two Optional arguments ShiftLeftPix and ShiftTopPix for adjusting the Left and Top positions in case the animated icon is a few pixels off )

VBA Code:
Option Explicit

Private IconClassInstance As IconClass

Private Sub UserForm_Initialize()
    Set IconClassInstance = New IconClass
    IconClassInstance.AddAnimatedIcon Me, ThisWorkbook.Path & "\1.gif"
End Sub

Private Sub UserForm_Activate()
    IconClassInstance.RefreshIcon True
End Sub

Private Sub UserForm_Deactivate()
    IconClassInstance.RefreshIcon False
End Sub

Private Sub Btn_ShowNextForm_Click()
    Dim oUF As Object
    Set oUF = New UserForm2
    oUF.Show vbModeless
End Sub
 
Last edited:
Upvote 0
This my final attempt at getting the animated gif background to blend with the current windows theme color so the ugly white background doesn't show up and interfere.
I had wondered whether it would be possible to use the SetLayeredWindowAttributes API to remove the background colour (like you might do with the WebBrowser control, for example), but I couldn't get it to work. Thank you for your updated solution - it works perfectly minus the side effects experienced by @GWteB (!!?!) ?
 
Upvote 0
I had wondered whether it would be possible to use the SetLayeredWindowAttributes API to remove the background colour (like you might do with the WebBrowser control, for example), but I couldn't get it to work. Thank you for your updated solution - it works perfectly minus the side effects experienced by @GWteB (!!?!) ?
Alphablending was the first thing that I thought of and tested but it doesn't work with animated gifs ... I also tried extracting each frame of the animated gif as a bitmap, alphablending the bitmap and then merging back the blended bitmaps together but that would be slow plus I couldn't make the idea work.

I am exploring a different approach that uses a transparent static control as the animated gif file host but not sure if that will work.
 
Upvote 0
Alphablending was the first thing that I thought of and tested but it doesn't work with animated gifs
Meaning that it doesn't work specifically with the AtlAxWin window class?

Which is disappointing - when I saw that you had uploaded this workbook on the forum, I was excited to look through the code to see how you had done it. I have a personal project involving an animated GIF that I had wanted to have float around the screen, but the only way I could manage to do it thus far was with either an HTA file, a WebBrowser control on a userform (as below) or an instance of Internet Explorer (each using alphablending). I had high hopes that the AtlAxWin window class might be a good option.

1632764490618.png
1632764598508.png
 
Upvote 0
Good news (y)

In fact, I just realized that as long as the animated gif file is transparent (which is what we have been using all along- so far so good) and as long as the form window is made the owner (GWL_HWNDPARENT) of the AtlAxWin browser then WS_EX_LAYERED\SetLayeredWindowAttributes should work perfectly well regardless of the Widows title bars and borders theme color that the user currently has.

And since the created AtlAxWin window is a popup (WS_POPUP) window and not a child window (WS_CHILD), this means it can be successfully layered and therefore, it should also work with no issue in Windows versions prior to Window 10.

So, the following update now works extremely well and the animated gif white background no longer shows up - No timer needed, no hacky commandbars OnUpdate event slowing down the code and less code overall.

Download Example






1- IconClass Code:
VBA Code:
Option Explicit

Private WithEvents UfEvents As MSForms.UserForm

Private Type POINTAPI
    x As Long
    y As Long
End Type

Private Type Size
    cx As Long
    cy As Long
End Type

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

Private Type RGB
    R As Long
    G As Long
    B As Long
End Type

Private Const LF_FACESIZE = 32
Private Type LOGFONT
    lfHeight As Long
    lfWidth As Long
    lfEscapement As Long
    lfOrientation As Long
    lfWeight As Long
    lfItalic As Byte
    lfUnderline As Byte
    lfStrikeOut As Byte
    lfCharSet As Byte
    lfOutPrecision As Byte
    lfClipPrecision As Byte
    lfQuality As Byte
    lfPitchAndFamily As Byte
    lfFaceName(1 To LF_FACESIZE) As Byte
End Type

Private Type NONCLIENTMETRICS
    cbSize As Long
    iBorderWidth As Long
    iScrollWidth As Long
    iScrollHeight As Long
    iCaptionWidth As Long
    iCaptionHeight As Long
    lfCaptionFont As LOGFONT
    iSMCaptionWidth As Long
    iSMCaptionHeight As Long
    lfSMCaptionFont As LOGFONT
    iMenuWidth As Long
    iMenuHeight As Long
    lfMenuFont As LOGFONT
    lfStatusFont As LOGFONT
    lfMessageFont As LOGFONT
    iPaddedBorderWidth As Long
End Type


#If VBA7 Then
    #If Win64 Then
        Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
    #Else
        Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As LongPtr
    #End If
    Private Declare PtrSafe Function AtlAxWinInit Lib "Atl.dll" () As Long
    Private Declare PtrSafe Function AtlAxGetControl Lib "atl" (ByVal hwnd As LongPtr, Unk As stdole.IUnknown) As Long
    Private Declare PtrSafe Function CreateWindowEx Lib "user32.dll" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hwndParent As LongPtr, ByVal hMenu As LongPtr, ByVal hInstance As LongPtr, lpParam As Any) As LongPtr
    Private Declare PtrSafe Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String, ByVal hData As LongPtr) As Long
    Private Declare PtrSafe Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String) As LongPtr
    Private Declare PtrSafe Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As LongPtr
    Private Declare PtrSafe Function GetWindowRect Lib "user32" (ByVal hwnd As LongPtr, lpRect As Rect) As Long
    Private Declare PtrSafe Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hwnd As LongPtr, ByVal wFlag As Long) As LongPtr
    Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hwnd As LongPtr, ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
    Private Declare PtrSafe Function SystemParametersInfo Lib "user32" Alias "SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam As Long, ByRef lpvParam As Any, ByVal fuWinIni As Long) As Long
    Private Declare PtrSafe Function GetTextExtentPoint32 Lib "gdi32" Alias "GetTextExtentPoint32A" (ByVal hdc As LongPtr, ByVal lpsz As String, ByVal cbString As Long, lpSize As Size) As Long
    Private Declare PtrSafe Function GetDC Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
    Private Declare PtrSafe Function ReleaseDC Lib "user32" (ByVal hwnd As LongPtr, ByVal hdc As LongPtr) As Long
    Private Declare PtrSafe Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As LongPtr) As Long
    Private Declare PtrSafe Function DestroyWindow Lib "user32" (ByVal hwnd As LongPtr) As Long
    Private Declare PtrSafe Function SetLayeredWindowAttributes Lib "user32" (ByVal hwnd As LongPtr, ByVal crey As Long, ByVal bAlpha As Byte, ByVal dwFlags As Long) As LongPtr
#Else
    Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
    Private Declare Function AtlAxWinInit Lib "Atl.dll" () As Long
    Private Declare Function AtlAxGetControl Lib "atl" (ByVal hwnd As Long, Unk As stdole.IUnknown) As Long
    Private Declare Function CreateWindowEx Lib "user32.dll" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hwndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, lpParam As Any) As Long
    Private Declare Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
    Private Declare Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As Long, ByVal lpString As String) As Long
    Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
    Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
    Private Declare Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hwnd As Long, ByVal wFlag As Long) As Long
    Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
    Private Declare Function SystemParametersInfo Lib "user32" Alias "SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam As Long, ByRef lpvParam As Any, ByVal fuWinIni As Long) As Long
    Private Declare Function GetTextExtentPoint32 Lib "gdi32" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As Size) As Long
    Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long
    Private Declare Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As Long) As Long
    Private Declare Function DestroyWindow Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function SetLayeredWindowAttributes Lib "user32" (ByVal hwnd As Long, ByVal crey As Long, ByVal bAlpha As Byte, ByVal dwFlags As Long) As Long
#End If

Private lShiftLeftPix As Long, lShiftTopPix As Long



Public Function AddAnimatedIcon( _
        ByVal Form As Object, _
        ByVal IconURL As String, _
        Optional ByVal ShiftLeftPix As Long, _
        Optional ByVal ShiftTopPix As Long _
    ) As Boolean

    Set UfEvents = Form
    lShiftLeftPix = ShiftLeftPix: lShiftTopPix = ShiftTopPix
    AddAnimatedIcon = CreateIcon(Form, IconURL)
End Function

Private Sub UfEvents_Layout()
    Call SetIconPos(UfEvents)
End Sub

Private Function CreateIcon(ByVal Form As Object, ByVal url As String) As Boolean

    Const GWL_HWNDPARENT = (-8)
    Const WS_EX_LAYERED = &H80000
    Const WS_VISIBLE = &H10000000
    Const WS_POPUP = &H80000000
    Const WS_DISABLED = &H8000000
    Const WS_EX_TOOLWINDOW = &H80
    Const LWA_COLORKEY = &H1
   
    #If Win64 Then
        Dim hWebCtrl As LongLong, hwnd As LongLong, hdc As LongLong
    #Else
        Dim hWebCtrl As Long, hwnd As Long, hdc As Long
    #End If
    Dim oWbrowser  As Object, Unk As stdole.IUnknown
    Dim tTextSize As Size, sCaption  As String, lStyle As Long

    Call IUnknown_GetWindow(Form, VarPtr(hwnd))
    hdc = GetDC(hwnd)
    sCaption = Form.Caption
    Call GetTextExtentPoint32(hdc, sCaption, Len(sCaption), tTextSize)
    Call ReleaseDC(hwnd, hdc)
    Form.Caption = Space(tTextSize.cx / Len(sCaption)) & sCaption
   
    Call AtlAxWinInit
    lStyle = WS_VISIBLE Or WS_POPUP Or WS_DISABLED Or WS_EX_TOOLWINDOW
    hWebCtrl = CreateWindowEx(WS_EX_LAYERED, "AtlAxWin", "about:blank", lStyle, _
    0, 0, 0, 0, 0, 0, GetModuleHandle(vbNullString), ByVal 0)
    Call SetLayeredWindowAttributes(hWebCtrl, vbWhite, 0, LWA_COLORKEY)
    Call SetWindowLong(hWebCtrl, GWL_HWNDPARENT, hwnd)
    If hWebCtrl Then
        Call SetProp(hwnd, "hWebCtrl", hWebCtrl)
        Call AtlAxGetControl(hWebCtrl, Unk)
        Set oWbrowser = Unk
        With oWbrowser
            .Silent = True
            DoEvents
            .Document.body.innerHTML = "<img style=""position:absolute;top:0px;left:0px;width:" & Fix(.Width) & _
            "px;height:" & Fix(.Height) & "px"" src=""" & url & "?" & ObjPtr(Form) & """/>"
            CreateIcon = CBool(Len(Dir(url))) And CBool(hWebCtrl)
        End With
    End If
   
End Function


Private Sub SetIconPos(ByVal Form As Object)

    Const SWP_SHOWWINDOW = &H40
    Const SWP_NOACTIVATE = &H10
    Const SPI_GETNONCLIENTMETRICS = 41
    Const GW_CHILD = 5

    #If Win64 Then
        Dim hParent As LongLong, hwnd As LongLong
    #Else
        Dim hParent As Long, hwnd As Long
    #End If
    Dim tParentRect As Rect, tChildRect As Rect
    Dim tNCM As NONCLIENTMETRICS
    Dim x As Long, y As Long
                 
    Call IUnknown_GetWindow(Form, VarPtr(hParent))
    hwnd = GetProp(hParent, "hWebCtrl")
    Call GetWindowRect(GetNextWindow(hParent, GW_CHILD), tChildRect)
    Call GetWindowRect(hParent, tParentRect)
    tNCM.cbSize = Len(tNCM)
    If SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, tNCM, 0) Then
        If lShiftLeftPix < 0 Then lShiftLeftPix = 0
        If lShiftLeftPix >= 5 Then lShiftLeftPix = 5
        If lShiftTopPix < -5 Then lShiftTopPix = -5
        If lShiftTopPix >= 5 Then lShiftTopPix = 5
        With tChildRect
            x = .Left + lShiftLeftPix + 2
            y = .Top - tNCM.iCaptionHeight - tNCM.iPaddedBorderWidth + lShiftTopPix
        End With
        Call SetWindowPos(hwnd, 0, x, y, tNCM.iCaptionHeight - 2, tNCM.iCaptionHeight - 2, SWP_NOACTIVATE + SWP_SHOWWINDOW)
    End If
   
End Sub


2- UserForm code:
VBA Code:
Option Explicit

Private IconClassInstance As IconClass

Private Sub UserForm_Initialize()
    Set IconClassInstance = New IconClass
    IconClassInstance.AddAnimatedIcon Me, ThisWorkbook.Path & "\1.gif"
End Sub

Private Sub Btn_ShowNextForm_Click()
    Dim oUF As Object
    Set oUF = New UserForm2
    oUF.Show vbModeless
End Sub
 
Upvote 0
Wow - I just checked it - it really works perfectly.
In fact, I just realized that as long as the animated gif file is transparent (which is what we have been using all along- so far so good) and as long as the form window is made the owner (GWL_HWNDPARENT) of the AtlAxWin browser then WS_EX_LAYERED\SetLayeredWindowAttributes should work perfectly well regardless of the Widows title bars and borders theme color that the user currently has.

And since the created AtlAxWin window is a popup (WS_POPUP) window and not a child window (WS_CHILD), this means it can be successfully layered and therefore, it should work with no issue in Windows versions prior to Window 10.
And thank you very much for the detailed explanation - this will save me a long time trying to guess/reverse engineer how you did it!

1632768169355.png
 
Upvote 0

Forum statistics

Threads
1,224,818
Messages
6,181,152
Members
453,021
Latest member
Justyna P

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top