7

我有以下 MASM 代码:

.386
.model flat, stdcall
option casemap :none

include \masm32\include\masm32rt.inc

.data
    NewLine db  13, 10, 0
.code

LibMain proc instance:dword,reason:dword,unused:dword 
    mov     eax, 1
    ret
LibMain endp

PrintMess proc
    print "Printed from assembly"
    invoke StdOut, addr NewLine
    ret
PrintMess endp

TestReturn proc number:dword
    mov     eax, number
    ret
TestReturn endp

End LibMain

使用一个简单的 .def 文件:

LIBRARY MyLib
EXPORTS PrintMess
EXPORTS TestReturn

我正在从 C#调用PrintMess和调用:TestReturn

[DllImport("MyLib")]
static extern void PrintMess();

[DllImport("MyLib")]
static extern int TestReturn(int num);

static void Main(string[] args) {
    Console.WriteLine("Printed from C#");

    PrintMess();

    int value = TestReturn(30);
    Console.WriteLine("Returned: " + value);

    Console.ReadKey(true);
}

我第一次运行它时,它暂停了Console.ReadKey(true),我得到了预期的输出:

Printed from C#
Printed from assembly
Returned: 30

如果我然后在我的 C# 项目中进行更改,说TestReturn(30)更改为,TestReturn(50)那么它的行为会很奇怪。该程序终止没有错误并且不会暂停Console.ReadKey(true)(似乎它甚至没有到达该行),这是我的输出:

Printed from C#
Printed from assembly

我必须重建装配项目。具体来说,我必须重新构建,如果我进行另一个常规构建,程序会继续出现异常。当我重建时,输出和行为恢复正常并反映输出中的数字变化。我的猜测是 Build 和 Rebuild 之间的某些不同部分破坏了 DLL。

为什么我必须重建以及如何设置它以便我不必重建?

4

1 回答 1

2

@HansPassant 很可能是正确的,但没有解释为什么必须重建。@jacobaloysious 很接近。

VS 使用所谓的“托管进程”来简化和隔离调试。当您test.exe从 VS 调试时,实际上您实际上是在调试test.vshost.exe,它会加载您的 exe。停止调试时不会卸载主机可执行文件,因此是您真正的可执行文件。当您在不更改任何内容的情况下构建代码时,VS 实际上只检查文件是否是最新的。VS 认为,如果文件没有改变,那么就没有必要重新加载托管进程。如果您重建,即使您没有更改任何代码,您的真实可执行文件也会更新,这会强制主机进程与您的真实可执行文件一起终止并重新加载。

我认为,这个过程实际上是为了通过预加载汇编代码来加速调试的启动。您可以尝试在项目属性中关闭“启用 Visual Studio 托管进程”。之后,您应该会看到您的项目按预期工作。

在您进一步前进之前,请意识到 @HansPassant 是正确的,并且您的堆栈已损坏,并且随着时间的推移,您的项目将在意想不到的时间以最不愉快的方式以意想不到的方式失败。在大多数情况下,您的应用程序将抛出没有意义的异常,在某些情况下,您的应用程序将消失。

于 2013-06-12T07:47:26.497 回答