我们应该都知道UNIX环境下的文本文档中的回车加换行是直接由 "0ah" 实现的,而windows环境下的回车换行需要"0dh,0ah" 实现,所以如果把UNIX环境下的文本文档拿过来在WINDOWS环境下查看,那么文本的显示格式就乱了,结果是windows读不懂UNIX环境下的换行符,他以为是一行,事实也就是文本显示的就是一行,只能靠水平滚动条来回拖动查看,的确看着不爽,这次利用WIN32汇编语言 结合windows环境下内存管理的内容实现将UNIX文件格式转换为windows环境下的文件格式 的功能。下面看一下实现该功能的几个分步操作。
图标 | IDI_ICON1 |
对话框 | IDD_DIALOG1 |
静态文本框 | LTEXT类 在对话框中编辑文本框左边输入提示字符“文件名” |
编辑文本框 | 可以输入文本,显示要打开的文件名 |
按钮 | “开始” “浏览”两个按钮 |
处理缓冲区中(单行文本)的文本 | _FormatText() |
载入文件的处理 | _ProcFile() |
对话框窗口过程 | _ProcDlgMain() |
创建模态对话框 | DialogBoxParam() |
上面就是实现该功能的具体分支,下面首先来看一下资源文件中的问题:
资源文件中在使用ResEdit工具编写的时候,在对话框的风格中总会有一个风格DS-SHELLFONT,这个风格总是无效的,在用RadASM编译ResEdit工具编写的资源脚本的时候总会出现未找到定义的风格DS_SHELLFONT
也就是说在头文件的一些定义中并没有该风格的定义,总会出错,然后我果断的把这个风格从资源代码中移除。。。,结果呢,资源脚本可以编译运行了,那么这个风格到底是几个意思,如果要想用它的话怎么办呢?
我们还是来看一下MSDN博客是怎么说的,下面是连接:https://blogs.msdn.microsoft.com/oldnewthing/20050204-00/?p=36523/
具体可以参考一下,说的貌似很有道理,文件的版本需要更高才可以。
资源文件就这么多问题其他的就是以前常用的操作了,很熟练了。下面具体看一下资源脚本代码:
/
/ Generated by ResEdit 1.6.6
// Copyright (C) 2006-2015
// http://www.resedit.net
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "resource.h"
//
// Dialog resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG1 DIALOG 0, 0, 186, 95
STYLE DS_3DLOOK | DS_CENTER | DS_CONTEXTHELP | DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_GROUP | WS_TABSTOP | WS_POPUP | WS_SYSMENU
CAPTION "UNIX Text file -> PC Text file"
FONT 8, "Ms Shell Dlg"
{
PUSHBUTTON "开始", IDOK, 127, 70, 46, 18, 0, WS_EX_DLGMODALFRAME
PUSHBUTTON "浏览", IDC_BROWSE, 49, 70, 44, 18, NOT WS_TABSTOP | BS_VCENTER | BS_MULTILINE, WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE
LTEXT "文件名", 0, 17, 20, 43, 11, SS_LEFT, WS_EX_LEFT
EDITTEXT IDC_FILE, 50, 18, 135, 16, ES_AUTOHSCROLL, WS_EX_DLGMODALFRAME | WS_EX_ACCEPTFILES
}
//
// Icon resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDI_ICON1 ICON "icon2.ico"
本来想上传资源文件显示的截图的,但是上传文件时老出错误,就不传了。
下面看一下实现程序代码中遇到的问题:
这次程序中首先遇到的问题就是在处理缓冲区中的一单行的文本的时候,好多问题,下面来看一下子程序
_FormatText proc uses esi _lpData,_dwSize,_hFile ;首先该子程序需要三个参数,该子函数在_ProcFile()子程序中调用,_lpdata 就是将 一行文本读入到缓 冲区中,_lpData指向这个缓冲区,_dwSize 指向实际读入的 字节数,_hFile就是文件名只不过这个 文件名是存放在缓冲区中
LOCAL @szBuffer[128]:byte,@dwBytesWrite
mov esi,_lpData ;esi寄存器作为源操作内容存放的开始地址
mov ecx,_dwSize ;作为循环的计数
lea edi,@szBuffer ;另开一个缓冲区将其地址存放到目的寄存器edi中
xor edx,edx
cld ;将0标志位清零,表示传递数据的时候地址递增, std命令则相反。
_LoopBegin: ;一个标志 ,为了方便
or ecx,ecx ;或运算 其实就是ecx的值
jz _WriteLine
lodsb ;把esi寄存器中的内容从头开始依次放到al中,与stosb对应,stosb将al的 内容依次放到edi中
dec ecx
cmp al,0dh
jz _LoopBegin
cmp al,0ah
jz _LineEnd
stosb ;stosb将al的内容依次放到edi中,与lodsb对应
inc edx
cmp edx,sizeof @szBuffer-2 ;这个子程序处理的是一行文本而且这一行文本就放在缓冲区@szBuffer中,一 行文本最后就是0dh, 0ah,回车加换行,正好两个字节,如果处理的字节数edx大于等于缓冲区 总字节数减去2的值,那么 表示这一行的文本已经处理完了。
jae _WriteLine ;大于等于则跳转
jmp _LoopBegin
_LineEnd:
mov ax,0a0dh
stosw ;同stosb指令一个属性,只不过stosw是将ax中的内容放到edi中,与 lodsw对应
inc edx ;这里讲edx计数器连续自加两次,原因就是stosw将两个字节(0dh, 0ah)读入到了edi中,处理了 两个字节
inc edx
_WriteLine:
push ecx
.if edx
invoke WriteFile,_hFile,addr @szBuffer,edx,addr @dwBytesWrite,NULL
.endif
lea edi,@szBuffer ;上面代码中已经将@szBuffer的地址传递给edi在这里再传一次还有必要么?经过我的测试发现,确实需要再传一 次,不然的话程序会出错退出
xor edx,edx
pop ecx
or ecx,ecx
jnz _LoopBegin
ret
_FormatText endp
关于上面子程序的介绍应该已经很清楚了。
下面来看一下另一个问题:
以前也遇见过这个问题记不清楚了也没有解决,今天在这个程序中也用到了这个指令,如下一段代码所示:
.while TRUE
lea esi,@szReadBuffer
invoke ReadFile,@hFile,esi,sizeof @szReadBuffer,addr @dwBytesRead,0
.break .if ! @dwBytesRead ;这一行
invoke _FormatText,esi,@dwBytesRead,@hFileNew
.endw
如上面标注的一行 .break .if [条件] 这个语句
具体的意思就是:如果满足.if 中的条件则终止循环,跳出循环执行下面的语句。
与值对关的还有.continue .if [条件] 如果满足条件则结束当前语句,从新循环。
说完了这两个问题,下面来详细看一下程序实现代码:
.386
.model flat, stdcall
option casemap :none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
include comdlg32.inc
includelib comdlg32.lib
IDI_ICON1 equ 102
IDD_DIALOG1 equ 103
IDC_BROWSE equ 40000
IDC_FILE equ 40001
.data?
hInstance dd ?
hWinMain dd ?
szFileName db MAX_PATH dup (?)
.const
szFileExt db '文本文件',0,'*.txt',0,0
szNewFile db '.new.txt',0
szErrOpenFile db '无法打开源文件!',0
szErrCreateFile db '无法创建新的文本文件!',0
szSuccess db '文件转换成功,新的文本文件保存为',0dh,0ah,'%s',0
szSuccessCap db '提示'
.code
_FormatText proc uses esi _lpData,_dwSize,_hFile
LOCAL @szBuffer[128]:byte,@dwBytesWrite
mov esi,_lpData
mov ecx,_dwSize
lea edi,@szBuffer
xor edx,edx
cld
_LoopBegin:
or ecx,ecx
jz _WriteLine
lodsb
dec ecx
cmp al,0dh
jz _LoopBegin
cmp al,0ah
jz _LineEnd
stosb
inc edx
cmp edx,sizeof @szBuffer-2
jae _WriteLine
jmp _LoopBegin
_LineEnd:
mov ax,0a0dh
stosw
inc edx
inc edx
_WriteLine:
push ecx
.if edx
invoke WriteFile,_hFile,addr @szBuffer,edx,addr @dwBytesWrite,NULL
.endif
lea edi,@szBuffer ;上面代码中已经将@szBuffer的地址传递给edi在这里再传一次还有必要么?
xor edx,edx
pop ecx
or ecx,ecx
jnz _LoopBegin
ret
_FormatText endp
_ProcFile proc
LOCAL @hFile,@hFileNew,@dwBytesRead
LOCAL @szNewFile[MAX_PATH]:byte
LOCAL @szReadBuffer[512]:byte
invoke CreateFile,addr szFileName,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0 ;打开
.if eax == INVALID_HANDLE_VALUE
invoke MessageBox,hWinMain,addr szErrOpenFile,NULL,MB_OK or MB_ICONEXCLAMATION
ret
.endif
mov @hFile,eax
invoke lstrcpy,addr @szNewFile,addr szFileName
invoke lstrcat,addr @szNewFile,addr szNewFile
invoke CreateFile,addr @szNewFile,GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,0 ;创建
.if eax == INVALID_HANDLE_VALUE
invoke MessageBox,hWinMain,addr szErrCreateFile,NULL,MB_OK or MB_ICONEXCLAMATION
invoke CloseHandle,@hFile
ret
.endif
mov @hFileNew,eax
xor eax,eax
mov @dwBytesRead,eax
.while TRUE
lea esi,@szReadBuffer
invoke ReadFile,@hFile,esi,sizeof @szReadBuffer,addr @dwBytesRead,0
.break .if ! @dwBytesRead
invoke _FormatText,esi,@dwBytesRead,@hFileNew
.endw
invoke CloseHandle,@hFile
invoke CloseHandle,@hFileNew
invoke wsprintf,addr @szReadBuffer,addr szSuccess,addr @szNewFile
invoke MessageBox,hWinMain,addr @szReadBuffer,addr szSuccessCap,MB_OK
ret
_ProcFile endp
_ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam
LOCAL @stOpenFileName:OPENFILENAME
mov eax,wMsg
.if eax == WM_CLOSE
invoke EndDialog,hWnd,NULL
.elseif eax == WM_INITDIALOG
push hWnd
pop hWinMain
invoke LoadIcon,hInstance,IDI_ICON1
invoke SendMessage,hWnd,WM_SETICON,ICON_BIG,eax
invoke SendDlgItemMessage,hWnd,IDC_FILE,EM_SETLIMITTEXT,MAX_PATH,0
.elseif eax == WM_COMMAND
mov eax,wParam
.if ax == IDC_BROWSE
invoke RtlZeroMemory,addr @stOpenFileName,sizeof OPENFILENAME
mov @stOpenFileName.lStructSize,sizeof @stOpenFileName
mov @stOpenFileName.Flags,OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST
push hWinMain
pop @stOpenFileName.hwndOwner
mov @stOpenFileName.lpstrFilter,offset szFileExt
mov @stOpenFileName.lpstrFile,offset szFileName
mov @stOpenFileName.nMaxFile,MAX_PATH
invoke GetOpenFileName,addr @stOpenFileName
.if eax
invoke SetDlgItemText,hWnd,IDC_FILE,addr szFileName
.endif
.elseif ax == IDC_FILE
invoke GetDlgItemText,hWnd,IDC_FILE,addr szFileName,MAX_PATH
mov ebx,eax
invoke GetDlgItem,hWnd,IDOK
invoke EnableWindow,eax,ebx
.elseif ax == IDOK
call _ProcFile
.endif
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
_ProcDlgMain endp
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,offset _ProcDlgMain,NULL
invoke ExitProcess,NULL
end start
这次程序中使用的API函数和一些结构在以前的文章中都做过总结,没有出现新的内容,不在赘述,有某些不熟悉的,请查看以前的文章中的API函数和结构的信息的 详细描述。
本次程序分析到此结束。