├── .gitignore ├── README.md ├── VisualStudioHelpDownloader2012 ├── Docs │ └── screenshot.JPG ├── VisualStudioHelpDownloader.sln ├── VisualStudioHelpDownloader.vssscc └── VisualStudioHelpDownloader2012 │ ├── AuthenticodeTools.cs │ ├── Book.cs │ ├── BookGroup.cs │ ├── Downloader.cs │ ├── Extensions.cs │ ├── HelpIndexManager.cs │ ├── ItemBase.cs │ ├── Locale.cs │ ├── MainForm.Designer.cs │ ├── MainForm.cs │ ├── MainForm.resx │ ├── Package.cs │ ├── Program.cs │ ├── Properties │ ├── App.ico │ ├── App.manifest │ ├── AssemblyInfo.cs │ └── Default.snk │ ├── VisualStudioHelpDownloader.csproj │ ├── VisualStudioHelpDownloader.csproj.vspscc │ └── app.config └── docs ├── Home.md └── Home_screenshot.JPG /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/visualstudio 3 | 4 | ### VisualStudio ### 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | ## 8 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 9 | 10 | # User-specific files 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | 16 | # User-specific files (MonoDevelop/Xamarin Studio) 17 | *.userprefs 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | bld/ 27 | [Bb]in/ 28 | [Oo]bj/ 29 | [Ll]og/ 30 | 31 | # Visual Studio 2015 cache/options directory 32 | .vs/ 33 | # Uncomment if you have tasks that create the project's static files in wwwroot 34 | #wwwroot/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # .NET Core 50 | project.lock.json 51 | project.fragment.lock.json 52 | artifacts/ 53 | **/Properties/launchSettings.json 54 | 55 | *_i.c 56 | *_p.c 57 | *_i.h 58 | *.ilk 59 | *.meta 60 | *.obj 61 | *.pch 62 | *.pdb 63 | *.pgc 64 | *.pgd 65 | *.rsp 66 | *.sbr 67 | *.tlb 68 | *.tli 69 | *.tlh 70 | *.tmp 71 | *.tmp_proj 72 | *.log 73 | *.vspscc 74 | *.vssscc 75 | .builds 76 | *.pidb 77 | *.svclog 78 | *.scc 79 | 80 | # Chutzpah Test files 81 | _Chutzpah* 82 | 83 | # Visual C++ cache files 84 | ipch/ 85 | *.aps 86 | *.ncb 87 | *.opendb 88 | *.opensdf 89 | *.sdf 90 | *.cachefile 91 | *.VC.db 92 | *.VC.VC.opendb 93 | 94 | # Visual Studio profiler 95 | *.psess 96 | *.vsp 97 | *.vspx 98 | *.sap 99 | 100 | # TFS 2012 Local Workspace 101 | $tf/ 102 | 103 | # Guidance Automation Toolkit 104 | *.gpState 105 | 106 | # ReSharper is a .NET coding add-in 107 | _ReSharper*/ 108 | *.[Rr]e[Ss]harper 109 | *.DotSettings.user 110 | 111 | # JustCode is a .NET coding add-in 112 | .JustCode 113 | 114 | # TeamCity is a build add-in 115 | _TeamCity* 116 | 117 | # DotCover is a Code Coverage Tool 118 | *.dotCover 119 | 120 | # Visual Studio code coverage results 121 | *.coverage 122 | *.coveragexml 123 | 124 | # NCrunch 125 | _NCrunch_* 126 | .*crunch*.local.xml 127 | nCrunchTemp_* 128 | 129 | # MightyMoose 130 | *.mm.* 131 | AutoTest.Net/ 132 | 133 | # Web workbench (sass) 134 | .sass-cache/ 135 | 136 | # Installshield output folder 137 | [Ee]xpress/ 138 | 139 | # DocProject is a documentation generator add-in 140 | DocProject/buildhelp/ 141 | DocProject/Help/*.HxT 142 | DocProject/Help/*.HxC 143 | DocProject/Help/*.hhc 144 | DocProject/Help/*.hhk 145 | DocProject/Help/*.hhp 146 | DocProject/Help/Html2 147 | DocProject/Help/html 148 | 149 | # Click-Once directory 150 | publish/ 151 | 152 | # Publish Web Output 153 | *.[Pp]ublish.xml 154 | *.azurePubxml 155 | # TODO: Comment the next line if you want to checkin your web deploy settings 156 | # but database connection strings (with potential passwords) will be unencrypted 157 | *.pubxml 158 | *.publishproj 159 | 160 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 161 | # checkin your Azure Web App publish settings, but sensitive information contained 162 | # in these scripts will be unencrypted 163 | PublishScripts/ 164 | 165 | # NuGet Packages 166 | *.nupkg 167 | # The packages folder can be ignored because of Package Restore 168 | **/packages/* 169 | # except build/, which is used as an MSBuild target. 170 | !**/packages/build/ 171 | # Uncomment if necessary however generally it will be regenerated when needed 172 | #!**/packages/repositories.config 173 | # NuGet v3's project.json files produces more ignorable files 174 | *.nuget.props 175 | *.nuget.targets 176 | 177 | # Microsoft Azure Build Output 178 | csx/ 179 | *.build.csdef 180 | 181 | # Microsoft Azure Emulator 182 | ecf/ 183 | rcf/ 184 | 185 | # Windows Store app package directories and files 186 | AppPackages/ 187 | BundleArtifacts/ 188 | Package.StoreAssociation.xml 189 | _pkginfo.txt 190 | 191 | # Visual Studio cache files 192 | # files ending in .cache can be ignored 193 | *.[Cc]ache 194 | # but keep track of directories ending in .cache 195 | !*.[Cc]ache/ 196 | 197 | # Others 198 | ClientBin/ 199 | ~$* 200 | *~ 201 | *.dbmdl 202 | *.dbproj.schemaview 203 | *.jfm 204 | *.pfx 205 | *.publishsettings 206 | orleans.codegen.cs 207 | 208 | # Since there are multiple workflows, uncomment next line to ignore bower_components 209 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 210 | #bower_components/ 211 | 212 | # RIA/Silverlight projects 213 | Generated_Code/ 214 | 215 | # Backup & report files from converting an old project file 216 | # to a newer Visual Studio version. Backup files are not needed, 217 | # because we have git ;-) 218 | _UpgradeReport_Files/ 219 | Backup*/ 220 | UpgradeLog*.XML 221 | UpgradeLog*.htm 222 | 223 | # SQL Server files 224 | *.mdf 225 | *.ldf 226 | *.ndf 227 | 228 | # Business Intelligence projects 229 | *.rdl.data 230 | *.bim.layout 231 | *.bim_*.settings 232 | 233 | # Microsoft Fakes 234 | FakesAssemblies/ 235 | 236 | # GhostDoc plugin setting file 237 | *.GhostDoc.xml 238 | 239 | # Node.js Tools for Visual Studio 240 | .ntvs_analysis.dat 241 | node_modules/ 242 | 243 | # Typescript v1 declaration files 244 | typings/ 245 | 246 | # Visual Studio 6 build log 247 | *.plg 248 | 249 | # Visual Studio 6 workspace options file 250 | *.opt 251 | 252 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 253 | *.vbw 254 | 255 | # Visual Studio LightSwitch build output 256 | **/*.HTMLClient/GeneratedArtifacts 257 | **/*.DesktopClient/GeneratedArtifacts 258 | **/*.DesktopClient/ModelManifest.xml 259 | **/*.Server/GeneratedArtifacts 260 | **/*.Server/ModelManifest.xml 261 | _Pvt_Extensions 262 | 263 | # Paket dependency manager 264 | .paket/paket.exe 265 | paket-files/ 266 | 267 | # FAKE - F# Make 268 | .fake/ 269 | 270 | # JetBrains Rider 271 | .idea/ 272 | *.sln.iml 273 | 274 | # CodeRush 275 | .cr/ 276 | 277 | # Python Tools for Visual Studio (PTVS) 278 | __pycache__/ 279 | *.pyc 280 | 281 | # Cake - Uncomment if you are using it 282 | # tools/** 283 | # !tools/packages.config 284 | 285 | # Telerik's JustMock configuration file 286 | *.jmconfig 287 | 288 | # BizTalk build output 289 | *.btp.cs 290 | *.btm.cs 291 | *.odx.cs 292 | *.xsd.cs 293 | 294 | # End of https://www.gitignore.io/api/visualstudio -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Visual Studio Help Downloader 2 | 3 | Moved from Codeplex https://vshd.codeplex.com/ 4 | 5 | ## Project Description 6 | Tool for downloading the Visual Studio 2012/2013/2015/2017 help packages for offline use 7 | 8 | ## Overview 9 | 10 | Allows Visual Studio 2012/2013/2015/2017 packages to be downloaded to an offline cache location before importing them into Microsoft Help Viewer 2.0/2.1/2.2/2.3. 11 | 12 | If the cache is kept following the import, then only changes will be downloaded on subsequent occasions. 13 | 14 | ![Alt text](docs/Home_screenshot.JPG "Screenshot") 15 | 16 | ## Quick Guide 17 | 18 | * Select the version of Visual Studio to download the help for 19 | * Select your language from the drop down list 20 | * Press "Load Books" to retrieve the list of available books. Books that are already in the cache (partially or fully) will automatically be checked. Note that because packages are shared between different books, you may get extra items checked automatically. 21 | * Check (or uncheck) book that you want to download. The "Download Size" and "Num Downloads" columns indicate an approximate amount of data that needs to be downloaded for the book based on what is already in the cache. 22 | * Press "Download" to start downloading the requested books. Packages for books no longer selected will be deleted at this point. 23 | * When the download is complete, import the new books into "Microsoft Help Viewer" using the "Manage Content" tab. 24 | 25 | ## Credits 26 | 27 | This project is based on (and shares some code with) the earlier project [url:Visual Studio Help Downloader|http://vshd.codeplex.com/] by [url:nop|http://www.codeplex.com/site/users/view/nop]. 28 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/Docs/screenshot.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickdalt/VSHD/5119cb6945cbecb8c6ae269399d900d5039cb79b/VisualStudioHelpDownloader2012/Docs/screenshot.JPG -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2002 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisualStudioHelpDownloader", "VisualStudioHelpDownloader2012\VisualStudioHelpDownloader.csproj", "{2AE1E505-92AB-40DD-86D9-E47B307CEC94}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9431BA4B-D228-4CCD-88B7-7621C0DB4AA5}" 9 | ProjectSection(SolutionItems) = preProject 10 | ..\README.md = ..\README.md 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {2AE1E505-92AB-40DD-86D9-E47B307CEC94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {2AE1E505-92AB-40DD-86D9-E47B307CEC94}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {2AE1E505-92AB-40DD-86D9-E47B307CEC94}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {2AE1E505-92AB-40DD-86D9-E47B307CEC94}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {032C2CE1-6CC2-4DC7-BC35-CCE49349DDD3} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader.vssscc: -------------------------------------------------------------------------------- 1 | "" 2 | { 3 | "FILE_VERSION" = "9237" 4 | "ENLISTMENT_CHOICE" = "NEVER" 5 | "PROJECT_FILE_RELATIVE_PATH" = "" 6 | "NUMBER_OF_EXCLUDED_FILES" = "0" 7 | "ORIGINAL_PROJECT_FILE_PATH" = "" 8 | "NUMBER_OF_NESTED_PROJECTS" = "0" 9 | "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT" 10 | } 11 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/AuthenticodeTools.cs: -------------------------------------------------------------------------------- 1 | // Taken from https://stackoverflow.com/questions/6596327/how-to-check-if-a-file-is-signed-in-c 2 | 3 | namespace VisualStudioHelpDownloader2012 4 | { 5 | using System; 6 | using System.Runtime.InteropServices; 7 | internal static class AuthenticodeTools 8 | { 9 | [DllImport("Wintrust.dll", PreserveSig = true, SetLastError = false)] 10 | private static extern uint WinVerifyTrust(IntPtr hWnd, IntPtr pgActionID, IntPtr pWinTrustData); 11 | private static uint WinVerifyTrust(string fileName) 12 | { 13 | Guid wintrust_action_generic_verify_v2 = new Guid("{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}"); 14 | uint result = 0; 15 | using (WINTRUST_FILE_INFO fileInfo = new WINTRUST_FILE_INFO(fileName, Guid.Empty)) 16 | using (UnmanagedPointer guidPtr = new UnmanagedPointer(Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid))), AllocMethod.HGlobal)) 17 | using (UnmanagedPointer wvtDataPtr = new UnmanagedPointer(Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINTRUST_DATA))), AllocMethod.HGlobal)) 18 | { 19 | WINTRUST_DATA data = new WINTRUST_DATA(fileInfo); 20 | IntPtr pGuid = guidPtr; 21 | IntPtr pData = wvtDataPtr; 22 | Marshal.StructureToPtr(wintrust_action_generic_verify_v2, pGuid, true); 23 | Marshal.StructureToPtr(data, pData, true); 24 | result = WinVerifyTrust(IntPtr.Zero, pGuid, pData); 25 | } 26 | 27 | return result; 28 | } 29 | 30 | public static bool IsTrusted(string fileName) 31 | { 32 | return WinVerifyTrust(fileName) == 0; 33 | } 34 | } 35 | 36 | internal struct WINTRUST_FILE_INFO : IDisposable 37 | { 38 | public WINTRUST_FILE_INFO(string fileName, Guid subject) 39 | { 40 | cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_FILE_INFO)); 41 | pcwszFilePath = fileName; 42 | if (subject != Guid.Empty) 43 | { 44 | pgKnownSubject = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid))); 45 | Marshal.StructureToPtr(subject, pgKnownSubject, true); 46 | } 47 | else 48 | { 49 | pgKnownSubject = IntPtr.Zero; 50 | } 51 | 52 | hFile = IntPtr.Zero; 53 | } 54 | 55 | public uint cbStruct; 56 | [MarshalAs(UnmanagedType.LPTStr)] 57 | public string pcwszFilePath; 58 | public IntPtr hFile; 59 | public IntPtr pgKnownSubject; 60 | 61 | public void Dispose() 62 | { 63 | Dispose(true); 64 | } 65 | 66 | private void Dispose(bool disposing) 67 | { 68 | if (pgKnownSubject != IntPtr.Zero) 69 | { 70 | Marshal.DestroyStructure(pgKnownSubject, typeof(Guid)); 71 | Marshal.FreeHGlobal(pgKnownSubject); 72 | } 73 | } 74 | } 75 | 76 | enum AllocMethod 77 | { 78 | HGlobal, 79 | CoTaskMem 80 | }; 81 | enum UnionChoice 82 | { 83 | File = 1, 84 | Catalog, 85 | Blob, 86 | Signer, 87 | Cert 88 | }; 89 | enum UiChoice 90 | { 91 | All = 1, 92 | NoUI, 93 | NoBad, 94 | NoGood 95 | }; 96 | enum RevocationCheckFlags 97 | { 98 | None = 0, 99 | WholeChain 100 | }; 101 | enum StateAction 102 | { 103 | Ignore = 0, 104 | Verify, 105 | Close, 106 | AutoCache, 107 | AutoCacheFlush 108 | }; 109 | enum TrustProviderFlags 110 | { 111 | UseIE4Trust = 1, 112 | NoIE4Chain = 2, 113 | NoPolicyUsage = 4, 114 | RevocationCheckNone = 16, 115 | RevocationCheckEndCert = 32, 116 | RevocationCheckChain = 64, 117 | RecovationCheckChainExcludeRoot = 128, 118 | Safer = 256, 119 | HashOnly = 512, 120 | UseDefaultOSVerCheck = 1024, 121 | LifetimeSigning = 2048 122 | }; 123 | enum UIContext 124 | { 125 | Execute = 0, 126 | Install 127 | }; 128 | 129 | [StructLayout(LayoutKind.Sequential)] 130 | internal struct WINTRUST_DATA : IDisposable 131 | { 132 | public WINTRUST_DATA(WINTRUST_FILE_INFO fileInfo) 133 | { 134 | cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_DATA)); 135 | pInfoStruct = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINTRUST_FILE_INFO))); 136 | Marshal.StructureToPtr(fileInfo, pInfoStruct, false); 137 | dwUnionChoice = UnionChoice.File; 138 | pPolicyCallbackData = IntPtr.Zero; 139 | pSIPCallbackData = IntPtr.Zero; 140 | dwUIChoice = UiChoice.NoUI; 141 | fdwRevocationChecks = RevocationCheckFlags.None; 142 | dwStateAction = StateAction.Ignore; 143 | hWVTStateData = IntPtr.Zero; 144 | pwszURLReference = IntPtr.Zero; 145 | dwProvFlags = TrustProviderFlags.Safer; 146 | dwUIContext = UIContext.Execute; 147 | } 148 | 149 | public uint cbStruct; 150 | public IntPtr pPolicyCallbackData; 151 | public IntPtr pSIPCallbackData; 152 | public UiChoice dwUIChoice; 153 | public RevocationCheckFlags fdwRevocationChecks; 154 | public UnionChoice dwUnionChoice; 155 | public IntPtr pInfoStruct; 156 | public StateAction dwStateAction; 157 | public IntPtr hWVTStateData; 158 | private IntPtr pwszURLReference; 159 | public TrustProviderFlags dwProvFlags; 160 | public UIContext dwUIContext; 161 | 162 | public void Dispose() 163 | { 164 | Dispose(true); 165 | } 166 | 167 | private void Dispose(bool disposing) 168 | { 169 | if (dwUnionChoice == UnionChoice.File) 170 | { 171 | using (WINTRUST_FILE_INFO info = new WINTRUST_FILE_INFO()) 172 | { 173 | Marshal.PtrToStructure(pInfoStruct, info); 174 | } 175 | 176 | Marshal.DestroyStructure(pInfoStruct, typeof(WINTRUST_FILE_INFO)); 177 | } 178 | 179 | Marshal.FreeHGlobal(pInfoStruct); 180 | } 181 | } 182 | 183 | internal sealed class UnmanagedPointer : IDisposable 184 | { 185 | private IntPtr m_ptr; 186 | private AllocMethod m_meth; 187 | 188 | internal UnmanagedPointer(IntPtr ptr, AllocMethod method) 189 | { 190 | m_meth = method; 191 | m_ptr = ptr; 192 | } 193 | 194 | ~UnmanagedPointer() 195 | { 196 | Dispose(false); 197 | } 198 | 199 | private void Dispose(bool disposing) 200 | { 201 | if (m_ptr != IntPtr.Zero) 202 | { 203 | if (m_meth == AllocMethod.HGlobal) 204 | { 205 | Marshal.FreeHGlobal(m_ptr); 206 | } 207 | else if (m_meth == AllocMethod.CoTaskMem) 208 | { 209 | Marshal.FreeCoTaskMem(m_ptr); 210 | } 211 | 212 | m_ptr = IntPtr.Zero; 213 | } 214 | 215 | if (disposing) 216 | { 217 | GC.SuppressFinalize(this); 218 | } 219 | } 220 | 221 | public void Dispose() 222 | { 223 | Dispose(true); 224 | } 225 | 226 | public static implicit operator IntPtr(UnmanagedPointer ptr) 227 | { 228 | return ptr.m_ptr; 229 | } 230 | } 231 | } -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Book.cs: -------------------------------------------------------------------------------- 1 | namespace VisualStudioHelpDownloader2012 2 | { 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | 6 | /// 7 | /// Represents an MSDN book 8 | /// 9 | internal sealed class Book : ItemBase 10 | { 11 | /// 12 | /// Gets or sets the display category for the book 13 | /// 14 | public string Category 15 | { 16 | get; 17 | set; 18 | } 19 | 20 | /// 21 | /// Gets or sets a value indicating whether the a download of the book has been requested. 22 | /// 23 | public bool Wanted 24 | { 25 | get; 26 | set; 27 | } 28 | 29 | /// 30 | /// Gets or sets the collection of packages associated with the book 31 | /// 32 | public ICollection Packages 33 | { 34 | get; 35 | set; 36 | } 37 | 38 | /// 39 | /// Create a file name for the book index file 40 | /// 41 | /// 42 | /// A string containing the file name 43 | /// 44 | public string CreateFileName() 45 | { 46 | return string.Format( CultureInfo.InvariantCulture, "Book-{0}-{1}.xml", Code, Locale.Name ); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/BookGroup.cs: -------------------------------------------------------------------------------- 1 | namespace VisualStudioHelpDownloader2012 2 | { 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | 6 | /// 7 | /// Represents an MSDN book group 8 | /// 9 | internal sealed class BookGroup : ItemBase 10 | { 11 | /// 12 | /// Gets or sets the books associated with the book group 13 | /// 14 | public ICollection Books 15 | { 16 | get; 17 | set; 18 | } 19 | 20 | /// 21 | /// Create a file name for the book group index file 22 | /// 23 | /// 24 | /// A string containing the file name 25 | /// 26 | public string CreateFileName() 27 | { 28 | return string.Format( CultureInfo.InvariantCulture, "product-{0}.xml", Code ); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Downloader.cs: -------------------------------------------------------------------------------- 1 | namespace VisualStudioHelpDownloader2012 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Globalization; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Net; 10 | using System.Reflection; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | using System.Windows.Forms; 14 | using System.Xml; 15 | using System.Xml.Linq; 16 | 17 | /// 18 | /// Class to perfom the downloading of the MSDN book information and the books themselves 19 | /// 20 | internal sealed class Downloader : IDisposable 21 | { 22 | /// 23 | /// The http client used for downloading 24 | /// 25 | private WebClient client = new WebClient(); 26 | 27 | /// 28 | /// Initializes a new instance of the class. 29 | /// 30 | /// 31 | /// If the settings cannot be loaded 32 | /// 33 | /// 34 | /// If the data cannot be processed 35 | /// 36 | public Downloader() 37 | { 38 | client.BaseAddress = "https://services.mtps.microsoft.com/serviceapi/"; 39 | 40 | string directory = Path.GetDirectoryName( Application.ExecutablePath ); 41 | if ( directory != null ) 42 | { 43 | string settingsFile = Path.Combine( 44 | directory, 45 | string.Format( CultureInfo.InvariantCulture, "{0}.xml", Assembly.GetEntryAssembly().GetName().Name ) ); 46 | 47 | if ( File.Exists( settingsFile ) ) 48 | { 49 | XElement element = XDocument.Load( settingsFile ).Root; 50 | if ( element != null ) 51 | { 52 | element = element.Elements().Single( x => x.Name.LocalName?.Equals( "proxy", StringComparison.OrdinalIgnoreCase ) ?? false ); 53 | WebProxy proxy = new WebProxy( element.Attributes().Single( x => x.Name.LocalName?.Equals( "address", StringComparison.OrdinalIgnoreCase ) ?? false ).Value ) 54 | { 55 | Credentials = 56 | new NetworkCredential( 57 | element.Attributes().Single( x => x.Name.LocalName?.Equals( "login", StringComparison.OrdinalIgnoreCase ) ?? false ).Value, 58 | element.Attributes().Single( x => x.Name.LocalName?.Equals( "password", StringComparison.OrdinalIgnoreCase ) ?? false ).Value, 59 | element.Attributes().Single( x => x.Name.LocalName?.Equals( "domain", StringComparison.OrdinalIgnoreCase ) ?? false ).Value ) 60 | }; 61 | 62 | client.Proxy = proxy; 63 | } 64 | else 65 | { 66 | throw new XmlException( "Missing root element" ); 67 | } 68 | } 69 | } 70 | } 71 | 72 | /// 73 | /// Finalizes an instance of the class. 74 | /// 75 | ~Downloader() 76 | { 77 | Dispose( false ); 78 | } 79 | 80 | /// 81 | /// Check the current caching status of the packages so that the required downloads can be 82 | /// determined 83 | /// 84 | /// 85 | /// The collection of bookGroups to check the packages for 86 | /// 87 | /// 88 | /// The directory where the packages are locally cached 89 | /// 90 | /// 91 | /// If bookGroups or cachePath are null 92 | /// 93 | public static void CheckPackagesStates( ICollection bookGroups, string cachePath ) 94 | { 95 | if ( bookGroups == null ) 96 | { 97 | throw new ArgumentNullException( "bookGroups" ); 98 | } 99 | 100 | if ( cachePath == null ) 101 | { 102 | throw new ArgumentNullException( "cachePath" ); 103 | } 104 | 105 | foreach ( BookGroup bookGroup in bookGroups ) 106 | { 107 | foreach ( Book book in bookGroup.Books ) 108 | { 109 | foreach ( Package package in book.Packages ) 110 | { 111 | string packagePath = Path.Combine( cachePath, "Packages", package.Name + ".cab" ); 112 | FileInfo packageFile = new FileInfo( packagePath ); 113 | if ( packageFile.Exists ) 114 | { 115 | if ( packageFile.LastWriteTime == package.LastModified && packageFile.Length == package.Size ) 116 | { 117 | package.State = PackageState.OutOfDate; 118 | } 119 | else 120 | { 121 | package.State = PackageState.Ready; 122 | } 123 | } 124 | else 125 | { 126 | package.State = PackageState.NotDownloaded; 127 | } 128 | } 129 | } 130 | } 131 | } 132 | 133 | /// 134 | /// The dispose. 135 | /// 136 | public void Dispose() 137 | { 138 | Dispose( true ); 139 | GC.SuppressFinalize( this ); 140 | } 141 | 142 | /// 143 | /// Retrieves a collection of locales available to download the help for 144 | /// 145 | /// 146 | /// Collection of Locales available 147 | /// 148 | /// 149 | /// If the data cannot be downloaded 150 | /// 151 | /// 152 | /// If the data cannot be processed 153 | /// 154 | /// 155 | /// If the data cannot be processed 156 | /// 157 | public async Task> LoadAvailableLocalesAsync( string vsVersion ) 158 | { 159 | string catalogPath = string.Format("catalogs/{0}", vsVersion); 160 | Debug.Print("Downloading locales list from {0}{1}", client.BaseAddress, catalogPath); 161 | ICollection locales = HelpIndexManager.LoadLocales( await client.DownloadDataTaskAsync( catalogPath ) ); 162 | return locales; 163 | } 164 | 165 | /// 166 | /// Download information about the available books for the selected locale 167 | /// 168 | /// 169 | /// The relative path to the book catalog download location 170 | /// 171 | /// 172 | /// Collection of available bookGroups 173 | /// 174 | /// 175 | /// If path is null or empty 176 | /// 177 | /// 178 | /// If the data cannot be downloaded 179 | /// 180 | /// 181 | /// If the data cannot be processed 182 | /// 183 | /// 184 | /// If the data cannot be processed 185 | /// 186 | public async Task> LoadBooksInformationAsync( string path ) 187 | { 188 | if ( path == null ) 189 | { 190 | throw new ArgumentNullException( "path" ); 191 | } 192 | 193 | Debug.Print("Downloading books list from {0}{1}", client.BaseAddress, path); 194 | return HelpIndexManager.LoadBooks( await client.DownloadDataTaskAsync( path ) ); 195 | } 196 | 197 | /// 198 | /// Download the requested books and create the appropriate index files for MSDN HelpViewer 199 | /// 200 | /// 201 | /// The collection of bookGroups to with the books to download indicated by the Book.Wanted 202 | /// property 203 | /// 204 | /// 205 | /// The path where the downloaded books are cached 206 | /// 207 | /// 208 | /// Interface used to report the percentage progress back to the GUI 209 | /// 210 | /// 211 | /// If any of the parameters are null 212 | /// 213 | /// 214 | /// If the data cannot be downloaded 215 | /// 216 | /// 217 | /// If the data cannot be processed 218 | /// 219 | /// 220 | /// If there was a problem reading or writing to the cache directory 221 | /// 222 | /// 223 | /// If the user does not have permission to write to the cache directory 224 | /// 225 | /// 226 | /// If the data cannot be processed 227 | /// 228 | public async Task DownloadBooksAsync( ICollection bookGroups, string cachePath, IProgress progress ) 229 | { 230 | if ( bookGroups == null ) 231 | { 232 | throw new ArgumentNullException( "bookGroups" ); 233 | } 234 | 235 | if ( cachePath == null ) 236 | { 237 | throw new ArgumentNullException( "cachePath" ); 238 | } 239 | 240 | if ( cachePath == null ) 241 | { 242 | throw new ArgumentNullException( "progress" ); 243 | } 244 | 245 | // Create cachePath 246 | string targetDirectory = Path.Combine( cachePath, "Packages" ); 247 | 248 | if ( !Directory.Exists( targetDirectory ) ) 249 | { 250 | Directory.CreateDirectory( targetDirectory ); 251 | } 252 | 253 | // Cleanup index files 254 | Directory.GetFiles( cachePath, "*.msha" ).ForEach( File.Delete ); 255 | Directory.GetFiles( cachePath, "*.xml" ).ForEach( File.Delete ); 256 | 257 | // Creating setup indexes 258 | File.WriteAllText( 259 | Path.Combine( cachePath, "HelpContentSetup.msha" ), HelpIndexManager.CreateSetupIndex( bookGroups ), Encoding.UTF8 ); 260 | 261 | // Create list of unique packages for possible download and write the book group and 262 | // book index files 263 | Dictionary packages = new Dictionary(); 264 | foreach ( BookGroup bookGroup in bookGroups ) 265 | { 266 | File.WriteAllText( 267 | Path.Combine( cachePath, bookGroup.CreateFileName() ), 268 | HelpIndexManager.CreateBookGroupBooksIndex( bookGroup ), 269 | Encoding.UTF8 ); 270 | Debug.Print( "BookGroup: {0}", bookGroup.Name ); 271 | foreach ( Book book in bookGroup.Books ) 272 | { 273 | if ( book.Wanted ) 274 | { 275 | Debug.Print( " Book: {0}", book.Name ); 276 | File.WriteAllText( 277 | Path.Combine( cachePath, book.CreateFileName() ), 278 | HelpIndexManager.CreateBookPackagesIndex( bookGroup, book ), 279 | Encoding.UTF8 ); 280 | foreach ( Package package in book.Packages ) 281 | { 282 | string name = package.Name.ToUpperInvariant(); 283 | Debug.Print( " Package: {0}", name ); 284 | if ( !packages.ContainsKey( name ) ) 285 | { 286 | packages.Add( name, package ); 287 | } 288 | } 289 | } 290 | } 291 | } 292 | 293 | // Cleanup old files 294 | foreach ( string file in Directory.GetFiles( targetDirectory, "*.cab" ) ) 295 | { 296 | string fileName = Path.GetFileNameWithoutExtension( file ); 297 | if ( !string.IsNullOrEmpty( fileName ) ) 298 | { 299 | fileName = fileName.ToUpperInvariant(); 300 | if ( !packages.ContainsKey( fileName ) ) 301 | { 302 | File.Delete( file ); 303 | } 304 | } 305 | } 306 | 307 | // Download the packages 308 | int packagesCountCurrent = 0; 309 | client.BaseAddress = "https://packages.mtps.microsoft.com/"; 310 | foreach ( Package package in packages.Values ) 311 | { 312 | string targetFileName = Path.Combine( targetDirectory, package.CreateFileName() ); 313 | if ( package.State == PackageState.NotDownloaded || package.State == PackageState.OutOfDate ) 314 | { 315 | Debug.Print( " Downloading : '{0}' to '{1}'", package.Link, targetFileName ); 316 | await client.DownloadFileTaskAsync( package.Link, targetFileName ); 317 | 318 | if (AuthenticodeTools.IsTrusted(targetFileName)) 319 | { 320 | File.SetCreationTime(targetFileName, package.LastModified); 321 | File.SetLastAccessTime(targetFileName, package.LastModified); 322 | File.SetLastWriteTime(targetFileName, package.LastModified); 323 | } 324 | else 325 | { 326 | Debug.Print("The signature on '{0}' is not valid - deleting"); 327 | File.Delete(targetFileName); 328 | 329 | throw new InvalidDataException($"The signature on '{targetFileName}' is not valid - deleting"); 330 | } 331 | } 332 | 333 | progress.Report( 100 * ++packagesCountCurrent / packages.Count ); 334 | } 335 | } 336 | 337 | /// 338 | /// Standard IDispose pattern 339 | /// 340 | /// 341 | /// true if called by Dispose, false if called from destructor 342 | /// 343 | private void Dispose( bool disposing ) 344 | { 345 | if ( disposing ) 346 | { 347 | if ( client != null ) 348 | { 349 | client.Dispose(); 350 | client = null; 351 | } 352 | } 353 | } 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Extensions.cs: -------------------------------------------------------------------------------- 1 | namespace VisualStudioHelpDownloader2012 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | /// 7 | /// Help class with extension methods. 8 | /// 9 | internal static class Extensions 10 | { 11 | /// 12 | /// Perform action on each element from source. 13 | /// 14 | /// 15 | /// Element type. 16 | /// 17 | /// 18 | /// Elements source. 19 | /// 20 | /// 21 | /// Action to perform. 22 | /// 23 | public static void ForEach( this IEnumerable source, Action action ) 24 | { 25 | if ( null != action ) 26 | { 27 | foreach ( T x in source ) 28 | { 29 | action( x ); 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/HelpIndexManager.cs: -------------------------------------------------------------------------------- 1 | namespace VisualStudioHelpDownloader2012 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Xml; 9 | using System.Xml.Linq; 10 | 11 | /// 12 | /// Helper class for parsing and creating documents with help indexes. 13 | /// 14 | internal static class HelpIndexManager 15 | { 16 | /// 17 | /// Find the available locales for the help collections 18 | /// 19 | /// 20 | /// The page data downloaded containing the locale catalog data 21 | /// 22 | /// 23 | /// Collection of locales in which the help collection may be downloaded 24 | /// 25 | /// 26 | /// If data is null 27 | /// 28 | /// 29 | /// If there was a error processing the xml data 30 | /// 31 | /// 32 | /// If there was a error processing the xml data 33 | /// 34 | public static ICollection LoadLocales( byte[] data ) 35 | { 36 | XDocument document; 37 | List result = new List(); 38 | 39 | if ( data == null ) 40 | { 41 | throw new ArgumentNullException( "data" ); 42 | } 43 | 44 | // This would normally be loaded directly from the bytes but the MS server 45 | // seems to be returning junk whitespace at the start that cant be loaded 46 | // directly as XML 47 | document = XDocument.Parse( System.Text.Encoding.UTF8.GetString(data).TrimStart() ); 48 | 49 | if ( document.Root != null ) 50 | { 51 | IEnumerable query = 52 | document.Root.Elements() 53 | .Where( x => x.GetClassName()?.Equals( "catalogLocales", StringComparison.OrdinalIgnoreCase ) ?? false ) 54 | .Take( 1 ) 55 | .Single() 56 | .Elements() 57 | .Where( x => x.GetClassName()?.Equals( "catalog-locale-list", StringComparison.OrdinalIgnoreCase ) ?? false ) 58 | .Take( 1 ) 59 | .Single() 60 | .Elements() 61 | .Where( x => x.GetClassName()?.Equals( "catalog-locale", StringComparison.OrdinalIgnoreCase ) ?? false ); 62 | 63 | result.AddRange( 64 | query.Select( 65 | x => 66 | new Locale 67 | { 68 | Code = x.GetChildClassValue( "locale" ), 69 | CatalogLink = x.GetChildClassAttributeValue( "locale-link", "href" ) 70 | } ) ); 71 | } 72 | else 73 | { 74 | throw new XmlException( "Missing document root" ); 75 | } 76 | 77 | return result; 78 | } 79 | 80 | /// 81 | /// Find the available book groups, books, and packages for the selected language 82 | /// 83 | /// 84 | /// The page data downloaded containing the book data 85 | /// 86 | /// 87 | /// Collection of book groups for the help collection 88 | /// 89 | /// 90 | /// If data is null 91 | /// 92 | /// 93 | /// If there was a error processing the xml data 94 | /// 95 | /// 96 | /// If there was a error processing the xml data 97 | /// 98 | public static ICollection LoadBooks( byte[] data ) 99 | { 100 | XDocument document; 101 | using ( MemoryStream stream = new MemoryStream( data ) ) 102 | { 103 | document = XDocument.Load( stream ); 104 | } 105 | 106 | if ( data == null ) 107 | { 108 | throw new ArgumentNullException( "data" ); 109 | } 110 | 111 | List result = new List(); 112 | 113 | if ( document.Root != null ) 114 | { 115 | IEnumerable groups = 116 | document.Root.Elements() 117 | .Where( x => x.GetClassName()?.Equals( "book-list", StringComparison.OrdinalIgnoreCase ) ?? false ) 118 | .Take( 1 ) 119 | .Single() 120 | .Elements() 121 | .Where( x => x.GetClassName()?.Equals( "book-groups", StringComparison.OrdinalIgnoreCase ) ?? false ) 122 | .Take( 1 ) 123 | .Single() 124 | .Elements() 125 | .Where( x => x.GetClassName()?.Equals( "book-group", StringComparison.OrdinalIgnoreCase ) ?? false ); 126 | foreach ( XElement group in groups ) 127 | { 128 | BookGroup bookGroup = new BookGroup 129 | { 130 | Name = group.GetChildClassValue( "name" ), 131 | Code = group.GetChildClassValue( "id" ), 132 | Books = new List(), 133 | Locale = new Locale { Code = "en-us" }, 134 | Description = group.GetChildClassValue( "name" ) 135 | }; 136 | 137 | result.Add( bookGroup ); 138 | 139 | IEnumerable books = group.Elements().Where( x => x.GetClassName()?.Equals( "book", StringComparison.OrdinalIgnoreCase ) ?? false ); 140 | foreach ( XElement book in books ) 141 | { 142 | XElement path = 143 | book.Elements() 144 | .Where( x => x.GetClassName()?.Equals( "properties", StringComparison.OrdinalIgnoreCase ) ?? false ) 145 | .Take( 1 ) 146 | .Single() 147 | .Elements() 148 | .Where( x => x.GetClassName()?.Equals( "paths", StringComparison.OrdinalIgnoreCase ) ?? false ) 149 | .Take( 1 ) 150 | .Single() 151 | .Elements() 152 | .Where( x => x.GetClassName()?.Equals( "path", StringComparison.OrdinalIgnoreCase ) ?? false ) 153 | .Take( 1 ) 154 | .Single(); 155 | string bookPath = path.GetChildClassValue( "name" ).TrimStart( new[] { '\\' } ); 156 | 157 | Book b = new Book 158 | { 159 | Name = book.GetChildClassValue( "name" ), 160 | Description = book.GetChildClassValue( "description" ), 161 | Code = book.GetChildClassValue( "id" ), 162 | Packages = new List(), 163 | Locale = new Locale { Code = "en-us" }, 164 | Category = bookPath 165 | }; 166 | 167 | bookGroup.Name = bookPath; 168 | bookGroup.Books.Add( b ); 169 | IEnumerable packages = 170 | book.Elements() 171 | .Where( x => x.GetClassName()?.Equals( "packages", StringComparison.OrdinalIgnoreCase ) ?? false ) 172 | .Take( 1 ) 173 | .Single() 174 | .Elements() 175 | .Where( x => x.GetClassName()?.Equals( "package" , StringComparison.OrdinalIgnoreCase ) ?? false ); 176 | foreach ( XElement package in packages ) 177 | { 178 | Package p = new Package 179 | { 180 | LastModified = DateTime.Parse( package.GetChildClassValue( "last-modified" ), CultureInfo.InvariantCulture ), 181 | Link = package.GetChildClassAttributeValue( "current-link", "href" ), 182 | Tag = package.GetChildClassValue( "package-etag" ), 183 | Name = package.GetChildClassValue( "name" ), 184 | Size = long.Parse( package.GetChildClassValue( "package-size-bytes" ), CultureInfo.InvariantCulture ) 185 | }; 186 | 187 | b.Packages.Add( p ); 188 | } 189 | } 190 | } 191 | } 192 | else 193 | { 194 | throw new XmlException( "Missing document root" ); 195 | } 196 | 197 | return result; 198 | } 199 | 200 | /// 201 | /// Creates main help setup index. 202 | /// 203 | /// 204 | /// A collection of book groups to add to the index 205 | /// 206 | /// 207 | /// The xml document text 208 | /// 209 | public static string CreateSetupIndex( IEnumerable bookGroups ) 210 | { 211 | XDocument document = new XDocument( new XDeclaration( "1.0", "utf-8", null ), CreateElement( "html", null, null ) ); 212 | 213 | XElement bodyElement = CreateElement( "body", "product-list", null ); 214 | 215 | foreach ( BookGroup bookGroup in bookGroups ) 216 | { 217 | XElement productElement = CreateElement( "div", "product", null ); 218 | 219 | XElement linkElement = CreateElement( "a", "product-link", null ); 220 | linkElement.SetAttributeValue( XName.Get( "href", string.Empty ), bookGroup.CreateFileName() ); 221 | 222 | productElement.Add( 223 | CreateElement( "span", "name", bookGroup.Name ), 224 | CreateElement( "span", "locale", bookGroup.Locale.Code ), 225 | CreateElement( "span", "description", bookGroup.Description ), 226 | linkElement ); 227 | 228 | bodyElement.Add( productElement ); 229 | } 230 | 231 | if ( document.Root != null ) 232 | { 233 | document.Root.Add( bodyElement ); 234 | } 235 | 236 | return document.ToStringWithDeclaration(); 237 | } 238 | 239 | /// 240 | /// Create book group books index. 241 | /// 242 | /// 243 | /// The book group to create the index for. 244 | /// 245 | /// 246 | /// The xml document text 247 | /// 248 | public static string CreateBookGroupBooksIndex( BookGroup bookGroup ) 249 | { 250 | XDocument document = new XDocument( new XDeclaration( "1.0", "utf-8", null ), CreateElement( "html", null, null ) ); 251 | 252 | XElement headElement = CreateElement( "head", null, null ); 253 | XElement metaDateElemet = CreateElement( "meta", null, null ); 254 | metaDateElemet.SetAttributeValue( XName.Get( "http-equiv", string.Empty ), "Date" ); 255 | metaDateElemet.SetAttributeValue( XName.Get( "content", string.Empty ), DateTime.Now.ToString( "R", CultureInfo.InvariantCulture ) ); 256 | headElement.Add( metaDateElemet ); 257 | 258 | XElement bodyElement = CreateElement( "body", "product", null ); 259 | XElement detailsElement = CreateElement( "div", "details", null ); 260 | detailsElement.Add( 261 | CreateElement( "span", "name", bookGroup.Name ), 262 | CreateElement( "span", "locale", bookGroup.Locale.Code ), 263 | CreateElement( "span", "description", bookGroup.Description ) ); 264 | XElement bookListElement = CreateElement( "div", "book-list", null ); 265 | 266 | foreach ( Book book in bookGroup.Books ) 267 | { 268 | if ( book.Wanted ) 269 | { 270 | XElement bookElement = CreateElement( "div", "book", null ); 271 | 272 | XElement linkElement = CreateElement( "a", "book-link", null ); 273 | linkElement.SetAttributeValue( XName.Get( "href", string.Empty ), book.CreateFileName() ); 274 | 275 | bookElement.Add( 276 | CreateElement( "span", "name", book.Name ), 277 | CreateElement( "span", "locale", book.Locale.Code ), 278 | CreateElement( "span", "description", book.Description ), 279 | linkElement ); 280 | 281 | bookListElement.Add( bookElement ); 282 | } 283 | } 284 | 285 | bodyElement.Add( detailsElement, bookListElement ); 286 | if ( document.Root != null ) 287 | { 288 | document.Root.Add( headElement, bodyElement ); 289 | } 290 | 291 | return document.ToStringWithDeclaration(); 292 | } 293 | 294 | /// 295 | /// Create book packages index. 296 | /// 297 | /// 298 | /// The book Group associated with the book. 299 | /// 300 | /// 301 | /// The book associated with the packages 302 | /// 303 | /// 304 | /// The xml document text 305 | /// 306 | public static string CreateBookPackagesIndex( BookGroup bookGroup, Book book ) 307 | { 308 | XDocument document = new XDocument( new XDeclaration( "1.0", "utf-8", null ), CreateElement( "html", null, null ) ); 309 | 310 | XElement headElement = CreateElement( "head", null, null ); 311 | XElement metaDateElemet = CreateElement( "meta", null, null ); 312 | metaDateElemet.SetAttributeValue( XName.Get( "http-equiv", string.Empty ), "Date" ); 313 | metaDateElemet.SetAttributeValue( XName.Get( "content", string.Empty ), DateTime.Now.ToString( "R", CultureInfo.InvariantCulture ) ); 314 | headElement.Add( metaDateElemet ); 315 | 316 | XElement bodyElement = CreateElement( "body", "book", null ); 317 | XElement detailsElement = CreateElement( "div", "details", null ); 318 | 319 | XElement brandingPackageElement1 = CreateElement( "a", "branding-package-link", null ); 320 | XElement brandingPackageElement2 = CreateElement( "a", "branding-package-link", null ); 321 | XElement productLinkElement = CreateElement( "a", "product-link", null ); 322 | 323 | productLinkElement.SetAttributeValue( XName.Get( "href", string.Empty ), Uri.EscapeDataString(bookGroup.CreateFileName()) ); 324 | 325 | detailsElement.Add( 326 | CreateElement( "span", "name", book.Name ), 327 | CreateElement( "span", "description", book.Description ), 328 | CreateElement( "span", "vendor", "Microsoft" ), 329 | CreateElement( "span", "locale", book.Locale.Code ), 330 | brandingPackageElement1, 331 | brandingPackageElement2 ); 332 | 333 | XElement packageListElement = CreateElement( "div", "package-list", null ); 334 | 335 | foreach ( Package package in book.Packages ) 336 | { 337 | XElement packageElement = CreateElement( "div", "package", null ); 338 | XElement linkElement = CreateElement( "a", "current-link", null ); 339 | 340 | linkElement.SetAttributeValue( 341 | XName.Get( "href", string.Empty ), 342 | string.Format( CultureInfo.InvariantCulture, @"Packages\{0}", Uri.EscapeDataString(package.CreateFileName()) ) ); 343 | 344 | packageElement.Add( 345 | CreateElement( "span", "name", package.Name ), 346 | CreateElement( "span", "deployed", "true" ), 347 | CreateElement( "span", "package-etag", package.Tag ), 348 | CreateElement( "span", "last-modified", package.LastModified.ToUniversalTime().ToString( "O", CultureInfo.InvariantCulture ) ), 349 | linkElement ); 350 | 351 | packageListElement.Add( packageElement ); 352 | } 353 | 354 | bodyElement.Add( detailsElement, packageListElement ); 355 | if ( document.Root != null ) 356 | { 357 | document.Root.Add( headElement, bodyElement ); 358 | } 359 | 360 | return document.ToStringWithDeclaration(); 361 | } 362 | 363 | /// 364 | /// Create a new xml element 365 | /// 366 | /// 367 | /// The name of the element 368 | /// 369 | /// 370 | /// The class attribute value (may be null) 371 | /// 372 | /// 373 | /// The element content (may be null) 374 | /// 375 | /// 376 | /// The created element 377 | /// 378 | private static XElement CreateElement( string name, string className, string value ) 379 | { 380 | XElement element = new XElement( XName.Get( name, "http://www.w3.org/1999/xhtml" ) ); 381 | 382 | if ( className != null ) 383 | { 384 | element.SetAttributeValue( XName.Get( "class", string.Empty ), className ); 385 | } 386 | 387 | if ( value != null ) 388 | { 389 | element.Value = value; 390 | } 391 | 392 | return element; 393 | } 394 | 395 | /// 396 | /// Get the name of the class of an xml element 397 | /// 398 | /// 399 | /// The element to get the class name of 400 | /// 401 | /// 402 | /// The class name or null if there is no class name 403 | /// 404 | private static string GetClassName( this XElement element ) 405 | { 406 | return GetAttributeValue( element, "class" ); 407 | } 408 | 409 | /// 410 | /// The the value of an attribute of an xml element 411 | /// 412 | /// 413 | /// The element to get the attribute value from 414 | /// 415 | /// 416 | /// The name of the attrbute to query 417 | /// 418 | /// 419 | /// The attribute value or null if the attribute was not found 420 | /// 421 | private static string GetAttributeValue( this XElement element, string name ) 422 | { 423 | XAttribute attribute = element.Attribute( XName.Get( name, string.Empty ) ); 424 | 425 | return attribute == null ? null : attribute.Value; 426 | } 427 | 428 | /// 429 | /// Get the value of the first child element of the specified element with the class attribute that matched 430 | /// the specified name 431 | /// 432 | /// 433 | /// The element to get the child class value from 434 | /// 435 | /// 436 | /// The class name to find 437 | /// 438 | /// 439 | /// The value of the child class element 440 | /// 441 | /// 442 | /// If there was no child element with the class attribute 443 | /// 444 | private static string GetChildClassValue( this XElement element, string name ) 445 | { 446 | XElement result = element.Elements().Where( x => x.GetClassName()?.Equals( name, StringComparison.OrdinalIgnoreCase ) ?? false ).Take( 1 ).Single(); 447 | 448 | return null != result ? result.Value : null; 449 | } 450 | 451 | /// 452 | /// Get the value of the specified attribute of the first child element of the specified element with the 453 | /// class attribute that matched the specified name 454 | /// 455 | /// 456 | /// The element to get the child attribute value from 457 | /// 458 | /// 459 | /// The class name to find 460 | /// 461 | /// 462 | /// The attribute name to find 463 | /// 464 | /// 465 | /// The value of the attribute 466 | /// 467 | /// 468 | /// If there was no child element with the class attribute 469 | /// 470 | private static string GetChildClassAttributeValue( this XElement element, string name, string attribute ) 471 | { 472 | XElement result = element.Elements().Where( x => x.GetClassName()?.Equals( name, StringComparison.OrdinalIgnoreCase ) ?? false ).Take( 1 ).Single(); 473 | 474 | return null != result ? result.GetAttributeValue( attribute ) : null; 475 | } 476 | 477 | /// 478 | /// XDocument extension method to get the XML text including the declaration from an XDocument 479 | /// 480 | /// 481 | /// The document to get the xml text from 482 | /// 483 | /// 484 | /// The xml text for the document 485 | /// 486 | private static string ToStringWithDeclaration( this XDocument document ) 487 | { 488 | return document.Declaration == null ? 489 | document.ToString() : 490 | string.Format( CultureInfo.InvariantCulture, "{0}{1}{2}", document.Declaration, Environment.NewLine, document ); 491 | } 492 | } 493 | } 494 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/ItemBase.cs: -------------------------------------------------------------------------------- 1 | namespace VisualStudioHelpDownloader2012 2 | { 3 | using System.Globalization; 4 | 5 | /// 6 | /// Base class for container items. 7 | /// 8 | internal abstract class ItemBase 9 | { 10 | /// 11 | /// Gets or sets the Item code (for Uri combination). 12 | /// 13 | public string Code 14 | { 15 | get; 16 | set; 17 | } 18 | 19 | /// 20 | /// Gets or sets the item name. 21 | /// 22 | public string Name 23 | { 24 | get; 25 | set; 26 | } 27 | 28 | /// 29 | /// Gets or sets the item locale. 30 | /// 31 | public Locale Locale 32 | { 33 | get; 34 | set; 35 | } 36 | 37 | /// 38 | /// Gets or sets the item description. 39 | /// 40 | public string Description 41 | { 42 | get; 43 | set; 44 | } 45 | 46 | /// 47 | /// Returns a string representing the object 48 | /// 49 | /// 50 | /// String representing the object 51 | /// 52 | public override string ToString() 53 | { 54 | return string.Format( CultureInfo.InvariantCulture, "{0} [{1}]", Name ?? "NULL", Locale.Name ?? "NULL" ); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Locale.cs: -------------------------------------------------------------------------------- 1 | namespace VisualStudioHelpDownloader2012 2 | { 3 | /// 4 | /// The locale. 5 | /// 6 | public sealed class Locale 7 | { 8 | /// 9 | /// Gets the normalized locale name display. 10 | /// 11 | public string Name 12 | { 13 | get 14 | { 15 | return NormalizeLocale( Code ); 16 | } 17 | } 18 | 19 | /// 20 | /// Gets or sets the locale code. 21 | /// 22 | public string Code 23 | { 24 | get; 25 | set; 26 | } 27 | 28 | /// 29 | /// Gets or sets the relative location of the catalog page associated with the locale 30 | /// 31 | public string CatalogLink 32 | { 33 | get; 34 | set; 35 | } 36 | 37 | /// 38 | /// Returns a string representing the object 39 | /// 40 | /// 41 | /// The Name of the locale 42 | /// 43 | public override string ToString() 44 | { 45 | return Name; 46 | } 47 | 48 | /// 49 | /// Normalizes the locale code 50 | /// 51 | /// 52 | /// The locale code to normalize 53 | /// 54 | /// 55 | /// The normalized locale code 56 | /// 57 | private static string NormalizeLocale( string value ) 58 | { 59 | if ( null == value ) 60 | { 61 | return string.Empty; 62 | } 63 | 64 | string[] parts = value.Split( '-' ); 65 | 66 | if ( parts.Length != 2 ) 67 | { 68 | return value; 69 | } 70 | 71 | return string.Join( "-", parts[0], parts[1].ToUpperInvariant() ); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/MainForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VisualStudioHelpDownloader2012 2 | { 3 | partial class MainForm 4 | { 5 | /// 6 | /// Clean up any resources being used. 7 | /// 8 | /// true if managed resources should be disposed; otherwise, false. 9 | protected override void Dispose(bool disposing) 10 | { 11 | base.Dispose(disposing); 12 | } 13 | 14 | #region Windows Form Designer generated code 15 | 16 | /// 17 | /// Required method for Designer support - do not modify 18 | /// the contents of this method with the code editor. 19 | /// 20 | private void InitializeComponent() 21 | { 22 | System.Windows.Forms.Label _labelDirectory; 23 | System.Windows.Forms.Label _labelFilter; 24 | System.Windows.Forms.ColumnHeader bookName; 25 | System.Windows.Forms.ColumnHeader totalSize; 26 | System.Windows.Forms.ColumnHeader totalPackages; 27 | System.Windows.Forms.ColumnHeader downloadSize; 28 | System.Windows.Forms.ColumnHeader packagesToDownload; 29 | System.Windows.Forms.Label label1; 30 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); 31 | this.loadBooks = new System.Windows.Forms.Button(); 32 | this.cacheDirectory = new System.Windows.Forms.TextBox(); 33 | this.browseDirectory = new System.Windows.Forms.Button(); 34 | this.downloadProgress = new System.Windows.Forms.ProgressBar(); 35 | this.startupTip = new System.Windows.Forms.Label(); 36 | this.downloadBooks = new System.Windows.Forms.Button(); 37 | this.loadingBooksTip = new System.Windows.Forms.Label(); 38 | this.booksList = new System.Windows.Forms.ListView(); 39 | this.languageSelection = new System.Windows.Forms.ComboBox(); 40 | this.vsVersion = new System.Windows.Forms.ComboBox(); 41 | _labelDirectory = new System.Windows.Forms.Label(); 42 | _labelFilter = new System.Windows.Forms.Label(); 43 | bookName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); 44 | totalSize = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); 45 | totalPackages = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); 46 | downloadSize = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); 47 | packagesToDownload = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); 48 | label1 = new System.Windows.Forms.Label(); 49 | this.SuspendLayout(); 50 | // 51 | // _labelDirectory 52 | // 53 | _labelDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 54 | _labelDirectory.AutoSize = true; 55 | _labelDirectory.Location = new System.Drawing.Point(12, 684); 56 | _labelDirectory.Name = "_labelDirectory"; 57 | _labelDirectory.Size = new System.Drawing.Size(108, 25); 58 | _labelDirectory.TabIndex = 10; 59 | _labelDirectory.Text = "Store files in"; 60 | // 61 | // _labelFilter 62 | // 63 | _labelFilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 64 | _labelFilter.AutoSize = true; 65 | _labelFilter.Location = new System.Drawing.Point(364, 573); 66 | _labelFilter.Name = "_labelFilter"; 67 | _labelFilter.Size = new System.Drawing.Size(159, 25); 68 | _labelFilter.TabIndex = 8; 69 | _labelFilter.Text = "2. Select Language"; 70 | // 71 | // bookName 72 | // 73 | bookName.Text = "Book"; 74 | bookName.Width = 550; 75 | // 76 | // totalSize 77 | // 78 | totalSize.Text = "Total Size (MB)"; 79 | totalSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 80 | totalSize.Width = 131; 81 | // 82 | // totalPackages 83 | // 84 | totalPackages.Text = "# Packages"; 85 | totalPackages.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 86 | totalPackages.Width = 129; 87 | // 88 | // downloadSize 89 | // 90 | downloadSize.Text = "Download Size (MB)"; 91 | downloadSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 92 | downloadSize.Width = 174; 93 | // 94 | // packagesToDownload 95 | // 96 | packagesToDownload.Text = "Num Downloads"; 97 | packagesToDownload.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 98 | packagesToDownload.Width = 159; 99 | // 100 | // label1 101 | // 102 | label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 103 | label1.AutoSize = true; 104 | label1.Location = new System.Drawing.Point(12, 573); 105 | label1.Name = "label1"; 106 | label1.Size = new System.Drawing.Size(196, 25); 107 | label1.TabIndex = 12; 108 | label1.Text = "1. Visual Studio Version"; 109 | // 110 | // loadBooks 111 | // 112 | this.loadBooks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 113 | this.loadBooks.Enabled = false; 114 | this.loadBooks.Location = new System.Drawing.Point(914, 563); 115 | this.loadBooks.Name = "loadBooks"; 116 | this.loadBooks.Size = new System.Drawing.Size(170, 44); 117 | this.loadBooks.TabIndex = 3; 118 | this.loadBooks.Text = "3. Load Books"; 119 | this.loadBooks.UseVisualStyleBackColor = true; 120 | this.loadBooks.Click += new System.EventHandler(this.LoadBooksClick); 121 | // 122 | // cacheDirectory 123 | // 124 | this.cacheDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 125 | | System.Windows.Forms.AnchorStyles.Right))); 126 | this.cacheDirectory.Location = new System.Drawing.Point(126, 681); 127 | this.cacheDirectory.Name = "cacheDirectory"; 128 | this.cacheDirectory.ReadOnly = true; 129 | this.cacheDirectory.Size = new System.Drawing.Size(1075, 31); 130 | this.cacheDirectory.TabIndex = 5; 131 | // 132 | // browseDirectory 133 | // 134 | this.browseDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 135 | this.browseDirectory.Enabled = false; 136 | this.browseDirectory.Image = ((System.Drawing.Image)(resources.GetObject("browseDirectory.Image"))); 137 | this.browseDirectory.Location = new System.Drawing.Point(1207, 678); 138 | this.browseDirectory.Name = "browseDirectory"; 139 | this.browseDirectory.Size = new System.Drawing.Size(31, 35); 140 | this.browseDirectory.TabIndex = 2; 141 | this.browseDirectory.UseVisualStyleBackColor = true; 142 | this.browseDirectory.Click += new System.EventHandler(this.BrowseDirectoryClick); 143 | // 144 | // downloadProgress 145 | // 146 | this.downloadProgress.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 147 | | System.Windows.Forms.AnchorStyles.Right))); 148 | this.downloadProgress.Location = new System.Drawing.Point(12, 622); 149 | this.downloadProgress.MarqueeAnimationSpeed = 25; 150 | this.downloadProgress.Name = "downloadProgress"; 151 | this.downloadProgress.Size = new System.Drawing.Size(1226, 36); 152 | this.downloadProgress.Style = System.Windows.Forms.ProgressBarStyle.Continuous; 153 | this.downloadProgress.TabIndex = 9; 154 | // 155 | // startupTip 156 | // 157 | this.startupTip.Anchor = System.Windows.Forms.AnchorStyles.None; 158 | this.startupTip.AutoSize = true; 159 | this.startupTip.BackColor = System.Drawing.SystemColors.Window; 160 | this.startupTip.Location = new System.Drawing.Point(235, 299); 161 | this.startupTip.Name = "startupTip"; 162 | this.startupTip.Size = new System.Drawing.Size(778, 25); 163 | this.startupTip.TabIndex = 6; 164 | this.startupTip.Text = "Select your Visual Studio version and language, then press \"Load\" to retrieve th" + 165 | "e available books"; 166 | this.startupTip.Visible = false; 167 | // 168 | // downloadBooks 169 | // 170 | this.downloadBooks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 171 | this.downloadBooks.Enabled = false; 172 | this.downloadBooks.Location = new System.Drawing.Point(1090, 563); 173 | this.downloadBooks.Name = "downloadBooks"; 174 | this.downloadBooks.Size = new System.Drawing.Size(148, 44); 175 | this.downloadBooks.TabIndex = 5; 176 | this.downloadBooks.Text = "4. Download"; 177 | this.downloadBooks.UseVisualStyleBackColor = true; 178 | this.downloadBooks.Click += new System.EventHandler(this.DownloadBooksClick); 179 | // 180 | // loadingBooksTip 181 | // 182 | this.loadingBooksTip.Anchor = System.Windows.Forms.AnchorStyles.None; 183 | this.loadingBooksTip.AutoSize = true; 184 | this.loadingBooksTip.BackColor = System.Drawing.SystemColors.Window; 185 | this.loadingBooksTip.Location = new System.Drawing.Point(545, 299); 186 | this.loadingBooksTip.Name = "loadingBooksTip"; 187 | this.loadingBooksTip.Size = new System.Drawing.Size(223, 25); 188 | this.loadingBooksTip.TabIndex = 7; 189 | this.loadingBooksTip.Text = "Downloading please wait..."; 190 | // 191 | // booksList 192 | // 193 | this.booksList.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 194 | | System.Windows.Forms.AnchorStyles.Left) 195 | | System.Windows.Forms.AnchorStyles.Right))); 196 | this.booksList.CheckBoxes = true; 197 | this.booksList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { 198 | bookName, 199 | totalSize, 200 | totalPackages, 201 | downloadSize, 202 | packagesToDownload}); 203 | this.booksList.FullRowSelect = true; 204 | this.booksList.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; 205 | this.booksList.HoverSelection = true; 206 | this.booksList.Location = new System.Drawing.Point(17, 12); 207 | this.booksList.Name = "booksList"; 208 | this.booksList.ShowItemToolTips = true; 209 | this.booksList.Size = new System.Drawing.Size(1226, 537); 210 | this.booksList.TabIndex = 4; 211 | this.booksList.UseCompatibleStateImageBehavior = false; 212 | this.booksList.View = System.Windows.Forms.View.Details; 213 | this.booksList.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.BooksListItemChecked); 214 | // 215 | // languageSelection 216 | // 217 | this.languageSelection.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 218 | this.languageSelection.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 219 | this.languageSelection.Enabled = false; 220 | this.languageSelection.FormattingEnabled = true; 221 | this.languageSelection.Location = new System.Drawing.Point(538, 570); 222 | this.languageSelection.Name = "languageSelection"; 223 | this.languageSelection.Size = new System.Drawing.Size(121, 33); 224 | this.languageSelection.Sorted = true; 225 | this.languageSelection.TabIndex = 1; 226 | this.languageSelection.SelectedIndexChanged += new System.EventHandler(this.BookOptionsChanged); 227 | // 228 | // vsVersion 229 | // 230 | this.vsVersion.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 231 | this.vsVersion.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 232 | this.vsVersion.FormattingEnabled = true; 233 | this.vsVersion.Items.AddRange(new object[] { 234 | "2012", 235 | "2013", 236 | "2015", 237 | "2017"}); 238 | this.vsVersion.Location = new System.Drawing.Point(214, 570); 239 | this.vsVersion.Name = "vsVersion"; 240 | this.vsVersion.Size = new System.Drawing.Size(111, 33); 241 | this.vsVersion.TabIndex = 0; 242 | this.vsVersion.SelectedIndexChanged += new System.EventHandler(this.VsVersionChanged); 243 | // 244 | // MainForm 245 | // 246 | this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 25F); 247 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 248 | this.ClientSize = new System.Drawing.Size(1258, 725); 249 | this.Controls.Add(label1); 250 | this.Controls.Add(this.vsVersion); 251 | this.Controls.Add(this.languageSelection); 252 | this.Controls.Add(_labelFilter); 253 | this.Controls.Add(_labelDirectory); 254 | this.Controls.Add(this.loadingBooksTip); 255 | this.Controls.Add(this.startupTip); 256 | this.Controls.Add(this.downloadProgress); 257 | this.Controls.Add(this.browseDirectory); 258 | this.Controls.Add(this.cacheDirectory); 259 | this.Controls.Add(this.downloadBooks); 260 | this.Controls.Add(this.loadBooks); 261 | this.Controls.Add(this.booksList); 262 | this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); 263 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 264 | this.MinimumSize = new System.Drawing.Size(861, 490); 265 | this.Name = "MainForm"; 266 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 267 | this.Text = "%TITLE%"; 268 | this.ResumeLayout(false); 269 | this.PerformLayout(); 270 | 271 | } 272 | 273 | #endregion 274 | 275 | private System.Windows.Forms.Button loadBooks; 276 | private System.Windows.Forms.TextBox cacheDirectory; 277 | private System.Windows.Forms.Button browseDirectory; 278 | private System.Windows.Forms.ProgressBar downloadProgress; 279 | private System.Windows.Forms.Label startupTip; 280 | private System.Windows.Forms.Button downloadBooks; 281 | private System.Windows.Forms.Label loadingBooksTip; 282 | private System.Windows.Forms.ListView booksList; 283 | private System.Windows.Forms.ComboBox languageSelection; 284 | private System.Windows.Forms.ComboBox vsVersion; 285 | } 286 | } 287 | 288 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/MainForm.cs: -------------------------------------------------------------------------------- 1 | namespace VisualStudioHelpDownloader2012 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.Threading.Tasks; 8 | using System.Windows.Forms; 9 | using System.Diagnostics; 10 | /// 11 | /// Main application form. 12 | /// 13 | internal sealed partial class MainForm : Form, IProgress 14 | { 15 | /// 16 | /// The products. 17 | /// 18 | private ICollection products; 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | public MainForm() 24 | { 25 | InitializeComponent(); 26 | 27 | Text = Application.ProductName; 28 | products = new List(); 29 | startupTip.Visible = false; 30 | cacheDirectory.Text = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.UserProfile ), "Downloads", "MSDN Library" ); 31 | } 32 | 33 | /// 34 | /// Reports a progress update. 35 | /// 36 | /// 37 | /// The value of the updated progress. (percentage complete) 38 | /// 39 | public void Report( int value ) 40 | { 41 | Invoke( 42 | new MethodInvoker( 43 | delegate 44 | { 45 | downloadProgress.Value = value; 46 | } ) ); 47 | } 48 | 49 | /// 50 | /// Called when the form is loaded. Start retrieving the list of available 51 | /// languages in the background. 52 | /// 53 | /// 54 | /// The parameter is not used. 55 | /// 56 | protected override void OnLoad( EventArgs e ) 57 | { 58 | base.OnLoad( e ); 59 | loadingBooksTip.Visible = false; 60 | startupTip.Visible = true; 61 | } 62 | 63 | /// 64 | /// Called to update the available locales for the selected version of visual studio 65 | /// 66 | private async Task UpdateLocalesAsync() 67 | { 68 | if ( vsVersion.SelectedIndex == -1 ) 69 | { 70 | return; 71 | } 72 | 73 | string[] vsVersions = { "visualstudio11", "visualstudio12", "dev14", "dev15" }; 74 | string version = vsVersions[vsVersion.SelectedIndex]; 75 | startupTip.Visible = false; 76 | SetBusyState(); 77 | languageSelection.Items.Clear(); 78 | downloadProgress.Style = ProgressBarStyle.Marquee; 79 | 80 | try 81 | { 82 | using (Downloader downloader = new Downloader()) 83 | { 84 | (await downloader.LoadAvailableLocalesAsync(version)).ForEach(x => languageSelection.Items.Add(x)); 85 | } 86 | } 87 | catch ( Exception ex ) 88 | { 89 | MessageBox.Show( 90 | $"Locales update failed - {ex.Message}", 91 | Application.ProductName, 92 | MessageBoxButtons.OK, 93 | MessageBoxIcon.Error, 94 | MessageBoxDefaultButton.Button1, 95 | 0); 96 | } 97 | finally 98 | { 99 | ClearBusyState(); 100 | startupTip.Visible = true; 101 | } 102 | } 103 | 104 | /// 105 | /// Called when the download books button is clicked. Start downloading in a background thread 106 | /// 107 | /// 108 | /// The parameter is not used. 109 | /// 110 | /// 111 | /// The parameter is not used. 112 | /// 113 | private async void DownloadBooksClick( object sender, EventArgs e ) 114 | { 115 | SetBusyState(); 116 | downloadProgress.Style = ProgressBarStyle.Continuous; 117 | downloadProgress.Value = 0; 118 | 119 | try 120 | { 121 | using (Downloader downloader = new Downloader()) 122 | { 123 | await downloader.DownloadBooksAsync(products, cacheDirectory.Text, this); 124 | MessageBox.Show( 125 | "Download completed successfully", 126 | Application.ProductName, 127 | MessageBoxButtons.OK, 128 | MessageBoxIcon.Information, 129 | MessageBoxDefaultButton.Button1, 130 | 0); 131 | } 132 | } 133 | catch ( Exception ex ) 134 | { 135 | MessageBox.Show( 136 | $"Download failed - {ex.Message}", 137 | Application.ProductName, 138 | MessageBoxButtons.OK, 139 | MessageBoxIcon.Error, 140 | MessageBoxDefaultButton.Button1, 141 | 0); 142 | } 143 | finally 144 | { 145 | ClearBusyState(); 146 | DisplayBooks(); 147 | downloadProgress.Value = 0; 148 | } 149 | 150 | } 151 | 152 | /// 153 | /// Called when the load books button is clicked. Load the list of available books for the selected 154 | /// language 155 | /// 156 | /// 157 | /// The parameter is not used. 158 | /// 159 | /// 160 | /// The parameter is not used. 161 | /// 162 | private async void LoadBooksClick( object sender, EventArgs e ) 163 | { 164 | string path = ((Locale)languageSelection.SelectedItem).CatalogLink; 165 | 166 | SetBusyState(); 167 | downloadProgress.Style = ProgressBarStyle.Marquee; 168 | startupTip.Visible = false; 169 | 170 | try 171 | { 172 | using (Downloader downloader = new Downloader()) 173 | { 174 | products = await downloader.LoadBooksInformationAsync(path); 175 | DisplayBooks(); 176 | } 177 | } 178 | catch ( Exception ex ) 179 | { 180 | MessageBox.Show( 181 | $"Failed to retrieve book information - {ex.Message}", 182 | Application.ProductName, 183 | MessageBoxButtons.OK, 184 | MessageBoxIcon.Error, 185 | MessageBoxDefaultButton.Button1, 186 | 0); 187 | } 188 | finally 189 | { 190 | ClearBusyState(); 191 | } 192 | } 193 | 194 | /// 195 | /// Enable/disable, hide/show controls for when the program is not busy 196 | /// 197 | private void ClearBusyState() 198 | { 199 | vsVersion.Enabled = true; 200 | languageSelection.Enabled = languageSelection.Items.Count > 0; 201 | loadBooks.Enabled = languageSelection.Items.Count > 0; 202 | downloadBooks.Enabled = (booksList.Items.Count > 0) && !string.IsNullOrEmpty( cacheDirectory.Text ); 203 | browseDirectory.Enabled = true; 204 | downloadProgress.Style = ProgressBarStyle.Continuous; 205 | startupTip.Visible = false; 206 | loadingBooksTip.Visible = false; 207 | booksList.Enabled = true; 208 | } 209 | 210 | /// 211 | /// Enable/disable, hide/show controls for when the program is busy 212 | /// 213 | private void SetBusyState() 214 | { 215 | vsVersion.Enabled = false; 216 | languageSelection.Enabled = false; 217 | loadBooks.Enabled = false; 218 | downloadBooks.Enabled = false; 219 | browseDirectory.Enabled = false; 220 | booksList.Enabled = false; 221 | loadingBooksTip.Visible = true; 222 | } 223 | 224 | /// 225 | /// Populate the list view control with the books available for download 226 | /// 227 | private void DisplayBooks() 228 | { 229 | booksList.Items.Clear(); 230 | if ( !string.IsNullOrEmpty( cacheDirectory.Text ) ) 231 | { 232 | Downloader.CheckPackagesStates( products, cacheDirectory.Text ); 233 | } 234 | 235 | Dictionary groups = new Dictionary(); 236 | foreach ( BookGroup product in products ) 237 | { 238 | foreach ( Book book in product.Books ) 239 | { 240 | // Calculate some details about any prospective download 241 | long totalSize = 0; 242 | long downloadSize = 0; 243 | int packagesOutOfDate = 0; 244 | int packagesCached = 0; 245 | foreach ( Package package in book.Packages ) 246 | { 247 | totalSize += package.Size; 248 | if ( package.State != PackageState.Ready ) 249 | { 250 | downloadSize += package.Size; 251 | packagesOutOfDate++; 252 | } 253 | 254 | if ( package.State != PackageState.NotDownloaded ) 255 | { 256 | packagesCached++; 257 | } 258 | } 259 | 260 | // Make sure the groups aren't duplicated 261 | ListViewGroup itemGroup; 262 | if ( groups.ContainsKey( book.Category ) ) 263 | { 264 | itemGroup = groups[book.Category]; 265 | } 266 | else 267 | { 268 | itemGroup = booksList.Groups.Add( book.Category, book.Category ); 269 | groups.Add( book.Category, itemGroup ); 270 | } 271 | 272 | ListViewItem item = booksList.Items.Add( book.Name ); 273 | item.SubItems.Add( (totalSize / 1000000).ToString( "F1", CultureInfo.CurrentCulture ) ); 274 | item.SubItems.Add( book.Packages.Count.ToString( CultureInfo.CurrentCulture ) ); 275 | item.SubItems.Add( (downloadSize / 1000000).ToString( "F1", CultureInfo.CurrentCulture ) ); 276 | item.SubItems.Add( packagesOutOfDate.ToString( CultureInfo.CurrentCulture ) ); 277 | item.ToolTipText = book.Description; 278 | item.Checked = packagesCached > 1; 279 | book.Wanted = item.Checked; 280 | item.Tag = book; 281 | item.Group = itemGroup; 282 | } 283 | } 284 | } 285 | 286 | /// 287 | /// Called when the browse for directory button is clicked. Show an folder browser to allow the 288 | /// user to select a directory to store the cached file in 289 | /// 290 | /// 291 | /// The parameter is not used. 292 | /// 293 | /// 294 | /// The parameter is not used. 295 | /// 296 | private void BrowseDirectoryClick( object sender, EventArgs e ) 297 | { 298 | using ( FolderBrowserDialog dialog = new FolderBrowserDialog() ) 299 | { 300 | dialog.RootFolder = Environment.SpecialFolder.MyComputer; 301 | dialog.SelectedPath = cacheDirectory.Text; 302 | dialog.ShowNewFolderButton = true; 303 | dialog.Description = "Select local cache folder to store selected MSDN Library books"; 304 | 305 | if ( DialogResult.OK == dialog.ShowDialog( this ) ) 306 | { 307 | cacheDirectory.Text = dialog.SelectedPath; 308 | downloadBooks.Enabled = (booksList.Items.Count > 0) && !string.IsNullOrEmpty( cacheDirectory.Text ); 309 | DisplayBooks(); 310 | } 311 | } 312 | } 313 | 314 | /// 315 | /// Called when the checkbox of one of the listview items is checked or unchecked. Mark the associated book state 316 | /// 317 | /// 318 | /// The parameter is not used. 319 | /// 320 | /// 321 | /// Details about the item checked/unchecked 322 | /// 323 | private void BooksListItemChecked( object sender, ItemCheckedEventArgs e ) 324 | { 325 | Book book = e.Item.Tag as Book; 326 | if ( book != null ) 327 | { 328 | book.Wanted = e.Item.Checked; 329 | } 330 | } 331 | 332 | /// 333 | /// Called when the language combobox selection is changed. Clear the 334 | /// currently list of available books and reshow the instruction. 335 | /// 336 | /// 337 | /// The parameter is not used. 338 | /// 339 | /// 340 | /// The parameter is not used. 341 | /// 342 | private void BookOptionsChanged( object sender, EventArgs e ) 343 | { 344 | booksList.Items.Clear(); 345 | downloadBooks.Enabled = false; 346 | startupTip.Visible = true; 347 | } 348 | 349 | /// 350 | /// Called when the visual studio language combobox selection is changed. Clear the 351 | /// currently list of available books and reshow the instruction. 352 | /// 353 | /// 354 | /// The parameter is not used. 355 | /// 356 | /// 357 | /// The parameter is not used. 358 | /// 359 | private async void VsVersionChanged(object sender, EventArgs e) 360 | { 361 | booksList.Items.Clear(); 362 | languageSelection.Items.Clear(); 363 | languageSelection.SelectedItem = -1; 364 | await UpdateLocalesAsync(); 365 | } 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/MainForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | False 122 | 123 | 124 | False 125 | 126 | 127 | False 128 | 129 | 130 | False 131 | 132 | 133 | False 134 | 135 | 136 | False 137 | 138 | 139 | False 140 | 141 | 142 | False 143 | 144 | 145 | 146 | 147 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m 148 | dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIISURBVDhPY/jx4wdFGKsgFDMBsQMQpwFxAhBbAjGG 149 | OgwBKHb5+v3HtkW3X5+vPPv0fv35Z3dW3X1zFijeBMTmSOqwGuB0/dWn/aFbbr03W37lPzJO3Hnn1dMP 150 | X3YB1RjD1KNrZvr67fu2yKVX3llOOfs/etnVd4tPP70w58Tjy0ELL38CiaWtuf4CqK4OpgfdALvlp56c 151 | s+w5+T9y3sV3IMOAYk5A7P3649cd3tPOfgXJnbn/9hBQTAGkB92AlPp1N+5Zthz9P+/gg0tAvhuSXFzr 152 | ppu30eWQNYNwQvOaa3csaw78n7L99lUgPxxJLq1mxZX7ILnFB+9fAPJBLsMwQHf3uafHLUt3//eoP/D9 153 | 0cuPu4Fi+UBcfPHumwMOVXv/2JTv+Xv32fu9QDFhkB50A+JOXnt+2LZg2x/L3C3/HYu3/y6ZcfJRwdQT 154 | T2Bik9ZduQZUB0obYD2omq88PeyUs/6XRerq/8FV2z6CaBi2zVj7Z9Kq8yDNWUDMBtMH13z8wsPDtvHz 155 | fhmHz/yf1rjp+dev37fdf/xmz6b9105tP3zzxIvXH3YC1aUAMVwzCIMIpaNnbx82Der4pe3Z9D+hbCFQ 156 | 8zdQ9OkDsSAQgxINiM0NxHCNMAwiQhv7VtxQssr4H5ndi6wZQzE2DCJAJmdNmbnw3JcvX0nSDMLIHDsg 157 | BuVAZDEC+AcDADVk7WUUiqKZAAAAAElFTkSuQmCC 158 | 159 | 160 | 161 | 162 | AAABAAQAMDAAAAEAIACoJQAARgAAACAgAAABACAAqBAAAO4lAAAYGAAAAQAgAIgJAACWNgAAEBAAAAEA 163 | IABoBAAAHkAAACgAAAAwAAAAYAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 164 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 165 | AAAAAAAAAAAAAAAAAAAAAAABAAAAAwAAAAYAAAAIAAAABgAAAAMAAAABAAAAAAAAAAAAAAAAAAAAAAAA 166 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 167 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 168 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAACwAAABYAAAAbAAAAFwAAAA0AAAAEAAAAAQAA 169 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 170 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 171 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAALAAAAHQAAADIAAAA9AAAANQAA 172 | ACEAAAANAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 173 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 174 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAkfHx8fnJyc4piY 175 | mOCamprrFRUVXAAAADoAAAAeAAAACgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 176 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 177 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAABwAA 178 | ABeUlJS/nZ2d8cbGxv+kpKT2kJCQ3AAAAFUAAAA1AAAAGQAAAAgAAAABAAAAAAAAAAAAAAAAAAAAAAAA 179 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 180 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 181 | AAEAAAAFAAAAE4aGho6enp70qKio/8PDw//R0dH/oKCg+Hx8fL4AAABPAAAALwAAABUAAAAGAAAAAQAA 182 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 183 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 184 | AAAAAAAAAAAAAAAAAAQAAAAPbW1tXJ2dnfiqqqr/rq6u/6urq//Ly8v/ycnJ/6CgoPtbW1uaAAAASQAA 185 | ACkAAAARAAAABQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 186 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 187 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAxCQkIynJyc9aenp/mzs7P/s7Oz/6+vr/+urq7/0tLS/729 188 | vfyfn5/5MzMzeAAAAEIAAAAkAAAADgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 189 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 190 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAACRwcHCGWlpbhn5+f87i4uP+5ubn/tra2/7Ky 191 | sv+wsLD/srKy/9fX1/+xsbH4mZmZ7hQUFGMAAAA8AAAAHgAAAAsAAAACAAAAAAAAAAAAAAAAAAAAAAAA 192 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 193 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAHAAAAGIyMjL2amprzvLy8/8DA 194 | wP+9vb3/urq6/7e3t/+0tLT/sLCw/7q6uv/X19f/qKio942NjdsAAABVAAAANQAAABkAAAAIAAAAAQAA 195 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 196 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAYAAAAUfX19jZeX 197 | l/a7u7v/xcXF/8TExP/AwMD/vr6+/7q6uv+3t7f/s7Oz/7CwsP/CwsL/1dXV/6Ojo/l6enq9AAAATwAA 198 | AC8AAAAVAAAABgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 199 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAA 200 | ABBlZWVblZWV+La2tv/Kysr/y8vL/8jIyP/ExMT/wsLC/76+vv+6urr/t7e3/7W1tf+xsbH/zMzM/87O 201 | zv+fn5/6W1tbmAAAAEkAAAAqAAAAEQAAAAUAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 202 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 203 | AAAAAAADAAAADTw8PDShoaH1q6ur+83Nzf/R0dH/zs7O/8vLy//IyMj/xcXF/8HBwf++vr7/u7u7/7i4 204 | uP+0tLT/srKy/9LS0v/Dw8P9n5+f+DMzM3gAAABCAAAAJAAAAA4AAAADAAAAAAAAAAAAAAAAAAAAAAAA 205 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 206 | AAAAAAAAAAAAAAAAAAIAAAAKDg4OIIyMjN+enp72z8/P/9XV1f/U1NT/0dHR/8/Pz//Ly8v/ycnJ/8XF 207 | xf/CwsL/v7+//7y8vP+4uLj/tbW1/7W1tf/Y2Nj/tbW1+ZmZme4PDw9hAAAAPAAAAB4AAAALAAAAAgAA 208 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 209 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAgAAAAZgoKCuZSUlPbNzc3/2dnZ/9nZ2f/X19f/1NTU/9HR 210 | 0f/Ozs7/zMzM/8jIyP/FxcX/wsLC/7+/v/+7u7v/ubm5/7W1tf+8vLz/2tra/6urq/iLi4vYAAAAVQAA 211 | ADYAAAAaAAAACAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 212 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAABgAAABVycnKKnJyc+crKyv/c3Nz/3t7e/9zc 213 | 3P/Z2dn/19fX/9TU1P/S0tL/z8/P/8zMzP/Jycn/xsbG/8PDw//AwMD/vb29/7m5uf+2trb/w8PD/9nZ 214 | 2f+kpKT6dXV1uQAAAFAAAAAwAAAAFgAAAAYAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 215 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAFAAAAEVlZWVqZmZn5vb29/+Hh 216 | 4f/i4uL/4ODg/97e3v/c3Nz/2tra/9fX1//V1dX/0tLS/8/Pz//MzMz/ycnJ/8bGxv/Dw8P/wMDA/7y8 217 | vP+5ubn/tra2/8zMzP/S0tL/oKCg+1ZWVpYAAABJAAAAKgAAABIAAAAFAAAAAQAAAAAAAAAAAAAAAAAA 218 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAOMDAwM6Sk 219 | pPWurq7+4ODg/+np6f/l5eX/4uLi/+Dg4P/e3t7/3Nzc/9ra2v/X19f/1dXV/9LS0v/Q0ND/zc3N/8rK 220 | yv/Hx8f/xMTE/8DAwP+9vb3/urq6/7e3t//T09P/yMjI/ZycnPcvLy93AAAAQwAAACQAAAAOAAAAAwAA 221 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA 222 | AAsMDAwioaGh5ZmZmfnd3d3/6+vr/+rq6v/n5+f/5OTk/+Li4v/h4eH/39/f/93d3f/a2tr/2NjY/9XV 223 | 1f/T09P/0NDQ/83Nzf/Kysr/x8fH/8PDw//BwcH/vb29/7q6uv+5ubn/2dnZ/7q6uvqWlpbsCgoKXwAA 224 | ADwAAAAfAAAACwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 225 | AAAAAAACAAAACQAAABp3d3e4p6en+dfX1//p6en/6+vr/+vr6//p6en/5eXl/+Tk5P/i4uL/4eHh/9/f 226 | 3//d3d3/2tra/9jY2P/W1tb/09PT/9HR0f/Nzc3/y8vL/8fHx//FxcX/wcHB/76+vv+7u7v/vr6+/9zc 227 | 3P+urq75iYmJ1wAAAFUAAAA2AAAAGgAAAAgAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 228 | AAAAAAAAAAAAAAAAAAEAAAAHAAAAFmdnZ4ahoaH6ycnJ/+fn5//r6+v/6+vr/+vr6//q6ur/5ubm/+Xl 229 | 5f/k5OT/4+Pj/+Hh4f/f39//3d3d/9vb2//Y2Nj/1tbW/9PT0//Q0ND/zs7O/8rKyv/IyMj/xMTE/8HB 230 | wf++vr7/urq6/8TExP/b29v/paWl+3FxcbUAAABQAAAAMAAAABYAAAAGAAAAAQAAAAAAAAAAAAAAAAAA 231 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAUAAAATT09PWp+fn/q4uLj/4+Pj/+rq6v/r6+v/6+vr/+vr 232 | 6//r6+v/6Ojo/+bm5v/l5eX/5OTk/+Pj4//h4eH/39/f/93d3f/b29v/2dnZ/9fX1//U1NT/0dHR/87O 233 | zv/Ly8v/yMjI/8XFxf/CwsL/vr6+/7u7u//MzMz/1tbW/6CgoPtSUlKTAAAASgAAACoAAAASAAAABQAA 234 | AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAA4lJSUznJyc9aSkpP7d3d3/6Ojo/+np 235 | 6f/q6ur/6+vr/+vr6//r6+v/6urq/+fn5//m5ub/5eXl/+Tk5P/j4+P/4uLi/+Dg4P/e3t7/29vb/9nZ 236 | 2f/W1tb/1NTU/9HR0f/Ozs7/y8vL/8jIyP/FxcX/wcHB/7+/v/+7u7v/1NTU/8zMzP2bm5v3LCwsdQAA 237 | AEMAAAAkAAAADgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABg8PDxmNjY3fn5+f+9bW 238 | 1v/l5eX/5+fn/+jo6P/p6en/6urq/+vr6//r6+v/6urq/+fn5//n5+f/5ubm/+bm5v/k5OT/4+Pj/+Hh 239 | 4f/g4OD/3t7e/9zc3P/Z2dn/19fX/9TU1P/S0tL/z8/P/8zMzP/Jycn/xcXF/8PDw/+/v7//vr6+/9nZ 240 | 2f+/v7/6lZWV6AwMDFEAAAAwAAAAFQAAAAYAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAB3V1 241 | daeSkpL5y8vL/+Dg4P/k5OT/5eXl/+bm5v/n5+f/6enp/+rq6v/r6+v/6+vr/+fn5//n5+f/5+fn/+fn 242 | 5//m5ub/5eXl/+Pj4//i4uL/4ODg/97e3v/c3Nz/2tra/9fX1//U1NT/0tLS/8/Pz//MzMz/ycnJ/8bG 243 | xv/CwsL/v7+//8HBwf/d3d3/srKy+I6OjsYAAAAvAAAAFwAAAAgAAAABAAAAAAAAAAAAAAAAAAAAAAAA 244 | AAAAAAAAeHh4ao2Njfm8vLz/29vb/+Hh4f/i4uL/5OTk/+bm5v/n5+f/6enp/+rq6v/q6ur/6+vr/+jo 245 | 6P/n5+f/5+fn/+fn5//n5+f/5ubm/+Tk5P/j4+P/4uLi/+Hh4f/e3t7/3Nzc/9ra2v/X19f/1dXV/9PT 246 | 0//Q0ND/zc3N/8rKyv/Gxsb/w8PD/8DAwP/Hx8f/3d3d/6SkpPiPj4+KAAAADgAAAAUAAAABAAAAAAAA 247 | AAAAAAAAAAAAAAAAAAAAAAAAfHx80rS0tP/d3d3/39/f/+Hh4f/k5OT/5eXl/+fn5//p6en/6urq/+jo 248 | 6P/o6Oj/6urq/+jo6P/n5+f/5+fn/+fn5//n5+f/5+fn/+bm5v/l5eX/5OTk/+Li4v/h4eH/39/f/9zc 249 | 3P/q6ur/6enp/+zs7P/s7Oz/6+vr/+rq6v/o6Oj/5+fn/+bm5v/l5eX/5+fn/+Tk5P+enp7lAAAABgAA 250 | AAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAenp6pIGBgf+CgoL/g4OD/4ODg/+EhIT/hYWF/4WF 251 | hf+Ghob/hoaG/4aGhv+Hh4f/6+vr/+jo6P/m5ub/5+fn/+fn5//n5+f/5+fn/+fn5//m5ub/5eXl/+Tk 252 | 5P/j4+P/4eHh/9/f3//t7e3/rKys/56env+enp7/n5+f/6Ghof+hoaH/oqKi/6Ojo/+jo6P/pKSk/6am 253 | pv+NjY2XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 254 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGhob/6urq/+jo6P/l5eX/5ubm/+fn5//n5+f/5+fn/+fn 255 | 5//n5+f/5ubm/+Xl5f/k5OT/4+Pj/+Hh4f/v7+//qqqq/wAAACsAAAAOAAAAAAAAAAAAAAAAAAAAAAAA 256 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 257 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGhob/6Ojo/+fn5//l5eX/5eXl/+bm 258 | 5v/n5+f/5+fn/+fn5//n5+f/5+fn/+bm5v/l5eX/5OTk/+Pj4//w8PD/qamp/wAAACsAAAAOAAAAAAAA 259 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 260 | 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 344 | AAAAAAAAAAAAAQAAAAein5ust7Sv/+jo6P/g4OD/3d3d/9ra2v/W1tb/09PT/8/Pz//Ly8v/x8fH/8PD 345 | w/+/v7//0tLS/7a2tv+np6f7REREXAAAADAAAAAUAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 346 | AAAAAAAAAAAAAAAAAAAAAAAEn5uYeKShm/7r6ur/5+fn/+Pj4//g4OD/3t7e/9vb2//X19f/1NTU/9DQ 347 | 0P/MzMz/yMjI/8PDw//CwsL/1NTU/6+vr/+mpqbuJiYmTwAAACsAAAAPAAAAAgAAAAAAAAAAAAAAAAAA 348 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAApKPjEGemZT74+Lg/+zs7P/q6ur/5eXl/+Pj4//h4eH/3t7e/9vb 349 | 2//Y2Nj/1NTU/9DQ0P/MzMz/yMjI/8TExP/Gxsb/z8/P/6qqqv+enp7NAAAAPwAAACQAAAALAAAAAQAA 350 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAItKSUUnZiQ8dLQzf/s7Oz/6+vr/+rq6v/n5+f/5OTk/+Pj 351 | 4//h4eH/3t7e/9vb2//Y2Nj/1dXV/9HR0f/Nzc3/yMjI/8TExP/Ly8v/xcXF/6ampv+KioqcAAAAOgAA 352 | AB8AAAAIAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAC6Kak9bAu7P/6+vr/+rq6v/r6+v/6+vr/+np 353 | 6f/m5ub/5eXl/+Pj4//h4eH/39/f/9zc3P/Z2dn/1dXV/9HR0f/MzMz/yMjI/8TExP/Q0ND/vb29/6am 354 | pv5ubm56AAAANgAAABoAAAAGAAAAAQAAAAAAAAAAAAAAAAAAAAafnJqtp6Og/+jo6P/o6Oj/6Ojo/+rq 355 | 6v/r6+v/6enp/+fn5//m5ub/5ubm/+Tk5P/i4uL/39/f/9zc3P/Z2dn/1tbW/9LS0v/Nzc3/ycnJ/8bG 356 | xv/X19f/tra2/6mpqftLS0tgAAAALgAAABMAAAADAAAAAAAAAAAAAAAAp6WjcJiUj//d3Nz/4+Pj/+Xl 357 | 5f/n5+f/6enp/+rq6v/q6ur/5+fn/+fn5//n5+f/5ubm/+Tk5P/i4uL/4ODg/9zc3P/Z2dn/1dXV/9LS 358 | 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 454 | rEHwH6xB8B+sQfAfrEHwH6xB8B+sQQ== 455 | 456 | 457 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Package.cs: -------------------------------------------------------------------------------- 1 | namespace VisualStudioHelpDownloader2012 2 | { 3 | using System; 4 | using System.Globalization; 5 | 6 | /// 7 | /// The possible download states for a package 8 | /// 9 | public enum PackageState 10 | { 11 | /// 12 | /// The package has not been downloaded yet 13 | /// 14 | NotDownloaded, 15 | 16 | /// 17 | /// The package has been previously downloaded but is now out of date 18 | /// 19 | OutOfDate, 20 | 21 | /// 22 | /// The package has been downloaded and is up to date 23 | /// 24 | Ready 25 | } 26 | 27 | /// 28 | /// Represents an MSDN package 29 | /// 30 | internal sealed class Package 31 | { 32 | /// 33 | /// Gets or sets the last modified time. 34 | /// 35 | public DateTime LastModified 36 | { 37 | get; 38 | set; 39 | } 40 | 41 | /// 42 | /// Gets or sets the tag associated with the package 43 | /// 44 | public string Tag 45 | { 46 | get; 47 | set; 48 | } 49 | 50 | /// 51 | /// Gets or sets the package relative URL for downloading. 52 | /// 53 | public string Link 54 | { 55 | get; 56 | set; 57 | } 58 | 59 | /// 60 | /// Gets or sets the package name 61 | /// 62 | public string Name 63 | { 64 | get; 65 | set; 66 | } 67 | 68 | /// 69 | /// Gets or sets the size in bytes. 70 | /// 71 | public long Size 72 | { 73 | get; 74 | set; 75 | } 76 | 77 | /// 78 | /// Gets or sets the state. 79 | /// 80 | public PackageState State 81 | { 82 | get; 83 | set; 84 | } 85 | 86 | /// 87 | /// Create a file name for the package file 88 | /// 89 | /// 90 | /// A string containing the file name 91 | /// 92 | public string CreateFileName() 93 | { 94 | return string.Format( CultureInfo.InvariantCulture, "{0}.cab", Name.ToUpperInvariant() ); 95 | } 96 | 97 | /// 98 | /// Returns a string representing the object 99 | /// 100 | /// 101 | /// String representing the object 102 | /// 103 | public override string ToString() 104 | { 105 | return Name ?? "NULL"; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Program.cs: -------------------------------------------------------------------------------- 1 | namespace VisualStudioHelpDownloader2012 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.Globalization; 6 | using System.Windows.Forms; 7 | 8 | /// 9 | /// The program. 10 | /// 11 | internal static class Program 12 | { 13 | /// 14 | /// Log exception to event log. 15 | /// 16 | /// 17 | /// The exeception to log 18 | /// 19 | public static void LogException( Exception ex ) 20 | { 21 | if ( null != ex ) 22 | { 23 | EventLog.WriteEntry( 24 | "Application", 25 | string.Format( CultureInfo.InvariantCulture, "{1}{0}{0}{2}", Environment.NewLine, ex.Message, ex.StackTrace ), 26 | EventLogEntryType.Error ); 27 | } 28 | } 29 | 30 | /// 31 | /// The main. 32 | /// 33 | [STAThread] 34 | private static void Main() 35 | { 36 | AppDomain.CurrentDomain.UnhandledException += ( sender, e ) => LogException( e.ExceptionObject as Exception ); 37 | Application.ThreadException += ( sender, e ) => LogException( e.Exception ); 38 | Application.EnableVisualStyles(); 39 | Application.SetCompatibleTextRenderingDefault( false ); 40 | Application.Run( new MainForm() ); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Properties/App.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickdalt/VSHD/5119cb6945cbecb8c6ae269399d900d5039cb79b/VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Properties/App.ico -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Properties/App.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Visual Studio 2010 Help Downloader 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | true 20 | 21 | 22 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("VisualStudioHelpDownloader")] 6 | [assembly: AssemblyDescription("MSDN Library packages downloader")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyProduct("Visual Studio Help Downloader")] 9 | [assembly: AssemblyCopyright("© 2012-2020 NickDal, Based on Visual Studio 2010 Help Downloader Alexander Kozlenko")] 10 | [assembly: AssemblyCulture("")] 11 | [assembly: AssemblyVersion("1.5.0.0")] 12 | [assembly: AssemblyFileVersion("1.5.0.0")] 13 | [assembly: CLSCompliant(true)] 14 | [assembly: ComVisible(false)] 15 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Properties/Default.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickdalt/VSHD/5119cb6945cbecb8c6ae269399d900d5039cb79b/VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/Properties/Default.snk -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/VisualStudioHelpDownloader.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {2AE1E505-92AB-40DD-86D9-E47B307CEC94} 9 | WinExe 10 | Properties 11 | VisualStudioHelpDownloader 12 | VisualStudioHelpDownloader 13 | v4.7.2 14 | 15 | 16 | 512 17 | VisualStudioHelpDownloader2012.Program 18 | Properties\App.manifest 19 | true 20 | Properties\Default.snk 21 | Properties\App.ico 22 | SAK 23 | SAK 24 | SAK 25 | SAK 26 | 27 | 28 | true 29 | bin\Debug\ 30 | DEBUG;TRACE 31 | full 32 | AnyCPU 33 | prompt 34 | AllRules.ruleset 35 | 36 | 37 | bin\Release\ 38 | TRACE 39 | true 40 | AnyCPU 41 | prompt 42 | MinimumRecommendedRules.ruleset 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | Form 56 | 57 | 58 | MainForm.cs 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | MainForm.cs 71 | Designer 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | False 85 | Microsoft .NET Framework 4 Client Profile %28x86 and x64%29 86 | true 87 | 88 | 89 | False 90 | .NET Framework 3.5 SP1 Client Profile 91 | false 92 | 93 | 94 | False 95 | .NET Framework 3.5 SP1 96 | false 97 | 98 | 99 | False 100 | Windows Installer 3.1 101 | true 102 | 103 | 104 | 105 | 112 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/VisualStudioHelpDownloader.csproj.vspscc: -------------------------------------------------------------------------------- 1 | "" 2 | { 3 | "FILE_VERSION" = "9237" 4 | "ENLISTMENT_CHOICE" = "NEVER" 5 | "PROJECT_FILE_RELATIVE_PATH" = "" 6 | "NUMBER_OF_EXCLUDED_FILES" = "0" 7 | "ORIGINAL_PROJECT_FILE_PATH" = "" 8 | "NUMBER_OF_NESTED_PROJECTS" = "0" 9 | "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" 10 | } 11 | -------------------------------------------------------------------------------- /VisualStudioHelpDownloader2012/VisualStudioHelpDownloader2012/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/Home.md: -------------------------------------------------------------------------------- 1 | **Project Description** 2 | Tool for downloading the Visual Studio 2012/2013/2015 help packages for offline use 3 | 4 | # Overview 5 | 6 | Allows Visual Studio 2012/2013/2015 packages to be downloaded to an offline cache location before importing them into Microsoft Help Viewer 2.0/2.1/2.2. 7 | 8 | If the cache is kept following the import, then only changes will be downloaded on subsequent occasions. 9 | 10 | ![](Home_screenshot.jpg) 11 | 12 | # Quick Guide 13 | 14 | * Select the version of Visual Studio to download the help for 15 | * Select your language from the drop down list 16 | * Press "Load Books" to retrieve the list of available books. Books that are already in the cache (partially or fully) will automatically be checked. Note that because packages are shared between different books, you may get extra items checked automatically. 17 | * Check (or uncheck) book that you want to download. The "Download Size" and "Num Downloads" columns indicate an approximate amount of data that needs to be downloaded for the book based on what is already in the cache. 18 | * Press "Download" to start downloading the requested books. Packages for books no longer selected will be deleted at this point. 19 | * When the download is complete, import the new books into "Microsoft Help Viewer" using the "Manage Content" tab. 20 | 21 | # Credits 22 | 23 | This project is based on (and shares some code with) the earlier project [Visual Studio Help Downloader](http://vshd.codeplex.com/) by [nop](http://www.codeplex.com/site/users/view/nop). -------------------------------------------------------------------------------- /docs/Home_screenshot.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickdalt/VSHD/5119cb6945cbecb8c6ae269399d900d5039cb79b/docs/Home_screenshot.JPG --------------------------------------------------------------------------------