2008年12月

忙里偷闲 - 封装汇编编写的水波特效供C++调用 - 汇编语言与C++语言联编

考研复习呀!时间好紧张!很久没碰编程了,心痒难搔!呵呵

业余时间在看罗云彬的windows 32位汇编语言程序设计,苦于没有时间好好写点代码,那就做点与汇编语言相关的小事情,算是实践一下汇编语言了,呵呵

好了,进入正题!

本文的内容是将罗云彬编写的水波特效的汇编代码(原代码只是一个演示程序)编译为lib库,然后使用C++语言调用该库以实现水波特效。

一直很喜欢水波特效,尤其在一些自动脱壳程序,keygen里这种特效使用的比较常见,感觉很酷!然后上网上找一些相关的代码,很多是使用DirectDraw实现的,要么就是给你一个算法,然后自己写代码吧,没找到使用高级语言尤其是VC实现的基于GDI的现成代码,着实郁闷了很久,本来打算自己操刀,用VC写,唉。。。搁浅了。。。呵呵

还好,这次找到了用汇编写的水波特效,那叫个酷呀!顺便向罗老大致敬,好好学习一番!

推荐大家参考一下这个编译之后只有5.9k的水波特效演示!确实很不错

这篇博文主要介绍一下VC语言与汇编语言的库文件链接及代码调用问题,是我自己的实际练习过程总结!

1.简单修改代码,用于编译为lib库

原版的代码是单独写在一个asm文件里的,汇编项目要用的时候include进去,然后一起编译。

实现水波特效的asm文件不能独立编译。

下面把它简单修改一下。

按照MASM汇编的代码格式

在开头加入如下代码:

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
              .386
              .model flat, stdcall
              option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include           windows.inc
include           user32.inc
includelib     user32.lib
include           kernel32.inc
includelib     kernel32.lib
include           Gdi32.inc
includelib     Gdi32.lib

然后去掉ifndef和endif预处理指令,这个指令是为了防止重复包含原asm文件的,现在不用了。

然后要在最后写入end伪指令,结束该asm模块,使之能够单独编译。end指令在程序项目中是用于指定入口点的,该库文件不能指定入口点,因为这不是主程序,所以直接写end即可。

2.编译并整理为lib库

然后编译该asm文件,再使用lib.exe整理为lib库!插入一个小话题,我用RadASM执行时lib.exe总是运行失败,但是直接在cmd里用lib程序就OK!不知道怎么的!原因不明,正在探索中。。。

3.为C++语言编写h文件

将原来的asm中定义的结构体WAVE_OBJECT用C++语言的结构体语法重新整理一下。

typedef struct _WAVE_OBJECT
{
HWND hWnd;
DWORD dwFlag;
DWORD hDcRender;
DWORD hBmpRender;
DWORD lpDIBitsSource;
DWORD   lpDIBitsRender;// 用于显示到屏幕的像素数据
DWORD lpWave1;       // 水波能量数据缓冲1
DWORD lpWave2;       // 水波能量数据缓冲2
//********************************************************************
DWORD dwBmpWidth;
DWORD dwBmpHeight;
DWORD dwDIByteWidth;// = (dwBmpWidth * 3 + 3) and ~3
DWORD dwWaveByteWidth;// = dwBmpWidth * 4
DWORD dwRandom;
//********************************************************************
// 特效参数
//********************************************************************
DWORD dwEffectType;
DWORD dwEffectParam1;
DWORD dwEffectParam2;
DWORD dwEffectParam3;
//********************************************************************
// 用于行船特效
//********************************************************************
DWORD dwEff2X;
DWORD dwEff2Y;
DWORD dwEff2XAdd;
DWORD dwEff2YAdd;
DWORD dwEff2Flip;
//********************************************************************
BITMAPINFO stBmpInfo;// BITMAPINFO <>
} WAVE_OBJECT,*LPWAVE_OBJECT;

然后将函数声明用C++语法添加进去(部分内部使用的函数可以不用进行声明,因为外部并不需要调用):

extern "C" void __stdcall _WaveInit(LPWAVE_OBJECT lpWaveObject,HWND hWnd,HBITMAP hBmp,DWORD dwSpeed,DWORD dwType);

extern "C" void __stdcall _WaveDropStone(LPWAVE_OBJECT lpWaveObject,DWORD dwPosX,DWORD dwPosY,DWORD dwStoneSize,DWORD dwStoneWeight);

extern "C" void __stdcall _WaveUpdateFrame(LPWAVE_OBJECT lpWaveObject,HDC hDc,DWORD bIfForce);

extern "C" void __stdcall _WaveFree(LPWAVE_OBJECT lpWaveObject);

extern "C" void __stdcall _WaveEffect(LPWAVE_OBJECT lpWaveObject,DWORD dwType,DWORD dwParam1,DWORD dwParam2,DWORD dwParam3);

需要注意的是以下几点:

<1>extern “C”

这里的C告诉编译器这个外部函数的修饰名使用C语言的修饰规则而不是C++语言,这里是我自己用的,好象extern “asm”更确切一点,貌似MASM的函数修饰规则和C的有些类同,仅仅是猜的!还没有找相关的文档验证,等搞清楚了再落实吧,这里还是采用我自己的测试结果!实践为重,呵呵!

这样写了以后,链接器在链接的时候就知道该链接那个函数的代码了,因为编译后的函数代码就是用函数的修饰名进行区别的。

<2>__stdcall

这里前面是两个下滑划线,这是C++语言中指定函数调用规则的关键字,这里使用stdcall调用规则,因为原来的汇编代码中的指令.model flat, stdcall,指定了函数的调用规则,所以这里要一致,否则就会出现堆栈错误。程序就挂了。。。

好了,头文件也写完了!对了在头文件里加上一句#pragma once,防止其被多次包含,免得弄得一身麻烦!我把它命名为waveHead.h,以后在程序里include这个头文件,然后在工程里添加上面编译的lib文件,就可以在自己的C++程序秀一把水波特效了。呵呵!

现在操刀试试吧!

1.打开VC,新建一个基于对话框应用程序。我把他命名为waveObject

2.添加一个位图资源,ID为IDB_BITMAP1,编辑对话框资源,把里面的Static的ID设置为IDC_STATIC1,在水波的初始化中要用到。

最好把该Static设置为隐藏,因为在VC里应用该水波并不完美,藏起来为好,呵呵!考研完之后再好好完善吧,拿类把它包起来,呵呵!

3.将waveHead.h添加到waveObjectDlg.cpp和waveObjectDlg.h中

4.在waveObjectDlg中添加成员变量的WAVE_OBJECT wb,这是用于实现水波效果的结构体,必不可少!然后添加一个HBITMAP pic,这是位图句柄,因为该水波特效必须要一个位图。(要在上面添加效果嘛,呵呵)

5.初始化WAVE_OBJECT

在对话框的初始化消息的处理函数中,添加代码

     pic = LoadBitmap(GetModuleHandle(NULL),MAKEINTRESOURCE(IDB_BITMAP1));
     _WaveInit(&wb,GetDlgItem(IDC_STATIC1)->GetSafeHwnd(),pic,30,0);
    _WaveEffect(&wb,2,4,2,180);

载入位图资源,并初始化WAVE_OBJECT,然后添加罗云彬给我们提供的一个基于水波的行船特效,小秀一把!呵呵

好了,在图片上的“水”里砸一个石头吧!呵呵

6.在WM_LBUTTONDOWN消息里添加代码,以实现石头砸水时的水波,很酷!

     ClientToScreen(&point);
     GetDlgItem(IDC_STATIC1)->ScreenToClient(&point);
     _WaveDropStone(&wb,point.x,point.y,2,256);

7.最后别忘了,关闭程序的时候要释放WAVE_OBJECT

在WM_CLOSE消息的处理函数里添加代码:

_WaveFree(&wb);

8.OK,编译链接

截个图:
1.jpg

原汇编演示程序截个图:
2.jpg