阅读:3371回复:2
[转帖]Win32汇编语言教程
Win32汇编语言教程
作者:TBsoft -------------------------------------------------------------------------------- 一、引言 Win32应用程序一般使用C语言编程,但是在某些需要进行深层编程的情况下,例如Win32应用程序执行机制分析、病毒清除、加密解密等深层编程,或者对于某些速度要求较高的程序,需要使用汇编语言(甚至机器语言)直接编写Win32应用程序。Win32应用程序虽然和其他32位应用程序(例如32位保护模式DOS程序)一样可以使用386汇编语言和保护模式编程,但是Win32应用程序的执行机制与其他32位应用程序有一定的差别,例如消息循环、动态链接等,Win32汇编语言也有其特殊的编程方式。目前国内极少看到有关Win32汇编语言的资料,市面上的汇编语言书籍一般只介绍DOS实模式汇编语言和386保护模式汇编语言,金山公司的《深入Windows编程》一书虽然介绍了使用汇编语言写Windows应用程序的方法,可惜该书只介绍了Win16汇编语言。为了使大家能对Win32汇编语言的基本编程方法有一定的了解,近日得闲,笔者编写了本教程,旨在抛砖引玉,如果本教程能够带领你走进神秘的Win32汇编语言世界,笔者心愿足矣。使用本教程,要求读者具有C语言编写Win32应用程序(Win32SDK编程)的基础。 二、进行Win32汇编语言编程的基本软件 进行Win32汇编语言编程,应该准备下列基本软件: 1、MASM 6.11以上版本的汇编器 MASM是Microsoft公司的汇编器,这是最基本的软件,必需MASM 6.11以上版本才能够汇编Win32汇编语言源程序。不过进行Win32汇编语言编程不必要全套的MASM 6.11,只要一个ML.EXE文件就可以了,Windows 95 DDK中带有MASM 6.11c的ML.EXE文 件,Windows 98 DDK中带有MASM 6.11d的ML.EXE文件,都可以使用。Turbo MASM 5.0(TASM)是Borland公司的汇编器,也可以用来汇编Win32汇编语言源程序,但是TASM的部分语法与MASM不同,用于MASM的Win32汇编语言源程序可能需要修改后才能用TASM汇编。本教程中的所有Win32汇编语言源程序都基于MASM。 2、Win32SDK 进行Win32汇编语言编程需要用到Win32SDK中的资源编译器(RC.EXE)和连接器(LINK.EXE),还需要用到Win32SDK中的引入库文件(KERNEL32.LIB、USER32.LIB、GDI32.LIB等)。如果没有Win32SDK,Platform SDK也可以,还可以安装Visual C++ 2.0以上版本的Visual C++,笔者使用的是Visual C++ 6.0。Borland C++ 4.0以上版本的Borland C++也可以使用,只是资源编译器和连接器的 文件名不同,分别是BRC.EXE(BRC32.EXE)和TLINK.EXE(TLINK32.EXE),选项也不尽相同,另外Borland C++不支持COFF格式的OBJ文件,汇编时不能使用/coff选项。 3、汇编语言编辑器 一个普通的文本编辑器,用于编辑Win32汇编语言源程序。EDIT、PWB等都可以,Visual C++等编程语言中的编辑器也可以,甚至WORD、WPS 97等可以编辑文本文件的字处理软件都可以,不过笔者推荐使用ASMEDIT,这是一个专用的汇编语言编辑 器,效果非常好。Win32汇编语言一般使用命令行方式汇编连接,经过一定的设置也可以在某些集成 环境(PWB、Visual C++、ASMEDIT等)下汇编连接,还可以使用NMAKE工具,不过本教程中只使用命令行方式汇编连接,也不使用NMAKE工具。 三、ANSI字符集API与UNICODE字符集API Win32 API中凡是与字符有关的API都有两种不同的类型:ANSI字符集API和UNICODE字符集API,分别对应ANSI字符和UNICODE字符,Windows NT支持两种类型的API,Windows 95/98只支持ANSI字符集API。在WINDOWS.H头文件和其他Win32 API定义 头文件中,凡是与字符有关的API都有两种不同的定义,ANSI字符集API以API名称加字符“A”表示,UNICODE字符集API以API名称加字符“W”表示,并使用条件编译和宏定义实现自动根据当前字符集使用对应的API定义,例如GetModuleHandle函数的定义(包括在WINBASE.H头文件中): WINBASEAPI HMODULE WINAPI GetModuleHandleA( LPCSTR lpModuleName ); WINBASEAPI HMODULE WINAPI GetModuleHandleW( LPCWSTR lpModuleName ); #ifdef UNICODE #define GetModuleHandle GetModuleHandleW #else #define GetModuleHandle GetModuleHandleA #endif // !UNICODE 与字符有关的数据结构也有类似的定义。本教程考虑到汇编语言使用条件汇编会导致不太直观,全部使用ANSI字符集API,这样也可以保证在Windows 95/98和Windows NT环境下的兼容性,所以本教程中许多API名称和数据结构的名称都加有“A”字符,读者可以方便地改用UNICODE字符集API。 四、一个简单的Win32汇编语言程序 读者可能一听到“汇编语言”四个字就觉得十分头疼!汇编语言给人的第一印象就是一大堆难以看懂又不直观的指令,而且不结构化,大量的标号、无条件跳转指令(JMP)和条件跳转指令让你难以看懂程序;过程(或者函数)的调用参数传递又不直观,要么直接使用寄存器传递参数,不符合结构化程序设计原则;要么使用堆栈传递参数,又不能有效地检验参数类型……想必Win32汇编语言更麻烦吧!还好,MASM 6.0以上版本的汇编器提供了很多结构化汇编语言伪指令,可以方便地实现汇编语言结构化程序设计,当你看完本教程以后,你可能会感觉到:Win32汇编语言并不比C语言麻烦多少。(如果读者看不懂本教程中的汇编语言源程序也不要紧,可以对照MASM 6.11的帮助看)和C语言Win32编程需要WINDOWS.H头文件和其他Win32 API定义头文件定义常量、数据结构和API一样,Win32汇编语言也需要包含文件(INC文件)定义常量、数据结构和API。不过笔者找了很长时间也没有找到一个完整的可用于Win32汇编语言的WINDOWS.INC文件或者WIN32.INC文件(倒是找到了用于Win16汇编语言的WINDOWS.INC文件),Turbo MASM 5.0中提供的WIN32.INC文件也不完整,只能用于自带的WAP32例子程序,而且与MASM 6.11不太兼容(听说NASM中有完整的WIN32.INC文件,可惜没有找到,也不知道与MASM 6.11是否兼容)。笔者只好自己定义常量、数据结构和API(根据WINDOWS.H头文件和其他Win32 API定义头文件定义),不过这倒 带来了不少好处――可以更好地了解Win32汇编语言的编程方法和原理。笔者编写了一个简单的Win32汇编语言程序,该程序的功能很简单:在屏幕上显示一个消息框。本程序只调用了两个API函数:MessageBox函数和ExitProcess函数,程序如下: 包含文件(MSGBOX.INC): UINT TYPEDEF DWORD LPSTR TYPEDEF PTR BYTE LPCSTR TYPEDEF LPSTR PVOID TYPEDEF PTR HANDLE TYPEDEF PVOID HWND TYPEDEF HANDLE MB_ICONINFORMATION = 00000040h MB_OK = 00000000h MessageBoxA PROTO stdcall, :HWND,:LPCSTR,:LPCSTR,:UINT ExitProcess PROTO stdcall, :UINT 源程序(MSGBOX.ASM): .386p .MODEL flat,stdcall INCLUDE MSGBOX.INC .STACK 4096 .DATA WindowTitle BYTE \'MsgBox\',0 Message1 BYTE \'This is a simple MessageBox Win32 application.\',0 .CODE _start: INVOKE MessageBoxA,0,ADDR Message1,ADDR WindowTitle, MB_ICONINFORMATION or MB_OK INVOKE ExitProcess,0 PUBLIC _start END 汇编连接本程序的命令如下: ml /c /coff /Cp msgbox.asm link /subsystem:windows /entry:_start msgbox.obj kernel32.lib user32.lib 汇编命令中的/c选项表示只汇编,不自动连接;/coff选项表示生成COFF格式的OBJ文件(如果使用Borland的连接器不能使用/coff参数);/Cp选项表示标识符区分大小写。连接命令中/subsystem:windows选项表示连接器生成普通Windows可执行文件;/entry:_start选项表示程序入口点是_start标识符。连接时连接KERNEL32.LIB和USER32.LIB引入库。运行汇编连接后生成的MSGBOX.EXE文件,屏幕上将显示出一个消息框,消息框的标题是“MsgBox”,消息框中的字符串是“This is a simple MessageBox Win32 application.”。Win32汇编语言源程序应该由.386p伪指令和.MODEL flat,stdcall伪指令开始,指示汇编器汇编386保护模式指令,并使用平坦内存模式(Win32内存模式)和stdcall函数调用方式(Win32标准函数调用方式)。PROTO伪指令定义函数原型(与C语言中函数原型的定义相似),可以定义函数名、调用方式和参数,INVOKE伪指令调用由PROTO伪指令定义的函数,可以方便地传递参数和检查参数类型。MSGBOX.INC文件中使用PROTO伪指令定义API函数,MSGBOX.ASM文件中使用INVOKE伪指令调用API函数,可见MASM 6.0以上版本的汇编器提供的结构化汇编语言伪指令大大简化了Win32汇编语言编程(本程序一条汇编语言指令也没有用到)。本程序调用了MessageBox函数显示消息框以后,调用了ExitProcess函数终止程序的执行,ExitProcess函数的作用是终止当前进程。 五、显示一个窗口的Win32汇编语言程序 学习过Win32SDK编程的读者编写的第一个应用程序可能就是显示一个窗口的C语言程序,笔者也编写了这样一个C语言程序,该程序文件名为SIMPLE.C,程序如下: #include static char szWindowClass[]=\"SIMPLE\"; LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { WNDCLASSEXA wcex; HWND hWnd; MSG msg; if(!hPrevInstance) { wcex.cbSize=sizeof(WNDCLASSEXA); wcex.style=CS_HREDRAW|CS_VREDRAW; wcex.cbClsExtra=0; wcex.cbWndExtra=0; wcex.lpfnWndProc=WndProc; wcex.hInstance=hInstance; wcex.hIcon=LoadIconA(hInstance,IDI_APPLICATION); wcex.hCursor=LoadCursorA(0,IDC_ARROW); wcex.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName=NULL; wcex.lpszClassName=szWindowClass; wcex.hIconSm=LoadIconA(hInstance,IDI_APPLICATION); if(!RegisterClassExA(&wcex)) return FALSE; } hWnd=CreateWindowExA(0,szWindowClass,\"SIMPLE\", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, 0,0,hInstance,NULL); if(!hWnd) return FALSE; ShowWindow(hWnd,nShowCmd); UpdateWindow(hWnd); while(GetMessageA(&msg,0,0,0)) { TranslateMessage(&msg); DispatchMessageA(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { HDC hDC; PAINTSTRUCT ps; switch(message) { case WM_PAINT: hDC=BeginPaint(hWnd,&ps); EndPaint(hWnd,&ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProcA(hWnd,message,wParam,lParam); } return -1; } 本程序与一般的显示一个窗口的C语言程序基本相同,只是只使用ANSI字符集。现在笔者用Win32汇编语言程序实现本程序的功能,程序如下: 包含文件(SIMPLE.INC): UINT TYPEDEF DWORD LONG TYPEDEF DWORD LPSTR TYPEDEF PTR BYTE LPCSTR TYPEDEF LPSTR PVOID TYPEDEF PTR LPVOID TYPEDEF PVOID HANDLE TYPEDEF PVOID HINSTANCE TYPEDEF HANDLE HWND TYPEDEF HANDLE HMENU TYPEDEF HANDLE HDC TYPEDEF HANDLE HGDIOBJ TYPEDEF HANDLE HICON TYPEDEF HANDLE HCURSOR TYPEDEF HANDLE HBRUSH TYPEDEF HANDLE tagWNDCLASSEXA STRUCT cbSize UINT ? style UINT ? lpfnWndProc DWORD ? cbClsExtra DWORD ? cbWndExtra DWORD ? hInstance DWORD ? hIcon DWORD ? hCursor DWORD ? hbrBackground DWORD ? lpszMenuName DWORD ? lpszClassName DWORD ? hIconSm DWORD ? tagWNDCLASSEXA ENDS WNDCLASSEXA TYPEDEF tagWNDCLASSEXA tagPOINT STRUCT x LONG ? y LONG ? tagPOINT ENDS POINT TYPEDEF tagPOINT tagMSG STRUCT message UINT ? wParam DWORD ? lParam DWORD ? time DWORD ? pt POINT <> tagMSG ENDS MSG TYPEDEF tagMSG LPMSG TYPEDEF PTR MSG tagRECT STRUCT left LONG ? top LONG ? right LONG ? bottom LONG ? tagRECT ENDS RECT TYPEDEF tagRECT tagPAINTSTRUCT STRUCT hdc DWORD ? fErase DWORD ? rcPaint RECT <> fRestore DWORD ? fIncUpdate DWORD ? rgbReserved BYTE 32 DUP(?) tagPAINTSTRUCT ENDS PAINTSTRUCT TYPEDEF tagPAINTSTRUCT LPPAINTSTRUCT TYPEDEF PTR PAINTSTRUCT NULL = 0 TRUE = 0ffffffffh FALSE = 0 SW_SHOWDEFAULT = 10 CS_HREDRAW = 0002h CS_VREDRAW = 0001h IDI_APPLICATION = 32512 IDC_ARROW = 32512 COLOR_WINDOW = 5 WS_OVERLAPPEDWINDOW = 00cf0000h CW_USEDEFAULT = 80000000h WM_PAINT = 000fh WM_DESTROY = 0002h GetModuleHandleA PROTO stdcall, :LPCSTR GetCommandLineA PROTO stdcall ExitProcess PROTO stdcall, :UINT LoadIconA PROTO stdcall, :HINSTANCE,:LPCSTR LoadCursorA PROTO stdcall, :HINSTANCE,:LPCSTR RegisterClassExA PROTO stdcall, :PTR WNDCLASSEXA CreateWindowExA PROTO stdcall, :DWORD,:LPCSTR,:LPCSTR,:DWORD,: DWORD,:DWORD,:DWORD,:DWORD,:HWND,:HMENU,:HINSTANCE,:LPVOID ShowWindow PROTO stdcall, :HWND,:DWORD UpdateWindow PROTO stdcall, :HWND GetMessageA PROTO stdcall, :LPMSG,:HWND,:UINT,:UINT TranslateMessage PROTO stdcall, :PTR MSG DispatchMessageA PROTO stdcall, :PTR MSG BeginPaint PROTO stdcall, :HWND,:LPPAINTSTRUCT EndPaint PROTO stdcall, :HWND,:PTR PAINTSTRUCT PostQuitMessage PROTO stdcall, :DWORD DefWindowProcA PROTO stdcall, :HWND,:UINT,:DWORD,:DWORD 源程序(SIMPLE.ASM): .386p .MODEL flat,stdcall INCLUDE SIMPLE.INC WinMain PROTO stdcall, :HINSTANCE,:HINSTANCE,:LPSTR,: DWORD .STACK 4096 .DATA WindowClass BYTE \'SIMPLE\',0 WindowTitle BYTE \'SIMPLE\',0 hInst1 HINSTANCE 0 lpCmdLine1 LPSTR 0 .CODE _start: INVOKE GetModuleHandleA,NULL mov hInst1,eax INVOKE GetCommandLineA mov lpCmdLine1,eax INVOKE WinMain,hInst1,NULL,lpCmdLine1,SW_SHOWDEFAULT INVOKE ExitProcess,eax WinMain PROC hInst:HINSTANCE,hPrevInst:HINSTANCE,lpCmdLine:LPSTR, nShowCmd:DWORD LOCAL wcex:WNDCLASSEXA LOCAL hWnd:HWND LOCAL msg:MSG .IF !hPrevInst mov wcex.cbSize,SIZEOF WNDCLASSEXA mov wcex.style,CS_HREDRAW or CS_VREDRAW mov wcex.cbClsExtra,0 mov wcex.cbWndExtra,0 mov wcex.lpfnWndProc,OFFSET WndProc mov eax,hInst mov wcex.hInstance,eax INVOKE LoadIconA,hInst,IDI_APPLICATION mov wcex.hIcon,eax INVOKE LoadCursorA,0,IDC_ARROW mov wcex.hCursor,eax mov wcex.hbrBackground,COLOR_WINDOW+1 mov wcex.lpszMenuName,NULL mov wcex.lpszClassName,OFFSET WindowClass INVOKE LoadIconA,hInst,IDI_APPLICATION mov wcex.hIconSm,eax INVOKE RegisterClassExA,ADDR wcex .IF !eax mov eax,FALSE ret .ENDIF .ENDIF INVOKE CreateWindowExA,0,ADDR WindowClass,ADDR WindowTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,0,0,hInst,NULL mov hWnd,eax .IF !eax mov eax,FALSE ret .ENDIF INVOKE ShowWindow,hWnd,nShowCmd INVOKE UpdateWindow,hWnd .WHILE TRUE INVOKE GetMessageA,ADDR msg,0,0,0 .BREAK .IF !eax INVOKE TranslateMessage,ADDR msg INVOKE DispatchMessageA,ADDR msg .ENDW mov eax,msg.wParam ret WinMain ENDP WndProc PROC hWnd:HWND,message:UINT,wParam:DWORD,lParam:DWORD LOCAL hDC:HDC LOCAL ps:PAINTSTRUCT .IF message==WM_PAINT INVOKE BeginPaint,hWnd,ADDR ps mov hDC,eax INVOKE EndPaint,hWnd,ADDR ps mov eax,0 ret .ELSEIF message==WM_DESTROY INVOKE PostQuitMessage,0 mov eax,0 ret .ELSE INVOKE DefWindowProcA,hWnd,message,wParam,lParam ret .ENDIF mov eax,0ffffffffh ret WndProc ENDP PUBLIC _start END 汇编连接本程序的命令如下: ml /c /coff /Cp simple.asm link /subsystem:windows /entry:_start simple.obj kernel32.lib user32.lib gdi32.lib 运行汇编连接后生成的SIMPLE.EXE文件,屏幕上将显示出一个标准的窗口,窗口的标题是“SIMPLE”。学习过Win32SDK编程的读者都知道,Win32应用程序的入口点是WinMain函数,实际上WinMain函数是被C语言的初始化和结束代码调用的,Win32应用程序的真正入口点和DOS应用程序没有什么区别,都是在文件头中指定的应用程序起始点。Win32汇编语言没有C语言的初始化和结束代码,必须自己编写初始化和结束代码调用 WinMain函数(过程),WinMain函数的原型是: int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd); WinMain函数有4个参数,分别是: hInstance――应用程序当前实例的句柄,可以通过调用GetModuleHandle函数获取. hPrevInstance――应用程序前一个实例的句柄,Win32中当前地址空间中不会有应用程序的其他实例在运行,该参数通常设置为NULL(提供该参数只是便于移植Win16应用程序源程序)。 lpCmdLine――命令行参数,可以通过调用GetCommandLine函数获取。nShowCmd――主窗口的显示状态,可以设置成SW_SHOWDEFAULT(缺省状态)。本程序的初始化和结束代码如下: INVOKE GetModuleHandleA,NULL mov hInst1,eax INVOKE GetCommandLineA mov lpCmdLine1,eax INVOKE WinMain,hInst1,NULL,lpCmdLine1,SW_SHOWDEFAULT INVOKE ExitProcess,eax Win32标准函数调用方式,函数返回值通过EAX寄存器返回。可见本程序的初始化和结束代码很简单,只是调用GetModuleHandle函数获取应用程序当前实例的句柄,调用GetCommandLine函数获取命令行参数,然后调用WinMain函数(过程),WinMain函数返回后调用ExitProcess函数终止程序的执行。将本程序与SIMPLE.C程序比较,可以看出程序结构十分相似,本程序大量使用了MASM 6.0以上版本的汇编器提供的结构化汇编语言伪指令,并不比C语言麻烦多少。 六、资源在Win32汇编语言程序中的使用 资源在Win32应用程序中是很重要的,笔者编写了一个使用资源的C语言程序,该程序实现了菜单和一个“About”对话框,程序如下: 源程序(GENERIC.C): #include #include \"resource.h\" static HINSTANCE hInst; static char szWindowClass[]=\"GENERIC\"; LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam); BOOL CALLBACK AboutDlgProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { WNDCLASSEXA wcex; HWND hWnd; MSG msg; hInst=hInstance; if(!hPrevInstance) { wcex.cbSize=sizeof(WNDCLASSEXA); wcex.style=CS_HREDRAW|CS_VREDRAW; wcex.cbClsExtra=0; wcex.cbWndExtra=0; wcex.lpfnWndProc=WndProc; wcex.hInstance=hInstance; wcex.hIcon=LoadIconA(hInstance,IDI_APPLICATION); wcex.hCursor=LoadCursorA(0,IDC_ARROW); wcex.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName=MAKEINTRESOURCE(IDR_MAINMENU); wcex.lpszClassName=szWindowClass; wcex.hIconSm=LoadIconA(hInstance,IDI_APPLICATION); if(!RegisterClassExA(&wcex)) return FALSE; } hWnd=CreateWindowExA(0,szWindowClass,\"Generic\", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, 0,0,hInstance,NULL); if(!hWnd) return FALSE; ShowWindow(hWnd,nShowCmd); UpdateWindow(hWnd); while(GetMessageA(&msg,0,0,0)) { TranslateMessage(&msg); DispatchMessageA(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { HDC hDC; PAINTSTRUCT ps; switch(message) { case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_EXIT: SendMessageA(hWnd,WM_CLOSE,0,0); return 0; case IDM_ABOUT: DialogBoxParamA(hInst,MAKEINTRESOURCE(IDD_ABOUT), hWnd,(DLGPROC)AboutDlgProc,0); return 0; default: return DefWindowProcA(hWnd,message,wParam,lParam) ; } case WM_PAINT: hDC=BeginPaint(hWnd,&ps); EndPaint(hWnd,&ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProcA(hWnd,message,wParam,lParam); } return -1; } BOOL CALLBACK AboutDlgProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam) { switch(message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: if((LOWORD(wParam)==IDOK)||(LOWORD(wParam)==IDCANCEL)) { EndDialog(hDlg,LOWORD(wParam)); return TRUE; } return FALSE; default: return FALSE; } } 资源源文件(GENERIC.RC,使用Visual C++ 6.0建立): //Microsoft Developer Studio generated resource script. // #include \"resource.h\" #define APSTUDIO_READONLY_SYMBOLS //////////////////////////////////////////////////////////////////////// ///// // // Generated from the TEXTINCLUDE 2 resource. // #include \"afxres.h\" //////////////////////////////////////////////////////////////////////// ///// #undef APSTUDIO_READONLY_SYMBOLS //////////////////////////////////////////////////////////////////////// ///// // Chinese (P.R.C.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) #ifdef _WIN32 LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #pragma code_page(936) #endif //_WIN32 //////////////////////////////////////////////////////////////////////// ///// // // Menu // IDR_MAINMENU MENU DISCARDABLE BEGIN POPUP \"&File\" BEGIN MENUITEM \"&Exit\", IDM_EXIT END POPUP \"&Help\" BEGIN MENUITEM \"&About...\", IDM_ABOUT END END #ifdef APSTUDIO_INVOKED //////////////////////////////////////////////////////////////////////// ///// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN \"resource.h\\0\" END 2 TEXTINCLUDE DISCARDABLE BEGIN \"#include \"\"afxres.h\"\"\\r\\n\" \"\\0\" END 3 TEXTINCLUDE DISCARDABLE BEGIN \"\\r\\n\" \"\\0\" END #endif // APSTUDIO_INVOKED //////////////////////////////////////////////////////////////////////// ///// // // Dialog // IDD_ABOUT DIALOG DISCARDABLE 0, 0, 92, 65 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION \"About\" FONT 9, \"宋体\" BEGIN LTEXT \"Generic V1.0\",IDC_STATIC1,20,10,50,10 DEFPUSHBUTTON \"OK\",IDOK,20,40,50,15 END //////////////////////////////////////////////////////////////////////// ///// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_ABOUT, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 85 TOPMARGIN, 7 BOTTOMMARGIN, 58 END END #endif // APSTUDIO_INVOKED #endif // Chinese (P.R.C.) resources //////////////////////////////////////////////////////////////////////// ///// #ifndef APSTUDIO_INVOKED //////////////////////////////////////////////////////////////////////// ///// // // Generated from the TEXTINCLUDE 3 resource. // //////////////////////////////////////////////////////////////////////// ///// #endif // not APSTUDIO_INVOKED 资源头文件(RESOURCE.H,Visual C++ 6.0自动建立): //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by GENERIC.rc // #define IDR_MAINMENU 101 #define IDD_ABOUT 102 #define IDC_STATIC1 1000 #define IDM_EXIT 40001 #define IDM_ABOUT 40002 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40003 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif 本程序与一般Win32编程资料中的GENERIC程序基本相同,只是只使用ANSI字符集。 现在笔者用Win32汇编语言程序实现本程序的功能,程序如下: 包含文件(GENERIC.INC): UINT TYPEDEF DWORD LONG TYPEDEF DWORD LPSTR TYPEDEF PTR BYTE LPCSTR TYPEDEF LPSTR PVOID TYPEDEF PTR LPVOID TYPEDEF PVOID HANDLE TYPEDEF PVOID HINSTANCE TYPEDEF HANDLE HWND TYPEDEF HANDLE HMENU TYPEDEF HANDLE HDC TYPEDEF HANDLE HGDIOBJ TYPEDEF HANDLE HICON TYPEDEF HANDLE HCURSOR TYPEDEF HANDLE HBRUSH TYPEDEF HANDLE tagWNDCLASSEXA STRUCT cbSize UINT ? style UINT ? lpfnWndProc DWORD ? cbClsExtra DWORD ? cbWndExtra DWORD ? hInstance DWORD ? hIcon DWORD ? hCursor DWORD ? hbrBackground DWORD ? lpszMenuName DWORD ? lpszClassName DWORD ? hIconSm DWORD ? tagWNDCLASSEXA ENDS WNDCLASSEXA TYPEDEF tagWNDCLASSEXA tagPOINT STRUCT x LONG ? y LONG ? tagPOINT ENDS POINT TYPEDEF tagPOINT tagMSG STRUCT message UINT ? wParam DWORD ? lParam DWORD ? time DWORD ? pt POINT <> tagMSG ENDS MSG TYPEDEF tagMSG LPMSG TYPEDEF PTR MSG tagRECT STRUCT left LONG ? top LONG ? right LONG ? bottom LONG ? tagRECT ENDS RECT TYPEDEF tagRECT tagPAINTSTRUCT STRUCT hdc DWORD ? fErase DWORD ? rcPaint RECT <> fRestore DWORD ? fIncUpdate DWORD ? rgbReserved BYTE 32 DUP(?) tagPAINTSTRUCT ENDS PAINTSTRUCT TYPEDEF tagPAINTSTRUCT LPPAINTSTRUCT TYPEDEF PTR PAINTSTRUCT NULL = 0 TRUE = 0ffffffffh FALSE = 0 SW_SHOWDEFAULT = 10 CS_HREDRAW = 0002h CS_VREDRAW = 0001h IDI_APPLICATION = 32512 IDC_ARROW = 32512 COLOR_WINDOW = 5 WS_OVERLAPPEDWINDOW = 00cf0000h CW_USEDEFAULT = 80000000h WM_COMMAND = 0111h WM_CLOSE = 0010h WM_PAINT = 000fh WM_DESTROY = 0002h WM_INITDIALOG = 0110h IDOK = 1 IDCANCEL = 2 GetModuleHandleA PROTO stdcall, :LPCSTR GetCommandLineA PROTO stdcall ExitProcess PROTO stdcall, :UINT LoadIconA PROTO stdcall, :HINSTANCE,:LPCSTR LoadCursorA PROTO stdcall, :HINSTANCE,:LPCSTR RegisterClassExA PROTO stdcall, :PTR WNDCLASSEXA CreateWindowExA PROTO stdcall, :DWORD,:LPCSTR,:LPCSTR,:DWORD,: DWORD,:DWORD,:DWORD,:DWORD,:HWND,:HMENU,:HINSTANCE,:LPVOID ShowWindow PROTO stdcall, :HWND,:DWORD UpdateWindow PROTO stdcall, :HWND GetMessageA PROTO stdcall, :LPMSG,:HWND,:UINT,:UINT TranslateMessage PROTO stdcall, :PTR MSG DispatchMessageA PROTO stdcall, :PTR MSG SendMessageA PROTO stdcall, :HWND,:UINT,:DWORD,:DWORD DialogBoxParamA PROTO stdcall, :HINSTANCE,:LPCSTR,:HWND,:DWORD,: DWORD BeginPaint PROTO stdcall, :HWND,:LPPAINTSTRUCT EndPaint PROTO stdcall, :HWND,:PTR PAINTSTRUCT PostQuitMessage PROTO stdcall, :DWORD DefWindowProcA PROTO stdcall, :HWND,:UINT,:DWORD,:DWORD EndDialog PROTO stdcall, :HWND,:DWORD 资源包含文件(RESOURCE.INC): IDR_MAINMENU = 101 IDD_ABOUT = 102 IDC_STATIC1 = 1000 IDM_EXIT = 40001 IDM_ABOUT = 40002 源程序(GENERIC.ASM): .386p .MODEL flat,stdcall INCLUDE GENERIC.INC INCLUDE RESOURCE.INC WinMain PROTO stdcall, :HINSTANCE,:HINSTANCE,:LPSTR,: DWORD .STACK 4096 .DATA WindowClass BYTE \'GENERIC\',0 WindowTitle BYTE \'Generic\',0 hInst1 HINSTANCE 0 lpCmdLine1 LPSTR 0 .CODE _start: INVOKE GetModuleHandleA,NULL mov hInst1,eax INVOKE GetCommandLineA mov lpCmdLine1,eax INVOKE WinMain,hInst1,NULL,lpCmdLine1,SW_SHOWDEFAULT INVOKE ExitProcess,eax WinMain PROC hInst:HINSTANCE,hPrevInst:HINSTANCE,lpCmdLine:LPSTR, nShowCmd:DWORD LOCAL wcex:WNDCLASSEXA LOCAL hWnd:HWND LOCAL msg:MSG .IF !hPrevInst mov wcex.cbSize,SIZEOF WNDCLASSEXA mov wcex.style,CS_HREDRAW or CS_VREDRAW mov wcex.cbClsExtra,0 mov wcex.cbWndExtra,0 mov wcex.lpfnWndProc,OFFSET WndProc mov eax,hInst mov wcex.hInstance,eax INVOKE LoadIconA,hInst,IDI_APPLICATION mov wcex.hIcon,eax INVOKE LoadCursorA,0,IDC_ARROW mov wcex.hCursor,eax mov wcex.hbrBackground,COLOR_WINDOW+1 mov wcex.lpszMenuName,IDR_MAINMENU and 0000ffffh mov wcex.lpszClassName,OFFSET WindowClass INVOKE LoadIconA,hInst,IDI_APPLICATION mov wcex.hIconSm,eax INVOKE RegisterClassExA,ADDR wcex .IF !eax mov eax,FALSE ret .ENDIF .ENDIF INVOKE CreateWindowExA,0,ADDR WindowClass,ADDR WindowTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,0,0,hInst,NULL mov hWnd,eax .IF !eax mov eax,FALSE ret .ENDIF INVOKE ShowWindow,hWnd,nShowCmd INVOKE UpdateWindow,hWnd .WHILE TRUE INVOKE GetMessageA,ADDR msg,0,0,0 .BREAK .IF !eax INVOKE TranslateMessage,ADDR msg INVOKE DispatchMessageA,ADDR msg .ENDW mov eax,msg.wParam ret WinMain ENDP WndProc PROC hWnd:HWND,message:UINT,wParam:DWORD,lParam:DWORD LOCAL hDC:HDC LOCAL ps:PAINTSTRUCT .IF message==WM_COMMAND mov eax,wParam .IF ax==IDM_EXIT INVOKE SendMessageA,hWnd,WM_CLOSE,0,0 mov eax,0 ret .ELSEIF ax==IDM_ABOUT mov ebx,OFFSET AboutDlgProc INVOKE DialogBoxParamA,hInst1,IDD_ABOUT and 0000ffffh,hWnd,ebx,0 mov eax,0 ret .ELSE INVOKE DefWindowProcA,hWnd,message,wParam, lParam ret .ENDIF .ELSEIF message==WM_PAINT INVOKE BeginPaint,hWnd,ADDR ps mov hDC,eax INVOKE EndPaint,hWnd,ADDR ps mov eax,0 ret .ELSEIF message==WM_DESTROY INVOKE PostQuitMessage,0 mov eax,0 ret .ELSE INVOKE DefWindowProcA,hWnd,message,wParam,lParam ret .ENDIF mov eax,0ffffffffh ret WndProc ENDP AboutDlgProc PROC hDlg:HWND,message:UINT,wParam:DWORD,lParam:DWORD .IF message==WM_INITDIALOG mov eax,TRUE ret .ELSEIF message==WM_COMMAND mov eax,wParam .IF (ax==IDOK)||(ax==IDCANCEL) INVOKE EndDialog,hDlg,ax mov eax,TRUE ret .ENDIF mov eax,FALSE ret .ELSE mov eax,FALSE ret .ENDIF mov eax,FALSE ret AboutDlgProc ENDP PUBLIC _start END 汇编连接本程序的命令如下: ml /c /coff /Cp generic.asm rc generic.rc link /subsystem:windows /entry:_start generic.obj generic.res kernel32. lib user32.lib gdi32.lib 运行汇编连接后生成的GENERIC.EXE文件,屏幕上将显示出一个带有菜单的窗口,窗口的标题是“Generic”,菜单有两个主菜单项,分别是“File”和“Help”,选择“File”菜单项下的“Exit”菜单项可以退出程序,选择“Help”菜单项下的“About”菜单项可以显示“About”对话框。本程序与SIMPLE.C程序很相似,Win32汇编语言程序中使用资源的方法也与C语言程序没有很大的差别,都可以用资源编辑工具生成资源源文件和资源头文件,然后使用资源编译器编译资源源文件,将生成的资源文件(RES文件)与汇编器生成的目标文件和引入库文件连接在一起就可以了(资源头文件需要移植到汇编语言上,建立一个资源包含文件)。 C语言程序中的MAKEINTRESOURCE宏的实质是将资源标识符数值的高位字(不用)清零,然后强行转换成字符指针,Win32汇编语言程序中可以将资源标识符数值与0000FFFFH作AND运算。 七、控制台Win32汇编语言程序 学习过Win32SDK编程的读者一定知道控制台Win32应用程序,控制台Win32应用程序运行在控制台(MS-DOS窗口)下,与DOS下的C语言程序十分相似,程序入口点是main函数,使用标准C语言I/O函数进行I/O,也可以调用API。实际上控制台Win32应用程序与普通Win32应用程序没有本质上的区别,标准C语言I/O函数实际上还是调用了API,在控制台上进行I/O。控制台Win32汇编语言程序与C语言程序有一定的差别,需要获取控制台I/O句柄,然后使用控制台I/O句柄进行I/O(与文件句柄I/O相似),笔者以MASM 6.11中自带的控制台Win32汇编语言程序实例(HELLO.ASM)为例,程序如下: .386 .MODEL flat, stdcall STD_OUTPUT_HANDLE EQU -11 GetStdHandle PROTO NEAR32 stdcall, nStdHandle:DWORD WriteFile PROTO NEAR32 stdcall, hFile:DWORD, lpBuffer:NEAR32, nNumberOfBytesToWrite:DWORD, lpNumberOfBytesWritten:NEAR32, lpOverlapped:NEAR32 ExitProcess PROTO NEAR32 stdcall, dwExitCode:DWORD .STACK 4096 .DATA msg DB \"Hello, world.\", 13, 10 written DD 0 hStdOut DD 0 .CODE _start: INVOKE GetStdHandle, STD_OUTPUT_HANDLE ; Standard output handle mov hStdOut, eax INVOKE WriteFile, hStdOut, ; File handle for screen NEAR32 PTR msg, ; Address of string LENGTHOF msg, ; Length of string NEAR32 PTR written, ; Bytes written 0 ; Overlapped mode INVOKE ExitProcess, 0 ; Result code for parent process PUBLIC _start END 汇编连接本程序的命令如下: ml /c /coff /Cp hello.asm link /subsystem:console /entry:_start hello.obj kernel32.lib 连接命令中/subsystem:console选项表示连接器生成控制台Win32应用程序。在MS-DOS窗口(控制台)下运行汇编连接后生成的HELLO.EXE文件,将会像MS-DOS程序一样显示出“Hello, world.”字符串。本程序调用了GetStdHandle函数获取标准控制台输出设备句柄,然后调用WriteFile函数向标准控制台输出设备句柄写字符串,完成控制台字符串输出,最后调用了ExitProcess函数终止程序的执行。 八、结束语 本教程读者阅读到这里,可能会莞尔一笑,原来Win32汇编语言也不过就是这么回事呀,确实,MASM 6.0以上版本的汇编器提供的结构化汇编语言伪指令大大简化了Win32汇编语言编程。汇编语言确实比较复杂,但是Win32汇编语言对某些特殊方面有高级语言不可比拟的优点,如果你正在想编程清除Win32病毒(例如CIH病毒),或者你正在编写对速度要求较高的程序(例如大量计算的程序),不妨试试Win32汇编语言――或许正能够解决你的燃眉之急。本教程还简单介绍了Win32应用程序的执行机制,相信会对你探索Win32深层有一定的帮助。本教程所有的源程序已经整理打包,需要者可以直接与笔者联系,更欢迎与笔者交流经验,笔者的E-mail:kv_nt@hotmail.com。 [编辑 - 11/8/02 by Koms Bomb] |
|
|
沙发#
发布于:2002-11-08 14:01
我也来一个 :D
双击窗体滚屏,单击停止 ADO应用(上) 在前面的讲座和例子中,我们是通过BDE来操作数据库的,接下来给大家介绍一下通过ADO来操作数据库。ADO是从Delphi5开始新增加的功能,Delphi5提供了一组ADO组件,通过微软的ADO存取各种类型的数据库,是目前越来越流行的存取数据库的方式,使用ADO就可以无需再使用BDE了,心铃感到非常有必要介绍给读者。一、ADO简介我们介绍一下ADO的来历。Universal Data Access (UDA)是微软公司推出的对数据库操作的一个策略,提供了快速访问各种数据库的能力,UDA提供了一种不受限制的能力,通过易用的API接口访问各种数据源(需要与其兼容的驱动程序),类似Delphi的BDE,这项技术能在一个程序中从多种的数据源中轻易的访问到数据。UDA用Microsoft Data Access Components(MDAC)来实现,而MDAC则包括Active Data Objects(ADO),Open Database Connectivity(ODBC)与OLE DB。ADO是MDAC的应用程序设计接口,OLE DB则是系统级的接口,定义了一套COM接口,提供了从关联数据库及文件系统的数据访问能力,ODBC为了向后兼容也包含在MDAC中,ODBC将会被OLE DB替代,因此就目前来说,如果可以直接通过OLE DB操作的数据库就不要使用ODBC了,目前OLE DB可直接用于Microsoft Access,Microsoft SQL以及Oracle等。 不知道大家对上面的简介是否能看懂,其实看不懂对于我们使用ADO没有太大影响。对初学者来说,能认识到:ADO是微软提供的可以访问数据库的一种接口方式就可以了。那么使用ADO有什么先决条件呢?从上面的简介可以看出,要使用ADO必须安装MDAC组件,不知大家注意没有,在安装Delphi5时会有提示是否安装Microsoft Data Access Components,如果你要使用ADO编程就要选择安装MDAC。当然现在情况已经有了一些变化,据说从Win98第二版开始系统中已经内置了ADO, Windows 2000也已经内置了ADO。如果你在安装Delphi5时没有安装MDAC,而系统中又没有,需要手工安装的话,可以从微软的网站上下载MDAC,目前最新的版本是2.7,Delphi5安装盘上有MDAC目录,版本是2.1版。不过最好是采用高一点的版本,目前最常用的是2.6版,对中文WIN操作系统来说,用MDAC中文版最好。二、ADO使用 在组件面板上有ADO组件页,其中前6个是ADO组件。我们一步一步来入门,先来看看如何使用ADO。以我们前面讲座中的劳保用品管理系统中使用的Access97数据库为例,我们读取数据显示在DbGrid表格中。在空白窗体上放置一个ADOConnection1组件,一个ADOTable1组件、一个DataSource1组件、一个DBGrid1组件。先设定一下部分组件属性如下:设定DBGrid1组件的DataSource属性为DataSource1,设定DataSource1组件的DataSet属性为ADOTable1。双击ADOConnection1组件或者在其属性列表中点击ConnectionString后的省略号都可以打开连接字符串设定窗口如下图(17-1): 图17-1点击“Build”按钮,将打开数据连接属性窗口如下图(17-2),其中有四个页面,在第一个“提供者”页面中,列出了可以使用的各种驱动数据库驱动。对于我们的ACCESS97库来说,用Microsoft Jet 3.51 OLE DB Provider驱动即可,当然选择Microsoft Jet 4.0 OLE DB Provider驱动也可,但对于ACCESS2000数据库来说必须使用JET 4.0,不能使用3.51。 (图17-2)选择完毕数据库驱动后,点击“连接”页面,在这里输入或选择数据库,这里我们查找选择数据库为“G:\\lklb\\lklb.mdb”,如果数据库有密码可将“空白密码”复选框去掉选择,就可以输入密码,选中“允许保存密码”这样以后就可以无需再输入密码了。接下来可以点击“测试连接”按钮,如果提示“测试连接成功”那么说明数据库可正常连接上。在“高级”页面中选择访问权限为“ReadWrite”。在“所有”页面中显示的是用户目前设定的连接属性,用户可以再修改。点击“确定”按钮后返回到图17-1,这时可以看到属性设定字符串已经自动生成,点击“OK”按钮返回即可。再把ADOConnection1组件的LoginPrompt属性设定为false,这样以后程序在运行时就不会弹出密码输入框了。但如果你的数据库有密码且在上面的“连接”页面中没有输入密码没有选中“允许保存密码”,那么不要把此属性设定为false,因为你以后需要手工输入密码。接着我们设定ADOTable1组件的Connection属性为ADOConnection1,单击ADOTable1组件的tablename属性后的省略号,将可以看到数据库lklb中的所有数据表名称列表,从中选择一个如bm(部门),设定ADOTable1组件的Active属性为True,则马上可以在DbGrid中看到表中的数据了。如果马上运行程序就可以在窗口中浏览和编辑数据了。到此,你已经初步学会了使用ADO存取数据了。通过上面的示例,我们看看通过ADO和BDE来读取数据有什么区别呢?其实从操作来说没有多大区别,当然内部的机理是不一样的,但我们无需关心。也许读者会说,比通过BDE读取数据多了一个ADOConnection组件,其实我们完全可以不用ADOConnection组件。这样试试:删除ADOConnection组件,点击ADOTable1组件的ConnectionString 属性后的省略号,怎么样?和上面设定ADOConnection组件的ConnectionString 属性完全是一样的吧。 有了上面的感性认识,下面我们就来对这六个ADO组件逐一做介绍。1 ADOConnection组件。ADOConnection组件有点类似于DataBase组件,正如在一些程序中可以不使用DataBase组件一样,我们在上面的示例中也演示了不使用ADOConnection组件的方法。不过,在用ADO写比较复杂的数据库程序时最好还是用ADOConnection组件,因为ADOConnection组件起到了一个共享桥梁作用,其他五个组件都可以通过它来操作数据库,这样就避免每个组件都要建立自己的连接字符串。ADOConnection组件对其他五个组件来说就相当于数据库别名。既然相当于数据库别名,那么另外一个优点是如果你的后台数据库的连接变化了,那么在程序里只需仅仅将ADOConnection组件的连接属性改变一下就可以了,否则,需要修改所有数据对象组件的连接属性,岂不太麻烦了。所以建议大家养成一个好习惯,这样写出来的程序不易出错、维护方便。 ADOConnection组件的重要属性也就是上面示例中提到的ConnectionString属性了,另外一个经常使用的是logionPrompt属性,上面也已经介绍了。现在补充说明一下ConnectionString属性设定时图17-1中的Use Data Link File是怎么回事:通过前面的示例我们知道,ConnectionString属性值是一个连接字符串,为了便于程序移植方便,特别是当程序已经发布后需要修改连接数据库信息时,再重新编译发布执行文件比较麻烦,这时udl文件就可以发挥很好的作用,其实udl文件的内容就是连接字符串信息,是文本文件,用户修改一下这个文件就可达到目的,无需再更新EXE文件了。UDL文件如何建立呢?常用方法:用记事本建立一个空文本文件,保存为如demo.udl,一般情况下udl是和ADO关联的,双击demo.udl文件会自动打开图17-2,大家都会设定了吧,设定完毕退出后,再打开demo.udl文件看看就会明白,UDL文件就像一个INI文件,例如以我们上面的示例建立的demo.udl文件内容如下:[oledb] ; Everything after this line is an OLE DB initstring Provider=Microsoft.Jet.OLEDB.4.0;DataSource=g:\\lklb\\lklb.mdb;Mode=ReadWrite|Share Deny None;Persist Security Info=False ADOConnection组件的事件和方法都不太常用,如果需要建议大家看看帮助文件吧,在这里由于版面所限不能详细解释,其他几个ADO组件的介绍也是如此,只介绍最常用的属性和方法、事件。2 ADOTable组件。ADOTable组件和前面讲座中讲到的table组件非常类似,许多属性、事件和方法也一样。如果不使用ADOConnection组件,ADOTable组件的ConnectionString属性就需要设定,上面我们已经讲了,如果使用了ADOConnection组件,那么ConnectionString属性就不需要设定了,直接设定其Connection属性指向ADOConnection组件即可。另外一个重要属性就是tablename属性,这个就不用讲了吧。需要指出的是,虽然ADOTable组件和table组件在属性设定、事件、方法等方面非常相似,但还是有不同之处,比如Table组件有findkey方法,但在ADOTable组件中却取消了findkey方法,因此在程序移植时需要注意,以防出现隐患。3 ADOQuery组件。此组件使用方法和query组件类似,其ConnectionString属性和Connection属性的设定同ADOTable组件。有一点需要注意,在程序中为参数赋值时和query组件有点区别,一般情况下原来的赋值语句为:ParamByname(\'p1\').value:=s1; 而在ADOQuery中,需要这样写:parameters.ParamByname(\'p1\').value:=s1; 原来在代码中使用的prepare方法在这里不能再使用了。 4 ADOStoredProc组件。 此组件是用来执行存储过程的,和StoredProc类似。由于在前面我们没有详细讲解存储过程是怎么回事,这里给大家做稍详细一点的介绍。存储过程主要是对客户/服务器(C/S)两层数据库或多层数据库而言的,由于文件型数据库如ACCESS等等不存在真正的存储过程,所以一般用不到存储过程,不过对ACCESS数据库而言有类似存储过程的“查询”,后面我们再讲。简单一点来说,存储过程就是存放在数据库服务器上的一段程序代码,用户程序可直接调用,无需在用户程序中再做更多的工作。为什么要把存储过程放在数据库服务器上呢?因为放在服务器上的代码在执行时效率高,能较少网络数据流量,防止出现网络阻塞。比如,在查询数据时,调用存储过程可以只把结果传递回来而无需先传递数据到本地再查询结果。在继续讲解ADOStoredProc之前,我们先讲一点MSSQL SERVER的知识吧。初学数据库编程的读者可能都对客户/服务器数据库感到有点难度,不知道如何写在网络上的(现在常用的是局域网)客户/服务器数据库程序,事实上,只要你能掌握了本地文件数据库编程知识,在网络上的客户/服务器编程就很简单了,只是需要通过BDE、ODBC或ADO等各种方式把数据源指向数据库服务器上的数据库即可,其他的操作几乎都是一样的。要想学习客户/服务器数据库编程,建议大家安装目前非常流行的MicroSoft SQL Server 2000中文个人版,此版本可安装在WIN9X系统下,其他几个版本如企业版、开发版等则只能安装在WIN NT或WIN2000下,对于使用WIN9X的用户,安装个人版在本机上就完全可以模拟局域网上的客户/服务器环境,没有任何区别,程序可直接移植到局域网上。对于稍复杂一些的需要在局域网上多人使用的数据库程序,采用SQL Server+ADO是很理想的选择,效率高、维护方便,数据安全性也很好。 SQL Server2000中文个人版的安装很简单,按照提示进行即可,安装完毕,从开始->程序中启动“服务管理器”,一般有三种服务,可以都启动或者任选需要启动的服务,不过SQL Server一般是必须要启动的。接下来我们运行“企业管理器” ,从控制台根目录开始逐级打开,直到打开一个数据库,在这里就可以看到存储过程了,这才是真正意义上的存储过程。如下图(17-3)。双击某个存储过程就可以查看其内容。 (图17-3)现在我们来看看如何写一个客户/服务器数据库程序。来看看ADO方式。以上面的例子为例,如果劳保数据库放在了SQL SERVER中,那么双击ADOConnection组件在图17-2中的“提供者”页面中选择“Microsoft OLE DB Provider for SQL Server”驱动,在“连接”页面中的内容和选择JET3.51就不一样了,用户输入服务器名称如:lkky,输入用户名和密码(如果有的话),然后就可以在服务器上选择数据库,比如lklb,再测试一下连接是否正常,如果成功,那么就可以返回了,余下的操作就完全一致了,就这么简单!其实客户/服务器程序和单机文件数据库程序的主要区别在于:对ADO方式来讲,就是ADOConnection组件的连接字符串属性不同,对于BDE方式来说,数据库别名就是通过ODBC建立一个指向数据库服务器的一个数据源。一句话:就是连接数据库的方式有点区别,其他的操作几乎一样。简单解释一下数据库服务器和单机文件数据库的区别。对于我们单机常用的文件数据库如Paradox、DBF、ACCESS类型的数据库来说,数据库就是一个文件夹、数据表是文件夹中的文件(如paradox、 DBF)或一个文件(ACCESS数据库),这类文件数据库没有管理者即无需运行管理程序管理它们,只有用户的数据库应用程序对它们进行读写操作。而对数据库服务器来说,如SQL SERVER,这个服务器负责管理数据库及其他信息如用户、存贮过程等等,用户要想操作数据库需要用过这个服务器才能进行,虽然对服务器来说,其管理的数据库也是以文件的形式放在磁盘中,但用户程序不能直接访问。这样做的好处就是由服务器处理来自多个用户的操作,并把信息返回到请求端(客户),客户端只管发请求即可,无需知道服务器是怎么处理的,如果多个客户都请求同样的内容,那么只需查询一份信息向各客户传送就可以了,充分利用资源,如果多个客户修改某个记录,服务器会自动进行锁定,防止出现错误。打个比方来说,文件型数据库就是一个没有人看守的仓库,客户进去自己放置货物或提出货物,服务器型数据库就是有一个管理员看守仓库,要提货或放入货物只需给管理员下个指令由管理员来进行,大家应该明白两者的区别了吧。下一讲继续给大家讲解以ADO方式操作数据库的知识。 更新时间2002-11-8 被阅览次数:851次 [关闭窗口] |
|
|
板凳#
发布于:2002-11-08 14:00
太好了!可惜没分给你! :D
|
|
|