This is a bit more of an EUC post rather than a specifically AppSense one. It deals with one of the most annoying bits of the user interface when upgrading users to Windows 7 or higher - that of the Libraries feature.
Libraries are a way of aggregating views in Windows Explorer (or File Explorer, as it's now known in Windows 8 and upwards). They simply bring together a collection of folders into one view. Removing folders from the Library doesn't remove the files from disk - effectively, they are just linked views that can connect to multiple locations on the same or many computers.
The problems with Libraries are that a) users are often used to doing things in different ways, particularly in environments with controlled network drive access, and b) the implementation of them isn't very straightforward, and they can't be managed particularly well with GPOs, making central standardization a real drag. Under the hood, libraries are controlled by a collection of .library-ms files that sit in %AppData%\Microsoft\Windows\Libraries, and they are in an XML file format, which (in my opinion, anyway) don't lend themselves to easy management.
The yucky innards of a Windows Library file |
By default, Libraries contain a couple of locations, and changing this (normally to remove the Public folder for a particular Library) can also be a royal pain in the backside. This article doesn't concern itself with this issue, but you can achieve it (amongst other ways) by changing the base/default profile, or by editing the .library-ms files directly and then deploying them through whatever means you find easiest. There isn't much GPO support - a lot of admins find themselves deploying File Actions in AppSense EM or Group Policy Preferences to get around this limitation.
An AppSense EM Mirror Action providing customized Library files to the endpoint |
What we are concerned with in this article, however, is trying to provide a way to disable Libraries on a per-Library basis. In the default configuration, you get four Libraries - Documents, Pictures, Music and Videos. Depending on your organization, some of these folders may not really feel relevant or appropriate, so it would be useful to find a way to easily and quickly disable any of them that you feel aren't necessary.
The default set of four Libraries |
Some digging on the Intertubes eventually turns up a Group Policy Object that may help us here. It is User Config | Admin Templates | Windows Components | Windows (or File) Explorer | Disable Known Folders.
The policy object prevents the underlying file or directory being created, so if the folder already exists, you will need to remove it first. You can specify a "Known Folder" by using either the known folder ID or the canonical name, but I find the folder ID more robust. You also need to be careful in case you have any applications that depend on the existence of the folder. I'd categorize them as poorly-written applications if they do, but we've all got to support or deploy bad applications, so it pays to do some analysis.
We can deploy the setting via standard GPO, AppSense Environment Manager, or any other software with similar capability. Naturally, I will be doing it through Environment Manager.
To disable the Videos folder, use this folder ID - {491E922F-5643-4af4-A7EB-4E7A138D8174}
To disable the Pictures folder, use this folder ID - {A990AE9F-A03B-4e80-94BC-9912D7504104}
To disable the Music folder, use this folder ID - {2112AB0A-C86A-4ffe-A368-0DE96E47012E}
And obviously you can mix and match these as required to produce more refined results
In some situations, dependent on the policy objects or profiles in use, you may see some aberrant results. I found myself in an environment recently where disabling the Music library didn't work - for some reason the folder ID specified above disabled all of them!
In this case, it involved some scripted trickery to get the results I wanted. What I did was take a script that re-enabled all of the libraries - and then simply commented out the lines for the Music library. Here's how it fitted into the EM configuration.
The script (which I'm not sure where I got it from, so apologies if I am printing someone's hard work without credit) is reproduced as I used it below. If you have bother with a particular library, then modify the commented-out lines as needed to "restore" the libraries you require.
Option Explicit
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8
Dim WshShell : Set WshShell = CreateObject("WScript.Shell")
Dim objFSO : Set objFSO = CreateObject("Scripting.FileSystemObject")
Dim objTextFile
Dim iMainTeller, iError
Dim strProfilePath, strLibrary
strProfilePath = WshShell.ExpandEnvironmentStrings("%appdata%")
If Right(strProfilePath,1) <> "\" Then strProfilePath = strProfilePath & "\"
For iMainTeller = 1 To 4
Select Case iMainTeller
Case 1 : strLibrary = "Documents"
Case 2 : strLibrary = "Videos"
' Case 3 : strLibrary = "Music"
Case 4 : strLibrary = "Pictures"
End Select
If Not objFSO.FileExists(strProfilePath & "Microsoft\Windows\Libraries\"& strLibrary & ".library-ms") Then
If Not objFSO.FolderExists(strProfilePath & "Microsoft\Windows\Libraries") Then
On Error Resume Next
objFSO.CreateFolder(strProfilePath & "Microsoft\Windows\Libraries")
Err.Clear
On Error Goto 0
End If
On Error Resume Next
Set objTextFile = objFSO.CreateTextFile(strProfilePath & "Microsoft\Windows\Libraries\"& strLibrary & ".library-ms",True)
iError = Err.Number
On Error Goto 0
If iError = 0 Then
objTextFile.WriteLine("<?xml version="& Chr(34) & "1.0"& Chr(34) & " encoding="& Chr(34) & "UTF-8"& Chr(34) & "?>")
objTextFile.WriteLine("<libraryDescription xmlns="& Chr(34) & "http://schemas.microsoft.com/windows/2009/library"& Chr(34) & ">")
Select Case iMainTeller
Case 1 : objTextFile.WriteLine(vbTab & "<name>@shell32.dll,-34575</name>")
Case 2 : objTextFile.WriteLine(vbTab & "<name>@shell32.dll,-34620</name>")
' Case 3 : objTextFile.WriteLine(vbTab & "<name>@shell32.dll,-34584</name>")
Case 4 : objTextFile.WriteLine(vbTab & "<name>@shell32.dll,-34595</name>")
End Select
' ownerSID will be added by Windows when first accessed
objTextFile.WriteLine(vbTab & "<version>8</version>") ' since no current version can be found a random one
objTextFile.WriteLine(vbTab & "<isLibraryPinned>true</isLibraryPinned>")
Select Case iMainTeller
Case 1 : objTextFile.WriteLine(vbTab & "<iconReference>imageres.dll,-1002</iconReference>")
Case 2 : objTextFile.WriteLine(vbTab & "<iconReference>imageres.dll,-1005</iconReference>")
' Case 3 : objTextFile.WriteLine(vbTab & "<iconReference>imageres.dll,-1004</iconReference>")
Case 4 : objTextFile.WriteLine(vbTab & "<iconReference>imageres.dll,-1003</iconReference>")
End Select
objTextFile.WriteLine(vbTab & "<templateInfo>")
'http://msdn.microsoft.com/en-us/library/windows/desktop/dd798386%28v=vs.85%29.aspx
Select Case iMainTeller
Case 1 : objTextFile.WriteLine(vbTab & vbTab & "<folderType>{7D49D726-3C21-4F05-99AA-FDC2C9474656}</folderType>")
Case 2 : objTextFile.WriteLine(vbTab & vbTab & "<folderType>{5fa96407-7e77-483c-ac93-691d05850de8}</folderType>")
' Case 3 : objTextFile.WriteLine(vbTab & vbTab & "<folderType>{94d6ddcc-4a68-4175-a374-bd584a510b78}</folderType>")
Case 4 : objTextFile.WriteLine(vbTab & vbTab & "<folderType>{B3690E58-E961-423B-B687-386EBFD83239}</folderType>")
End Select
objTextFile.WriteLine(vbTab & "</templateInfo>")
objTextFile.WriteLine(vbTab & "<searchConnectorDescriptionList>")
objTextFile.WriteLine(vbTab & vbTab & "<searchConnectorDescription publisher="& Chr(34) & "Microsoft"& Chr(34) & " product="& Chr(34) & "Windows"& Chr(34) & ">")
Select Case iMainTeller ' personal folder , volgende descriopt is public folder
Case 1 : objTextFile.WriteLine(vbTab & vbTab & vbTab & "<description>@shell32.dll,-34577</description>")
Case 2 : objTextFile.WriteLine(vbTab & vbTab & vbTab & "<description>@shell32.dll,-34622</description>")
' Case 3 : objTextFile.WriteLine(vbTab & vbTab & vbTab & "<description>@shell32.dll,-34586</description>")
Case 4 : objTextFile.WriteLine(vbTab & vbTab & vbTab & "<description>@shell32.dll,-34597</description>")
End Select
objTextFile.WriteLine(vbTab & vbTab & vbTab & "<isDefaultSaveLocation>true</isDefaultSaveLocation>")
objTextFile.WriteLine(vbTab & vbTab & vbTab & "<simpleLocation>")
'Know folders GUID : http://msdn.microsoft.com/en-us/library/bb882665.aspx
Select Case iMainTeller
Case 1 : objTextFile.WriteLine(vbTab & vbTab & vbTab & vbTab & "<url>knownfolder:{FDD39AD0-238F-46AF-ADB4-6C85480369C7}</url>")
Case 2 : objTextFile.WriteLine(vbTab & vbTab & vbTab & vbTab & "<url>knownfolder:{18989B1D-99B5-455B-841C-AB7C74E4DDFC}</url>")
' Case 3 : objTextFile.WriteLine(vbTab & vbTab & vbTab & vbTab & "<url>knownfolder:{4BD8D571-6D19-48D3-BE97-422220080E43}</url>")
Case 4 : objTextFile.WriteLine(vbTab & vbTab & vbTab & vbTab & "<url>knownfolder:{33E28130-4E1E-4676-835A-98395C3BC3BB}</url>")
End Select
'serialized will be created by Windows when first accessed http://msdn.microsoft.com/en-us/library/windows/desktop/dd940482(v=vs.85).aspx
objTextFile.WriteLine(vbTab & vbTab & vbTab & "</simpleLocation>")
objTextFile.WriteLine(vbTab & vbTab & "</searchConnectorDescription>")
objTextFile.WriteLine(vbTab & vbTab & "<searchConnectorDescription publisher="& Chr(34) & "Microsoft"& Chr(34) & " product="& Chr(34) & "Windows"& Chr(34) & ">")
Select Case iMainTeller
Case 1 : objTextFile.WriteLine(vbTab & vbTab & vbTab & "<description>@shell32.dll,-34579</description>")
Case 2 : objTextFile.WriteLine(vbTab & vbTab & vbTab & "<description>@shell32.dll,-34624</description>")
' Case 3 : objTextFile.WriteLine(vbTab & vbTab & vbTab & "<description>@shell32.dll,-34588</description>")
Case 4 : objTextFile.WriteLine(vbTab & vbTab & vbTab & "<description>@shell32.dll,-34599</description>")
End Select
objTextFile.WriteLine(vbTab & vbTab & vbTab & "<isDefaultNonOwnerSaveLocation>true</isDefaultNonOwnerSaveLocation>")
objTextFile.WriteLine(vbTab & vbTab & vbTab & "<simpleLocation>")
'Know folders GUID : http://msdn.microsoft.com/en-us/library/bb882665.aspx
Select Case iMainTeller
Case 1 : objTextFile.WriteLine(vbTab & vbTab & vbTab & vbTab & "<url>knownfolder:{ED4824AF-DCE4-45A8-81E2-FC7965083634}</url>")
Case 2 : objTextFile.WriteLine(vbTab & vbTab & vbTab & vbTab & "<url>knownfolder:{2400183A-6185-49FB-A2D8-4A392A602BA3}</url>")
' Case 3 : objTextFile.WriteLine(vbTab & vbTab & vbTab & vbTab & "<url>knownfolder:{3214FAB5-9757-4298-BB61-92A9DEAA44FF}</url>")
Case 4 : objTextFile.WriteLine(vbTab & vbTab & vbTab & vbTab & "<url>knownfolder:{B6EBFB86-6907-413C-9AF7-4FC2ABF07CC5}</url>")
End Select
'serialized will be created by Windows when first accessed http://msdn.microsoft.com/en-us/library/windows/desktop/dd940482(v=vs.85).aspx
objTextFile.WriteLine(vbTab & vbTab & vbTab & "</simpleLocation>")
objTextFile.WriteLine(vbTab & vbTab & "</searchConnectorDescription>")
objTextFile.WriteLine(vbTab & "</searchConnectorDescriptionList>")
objTextFile.WriteLine("</libraryDescription>")
objTextFile.Close
End If
End If
Next
Set objTextFile = Nothing
Set objFSO = Nothing
Hopefully, even if you find yourself in a situation where you see some odd behaviour from using the "Disable Known Folders" GPO, this bit of VB should allow you to restore the folders that you require into the user's environment.