【问题标题】:When release dlls don't work but debug dlls do当发布 dll 不起作用但调试 dll 起作用时
【发布时间】:2020-05-20 15:11:51
【问题描述】:

将我们庞大的分布式系统部署到我们的一个客户后,我们遇到了意外错误。在调查期间,我们将导致错误的组件替换为添加了一些诊断代码的组件。我们使用的 dll 是在调试模式下构建的。突然之间,一切都奏效了!

用发布版本(带有诊断代码)替换调试 dll 会使其再次崩溃。

我们的代码中没有预编译器指令、条件调试属性等。该问题已在两个不同的安装站点中发现,而在其他几个站点中都可以正常工作。

(该项目混合了 C# 和 VB.NET,麻烦的程序集是 VB.NET..,如果这有什么不同的话)

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

【问题讨论】:

  • 这可能有助于提供有关问题的一些详细信息,而不是一般的“它在调试中有效但在发布中无效。”什么是“它”,什么是“它”?
  • 我还不能确定,但​​它是一个空引用异常(所以这并没有真正的帮助,对吧!?)。
  • 可能,连同调用堆栈。检查调用堆栈是您应该做的第一件事。

标签: .net debugging


【解决方案1】:

对于原因......好吧,一些症状的暗示会有所帮助。一种可能性是你有像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";
    }

在调试模式下编译它会打印“我正在工作”;在 RELEASE 模式下编译它会打印“我坏了”。这听起来相似吗?检查您没有直接调用任何带有副作用的调试方法。在大多数情况下,您可以通过间接方式修复:

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

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

【讨论】:

  • 正如我所说,我认为我们的代码中没有任何预编译器指令、条件调试属性等。 Debug.WriteLine 也是如此。但需要进行更多调查。
【解决方案2】:

您可以尝试在构建设置中关闭优化代码。你得到的实际错误是什么。

您可以做的另一件事是在发布模式下编译,但启用 #Debug 条件。如果您使用 Diagnostics.Debug 并且其中的代码影响您的应用程序,这将处理这种情况。

【讨论】:

    【解决方案3】:

    您是否尝试过包含调试文件? (pdb)

    如果你去你的项目设置,然后编译'选项卡'在顶部附近的下拉列表中选择你的发布版本,然后选择底部附近的高级编译选项,确保将其设置为创建完整的调试信息,然后重新部署,您现在应该获得有关崩溃原因的更多详细信息。

    【讨论】:

      【解决方案4】:

      我已经看到导致 Debug 和 Release 构建之间出现问题的时间。通常 Debug 构建运行比 Release 构建慢。您可能需要检查代码的时间关键部分。

      【讨论】:

        【解决方案5】:
        Debug.Assert(ImportantMethod());
        

        【讨论】:

          【解决方案6】:

          如果您不是在单线程代码中工作,这可能是您遇到的某种竞争条件。例如,如果您的程序的某些部分需要在其他部分访问它之前执行某些操作,它可能会在发布模式下失败,因为代码访问得太早。我们曾经遇到过类似的问题,一些手机代码在模拟器中运行良好,但在速度较慢的手机上,情况完全不一样。

          【讨论】:

            【解决方案7】:

            在包含为多个客户端应用程序提供的 WCF 客户端的 C# DLL 中遇到了同样的问题。

            发现 C# 客户端库中有一个方法访问 StackTrace 以进行日志记录,但在调试时编译时恰好不同。

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

            GetFrame(2) 在发行版中不存在。更多信息可以在这里找到:StackTrace class methods not working in release mode

            有趣的是,一个 VB.NET 客户端受到影响,而在另一个 C#.NET 客户端中却正常工作。

            希望对你有帮助。

            【讨论】:

              【解决方案8】:

              我曾遇到过终结器比预期更早关闭的问题,因为我没有完全理解终结器、垃圾收集器以及本地对象何时被视为可回收之间的相互作用(提示:它不是在结束时块的花括号)。如果您的代码使用终结器,您可能希望查看 GC.KeepAlive()。在以下块中:

              void MyFunction() 
              { 
                Foo f = new Foo();
                SomeFunction(f.SomeProp);
              }
              

              fSomeFunction() 甚至运行之前就有资格完成!如果终结者做了类似处置SomeProp 分发的任何事情,你可能会遇到麻烦。在对SomeFunction 的调用之后添加对GC.KeepAlive(f) 的调用可确保f 在调用KeepAlive() 之前没有资格完成。

              编辑:毕竟,我忘了指出这个问题在发布模式下运行时更加明显。我不知道 Debug 构建是否将隐式 KeepAlives 放在函数末尾以使调试器受益,或者垃圾收集是否只是不那么激进,或者是什么,但在我的情况下,Release 模式极大地加剧了这个问题。

              【讨论】:

                【解决方案9】:

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

                【讨论】:

                • 我的特殊情况,我最终将所有项目设置为 x86。
                【解决方案10】:

                首先对我的英语感到抱歉。我知道这篇文章很旧,但我也遇到了同样的问题,我意识到在调试模式下,构建是为 32 位操作系统 制作的,而发布模式是 64 位强> 默认情况下。这使得为​​ 32 位制作的 DLL 在发行版中不起作用。如果你去 Project properties -> build 你可以选择你想要的架构。这对我有用。

                【讨论】:

                  【解决方案11】:

                  您可能知道这一点,但是, 在调试和发布版本中,变量的初始化有时会有所不同。 例如。 我认为变量在 VC6 调试版本中是自动初始化的,如果你没有初始化某些东西,这可能会隐藏问题。我还认为调试数组可能会使用哨兵字节来指示溢出。这也可能导致不同的行为。

                  【讨论】:

                  • 如果提问者没有表示他们正在使用托管代码,我会 +1。当我遇到这样的问题时,10 次中有 9 次出现在 c++ 中,这是因为我忘记在 .ctor 中正确初始化某些东西。对我来说幸运的是,这些错误都没有进入生产阶段。
                  【解决方案12】:

                  您的问题解决了吗?
                  我和你有同样的问题。
                  如果我在调试中编译 dll,一切正常。
                  如果我在发行版中编译,我会得到一个空引用异常。
                  但是,如果我在某些方法调用中包含了与上述类似的一些行,那么即使在发布模式下,异常也会消失:
                  System.Diagnostics.EventLog.WriteEntry("blablabla", "blablabla")

                  希望对你有所帮助。

                  【讨论】:

                    【解决方案13】:

                    我遇到了类似的问题。我的情况是这样的: 我在类库 A 中定义了一些反射函数。然后我定义了一个 WPF 用户控件库 B,它使用库 A 中的函数。然后我编写了一个应用程序,它使用库 B 中的用户控件和库 A 中的函数。当我使用库B的调试版本,它工作正常。但是当我使用库B的发布版本时,反射功能不起作用。我还在库A中定义了其他函数。似乎只有反射函数会造成麻烦。我想不出原因。最后,我放弃了,将反射函数从库 A 移到库 B。它成功了。

                    【讨论】:

                      【解决方案14】:

                      在我的情况下,我的 DLL 使用者项目(在 VS 中)具有 x64 配置,但解决方案在任何 CPU 中。 由于某种原因,在运行应用程序时,这并没有与我的 x64 DLL 挂钩。 我将应用程序配置为明确的 x64 平台,一切正常。

                      【讨论】:

                        【解决方案15】:

                        这可能与原始问题无关,但对于任何运行应用程序的人来说

                        var isThisMyAssembly = System.Reflection.Assembly.GetCallingAssembly();
                        

                        该调用可能会在运行时内联到另一个程序集中的方法中,因此会得到意外的输出(如 MSDN documentation 中所述)。

                        为避免这种情况,您可以将MethodImplAttribute 应用于您的方法:

                        [MethodImpl(MethodImplOptions.NoInlining)]
                        void DoSomeThings()
                        {
                            var isThisMyAssembly = System.Reflection.Assembly.GetCallingAssembly();
                            //do stuff
                        }
                        

                        或者您可以改用Assembly.GetExecutingAssembly()

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2018-12-21
                          • 2013-06-08
                          • 2012-05-31
                          • 1970-01-01
                          • 2017-03-03
                          相关资源
                          最近更新 更多