发布版的dll无法运行但调试版dll可以运行的问题

17

在将我们的巨型分布式系统部署到客户端后,我们遇到了意外错误。在调查过程中,我们用添加了一些诊断代码的程序集替换了引起错误的程序集。我们使用的dll是以调试模式构建的。 突然间,一切都正常了!

用带有诊断代码的发布版本替换调试版本会再次导致崩溃。

我们的代码中没有预编译指令、条件调试属性等。这个问题已经在两个不同的安装站点中被发现,而在其他几个站点中运行良好。

(项目混合了C#和VB.NET,问题程序集是VB.NET...如果这有任何区别)

因此问题是:在这种情况下你会怎么做?通常情况下可能是什么原因?欢迎提供有关调试此问题的任何建议。


除了一般的“在调试中可以运行但在发布版本中无法运行”之外,提供一些关于问题的细节可能会有所帮助。这里的“它”是指什么?这里的“它”又是指什么? - user1228
我还没有找到确切的问题,但它是一个空引用异常(所以这并没有真正帮助,对吧!?)。 - Torbjørn
1
可能性,连同调用堆栈一起。检查调用堆栈是你应该做的第一件事情之一。 - user1228
16个回答

13

有些原因可能导致这种情况发生,一些症状的提示会有所帮助。可能的原因之一是你调用了像 Debug.WriteLine 这样具有副作用(即使代码工作)的方法。被标记为 [Conditional(...)] 的方法调用在没有定义正确符号的情况下不会被编译 - 因此任何标记为 [Conditional("DEBUG")] 的方法都将被静默丢弃。

另外也可能是编译器出现了错误,但这种情况比较少见(但不是不可能)。

请问这个问题的症状是什么?它会如何出错?

以上是一个例子。

    static string Bar { get; set; }
    static void Main()
    {
        Bar = "I'm broken";
        Debug.WriteLine(Foo());
        Console.WriteLine(Bar);
    }
    // note Foo only called in DEBUG builds
    static string Foo()
    {
        Bar = "I'm working";
        return "mwahahah";
    }

在 DEBUG 模式下编译,它会打印“我在工作”的信息;而在 RELEASE 模式下编译,它会打印“我崩溃了”的信息。这听起来相似吗?请检查您是否直接使用具有副作用的调试方法。在大多数情况下,您可以通过间接方式进行修复:

string foo = Foo();
Debug.WriteLine(foo);

现在它可以在任一模式下调用。


就像我说的,我认为我们的代码中没有任何预编译指令、条件调试属性等等。对于Debug.WriteLine也是同样的情况。但还需要进行更多的调查。 - Torbjørn

6
你可以尝试在“构建设置”中关闭“优化代码”。你实际上遇到了什么错误?
另一件事是在发布模式下编译,但启用#Debug条件。这将处理使用Diagnostics.Debug并且其中的代码影响应用程序的情况。

4

你尝试过包含调试文件吗?(pdbs)

如果你前往项目设置,然后选择“编译”选项卡,在顶部附近的下拉菜单中选择发布版本,然后在底部选择高级编译选项,请确保将其设置为创建完整的调试信息,然后重新部署,你应该可以获得有关崩溃原因的更详细信息。


4

我曾经看到过在调试版本和发布版本之间会出现时间问题。通常情况下,调试版本比发布版本运行得更慢。你可能需要检查代码中的关键时间段。


3

我曾遇到一个问题,涉及包含 WCF 客户端的 C# DLL,该 DLL 提供给多个客户端应用程序使用。

后来发现,在 C# 客户端库中有一个方法用于记录日志,其中访问了 StackTrace,而在调试模式下编译时,StackTrace 是不同的。

StackTrace stackTrace = new StackTrace();
MethodBase methodBase = stackTrace.GetFrame(2).GetMethod();

GetFrame(2)在发布版中不存在。更多信息请参见此处:StackTrace类的方法在发布模式下无法工作

希望能对您有所帮助。


3
Debug.Assert(ImportantMethod());

3

如果你不是在单线程代码中工作,那么可能会出现某种竞争条件。例如,如果程序的某部分需要在其他部分访问之前执行某些操作,则在发布模式下访问代码过早可能会导致失败。我们曾经遇到过类似的问题,针对移动电话编写的某些代码在模拟器中运行良好,但在速度较慢的手机上却完全不同。


1

确保应用程序在正确的平台目标下构建。这可能是一个问题,特别是在提供或使用DLL时。在“项目->属性”下查看并选择“生成”选项卡。平台目标选项将允许您在任意CPU(默认)、x86或x64之间进行选择。


在我的特定情况下,我最终将所有项目设置为x86进行构建。 - dev1998

1

首先,抱歉我的英语不好。我知道这篇文章很旧了,但我有同样的问题,并且我意识到在调试模式下,构建是为32位操作系统制作的,而发布模式默认为64位。这使得为32位制作的DLL在发布时无法工作。如果您转到项目属性 -> 构建,您可以选择所需的架构。这对我有效。


1
我曾经遇到过一个问题,即终结器比预期早地触发了,因为我没有完全理解终结器、垃圾回收器以及何时本地对象被视为可回收的相互作用(提示:它不是在块的闭合大括号处)。如果您的代码使用终结器,您可能希望研究GC.KeepAlive()。在以下块中:
void MyFunction() 
{ 
  Foo f = new Foo();
  SomeFunction(f.SomeProp);
}

fSomeFunction() 运行之前就有资格进行终结!如果终结器做了像处理 SomeProp 分配的任何内容这样的事情,你可能会遇到麻烦。在调用 SomeFunction 后添加对 GC.KeepAlive(f) 的调用可以确保在调用 KeepAlive() 之后才使 f 有资格进行终结。

编辑:在所有这些之后,我忘记指出这个问题在 Release 模式下更加明显。我不知道 Debug 构建是否为了调试器的好处在函数末尾放置了本地变量的隐式 KeepAlives,或者垃圾回收是否只是不那么积极,但在我的情况下,Release 模式大大加剧了这个问题。


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