我有一个非常令人沮丧的错误,团队中的其他人无法重现。我尝试清理项目,擦除目录,从存储库中提取,重建,甚至在VS 2013中进行测试。
背景:a.c,b.c,c.c和b.inc都被编译成一个DLL(假设它们是外部的)。 首先从托管代码调用setfoo()
。目前为止,一切都好。
后来,testfoo()
被召唤。全局变量很好,没有问题。然后调用testfoo2()
。这就是事情变得有趣的地方。我有带有foo's
地址的内存调试器,它将在内存中读取 4。但是,如果将鼠标悬停在 Visual Studio 中的代码上,它将返回 0!。此外,它将输出 0。有许多全局变量(包括FILE
句柄,它们被重置为零(导致讨厌的 ASSERT 故障),仅在 c.c 中,但在使用调试器检查时很好)。还有许多其他 x.c 模块包含没有问题。
好的,现在再次调用testfoo()
。在BC世界一切都很好。可怕的是,问题只发生在我的工作站上!关于如何调试它的任何线索?
这是我的记忆,我相信代码非常接近这个骨架:
B.Inc
int foo;
交流电
#include <b.inc>
void setfoo(){
foo = 4;
}
不列颠哥伦比亚省
#include <b.inc>
void testfoo(){
printf(foo); //works
}
C.C
#include <b.inc>
void testfoo2(){
printf(foo); //foo is now 0
}
补充:这是一个非常复杂的遗留代码(想想70年代),在一个非常大的公司中,没有太多可以改变的。如果我们开始添加 extern,有数千个变量会受到影响。另外,我尝试对一个变量进行 extern,但它仍然与该文件有问题。
我漏掉了一个花絮。'testfoo2()' 在托管线程中启动。同样,这似乎与我的机器有关,可能是Visual Studio中的一些设置,在这一点上,我们只想重新映像盒子。
你需要声明为 extern:
extern int foo;
然后在一个(并且只有一个)源文件中定义它:
int foo;
这样,一个源文件中只有一个符号,并且包括标头在内的所有其他源都知道链接到它。 如果省略extern
则每个源文件都认为它有自己的本地私有副本foo
。
那么哪个文件应该定义它呢? 最相关的一个。 我会说a.c
,因为这提供了在foo
上运行的功能。 我认为该文件是"所有者"。 但是,这有点令人困惑,因为您在b.inc
中声明它,暗示b.c
是所有者。
试试这个。
B.Inc
extern int foo;
交流电
#include <b.inc>
int foo;
void setfoo(){
foo = 4;
}
我在另一个线程中发布了这个,但我会在这里复制答案。这是一个非常微妙的错误,可能会为您节省几个小时:
好的,这是发生的事情:(以防您将来遇到这种情况)
1)在以前的构建中,有人将所有 C# Extern dll 调用拆分为两个文件。(我没有被告知)
2)他们创建了一个后期构建过程,将 dll 复制到另一个目录(请参阅其去向)。
3)涉及一个用户设置文件,该文件不在存储库中,但在代码中具有默认值。
4)其中一个 extern DLL 函数保留了我原来的默认值(在 filea.cs 中),但新的默认值 (fileb.cs) 指向构建后的位置。
据我所知,没有真正的方法可以在运行时反映 dll 的路径......
所以,当我使用 dll 副本输入函数时,当然没有全局变量,因为它是 dll 的新鲜副本。这是预期行为。
内存调试器是正确的,因为另一个 dll 在内存中的某个地方保存正确的变量。
将来如何防止这种情况发生?当VS加载同一dll的副本时,它似乎被欺骗了。