Winapi 如何在MASM中为一个项目编写和组合多个源文件?

Winapi 如何在MASM中为一个项目编写和组合多个源文件?,winapi,assembly,x86,linker,masm,Winapi,Assembly,X86,Linker,Masm,非常新的组装,有乐趣拨弄它。我想将我的程序的功能划分到多个文件中,特别是通过将类似的功能组合在一起以便于组织。这些其他文件将由主文件调用(希望甚至是其他非主文件)。我还没有做到这一点,希望得到帮助 我不使用IDE,而是自己使用notepad++、ml.exe和link.exe(来自MASM文件夹)来编写、汇编和链接程序。我看过的大多数在线资源都假设VisualStudio,并给出了不适合我的代码,或者可能是不完整的b/c IDE做了其他事情。我不打算开始使用IDE 我想学习“最佳”方式,也就是说

非常新的组装,有乐趣拨弄它。我想将我的程序的功能划分到多个文件中,特别是通过将类似的功能组合在一起以便于组织。这些其他文件将由主文件调用(希望甚至是其他非主文件)。我还没有做到这一点,希望得到帮助

我不使用IDE,而是自己使用notepad++、ml.exe和link.exe(来自MASM文件夹)来编写、汇编和链接程序。我看过的大多数在线资源都假设VisualStudio,并给出了不适合我的代码,或者可能是不完整的b/c IDE做了其他事情。我不打算开始使用IDE

我想学习“最佳”方式,也就是说,对未来项目最有用的方式。我是否可以将其设置为只需复制文件并编写几行代码,以便将来在不同的项目中使用它?或者这是一种不好的做法,我应该学习一种更标准的方法?我知道这个平台不是用来提出自以为是的问题,我希望这个问题更符合事实,而不是观点

我能想到的所有有用信息:
语言:Masm汇编x86
计算机:64位Windows

代码:

RUN.bat

@echo off

ml /c /coff /Zi /Fl Driver.asm
ml /c /coff /Zi /Fl Utils.asm

link /debug /subsystem:console /entry:start /out:Driver.exe Utils.obj Driver.obj \masm32\lib\kernel32.lib

Driver.exe

pause
Driver.asm

.386
.model flat
.stack 100h

ExitProcess PROTO Near32 STDCALL, dwExitCode:DWORD
ClearRegs PROTO

.DATA
.CODE

PUBLIC _start
_start:

    Main PROC
        MOV EAX, 0
        INVOKE ClearRegs
        INVOKE ExitProcess, 0
    Main ENDP

END
Utils.asm

.386
.model flat
.stack 100h

OPTION PROC:PRIVATE ; Set procedures to private by default

PUBLIC ClearRegs

.DATA
.CODE
    
    ClearRegs PROC C
        XOR EAX, EAX
        XOR EBX, EBX
        XOR ECX, ECX
        XOR EDX, EDX
        XOR ESI, ESI
        XOR EDI, EDI
        RET
    ClearRegs ENDP

END
终端输出

Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: Driver.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: Utils.asm
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

Driver.obj : error LNK2001: unresolved external symbol ClearRegs
Driver.exe : fatal error LNK1120: 1 unresolved externals
'Driver.exe' is not recognized as an internal or external command,
operable program or batch file.
Press any key to continue . . .

现在,您的问题已经更新为一个最小的、完整的、可验证的示例,可以确定一些特定的问题。使用
PROC
声明函数时,每个函数都应用了语言命名和调用约定。不指定一个关联没有特殊处理

可以使用model指令作为第二个参数指定默认语言。在您使用的两个文件中:

.model flat
所以您没有关联默认语言。您已将
ClearRegs
定义为:

ClearRegs PROC C
[snip]
ClearRegs ENDP
这里的问题是
PROC
指定了C语言的调用约定和命名约定。对于COFF格式(32位),C命名约定要求在函数名的开头加一个下划线(
)。如果要生成映射文件,您会发现从utils.asm导出的函数名实际上是
\u ClearRegs
,而不是
ClearRegs

有很多方法可以解决这个问题。您可以选择不向
.model
指令添加默认语言,并通过更改以下内容告知
Driver.asm
ClearRegs
定义为C原型:

ClearRegs PROTO
.model flat

因此,现在utils.asm正在导出
\u ClearRegs
,Driver.asm正在导入
\u ClearRegs
,因为双方都匹配,MASM将处理添加额外下划线的问题
INVOKE ClearRegs
将使用与
PROTO
语句相关联的命名约定,该语句表示语言为C,因此它将为您添加额外的

这带来了您可以进行的额外更改。可以使用
END
指令指定程序的入口点,而不是在链接器命令行上使用
/entry:
。入口点必须有一个以
开头的名称才能满足链接器的要求

您当前在Driver.asm中使用此选项:

链接时使用
/entry:start
。您可以将其更改为:

_Main PROC
    [snip]
_Main ENDP

END _Main   ; END with a function name tells linker to use _Main as program entry point
链接时,您现在可以完全删除
/entry
选项,并且不再需要
\u start
标签。不过我们可以做得更好。MS C运行时启动调用的入口点假定函数遵循C语言命名和调用约定。最好是这样做:

Main PROC C
    [snip]
Main ENDP

END Main   ; END with a function name tells linker to use _Main as program entry point
如果要使所有函数
PROC
,可以通过更改Utils.asm和Driver.asm中的默认语言来避免在大多数地方指定
C

ClearRegs PROTO
.model flat
致:

这将更改
PROTO
语句、
PUBLIC
语句的默认值,这些语句指定使用
PROC
PROC
语句本身定义的函数。Driver.asm中的代码可能如下所示:

.386
.model flat, C
.stack 100h

ExitProcess PROTO Near32 STDCALL, dwExitCode:DWORD
ClearRegs PROTO

.DATA
.CODE

Main PROC
    MOV EAX, 0
    INVOKE ClearRegs
    INVOKE ExitProcess, 0
Main ENDP

END Main
.386
.model flat, C
.stack 100h

OPTION PROC:PRIVATE ; Set procedures to private by default

PUBLIC ClearRegs

.DATA
.CODE
    
ClearRegs PROC
    XOR EAX, EAX
    XOR EBX, EBX
    XOR ECX, ECX
    XOR EDX, EDX
    XOR ESI, ESI
    XOR EDI, EDI
    RET
ClearRegs ENDP

END
Utils.asm可能看起来像:

.386
.model flat, C
.stack 100h

ExitProcess PROTO Near32 STDCALL, dwExitCode:DWORD
ClearRegs PROTO

.DATA
.CODE

Main PROC
    MOV EAX, 0
    INVOKE ClearRegs
    INVOKE ExitProcess, 0
Main ENDP

END Main
.386
.model flat, C
.stack 100h

OPTION PROC:PRIVATE ; Set procedures to private by default

PUBLIC ClearRegs

.DATA
.CODE
    
ClearRegs PROC
    XOR EAX, EAX
    XOR EBX, EBX
    XOR ECX, ECX
    XOR EDX, EDX
    XOR ESI, ESI
    XOR EDI, EDI
    RET
ClearRegs ENDP

END
你可以链接到:

link /debug /subsystem:console /out:Driver.exe Utils.obj Driver.obj \masm32\lib\kernel32.lib

实现代码重用的另一种方法是创建库(静态或动态)。这避免了在根本不需要更改函数实现的情况下重复源代码。但是在一个项目中,是的,你可以制作多个
.asm
文件,单独组装,然后将它们链接到一个
.exe
中。你是在问怎么做,还是在问这是个好主意?我假设您可以将多个
.obj
文件传递到
链接
。您似乎有一个
.bat
文件,其模式过于简单,假设为.obj,因此,如果您使用
*.asm
或其他什么,请使用更好的构建系统(如
make
)或改进您的.bat来工作。我正在询问如何以及是否这样做。根据您的建议,我分别组装了每一个,效果很好,然后尝试将这两个.obj都传递给link,我得到了未解析的外部符号(方法的名称)。Make似乎是GNU的一部分,我认为我不想要它(我应该得到它吗?)。我开始研究库,发现我在MASM文件夹中没有lib.exe,这导致我发现老师给了我一个更简单的版本。使用.lib文件是我应该做的吗?那些未定义的符号。。。您是否在声明它们的文件中标记它们
public
,以便在链接完成时其他模块(对象文件)可以看到它们?我相信是这样的。我已经为您和其他人在原始问题以及终端输出中添加了代码。非常感谢,这是一个极好的答案。你帮我省去了使用lib.exe的麻烦,还帮我去掉了_start标签,我真的很讨厌使用这个标签。一只猫