Results 1 to 25 of 25

Thread: [RESOLVED] Trying to implement IShellItem - where'd I go wrong?

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Resolved [RESOLVED] Trying to implement IShellItem - where'd I go wrong?

    Just on the off chance anyone around here is familiar with typelibs and interfaces...
    Code:
    [
    	odl,
    	uuid(43826D1E-E718-42EE-BC55-A1E261C37BFE)
    ]
    interface IShellItem : stdole.IUnknown {
    	HRESULT BindToHandler(
    		[in] IBindCtx *pbc,
    		[in] UUID rbhid,
    		[in] UUID riid,
    		[out] void *ppvOut);
    
    	HRESULT Compare(
    		[in] IShellItem *psi,
    		[in] LONG hint,
    		[out, retval] LONG *piOrder);
    
    	HRESULT GetAttributes(
    		[in] SFGAO_Flags sfgaoMask,
    		[out, retval] SFGAO_Flags *psfgaoAttribs);
    
    	HRESULT GetDisplayName(
    		[in] SIGDN sigdnName,
    		[out, retval] LPWSTR *ppszName);
    
    	HRESULT GetParent(
    		[out] IShellItem *ppsi);
    
    };
    
    Public Declare Function SHCreateShellItem Lib "shell32" (ByVal pidlParent As Long, psfParent As Any, pidl As Long, ppsi As IShellItem)
    Dim hRes As Long
    Dim isi As IShellItem
    hRes = SHCreateShellItem(0, ByVal 0&, ByVal GetPIDLFromPath(Me.hwnd, "C:\temp"), isi)
    
    'lpw = isi.GetDisplayName(0)
    n = isi.GetAttributes(SFGAO_CANRENAME Or SFGAO_CANDELETE)
    
    MsgBox hRes & "|||" & CStr(n)
    All the types are correctly defined (long, uuid, IBindCtx, IShellFolder)... because they're all parts of working implementations from the same typelib of IShellFolder et al. that I use in my project. I know maybe psfParent should be explicitly declared as IShellFolder, but MSDN says I can use either that or pass a fully-qualified pidl, which is what I was doing. The typelib compiles without error.

    SHCreateShellItem is returning S_OK but fails to create the object (isi Is Nothing = True). If I pass a valid IShellFolder, VB crashes.

    My eventual goal is getting to IThumbnailProvider, which I can't bind to from IShellFolder2.

    For reference, this is the source from shodjidl.idl I translated from:
    Code:
    cpp_quote("      HRESULT (WINAPI *BindToHandler)(IShellItem *This,IBindCtx *pbc,REFGUID rbhid,REFIID riid,void **ppvOut);")
    cpp_quote("      HRESULT (WINAPI *GetParent)(IShellItem *This,IShellItem **ppsi);")
    cpp_quote("      HRESULT (WINAPI *GetDisplayName)(IShellItem *This,SIGDN sigdnName,LPOLESTR *ppszName);")
    cpp_quote("      HRESULT (WINAPI *GetAttributes)(IShellItem *This,SFGAOF sfgaoMask,SFGAOF *psfgaoAttribs);")
    cpp_quote("      HRESULT (WINAPI *Compare)(IShellItem *This,IShellItem *psi,SICHINTF hint,int *piOrder);")

  2. #2
    PowerPoster Zvoni's Avatar
    Join Date
    Sep 2012
    Location
    To the moon and then left
    Posts
    5,261

    Re: Trying to implement IShellItem - where'd I go wrong?

    Your
    Code:
    ByVal GetPIDLFromPath(Me.hwnd, "C:\temp")
    looks suspicious to me. Could you elaborate on this function?
    What i could find is, that this function is used as a wrapper for "SHSimpleIDListFromPath"-API, and that one only has one argument
    Last edited by Zvoni; Tomorrow at 31:69 PM.
    ----------------------------------------------------------------------------------------

    One System to rule them all, One Code to find them,
    One IDE to bring them all, and to the Framework bind them,
    in the Land of Redmond, where the Windows lie
    ---------------------------------------------------------------------------------
    People call me crazy because i'm jumping out of perfectly fine airplanes.
    ---------------------------------------------------------------------------------
    Code is like a joke: If you have to explain it, it's bad

  3. #3

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    Quote Originally Posted by Zvoni View Post
    Your
    Code:
    ByVal GetPIDLFromPath(Me.hwnd, "C:\temp")
    looks suspicious to me. Could you elaborate on this function?
    What i could find is, that this function is used as a wrapper for "SHSimpleIDListFromPath"-API, and that one only has one argument
    It returns a fully qualified pidl from a path. It works fine, I use it hundreds of times throughout the project.

  4. #4
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: Trying to implement IShellItem - where'd I go wrong?

    I see a few things, which are wrong.

    - the VTable-Order is incorrect (please stick with the order of the C++-original)
    - the Declare returns a Variant (not the usual 32Bit-Long for the HResult).
    - only potentially a problem: make sure, you pass an absolute PIDL in case the two leading (Parent-) Parameters remain at zero.
    (not sure what your GetPIDLPath - function wraps under the hood)

    To keep the VTable-order of the method-signatures correct, you cannot rely on the MSDN - they sort alphabetically, which is almost always the wrong order.
    I usually take a good look into e.g. the Wine-Implementations (or -Documentation), where the real VTable-Order can be seen:
    http://fossies.org/dox/wine-1.4.1/in...ShellItem.html


    usage of the cShellItem-Class I've put together (class-code comes further below):

    vb Code:
    1. Option Explicit
    2.  
    3. Private Sub Form_Load()
    4. Dim SI1 As New cShellItem, SI2 As New cShellItem
    5.   SI1.InitFromPath "D:\Test"
    6.   SI2.InitFromPath "D:\Test"
    7.  
    8.   Debug.Print SI1.GetDisplayName() 'Test
    9.   Debug.Print SI1.GetParent.GetDisplayName() 'Data (D:)
    10.   Debug.Print SI1.GetParent.GetParent.GetDisplayName() 'Computer
    11.   Debug.Print SI1.GetParent.GetParent.GetParent.GetDisplayName() 'Desktop
    12.  
    13.   Debug.Print SI1.GetAttributes(SFGAO_CANCOPY) = SFGAO_CANCOPY
    14.   Debug.Print SI1.Compare(SI2, SICHINT_ALLFIELDS) = 0 '0 means the Items are equal
    15. End Sub

    as said, now the demo-code for a small cShellItem-Class, which doesn't use a TypeLib, but is directly VTable-bound
    vb Code:
    1. 'Into a Class, named: cShellItem
    2. Public Enum enmSIGDN
    3.   SIGDN_NORMALDISPLAY = &H0
    4.   SIGDN_PARENTRELATIVEPARSING = &H80018001
    5.   SIGDN_DESKTOPABSOLUTEPARSING = &H80028000
    6.   SIGDN_PARENTRELATIVEEDITING = &H80031001
    7.   SIGDN_DESKTOPABSOLUTEEDITING = &H8004C000
    8.   SIGDN_FILESYSPATH = &H80058000
    9.   SIGDN_URL = &H80068000
    10.   SIGDN_PARENTRELATIVEFORADDRESSBAR = &H8007C001
    11.   SIGDN_PARENTRELATIVE = &H80080001
    12.   SIGDN_PARENTRELATIVEFORUI = &H80094001
    13. End Enum
    14.  
    15. Public Enum enmSICHINTF
    16.   SICHINT_DISPLAY = &H0
    17.   SICHINT_ALLFIELDS = &H80000000
    18.   SICHINT_CANONICAL = &H10000000
    19.   SICHINT_TEST_FILESYSPATH_IF_NOT_EQUAL = &H20000000
    20. End Enum
    21.  
    22. Public Enum enmSFGAO
    23.   SFGAO_CANCOPY = &H1
    24.   SFGAO_CANMOVE = &H2
    25.   SFGAO_CANLINK = &H4
    26.   SFGAO_STORAGE = &H8
    27.   SFGAO_CANRENAME = &H10
    28.   SFGAO_CANDELETE = &H20
    29.   SFGAO_HASPROPSHEET = &H40
    30.   SFGAO_DROPTARGET = &H100
    31.   SFGAO_CAPABILITYMASK = &H177
    32.   SFGAO_SYSTEM = &H1000
    33.   SFGAO_ENCRYPTED = &H2000
    34.   SFGAO_ISSLOW = &H4000
    35.   SFGAO_LINK = &H10000
    36.   SFGAO_SHARE = &H20000
    37.   SFGAO_READONLY = &H40000
    38.   SFGAO_GHOSTED = &H8000
    39.   SFGAO_HIDDEN = &H80000
    40.   SFGAO_DISPLAYATTRMASK = &HFC000
    41.   SFGAO_NONENUMERATED = &H100000
    42.   SFGAO_NEWCONTENT = &H200000
    43.   SFGAO_STREAM = &H400000
    44.   SFGAO_VALIDATE = &H1000000
    45.   SFGAO_REMOVABLE = &H2000000
    46.   SFGAO_COMPRESSED = &H4000000
    47.   SFGAO_BROWSABLE = &H8000000
    48.   SFGAO_FILESYSANCESTOR = &H10000000
    49.   SFGAO_STORAGEANCESTOR = &H800000
    50.   SFGAO_FOLDER = &H20000000
    51.   SFGAO_FILESYSTEM = &H40000000
    52.   SFGAO_HASSUBFOLDER = &H80000000
    53.   SFGAO_CONTENTSMASK = &H80000000
    54.   SFGAO_STORAGECAPMASK = &H70C50008
    55.   SFGAO_PKEYSFGAOMASK = &H81044000
    56. End Enum
    57.  
    58. Private Enum enmIShellItem 'this is the VTbl-Idx-Enum, used in the Private Helper-Func vtblcall, further below
    59.   'IUnknown
    60.   isi_QueryInterface
    61.   isi_AddRef
    62.   isi_Release
    63.  
    64.   'IShellItem ...definitions and vtable-order from here: [url]http://fossies.org/dox/wine-1.4.1/interfaceIShellItem.html[/url]
    65.   isi_BindToHandler   '([in]  IBindCtx *pbc,[in] REFGUID rbhid,[in] REFIID riid,[out, iid_is(riid)] void **ppvOut)
    66.   isi_GetParent       '([out] IShellItem **ppsi)
    67.   isi_GetDisplayName  '([in]  SIGDN sigdnName,[out] LPWSTR *ppszName)
    68.   isi_GetAttributes   '([in]  SFGAOF sfgaoMask,[out] SFGAOF *psfgaoAttribs)
    69.   isi_Compare         '([in]  IShellItem *psi,[in] SICHINTF hint,[out] int *piOrder)
    70. End Enum
    71.  
    72. Private Declare Function SysReAllocString Lib "oleaut32" (ByVal pBSTR&, ByVal lpWStr&) As Long
    73. Private Declare Function DispCallFunc& Lib "oleaut32" (ByVal ppv&, ByVal oVft&, _
    74.         ByVal CC&, ByVal rtTYP%, ByVal paCount&, paTypes%, paValues&, fuReturn)
    75. Private Declare Sub CoTaskMemFree Lib "ole32" (ByVal pv As Long)
    76.  
    77. Private Declare Function ILCreateFromPathW Lib "shell32" (ByVal pszPath As Long) As Long
    78. Private Declare Sub ILFree Lib "shell32" (ByVal pIDL As Long)
    79. Private Declare Function SHCreateShellItem Lib "shell32" (ByVal pidlParent As Long, _
    80.         ByVal psfParent As Long, ByVal pIDL As Long, ppsi As stdole.IUnknown) As Long
    81.  
    82. Private mUnk As stdole.IUnknown, pUnk As Long, mPathName As String, pIDLAbs As Long, HRes As Long
    83.  
    84. Public Function InitFromPath(PathName As String)
    85.   Cleanup
    86.   mPathName = PathName
    87.   pIDLAbs = ILCreateFromPathW(ByVal StrPtr(mPathName))
    88.   HRes = SHCreateShellItem(0, 0, pIDLAbs, mUnk)
    89.   pUnk = ObjPtr(mUnk)
    90.   If HRes Then Err.Raise HRes
    91. End Function
    92.  
    93. Friend Sub InitFromParentItem(ParentShellItem As stdole.IUnknown)
    94.   Cleanup
    95.   Set mUnk = ParentShellItem
    96.   pUnk = ObjPtr(mUnk)
    97. End Sub
    98.  
    99. Public Property Get PathName() As String
    100.   PathName = mPathName
    101. End Property
    102.  
    103. Public Property Get ShellItemUnk() As stdole.IUnknown
    104.   Set ShellItemUnk = mUnk
    105. End Property
    106.  
    107. 'after the few additional Public Props above,
    108. 'now the real IShellItem interface-implementation
    109. '
    110. 'Public Function BindToHandler(...) '<- not yet implemented
    111.  
    112. Public Function GetParent() As cShellItem
    113. Dim ParentUnk As stdole.IUnknown
    114.   HRes = vtblCall(isi_GetParent, VarPtr(ParentUnk))
    115.   If HRes Then Err.Raise HRes
    116.   If ParentUnk Is Nothing Then Exit Function
    117.   Set GetParent = New cShellItem
    118.       GetParent.InitFromParentItem ParentUnk
    119. End Function
    120.  
    121. Public Function GetDisplayName(Optional ByVal SIGDN As enmSIGDN) As String
    122. Dim pS As Long
    123.   HRes = vtblCall(isi_GetDisplayName, SIGDN, VarPtr(pS))
    124.   If HRes Then Err.Raise HRes
    125.   SysReAllocString VarPtr(GetDisplayName), pS
    126.   If pS Then CoTaskMemFree pS
    127. End Function
    128.  
    129. Public Function GetAttributes(ByVal SFGAO As enmSFGAO) As Long
    130.   HRes = vtblCall(isi_GetAttributes, SFGAO, VarPtr(GetAttributes))
    131.   If HRes Then Err.Raise HRes
    132. End Function
    133.  
    134. Public Function Compare(OtherItem As cShellItem, Optional ByVal SICHINTF As enmSICHINTF) As Long
    135.   HRes = vtblCall(isi_Compare, ObjPtr(OtherItem.ShellItemUnk), SICHINTF, VarPtr(Compare))
    136.   If HRes <> 0 And HRes <> 1 Then Err.Raise HRes
    137. End Function
    138.  
    139.  
    140. 'vtblcall-by-index-> Helper-function
    141. Private Function vtblCall(ByVal vtblIdx As enmIShellItem, ParamArray P())
    142. Static VType(0 To 5) As Integer, VPtr(0 To 5) As Long
    143. Const CC_CDECL& = 1, CC_STDCALL& = 4
    144. Dim i As Long, V(), HResDisp As Long
    145.  
    146.   If pUnk = 0 Then Exit Function
    147.  
    148.   V = P 'make a copy of the params, to prevent problems with VT_ByRef-Members in the ParamArray
    149.   For i = 0 To UBound(V)
    150.     VType(i) = VarType(V(i))
    151.     VPtr(i) = VarPtr(V(i))
    152.   Next i
    153.  
    154.   HResDisp = DispCallFunc(pUnk, vtblIdx * 4, CC_STDCALL, vbLong, i, VType(0), VPtr(0), vtblCall)
    155.   If HResDisp Then Err.Raise HResDisp, , "Error in DispCallFunc"
    156. End Function
    157.  
    158. Private Sub Cleanup()
    159.   Set mUnk = Nothing: pUnk = 0
    160.   If pIDLAbs Then ILFree pIDLAbs: pIDLAbs = 0
    161. End Sub
    162.  
    163. Private Sub Class_Terminate()
    164.   Cleanup
    165. End Sub

    Olaf
    Last edited by Schmidt; Jun 25th, 2013 at 06:07 AM. Reason: small correction in cShellItem (forgot to cleanup a String-Handle)

  5. #5

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    Thanks, very informative! I didn't know the VTable order mattered/was different from MSDN. I'm definitely on the right track now. It is successfully creating the item, and GetAttributes works fine, and now I'm on to display name.

    So I can't pass the string directly for GetDisplayName? It seems to work ok,

    Code:
    Call isi.GetDisplayName(SIGDN_FILESYSPATH, lpw)
    MsgBox lpw
    Displays the correct name, but then VB crashes after I click OK. I only attempt doing it this way because it's most similar to how I use all the other interfaces.

    Most importantly- BindToHandler requires IBindCtx? Do you know anything about what I have to set in that? MSDN says it's just for optional things, would it be needed for IThumbnailProvider or IEnumShellItems?

    Also for reference,
    Code:
    Public Function GetPIDLFromPath(hwnd As Long, sPath As String) As Long
      Dim pchEaten As Long
      Dim pidl As Long
    
      ' Doesn't check the validity of the path as SUCCEEDED
      ' handles a failure from ParseDisplayName...
      If SUCCEEDED(isfDesktop.ParseDisplayName(hwnd, 0, StrConv(sPath, vbUnicode), pchEaten, pidl, 0)) Then
        GetPIDLFromPath = pidl
      End If
    End Function
    Just for reference, since it's creating the correct IShellItem it works, as with many other places it's used.
    Last edited by fafalone; Jun 24th, 2013 at 09:34 PM.

  6. #6
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: Trying to implement IShellItem - where'd I go wrong?

    As for the GetDisplayName-crash.
    The MSDN states, that this is not a BSTR which is returned, so you can't "map" or "pass" it into the VB-String-Type directly.
    They also make clear that:
    "It is the responsibility of the caller to free the string pointed to by ppszName when it is no longer needed. Call CoTaskMemFree on *ppszName to free the memory."

    Best to redefine your Typelib-Definition to a VB-Long - and handle it similar to what I do in the cShellItem-Class.
    Maybe rip-out the code and make a small function from it:

    Function BStrFromLPWStr(lpWStr As Long, Optional ByVal CleanupLPWStr As Boolean = True) As String
    SysReAllocString VarPtr(BStrFromLPWStr), lpWStr
    If CleanupLPWStr Then CoTaskMemFree lpWStr
    End Function


    As for the BindToHandler method - never used that - and also no experience with IBindCtx.

    What do you want to achieve in the end, a Thumbnail-Preview?

    Olaf

  7. #7

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    Thanks, that worked without the crash. I'm working on thumbnail preview yeah, but I specifically want to try using IThumbnailProvider and IShellImageFactory.

  8. #8

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    Ok, maybe you can help with this.
    Code:
    [
    	odl,
    	uuid(BCC18B79-BA16-442F-80C4-8A59C30C463B)
    ]
    interface IShellItemImageFactory : stdole.IUnknown {
    	HRESULT GetImage(
    		[in] SIZE *size,
    		[in] SIIGBF flags,
    		[in, out] LONG* phbm);
    };
    
    Public Declare Function SHCreateItemFromIDList Lib "shell32" (ByVal pidl As Long, REFIID As UUID, ppv As Any) As Long
    
    pidlFQ = GetPIDLFromPath(Me.hwnd, "C:\temp")
    hRes = SHCreateItemFromIDList(pidlFQ, IID_IShellItemImageFactory(), isif)
    It works up to this point. hRes is S_OK and isif Is Nothing =False.

    But when I call isif.GetImage, VB immediately crashes. I tried SIZE* size and SIZE *size, same crash, and without a *, how I thought it should be, I get 'can't pass user type byval'. I also tried making it just [out] LONG* phbm and no effect. Also tried explicitly setting the return type instead of Any.
    Last edited by fafalone; Jun 26th, 2013 at 11:03 PM.

  9. #9
    Default Member Bonnie West's Avatar
    Join Date
    Jun 2012
    Location
    InIDE
    Posts
    4,060

    Re: Trying to implement IShellItem - where'd I go wrong?

    Is IID_IShellItemImageFactory() an array? Shouldn't it be a UUID UDT?
    On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
    Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)

  10. #10

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    Quote Originally Posted by Bonnie West View Post
    Is IID_IShellItemImageFactory() an array? Shouldn't it be a UUID UDT?
    It's a UUID, it's a function call defined the same way as IID_IShellFolder and a couple other interfaces I'm using.

    Code:
    Public Function IID_IShellItemImageFactory() As UUID
    '{BCC18B79-BA16-442F-80C4-8A59C30C463B}
    Static iid As UUID
    If (iid.Data1 = 0) Then Call DEFINE_UUID(iid, &HBCC18B79, CInt(&HBA16), CInt(&H442F), &H80, &HC4, &H8A, &H59, &HC3, &HC, &H46, &H3B)
    IID_IShellItemImageFactory = iid
    End Function
    Last edited by fafalone; Jun 27th, 2013 at 11:30 PM.

  11. #11

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    So I tried using your vTable call method to see if I could get it to work that way, but alas VB immediately crashes again.

    Code:
        Private Enum enmIShellItemImageFactory
          'IUnknown
          isi_QueryInterface
          isi_AddRef
          isi_Release
          
          isi_GetImage
        End Enum
        Public Function GetImage(aa As Long, bb As Long, ByVal flags As enmSIIGBF) As Long
    Dim sz As sizeV
    sz.cx = aa
    sz.cy = bb
        HRes = vtblCall(isi_GetImage, VarPtr(sz), flags, VarPtr(GetImage))
        Debug.Print "vtbl hres=" & HRes
    
        End Function
    I might be passing the size type wrong, but I have no idea how to do it. Without varptr i get a compile error saying only public user types from public user modules can be passed. Even though it IS a public defined type in a public user module.

  12. #12
    PowerPoster Zvoni's Avatar
    Join Date
    Sep 2012
    Location
    To the moon and then left
    Posts
    5,261

    Re: Trying to implement IShellItem - where'd I go wrong?

    If i understand the vtbCall-Function correctly you're passing a pointer to a pointer to DispCallFunc
    Last edited by Zvoni; Tomorrow at 31:69 PM.
    ----------------------------------------------------------------------------------------

    One System to rule them all, One Code to find them,
    One IDE to bring them all, and to the Framework bind them,
    in the Land of Redmond, where the Windows lie
    ---------------------------------------------------------------------------------
    People call me crazy because i'm jumping out of perfectly fine airplanes.
    ---------------------------------------------------------------------------------
    Code is like a joke: If you have to explain it, it's bad

  13. #13
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: Trying to implement IShellItem - where'd I go wrong?

    @fafalone
    Before using the vtblCall, did you made sure, that the (class-level) Variables Unk and pUnk were filled?
    Maybe it is better, to put the vtblCall-function into a *.bas-Module, so that it can be shared by more than one "Implementer-Class".
    What would then be needed, is the passing of the formerly class-level Variable pUnk as a Parameter - but this'd make the
    call-definition more "speaking" (what's needed), not hiding important things...
    e.g. with a signature like this:

    Public Function vtblCall(vtblIdx As Long, pUnk As Long, ParamArray P())

    You could move it "out of the implementing class (and put it into a *.bas-Module).

    Since ShellItem-wrapping (with all bells and whistles) is new to me (but interesting), maybe just wait
    a bit more - I'm already playing with it and work with a small example, which exposes a bit more and
    will wrap also IShellItemFactory in a small class - from a first view into the API-Def for its GetImage-
    Method, it seems that the [Size sz] param needs to be passed ByValue - so you will have to copy it over
    into an 8Byte-sized "ByVal transport-container" (as e.g. a Currency or a Double) - or just pass it
    on the stack as two (isolated) Parameters (that will work too, since the Alignment of Stacked Params
    for Windows-StdCalls is 4Byte).

    @Zvoni
    The DispCallFunc is basically the "generic and official Function-Caller by pointer", every VB-Dev was
    always looking for (often resorting to CallWindowProc- or ASM-Hacks instead) - not sure who needs
    to be given the credits for the first working VB-example (wasn't me, found it not that long ago
    in a german forum the first time)... googling - ... - well, seems that the first "working VB-example"
    is from 2003: http://www.activevb.de/tipps/vb6tipps/tipp0600.html


    I was using Paul Catons stuff before - but since there was this nice explicit Parameter CC_STDCALL
    I googled a bit more and found a whole Enum-List:

    Public Enum CallingConventions
    CC_FASTCALL
    CC_CDECL
    CC_PASCAL
    CC_MACPASCAL
    CC_STDCALL
    CC_RESERVED
    CC_SYSCALL
    CC_MPWCDECL
    CC_MPWPASCAL
    End Enum

    More googling and experimenting revealed, that the Function-Pointer is internally resolved by:
    dereferencedResultOfParam_ppv + Vtbl_Offset

    And (luckily) passing a Null to ppV - and the direct Function-Pointer in the Vtbl-Offset-Param,
    one can do also all kind of "normal API-Calls" (not only Disp- or COM-Calls).
    And in this "direct FuncPtr-Mode", the above Enum comes in very handy - and is working as
    expected.

    Here's a small Demo, which shows all 3 "Modes", in which DispCallFunc can be used now with
    VB (StdCall-, CDECL- calls and of course also COM- (VTbl-) Calls.

    http://www.vbRichClient.com/Download...InvokeDemo.zip

    Olaf

  14. #14

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    In the mean time, do you know any C++? I need a temporary workaround so was just going to make a dll to do it.

    But I keep getting "unresolved external symbol" for ImageList_Add, even though windows.h and commctrl.h are included.

  15. #15

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    Well I still can't get it working in VB. But I now have a workaround that works perfectly. It's actually pretty simple. I know absolutely nothing about C++ and was able to figure this out from an example in a couple hours.

    First, need a DLL:
    Code:
    // RNThumb.cpp : Retrieves a thumbnail from IShellItemImageFactory and adds it to the specified imagelist
    //
    
    #include "stdafx.h"
    #include "RNThumb.h"
    #include <stdexcept>
    //AddTohIML - Adds the thumbnail for a file to the given imagelist
    //Inputs: Imagelist, absolute pidl for a file, and size of thumbnail
    //Outputs: The position in the imagelist of the new icon
    
    	extern "C" RNTHUMB_API HRESULT __stdcall AddTohIML(HIMAGELIST himl, PCIDLIST_ABSOLUTE pidlSrc, int cxThumb, int cyThumb)
    	{
            int hpos1 = -1;
    		HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
            if (SUCCEEDED(hr))
            {
                
    			// Getting the IShellItemImageFactory interface pointer for the file.
                IShellItemImageFactory *pImageFactory;
                hr = SHCreateItemFromIDList(pidlSrc, IID_PPV_ARGS(&pImageFactory));
                if (SUCCEEDED(hr))
                {
                    SIZE size = { cxThumb, cyThumb };
                    HBITMAP hbmp;
                    hr = pImageFactory->GetImage(size, SIIGBF_RESIZETOFIT, &hbmp);
                    if (SUCCEEDED(hr))
                    {
                        
    					
    					hpos1 = ImageList_Add(himl, hbmp, NULL);
    					return (HRESULT)hpos1;
                        DeleteObject(hbmp);
                    }
    
                    pImageFactory->Release();
    			}
    		}
    		CoUninitialize();
    		return (HRESULT)hpos1;
    	}
    And then in VB,

    Code:
    Public Declare Function AddTohIML Lib "C:\rename\RNThumb.dll" (ByVal hIML As Long, ByVal pidlFile As Long, ByVal cx As Long, ByVal cy As Long) As Long
    
    Dim lRtn As Long
    lRtn = AddTohIML(hIML_Thumb, pidlfqChild, cxThumb, cyThumb)
    lvi.iImage = lRtn

  16. #16
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: Trying to implement IShellItem - where'd I go wrong?

    Code:
    Public Function GetImage(Byval aa As Long, Byval bb As Long, ByVal flags As enmSIIGBF) As Long
        HRes = vtblCall(isi_GetImage, aa, bb, flags, VarPtr(GetImage))
        Debug.Print "vtbl hres=" & HRes
    End Function
    Splitting up the 'SIZE sz' Single-Param into two separate Params didn't help?
    Did you try it (because it works here).

    Olaf

  17. #17

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    HRes = vtblCall(isi_GetImage, aa, bb, Flags, VarPtr(GetImage))

    That's what I tried, and VB still immediately crashes.

  18. #18
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: Trying to implement IShellItem - where'd I go wrong?

    Quote Originally Posted by fafalone View Post
    HRes = vtblCall(isi_GetImage, aa, bb, Flags, VarPtr(GetImage))

    That's what I tried, and VB still immediately crashes.
    Then there's something wrong elsewhere (no instance in mUnk, or not the correct instance)...
    The call looks good - not different from what I have here (aside from, that I pass the pUnk now explicitely)...

    Anyways - just did an upload of a Demo-Project, which does ShellItem-Rendering with different Fallbacks.
    It is working also on XP (due to appropriate Fallbacks over IExtractImage, and IExtractIcon).

    The following Interfaces are implemented completely now:
    - IShellItem (in cShellItem)
    - IExtractIcon (in cExtractIcon )
    - IExtractImage (in cExtractImage )
    - IShellItemImageFactory (in cShellItemImageFactory)

    Still no TypeLib and not much code (the implementing classes share a module modImplHelpers.bas now).

    One note though - a few small Routines in the Test-Form (and only there) use my Cairo-Graphics-Wrapper
    to bring the different Alpha-hBmps or Alpha-hIcons onto the Screen (in a for me convenient way - since I have
    PreMultiplying and a lot of other useful stuff there, which was time-saving).

    As said, this is only in the Form - all the other Modules are directly usable with your own hBmp- and hIcon-
    Handler-Routines and don't require the vbRichClient5.dll...

    But for the example to be immediately usable, you would need to download the latest version from:
    http://www.vbrichclient.com/#/en/Downloads.htm
    and register it locally in a Folder on your Dev-Machine.

    The Demo-Download for the ShellItem-stuff is here:
    http://www.vbrichclient.com/Download...mRendering.zip

    Hope this sheds some light on things - and that your crashes will vanish... ;-)

    Olaf

  19. #19

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    Thanks very much, this is all going very smoothly now. Just one general question; I thought IShellItemImageFactory returned a thumbnail if available? And only returned the icon if no thumbnail was available. Both your code (including an unmodified run of the sample) and mine are only showing the file type icons for image files instead of actual thumbnails. Is this dependent on the setting in Explorer? (I have thumbnails disabled in Explorer), or a problem with something elsewhere in my computer? I could have sworn it was showing them the other day... then something I did wiped my associations. Maybe the preview handlers didn't reset when I fixed the associations? Any way to check that manually? IExtractImage still works but I don't want to have to go through a fallback if I don't have to.
    Last edited by fafalone; Jul 5th, 2013 at 02:08 AM.

  20. #20
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: Trying to implement IShellItem - where'd I go wrong?

    Ah, thanks for the feeedback - as said, it was my first attempt in wrapping those ShellItem-related Interfaces - so this is good to know...
    That's what I experienced recently, after doing more and more "direct implementations" (using the DispCallFunc) instead of writing real typelibs for such "more exotic stuff" ...
    It does make a bit more work in the beginning - but having a direct WrapperClass (cSomething) instead of an ISomething (which mayhap doesn't support the native VB-Types directly),
    so far always payed off in the end.

    As for your question ... well, there *is* some caching involved - and when you not force explicitely per (Optional) Flag, then the call might be using the first thing it finds in the thumbnail-cache.

    I'd offer settings in some config-dialogue of your app for that - as e.g.:
    [ ] Icon_first_then_Thumbnail
    [ ] Thumbnail_first_then_Icon

    and then call the Factory always with those potentially two stages - passing the explicit flags
    in the Order as currently set in your Dialogue.

    If the first (explicitely flagged) call fails, then proceed with the second -
    if that fails too, then proceed with the IExtractIcon or other fallbacks...

    Here's the possible flags again ... and in my tests those *did* have an effect:
    Code:
    Public Enum enmSIIGBF
      SIIGBF_RESIZETOFIT = &H0
      SIIGBF_BIGGERSIZEOK = &H1
      SIIGBF_MEMORYONLY = &H2
      SIIGBF_ICONONLY = &H4
      SIIGBF_THUMBNAILONLY = &H8
      SIIGBF_INCACHEONLY = &H10
      SIIGBF_CROPTOSQUARE = &H20   'Introduced in Windows 8. If necessary, crop the bitmap to a square.
      SIIGBF_WIDETHUMBNAILS = &H40 'Introduced in Windows 8. Stretch and crop the bitmap to a 0.7 aspect ratio.
      SIIGBF_ICONBACKGROUND = &H80 'Introduced in Windows 8. If returning an icon, paint a background using the associated app's registered background color.
      SIIGBF_SCALEUP = &H100       'Introduced in Windows 8. If necessary, stretch the bitmap so that the height and width fit the given size.
    End Enum
    Olaf

  21. #21

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    It seems it will return the thumbnail if I use the SIIGBF_THUMBNAILONLY flag, but the file type icon with any other combination of flags. This seems a bit weird. It's probably related to how thoroughly I've disabled thumbnails in Explorer.

    So it seems all I can do is first try THUMBNAILONLY and then if that fails ICONONLY.

  22. #22
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: Trying to implement IShellItem - where'd I go wrong?

    Quote Originally Posted by fafalone View Post
    It seems it will return the thumbnail if I use the SIIGBF_THUMBNAILONLY flag, but the file type icon with any other combination of flags. This seems a bit weird. It's probably related to how thoroughly I've disabled thumbnails in Explorer.

    So it seems all I can do is first try THUMBNAILONLY and then if that fails ICONONLY.
    Yep, that's how I'd have coded it in either case... with that two-stage-approach you simply avoid any "non-crystal-clear behavior in case the system interprets the default-flags in a funny way".

    Olaf

  23. #23

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: Trying to implement IShellItem - where'd I go wrong?

    I wanted to revisit this thread since I finally found a solution to IShellItemImageFactory crashing.
    MSDN, ShObjIdl.idl:
    [in] SIZE* size,

    MSDN:
    Code:
    typedef struct tagSIZE {
      LONG cx;
      LONG cy;
    } SIZE, *PSIZE;
    olelib:
    Code:
    typedef struct SIZE {
        long cx;
        long cy;
    } SIZE;
    Working C++ version i had been using:
    Code:
    SIZE size = { cxThumb, cyThumb };
                    HBITMAP hbmp;
                    hr = pImageFactory->GetImage(size, SIIGBF_RESIZETOFIT, &hbmp);
    So what's the problem, everything looks good right? Well I was looking at Schmidt's code again, and noticed he sent them to the v-table as two separate args, so I tried redefining my interface:
    Code:
    interface IShellItemImageFactory : stdole.IUnknown {
    	long GetImage(
    		[in] LONG cx,
    		[in] LONG cy,
    		[in] SIIGBF flags,
    		[out] HBITMAP *phbm);
    };
    And it works! This all seems truly bizarre to me since I don't understand why adding an extra arg in isn't throwing the v-table off, not to mention the thousands of other structs that aren't passed argument by argument (i.e. every single one in all of olelib and oleexp except this instance). I had revisited this again while I was re-writing my C workaround for a new app, and gave it one more go. So anyway... resolved 2 years later. And dear sweet deity$ i posted this thread 2 years ago?

  24. #24
    Default Member Bonnie West's Avatar
    Join Date
    Jun 2012
    Location
    InIDE
    Posts
    4,060

    Re: Trying to implement IShellItem - where'd I go wrong?

    Quote Originally Posted by fafalone View Post
    This all seems truly bizarre to me since I don't understand why adding an extra arg in isn't throwing the v-table off, not to mention the thousands of other structs that aren't passed argument by argument (i.e. every single one in all of olelib and oleexp except this instance).
    I don't think a method's number of arguments has anything to do with its offset from the VTable. As you know, a VTable is just an array of pointers to the methods of an interface. It doesn't specify how many parameters a particular method expects.


    Schmidt already pointed out that the IShellItemImageFactory::GetImage method's size parameter is supposed to be passed ByVal, but, as you have discovered, VB doesn't allow passing UDTs by value. The usual workaround for this, which you are now using, is to pass the UDT's members individually in the correct order and ensuring that the original alignment of the structure's members are preserved.
    On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
    Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)

  25. #25

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    7,653

    Re: [RESOLVED] Trying to implement IShellItem - where'd I go wrong?

    Makes sense but I've written dozens of interfaces now and never experienced this.. Have always just passed a pointer if i couldn't pass byref, and certainly never got a full ide crash as a result.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width