Results 1 to 4 of 4

Thread: How to pin/unpin from QuickAccessFolder the easy way

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    940

    How to pin/unpin from QuickAccessFolder the easy way

    I was wondering how I could add pinned folder to QuickAccess and did find an easy way but quite poorly documented indeed.
    That is done is by sending "pintohome" verb command either in ShellExecute API or in ShellExecuteEx
    I choosed the latter one.

    Code:
    Public Declare Function ILCreateFromPathW Lib "shell32" (ByVal pszPath As Long) As Long
    
    Public Enum ShellExecuteEx_Flags
        SEE_MASK_CLASSKEY = &H3
        SEE_MASK_CLASSNAME = &H1
        SEE_MASK_CONNECTNETDRV = &H80
        SEE_MASK_DOENVSUBST = &H200
        SEE_MASK_FLAG_DDEWAIT = &H100
        SEE_MASK_FLAG_NO_UI = &H400
        SEE_MASK_HOTKEY = &H20
        SEE_MASK_ICON = &H10
        SEE_MASK_IDLIST = &H4
        SEE_MASK_INVOKEIDLIST = &HC
        SEE_MASK_NOCLOSEPROCESS = &H40
    End Enum
    
    Public Type SHELLEXECUTEINFO
      cbSize As Long
      fMask As ShellExecuteEx_Flags
      hWnd As Long
      lpVerb As Long
      lpFile As Long
      lpParameters As Long
      lpDirectory As Long
      nShow As Integer
      hInstApp As Long
      lpIDList As Long
      lpClass As Long
      hkeyClass As Long
      dwHotKey As Long
      hIcon As Long
      hMonitor As Long
      hProcess As Long
    End Type
    
    Public Declare Function ShellExecuteEx Lib "shell32.dll" Alias "ShellExecuteExW" (lpSEEI As Any) As Long
    
    Public Const sCLSID_ShellLibrary As String = "{D9B3211D-E57F-4426-AAEF-30A806ADD397}"
    
    Public Declare Function CLSIDFromString Lib "ole32.dll" (ByVal pszCLSID As Long, pguid As Any) As Long
    
    Public Enum CLSCTX
      CLSCTX_INPROC_SERVER = &H1
      CLSCTX_INPROC_HANDLER = &H2
      CLSCTX_LOCAL_SERVER = &H4
      CLSCTX_INPROC_SERVER16 = &H8
      CLSCTX_REMOTE_SERVER = &H10
      CLSCTX_INPROC_HANDLER16 = &H20
      CLSCTX_RESERVED1 = &H40
      CLSCTX_RESERVED2 = &H80
      CLSCTX_RESERVED3 = &H100
      CLSCTX_RESERVED4 = &H200
      CLSCTX_NO_CODE_DOWNLOAD = &H400
      CLSCTX_RESERVED5 = &H800
      CLSCTX_NO_CUSTOM_MARSHAL = &H1000
      CLSCTX_ENABLE_CODE_DOWNLOAD = &H2000
      CLSCTX_NO_FAILURE_LOG = &H4000
      CLSCTX_DISABLE_AAA = &H8000
      CLSCTX_ENABLE_AAA = &H10000
      CLSCTX_FROM_DEFAULT_CONTEXT = &H20000
      CLSCTX_ACTIVATE_X86_SERVER = &H40000
      CLSCTX_ACTIVATE_32_BIT_SERVER
      CLSCTX_ACTIVATE_64_BIT_SERVER = &H80000
      CLSCTX_ENABLE_CLOAKING = &H100000
      CLSCTX_APPCONTAINER = &H400000
      CLSCTX_ACTIVATE_AAA_AS_IU = &H800000
      CLSCTX_RESERVED6 = &H1000000
      CLSCTX_ACTIVATE_ARM32_SERVER = &H2000000
      CLSCTX_ALLOW_LOWER_TRUST_REGISTRATION
      CLSCTX_PS_DLL = &H80000000
      CLSCTX_SERVER = CLSCTX_INPROC_SERVER Or CLSCTX_LOCAL_SERVER Or CLSCTX_REMOTE_SERVER
      CLSCTX_ALL = CLSCTX_INPROC_SERVER Or CLSCTX_INPROC_HANDLER Or CLSCTX_LOCAL_SERVER Or CLSCTX_REMOTE_SERVER
    End Enum
    
    Public Declare Function CoCreateInstance Lib "ole32.dll" (rclsid As Any, ByVal pUnkOuter As Long, ByVal dwClsContext As Long, riid As Any, ppv As Any) As Long
    
    Public Sub DEFINE_UUID(Name As UUID, L As Long, w1 As Integer, w2 As Integer, B0 As Byte, b1 As Byte, b2 As Byte, B3 As Byte, b4 As Byte, b5 As Byte, b6 As Byte, b7 As Byte)
      With Name
        .Data1 = L: .Data2 = w1: .Data3 = w2: .Data4(0) = B0: .Data4(1) = b1: .Data4(2) = b2: .Data4(3) = B3: .Data4(4) = b4: .Data4(5) = b5: .Data4(6) = b6: .Data4(7) = b7
      End With
    End Sub
    
    Public Function IID_IShellLibrary() As UUID
    '{&H11a66efa, &H382e, &H451a, {&H92, &H34, &H1e, &He, &H12, &Hef, &H30, &H85}}
    Static iid As UUID
     If (iid.Data1 = 0) Then Call DEFINE_UUID(iid, &H11A66EFA, CInt(&H382E), CInt(&H451A), &H92, &H34, &H1E, &HE, &H12, &HEF, &H30, &H85)
      IID_IShellLibrary = iid
    End Function
    
    
    Public Function ShCreateShellLibrary(pISL As IShellLibrary) As Long
      Dim CLSID_ShellLibrary As GUID
      
      CLSIDFromString StrPtr(sCLSID_ShellLibrary), CLSID_ShellLibrary
      
      hr = CoCreateInstance(CLSID_ShellLibrary, 0&, CLSCTX_INPROC_SERVER, IID_IShellLibrary, pISL)
      
      ShCreateShellLibrary = hr
      
    End Function
    
    Public Function ShPinToQuickAccessFolder(ByVal sPathOrFile As String) As Long
       Dim lpSEIEX As SHELLEXECUTEINFO
       Dim lr As Long
       Dim pISI As IShellItem2
       Dim pISL As IShellLibrary
       
       lpSEIEX.cbSize = LenB(lpSEIEX)
       lpSEIEX.fMask = SEE_MASK_NO_CONSOLE Or SEE_MASK_IDLIST
       lpSEIEX.lpVerb = StrPtr("pintohome")
       lpSEIEX.lpIDList = ILCreateFromPathW(StrPtr(sPathOrFile))
       ShCreateShellLibrary pISL
       lr = ShellExecuteEx(lpSEIEX)
       If lr <> 0 Then
          SHCreateShellItem 0, 0, lpSEIEX.lpIDList, pISI
          hr = pISL.RemoveFolder(pISI)
          pISL.Commit
       End If
       If lr = 1 Then ShPinToQuickAccessFolder = S_OK Else ShPinToQuickAccessFolder = S_FALSE
       Set pISI = Nothing
       Set pISL = Nothing
       CoTaskMemFree lpSEIEX.lpIDList
    End Function
    But at first I didn't find any good solution for unpinning the folder corresponding to "pintohome" for i.e "unpinfromhome" but nothing such exists.

    So I come up with creating a Library Folder and creating a IShellItem pointing to the currently pinned folder via the pidl from it's path.

    And by assigning the created IShellItem to HRESULT IShellLibrary::RemoveFolder([in] IShellItem *psiLocation) method you can easily unpin the folder.

    So the folder to be pinned MUST physically exist or be a registered virtual folder and it DOES NOT become deleted by unpinning it.

    You need Fafalone's TypeLib or other TypeLib that deals with IShellLibrary and IShellItem interfaces.
    Attached Images Attached Images  
    Last edited by nebeln; Jan 21st, 2025 at 03:57 PM.

  2. #2
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,819

    Re: How to pin/unpin from QuickAccessFolder the easy way

    Be careful with that folder... MS has laid traps that crash your app when using their own documented methods with it in other contexts.

    'Do you support normal shell item thing X?' 'Yes, S_OK!'

    'Do you support normal shell item thing x.y?' 'Yes, S_OK!'

    'Can I have a pointer for x.y' 'Yes, S_OK!'

    <boom>

    'But it was non-zero and you said S_OK!' 'Haha got you it was invalid!'

  3. #3

    Thread Starter
    Fanatic Member
    Join Date
    Mar 2023
    Posts
    940

    Re: How to pin/unpin from QuickAccessFolder the easy way

    Aha okey, but I have still not runned into these traps yet.
    However as I wrote in my post that there exist no ”unpinfromhome” or similar.
    I also scanned thrue shell32.dll for res-strings named unpin something but nothing.
    The only I’ve seen is registry scripting for and dealing with .reg files for unpinning.
    So last resort was to creating a IShellLibrary folder and take it from there.

  4. #4
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    6,819

    Re: How to pin/unpin from QuickAccessFolder the easy way

    In principle it's a shell item createable via "shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}"

    The crash sequence I mentioned came up when attempting to load a default categorizer.

    In theory pinned status should be controllable via a property key representing pinned status; but I can't find the right one. Most are unimplemented, one says pinned for all of them, another has incorrect pinned status for some items but not others.

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