windows - 将字符串参数传递给 PROC

标签 windows winapi x86 masm

我想调用一个函数,该函数将对用户键入的字符串执行大写到小写的转换,同时保留特殊字符。这部分有效,但仅适用于前 4 个字符,之后的所有字符都会被截断。我相信这是因为我将参数定义为 DWORD:

我试过使用PAGEPARABYTE。前两个不起作用,字节表示类型不匹配。

upperToLower proc, source:dword, auxtarget:dword
  mov eax, source       ;Point to string
  mov ebx, auxtarget     ; point to destination
  L1:
  mov dl, [eax]            ; Get a character from buffer
    cmp byte ptr [eax], 0                  ; End of string? (not counters)
    je printString             ; if true, jump to printString
    cmp dl, 65                 ; 65 == 'A'
    jl notUpper                ; if less, it's not uppercase
    cmp dl, 90                 ; 90 == 'Z'
    jg notUpper                ; if greater, it's not uppercase
    xor dl, 100000b            ; XOR to change upper to lower
    mov [ebx], dl      ; add char to target
    inc eax                    ; Move counter up
    inc ebx                    ; move counter up
    jmp L1                     ; loop

    notUpper:                  ; not uppercase
    mov [ebx], dl      ; copy the letter
    inc eax            ;next letter
    inc ebx
    jmp L1

    printString:
    invoke WriteConsoleA,   consoleOutHandle, auxtarget, sizeof auxtarget, bytesWritten,    0
    ret
upperToLower endp

原型(prototype):

  upperToLower PROTO,
    source: dword,
    auxtarget: dword

调用:

    invoke upperToLower, offset buffer, offset target

缓冲区参数为:buffer db 128 DUP(?)

如何打印整个字符串,而不仅仅是前 4 个字符?

最佳答案

为什么只打印了 4 个字符?您将字符串写入控制台:

invoke WriteConsoleA,   consoleOutHandle, auxtarget, sizeof auxtarget, bytesWritten,    0

sizeof auxtarget 参数是 auxtarget 的大小,它是一个 DWORD(4 个字节),因此您只要求打印 4 个字节.您需要传递字符串的长度。您可以通过在 EAX 中获取结束地址并从中减去 source 指针来轻松地做到这一点。结果将是您遍历的字符串的长度。

修改代码为:

printString:
sub eax, source
invoke WriteConsoleA,   consoleOutHandle, auxtarget, eax, bytesWritten,    0

您的代码版本遵循 C 调用约定,同时使用源缓冲区和目标缓冲区,测试指针以确保它们不为 NULL,使用类似的 method described by Peter Cordes 进行转换如下:

upperToLower proc uses edi esi, source:dword, dest:dword 
    ; uses ESI EDI is used to tell assembler we are clobbering two of
    ; the cdecl calling convetions non-volatile registers. See:
    ; https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl
    mov esi, source            ; ESI = Pointer to string
    test esi, esi              ; Is source a NULL pointer?
    jz done                    ;     If it is then we are done

    mov edi, dest              ; EDI = Pointer to string
    test edi, edi              ; Is dest a NULL pointer?
    jz done                    ;     If it is then we are done

    xor edx, edx               ; EDX = 0 = current character index into the strings

    jmp getnextchar            ; Jump into loop at point of getting next character

  charloop:
    lea ecx, [eax - 'A']       ; cl = al-'A', and we do not care about the rest
                               ;     of the register

    cmp cl, 25                 ; if(c >= 'A' && c <= 'Z') c += 0x20;
    lea ecx, [eax + 20h]       ; without affecting flags
    cmovna eax, ecx            ; take the +0x20 version if it was in the 
                               ;     uppercase range to start with
    mov [edi + edx], al        ; Update character in destination string
    inc edx                    ; Go to next character

  getnextchar:
    movzx eax, byte ptr [esi + edx]
                               ; mov al, [esi + edx] leaving high garbage in EAX is ok
                               ;     too, but this avoids a partial-register stall
                               ;     when doing the mov+sub
                               ;     in one instruction with LEA
    test eax, eax              ; Is the character NUL(0) terminator?
    jnz charloop               ;     If not go back and process character

  printString:
    ; EDI = source, EDX = length of string

    invoke WriteConsoleA, consoleOutHandle, edi, edx, bytesWritten, 0
    mov edx, sizeof buffer
  done:
    ret
upperToLower endp

采用一个参数并将源字符串更改为大写的版本可以这样完成:

upperToLower proc, source:dword
    mov edx, source            ; EDX = Pointer to string
    test edx, edx              ; Is it a NULL pointer?
    jz done                    ;     If it is then we are done

    jmp getnextchar            ; Jump into loop at point of getting next character

  charloop:
    lea ecx, [eax - 'A']       ; cl = al-'A', and we do not care about the rest
                               ;     of the register

    cmp cl, 25                 ; if(c >= 'A' && c <= 'Z') c += 0x20;
    lea ecx, [eax + 20h]       ; without affecting flags
    cmovna eax, ecx            ; take the +0x20 version if it was in the
                               ;     uppercase range to start with
    mov [edx], al              ; Update character in string
    inc edx                    ; Go to next character

  getnextchar:
    movzx eax, byte ptr [edx]  ; mov al, [edx] leaving high garbage in EAX is ok, too,
                               ;     but this avoids a partial-register stall 
                               ;     when doing the mov+sub in one instruction with LEA
    test eax, eax              ; Is the character NUL(0) terminator?
    jnz charloop               ;     If not go back and process character

  printString:
    sub edx, source            ; EDX-source=length
    invoke WriteConsoleA, consoleOutHandle, source, edx, bytesWritten, 0
  done:
    ret
upperToLower endp

观察

  • 执行字符串转换的通用 upperToLower 函数通常不会自行打印。您通常会调用 upperToLower 仅进行转换,然后在单独的调用中将字符串输出到显示器。

关于windows - 将字符串参数传递给 PROC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57338869/

相关文章:

c++ - WM_NEXTDLGCTL 可以与非对话框窗口一起使用吗?

c++ - CreateProcess 这样子进程在父进程被杀死时被杀死?

winapi - Windows 中是否有屏幕调整大小事件

performance - 将位向量转换为一位

windows - 用于解码 RSA 的 Crypto++ 的小型 Windows 替代方案?

windows - 使用以太网电缆连接两台计算机

assembly - 如果函数相等则 NASM 程序集跳转

c - 在禁用优化的情况下,演示代码无法显示 4 倍快的 SIMD 速度

node.js - 即使用户注销后也保持 Node 处于运行状态

Windows程序文件路径名?