Visual Studio 2010构建文件锁问题

21

我有一个Visual Studio 2008项目,我将其“升级”到了Visual Studio 2010。自从升级以来,我一直在这个项目中遇到很多问题(我想要补充的是,该项目在2008年仍然非常稳定)。

第一个问题是,构建主可执行文件会锁定该文件,导致后续重建失败。这在相关问题中有描述:Visual Studio locks output file on build,我从中找到了解决方法:

if exist "$(TargetPath).locked" del "$(TargetPath).locked"
if exist "$(TargetPath)" if not exist "$(TargetPath).locked" move "$(TargetPath)" "$(TargetPath).locked"

但这个解决方法只能有效地使用一次。.locked 文件会被 devenv.exe 锁定,必须将其移动。我通过添加 .1.locked、.2.locked 等来解决此问题。文件可以在 devenv.exe 关闭时删除。

调试器不必使用即可导致此问题,这表明了 2010 构建系统存在相当严重的问题。

我认为可以排除以下一些理论:

  • 杀毒软件或其他后台任务:如果这是一个问题,2008 年版本似乎也会失败。但是,作为完美主义者,我完全删除了 avast!系统,但没用。

更新:该项目在没有杀毒软件和备份实用程序的计算机上出现了相同的症状。办公室的计算机都运行 XP SP3 32 位操作系统,我的本地计算机是 Windows 7 64 位操作系统。这似乎与操作系统无关。

  • 调试器锁定文件:只需重复构建过程而不进行调试即可复现此问题。ProcessExplorer 显示 devenv.exe 是锁定的持有者,而不是 vshost;杀死 vshost.exe 不会移除锁。

一旦文件被锁定,我遇到了一个次要问题:表单设计器停止加载并出现“找不到程序集”错误。我认为这些问题与早期的锁定问题有关,因为在构建之前,设计器会立即启动,但是进行任何更改并重新构建后,所有设计器都会出现该错误(即使我已经打开并作为当前视图)。更改 "dummy=1" 为 "dummy=2" 可以强制重新编译完全无关联的程序集,从而导致表单关闭至白色错误屏幕。

更新:我尝试了一些其他疗法:未选中“启用 .NET 源代码步进”,因此这不是问题。删除 .SUO(解决方案用户选项)仅能在正常重启可以解决问题的时间内有效(两个构建:第一个是因为没有锁定文件,第二个是因为有一个,但是脚本可以将其重命名)。

Error   28  Unable to copy file "obj\Debug\PolicyTracker3.exe" to "bin\Debug\PolicyTracker3.exe". 
The process cannot access the file 'bin\Debug\PolicyTracker3.exe' because it is being used by another process.  

根据我在SO上得到的唯一答案中的信息,提到的功能(.NET框架源代码)从未启用过,也没有被启用。 - Godeke
记录一下,我也遇到了这个问题,解决方案也是从2008年转换过来的,所以可能与此有关。我没有遇到表单设计器的问题,但其他所有问题几乎都和你描述的一样。 - Brian Ortiz
@Brian Ortiz - 请尝试我在答案部分放置的代码。自从我添加了这个代码后,我发现偶尔需要处理锁定的DLL,但是当被锁定文件的重命名自动化时,这就不那么烦人了,您只需要每天重新启动一两次即可。 - Godeke
这似乎已经为我解决了:https://dev59.com/tHA75IYBdhLWcg3waIMJ - kirasglimmer
这对我来说听起来非常可信:当我不使用它们时,我有关闭那样的服务的倾向。我遇到问题的机器已经被“优化”了一些,而我的笔记本电脑(没有出现这种情况)正在运行该服务。一旦我再次使用该服务的机器运行一段时间后,我会更新这个评论! - Godeke
7个回答

15

在此之前,如果没有补丁可用,我有以下解决方法。只需使用类似 "C:\MyBin\VisualStudioLockWorkaround.exe" "$(TargetPath)" 的命令(将 MyBin 替换为您放置可执行文件的位置)。

将其创建为 C# 控制台应用程序并与原始预构建重命名一样在 Pre-Build 部分中使用(有关详细信息,请参阅原始问题的顶部)。

 using System;
 using System.IO;

 namespace VisualStudioLockWorkaround
 {
  class Program
  {
   static void Main(string[] args)
   {
    string file = args[0];
    string fileName = Path.GetFileName(file);
    string directory = Path.GetDirectoryName(args[0]);
    if (!Directory.Exists(directory)) //If we don't have a folder, nothing to do.
    {
     Console.WriteLine(String.Format("Folder {0} not found. Exiting.", directory));
     return;
    }
    if (!File.Exists(file)) //If the offending executable is missing, no reason to remove the locked files.
    {
     Console.WriteLine(String.Format("File {0} not found. Exiting.", file));
     return;
    }
    foreach (string lockedFile in Directory.EnumerateFiles(directory, "*.locked"))
    {
     try //We know IOExceptions will occur due to the locking bug.
     {
      File.Delete(lockedFile);
     }
     catch (IOException)
     {
      //Nothing to do, just absorbing the IO error.
     }
     catch (UnauthorizedAccessException)
     {
      //Nothing to do, just absorbing the IO error.
     }                                        
    }

    //Rename the executable to a .locked
    File.Move(file, Path.Combine(directory, String.Format("{0}{1:ddmmyyhhmmss}.locked", fileName, DateTime.Now)));
   }
  }
 }

修改后包括UnauthorizedAccessException。不知道这是否只是锁定文件抛出的异常(显然我原本期望的是IOException),还是它指向了问题的一部分? - Godeke

3

.NET框架源代码调试功能存在已知的文件句柄泄漏错误。可以通过在“工具+选项+调试器”中关闭该选项来轻松避免此问题。但是,如果您从未启动过调试器,则不太可能出现此问题。

不清楚为什么Visual Studio会对生成输出感兴趣,更不用说加载和锁定它了。也许您曾经打开过.exe文件?确保删除解决方案目录中的隐藏.suo文件。connect.microsoft.com上没有与您的问题匹配的反馈报告,我建议您自己开始一个。他们需要可重现的内容才能查看,请确保包含一个展示此行为的示例项目。


你有关于源代码步进问题的参考资料吗?我找到了2008年的相关参考资料,但他们声称在2010年已经修复了这个问题(这与我的经验完全相反)。虽然这不是构建问题的主要关注点,但我只是好奇细节。 - Godeke
我是在浏览连接报告时找到的。那是一个2010年的问题。由于丢失了链接,我相信你可以找回它。 - Hans Passant
奇怪的是,删除.suo文件似乎产生了某种差异。我会假设转换过程中出现了一些损坏(尽管我本来期望它会在转换时被删除)。 - Godeke
好的,不,它修复了一段时间(甚至在多次调试中),现在问题又回来了,就像最初所述的那样。我已经搜索了connect,但找不到有关泄漏文件问题的参考资料,但也许问题是相关的,因为如果过去进行了任何调试,则suo会更新为某些触发构建问题的内容? - Godeke

2

很遗憾,对于我来说重命名/移动锁定文件并没有起作用(句柄仅以FILE_SHARE_READ打开),但有效的方法是在Visual Studio进程中杀死那些(可能泄漏的)句柄。您可以使用KillHandle自动执行此步骤。


KillHandle 对我没用。它打印了“KillHandle:在进程 0x00000060 中杀死句柄 0x000036F0”,但 VS 仍然锁定文件。 - ssoldatenkov

1

我听说过这个解决方法,但我发现它实际上并没有帮助,这就是为什么我创建了这个丑陋的黑客。 - Godeke

1
在我的情况下,我有一个高度定制的Visual Studio 2010扩展构建,它在Visual Studio 2008中无问题运行。在构建的某个阶段,一些输出程序集被加载到一个单独的应用程序域中进行文档生成,然后卸载该域。确认通过检查应用程序域列表显示域卸载正常工作。
Visual Studio 2010似乎会在使用基于文件句柄或路径的Assembly.Load* API加载的任何程序集上持有文件句柄锁。我可以看到文件句柄出现在ProcessExplorer中,即使在AppDomain被卸载后,该句柄也永远不会被释放。
我能够通过使用FileStream和byte[] Assembly.Load重载来加载原始程序集字节来解决这个问题。

0

我来晚了,但这个问题是我们升级到VS2015后第一次出现的。在使用VS2010时从未发生过!

我们经常同时打开多个VS实例进行开发 - 这是一个客户端/服务器应用程序,我们同时对两个层进行更改。

升级到VS2015后,两个VS实例会相互“锁定”,无法在obj\Debug中构建共享项目,即我们的DTO、实体框架项目。

我尝试了预构建脚本,有时候它能够起作用。但对于我们的情况来说,它并不可靠 - 有时候dll被锁定得连重命名/移动都做不到。

最终,我只是为每个解决方案使用了不同的解决方案构建配置(从Debug复制)。这样创建了新的解决方案特定的obj和bin目录,并完全避免了与共享项目对应的dll的争用。

希望这能帮助其他人。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接