├── .gitignore ├── BUILD.md ├── LICENSE.md ├── README.md ├── Research └── Virtual hard disk format specification.pdf ├── Setup ├── License.rtf ├── Publish-GitHub.sh ├── Publish.cmd └── VhdAttach.iss └── Source ├── VhdAttach-Service ├── (Medo) │ ├── AboutBox [013].cs │ ├── Args [002].cs │ ├── CallingAssembly [002].cs │ ├── ErrorReport [020].cs │ ├── NamedPipe [003].cs │ ├── Settings [012].cs │ ├── TinyMessage [009].cs │ ├── UnhandledCatch [008].cs │ └── VirtualDisk [010].cs ├── App.cs ├── AppService.cs ├── AppServiceInstaller.cs ├── DeviceFromPath.cs ├── DiskIO.cs ├── FileWithOptions.cs ├── PipeServer.cs ├── Properties │ ├── App.ico │ ├── App.manifest │ ├── App.snk │ └── AssemblyInfo.cs ├── Resources │ ├── Service_RunningInteractive_12.png │ ├── Service_Running_12.png │ ├── Service_Stopped_12.png │ └── Service_Unknown_12.png ├── Service.cs ├── ServiceSettings.cs ├── ServiceStatusThread.cs ├── Tray.cs ├── VhdAttach-Service.csproj ├── VirtualDiskServiceHelper.cs └── Volume.cs ├── VhdAttach-Test ├── AssemblyInfo.cs ├── FileWithOptionsTest.cs ├── Properties │ └── App.snk ├── VhdAttach-Test.csproj └── VhdFooterTest.cs ├── VhdAttach.sln └── VhdAttach ├── (Medo) ├── AboutBox [014].cs ├── Args [002].cs ├── BinaryPrefixExtensions [002x].cs ├── EntryAssembly [003].cs ├── ErrorReport [020].cs ├── MessageBox [007].cs ├── NamedPipe [003].cs ├── NumberDeclination [001].cs ├── RecentFiles [005].cs ├── Settings [012].cs ├── State [015].cs ├── StringAdder [002].cs ├── TaskbarProgress [003].cs ├── TinyMessage [009].cs ├── UnhandledCatch [008].cs ├── Upgrade [005].cs └── VirtualDisk [010].cs ├── App.cs ├── AttachForm.Designer.cs ├── AttachForm.cs ├── AttachForm.resx ├── ChangeDriveLetterForm.Designer.cs ├── ChangeDriveLetterForm.cs ├── ChangeDriveLetterForm.resx ├── CommandLineAddon.cs ├── CreateFixedDiskForm.Designer.cs ├── CreateFixedDiskForm.cs ├── CreateFixedDiskForm.resx ├── DetachDriveForm.Designer.cs ├── DetachDriveForm.cs ├── DetachDriveForm.resx ├── DetachForm.Designer.cs ├── DetachForm.cs ├── DetachForm.resx ├── FileWithOptionsCollection.cs ├── Helper.cs ├── ListViewVhdItem.cs ├── MainForm.cs ├── MainForm.designer.cs ├── MainForm.resx ├── Messages.cs ├── NewDiskForm.Designer.cs ├── NewDiskForm.cs ├── NewDiskForm.resx ├── PathFromDevice.cs ├── PipeClient.cs ├── PipeResponse.cs ├── Properties ├── App.ico ├── App.manifest ├── App.snk ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx └── SolutionInfo.cs ├── ReFS.cs ├── RemoveIntegrityStreamForm.Designer.cs ├── RemoveIntegrityStreamForm.cs ├── RemoveIntegrityStreamForm.resx ├── ServiceWaitForm.Designer.cs ├── ServiceWaitForm.cs ├── ServiceWaitForm.resx ├── Settings.cs ├── SettingsForm.cs ├── SettingsForm.designer.cs ├── SettingsForm.resx ├── Utility.cs ├── VhdAttach.csproj └── VirtualHardDiskImage ├── DynamicDiskHeader.cs └── HardDiskFooter.cs /.gitignore: -------------------------------------------------------------------------------- 1 | **/Bin 2 | **/bin 3 | **/Debug 4 | **/obj 5 | **/TestResults 6 | **/ApiPortabilityAnalysis.htm 7 | **/.vs 8 | 9 | Binaries/** 10 | Releases/** 11 | Backup/** 12 | Temporary/** 13 | 14 | *.bak 15 | *.csproj.user 16 | *.csproj.vspscc 17 | *.ldb 18 | *.mcs 19 | *.msi 20 | *.pdb 21 | *.rar 22 | *.SCC 23 | *.suo 24 | *.testsettings 25 | *.tmp 26 | *.vbproj.user 27 | *.vbproj.vspscc 28 | *.vbw 29 | *.vshost.exe 30 | *.vsmdi 31 | *.vspscc 32 | *.vssscc 33 | *.zip -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | ### Building Project ### 2 | 3 | First step would be to get current version of code from [GitHub](https://github.com/medo64/vhdattach). 4 | 5 | After that you can download and install required tools. 6 | 7 | 8 | #### Required Tools #### 9 | 10 | * [Visual Studio Express 2013 for Windows Desktop](http://www.visualstudio.com/downloads/download-visual-studio-vs#d-express-windows-desktop) 11 | * [Inno setup](http://www.jrsoftware.org/isinfo.php) 12 | * [Sign tool](http://msdn.microsoft.com/en-us/library/windows/desktop/aa387764.aspx) 13 | 14 | 15 | #### Script Modifications #### 16 | 17 | In `Publish.cmd` check location of Visual Studio (`COMPILE_TOOL` variable) and 18 | location of Sign Tool (`SIGN_TOOL` variable). 19 | 20 | In case you have certificate that you want to use for signing, update it also 21 | (`SIGN_HASH` variable). In case you don't have a certificate, it is ok, script 22 | will just avoid signing executables. 23 | 24 | After this you can start script and it will compile project and make its setup. 25 | Executables will be in `Binaries` directory and setup will be in `Releases`. 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ### The MIT License ### 2 | 3 | Copyright 2009 Josip Medved 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | * The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### VHD Attach ### 2 | 3 | This is small tool that adds Attach and Detach option to contextual (aka 4 | right-click) menu of the Virtual disk (vhd) files. That enables those 5 | operations to be done without trip to Disk Management console and allows for 6 | automatic disk attachment during sysstem startup. 7 | 8 | 9 | #### Shortcut Keys #### 10 | 11 | * `F5` Refresh. 12 | * `F6` Attach. 13 | * `Ctrl+A` Select all. 14 | * `Ctrl+C` Copy. 15 | * `Ctrl+N` New file. 16 | * `Ctrl+O` Open file. 17 | * `Alt+A` Show attach menu. 18 | * `Alt+D` Detach. 19 | * `Alt+M` Auto-mount. 20 | * `Alt+O` Show open menu (recent files). 21 | 22 | 23 | #### Command Line Parameters #### 24 | 25 | [/attach|/detach] "disk.vhd" 26 | 27 | Attaches (or detaches) virtual disk using file disk.vhd. 28 | 29 | [/detachdrive] "X:" 30 | 31 | Detaches virtual disk attached to drive letter. 32 | -------------------------------------------------------------------------------- /Research/Virtual hard disk format specification.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medo64/VhdAttach/1cf3394cb1939062bdc7a284549080548e78b035/Research/Virtual hard disk format specification.pdf -------------------------------------------------------------------------------- /Setup/License.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033\deflangfe1033{\fonttbl{\f0\fswiss\fprq2\fcharset238 Calibri;}{\f1\fnil\fcharset2 Symbol;}} 2 | {\*\generator Riched20 10.0.17134}{\*\mmathPr\mnaryLim0\mdispDef1\mwrapIndent1440 }\viewkind4\uc1 3 | \pard\nowidctlpar\sa200\sl276\slmult1\b\f0\fs22\lang9 The MIT License\par 4 | \b0 Copyright 2009 Josip Medved \par 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\par 6 | 7 | \pard{\pntext\f1\'B7\tab}{\*\pn\pnlvlblt\pnf1\pnindent0{\pntxtb\'B7}}\nowidctlpar\fi-360\li720\sa200\sl276\slmult1 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\par 8 | {\pntext\f1\'B7\tab}THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\par 9 | } 10 | -------------------------------------------------------------------------------- /Setup/Publish-GitHub.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OWNER=medo64 4 | REPOSITORY=vhdattach 5 | TOKEN=`cat ~/GitHub.token 2> /dev/null` 6 | 7 | if [[ "$TOKEN" == "" ]]; then 8 | echo "No GitHub token found." >&2 9 | exit 1 10 | fi 11 | 12 | VERSION_HASH=`git log -n 1 --format=%h` 13 | VERSION_NUMBER=`git rev-list --count HEAD` 14 | FILE_PREFIX=$REPOSITORY-rev$VERSION_NUMBER-$VERSION_HASH 15 | 16 | 17 | BRANCH=`git rev-parse --abbrev-ref HEAD` 18 | 19 | if [[ "$BRANCH" == "master" ]]; then 20 | UNCOMMITED_FILE_COUNT=`git ls | sed 1d | wc -l` 21 | if [[ $UNCOMMITED_FILE_COUNT == 0 ]]; then 22 | git push --dry-run 2>&1 | grep --quiet "Everything up-to-date" 23 | if [[ $? == 0 ]]; then 24 | ./Publish.cmd 25 | 26 | for FILE_EXTENSION in "exe"; do 27 | if [ ! -e ../Releases/$FILE_PREFIX.$FILE_EXTENSION ]; then 28 | echo "Executables cannot be found." >&2 29 | exit 1 30 | fi 31 | done 32 | 33 | RELEASE_URL=`curl -s -H "Authorization: token $TOKEN" https://api.github.com/repos/$OWNER/$REPOSITORY/releases/tags/latest | grep "\"url\"" | head -1 | cut -d\" -f4` 34 | if [[ "$RELEASE_URL" != "" ]]; then 35 | curl -s -H "Authorization: token $TOKEN" -X DELETE $RELEASE_URL 36 | fi 37 | 38 | git push origin :refs/tags/latest 2> /dev/null 39 | 40 | ASSET_UPLOAD_URL=`curl -s -H "Authorization: token $TOKEN" --data "{\"tag_name\": \"latest\", \"target_commitish\": \"master\", \"name\": \"Most recent build\", \"body\": \"This is the most recent automated build (#$VERSION_HASH, revision $VERSION_NUMBER).\n\nFor the latest stable release go to https://www.medo64.com/$REPOSITORY/.\", \"draft\": false, \"prerelease\": true}" -X POST https://api.github.com/repos/$OWNER/$REPOSITORY/releases | grep "\"upload_url\"" | cut -d\" -f4 | cut -d{ -f1` 41 | for FILE_EXTENSION in "exe"; do 42 | UPLOAD_RESULT=`curl -s -H "Authorization: token $TOKEN" -H "Content-Type: application/octet-stream" --data-binary @../Releases/$FILE_PREFIX.$FILE_EXTENSION -X POST $ASSET_UPLOAD_URL?name=$FILE_PREFIX.$FILE_EXTENSION` 43 | echo $UPLOAD_RESULT | grep --quiet "browser_download_url" 44 | if [[ $? == 0 ]]; then 45 | echo "$FILE_PREFIX.$FILE_EXTENSION" 46 | else 47 | echo "Failed upload for $FILE_PREFIX.$FILE_EXTENSION" >&2 48 | 49 | RELEASE_URL=`curl -s -H "Authorization: token $TOKEN" https://api.github.com/repos/$OWNER/$REPOSITORY/releases/tags/latest | grep "\"url\"" | head -1 | cut -d\" -f4` 50 | curl -s -H "Authorization: token $TOKEN" -X DELETE $RELEASE_URL 51 | 52 | exit 1; 53 | fi 54 | done 55 | echo 56 | echo "https://github.com/medo64/$REPOSITORY/releases/tag/latest" 57 | else 58 | echo "Not all changes have been pushed to origin." >&2 59 | exit 1 60 | fi 61 | else 62 | echo "Not all modified files are commited." >&2 63 | exit 1 64 | fi 65 | else 66 | echo "Not in master branch." >&2 67 | exit 1 68 | fi 69 | -------------------------------------------------------------------------------- /Setup/Publish.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | SETLOCAL EnableDelayedExpansion 3 | 4 | SET FILES="..\Binaries\VhdAttach.exe" "..\Binaries\VhdAttachService.exe" "..\Binaries\README.md" "..\Binaries\LICENSE.md" 5 | SET SOURCE_SOLUTION="..\Source\VhdAttach.sln" 6 | SET SOURCE_INNOSETUP=".\VhdAttach.iss" 7 | 8 | SET TOOLS_GIT="%PROGRAMFILES(X86)%\Git\mingw64\bin\git.exe" "%PROGRAMFILES%\Git\mingw64\bin\git.exe" "C:\Program Files\Git\mingw64\bin\git.exe" 9 | SET TOOLS_VISUALSTUDIO="%PROGRAMFILES(X86)%\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.exe" 10 | SET TOOLS_SIGNTOOL="%PROGRAMFILES(X86)%\Microsoft SDKs\ClickOnce\SignTool\signtool.exe" "%PROGRAMFILES(X86)%\Windows Kits\10\App Certification Kit\signtool.exe" "%PROGRAMFILES(X86)%\Windows Kits\10\bin\x86\signtool.exe" 11 | SET TOOLS_INNOSETUP="%PROGRAMFILES(X86)%\Inno Setup 6\iscc.exe" 12 | SET TOOLS_WINRAR="%PROGRAMFILES(X86)%\WinRAR\WinRAR.exe" "%PROGRAMFILES%\WinRAR\WinRAR.exe" "C:\Program Files\WinRAR\WinRAR.exe" 13 | 14 | SET CERTIFICATE_THUMBPRINT="026184de8dbf52fdcbae75fd6b1a7d9ce4310e5d" 15 | SET SIGN_TIMESTAMPURL="http://timestamp.comodoca.com/rfc3161" 16 | 17 | 18 | ECHO --- DISCOVER TOOLS 19 | ECHO: 20 | 21 | WHERE /Q git 22 | IF ERRORLEVEL 1 ( 23 | FOR %%I IN (%TOOLS_GIT%) DO ( 24 | IF EXIST %%I IF NOT DEFINED TOOL_GIT SET TOOL_GIT=%%I 25 | ) 26 | ) ELSE ( 27 | SET TOOL_GIT="git" 28 | ) 29 | IF [%TOOL_GIT%]==[] SET WARNING=1 30 | ECHO Git .................: %TOOL_GIT% 31 | 32 | FOR %%I IN (%TOOLS_VISUALSTUDIO%) DO ( 33 | IF EXIST %%I IF NOT DEFINED TOOL_VISUALSTUDIO SET TOOL_VISUALSTUDIO=%%I 34 | ) 35 | IF [%TOOL_VISUALSTUDIO%]==[] ECHO Visual Studio not found^^! & GOTO Error 36 | ECHO Visual Studio .......: %TOOL_VISUALSTUDIO% 37 | 38 | FOR %%I IN (%TOOLS_SIGNTOOL%) DO ( 39 | IF EXIST %%I IF NOT DEFINED TOOL_SIGNTOOL SET TOOL_SIGNTOOL=%%I 40 | ) 41 | IF [%TOOL_SIGNTOOL%]==[] SET WARNING=1 42 | ECHO SignTool ............: %TOOL_SIGNTOOL% 43 | 44 | FOR %%I IN (%TOOLS_INNOSETUP%) DO ( 45 | IF EXIST %%I IF NOT DEFINED TOOL_INNOSETUP SET TOOL_INNOSETUP=%%I 46 | ) 47 | IF [%TOOL_INNOSETUP%]==[] ECHO InnoSetup not found^^! & GOTO Error 48 | ECHO InnoSetup ...........: %TOOL_INNOSETUP% 49 | 50 | FOR %%I IN (%TOOLS_WINRAR%) DO ( 51 | IF EXIST %%I IF NOT DEFINED TOOL_WINRAR SET TOOL_WINRAR=%%I 52 | ) 53 | IF [%TOOL_WINRAR%]==[] SET WARNING=1 54 | ECHO WinRAR ..............: %TOOL_WINRAR% 55 | 56 | IF NOT [%CERTIFICATE_THUMBPRINT%]==[] ( 57 | CERTUTIL -silent -verifystore -user My %CERTIFICATE_THUMBPRINT% > NUL 58 | IF NOT ERRORLEVEL 0 ( 59 | SET CERTIFICATE_THUMBPRINT= 60 | SET WARNING=1 61 | ) 62 | ) 63 | 64 | ECHO: 65 | ECHO: 66 | 67 | 68 | IF NOT [%TOOL_GIT%]==[] ( 69 | ECHO --- DISCOVER VERSION 70 | ECHO: 71 | 72 | FOR /F "delims=" %%I IN ('%TOOL_GIT% log -n 1 --format^=%%h') DO @SET VERSION_HASH=%%I% 73 | 74 | IF NOT [!VERSION_HASH!]==[] ( 75 | FOR /F "delims=" %%I IN ('%TOOL_GIT% rev-list --count HEAD') DO @SET VERSION_NUMBER=%%I% 76 | %TOOL_GIT% diff --exit-code --quiet 77 | IF NOT ERRORLEVEL 0 SET VERSION_HASH=%VERSION_HASH%+ 78 | ) 79 | ECHO Hash ...: !VERSION_HASH! 80 | ECHO Revision: !VERSION_NUMBER! 81 | 82 | ECHO: 83 | ECHO: 84 | ) 85 | 86 | 87 | ECHO --- BUILD SOLUTION 88 | ECHO: 89 | 90 | RMDIR /Q /S "..\Binaries" 2> NUL 91 | %TOOL_VISUALSTUDIO% /Build "Release" %SOURCE_SOLUTION% 92 | IF NOT ERRORLEVEL 0 ECHO Build failed^^! & GOTO Error 93 | 94 | ECHO Build successful. 95 | 96 | ECHO: 97 | ECHO: 98 | 99 | 100 | IF NOT [%TOOL_SIGNTOOL%]==[] IF NOT [%CERTIFICATE_THUMBPRINT%]==[] ( 101 | ECHO --- SIGN EXECUTABLES 102 | ECHO: 103 | 104 | FOR %%I IN (%FILES%) DO ( 105 | IF [%%~xI]==[.exe] ( 106 | IF [%SIGN_TIMESTAMPURL%]==[] ( 107 | %TOOL_SIGNTOOL% sign /s "My" /sha1 %CERTIFICATE_THUMBPRINT% /v %%I 108 | ) ELSE ( 109 | %TOOL_SIGNTOOL% sign /s "My" /sha1 %CERTIFICATE_THUMBPRINT% /tr %SIGN_TIMESTAMPURL% /v %%I 110 | ) 111 | IF NOT ERRORLEVEL 0 GOTO Error 112 | ) 113 | ) 114 | 115 | ECHO: 116 | ECHO: 117 | ) 118 | 119 | 120 | RMDIR /Q /S ".\Temp" 2> NUL 121 | MKDIR ".\Temp" 122 | 123 | 124 | ECHO --- BUILD SETUP 125 | ECHO: 126 | 127 | SET TOOL_INNOSETUP_ISPP=%TOOL_INNOSETUP:iscc.exe=ispp.dll% 128 | IF NOT EXIST !!TOOL_INNOSETUP_ISPP!! ECHO InnoSetup pre-processor not installed^^! & GOTO Error 129 | 130 | RMDIR /Q /S ".\Temp" 2> NUL 131 | CALL %TOOL_INNOSETUP% /DVersionHash=%VERSION_HASH% /O".\Temp" %SOURCE_INNOSETUP% 132 | IF NOT ERRORLEVEL 0 GOTO Error 133 | 134 | FOR /F %%I IN ('DIR ".\Temp\*.exe" /B') DO SET SETUPEXE=%%I 135 | 136 | ECHO: 137 | ECHO: 138 | 139 | 140 | IF NOT [%CERTIFICATE_THUMBPRINT%]==[] ( 141 | ECHO --- SIGN SETUP 142 | ECHO: 143 | 144 | IF [%SIGN_TIMESTAMPURL%]==[] ( 145 | %TOOL_SIGNTOOL% sign /s "My" /sha1 %CERTIFICATE_THUMBPRINT% /v ".\Temp\!SETUPEXE!" 146 | ) ELSE ( 147 | %TOOL_SIGNTOOL% sign /s "My" /sha1 %CERTIFICATE_THUMBPRINT% /tr %SIGN_TIMESTAMPURL% /v ".\Temp\!SETUPEXE!" 148 | ) 149 | IF NOT ERRORLEVEL 0 GOTO Error 150 | 151 | ECHO: 152 | ECHO: 153 | ) 154 | 155 | 156 | 157 | ECHO --- RELEASE 158 | ECHO: 159 | 160 | MKDIR "..\Releases" 2> NUL 161 | FOR %%I IN (".\Temp\*.*") DO ( 162 | SET FILE_FROM=%%~nI%%~xI 163 | IF NOT [%VERSION_HASH%]==[] ( 164 | SET FILE_TO=!FILE_FROM:000=-rev%VERSION_NUMBER%-%VERSION_HASH%! 165 | ) ELSE ( 166 | SET FILE_TO=!FILE_FROM! 167 | ) 168 | MOVE ".\Temp\!FILE_FROM!" "..\Releases\!FILE_TO!" > NUL 169 | IF NOT ERRORLEVEL 0 GOTO Error 170 | ECHO !FILE_TO! 171 | IF NOT DEFINED FILE_RELEASED SET FILE_RELEASED=!FILE_TO! 172 | ) 173 | 174 | IF [%FILE_RELEASED%]==[] ECHO No files. 175 | 176 | ECHO: 177 | ECHO: 178 | 179 | 180 | ECHO --- DONE 181 | 182 | IF NOT [%WARNING%]==[] ( 183 | ECHO: 184 | IF [%TOOL_GIT%]==[] ECHO Git executable not found. 185 | IF [%TOOL_SIGNTOOL%]==[] ECHO SignTool executable not found. 186 | IF [%TOOL_INNOSETUP%]==[] ECHO InnoSetup executable not found. 187 | IF [%TOOL_WINRAR%]==[] ECHO WinRAR executable not found. 188 | 189 | IF [%CERTIFICATE_THUMBPRINT%]==[] ECHO Executables not signed. 190 | PAUSE 191 | ) 192 | IF NOT [%FILE_RELEASED%]==[] explorer /select,"..\Releases\!FILE_RELEASED!" 193 | 194 | ENDLOCAL 195 | RMDIR /Q /S ".\Temp" 2> NUL 196 | EXIT /B 0 197 | 198 | 199 | :Error 200 | ENDLOCAL 201 | RMDIR /Q /S ".\Temp" 2> NUL 202 | PAUSE 203 | EXIT /B 1 204 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/(Medo)/CallingAssembly [002].cs: -------------------------------------------------------------------------------- 1 | //Josip Medved http://www.jmedved.com 2 | 3 | //2008-05-27: First version. 4 | //2008-06-06: Added Copyright. 5 | 6 | 7 | using System.Reflection; 8 | 9 | namespace Medo.Reflection { 10 | 11 | /// 12 | /// Returns various info about assembly that started process. 13 | /// 14 | public static class CallingAssembly { 15 | 16 | private readonly static Assembly Assembly = System.Reflection.Assembly.GetCallingAssembly(); 17 | private readonly static AssemblyName AssemblyName = Assembly.GetName(); 18 | 19 | /// 20 | /// Gets entry assembly's full name. 21 | /// 22 | public static string FullName { 23 | get { return AssemblyName.FullName; } 24 | } 25 | 26 | /// 27 | /// Gets entry assembly's application name. 28 | /// 29 | public static string Name { 30 | get { return AssemblyName.Name; } 31 | } 32 | 33 | /// 34 | /// Gets entry assembly's version. 35 | /// 36 | public static System.Version Version { 37 | get { return AssemblyName.Version; } 38 | } 39 | 40 | /// 41 | /// Returns entry assembly's version in x.xx format. 42 | /// 43 | public static string ShortVersionString { 44 | get { 45 | System.Version version = AssemblyName.Version; 46 | return version.Major.ToString("0", System.Globalization.CultureInfo.InvariantCulture) + "." + version.Minor.ToString("00", System.Globalization.CultureInfo.InvariantCulture); 47 | } 48 | } 49 | 50 | /// 51 | /// Returns entry assembly's version in x.xx.xxx.xxxx format. 52 | /// 53 | public static string LongVersionString { 54 | get { 55 | System.Version version = AssemblyName.Version; 56 | return version.Major.ToString("0", System.Globalization.CultureInfo.CurrentCulture) + "." + version.Minor.ToString("00", System.Globalization.CultureInfo.CurrentCulture) + "." + version.Build.ToString("000", System.Globalization.CultureInfo.CurrentCulture) + "." + version.Revision.ToString("0000", System.Globalization.CultureInfo.CurrentCulture); 57 | } 58 | } 59 | 60 | /// 61 | /// Returns entry assembly's company or null if not found. 62 | /// 63 | public static string Company { 64 | get{ 65 | object[] companyAttributes = Assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyCompanyAttribute), true); 66 | if ((companyAttributes != null) && (companyAttributes.Length >= 1)) { 67 | return ((System.Reflection.AssemblyCompanyAttribute)companyAttributes[companyAttributes.Length - 1]).Company; 68 | } 69 | return null; 70 | } 71 | } 72 | 73 | /// 74 | /// Returns entry assembly's title or name if title is not found. 75 | /// 76 | public static string Title { 77 | get{ 78 | object[] titleAttributes = Assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyTitleAttribute), true); 79 | if ((titleAttributes != null) && (titleAttributes.Length >= 1)) { 80 | return ((System.Reflection.AssemblyTitleAttribute)titleAttributes[titleAttributes.Length - 1]).Title; 81 | } else { 82 | return Name; 83 | } 84 | } 85 | } 86 | 87 | /// 88 | /// Retuns entry's assembly product. If product is not found, title is returned. 89 | /// 90 | public static string Product { 91 | get{ 92 | object[] productAttributes = Assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyProductAttribute), true); 93 | if ((productAttributes != null) && (productAttributes.Length >= 1)) { 94 | return ((System.Reflection.AssemblyProductAttribute)productAttributes[productAttributes.Length - 1]).Product; 95 | } else { 96 | return Title; 97 | } 98 | } 99 | } 100 | 101 | /// 102 | /// Retuns entry's assembly description. If description is not found, empty string is returned. 103 | /// 104 | public static string Description { 105 | get { 106 | object[] descriptionAttributes = Assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyDescriptionAttribute), true); 107 | if ((descriptionAttributes != null) && (descriptionAttributes.Length >= 1)) { 108 | return ((System.Reflection.AssemblyDescriptionAttribute)descriptionAttributes[descriptionAttributes.Length - 1]).Description; 109 | } else { 110 | return string.Empty; 111 | } 112 | } 113 | } 114 | 115 | /// 116 | /// Retuns entry's assembly copyright. If copyright is not found, empty string is returned. 117 | /// 118 | public static string Copyright { 119 | get { 120 | object[] copyrightAttributes = Assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyCopyrightAttribute), true); 121 | if ((copyrightAttributes != null) && (copyrightAttributes.Length >= 1)) { 122 | return ((System.Reflection.AssemblyCopyrightAttribute)copyrightAttributes[copyrightAttributes.Length - 1]).Copyright; 123 | } else { 124 | return string.Empty; 125 | } 126 | } 127 | } 128 | 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/(Medo)/UnhandledCatch [008].cs: -------------------------------------------------------------------------------- 1 | //Josip Medved http://www.jmedved.com 2 | 3 | //2007-12-30: New version. 4 | //2008-01-02: Added support for inner exceptions. 5 | // Added thread-safe locking. 6 | //2008-01-03: Added Resources. 7 | //2008-01-06: System.Environment.Exit returns E_UNEXPECTED (0x8000ffff). 8 | //2008-01-13: Changed default mode to ThrowException. 9 | // Uses FailFast to exit application. 10 | //2009-03-31: Changed FailFast to optional in order to avoid WER messages. 11 | //2010-11-07: Compatible with Mono (ignoring FailFast). 12 | //2010-11-22: Changed default exception mode to CatchException. 13 | 14 | 15 | using System; 16 | using System.Threading; 17 | 18 | namespace Medo.Application { 19 | 20 | /// 21 | /// Handling of unhandled errors. 22 | /// This class is thread-safe. 23 | /// 24 | public static class UnhandledCatch { 25 | 26 | /// 27 | /// Occurs when an exception is not caught. 28 | /// 29 | public static event EventHandler ThreadException; 30 | 31 | private static readonly object SyncRoot = new object(); 32 | 33 | /// 34 | /// Initializes handlers for unhandled exception. 35 | /// 36 | public static void Attach() { 37 | lock (SyncRoot) { 38 | Attach(System.Windows.Forms.UnhandledExceptionMode.CatchException, true); 39 | } 40 | } 41 | 42 | /// 43 | /// Initializes handlers for unhandled exception. 44 | /// 45 | /// Defines where a Windows Forms application should send unhandled exceptions. 46 | public static void Attach(System.Windows.Forms.UnhandledExceptionMode mode) { 47 | lock (SyncRoot) { 48 | Attach(mode, true); 49 | } 50 | } 51 | 52 | /// 53 | /// Initializes handlers for unhandled exception. 54 | /// 55 | /// Defines where a Windows Forms application should send unhandled exceptions. 56 | /// True to set the thread exception mode. 57 | public static void Attach(System.Windows.Forms.UnhandledExceptionMode mode, bool threadScope) { 58 | lock (SyncRoot) { 59 | System.Windows.Forms.Application.SetUnhandledExceptionMode(mode, threadScope); 60 | System.Windows.Forms.Application.ThreadException += Application_ThreadException; 61 | System.AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException; 62 | } 63 | } 64 | 65 | /// 66 | /// Initializes handlers for unhandled exception. 67 | /// 68 | /// Defines where a Windows Forms application should send unhandled exceptions. 69 | /// True to set the thread exception mode. 70 | /// When true, FailFast will be used to stop application. If false, standard Environment.Exit will be used instead. 71 | [Obsolete("Use UseFailFast property instead.")] 72 | public static void Attach(System.Windows.Forms.UnhandledExceptionMode mode, bool threadScope, bool useFailFast) { 73 | lock (SyncRoot) { 74 | UnhandledCatch.UseFailFast = useFailFast; 75 | System.Windows.Forms.Application.SetUnhandledExceptionMode(mode, threadScope); 76 | System.Windows.Forms.Application.ThreadException += Application_ThreadException; 77 | System.AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException; 78 | } 79 | } 80 | 81 | private static bool _useFailFast; 82 | /// 83 | /// Gets/sets whether to use FailFast terminating application. 84 | /// Under Mono this property always remains false. 85 | /// 86 | public static bool UseFailFast { 87 | get { lock (SyncRoot) { return _useFailFast; } } 88 | set { lock (SyncRoot) { _useFailFast = value && !IsRunningOnMono; } } 89 | } 90 | 91 | private static void AppDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e) { 92 | lock (SyncRoot) { 93 | Process(e.ExceptionObject as System.Exception); 94 | } 95 | } 96 | 97 | private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { 98 | lock (SyncRoot) { 99 | Process(e.Exception); 100 | } 101 | } 102 | 103 | 104 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "General exceptions are catched on purpose because this is handler for unhandled exceptions.")] 105 | private static void Process(System.Exception exception) { 106 | lock (SyncRoot) { 107 | System.Environment.ExitCode = unchecked((int)0x8000ffff); //E_UNEXPECTED(0x8000ffff) 108 | 109 | if (exception != null) { 110 | System.Diagnostics.Trace.TraceError(exception.ToString() + " {Medo.Application.UnhandledCatch}"); 111 | 112 | System.Windows.Forms.Application.ThreadException -= Application_ThreadException; 113 | System.AppDomain.CurrentDomain.UnhandledException -= AppDomain_UnhandledException; 114 | 115 | if (ThreadException != null) { ThreadException(null, new ThreadExceptionEventArgs(exception)); } 116 | } 117 | 118 | System.Diagnostics.Trace.TraceError("Exit(E_UNEXPECTED): Unhandled exception has occurred. {Medo.Application.UnhandledCatch}"); 119 | 120 | if (UnhandledCatch.UseFailFast) { 121 | System.Environment.FailFast(exception.Message); 122 | } else { 123 | System.Environment.Exit(unchecked((int)0x8000ffff)); //E_UNEXPECTED(0x8000ffff) 124 | } 125 | } 126 | } 127 | 128 | private static bool IsRunningOnMono { 129 | get { 130 | return (Type.GetType("Mono.Runtime") != null); 131 | } 132 | } 133 | 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/App.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Configuration.Install; 3 | using System.Diagnostics; 4 | using System.Reflection; 5 | using System.ServiceProcess; 6 | using System.Windows.Forms; 7 | 8 | namespace VhdAttachService { 9 | 10 | internal static class App { 11 | 12 | [STAThread()] 13 | static void Main() { 14 | System.AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); 15 | 16 | if (Medo.Application.Args.Current.ContainsKey("Interactive")) { 17 | 18 | Tray.Show(); 19 | Service.Start(); 20 | Tray.SetStatusToRunningInteractive(); 21 | Application.Run(); 22 | Service.Stop(); 23 | Tray.Hide(); 24 | Environment.Exit(0); 25 | 26 | } else if (Medo.Application.Args.Current.ContainsKey("Install")) { 27 | 28 | try { 29 | using (ServiceController sc = new ServiceController(AppService.Instance.ServiceName)) { 30 | if (sc.Status != ServiceControllerStatus.Stopped) { sc.Stop(); } 31 | } 32 | } catch (Exception) { } 33 | 34 | ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location }); 35 | System.Environment.Exit(0); 36 | 37 | } else if (Medo.Application.Args.Current.ContainsKey("Uninstall")) { 38 | 39 | try { 40 | using (ServiceController sc = new ServiceController(AppService.Instance.ServiceName)) { 41 | if (sc.Status != ServiceControllerStatus.Stopped) { sc.Stop(); } 42 | } 43 | } catch (Exception) { } 44 | try { 45 | ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location }); 46 | System.Environment.Exit(0); 47 | } catch (System.Configuration.Install.InstallException) { //no service with that name 48 | System.Environment.Exit(1); 49 | } 50 | 51 | } else if (Medo.Application.Args.Current.ContainsKey("Start")) { 52 | 53 | try { 54 | using (var service = new ServiceController("VhdAttach")) { 55 | if (service.Status != ServiceControllerStatus.Running) { 56 | service.Start(); 57 | service.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 1)); 58 | } 59 | } 60 | } catch (Exception) { } 61 | System.Environment.Exit(0); 62 | 63 | } else { 64 | 65 | if (Environment.UserInteractive) { 66 | Tray.Show(); 67 | ServiceStatusThread.Start(); 68 | Application.Run(); 69 | ServiceStatusThread.Stop(); 70 | Tray.Hide(); 71 | Environment.Exit(0); 72 | } else { 73 | ServiceBase.Run(new ServiceBase[] { AppService.Instance }); 74 | } 75 | 76 | } 77 | } 78 | 79 | 80 | static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { 81 | Medo.Diagnostics.ErrorReport.SaveToTemp(e.ExceptionObject as Exception); 82 | AppService.Instance.ExitCode = 1064; //ERROR_EXCEPTION_IN_SERVICE 83 | AppService.Instance.AutoLog = false; 84 | System.Threading.Thread.Sleep(1000); //just to sort it properly in event log. 85 | Environment.Exit(AppService.Instance.ExitCode); 86 | } 87 | 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/AppService.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.ServiceProcess; 3 | 4 | namespace VhdAttachService { 5 | internal class AppService : ServiceBase { 6 | 7 | private static AppService _instance = new AppService(); 8 | public static AppService Instance { get { return _instance; } } 9 | 10 | 11 | private AppService() { 12 | this.AutoLog = true; 13 | this.CanStop = true; 14 | this.ServiceName = "VhdAttach"; 15 | } 16 | 17 | protected override void OnStart(string[] args) { 18 | Debug.WriteLine("AppService : Start requested."); 19 | Service.Start(); 20 | } 21 | 22 | protected override void OnStop() { 23 | Debug.WriteLine("AppService : Stop requested."); 24 | Service.Stop(); 25 | } 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/AppServiceInstaller.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.ComponentModel; 3 | using System.Configuration.Install; 4 | using System.Diagnostics; 5 | using System.ServiceProcess; 6 | 7 | namespace VhdAttachService { 8 | 9 | [RunInstaller(true)] 10 | public class AppServiceInstaller : Installer { 11 | 12 | public AppServiceInstaller() { 13 | ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller(); 14 | serviceProcessInstaller.Account = ServiceAccount.LocalSystem; 15 | serviceProcessInstaller.Username = null; 16 | serviceProcessInstaller.Password = null; 17 | this.Installers.Add(serviceProcessInstaller); 18 | 19 | ServiceInstaller serviceInstaller = new ServiceInstaller(); 20 | serviceInstaller.ServiceName = AppService.Instance.ServiceName; 21 | serviceInstaller.DisplayName = Medo.Reflection.CallingAssembly.Product; 22 | serviceInstaller.Description = Medo.Reflection.CallingAssembly.Description; 23 | serviceInstaller.StartType = ServiceStartMode.Automatic; 24 | this.Installers.Add(serviceInstaller); 25 | } 26 | 27 | 28 | protected override void OnCommitted(IDictionary savedState) { 29 | Debug.WriteLine("OnCommitted : Begin."); 30 | base.OnCommitted(savedState); 31 | using (ServiceController sc = new ServiceController(AppService.Instance.ServiceName)) { 32 | Debug.WriteLine("OnCommitted : Service starting..."); 33 | sc.Start(); 34 | Debug.WriteLine("OnCommitted : Service started."); 35 | } 36 | Debug.WriteLine("OnCommitted : End."); 37 | } 38 | 39 | protected override void OnBeforeUninstall(IDictionary savedState) { 40 | Debug.WriteLine("OnBeforeUninstall : Begin."); 41 | using (ServiceController sc = new ServiceController(AppService.Instance.ServiceName)) { 42 | if (sc.Status != ServiceControllerStatus.Stopped) { 43 | Debug.WriteLine("OnBeforeUninstall : Service stopping..."); 44 | sc.Stop(); 45 | Debug.WriteLine("OnBeforeUninstall : Service stopped..."); 46 | } 47 | } 48 | base.OnBeforeUninstall(savedState); 49 | Debug.WriteLine("OnBeforeUninstall : End."); 50 | } 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/DeviceFromPath.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Management; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | namespace VhdAttachService { 10 | internal static class DeviceFromPath { 11 | 12 | public static string GetDevice(string path) { 13 | var device = FindCdRom(path); 14 | if (device == null) { device = FindPhysicalDrive(path); } 15 | return device; 16 | } 17 | 18 | 19 | #region PhysicalDrive 20 | 21 | private static string FindPhysicalDrive(string path) { 22 | FileSystemInfo iDirectory = null; 23 | var wmiQuery = new ObjectQuery("SELECT Antecedent, Dependent FROM Win32_LogicalDiskToPartition"); 24 | var wmiSearcher = new ManagementObjectSearcher(wmiQuery); 25 | 26 | var iFile = new FileInfo(path); 27 | try { 28 | if ((iFile.Attributes & FileAttributes.Directory) == FileAttributes.Directory) { 29 | iDirectory = new DirectoryInfo(iFile.FullName); 30 | } else { 31 | iDirectory = iFile; 32 | throw new FormatException("Argument is not a directory."); 33 | } 34 | } catch (IOException) { 35 | iDirectory = new DirectoryInfo(iFile.FullName); 36 | } 37 | 38 | 39 | var wmiPhysicalDiskNumber = -1; 40 | foreach (var iReturn in wmiSearcher.Get()) { 41 | var disk = GetSubsubstring((string)iReturn["Antecedent"], "Win32_DiskPartition.DeviceID", "Disk #", ","); 42 | var partition = GetSubsubstring((string)iReturn["Dependent"], "Win32_LogicalDisk.DeviceID", "", ""); 43 | if (iDirectory.Name.StartsWith(partition, StringComparison.InvariantCultureIgnoreCase)) { 44 | if (int.TryParse(disk, NumberStyles.Integer, CultureInfo.InvariantCulture, out wmiPhysicalDiskNumber)) { 45 | return @"\\?\PHYSICALDRIVE" + wmiPhysicalDiskNumber.ToString(CultureInfo.InvariantCulture); 46 | } else { 47 | throw new FormatException("Cannot retrieve physical disk number."); 48 | } 49 | } 50 | } 51 | return null; 52 | } 53 | 54 | private static string GetSubsubstring(string value, string type, string start, string end) { 55 | var xStart0 = value.IndexOf(":" + type + "=\""); 56 | if (xStart0 < 0) { return null; } 57 | var xStart1 = value.IndexOf("\"", xStart0 + 1); 58 | if (xStart1 < 0) { return null; } 59 | var xEnd1 = value.IndexOf("\"", xStart1 + 1); 60 | if (xEnd1 < 0) { return null; } 61 | var extract = value.Substring(xStart1 + 1, xEnd1 - xStart1 - 1); 62 | 63 | int xStart2 = 0; 64 | if (!string.IsNullOrEmpty(start)) { xStart2 = extract.IndexOf(start); } 65 | if (xStart2 < 0) { return null; } 66 | 67 | int xEnd2 = extract.Length; 68 | if (!string.IsNullOrEmpty(end)) { xEnd2 = extract.IndexOf(end); } 69 | if (xEnd2 < 0) { return null; } 70 | 71 | return extract.Substring(xStart2 + start.Length, xEnd2 - xStart2 - start.Length); 72 | } 73 | 74 | #endregion 75 | 76 | 77 | #region CdRom 78 | 79 | private static string FindCdRom(string path) { 80 | var dosDevice = path[0] + ":"; 81 | var sb = new StringBuilder(64); 82 | if (NativeMethods.QueryDosDeviceW(dosDevice, sb, (uint)sb.Capacity) > 0) { 83 | var dosPath = sb.ToString(); 84 | Debug.WriteLine(sb.ToString() + " is at " + dosDevice); 85 | if (dosPath.StartsWith(@"\Device\CdRom", StringComparison.OrdinalIgnoreCase)) { 86 | int cdromNumber = 0; 87 | if (int.TryParse(dosPath.Substring(13), NumberStyles.Integer, CultureInfo.InvariantCulture, out cdromNumber)) { 88 | return @"\\?\CDROM" + cdromNumber.ToString(CultureInfo.InvariantCulture); 89 | } 90 | } 91 | } 92 | return null; 93 | } 94 | 95 | 96 | private static class NativeMethods { 97 | 98 | [DllImportAttribute("kernel32.dll", EntryPoint = "QueryDosDeviceW")] 99 | public static extern UInt32 QueryDosDeviceW( 100 | [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] string lpDeviceName, 101 | [OutAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] StringBuilder lpTargetPath, 102 | UInt32 ucchMax); 103 | 104 | } 105 | 106 | #endregion 107 | 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/FileWithOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace VhdAttachCommon { 6 | internal class FileWithOptions { 7 | 8 | public FileWithOptions(string fileNameWithOptions) { 9 | if (fileNameWithOptions.StartsWith("/")) { 10 | /* 11 | * Each file can have additional settings area that starts with / and ends with next /. 12 | * E.g. "/readonly,nodriveletter/D:\Test.vhd" 13 | */ 14 | var iEndPipe = fileNameWithOptions.IndexOf("/", 1); 15 | var additionalSettings = fileNameWithOptions.Substring(1, iEndPipe - 1); 16 | this.FileName = fileNameWithOptions.Substring(iEndPipe + 1); 17 | foreach (var setting in additionalSettings.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { 18 | switch (additionalSettings.ToUpperInvariant()) { 19 | case "READONLY": this.ReadOnly = true; break; 20 | case "NODRIVELETTER": this.NoDriveLetter = true; break; 21 | } 22 | } 23 | } else { 24 | this.FileName = fileNameWithOptions; 25 | } 26 | } 27 | 28 | public string FileName { get; private set; } 29 | public bool ReadOnly { get; set; } 30 | public bool NoDriveLetter { get; set; } 31 | 32 | public override string ToString() { 33 | var options = new List(); 34 | if (this.ReadOnly) { options.Add("readonly"); } 35 | if (this.NoDriveLetter) { options.Add("nodriveletter"); } 36 | 37 | var sb = new StringBuilder(); 38 | if (options.Count >= 1) { 39 | sb.Append("/"); 40 | for (int i = 0; i < options.Count; i++) { 41 | if (i > 0) { sb.Append(","); } 42 | sb.Append(options[i]); 43 | } 44 | sb.Append("/"); 45 | } 46 | sb.Append(this.FileName); 47 | return sb.ToString(); 48 | } 49 | 50 | } 51 | } -------------------------------------------------------------------------------- /Source/VhdAttach-Service/Properties/App.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medo64/VhdAttach/1cf3394cb1939062bdc7a284549080548e78b035/Source/VhdAttach-Service/Properties/App.ico -------------------------------------------------------------------------------- /Source/VhdAttach-Service/Properties/App.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | true 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/Properties/App.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medo64/VhdAttach/1cf3394cb1939062bdc7a284549080548e78b035/Source/VhdAttach-Service/Properties/App.snk -------------------------------------------------------------------------------- /Source/VhdAttach-Service/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Resources; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | [assembly: AssemblyTitle("VhdAttachService")] 8 | [assembly: AssemblyDescription("Handles VHD tasks that require administrator priviledges.")] 9 | 10 | [assembly: CLSCompliant(false)] 11 | [assembly: ComVisible(false)] 12 | [assembly: NeutralResourcesLanguage("en-US")] 13 | 14 | 15 | [assembly: InternalsVisibleTo("VhdAttachTest, PublicKey=00240000048000009400000006020000002400005253413100040000010001008f35f900bf246cdcfd0d9eb278b58748591354ee056c42803acb0d99bbaf5e9deb5d995c47d15f925c39be2639210ac76d0417fc658754a432c899f1ddb1ddefa834a314775cf30f4db8739b65777752c700d316daaa43dc3b27c6ca2daa87332f6c6129e4e57f32324839d86a4bb02c9465c4c814a004a1f1b03997473efca8")] 16 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/Resources/Service_RunningInteractive_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medo64/VhdAttach/1cf3394cb1939062bdc7a284549080548e78b035/Source/VhdAttach-Service/Resources/Service_RunningInteractive_12.png -------------------------------------------------------------------------------- /Source/VhdAttach-Service/Resources/Service_Running_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medo64/VhdAttach/1cf3394cb1939062bdc7a284549080548e78b035/Source/VhdAttach-Service/Resources/Service_Running_12.png -------------------------------------------------------------------------------- /Source/VhdAttach-Service/Resources/Service_Stopped_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medo64/VhdAttach/1cf3394cb1939062bdc7a284549080548e78b035/Source/VhdAttach-Service/Resources/Service_Stopped_12.png -------------------------------------------------------------------------------- /Source/VhdAttach-Service/Resources/Service_Unknown_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medo64/VhdAttach/1cf3394cb1939062bdc7a284549080548e78b035/Source/VhdAttach-Service/Resources/Service_Unknown_12.png -------------------------------------------------------------------------------- /Source/VhdAttach-Service/Service.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Runtime.InteropServices; 5 | using System.Threading; 6 | using VhdAttachCommon; 7 | 8 | namespace VhdAttachService { 9 | 10 | internal static class Service { 11 | 12 | private static Thread _thread; 13 | private static ManualResetEvent _cancelEvent; 14 | 15 | 16 | public static void Start() { 17 | if (_thread != null) { return; } 18 | 19 | _cancelEvent = new ManualResetEvent(false); 20 | _thread = new Thread(Run); 21 | _thread.Name = "Service"; 22 | _thread.Start(); 23 | Debug.WriteLine("Service thread started."); 24 | } 25 | 26 | public static void Stop() { 27 | Debug.WriteLine("Service thread stopping..."); 28 | try { 29 | _cancelEvent.Set(); 30 | PipeServer.Pipe.Close(); 31 | NativeMethods.DeleteFile(PipeServer.Pipe.FullPipeName); //I have no idea why exactly this unblocks ConnectNamedPipe... 32 | for (int i = 0; i < 10; i++) { 33 | if (_thread.IsAlive) { Thread.Sleep(100); } else { break; } 34 | } 35 | if (_thread.IsAlive) { 36 | Debug.WriteLine("Service thread aborting..."); 37 | _thread.Abort(); 38 | } 39 | _thread = null; 40 | } catch { } 41 | Debug.WriteLine("Service thread stoped."); 42 | } 43 | 44 | private static void Run() { 45 | try { 46 | ThreadPool.QueueUserWorkItem(new WaitCallback(RunAttachAutomatics)); 47 | 48 | try { 49 | PipeServer.Start(); 50 | while (!_cancelEvent.WaitOne(0, false)) { 51 | try { 52 | var response = PipeServer.Receive(); 53 | if (response != null) { 54 | PipeServer.Reply(response); 55 | } 56 | } catch (Exception ex) { 57 | if (_cancelEvent.WaitOne(0, false)) { return; } 58 | Debug.WriteLine(ex.Message); 59 | PipeServer.Stop(); 60 | PipeServer.Start(); 61 | Thread.Sleep(50); 62 | } 63 | } 64 | } finally { 65 | Debug.WriteLine("AppServiceThread.Run: Finally."); 66 | PipeServer.Stop(); 67 | } 68 | 69 | } catch (ThreadAbortException) { 70 | Debug.WriteLine("AppServiceThread.Run: Thread aborted."); 71 | } 72 | } 73 | 74 | private static void RunAttachAutomatics(Object stateInfo) { 75 | var todoList = new List(ServiceSettings.AutoAttachVhdList); 76 | var failedList = new List(); 77 | 78 | for (int i = 0; i < 5; i++) { 79 | AttachAutomatics(todoList, failedList); 80 | if (failedList.Count == 0) { break; } //no failed 81 | todoList.Clear(); 82 | todoList.AddRange(failedList); //repeat only failed 83 | failedList.Clear(); 84 | Thread.Sleep(1000 + i * 2 * 1000); //give it a bit more time 85 | } 86 | } 87 | 88 | private static void AttachAutomatics(List todoList, List failedList) { 89 | foreach (var fwo in todoList) { 90 | try { 91 | Thread.Sleep(1000); //a bit of breather 92 | var access = Medo.IO.VirtualDiskAccessMask.All; 93 | var options = Medo.IO.VirtualDiskAttachOptions.PermanentLifetime; 94 | if (fwo.ReadOnly) { options |= Medo.IO.VirtualDiskAttachOptions.ReadOnly; } 95 | if (fwo.NoDriveLetter) { options |= Medo.IO.VirtualDiskAttachOptions.NoDriveLetter; } 96 | var fileName = fwo.FileName; 97 | using (var disk = new Medo.IO.VirtualDisk(fileName)) { 98 | disk.Open(access); 99 | disk.Attach(options); 100 | } 101 | } catch (Exception ex) { 102 | if (failedList != null) { failedList.Add(fwo); } 103 | Trace.TraceError("E: Cannot attach file \"" + fwo.FileName + "\". " + ex.Message); 104 | Medo.Diagnostics.ErrorReport.SaveToTemp(ex, fwo.FileName); 105 | } 106 | } 107 | } 108 | 109 | 110 | 111 | private static class NativeMethods { 112 | 113 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 114 | [return: MarshalAs(UnmanagedType.Bool)] 115 | public static extern Boolean DeleteFile(String lpFileName); 116 | 117 | } 118 | 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/ServiceStatusThread.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.ServiceProcess; 5 | using System.Threading; 6 | 7 | namespace VhdAttachService { 8 | internal static class ServiceStatusThread { 9 | 10 | private static Thread Thread; 11 | private static ManualResetEvent CancelEvent; 12 | 13 | public static void Start() { 14 | if (ServiceStatusThread.Thread != null) { return; } 15 | 16 | ServiceStatusThread.CancelEvent = new ManualResetEvent(false); 17 | ServiceStatusThread.Thread = new Thread(Run); 18 | ServiceStatusThread.Thread.Name = "Service status"; 19 | ServiceStatusThread.Thread.CurrentCulture = CultureInfo.InvariantCulture; 20 | ServiceStatusThread.Thread.Start(); 21 | } 22 | 23 | public static void Stop() { 24 | try { 25 | ServiceStatusThread.CancelEvent.Set(); 26 | while (ServiceStatusThread.Thread.IsAlive) { Thread.Sleep(10); } 27 | ServiceStatusThread.Thread = null; 28 | } catch { } 29 | } 30 | 31 | 32 | private static void Run() { 33 | try { 34 | var sw = new Stopwatch(); 35 | using (var service = new ServiceController(AppService.Instance.ServiceName)) { 36 | bool? lastIsRunning = null; 37 | Tray.SetStatusToUnknown(); 38 | while (!ServiceStatusThread.CancelEvent.WaitOne(0, false)) { 39 | if ((sw.IsRunning == false) || (sw.ElapsedMilliseconds > 1000)) { 40 | bool? currIsRunning; 41 | try { 42 | service.Refresh(); 43 | currIsRunning = (service.Status == ServiceControllerStatus.Running); 44 | } catch (InvalidOperationException) { 45 | currIsRunning = null; 46 | } 47 | if (lastIsRunning != currIsRunning) { 48 | if (currIsRunning == null) { 49 | Tray.SetStatusToUnknown(); 50 | } else if (currIsRunning == true) { 51 | Tray.SetStatusToRunning(); 52 | } else { 53 | Tray.SetStatusToStopped(); 54 | } 55 | } 56 | lastIsRunning = currIsRunning; 57 | sw.Reset(); 58 | sw.Start(); 59 | } 60 | Thread.Sleep(100); 61 | } 62 | } 63 | } catch (ThreadAbortException) { } 64 | } 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/Tray.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Drawing; 4 | using System.Globalization; 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | using System.Windows.Forms; 8 | 9 | namespace VhdAttachService { 10 | internal static class Tray { 11 | 12 | private static NotifyIcon Notify; 13 | 14 | internal static void Show() { 15 | Tray.Notify = new NotifyIcon(); 16 | Tray.Notify.ContextMenu = new ContextMenu(); 17 | Tray.Notify.ContextMenu.MenuItems.Add(new MenuItem("Exit", Tray_Exit_OnClick)); 18 | Tray.Notify.Icon = GetApplicationIcon(); 19 | Tray.Notify.Text = Medo.Reflection.CallingAssembly.Title; 20 | Tray.Notify.Visible = true; 21 | } 22 | 23 | internal static void SetStatusToRunningInteractive() { 24 | Tray.Notify.Icon = GetAnnotatedIcon(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(Medo.Reflection.CallingAssembly.Name + ".Resources.Service_RunningInteractive_12.png"))); 25 | Tray.Notify.Text = Medo.Reflection.CallingAssembly.Title + " (PID=" + Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture) + ")"; 26 | } 27 | 28 | internal static void SetStatusToUnknown() { 29 | Tray.Notify.Icon = GetAnnotatedIcon(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(Medo.Reflection.CallingAssembly.Name + ".Resources.Service_Unknown_12.png"))); 30 | Tray.Notify.Text = Medo.Reflection.CallingAssembly.Title + " - Unknown state."; 31 | } 32 | 33 | internal static void SetStatusToRunning() { 34 | Tray.Notify.Icon = GetAnnotatedIcon(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(Medo.Reflection.CallingAssembly.Name + ".Resources.Service_Running_12.png"))); 35 | Tray.Notify.Text = Medo.Reflection.CallingAssembly.Title + " - Running."; 36 | } 37 | 38 | internal static void SetStatusToStopped() { 39 | Tray.Notify.Icon = GetAnnotatedIcon(Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(Medo.Reflection.CallingAssembly.Name + ".Resources.Service_Stopped_12.png"))); 40 | Tray.Notify.Text = Medo.Reflection.CallingAssembly.Title + " - Stopped."; 41 | } 42 | 43 | internal static void Hide() { 44 | Tray.Notify.Visible = false; 45 | } 46 | 47 | 48 | private static void Tray_Exit_OnClick(object sender, EventArgs e) { 49 | Application.Exit(); 50 | } 51 | 52 | 53 | 54 | #region Helpers 55 | 56 | private static Icon GetAnnotatedIcon(Image annotation) { 57 | var icon = GetApplicationIcon(); 58 | 59 | if (icon != null) { 60 | var image = icon.ToBitmap(); 61 | if (icon != null) { 62 | using (var g = Graphics.FromImage(image)) { 63 | g.DrawImage(annotation, (int)g.VisibleClipBounds.Width - annotation.Width - 2, (int)g.VisibleClipBounds.Height - annotation.Height - 2); 64 | g.Flush(); 65 | } 66 | } 67 | return Icon.FromHandle(image.GetHicon()); 68 | } 69 | return null; 70 | } 71 | 72 | private static Icon GetApplicationIcon() { 73 | IntPtr hLibrary = NativeMethods.LoadLibrary(Assembly.GetEntryAssembly().Location); 74 | if (!hLibrary.Equals(IntPtr.Zero)) { 75 | IntPtr hIcon = NativeMethods.LoadImage(hLibrary, "#32512", NativeMethods.IMAGE_ICON, 20, 20, 0); 76 | if (!hIcon.Equals(System.IntPtr.Zero)) { 77 | Icon icon = Icon.FromHandle(hIcon); 78 | if (icon != null) { return icon; } 79 | } 80 | } 81 | return null; 82 | } 83 | 84 | private static class NativeMethods { 85 | 86 | public const UInt32 IMAGE_ICON = 1; 87 | 88 | 89 | [DllImport("user32.dll", CharSet = CharSet.Unicode)] 90 | static extern internal IntPtr LoadImage(IntPtr hInstance, String lpIconName, UInt32 uType, Int32 cxDesired, Int32 cyDesired, UInt32 fuLoad); 91 | 92 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] 93 | static extern internal IntPtr LoadLibrary(string lpFileName); 94 | 95 | } 96 | 97 | #endregion 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Source/VhdAttach-Service/VhdAttach-Service.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {6FACC55B-3441-4A4E-885B-269D82820F9E} 9 | WinExe 10 | Properties 11 | VhdAttachService 12 | VhdAttachService 13 | v4.0 14 | 15 | 16 | 512 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Properties\App.ico 26 | true 27 | Properties\App.snk 28 | VhdAttachService.App 29 | 30 | 31 | Properties\App.manifest 32 | 33 | 34 | true 35 | ..\..\Binaries\ 36 | DEBUG;TRACE 37 | full 38 | AnyCPU 39 | prompt 40 | false 41 | MinimumRecommendedRules.ruleset 42 | 43 | 44 | ..\..\Binaries\ 45 | TRACE 46 | true 47 | pdbonly 48 | AnyCPU 49 | false 50 | prompt 51 | BasicDesignGuidelineRules.ruleset 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Properties\SolutionInfo.cs 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Component 83 | 84 | 85 | 86 | Component 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | Designer 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 123 | -------------------------------------------------------------------------------- /Source/VhdAttach-Test/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | [assembly: AssemblyVersion("0.00.*")] 4 | -------------------------------------------------------------------------------- /Source/VhdAttach-Test/FileWithOptionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using VhdAttachCommon; 5 | 6 | namespace VhdAttachTest { 7 | 8 | [TestClass()] 9 | public class FileWithOptionsTest { 10 | 11 | public TestContext TestContext { get; set; } 12 | 13 | 14 | [TestMethod()] 15 | public void Test_FileWithOptions_Read_01() { 16 | var x = new FileWithOptions(@"/readonly/E:\Virtual Disks\Install.vhd"); 17 | Assert.AreEqual(@"E:\Virtual Disks\Install.vhd", x.FileName ); 18 | Assert.AreEqual(true, x.ReadOnly); 19 | Assert.AreEqual(false, x.NoDriveLetter); 20 | } 21 | 22 | [TestMethod()] 23 | public void Test_FileWithOptions_Read_02() { 24 | var x = new FileWithOptions(@"E:\Virtual Disks\Floppy.vhd"); 25 | Assert.AreEqual(@"E:\Virtual Disks\Floppy.vhd", x.FileName); 26 | Assert.AreEqual(false, x.ReadOnly); 27 | Assert.AreEqual(false, x.NoDriveLetter); 28 | } 29 | 30 | 31 | [TestMethod()] 32 | public void Test_FileWithOptions_Write_01() { 33 | var x = new FileWithOptions("Test.vhd"); 34 | x.ReadOnly = true; 35 | Assert.AreEqual("Test.vhd", x.FileName); 36 | Assert.AreEqual(true, x.ReadOnly); 37 | Assert.AreEqual(false, x.NoDriveLetter); 38 | Assert.AreEqual("/readonly/Test.vhd", x.ToString()); 39 | } 40 | 41 | [TestMethod()] 42 | public void Test_FileWithOptions_Write_02() { 43 | var x = new FileWithOptions("Test.vhd"); 44 | x.ReadOnly = true; 45 | x.NoDriveLetter = true; 46 | Assert.AreEqual("Test.vhd", x.FileName); 47 | Assert.AreEqual(true, x.ReadOnly); 48 | Assert.AreEqual(true, x.NoDriveLetter); 49 | Assert.AreEqual("/readonly,nodriveletter/Test.vhd", x.ToString()); 50 | } 51 | 52 | [TestMethod()] 53 | public void Test_FileWithOptions_Write_03() { 54 | var x = new FileWithOptions("/readonly/Test.vhd"); 55 | Assert.AreEqual(true, x.ReadOnly); 56 | x.ReadOnly = false; 57 | Assert.AreEqual("Test.vhd", x.FileName); 58 | Assert.AreEqual(false, x.ReadOnly); 59 | Assert.AreEqual(false, x.NoDriveLetter); 60 | Assert.AreEqual("Test.vhd", x.ToString()); 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Source/VhdAttach-Test/Properties/App.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medo64/VhdAttach/1cf3394cb1939062bdc7a284549080548e78b035/Source/VhdAttach-Test/Properties/App.snk -------------------------------------------------------------------------------- /Source/VhdAttach-Test/VhdAttach-Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 7 | 8 | 2.0 9 | {0F83D9F3-817E-4207-AF11-9F120A7AFC30} 10 | Library 11 | Properties 12 | VhdAttachTest 13 | VhdAttachTest 14 | v4.0 15 | 512 16 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | true 37 | 38 | 39 | Properties\App.snk 40 | 41 | 42 | 43 | 44 | 45 | 46 | 3.5 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | {523842B0-C6A8-4126-B9DE-0D9D814F838E} 60 | VhdAttach 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Code 71 | 72 | 73 | 74 | 81 | -------------------------------------------------------------------------------- /Source/VhdAttach.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 14 3 | VisualStudioVersion = 14.0.22823.1 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VhdAttach", "VhdAttach\VhdAttach.csproj", "{523842B0-C6A8-4126-B9DE-0D9D814F838E}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VhdAttach-Service", "VhdAttach-Service\VhdAttach-Service.csproj", "{6FACC55B-3441-4A4E-885B-269D82820F9E}" 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VhdAttach-Test", "VhdAttach-Test\VhdAttach-Test.csproj", "{0F83D9F3-817E-4207-AF11-9F120A7AFC30}" 10 | EndProject 11 | Global 12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 13 | Debug|Any CPU = Debug|Any CPU 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x86 = Release|x86 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {523842B0-C6A8-4126-B9DE-0D9D814F838E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {523842B0-C6A8-4126-B9DE-0D9D814F838E}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {523842B0-C6A8-4126-B9DE-0D9D814F838E}.Debug|x86.ActiveCfg = Debug|Any CPU 22 | {523842B0-C6A8-4126-B9DE-0D9D814F838E}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {523842B0-C6A8-4126-B9DE-0D9D814F838E}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {523842B0-C6A8-4126-B9DE-0D9D814F838E}.Release|x86.ActiveCfg = Release|Any CPU 25 | {6FACC55B-3441-4A4E-885B-269D82820F9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {6FACC55B-3441-4A4E-885B-269D82820F9E}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {6FACC55B-3441-4A4E-885B-269D82820F9E}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {6FACC55B-3441-4A4E-885B-269D82820F9E}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {6FACC55B-3441-4A4E-885B-269D82820F9E}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {6FACC55B-3441-4A4E-885B-269D82820F9E}.Release|x86.ActiveCfg = Release|Any CPU 31 | {0F83D9F3-817E-4207-AF11-9F120A7AFC30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {0F83D9F3-817E-4207-AF11-9F120A7AFC30}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {0F83D9F3-817E-4207-AF11-9F120A7AFC30}.Debug|x86.ActiveCfg = Debug|Any CPU 34 | {0F83D9F3-817E-4207-AF11-9F120A7AFC30}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {0F83D9F3-817E-4207-AF11-9F120A7AFC30}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {0F83D9F3-817E-4207-AF11-9F120A7AFC30}.Release|x86.ActiveCfg = Release|Any CPU 37 | EndGlobalSection 38 | GlobalSection(SolutionProperties) = preSolution 39 | HideSolutionNode = FALSE 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /Source/VhdAttach/(Medo)/BinaryPrefixExtensions [002x].cs: -------------------------------------------------------------------------------- 1 | //Josip Medved http://www.jmedved.com 2 | 3 | //2008-03-29: Initial version. 4 | //2008-11-15: All methods now accept long instead of double (to be in sync with IO classes). 5 | 6 | 7 | using System; 8 | using System.Globalization; 9 | 10 | namespace Medo.Extensions { 11 | 12 | /// 13 | /// Conversions to closest binary IEC prefix. 14 | /// This extension methods are intended for double. 15 | /// 16 | public static class BinaryPrefixExtensions { 17 | 18 | private static readonly double[] prefixBigValues = new double[] { System.Math.Pow(2, 60), System.Math.Pow(2, 50), System.Math.Pow(2, 40), System.Math.Pow(2, 30), System.Math.Pow(2, 20), System.Math.Pow(2, 10) }; 19 | private static readonly string[] prefixBigTexts = new string[] { "exbi", "pebi", "tebi", "gibi", "mebi", "kibi" }; 20 | private static readonly string[] prefixBigSymbols = new string[] { "Ei", "Pi", "Ti", "Gi", "Mi", "Ki" }; 21 | 22 | 23 | /// 24 | /// Converts the value of this instance to its equivalent string representation with measurement unit prefixed with binary IEC prefix symbol. 25 | /// 26 | /// Value to convert. 27 | /// Measurement unit to which prefix will be attached. 28 | public static string ToBinaryPrefixString(long value, string measurementUnit) { 29 | return ConvertToString(value, measurementUnit, null, CultureInfo.CurrentCulture, prefixBigValues , prefixBigSymbols ); 30 | } 31 | 32 | /// 33 | /// Converts the value of this instance to its equivalent string representation with measurement unit prefixed with binary IEC prefix symbol. 34 | /// 35 | /// Value to convert. 36 | /// Measurement unit to which prefix will be attached. 37 | /// A numeric format string for value part. 38 | public static string ToBinaryPrefixString(long value, string measurementUnit, string format) 39 | { 40 | return ConvertToString(value, measurementUnit, format, CultureInfo.CurrentCulture, prefixBigValues, prefixBigSymbols); 41 | } 42 | 43 | /// 44 | /// Converts the value of this instance to its equivalent string representation with measurement unit prefixed with binary IEC prefix symbol. 45 | /// 46 | /// Value to convert. 47 | /// Measurement unit to which prefix will be attached. 48 | /// A numeric format string for value part. 49 | /// An System.IFormatProvider that supplies culture-specific formatting information for value part. 50 | public static string ToBinaryPrefixString(long value, string measurementUnit, string format, IFormatProvider formatProvider) 51 | { 52 | return ConvertToString(value, measurementUnit, format, formatProvider, prefixBigValues, prefixBigSymbols); 53 | } 54 | 55 | 56 | /// 57 | /// Converts the value of this instance to its equivalent string representation with measurement unit prefixed with binary IEC prefix text. 58 | /// 59 | /// Value to convert. 60 | /// Measurement unit to which prefix will be attached. 61 | public static string ToLongBinaryPrefixString(long value, string measurementUnit) 62 | { 63 | return ConvertToString(value, measurementUnit, null, CultureInfo.CurrentCulture, prefixBigValues, prefixBigTexts); 64 | } 65 | 66 | /// 67 | /// Converts the value of this instance to its equivalent string representation with measurement unit prefixed with binary IEC prefix text. 68 | /// 69 | /// Value to convert. 70 | /// Measurement unit to which prefix will be attached. 71 | /// A numeric format string for value part. 72 | public static string ToLongBinaryPrefixString(long value, string measurementUnit, string format) 73 | { 74 | return ConvertToString(value, measurementUnit, format, CultureInfo.CurrentCulture, prefixBigValues, prefixBigTexts); 75 | } 76 | 77 | /// 78 | /// Converts the value of this instance to its equivalent string representation with measurement unit prefixed with binary IEC prefix text. 79 | /// 80 | /// Value to convert. 81 | /// Measurement unit to which prefix will be attached. 82 | /// A numeric format string for value part. 83 | /// An System.IFormatProvider that supplies culture-specific formatting information for value part. 84 | public static string ToLongBinaryPrefixString(long value, string measurementUnit, string format, IFormatProvider formatProvider) 85 | { 86 | return ConvertToString(value, measurementUnit, format, formatProvider, prefixBigValues, prefixBigTexts); 87 | } 88 | 89 | 90 | 91 | 92 | private static string ConvertToString(long value, string measurementUnit, string format, IFormatProvider formatProvider, double[] bigValues, string[] bigStrings) 93 | { 94 | for (int i = 0; i < bigValues.Length; ++i) { 95 | double prefixValue = bigValues[i]; 96 | if (value >= prefixValue) { 97 | return (value / prefixValue).ToString(format, formatProvider) + " " + bigStrings[i] + measurementUnit; 98 | } 99 | } 100 | 101 | return value.ToString(format, formatProvider) + " " + measurementUnit; 102 | } 103 | 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /Source/VhdAttach/(Medo)/EntryAssembly [003].cs: -------------------------------------------------------------------------------- 1 | //Josip Medved http://www.jmedved.com 2 | 3 | //2007-12-31: New version. 4 | //2008-05-22: Added Description. 5 | //2008-06-06: Added Copyright. 6 | 7 | 8 | using System.Reflection; 9 | 10 | namespace Medo.Reflection { 11 | 12 | /// 13 | /// Returns various info about assembly that started process. 14 | /// 15 | public static class EntryAssembly { 16 | 17 | private readonly static Assembly Assembly = System.Reflection.Assembly.GetEntryAssembly(); 18 | //private readonly static AssemblyName AssemblyName = Assembly.GetName(); 19 | 20 | /// 21 | /// Gets entry assembly's full name. 22 | /// 23 | public static string FullName { 24 | get { return System.Reflection.Assembly.GetEntryAssembly().GetName().FullName; } 25 | } 26 | 27 | /// 28 | /// Gets entry assembly's application name. 29 | /// 30 | public static string Name { 31 | get { return System.Reflection.Assembly.GetEntryAssembly().GetName().Name; } 32 | } 33 | 34 | /// 35 | /// Gets entry assembly's version. 36 | /// 37 | public static System.Version Version { 38 | get { return System.Reflection.Assembly.GetEntryAssembly().GetName().Version; } 39 | } 40 | 41 | /// 42 | /// Returns entry assembly's version in x.xx format. 43 | /// 44 | public static string ShortVersionString { 45 | get { 46 | System.Version version = EntryAssembly.Version; 47 | return version.Major.ToString("0", System.Globalization.CultureInfo.InvariantCulture) + "." + version.Minor.ToString("00", System.Globalization.CultureInfo.InvariantCulture); 48 | } 49 | } 50 | 51 | /// 52 | /// Returns entry assembly's version in x.xx.xxx.xxxx format. 53 | /// 54 | public static string LongVersionString { 55 | get { 56 | System.Version version = EntryAssembly.Version; 57 | return version.Major.ToString("0", System.Globalization.CultureInfo.CurrentCulture) + "." + version.Minor.ToString("00", System.Globalization.CultureInfo.CurrentCulture) + "." + version.Build.ToString("000", System.Globalization.CultureInfo.CurrentCulture) + "." + version.Revision.ToString("0000", System.Globalization.CultureInfo.CurrentCulture); 58 | } 59 | } 60 | 61 | /// 62 | /// Returns entry assembly's company or null if not found. 63 | /// 64 | public static string Company { 65 | get{ 66 | object[] companyAttributes = System.Reflection.Assembly.GetEntryAssembly().GetCustomAttributes(typeof(System.Reflection.AssemblyCompanyAttribute), true); 67 | if ((companyAttributes != null) && (companyAttributes.Length >= 1)) { 68 | return ((System.Reflection.AssemblyCompanyAttribute)companyAttributes[companyAttributes.Length - 1]).Company; 69 | } 70 | return null; 71 | } 72 | } 73 | 74 | /// 75 | /// Returns entry assembly's title or name if title is not found. 76 | /// 77 | public static string Title { 78 | get{ 79 | object[] titleAttributes = System.Reflection.Assembly.GetEntryAssembly().GetCustomAttributes(typeof(System.Reflection.AssemblyTitleAttribute), true); 80 | if ((titleAttributes != null) && (titleAttributes.Length >= 1)) { 81 | return ((System.Reflection.AssemblyTitleAttribute)titleAttributes[titleAttributes.Length - 1]).Title; 82 | } else { 83 | return Name; 84 | } 85 | } 86 | } 87 | 88 | /// 89 | /// Retuns entry's assembly product. If product is not found, title is returned. 90 | /// 91 | public static string Product { 92 | get{ 93 | object[] productAttributes = System.Reflection.Assembly.GetEntryAssembly().GetCustomAttributes(typeof(System.Reflection.AssemblyProductAttribute), true); 94 | if ((productAttributes != null) && (productAttributes.Length >= 1)) { 95 | return ((System.Reflection.AssemblyProductAttribute)productAttributes[productAttributes.Length - 1]).Product; 96 | } else { 97 | return Title; 98 | } 99 | } 100 | } 101 | 102 | /// 103 | /// Retuns entry's assembly description. If description is not found, empty string is returned. 104 | /// 105 | public static string Description { 106 | get { 107 | object[] descriptionAttributes = System.Reflection.Assembly.GetEntryAssembly().GetCustomAttributes(typeof(System.Reflection.AssemblyDescriptionAttribute), true); 108 | if ((descriptionAttributes != null) && (descriptionAttributes.Length >= 1)) { 109 | return ((System.Reflection.AssemblyDescriptionAttribute)descriptionAttributes[descriptionAttributes.Length - 1]).Description; 110 | } else { 111 | return string.Empty; 112 | } 113 | } 114 | } 115 | 116 | /// 117 | /// Retuns entry's assembly copyright. If copyright is not found, empty string is returned. 118 | /// 119 | public static string Copyright { 120 | get { 121 | object[] copyrightAttributes = Assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyCopyrightAttribute), true); 122 | if ((copyrightAttributes != null) && (copyrightAttributes.Length >= 1)) { 123 | return ((System.Reflection.AssemblyCopyrightAttribute)copyrightAttributes[copyrightAttributes.Length - 1]).Copyright; 124 | } else { 125 | return string.Empty; 126 | } 127 | } 128 | } 129 | 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /Source/VhdAttach/(Medo)/NumberDeclination [001].cs: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2008 Josip Medved 2 | 3 | //2008-08-09: Initial version. 4 | //2008-11-21: Added GetText method. 5 | // Added IDisposable. 6 | 7 | 8 | using System; 9 | using System.Globalization; 10 | 11 | namespace Medo.Localization.Croatia { 12 | 13 | /// 14 | /// Declines numbers and text according to croatian language rules. 15 | /// 16 | public class NumberDeclination : IDisposable { 17 | 18 | private string _text1; 19 | private string _text2; 20 | private string _text5; 21 | 22 | /// 23 | /// Creates new instance. 24 | /// 25 | /// Suffix text as if it is used with number 1 (ex. 1 "tim"). 26 | /// Suffix text as if it is used with number 2 (ex. 2 "tima"). 27 | /// Suffix text as if it is used with number 5 (ex. 5 "timova"). 28 | public NumberDeclination(string text1, string text2, string text5) { 29 | this._text1 = text1; 30 | this._text2 = text2; 31 | this._text5 = text5; 32 | } 33 | 34 | /// 35 | /// Gets number with appropriate suffix. 36 | /// 37 | /// Value 38 | public string this[int value] { 39 | get { 40 | return GetText(value); 41 | } 42 | } 43 | 44 | /// 45 | /// Gets number with appropriate suffix. 46 | /// 47 | /// Value 48 | public string GetText(int value) { 49 | int desetice = value % 100; 50 | int jedinice = value % 10; 51 | if ((desetice >= 10) && (desetice <= 20)) { 52 | return string.Format(CultureInfo.CurrentCulture, "{0} {1}", value, this._text5); 53 | } else if (jedinice == 1) { 54 | return string.Format(CultureInfo.CurrentCulture, "{0} {1}", value, this._text1); 55 | } else if ((jedinice >= 2) && (jedinice <= 4)) { 56 | return string.Format(CultureInfo.CurrentCulture, "{0} {1}", value, this._text2); 57 | } else { 58 | return string.Format(CultureInfo.CurrentCulture, "{0} {1}", value, this._text5); 59 | } 60 | } 61 | 62 | 63 | #region IDisposable Members 64 | 65 | /// 66 | /// Clean up any resources being used. 67 | /// 68 | /// True if managed resources should be disposed; otherwise, false. 69 | protected virtual void Dispose(bool disposing) { 70 | //no real dispose is neccessary, this is just for using() statement. 71 | } 72 | 73 | /// 74 | /// Clean up any resources being used. 75 | /// 76 | public void Dispose() { 77 | Dispose(true); 78 | GC.SuppressFinalize(this); 79 | } 80 | 81 | #endregion 82 | 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /Source/VhdAttach/(Medo)/StringAdder [002].cs: -------------------------------------------------------------------------------- 1 | //Josip Medved http://www.jmedved.com 2 | 3 | //2008-01-26: New version. 4 | //2008-04-11: Cleaned code to match FxCop 1.36 beta 2 (SpecifyIFormatProvider, SpecifyStringComparison). 5 | 6 | 7 | using System.Globalization; 8 | using System; 9 | namespace Medo.Text { 10 | 11 | /// 12 | /// StringBuilder with user defined separator. 13 | /// 14 | public class StringAdder { 15 | 16 | /// 17 | /// Creates new instance with system defined separator. 18 | /// 19 | public StringAdder() { 20 | this.StringBuilder = new System.Text.StringBuilder(); 21 | this.Separator = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ListSeparator; 22 | } 23 | 24 | /// 25 | /// Creates new instance with user defined separator. 26 | /// 27 | /// String to use for separating different values. 28 | public StringAdder(string separator) { 29 | this.StringBuilder = new System.Text.StringBuilder(); 30 | this.Separator = separator; 31 | } 32 | 33 | 34 | 35 | /// 36 | /// Appends a copy of specified string to instance. Separator is added in front of given value. 37 | /// 38 | /// The System.String to append. 39 | public void Append(string value) { 40 | Append(value, this.Separator); 41 | } 42 | 43 | /// 44 | /// Appends a copy of specified string to instance. Separator is added in front of given value. 45 | /// 46 | /// The System.String to append. 47 | /// If true, additional check is made to see if separator already exists. 48 | public void Append(string value, bool checkForExistingSeparator) { 49 | this.Append(value, this.Separator, checkForExistingSeparator); 50 | } 51 | 52 | /// 53 | /// Appends a copy of specified string to instance. Separator is added in front of given value. 54 | /// 55 | /// The System.String to append. 56 | /// Separator to be added before text. 57 | public void Append(string value, string separator) { 58 | this.Append(value, separator, false); 59 | } 60 | 61 | /// 62 | /// Appends a copy of specified string to instance. Separator is added in front of given value. 63 | /// 64 | /// The System.String to append. 65 | /// Separator to be added before text. 66 | /// If true, additional check is made to see if separator already exists. 67 | public void Append(string value, string separator, bool checkForExistingSeparator) { 68 | if (value == null) { return; } 69 | if (separator == null) { separator = string.Empty; } 70 | if (this.StringBuilder.Length == 0) { 71 | this.StringBuilder.Append(value); 72 | } else { 73 | if ((checkForExistingSeparator == false) || ((!this.StringBuilder.ToString().EndsWith(this.Separator, StringComparison.CurrentCulture)) && (!value.StartsWith(this.Separator, StringComparison.CurrentCulture)) && (!string.IsNullOrEmpty(value)))) { 74 | this.StringBuilder.Append(separator); 75 | } 76 | this.StringBuilder.Append(value); 77 | } 78 | } 79 | 80 | /// 81 | /// Appends a formatted string, which contains zero or more format specifications, to this instance. Each format specification is replaced by the string representation of a corresponding object argument. 82 | /// 83 | /// A string containing zero or more format specifications. 84 | /// An array of objects to format. 85 | /// A reference to this instance with format appended. Any format specification in format is replaced by the string representation of the corresponding object argument. 86 | public StringAdder AppendFormat(string format, params object[] args) { 87 | return AppendFormat(CultureInfo.CurrentCulture, format, args); 88 | } 89 | 90 | /// 91 | /// Appends a formatted string, which contains zero or more format specifications, to this instance. Each format specification is replaced by the string representation of a corresponding object argument. 92 | /// 93 | /// An System.IFormatProvider that supplies culture-specific formatting information. 94 | /// A string containing zero or more format specifications. 95 | /// An array of objects to format. 96 | /// A reference to this instance with format appended. Any format specification in format is replaced by the string representation of the corresponding object argument. 97 | public StringAdder AppendFormat(IFormatProvider provider, string format, params object[] args) { 98 | if (this.StringBuilder.Length == 0) { 99 | this._stringBuilder.AppendFormat(provider, format, args); 100 | } else { 101 | this.StringBuilder.Append(this.Separator); 102 | this._stringBuilder.AppendFormat(provider, format, args); 103 | } 104 | return this; 105 | } 106 | 107 | 108 | /// 109 | /// Converts this instance to string. 110 | /// 111 | /// String. 112 | public new string ToString() { 113 | return this.StringBuilder.ToString(); 114 | } 115 | 116 | 117 | 118 | private string _separator; 119 | /// 120 | /// Gets or sets string to use for separating different values. 121 | /// 122 | public string Separator { 123 | get { return this._separator; } 124 | set { this._separator = value; } 125 | } 126 | 127 | private System.Text.StringBuilder _stringBuilder; 128 | /// 129 | /// Gets underlying string builder. 130 | /// 131 | public System.Text.StringBuilder StringBuilder { 132 | get { return this._stringBuilder; } 133 | private set { this._stringBuilder = value; } 134 | } 135 | 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /Source/VhdAttach/(Medo)/UnhandledCatch [008].cs: -------------------------------------------------------------------------------- 1 | //Josip Medved http://www.jmedved.com 2 | 3 | //2007-12-30: New version. 4 | //2008-01-02: Added support for inner exceptions. 5 | // Added thread-safe locking. 6 | //2008-01-03: Added Resources. 7 | //2008-01-06: System.Environment.Exit returns E_UNEXPECTED (0x8000ffff). 8 | //2008-01-13: Changed default mode to ThrowException. 9 | // Uses FailFast to exit application. 10 | //2009-03-31: Changed FailFast to optional in order to avoid WER messages. 11 | //2010-11-07: Compatible with Mono (ignoring FailFast). 12 | //2010-11-22: Changed default exception mode to CatchException. 13 | 14 | 15 | using System; 16 | using System.Threading; 17 | 18 | namespace Medo.Application { 19 | 20 | /// 21 | /// Handling of unhandled errors. 22 | /// This class is thread-safe. 23 | /// 24 | public static class UnhandledCatch { 25 | 26 | /// 27 | /// Occurs when an exception is not caught. 28 | /// 29 | public static event EventHandler ThreadException; 30 | 31 | private static readonly object SyncRoot = new object(); 32 | 33 | /// 34 | /// Initializes handlers for unhandled exception. 35 | /// 36 | public static void Attach() { 37 | lock (SyncRoot) { 38 | Attach(System.Windows.Forms.UnhandledExceptionMode.CatchException, true); 39 | } 40 | } 41 | 42 | /// 43 | /// Initializes handlers for unhandled exception. 44 | /// 45 | /// Defines where a Windows Forms application should send unhandled exceptions. 46 | public static void Attach(System.Windows.Forms.UnhandledExceptionMode mode) { 47 | lock (SyncRoot) { 48 | Attach(mode, true); 49 | } 50 | } 51 | 52 | /// 53 | /// Initializes handlers for unhandled exception. 54 | /// 55 | /// Defines where a Windows Forms application should send unhandled exceptions. 56 | /// True to set the thread exception mode. 57 | public static void Attach(System.Windows.Forms.UnhandledExceptionMode mode, bool threadScope) { 58 | lock (SyncRoot) { 59 | System.Windows.Forms.Application.SetUnhandledExceptionMode(mode, threadScope); 60 | System.Windows.Forms.Application.ThreadException += Application_ThreadException; 61 | System.AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException; 62 | } 63 | } 64 | 65 | /// 66 | /// Initializes handlers for unhandled exception. 67 | /// 68 | /// Defines where a Windows Forms application should send unhandled exceptions. 69 | /// True to set the thread exception mode. 70 | /// When true, FailFast will be used to stop application. If false, standard Environment.Exit will be used instead. 71 | [Obsolete("Use UseFailFast property instead.")] 72 | public static void Attach(System.Windows.Forms.UnhandledExceptionMode mode, bool threadScope, bool useFailFast) { 73 | lock (SyncRoot) { 74 | UnhandledCatch.UseFailFast = useFailFast; 75 | System.Windows.Forms.Application.SetUnhandledExceptionMode(mode, threadScope); 76 | System.Windows.Forms.Application.ThreadException += Application_ThreadException; 77 | System.AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException; 78 | } 79 | } 80 | 81 | private static bool _useFailFast; 82 | /// 83 | /// Gets/sets whether to use FailFast terminating application. 84 | /// Under Mono this property always remains false. 85 | /// 86 | public static bool UseFailFast { 87 | get { lock (SyncRoot) { return _useFailFast; } } 88 | set { lock (SyncRoot) { _useFailFast = value && !IsRunningOnMono; } } 89 | } 90 | 91 | private static void AppDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e) { 92 | lock (SyncRoot) { 93 | Process(e.ExceptionObject as System.Exception); 94 | } 95 | } 96 | 97 | private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { 98 | lock (SyncRoot) { 99 | Process(e.Exception); 100 | } 101 | } 102 | 103 | 104 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "General exceptions are catched on purpose because this is handler for unhandled exceptions.")] 105 | private static void Process(System.Exception exception) { 106 | lock (SyncRoot) { 107 | System.Environment.ExitCode = unchecked((int)0x8000ffff); //E_UNEXPECTED(0x8000ffff) 108 | 109 | if (exception != null) { 110 | System.Diagnostics.Trace.TraceError(exception.ToString() + " {Medo.Application.UnhandledCatch}"); 111 | 112 | System.Windows.Forms.Application.ThreadException -= Application_ThreadException; 113 | System.AppDomain.CurrentDomain.UnhandledException -= AppDomain_UnhandledException; 114 | 115 | if (ThreadException != null) { ThreadException(null, new ThreadExceptionEventArgs(exception)); } 116 | } 117 | 118 | System.Diagnostics.Trace.TraceError("Exit(E_UNEXPECTED): Unhandled exception has occurred. {Medo.Application.UnhandledCatch}"); 119 | 120 | if (UnhandledCatch.UseFailFast) { 121 | System.Environment.FailFast(exception.Message); 122 | } else { 123 | System.Environment.Exit(unchecked((int)0x8000ffff)); //E_UNEXPECTED(0x8000ffff) 124 | } 125 | } 126 | } 127 | 128 | private static bool IsRunningOnMono { 129 | get { 130 | return (Type.GetType("Mono.Runtime") != null); 131 | } 132 | } 133 | 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /Source/VhdAttach/App.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | using System.Security.AccessControl; 7 | using System.Security.Principal; 8 | using System.Threading; 9 | using System.Windows.Forms; 10 | 11 | namespace VhdAttach { 12 | internal static class App { 13 | 14 | [STAThread] 15 | static void Main() { 16 | bool createdNew; 17 | var mutexSecurity = new MutexSecurity(); 18 | mutexSecurity.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow)); 19 | using (var setupMutex = new Mutex(false, @"Global\JosipMedved_VhdAttach", out createdNew, mutexSecurity)) { 20 | System.Windows.Forms.Application.EnableVisualStyles(); 21 | System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false); 22 | 23 | Medo.Application.UnhandledCatch.ThreadException += new EventHandler(UnhandledCatch_ThreadException); 24 | Medo.Application.UnhandledCatch.Attach(); 25 | 26 | if (!((Environment.OSVersion.Version.Build < 7000) || (App.IsRunningOnMono))) { 27 | var appId = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.FullName; 28 | if (appId.Length > 127) { appId = @"JosipMedved_VhdAttach\" + appId.Substring(appId.Length - 127 - 20); } 29 | NativeMethods.SetCurrentProcessExplicitAppUserModelID(appId); 30 | } else { 31 | Medo.MessageBox.ShowError(null, "This program requires Windows 7 or later."); 32 | System.Environment.Exit(1); 33 | } 34 | 35 | Medo.Windows.Forms.TaskbarProgress.DoNotThrowNotImplementedException = true; 36 | 37 | 38 | bool doAttach = Medo.Application.Args.Current.ContainsKey("Attach"); 39 | bool doDetach = Medo.Application.Args.Current.ContainsKey("Detach") && (!doAttach); 40 | bool doDetachDrive = Medo.Application.Args.Current.ContainsKey("DetachDrive") && (!doAttach) && (!doDetach); 41 | bool doChangeLetter = Medo.Application.Args.Current.ContainsKey("ChangeLetter") && (!doAttach) && (!doDetach) && (!doDetachDrive); 42 | 43 | bool doAnything = doAttach || doDetach || doDetachDrive || doChangeLetter; 44 | 45 | if (doAnything) { 46 | 47 | string[] argfiles = Medo.Application.Args.Current.GetValues(""); 48 | 49 | if (doChangeLetter) { 50 | CommandLineAddon cla = new CommandLineAddon(); 51 | int res = cla.ChangeDriveLetter(argfiles); 52 | System.Environment.Exit(res); 53 | return; 54 | } 55 | 56 | var files = new List(); 57 | foreach (var iFile in argfiles) { 58 | files.Add(new FileInfo(iFile.TrimEnd(new char[] { '\"' }))); 59 | } 60 | 61 | if (files.Count == 0) { 62 | System.Environment.Exit(1); 63 | return; 64 | } 65 | 66 | Form appForm = null; 67 | if (doAttach) { 68 | appForm = new AttachForm(files, Medo.Application.Args.Current.ContainsKey("readonly"), false); 69 | } else if (doDetach) { 70 | appForm = new DetachForm(files); 71 | } else if (doDetachDrive) { 72 | appForm = new DetachDriveForm(files); 73 | } 74 | 75 | if (appForm != null) { 76 | Medo.Windows.Forms.TaskbarProgress.DefaultOwner = appForm; 77 | Application.Run(appForm); 78 | System.Environment.Exit(System.Environment.ExitCode); 79 | } else { 80 | System.Environment.Exit(1); 81 | } 82 | 83 | } else { //open localy 84 | 85 | Application.Run(new MainForm()); 86 | 87 | } 88 | } 89 | } 90 | 91 | 92 | 93 | private static void UnhandledCatch_ThreadException(object sender, ThreadExceptionEventArgs e) { 94 | #if !DEBUG 95 | Medo.Diagnostics.ErrorReport.ShowDialog(null, e.Exception, new Uri("https://medo64.com/feedback/")); 96 | #else 97 | throw e.Exception; 98 | #endif 99 | } 100 | 101 | 102 | private static class NativeMethods { 103 | 104 | [DllImport("Shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 105 | public static extern UInt32 SetCurrentProcessExplicitAppUserModelID(String AppID); 106 | 107 | } 108 | 109 | private static bool IsRunningOnMono { 110 | get { 111 | return (Type.GetType("Mono.Runtime") != null); 112 | } 113 | } 114 | 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Source/VhdAttach/AttachForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VhdAttach { 2 | partial class AttachForm { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AttachForm)); 27 | this.progress = new System.Windows.Forms.ProgressBar(); 28 | this.StatusLabel = new System.Windows.Forms.Label(); 29 | this.bw = new System.ComponentModel.BackgroundWorker(); 30 | this.SuspendLayout(); 31 | // 32 | // progress 33 | // 34 | this.progress.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 35 | | System.Windows.Forms.AnchorStyles.Right))); 36 | this.progress.Location = new System.Drawing.Point(12, 72); 37 | this.progress.Margin = new System.Windows.Forms.Padding(3, 15, 3, 3); 38 | this.progress.Name = "progress"; 39 | this.progress.Size = new System.Drawing.Size(290, 23); 40 | this.progress.Style = System.Windows.Forms.ProgressBarStyle.Marquee; 41 | this.progress.TabIndex = 1; 42 | // 43 | // StatusLabel 44 | // 45 | this.StatusLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 46 | | System.Windows.Forms.AnchorStyles.Left) 47 | | System.Windows.Forms.AnchorStyles.Right))); 48 | this.StatusLabel.Location = new System.Drawing.Point(12, 9); 49 | this.StatusLabel.Name = "StatusLabel"; 50 | this.StatusLabel.Size = new System.Drawing.Size(290, 48); 51 | this.StatusLabel.TabIndex = 2; 52 | this.StatusLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; 53 | // 54 | // bw 55 | // 56 | this.bw.WorkerReportsProgress = true; 57 | this.bw.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_DoWork); 58 | this.bw.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_ProgressChanged); 59 | this.bw.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_RunWorkerCompleted); 60 | // 61 | // AttachForm 62 | // 63 | this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); 64 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 65 | this.ClientSize = new System.Drawing.Size(314, 107); 66 | this.Controls.Add(this.StatusLabel); 67 | this.Controls.Add(this.progress); 68 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 69 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 70 | this.MaximizeBox = false; 71 | this.MinimizeBox = false; 72 | this.Name = "AttachForm"; 73 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 74 | this.Text = "VHD Attach"; 75 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form_FormClosed); 76 | this.Load += new System.EventHandler(this.Form_Load); 77 | this.Shown += new System.EventHandler(this.Form_Shown); 78 | this.ResumeLayout(false); 79 | 80 | } 81 | 82 | #endregion 83 | 84 | private System.Windows.Forms.ProgressBar progress; 85 | private System.Windows.Forms.Label StatusLabel; 86 | private System.ComponentModel.BackgroundWorker bw; 87 | 88 | } 89 | } -------------------------------------------------------------------------------- /Source/VhdAttach/AttachForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.IO; 6 | using System.Windows.Forms; 7 | 8 | namespace VhdAttach { 9 | internal partial class AttachForm : Form { 10 | 11 | private readonly IList Files; 12 | private readonly bool MountReadOnly; 13 | private readonly bool InitializeDisk; 14 | private List _exceptions; 15 | 16 | private AttachForm() { 17 | InitializeComponent(); 18 | this.Font = SystemFonts.MessageBoxFont; 19 | } 20 | 21 | public AttachForm(IList files, bool mountReadOnly, bool initializeDisk) 22 | : this() { 23 | this.Files = files; 24 | this.MountReadOnly = mountReadOnly; 25 | this.InitializeDisk = initializeDisk; 26 | } 27 | 28 | public AttachForm(FileInfo file, bool mountReadOnly, bool initializeDisk) 29 | : this(new FileInfo[] { file }, mountReadOnly, initializeDisk) { 30 | } 31 | 32 | private void Form_Load(object sender, EventArgs e) { 33 | bw.RunWorkerAsync(); 34 | } 35 | 36 | private void Form_Shown(object sender, EventArgs e) { 37 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Indeterminate); 38 | } 39 | 40 | private void Form_FormClosed(object sender, FormClosedEventArgs e) { 41 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.NoProgress); 42 | } 43 | 44 | 45 | private void bw_DoWork(object sender, DoWorkEventArgs e) { 46 | this._exceptions = new List(); 47 | FileInfo iFile = null; 48 | try { 49 | for (var i = 0; i < this.Files.Count; ++i) { 50 | iFile = this.Files[i]; 51 | bw.ReportProgress(-1, iFile.Name); 52 | 53 | Utility.FixServiceErrorsIfNeeded(); 54 | var res = PipeClient.Attach(iFile.FullName, this.MountReadOnly, this.InitializeDisk); 55 | if (res.IsError) { 56 | this._exceptions.Add(new InvalidOperationException(iFile.Name, new Exception(res.Message))); 57 | } 58 | } 59 | } catch (IOException) { 60 | this._exceptions.Add(new InvalidOperationException(iFile.Name, new Exception(Messages.ServiceIOException))); 61 | } catch (Exception ex) { 62 | this._exceptions.Add(new InvalidOperationException(iFile.Name, ex)); 63 | } 64 | if (this._exceptions.Count > 0) { throw new InvalidOperationException(); } 65 | } 66 | 67 | private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { 68 | this.StatusLabel.Text = "Attaching" + Environment.NewLine + e.UserState.ToString(); 69 | } 70 | 71 | private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 72 | if (this.IsDisposed) { return; } 73 | 74 | this.progress.Value = 100; 75 | Medo.Windows.Forms.TaskbarProgress.SetPercentage(100); 76 | if (e.Error == null) { 77 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Normal); 78 | } else { 79 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Error); 80 | System.Environment.ExitCode = 1; 81 | foreach (var iException in this._exceptions) { 82 | Medo.MessageBox.ShowError(this, string.Format("Virtual disk file \"{0}\" cannot be attached.\n\n{1}", iException.Message, iException.InnerException.Message)); 83 | } 84 | } 85 | this.Close(); 86 | } 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Source/VhdAttach/ChangeDriveLetterForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VhdAttach { 2 | partial class ChangeDriveLetterForm { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | this.btnCancel = new System.Windows.Forms.Button(); 27 | this.btnOK = new System.Windows.Forms.Button(); 28 | this.lblDriveLetter = new System.Windows.Forms.Label(); 29 | this.cmbDriveLetter = new System.Windows.Forms.ComboBox(); 30 | this.SuspendLayout(); 31 | // 32 | // btnCancel 33 | // 34 | this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 35 | this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; 36 | this.btnCancel.Location = new System.Drawing.Point(118, 54); 37 | this.btnCancel.Margin = new System.Windows.Forms.Padding(3, 15, 3, 3); 38 | this.btnCancel.Name = "btnCancel"; 39 | this.btnCancel.Size = new System.Drawing.Size(100, 28); 40 | this.btnCancel.TabIndex = 3; 41 | this.btnCancel.Text = "Cancel"; 42 | this.btnCancel.UseVisualStyleBackColor = true; 43 | // 44 | // btnOK 45 | // 46 | this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 47 | this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; 48 | this.btnOK.Enabled = false; 49 | this.btnOK.Location = new System.Drawing.Point(12, 54); 50 | this.btnOK.Margin = new System.Windows.Forms.Padding(3, 15, 3, 3); 51 | this.btnOK.Name = "btnOK"; 52 | this.btnOK.Size = new System.Drawing.Size(100, 28); 53 | this.btnOK.TabIndex = 2; 54 | this.btnOK.Text = "Change"; 55 | this.btnOK.UseVisualStyleBackColor = true; 56 | this.btnOK.Click += new System.EventHandler(this.btnOK_Click); 57 | // 58 | // lblDriveLetter 59 | // 60 | this.lblDriveLetter.AutoSize = true; 61 | this.lblDriveLetter.Location = new System.Drawing.Point(12, 15); 62 | this.lblDriveLetter.Name = "lblDriveLetter"; 63 | this.lblDriveLetter.Size = new System.Drawing.Size(81, 17); 64 | this.lblDriveLetter.TabIndex = 0; 65 | this.lblDriveLetter.Text = "Drive letter:"; 66 | // 67 | // cmbDriveLetter 68 | // 69 | this.cmbDriveLetter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 70 | this.cmbDriveLetter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 71 | this.cmbDriveLetter.FormattingEnabled = true; 72 | this.cmbDriveLetter.Location = new System.Drawing.Point(143, 12); 73 | this.cmbDriveLetter.Name = "cmbDriveLetter"; 74 | this.cmbDriveLetter.Size = new System.Drawing.Size(75, 24); 75 | this.cmbDriveLetter.TabIndex = 1; 76 | this.cmbDriveLetter.SelectedIndexChanged += new System.EventHandler(this.cmbDriveLetter_SelectedIndexChanged); 77 | // 78 | // ChangeDriveLetterForm 79 | // 80 | this.AcceptButton = this.btnOK; 81 | this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); 82 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 83 | this.CancelButton = this.btnCancel; 84 | this.ClientSize = new System.Drawing.Size(230, 94); 85 | this.Controls.Add(this.cmbDriveLetter); 86 | this.Controls.Add(this.lblDriveLetter); 87 | this.Controls.Add(this.btnCancel); 88 | this.Controls.Add(this.btnOK); 89 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 90 | this.MaximizeBox = false; 91 | this.MinimizeBox = false; 92 | this.Name = "ChangeDriveLetterForm"; 93 | this.ShowIcon = false; 94 | this.ShowInTaskbar = false; 95 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 96 | this.Text = "Change drive letter"; 97 | this.ResumeLayout(false); 98 | this.PerformLayout(); 99 | 100 | } 101 | 102 | #endregion 103 | 104 | private System.Windows.Forms.Button btnCancel; 105 | private System.Windows.Forms.Button btnOK; 106 | private System.Windows.Forms.Label lblDriveLetter; 107 | private System.Windows.Forms.ComboBox cmbDriveLetter; 108 | } 109 | } -------------------------------------------------------------------------------- /Source/VhdAttach/ChangeDriveLetterForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Windows.Forms; 6 | using VhdAttachCommon; 7 | 8 | namespace VhdAttach { 9 | internal partial class ChangeDriveLetterForm : Form { 10 | 11 | public ChangeDriveLetterForm(Volume volume) { 12 | InitializeComponent(); 13 | this.Font = SystemFonts.MessageBoxFont; 14 | 15 | this.Volume = volume; 16 | 17 | string currDrive = volume.DriveLetter2; 18 | if (currDrive == null) { 19 | btnOK.Text = "Add"; 20 | } else { 21 | cmbDriveLetter.Items.Add(""); 22 | } 23 | 24 | var drives = new List(); 25 | for (char letter = 'A'; letter <= 'Z'; letter++) { 26 | drives.Add(letter.ToString() + ":"); 27 | } 28 | 29 | foreach (var drive in DriveInfo.GetDrives()) { 30 | var driveName = drive.Name.Substring(0, 2); 31 | if (driveName.Equals(currDrive, StringComparison.OrdinalIgnoreCase) == false) { 32 | drives.Remove(driveName); 33 | } 34 | } 35 | 36 | foreach (var drive in drives) { 37 | cmbDriveLetter.Items.Add(drive); 38 | } 39 | if (currDrive != null) { cmbDriveLetter.SelectedItem = currDrive; } 40 | } 41 | 42 | private readonly Volume Volume; 43 | 44 | 45 | private void cmbDriveLetter_SelectedIndexChanged(object sender, EventArgs e) { 46 | if (this.Volume.DriveLetter2 != null) { 47 | btnOK.Text = string.IsNullOrEmpty(cmbDriveLetter.Text) ? "Remove" : "Change"; 48 | } 49 | btnOK.Enabled = !(cmbDriveLetter.Text.Equals(this.Volume.DriveLetter2, StringComparison.InvariantCultureIgnoreCase)); 50 | } 51 | 52 | 53 | private void btnOK_Click(object sender, EventArgs e) { 54 | var driveLetter = string.IsNullOrEmpty(cmbDriveLetter.Text) ? "" : cmbDriveLetter.Text + "\\"; 55 | using (var frm = new ServiceWaitForm("Changing drive letter", 56 | delegate() { 57 | var res = PipeClient.ChangeDriveLetter(this.Volume.VolumeName, driveLetter); 58 | if (res.IsError) { 59 | throw new InvalidOperationException(res.Message); 60 | } 61 | })) { 62 | 63 | frm.ShowDialog(this); 64 | } 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Source/VhdAttach/ChangeDriveLetterForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | True 122 | 123 | 124 | True 125 | 126 | 127 | True 128 | 129 | 130 | True 131 | 132 | 133 | True 134 | 135 | -------------------------------------------------------------------------------- /Source/VhdAttach/CommandLineAddon.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Command line Addon - Exposes additional functionality to the command line 3 | * Author: Ralf Ostertag, (C) 2014 Ralf Ostertag 4 | * 5 | * ChangeLetter 6 | * Usage: VhdAttach -ChangeLetter VHDFilename DriveLetter 7 | * Drive letter can be: L, L:, L:\, l, l:, l:\ 8 | * 9 | * 2014-04-27 First version (adds ChangeDriveLetter) 10 | * 11 | */ 12 | 13 | using System; 14 | using System.Collections.Generic; 15 | using System.Text; 16 | using System.Runtime.InteropServices; 17 | using System.Windows.Forms; 18 | using System.IO; 19 | using VhdAttachCommon; 20 | 21 | namespace VhdAttach 22 | { 23 | /// 24 | /// Exposes additional functionality to the command line 25 | /// 26 | class CommandLineAddon 27 | { 28 | /// 29 | /// Change the drive letter of an attached VHD. 30 | /// 31 | public int ChangeDriveLetter(string[] args) 32 | { 33 | 34 | // Init and parameter checks 35 | 36 | if (args.Length != 2) { 37 | string err = "Changing drive letter failed. Wrong number of arguments.\n"; 38 | err += "Usage: VhdAttach -ChangeLetter VHDFilename Driveletter:"; 39 | showError(err); 40 | return 1; 41 | } 42 | 43 | string fileName = args[0]; 44 | 45 | if (!File.Exists(fileName)) { 46 | string err = "Changing drive letter failed. File not found: " + fileName + "\n"; 47 | err += "Usage: VhdAttach -ChangeLetter VHDFilename Driveletter:"; 48 | showError(err); 49 | return 1; 50 | } 51 | 52 | string driveLetterRaw = args[1]; 53 | 54 | if (driveLetterRaw.Length > 3 || (driveLetterRaw.Length == 3 && driveLetterRaw.Substring(1,2) != ":\\") || (driveLetterRaw.Length == 2 && driveLetterRaw.Substring(1,1) != ":")) { 55 | string err = "Changing drive letter failed. Drive Letter parameter seems to be invalid.\n"; 56 | err += "Usage: VhdAttach -ChangeLetter VHDFilename Driveletter:"; 57 | showError(err); 58 | return 1; 59 | } 60 | 61 | string allowedLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 62 | string driveLetter1 = args[1].Substring(0, 1); 63 | string driveLetter2 = driveLetter1 + ":"; 64 | 65 | if (allowedLetters.IndexOf(driveLetter1) == -1) { 66 | string err = "Changing drive letter failed. Drive Letter parameter seems to be invalid.\n"; 67 | err += "Usage: VhdAttach -ChangeLetter VHDFilename Driveletter:"; 68 | showError(err); 69 | return 1; 70 | } 71 | 72 | // Test-Code, please ignore 73 | // PipeClient.Attach(fileName, false, false); 74 | // PipeClient.Detach(fileName); 75 | 76 | // The actual drive letter operation begins here 77 | 78 | string attachedDevice = null; 79 | 80 | try { 81 | using (var document = new Medo.IO.VirtualDisk(fileName)) { 82 | document.Open(Medo.IO.VirtualDiskAccessMask.GetInfo); 83 | attachedDevice = document.GetAttachedPath(); 84 | } 85 | } catch { 86 | string err = "Changing Drive letter failed. Could not open device.\n"; 87 | err += "Possible reasons include:\n"; 88 | err += "The specified file might not be attached or maybe isn't an VHD file."; 89 | showError(err); 90 | return 1; 91 | } 92 | 93 | if (attachedDevice != null) { 94 | var volumes = new List(Volume.GetVolumesOnPhysicalDrive(attachedDevice)); 95 | var availableVolumes = new List(); 96 | if (volumes != null && volumes.Count > 0) { 97 | string oldLetter2 = volumes[0].DriveLetter2; 98 | PipeResponse res = PipeClient.ChangeDriveLetter(volumes[0].VolumeName, driveLetter2); 99 | if (res.IsError == true) { 100 | PipeClient.ChangeDriveLetter(volumes[0].VolumeName, oldLetter2); 101 | string err = "Changing drive letter failed. Drive letter possibly in use."; 102 | showError(err); 103 | return 1; 104 | } 105 | } 106 | } 107 | 108 | return 0; 109 | } 110 | 111 | 112 | [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)] 113 | static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName); 114 | 115 | [DllImport("user32.Dll")] 116 | static extern int PostMessage(IntPtr hWnd, UInt32 msg, int wParam, int lParam); 117 | 118 | private const UInt32 WM_CLOSE = 0x0010; 119 | 120 | /// 121 | /// Error MessageBox with timeout 122 | /// 123 | private void showError(string errorText) 124 | { 125 | string caption = "VhdAttach Error"; 126 | var timer = new System.Timers.Timer(5000) { AutoReset = false }; 127 | timer.Elapsed += delegate 128 | { 129 | IntPtr hWnd = FindWindowByCaption(IntPtr.Zero, caption); 130 | if (hWnd.ToInt32() != 0) PostMessage(hWnd, WM_CLOSE, 0, 0); 131 | }; 132 | timer.Enabled = true; 133 | MessageBox.Show(errorText, caption, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); 134 | } 135 | 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Source/VhdAttach/CreateFixedDiskForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VhdAttach { 2 | partial class CreateFixedDiskForm { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | this.btnCancel = new System.Windows.Forms.Button(); 27 | this.prg = new System.Windows.Forms.ProgressBar(); 28 | this.bgw = new System.ComponentModel.BackgroundWorker(); 29 | this.SuspendLayout(); 30 | // 31 | // btnCancel 32 | // 33 | this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 34 | this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; 35 | this.btnCancel.Location = new System.Drawing.Point(242, 53); 36 | this.btnCancel.Margin = new System.Windows.Forms.Padding(3, 15, 3, 3); 37 | this.btnCancel.Name = "btnCancel"; 38 | this.btnCancel.Size = new System.Drawing.Size(100, 28); 39 | this.btnCancel.TabIndex = 0; 40 | this.btnCancel.Text = "Cancel"; 41 | this.btnCancel.UseVisualStyleBackColor = true; 42 | this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); 43 | // 44 | // prg 45 | // 46 | this.prg.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 47 | | System.Windows.Forms.AnchorStyles.Right))); 48 | this.prg.Location = new System.Drawing.Point(12, 12); 49 | this.prg.Name = "prg"; 50 | this.prg.Size = new System.Drawing.Size(330, 23); 51 | this.prg.Style = System.Windows.Forms.ProgressBarStyle.Continuous; 52 | this.prg.TabIndex = 1; 53 | // 54 | // bgw 55 | // 56 | this.bgw.WorkerReportsProgress = true; 57 | this.bgw.WorkerSupportsCancellation = true; 58 | this.bgw.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bgw_DoWork); 59 | this.bgw.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bgw_ProgressChanged); 60 | this.bgw.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bgw_RunWorkerCompleted); 61 | // 62 | // CreateFixedDiskForm 63 | // 64 | this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); 65 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 66 | this.CancelButton = this.btnCancel; 67 | this.ClientSize = new System.Drawing.Size(354, 93); 68 | this.Controls.Add(this.prg); 69 | this.Controls.Add(this.btnCancel); 70 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 71 | this.MaximizeBox = false; 72 | this.MinimizeBox = false; 73 | this.Name = "CreateFixedDiskForm"; 74 | this.ShowIcon = false; 75 | this.ShowInTaskbar = false; 76 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 77 | this.Text = "Creating fixed disk"; 78 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form_FormClosing); 79 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form_FormClosed); 80 | this.Load += new System.EventHandler(this.Form_Load); 81 | this.ResumeLayout(false); 82 | 83 | } 84 | 85 | #endregion 86 | 87 | private System.Windows.Forms.Button btnCancel; 88 | private System.Windows.Forms.ProgressBar prg; 89 | private System.ComponentModel.BackgroundWorker bgw; 90 | } 91 | } -------------------------------------------------------------------------------- /Source/VhdAttach/CreateFixedDiskForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Threading; 6 | using System.Windows.Forms; 7 | using VirtualHardDiskImage; 8 | 9 | namespace VhdAttach { 10 | internal partial class CreateFixedDiskForm : Form { 11 | 12 | public CreateFixedDiskForm(string fileName, long sizeInBytes, bool isVhdX) { 13 | InitializeComponent(); 14 | this.Font = SystemFonts.MessageBoxFont; 15 | 16 | this.FileName = fileName; 17 | this.SizeInBytes = sizeInBytes; 18 | this.IsVhdX = isVhdX; 19 | } 20 | 21 | 22 | private readonly string FileName; 23 | private readonly long SizeInBytes; 24 | private readonly bool IsVhdX; 25 | 26 | 27 | private void Form_Load(object sender, System.EventArgs e) { 28 | bgw.RunWorkerAsync(); 29 | } 30 | 31 | private void Form_FormClosing(object sender, FormClosingEventArgs e) { 32 | if (e.CloseReason == CloseReason.UserClosing) { 33 | btnCancel.PerformClick(); 34 | e.Cancel = true; 35 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Error); 36 | } 37 | } 38 | 39 | private void Form_FormClosed(object sender, FormClosedEventArgs e) { 40 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.NoProgress); 41 | } 42 | 43 | 44 | private void bgw_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { 45 | if (this.IsVhdX) { 46 | bgw.ReportProgress(-1); 47 | if (CreateVhdX() == false) { 48 | e.Cancel = true; 49 | return; 50 | } 51 | } else { 52 | if (CreateVhd() == false) { 53 | e.Cancel = true; 54 | return; 55 | } 56 | } 57 | 58 | bgw.ReportProgress(100); 59 | } 60 | 61 | private void bgw_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) { 62 | if (e.ProgressPercentage >= 0) { 63 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Normal); 64 | Medo.Windows.Forms.TaskbarProgress.SetPercentage(e.ProgressPercentage); 65 | prg.Style = ProgressBarStyle.Continuous; 66 | prg.Value = e.ProgressPercentage; 67 | } else { 68 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Indeterminate); 69 | prg.Style = ProgressBarStyle.Marquee; 70 | } 71 | } 72 | 73 | private void bgw_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) { 74 | if (e.Cancelled) { 75 | this.DialogResult = DialogResult.Cancel; 76 | } else { 77 | this.DialogResult = DialogResult.OK; 78 | } 79 | } 80 | 81 | 82 | private void btnCancel_Click(object sender, System.EventArgs e) { 83 | bgw.CancelAsync(); 84 | btnCancel.Enabled = false; 85 | } 86 | 87 | 88 | private bool CreateVhd() { 89 | using (var stream = new FileStream(this.FileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 1, FileOptions.WriteThrough)) { 90 | ReFS.RemoveIntegrityStream(stream.SafeFileHandle); 91 | 92 | var footer = new HardDiskFooter(); 93 | footer.BeginUpdate(); 94 | footer.CreatorApplication = VhdCreatorApplication.JosipMedvedVhdAttach; 95 | footer.CreatorVersion = Medo.Reflection.EntryAssembly.Version; 96 | footer.SetSize((UInt64)this.SizeInBytes); 97 | footer.OriginalSize = footer.CurrentSize; 98 | footer.DiskType = VhdDiskType.FixedHardDisk; 99 | 100 | footer.EndUpdate(); 101 | 102 | var lastReport = DateTime.UtcNow; 103 | byte[] buffer = new byte[Settings.WriteBufferSize]; 104 | ulong remaining = footer.CurrentSize; 105 | while (remaining > 0) { 106 | if (bgw.CancellationPending) { 107 | stream.Dispose(); 108 | File.Delete(this.FileName); 109 | return false; 110 | } 111 | ulong count = (ulong)buffer.Length; 112 | if ((ulong)count > remaining) { count = remaining; } 113 | stream.Write(buffer, 0, (int)count); 114 | remaining -= count; 115 | if (lastReport.AddSeconds(1) < DateTime.UtcNow) { 116 | bgw.ReportProgress(100 - (int)(remaining * 100 / footer.CurrentSize)); 117 | lastReport = DateTime.UtcNow; 118 | } 119 | } 120 | buffer = footer.Bytes; 121 | stream.Write(buffer, 0, buffer.Length); 122 | } 123 | return true; 124 | } 125 | 126 | private bool CreateVhdX() { 127 | using (var vhdx = new Medo.IO.VirtualDisk(this.FileName)) { 128 | vhdx.CreateAsync(this.SizeInBytes, Medo.IO.VirtualDiskCreateOptions.FullPhysicalAllocation, 0, 0, Medo.IO.VirtualDiskType.Vhdx); 129 | var progress = vhdx.GetCreateProgress(); 130 | while (progress.IsDone == false) { 131 | //bgw.ReportProgress(progress.ProgressPercentage); 132 | bgw.ReportProgress(-1); 133 | if (bgw.CancellationPending) { 134 | //TODO 135 | return false; 136 | } 137 | Thread.Sleep(500); 138 | progress = vhdx.GetCreateProgress(); 139 | } 140 | } 141 | return true; 142 | } 143 | 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Source/VhdAttach/CreateFixedDiskForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | -------------------------------------------------------------------------------- /Source/VhdAttach/DetachDriveForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VhdAttach { 2 | partial class DetachDriveForm { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DetachDriveForm)); 27 | this.StatusLabel = new System.Windows.Forms.Label(); 28 | this.progress = new System.Windows.Forms.ProgressBar(); 29 | this.bw = new System.ComponentModel.BackgroundWorker(); 30 | this.SuspendLayout(); 31 | // 32 | // StatusLabel 33 | // 34 | this.StatusLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 35 | | System.Windows.Forms.AnchorStyles.Left) 36 | | System.Windows.Forms.AnchorStyles.Right))); 37 | this.StatusLabel.Location = new System.Drawing.Point(12, 9); 38 | this.StatusLabel.Name = "StatusLabel"; 39 | this.StatusLabel.Size = new System.Drawing.Size(290, 48); 40 | this.StatusLabel.TabIndex = 4; 41 | this.StatusLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; 42 | // 43 | // progress 44 | // 45 | this.progress.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 46 | | System.Windows.Forms.AnchorStyles.Right))); 47 | this.progress.Location = new System.Drawing.Point(12, 72); 48 | this.progress.Margin = new System.Windows.Forms.Padding(3, 15, 3, 3); 49 | this.progress.Name = "progress"; 50 | this.progress.Size = new System.Drawing.Size(290, 23); 51 | this.progress.Style = System.Windows.Forms.ProgressBarStyle.Marquee; 52 | this.progress.TabIndex = 3; 53 | // 54 | // bw 55 | // 56 | this.bw.WorkerReportsProgress = true; 57 | this.bw.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_DoWork); 58 | this.bw.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_ProgressChanged); 59 | this.bw.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_RunWorkerCompleted); 60 | // 61 | // DetachDriveForm 62 | // 63 | this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); 64 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 65 | this.ClientSize = new System.Drawing.Size(314, 107); 66 | this.Controls.Add(this.StatusLabel); 67 | this.Controls.Add(this.progress); 68 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 69 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 70 | this.MaximizeBox = false; 71 | this.MinimizeBox = false; 72 | this.Name = "DetachDriveForm"; 73 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 74 | this.Text = "VHD Attach"; 75 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form_FormClosed); 76 | this.Load += new System.EventHandler(this.Form_Load); 77 | this.Shown += new System.EventHandler(this.Form_Shown); 78 | this.ResumeLayout(false); 79 | 80 | } 81 | 82 | #endregion 83 | 84 | private System.Windows.Forms.Label StatusLabel; 85 | private System.Windows.Forms.ProgressBar progress; 86 | private System.ComponentModel.BackgroundWorker bw; 87 | } 88 | } -------------------------------------------------------------------------------- /Source/VhdAttach/DetachDriveForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.IO; 6 | using System.Windows.Forms; 7 | 8 | namespace VhdAttach { 9 | internal partial class DetachDriveForm : Form { 10 | 11 | private IList _files; 12 | private List _exceptions; 13 | 14 | 15 | public DetachDriveForm(IList file) { 16 | InitializeComponent(); 17 | this.Font = SystemFonts.MessageBoxFont; 18 | 19 | this._files = file; 20 | } 21 | 22 | private void Form_Load(object sender, EventArgs e) { 23 | bw.RunWorkerAsync(); 24 | } 25 | 26 | private void Form_Shown(object sender, EventArgs e) { 27 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Indeterminate); 28 | } 29 | 30 | private void Form_FormClosed(object sender, FormClosedEventArgs e) { 31 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.NoProgress); 32 | } 33 | 34 | 35 | private void bw_DoWork(object sender, DoWorkEventArgs e) { 36 | this._exceptions = new List(); 37 | FileSystemInfo iDirectory = null; 38 | try { 39 | for (var i = 0; i < this._files.Count; ++i) { 40 | iDirectory = new DirectoryInfo(this._files[i].FullName); 41 | bw.ReportProgress(-1, iDirectory.Name); 42 | 43 | Utility.FixServiceErrorsIfNeeded(); 44 | var res = PipeClient.DetachDrive(iDirectory.FullName); 45 | if (res.IsError) { 46 | this._exceptions.Add(new InvalidOperationException(iDirectory.Name, new Exception(res.Message))); 47 | } 48 | } 49 | } catch (TimeoutException) { 50 | this._exceptions.Add(new InvalidOperationException(iDirectory.Name, new Exception("Cannot access VHD Attach service."))); 51 | } catch (Exception ex) { 52 | this._exceptions.Add(new InvalidOperationException(iDirectory.Name, ex)); 53 | } 54 | if (this._exceptions.Count > 0) { throw new InvalidOperationException(); } 55 | } 56 | 57 | private static int IndexOfAny(string text, int startingIndex, params string[] fragment) { 58 | if ((fragment == null) || (fragment.Length == 0)) { return -1; } 59 | int minValue = text.IndexOf(fragment[0], startingIndex, StringComparison.InvariantCultureIgnoreCase); 60 | for (int i = 1; i < fragment.Length; ++i) { 61 | int iCurrMinValue = text.IndexOf(fragment[i], startingIndex); 62 | if ((iCurrMinValue >= 0) && (iCurrMinValue < minValue)) { 63 | minValue = iCurrMinValue; 64 | } 65 | } 66 | return minValue; 67 | } 68 | 69 | private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { 70 | if (e.UserState != null) { 71 | this.StatusLabel.Text = "Detaching drive" + Environment.NewLine + e.UserState.ToString(); 72 | } 73 | } 74 | 75 | private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 76 | if (this.IsDisposed) { return; } 77 | 78 | this.progress.Value = 100; 79 | Medo.Windows.Forms.TaskbarProgress.SetPercentage(100); 80 | if (e.Error == null) { 81 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Normal); 82 | } else { 83 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Error); 84 | System.Environment.ExitCode = 1; 85 | foreach (var iException in this._exceptions) { 86 | Medo.MessageBox.ShowError(this, string.Format("Drive \"{0}\" cannot be detached.\n\n{1}", iException.Message, iException.InnerException.Message)); 87 | } 88 | } 89 | this.Close(); 90 | } 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Source/VhdAttach/DetachForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VhdAttach { 2 | partial class DetachForm { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DetachForm)); 27 | this.StatusLabel = new System.Windows.Forms.Label(); 28 | this.progress = new System.Windows.Forms.ProgressBar(); 29 | this.bw = new System.ComponentModel.BackgroundWorker(); 30 | this.SuspendLayout(); 31 | // 32 | // StatusLabel 33 | // 34 | this.StatusLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 35 | | System.Windows.Forms.AnchorStyles.Left) 36 | | System.Windows.Forms.AnchorStyles.Right))); 37 | this.StatusLabel.Location = new System.Drawing.Point(12, 9); 38 | this.StatusLabel.Name = "StatusLabel"; 39 | this.StatusLabel.Size = new System.Drawing.Size(290, 48); 40 | this.StatusLabel.TabIndex = 4; 41 | this.StatusLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; 42 | // 43 | // progress 44 | // 45 | this.progress.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 46 | | System.Windows.Forms.AnchorStyles.Right))); 47 | this.progress.Location = new System.Drawing.Point(12, 72); 48 | this.progress.Margin = new System.Windows.Forms.Padding(3, 15, 3, 3); 49 | this.progress.Name = "progress"; 50 | this.progress.Size = new System.Drawing.Size(290, 23); 51 | this.progress.Style = System.Windows.Forms.ProgressBarStyle.Marquee; 52 | this.progress.TabIndex = 3; 53 | // 54 | // bw 55 | // 56 | this.bw.WorkerReportsProgress = true; 57 | this.bw.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_DoWork); 58 | this.bw.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_ProgressChanged); 59 | this.bw.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_RunWorkerCompleted); 60 | // 61 | // DetachForm 62 | // 63 | this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); 64 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 65 | this.ClientSize = new System.Drawing.Size(314, 107); 66 | this.Controls.Add(this.StatusLabel); 67 | this.Controls.Add(this.progress); 68 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 69 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 70 | this.MaximizeBox = false; 71 | this.MinimizeBox = false; 72 | this.Name = "DetachForm"; 73 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 74 | this.Text = "VHD Attach"; 75 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form_FormClosed); 76 | this.Load += new System.EventHandler(this.Form_Load); 77 | this.Shown += new System.EventHandler(this.Form_Shown); 78 | this.ResumeLayout(false); 79 | 80 | } 81 | 82 | #endregion 83 | 84 | private System.Windows.Forms.Label StatusLabel; 85 | private System.Windows.Forms.ProgressBar progress; 86 | private System.ComponentModel.BackgroundWorker bw; 87 | } 88 | } -------------------------------------------------------------------------------- /Source/VhdAttach/DetachForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.IO; 6 | using System.Windows.Forms; 7 | 8 | namespace VhdAttach { 9 | internal partial class DetachForm : Form { 10 | 11 | private IList _files; 12 | private List _exceptions; 13 | 14 | 15 | public DetachForm(IList file) { 16 | InitializeComponent(); 17 | this.Font = SystemFonts.MessageBoxFont; 18 | 19 | this._files = file; 20 | } 21 | 22 | private void Form_Load(object sender, EventArgs e) { 23 | bw.RunWorkerAsync(); 24 | } 25 | 26 | private void Form_Shown(object sender, EventArgs e) { 27 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Indeterminate); 28 | } 29 | 30 | private void Form_FormClosed(object sender, FormClosedEventArgs e) { 31 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.NoProgress); 32 | } 33 | 34 | 35 | private void bw_DoWork(object sender, DoWorkEventArgs e) { 36 | this._exceptions = new List(); 37 | FileInfo iFile = null; 38 | try { 39 | for (var i = 0; i < this._files.Count; ++i) { 40 | iFile = this._files[i]; 41 | bw.ReportProgress(-1, iFile.Name); 42 | 43 | Utility.FixServiceErrorsIfNeeded(); 44 | var res = PipeClient.Detach(iFile.FullName); 45 | if (res.IsError) { 46 | this._exceptions.Add(new InvalidOperationException(iFile.Name, new Exception(res.Message))); 47 | } 48 | } 49 | } catch (IOException) { 50 | this._exceptions.Add(new InvalidOperationException(iFile.Name, new Exception(Messages.ServiceIOException))); 51 | } catch (Exception ex) { 52 | this._exceptions.Add(new InvalidOperationException(iFile.Name, ex)); 53 | } 54 | if (this._exceptions.Count > 0) { throw new InvalidOperationException(); } 55 | } 56 | 57 | private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { 58 | this.StatusLabel.Text = "Detaching" + Environment.NewLine + e.UserState.ToString(); 59 | } 60 | 61 | private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 62 | if (this.IsDisposed) { return; } 63 | 64 | this.progress.Value = 100; 65 | Medo.Windows.Forms.TaskbarProgress.SetPercentage(100); 66 | if (e.Error == null) { 67 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Normal); 68 | } else { 69 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Error); 70 | System.Environment.ExitCode = 1; 71 | foreach (var iException in this._exceptions) { 72 | Medo.MessageBox.ShowError(this, string.Format("Virtual disk file \"{0}\" cannot be detached.\n\n{1}", iException.Message, iException.InnerException.Message)); 73 | } 74 | } 75 | this.Close(); 76 | } 77 | 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Source/VhdAttach/FileWithOptionsCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using VhdAttachCommon; 4 | 5 | namespace VhdAttach { 6 | internal class FileWithOptionsCollection : List { 7 | 8 | public FileWithOptionsCollection(IEnumerable collection) : base(collection) { } 9 | 10 | public bool Remove(string fileName) { 11 | FileWithOptions selectedItem = null; 12 | foreach (var item in this) { 13 | if (string.Equals(item.FileName, fileName, StringComparison.OrdinalIgnoreCase)) { 14 | selectedItem = item; 15 | break; 16 | } 17 | } 18 | return this.Remove(selectedItem); 19 | } 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/VhdAttach/Helper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Windows.Forms; 4 | 5 | namespace VhdAttach { 6 | internal static class Helper { 7 | 8 | #region ToolStripRenderer 9 | 10 | internal class ToolStripBorderlessProfessionalRenderer : ToolStripProfessionalRenderer { 11 | 12 | protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { 13 | e.Graphics.ResetClip(); 14 | e.Graphics.DrawLine(SystemPens.ControlDark, e.ToolStrip.ClientRectangle.Left, e.ToolStrip.ClientRectangle.Bottom - 1, e.ToolStrip.ClientRectangle.Right, e.ToolStrip.ClientRectangle.Bottom - 1); 15 | } 16 | 17 | } 18 | 19 | 20 | internal class ToolStripBorderlessSystemRenderer : ToolStripSystemRenderer { 21 | 22 | protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { 23 | //base.OnRenderToolStripBorder(e); 24 | } 25 | 26 | } 27 | 28 | #endregion 29 | 30 | 31 | #region Toolstrip images 32 | 33 | private static void GetToolstripSizeAndSet(ToolStrip toolstrip, out int size, out string set) { 34 | Form form = null; 35 | Control findParent = toolstrip; 36 | while ((findParent != null) && (form == null)) { 37 | form = findParent.Parent as Form; 38 | findParent = findParent.Parent; 39 | } 40 | if (form == null) { form = new Form(); } //workaround if parent is gone 41 | 42 | using (var g = form.CreateGraphics()) { 43 | var scale = Math.Max(Math.Max(g.DpiX, g.DpiY), 96.0) / 96.0; 44 | 45 | if (scale < 1.5) { 46 | size = 16; 47 | set = "_16"; 48 | } else if (scale < 2) { 49 | size = 24; 50 | set = "_24"; 51 | } else if (scale < 3) { 52 | size = 32; 53 | set = "_32"; 54 | } else { 55 | var base32 = 16 * scale / 32; 56 | var base48 = 16 * scale / 48; 57 | if ((base48 - (int)base48) < (base32 - (int)base32)) { 58 | size = 48 * (int)base48; 59 | set = "_48"; 60 | } else { 61 | size = 32 * (int)base32; 62 | set = "_32"; 63 | } 64 | } 65 | } 66 | } 67 | 68 | internal static void UpdateToolstripImages(ImageList imageList, ToolStrip toolstrip) { 69 | int size; 70 | string set; 71 | GetToolstripSizeAndSet(toolstrip, out size, out set); 72 | toolstrip.ImageScalingSize = new Size(size, size); 73 | 74 | var resources = VhdAttach.Properties.Resources.ResourceManager; 75 | foreach (ToolStripItem item in toolstrip.Items) { 76 | if (string.IsNullOrEmpty(item.Name)) { continue; } 77 | UpdateToolstripImage(item, item.Name, size, set); 78 | } 79 | 80 | if (imageList != null) { 81 | imageList.Images.Clear(); 82 | imageList.ImageSize = new Size(size, size); 83 | imageList.Images.Add(resources.GetObject("StatusInformation" + set) as Bitmap); 84 | imageList.Images.Add(resources.GetObject("StatusWarning" + set) as Bitmap); 85 | imageList.Images.Add(resources.GetObject("StatusError" + set) as Bitmap); 86 | imageList.Images.Add(resources.GetObject("StatusCritical" + set) as Bitmap); 87 | imageList.Images.Add(resources.GetObject("StatusLocked" + set) as Bitmap); 88 | } 89 | } 90 | 91 | private static void UpdateToolstripImage(ToolStripItem item, string name, int size, string set) { 92 | var resources = VhdAttach.Properties.Resources.ResourceManager; 93 | var resourceName = name + set; 94 | var bitmap = resources.GetObject(resourceName) as Bitmap; 95 | if (bitmap != null) { 96 | item.ImageScaling = ToolStripItemImageScaling.None; 97 | item.Image = new Bitmap(bitmap, new Size(size, size)); 98 | } 99 | } 100 | 101 | internal static void UpdateToolstripImage(ToolStripItem item, string name) { 102 | int size; 103 | string set; 104 | GetToolstripSizeAndSet(item.GetCurrentParent(), out size, out set); 105 | UpdateToolstripImage(item, name, size, set); 106 | } 107 | 108 | #endregion 109 | 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Source/VhdAttach/ListViewVhdItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Windows.Forms; 4 | using VhdAttachCommon; 5 | 6 | namespace VhdAttach { 7 | internal class ListViewVhdItem : ListViewItem { 8 | 9 | public ListViewVhdItem(FileWithOptions fwo) { 10 | this.FileName = fwo.FileName; 11 | this.IsReadOnly = fwo.ReadOnly; 12 | this.HasNoDriveLetter = fwo.NoDriveLetter; 13 | 14 | try { 15 | var file = new FileInfo(this.FileName); 16 | base.Text = file.Name; 17 | 18 | try { 19 | if (file.Exists) { 20 | if (file.IsReadOnly) { 21 | this.ToolTipText = "File is read-only." + Environment.NewLine + this.FileName; 22 | this.ImageIndex = 1; 23 | } else { 24 | this.ToolTipText = this.FileName; 25 | this.ImageIndex = (this.IsReadOnly) ? 4 : 0; 26 | } 27 | } else { 28 | this.ToolTipText = "File not found." + Environment.NewLine + this.FileName; 29 | this.ImageIndex = 2; 30 | } 31 | } catch (Exception ex) { 32 | this.ToolTipText = ex.Message + Environment.NewLine + this.FileName; 33 | this.ImageIndex = 3; 34 | } 35 | } catch (ArgumentException) { 36 | var segments = this.FileName.Split(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); 37 | if (segments.Length > 0) { 38 | base.Text = segments[segments.Length - 1]; 39 | } else { 40 | base.Text = this.FileName; 41 | } 42 | this.ToolTipText = "Cannot parse file name \"" + this.FileName + "\""; 43 | this.ImageIndex = 3; 44 | } 45 | 46 | } 47 | 48 | 49 | public string FileName { get; private set; } 50 | 51 | private bool _isReadOnly; 52 | public bool IsReadOnly { 53 | get { return this._isReadOnly; } 54 | set { 55 | this._isReadOnly = value; 56 | if ((this.ImageIndex == 0) || (this.ImageIndex == 4)) { 57 | this.ImageIndex = (value) ? 4 : 0; 58 | } 59 | } 60 | } 61 | 62 | public bool HasNoDriveLetter { get; private set; } 63 | 64 | 65 | public string GetSettingFileName() { 66 | var fwo = new FileWithOptions(this.FileName); 67 | fwo.ReadOnly = this.IsReadOnly; 68 | fwo.NoDriveLetter = this.HasNoDriveLetter; 69 | return fwo.ToString(); 70 | } 71 | 72 | 73 | public override string ToString() { 74 | return this.GetSettingFileName(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Source/VhdAttach/MainForm.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medo64/VhdAttach/1cf3394cb1939062bdc7a284549080548e78b035/Source/VhdAttach/MainForm.cs -------------------------------------------------------------------------------- /Source/VhdAttach/Messages.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace VhdAttach { 5 | internal static class Messages { 6 | 7 | public static readonly string ServiceIOException = "Cannot contact VHD Attach service."; 8 | 9 | public static void ShowServiceIOException(IWin32Window owner, Exception ex) { 10 | Medo.MessageBox.ShowError(owner, string.Format(ServiceIOException + "\n\n{0}", ex.Message)); 11 | } 12 | 13 | 14 | public static readonly string DiskTooSmall = "Disk is too small (8 MB minimum)."; 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/VhdAttach/PathFromDevice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Globalization; 5 | using System.Management; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | namespace VhdAttach { 10 | internal static class PathFromDevice { 11 | 12 | public static string[] GetPath(string device) { 13 | int driveNumber; 14 | if (device.StartsWith(@"\\.\PHYSICALDRIVE", StringComparison.InvariantCulture) && int.TryParse(device.Substring(17), NumberStyles.Integer, CultureInfo.InvariantCulture, out driveNumber)) { 15 | return GetLettersFromPhysicalDrive(driveNumber); 16 | } else if (device.StartsWith(@"\\.\CDROM", StringComparison.InvariantCulture) && int.TryParse(device.Substring(9), NumberStyles.Integer, CultureInfo.InvariantCulture, out driveNumber)) { 17 | return GetLettersFromCdRom(driveNumber); 18 | } else { 19 | return null; 20 | } 21 | } 22 | 23 | 24 | #region PhysicalDrive 25 | 26 | private static string[] GetLettersFromPhysicalDrive(int driveNumber) { 27 | var list = new List(); 28 | 29 | var wmiQuery = new ObjectQuery("SELECT Antecedent, Dependent FROM Win32_LogicalDiskToPartition"); 30 | using (var wmiSearcher = new ManagementObjectSearcher(wmiQuery)) { 31 | foreach (var iReturn in wmiSearcher.Get()) { 32 | var device = GetSubsubstring((string)iReturn["Antecedent"], "Win32_DiskPartition.DeviceID", "Disk #", ","); 33 | var letter = GetSubsubstring((string)iReturn["Dependent"], "Win32_LogicalDisk.DeviceID", "", ""); 34 | int diskNumber; 35 | if (int.TryParse(device, NumberStyles.Integer, CultureInfo.InvariantCulture, out diskNumber)) { 36 | if (driveNumber == diskNumber) { 37 | if ((letter.Length == 2) && (letter.EndsWith(":", StringComparison.OrdinalIgnoreCase))) { 38 | list.Add(letter + @"\"); 39 | } else { 40 | list.Add(letter); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | 47 | return list.ToArray(); 48 | } 49 | 50 | private static string GetSubsubstring(string value, string type, string start, string end) { 51 | var xStart0 = value.IndexOf(":" + type + "=\""); 52 | if (xStart0 < 0) { return null; } 53 | var xStart1 = value.IndexOf("\"", xStart0 + 1); 54 | if (xStart1 < 0) { return null; } 55 | var xEnd1 = value.IndexOf("\"", xStart1 + 1); 56 | if (xEnd1 < 0) { return null; } 57 | var extract = value.Substring(xStart1 + 1, xEnd1 - xStart1 - 1); 58 | 59 | int xStart2 = 0; 60 | if (!string.IsNullOrEmpty(start)) { xStart2 = extract.IndexOf(start); } 61 | if (xStart2 < 0) { return null; } 62 | 63 | int xEnd2 = extract.Length; 64 | if (!string.IsNullOrEmpty(end)) { xEnd2 = extract.IndexOf(end); } 65 | if (xEnd2 < 0) { return null; } 66 | 67 | return extract.Substring(xStart2 + start.Length, xEnd2 - xStart2 - start.Length); 68 | } 69 | 70 | #endregion 71 | 72 | 73 | #region CdRom 74 | 75 | private static string[] GetLettersFromCdRom(int driveNumber) { 76 | for (char letter = 'A'; letter <= 'Z'; letter++) { 77 | var dosDevice = letter + ":"; 78 | var sb = new StringBuilder(64); 79 | if (NativeMethods.QueryDosDeviceW(dosDevice, sb, (uint)sb.Capacity) > 0) { 80 | var dosPath = sb.ToString(); 81 | Debug.WriteLine(sb.ToString() + " is at " + dosDevice); 82 | if (dosPath.StartsWith(@"\Device\CdRom", StringComparison.OrdinalIgnoreCase)) { 83 | int cdromNumber = 0; 84 | if (int.TryParse(dosPath.Substring(13), NumberStyles.Integer, CultureInfo.InvariantCulture, out cdromNumber)) { 85 | if (cdromNumber == driveNumber) { 86 | return new string[] { dosDevice + @"\" }; 87 | } 88 | } 89 | } 90 | } 91 | } 92 | return null; 93 | } 94 | 95 | 96 | private static class NativeMethods { 97 | 98 | [DllImportAttribute("kernel32.dll", EntryPoint = "QueryDosDeviceW")] 99 | public static extern UInt32 QueryDosDeviceW( 100 | [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] string lpDeviceName, 101 | [OutAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] StringBuilder lpTargetPath, 102 | UInt32 ucchMax); 103 | 104 | } 105 | 106 | #endregion 107 | 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Source/VhdAttach/PipeClient.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.Threading; 5 | 6 | namespace VhdAttach { 7 | internal static class PipeClient { 8 | 9 | public static PipeResponse Attach(string path, bool mountReadOnly, bool initializeDisk) { 10 | var data = new Dictionary(); 11 | data.Add("Path", path); 12 | data.Add("MountReadOnly", mountReadOnly.ToString(CultureInfo.InvariantCulture)); 13 | data.Add("InitializeDisk", initializeDisk.ToString(CultureInfo.InvariantCulture)); 14 | return Send("Attach", data); 15 | } 16 | 17 | public static PipeResponse Detach(string path) { 18 | var data = new Dictionary(); 19 | data.Add("Path", path); 20 | return Send("Detach", data); 21 | } 22 | 23 | public static PipeResponse DetachDrive(string path) { 24 | var data = new Dictionary(); 25 | data.Add("Path", path); 26 | return Send("DetachDrive", data); 27 | } 28 | 29 | public static PipeResponse WriteContextMenuVhdSettings(bool open, bool attach, bool attachReadOnly, bool detach, bool detachDrive) { 30 | var data = new Dictionary(); 31 | data.Add("Open", open.ToString(CultureInfo.InvariantCulture)); 32 | data.Add("Attach", attach.ToString(CultureInfo.InvariantCulture)); 33 | data.Add("AttachReadOnly", attachReadOnly.ToString(CultureInfo.InvariantCulture)); 34 | data.Add("Detach", detach.ToString(CultureInfo.InvariantCulture)); 35 | data.Add("DetachDrive", detachDrive.ToString(CultureInfo.InvariantCulture)); 36 | return Send("WriteContextMenuVhdSettings", data); 37 | } 38 | 39 | public static PipeResponse WriteContextMenuIsoSettings(bool open, bool attachReadOnly, bool detach) { 40 | var data = new Dictionary(); 41 | data.Add("Open", open.ToString(CultureInfo.InvariantCulture)); 42 | data.Add("AttachReadOnly", attachReadOnly.ToString(CultureInfo.InvariantCulture)); 43 | data.Add("Detach", detach.ToString(CultureInfo.InvariantCulture)); 44 | return Send("WriteContextMenuIsoSettings", data); 45 | } 46 | 47 | public static PipeResponse WriteAutoAttachSettings(string[] autoAttachList) { 48 | var data = new Dictionary(); 49 | data.Add("AutoAttachList", string.Join("|", autoAttachList)); 50 | return Send("WriteAutoAttachSettings", data); 51 | } 52 | 53 | public static PipeResponse ChangeDriveLetter(string volumeName, string newDriveLetter) { 54 | var data = new Dictionary(); 55 | data.Add("VolumeName", volumeName); 56 | data.Add("NewDriveLetter", newDriveLetter); 57 | return Send("ChangeDriveLetter", data); 58 | } 59 | 60 | public static PipeResponse RegisterExtensionVhd() { 61 | var data = new Dictionary(); 62 | return Send("RegisterExtensionVhd", data); 63 | } 64 | 65 | private static Medo.IO.NamedPipe Pipe = new Medo.IO.NamedPipe("JosipMedved-VhdAttach-Commands"); 66 | 67 | private static PipeResponse Send(string operation, Dictionary data, int timeout = 5000) { 68 | var packetOut = new Medo.Net.TinyPacket("VhdAttach", operation, data); 69 | try { 70 | Pipe.Open(); 71 | Pipe.Write(packetOut.GetBytes()); 72 | var timer = new Stopwatch(); 73 | timer.Start(); 74 | while (timer.ElapsedMilliseconds < timeout) { 75 | if (Pipe.HasBytesToRead) { break; } 76 | Thread.Sleep(100); 77 | } 78 | timer.Stop(); 79 | if (Pipe.HasBytesToRead) { 80 | var buffer = Pipe.ReadAvailable(); 81 | var packetIn = Medo.Net.TinyPacket.Parse(buffer); 82 | return new PipeResponse(bool.Parse(packetIn["IsError"]), packetIn["Message"]); 83 | } else { 84 | return new PipeResponse(true, "Cannot contact service."); 85 | } 86 | } finally { 87 | Pipe.Close(); 88 | } 89 | } 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Source/VhdAttach/PipeResponse.cs: -------------------------------------------------------------------------------- 1 | namespace VhdAttach { 2 | internal class PipeResponse { 3 | 4 | public PipeResponse(bool isError, string message) { 5 | this.IsError = isError; 6 | this.Message = message; 7 | } 8 | 9 | public bool IsError { get; private set; } 10 | public string Message { get; private set; } 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/VhdAttach/Properties/App.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medo64/VhdAttach/1cf3394cb1939062bdc7a284549080548e78b035/Source/VhdAttach/Properties/App.ico -------------------------------------------------------------------------------- /Source/VhdAttach/Properties/App.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | true 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Source/VhdAttach/Properties/App.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/medo64/VhdAttach/1cf3394cb1939062bdc7a284549080548e78b035/Source/VhdAttach/Properties/App.snk -------------------------------------------------------------------------------- /Source/VhdAttach/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Resources; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | [assembly: AssemblyTitle("VhdAttach")] 8 | [assembly: AssemblyDescription("Handling VHD attach and detach operations.")] 9 | 10 | [assembly: CLSCompliant(false)] 11 | [assembly: ComVisible(false)] 12 | [assembly: NeutralResourcesLanguage("en-US")] 13 | 14 | 15 | [assembly: InternalsVisibleTo("VhdAttachTest, PublicKey=00240000048000009400000006020000002400005253413100040000010001008f35f900bf246cdcfd0d9eb278b58748591354ee056c42803acb0d99bbaf5e9deb5d995c47d15f925c39be2639210ac76d0417fc658754a432c899f1ddb1ddefa834a314775cf30f4db8739b65777752c700d316daaa43dc3b27c6ca2daa87332f6c6129e4e57f32324839d86a4bb02c9465c4c814a004a1f1b03997473efca8")] 16 | -------------------------------------------------------------------------------- /Source/VhdAttach/Properties/SolutionInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | [assembly: AssemblyProduct("VHD Attach")] 4 | [assembly: AssemblyCompany("Josip Medved")] 5 | [assembly: AssemblyCopyright("Copyright 2009 Josip Medved ")] 6 | 7 | [assembly: AssemblyVersion("0.00.*")] 8 | [assembly: AssemblyInformationalVersion("0.00")] 9 | -------------------------------------------------------------------------------- /Source/VhdAttach/RemoveIntegrityStreamForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VhdAttach { 2 | partial class RemoveIntegrityStreamForm { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | this.progress = new System.Windows.Forms.ProgressBar(); 27 | this.bwAction = new System.ComponentModel.BackgroundWorker(); 28 | this.SuspendLayout(); 29 | // 30 | // progress 31 | // 32 | this.progress.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 33 | | System.Windows.Forms.AnchorStyles.Left) 34 | | System.Windows.Forms.AnchorStyles.Right))); 35 | this.progress.Location = new System.Drawing.Point(12, 12); 36 | this.progress.Name = "progress"; 37 | this.progress.Size = new System.Drawing.Size(358, 23); 38 | this.progress.Style = System.Windows.Forms.ProgressBarStyle.Marquee; 39 | this.progress.TabIndex = 0; 40 | // 41 | // bwAction 42 | // 43 | this.bwAction.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bwAction_DoWork); 44 | this.bwAction.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bwAction_RunWorkerCompleted); 45 | // 46 | // RemoveIntegrityStreamForm 47 | // 48 | this.AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F); 49 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; 50 | this.ClientSize = new System.Drawing.Size(382, 47); 51 | this.ControlBox = false; 52 | this.Controls.Add(this.progress); 53 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 54 | this.MaximizeBox = false; 55 | this.MinimizeBox = false; 56 | this.Name = "RemoveIntegrityStreamForm"; 57 | this.ShowIcon = false; 58 | this.ShowInTaskbar = false; 59 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 60 | this.Text = "Removing integrity stream"; 61 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form_FormClosed); 62 | this.Load += new System.EventHandler(this.Form_Load); 63 | this.ResumeLayout(false); 64 | 65 | } 66 | 67 | #endregion 68 | 69 | private System.Windows.Forms.ProgressBar progress; 70 | private System.ComponentModel.BackgroundWorker bwAction; 71 | } 72 | } -------------------------------------------------------------------------------- /Source/VhdAttach/RemoveIntegrityStreamForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.IO; 4 | using System.Windows.Forms; 5 | 6 | namespace VhdAttach { 7 | internal partial class RemoveIntegrityStreamForm : Form { 8 | public RemoveIntegrityStreamForm(FileInfo file) { 9 | InitializeComponent(); 10 | this.Font = SystemFonts.MessageBoxFont; 11 | 12 | this.File = file; 13 | } 14 | 15 | private readonly FileInfo File; 16 | 17 | 18 | private void Form_Load(object sender, EventArgs e) { 19 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.Indeterminate); 20 | bwAction.RunWorkerAsync(); 21 | } 22 | 23 | private void Form_FormClosed(object sender, FormClosedEventArgs e) { 24 | Medo.Windows.Forms.TaskbarProgress.SetState(Medo.Windows.Forms.TaskbarProgressState.NoProgress); 25 | } 26 | 27 | 28 | private void bwAction_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { 29 | ReFS.RemoveIntegrityStream(this.File); 30 | } 31 | 32 | private void bwAction_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) { 33 | if (e.Error != null) { 34 | Medo.MessageBox.ShowError(this, "Cannot remove integrity stream.\n\n" + e.Error.Message); 35 | } 36 | this.Close(); 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/VhdAttach/RemoveIntegrityStreamForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | -------------------------------------------------------------------------------- /Source/VhdAttach/ServiceWaitForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VhdAttach { 2 | partial class ServiceWaitForm { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | this.progressBar1 = new System.Windows.Forms.ProgressBar(); 27 | this.bw = new System.ComponentModel.BackgroundWorker(); 28 | this.SuspendLayout(); 29 | // 30 | // progressBar1 31 | // 32 | this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 33 | | System.Windows.Forms.AnchorStyles.Left) 34 | | System.Windows.Forms.AnchorStyles.Right))); 35 | this.progressBar1.Location = new System.Drawing.Point(12, 12); 36 | this.progressBar1.Name = "progressBar1"; 37 | this.progressBar1.Size = new System.Drawing.Size(278, 23); 38 | this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee; 39 | this.progressBar1.TabIndex = 0; 40 | // 41 | // bw 42 | // 43 | this.bw.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_DoWork); 44 | this.bw.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_RunWorkerCompleted); 45 | // 46 | // ServiceWaitForm 47 | // 48 | this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); 49 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 50 | this.ClientSize = new System.Drawing.Size(302, 47); 51 | this.Controls.Add(this.progressBar1); 52 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 53 | this.MaximizeBox = false; 54 | this.MinimizeBox = false; 55 | this.Name = "ServiceWaitForm"; 56 | this.ShowIcon = false; 57 | this.ShowInTaskbar = false; 58 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 59 | this.Text = "-"; 60 | this.ResumeLayout(false); 61 | 62 | } 63 | 64 | #endregion 65 | 66 | private System.Windows.Forms.ProgressBar progressBar1; 67 | private System.ComponentModel.BackgroundWorker bw; 68 | } 69 | } -------------------------------------------------------------------------------- /Source/VhdAttach/ServiceWaitForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Reflection; 5 | using System.Windows.Forms; 6 | 7 | namespace VhdAttach { 8 | internal partial class ServiceWaitForm : Form { 9 | 10 | public ServiceWaitForm(string title, CrossAppDomainDelegate action) { 11 | InitializeComponent(); 12 | this.Font = SystemFonts.MessageBoxFont; 13 | this.ControlBox = false; 14 | 15 | this.Text = title; 16 | bw.RunWorkerAsync(action); 17 | } 18 | 19 | 20 | private void bw_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { 21 | var exceptions = new List(); 22 | var action = (Delegate)e.Argument; 23 | try { 24 | action.DynamicInvoke(); 25 | } catch (TargetInvocationException ex) { 26 | if (ex.InnerException != null) { 27 | throw ex.InnerException; 28 | } else { 29 | throw; 30 | } 31 | } 32 | } 33 | 34 | private void bw_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) { 35 | if (e.Error == null) { 36 | this.DialogResult = DialogResult.OK; 37 | } else { 38 | Messages.ShowServiceIOException(this, e.Error); 39 | this.DialogResult = DialogResult.Cancel; 40 | } 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Source/VhdAttach/ServiceWaitForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | -------------------------------------------------------------------------------- /Source/VhdAttach/Settings.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | namespace VhdAttach { 3 | 4 | internal static class Settings { 5 | 6 | public static bool ShowMenu { 7 | get { return Medo.Configuration.Settings.Read("ShowMenu", false); } 8 | set { Medo.Configuration.Settings.Write("ShowMenu", value); } 9 | } 10 | 11 | public static bool UseService { 12 | get { return Medo.Configuration.Settings.Read("UseService", true); } 13 | set { Medo.Configuration.Settings.Write("UseService", value); } 14 | } 15 | 16 | 17 | public static long LastSize { 18 | get { 19 | long size; 20 | if (long.TryParse(Medo.Configuration.Settings.Read("LastSize", "104857600"), NumberStyles.Integer, CultureInfo.InvariantCulture, out size)) { 21 | return size; 22 | } else { 23 | return 0; 24 | } 25 | } 26 | set { Medo.Configuration.Settings.Write("LastSize", value.ToString(CultureInfo.InvariantCulture)); } 27 | } 28 | 29 | public static string LastSizeUnit { 30 | get { return (Medo.Configuration.Settings.Read("LastSizeUnit", "GB").Equals("MB", System.StringComparison.OrdinalIgnoreCase)) ? "MB" : "GB"; } 31 | set { Medo.Configuration.Settings.Write("LastSizeUnit", value); } 32 | } 33 | 34 | public static bool LastSizeThousandBased { 35 | get { return Medo.Configuration.Settings.Read("LastSizeThousandBased", false); } 36 | set { Medo.Configuration.Settings.Write("LastSizeThousandBased", value); } 37 | } 38 | 39 | public static bool LastSizeFixed { 40 | get { return Medo.Configuration.Settings.Read("LastSizeFixed", false); } 41 | set { Medo.Configuration.Settings.Write("LastSizeFixed", value); } 42 | } 43 | 44 | public static bool LastSizeVhdX { 45 | get { return Medo.Configuration.Settings.Read("LastSizeVhdX", false); } 46 | set { Medo.Configuration.Settings.Write("LastSizeVhdX", value); } 47 | } 48 | 49 | public static int WriteBufferSize { 50 | get { return 1024 * 1024; } 51 | } 52 | 53 | 54 | public static double ScaleFactor { 55 | get { return Medo.Configuration.Settings.Read("ScaleFactor", 0.0); } 56 | set { Medo.Configuration.Settings.Write("ScaleFactor", 0.0); } 57 | } 58 | 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Source/VhdAttach/Utility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Reflection; 5 | using System.ServiceProcess; 6 | 7 | namespace VhdAttach { 8 | internal static class Utility { 9 | 10 | public static ProcessStartInfo GetProcessStartInfo(string exe, string arguments) { 11 | var startInfo = new ProcessStartInfo(); 12 | startInfo.CreateNoWindow = Process.GetCurrentProcess().StartInfo.CreateNoWindow; 13 | startInfo.FileName = exe; 14 | startInfo.Arguments = arguments; 15 | startInfo.WorkingDirectory = System.Environment.CurrentDirectory; 16 | return startInfo; 17 | } 18 | 19 | 20 | public static void FixServiceErrorsIfNeeded() { 21 | using (var service = new ServiceController("VhdAttach")) { 22 | try { 23 | if (service.Status != ServiceControllerStatus.Running) { 24 | try { 25 | Utility.ForceStartService(); 26 | } catch (InvalidOperationException) { } 27 | } 28 | } catch (InvalidOperationException) { 29 | try { 30 | Utility.ForceInstallService(); 31 | } catch (InvalidOperationException) { } 32 | } 33 | } 34 | } 35 | 36 | public static void ForceStartService() { 37 | try { 38 | var directory = (new FileInfo(Assembly.GetExecutingAssembly().Location)).DirectoryName; 39 | Process.Start(Path.Combine(directory, "VhdAttachService.exe"), "/Start").WaitForExit(); 40 | } catch (Exception ex) { 41 | throw new InvalidOperationException(ex.Message, ex); //cannot throw InvalidOperationException because of service existance detection 42 | } 43 | } 44 | 45 | public static void ForceInstallService() { 46 | try { 47 | var directory = (new FileInfo(Assembly.GetExecutingAssembly().Location)).DirectoryName; 48 | Process.Start(Path.Combine(directory, "VhdAttachService.exe"), "/Install").WaitForExit(); 49 | } catch (Exception ex) { 50 | throw new InvalidOperationException(ex.Message, ex); 51 | } 52 | } 53 | 54 | } 55 | } 56 | --------------------------------------------------------------------------------