Option Explicit
Private Enum ASSOC_FILTER
ASSOC_FILTER_NONE = &H0
ASSOC_FILTER_RECOMMENDED = &H1
End Enum
#If VBA7 Then
Private Declare PtrSafe Function SHAssocEnumHandlers Lib "shell32" (ByVal pszExtra As LongPtr, ByVal afFilter As ASSOC_FILTER, ByVal ppEnumHandler As LongPtr) As Long
Private Declare PtrSafe Function DispCallFunc Lib "oleAut32.dll" (ByVal pvInstance As LongPtr, ByVal offsetinVft As Long, ByVal CallConv As Long, ByVal retTYP As Integer, ByVal paCNT As Long, ByRef paTypes As Integer, ByRef paValues As LongPtr, ByRef retVAR As Variant) As Long
Private Declare PtrSafe Sub SetLastError Lib "kernel32.dll" (ByVal dwErrCode As Long)
Private Declare PtrSafe Function SysReAllocString Lib "oleAut32.dll" (ByVal pBSTR As LongPtr, Optional ByVal pszStrPtr As LongPtr) As Long
#Else
Private Declare Function SHAssocEnumHandlers Lib "shell32" (ByVal pszExtra As Long, ByVal afFilter As ASSOC_FILTER, ByVal ppEnumHandler As Long) As Long
Private Declare Function DispCallFunc Lib "oleAut32.dll" (ByVal pvInstance As Long, ByVal offsetinVft As Long, ByVal CallConv As Long, ByVal retTYP As Integer, ByVal paCNT As Long, ByRef paTypes As Integer, ByRef paValues As Long, ByRef retVAR As Variant) As Long
Private Declare Sub SetLastError Lib "kernel32.dll" (ByVal dwErrCode As Long)
Private Declare Function SysReAllocString Lib "oleAut32.dll" (ByVal pBSTR As Long, Optional ByVal pszStrPtr As Long) As Long
#End If
Public Function Is_PDF_Reader_Installed() As Boolean
Is_PDF_Reader_Installed = Len(FindExecutable_With_AssocHandlerINTERFACE("pdf"))
End Function
Private Function FindExecutable_With_AssocHandlerINTERFACE(ByVal DocumetFileExtension As String) As String
#If Win64 Then
Const vTblOffsetFac_32_64 = 2&
Dim pEnumHandlers As LongLong
Dim pAssocHandler As LongLong
Dim pceltFetched As LongLong
Dim pExecutablePathName As LongLong
#Else
Const vTblOffsetFac_32_64 = 1&
Dim pEnumHandlers As Long
Dim pAssocHandler As Long
Dim pceltFetched As Long
Dim pExecutablePathName As Long
#End If
Const IEnumAssocHandlers_Next = 12 * vTblOffsetFac_32_64
Const IAssocHandler_GetName = 12 * vTblOffsetFac_32_64
Const CC_STDCALL = 4&
Const S_OK = 0&
Dim Unk As IUnknown
If Len(DocumetFileExtension) Then
DocumetFileExtension = IIf(Left(DocumetFileExtension, 1) = ".", DocumetFileExtension, "." & DocumetFileExtension)
If SHAssocEnumHandlers(StrPtr(DocumetFileExtension), ASSOC_FILTER_RECOMMENDED, VarPtr(Unk)) = S_OK Then
pEnumHandlers = ObjPtr(Unk)
If vtblCall(pEnumHandlers, IEnumAssocHandlers_Next, vbLong, CC_STDCALL, 1&, VarPtr(pAssocHandler), VarPtr(pceltFetched)) = S_OK Then
If vtblCall(pAssocHandler, IAssocHandler_GetName, vbLong, CC_STDCALL, VarPtr(pExecutablePathName)) = S_OK Then
FindExecutable_With_AssocHandlerINTERFACE = GetStrFromPtrW(pExecutablePathName)
End If
End If
End If
End If
End Function
#If Win64 Then
Private Function vtblCall(ByVal InterfacePointer As LongLong, ByVal VTableOffset As Long, ByVal FunctionReturnType As Long, ByVal CallConvention As Long, ParamArray FunctionParameters() As Variant) As Variant
Dim vParamPtr() As LongLong
#Else
Private Function vtblCall(ByVal InterfacePointer As Long, ByVal VTableOffset As Long, ByVal FunctionReturnType As Long, ByVal CallConvention As Long, ParamArray FunctionParameters() As Variant) As Variant
Dim vParamPtr() As Long
#End If
If InterfacePointer = 0& Or VTableOffset < 0& Then Exit Function
If Not (FunctionReturnType And &HFFFF0000) = 0& Then Exit Function
Dim pIndex As Long, pCount As Long
Dim vParamType() As Integer
Dim vRtn As Variant, vParams() As Variant
vParams() = FunctionParameters()
pCount = Abs(UBound(vParams) - LBound(vParams) + 1&)
If pCount = 0& Then
ReDim vParamPtr(0 To 0)
ReDim vParamType(0 To 0)
Else
ReDim vParamPtr(0 To pCount - 1&)
ReDim vParamType(0 To pCount - 1&)
For pIndex = 0& To pCount - 1&
vParamPtr(pIndex) = VarPtr(vParams(pIndex))
vParamType(pIndex) = VarType(vParams(pIndex))
Next
End If
pIndex = DispCallFunc(InterfacePointer, VTableOffset, CallConvention, FunctionReturnType, pCount, vParamType(0), vParamPtr(0), vRtn)
If pIndex = 0& Then
vtblCall = vRtn
Else
SetLastError pIndex
End If
End Function
#If Win64 Then
Private Function GetStrFromPtrW(ByVal Ptr As LongLong) As String
#Else
Private Function GetStrFromPtrW(ByVal Ptr As Long) As String
#End If
SysReAllocString VarPtr(GetStrFromPtrW), Ptr
End Function