After years of pain, I finally found a clean-and-definitive way to get rid of the dreadful issue Could not copy assembly, the process cannot access the file because it is used by another process!
I have no idea how many .NET developers are coping with this issue, but on our side it used to be daily and there are many situations where an assembly file gets locked:
- sometimes VS just load in-process the assembly, or the PDB file or the .xml documentation file, for an unknown reason (usually after a debug session)
- sometimes the culprit seems to be vshost.exe
- sometimes the culprit is the test runner process
- sometimes, after a smoke test session, one just forgets to close all processes that hold assemblies…
- …or sometimes it is just a zombie process that should have stopped but just didn’t!
- and when developing a VS extension, all of those situations are happening more often
When the culprit process was not obviously identified, I started Process Explorer to kill it! And when the culprit process is my current VS instance, it means I need to restart it and interrupt current work for significant time and then lose my mental focus!!
The clean-and-definitive solution to this problem, is this script to copy in a .bat file. This script must be invoked from the VS project pre-build-events. It just moves the assembly file, the .pdb file and the .xml documentation file to a temporary location (if the .pdb or .xml file is missing no problem).
The key is that when a process locks a file, Windows authorizes to move and rename the file. I found the original idea from this stackoverflow answer, that itself found it from a Keyvan Nayyeri blog post that seems to have been removed.
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 |
REM This script is invoked before compiling an assembly, and if the target file exist, it moves it to a temporary location REM The file-move works even if the existing assembly file is currently locked-by/in-use-in any process. REM This way we can be sure that the compilation won't end up claiming the assembly cannot be erased! echo PreBuildEvents echo $(TargetPath) is %1 echo $(TargetFileName) is %2 echo $(TargetDir) is %3 echo $(TargetName) is %4 set dir=C:\temp\LockedAssemblies if not exist %dir% (mkdir %dir%) REM delete all assemblies moved not really locked by a process del "%dir%\*" /q REM assembly file (.exe / .dll) - .pdb file and eventually .xml file (documentation) are concerned REM use %random% to let coexists several processes that hold several versions of locked assemblies if exist "%1" move "%1" "%dir%\%2.locked.%random%" if exist "%3%4.pdb" move "%3%4.pdb" "%dir%\%4.pdb.locked%random%" if exist "%3%4.xml.locked" del "%dir%\%4.xml.locked%random%" REM Code with Macros REM if exist "$(TargetPath)" move "$(TargetPath)" "C:\temp\LockedAssemblies\$(TargetFileName).locked.%random%" REM if exist "$(TargetDir)$(TargetName).pdb" move "C:\temp\LockedAssemblies\$(TargetName).pdb" "$(TargetDir)$(TargetName).pdb.locked%random%" REM if exist "$(TargetDir)$(TargetName).xml.locked" del "C:\temp\LockedAssemblies\$(TargetName).xml.locked%random%" REM PreBuildEvent code REM $(SolutionDir)\BuildProcess\PreBuildEvents.bat "$(TargetPath)" "$(TargetFileName)" "$(TargetDir)" "$(TargetName)" REM References: REM http://www.hanselman.com/blog/ManagingMultipleConfigurationFileEnvironmentsWithPreBuildEvents.aspx REM http://stackoverflow.com/a/2738456/27194 REM http://stackoverflow.com/a/35800302/27194 |
To invoke this script just add this in all your VS project pre-build events command line. If you wish the script name and location to be different, just adapt $(SolutionDir)\BuildProcess\PreBuildEvents.bat
.
1 |
$(SolutionDir)\BuildProcess\PreBuildEvents.bat "$(TargetPath)" "$(TargetFileName)" "$(TargetDir)" "$(TargetName)" |
Notice that this script does the job no matter the actual configuration (Debug or Release) and no matter if the compilation is started from VS or from any other script.
-Patrick
Very nice. Works great. Thanks!
That doesn´t work for me 🙁
I don’t guarantee it works for all VS users, but at least it works for us 🙂
What problem exactly are you dealing with?
Compile complete — 0 errors, 1303 warnings
PreBuildEvents
$(TargetPath) is “E:\Bistro\bin\Debug\Bistro.exe”
$(TargetFileName) is “Bistro.exe”
$(TargetDir) is “E:\Bistro\bin\Debug\”
$(TargetName) is “Bistro”
Se han movido 1 archivos.
Se han movido 1 archivos.
El sistema no puede encontrar el archivo especificado.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3102,5): warning MSB3112: Two or more assemblies have the same identity ‘Bistro, Version=1.0.0.0, Culture=neutral, ProcessorArchitecture=x86’.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3102,5): warning MSB3181: Two or more files have the same target path ‘E:\Bistro\bin\Debug\Bistro.exe’.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3390,5): warning MSB3026: Could not copy “obj\x86\Debug\Bistro.exe” to “bin\Debug\Bistro.exe”. Beginning retry 1 in 1000ms. The process cannot access the file ‘bin\Debug\Bistro.exe’ because it is being used by another process.
….
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3390,5): error MSB3027: Could not copy “obj\x86\Debug\Bistro.exe” to “bin\Debug\Bistro.exe”. Exceeded retry count of 10. Failed.
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3390,5): error MSB3021: Unable to copy file “obj\x86\Debug\Bistro.exe” to “bin\Debug\Bistro.exe”. The process cannot access the file ‘bin\Debug\Bistro.exe’ because it is being used by another process.
I was struggling with this (worst?) issue and lost almost a day (Earlier whenever I faced this issue, it used to drive me crazy and I ended up doing all sort of things). However, this time it was different as I could figure out a way to solve the issue.
The solution was to : Click on Project Properties and DESELECT “Enable Visual Studio Hosting Process”
That’s it 🙂
But then you loose the Visual Studio Hosting Process benefits, with this astute you don’t 🙂
Hi,
“I started Process Explorer to kill it!”
Sometimes, the culprit is the Process explorer itself. Especially when you open the Properties window for the process and navigate through the tabs (probably it’s the Threads tab), then it may happen that the Process Explorer loads the PDB and does not free it up anymore.
As a result, you see that specific error in VS, eg. when you try to re-build your solution.
To fix that, just close the Process Explorer process (and also its taskbar icon).
Indeed, Process Explorer can also be the culprit, hopefully this astute fixes all situations 🙂
Is there any way I won’t have to type “-exec ” each line I wish to execute in Debug Console? Ubuntu, GDB, C language.
Most of the time I stuck when scripting this, but you did a great job to share this kind of information with us.