Option Explicit
#If VBA7 Then
Private Declare PtrSafe Function DispCallFunc Lib "oleAut32.dll" (ByVal pvInstance As LongPtr, ByVal offsetinVft As LongPtr, 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 CLSIDFromString Lib "ole32" (ByVal OleStringCLSID As LongPtr, ByRef cGUID As Any) As Long
Private Declare PtrSafe Function SHGetPropertyStoreFromParsingName Lib "shell32" (ByVal pszPath As LongPtr, pbc As Any, ByVal Flags As Long, riid As GUID, ppv As Any) As Long
#Else
Private Enum LongPtr
[_]
End Enum
Private Declare Function DispCallFunc Lib "oleAut32.dll" (ByVal pvInstance As LongPtr, ByVal offsetinVft As LongPtr, 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 Sub SetLastError Lib "kernel32.dll" (ByVal dwErrCode As Long)
Private Declare Function CLSIDFromString Lib "ole32" (ByVal OleStringCLSID As LongPtr, ByRef cGUID As Any) As Long
Private Declare Function SHGetPropertyStoreFromParsingName Lib "shell32" (ByVal pszPath As LongPtr, pbc As Any, ByVal Flags As Long, riid As GUID, ppv As Any) As Long
#End If
#If Win64 Then
Private Const NULL_PTR = 0^
Private Const PTR_LEN = 8&
#Else
Private Const NULL_PTR = 0&
Private Const PTR_LEN = 4&
#End If
Private Enum Vtble_Ordinals
'IUnknown
QueryInterface = 0&
Release = 2&
'IPropertyStore
SetValue = 6&
Commit = 7&
End Enum
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type
Private Type PROPERTYKEY
fmtid As GUID
pid As Long
End Type
Public Function Set_MP3_TrackNumber_Value(ByVal sMP3File As String, ByVal NewValue As Variant) As Boolean
Const IID_PropertyStore = "{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}"
Const GPS_READWRITE = 2&, GPS_OPENSLOWITEM = &H10
Const CC_STDCALL = 4&, S_OK = 0&
Dim tIID As GUID, pPS As LongPtr
If CLSIDFromString(StrPtr(IID_PropertyStore), tIID) <> S_OK Then
Debug.Print "unable to get the IID_PropertyStore interface."
GoTo ReleaseInterface
End If
If SHGetPropertyStoreFromParsingName _
(StrPtr(sMP3File), ByVal NULL_PTR, GPS_READWRITE Or GPS_OPENSLOWITEM, tIID, pPS) <> S_OK Then
Debug.Print "unable to get the property store from the file."
GoTo ReleaseInterface
End If
' IPropertyStore::SetValue
If vtblCall(pPS, SetValue * PTR_LEN, vbLong, CC_STDCALL, _
VarPtr(PKEY_Music_TrackNumber), VarPtr(CVar(NewValue))) <> S_OK Then
Debug.Print "unable to set the property value."
GoTo ReleaseInterface
End If
' IPropertyStore::Commit
If vtblCall(pPS, Commit * PTR_LEN, vbLong, CC_STDCALL) <> S_OK Then
Debug.Print "unable to commit the property value."
GoTo ReleaseInterface
End If
Set_MP3_TrackNumber_Value = True
ReleaseInterface:
If pPS Then
' IPropertyStore::Release
Call vtblCall(pPS, Release * PTR_LEN, vbLong, CC_STDCALL)
End If
End Function
Private Function PKEY_Music_TrackNumber() As PROPERTYKEY
Dim tPk As PROPERTYKEY
With tPk.fmtid
.Data1 = &H56A3372E
.Data2 = CInt(&HCE9C)
.Data3 = CInt(&H11D2)
.Data4(0) = &H9F
.Data4(1) = &HE
.Data4(2) = &H0
.Data4(3) = &H60
.Data4(4) = &H97
.Data4(5) = &HC6
.Data4(6) = &H86
.Data4(7) = &HF6
End With
tPk.pid = 7&
PKEY_Music_TrackNumber = tPk
End Function
Private Function vtblCall( _
ByVal InterfacePointer As LongPtr, _
ByVal VTableOffset As Long, _
ByVal FunctionReturnType As Long, _
ByVal CallConvention As Long, _
ParamArray FunctionParameters() As Variant _
) As Variant
Dim vParamPtr() As LongPtr
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