分类 C++ 下的文章

(转)字符编码转换

有时候你从外部文件读进来的字符串是MBCS编码(如GB2312),而你程序里面都是统一用Unicode处理字符串,这时候要进行字符编码转换。 Windows为我们提供了很好用的API函数 MultiByteToWideChar 和 WideCharToMultiByte 帮我们轻松实现转换。 代码如下:

GB2312 转换成 Unicode:
CODE:

wchar_t* GB2312ToUnicode(const char* szGBString)
{
        UINT nCodePage = 936; //GB2312
        int nLength=MultiByteToWideChar(nCodePage,0,szGBString,-1,NULL,0);
        wchar_t* pBuffer = new wchar_t[nLength+1];
        MultiByteToWideChar(nCodePage,0,szGBString,-1,pBuffer,nLength);
        pBuffer[nLength]=0;
        return pBuffer;
}

BIG5 转换成 Unicode:
CODE:

wchar_t* BIG5ToUnicode(const char* szBIG5String)
{
        UINT nCodePage = 950; //BIG5
        int nLength=MultiByteToWideChar(nCodePage,0,szBIG5String,-1,NULL,0);
        wchar_t* pBuffer = new wchar_t[nLength+1];
        MultiByteToWideChar(nCodePage,0,szBIG5String,-1,pBuffer,nLength);
        pBuffer[nLength]=0;
        return pBuffer;
}

Unicode 转换成 GB2312:
CODE:

char* UnicodeToGB2312(const wchar_t* szUnicodeString)
{
        UINT nCodePage = 936; //GB2312
        int nLength=WideCharToMultiByte(nCodePage,0,szUnicodeString,-1,NULL,0,NULL,NULL);
        char* pBuffer=new char[nLength+1];
        WideCharToMultiByte(nCodePage,0,szUnicodeString,-1,pBuffer,nLength,NULL,NULL);
        pBuffer[nLength]=0;
        return pBuffer;
}

Unicode 转换成 BIG5:
CODE:

char* UnicodeToBIG5(const wchar_t* szUnicodeString)
{
        UINT nCodePage = 950; //BIG5
        int nLength=WideCharToMultiByte(nCodePage,0,szUnicodeString,-1,NULL,0,NULL,NULL);
        char* pBuffer=new char[nLength+1];
        WideCharToMultiByte(nCodePage,0,szUnicodeString,-1,pBuffer,nLength,NULL,NULL);
        pBuffer[nLength]=0;
        return pBuffer;
}

繁体和简体的相互转换

利用Unicode作为媒介,还可以做出很有意思的应用。在处理中文过程中,一个经常用到的功能就是繁体和简体的互相转换。 代码如下:

繁体中文BIG5 转换成 简体中文 GB2312
CODE:

char* BIG5ToGB2312(const char* szBIG5String)
{
        LCID lcid = MAKELCID(MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED),SORT_CHINESE_PRC);

        wchar_t* szUnicodeBuff = BIG5ToUnicode(szBIG5String);
        char* szGB2312Buff = UnicodeToGB2312(szUnicodeBuff);

        int nLength = LCMapString(lcid,LCMAP_SIMPLIFIED_CHINESE, szGB2312Buff,-1,NULL,0);
        char* pBuffer = new char[nLength + 1];
        LCMapString(0x0804,LCMAP_SIMPLIFIED_CHINESE,szGB2312Buff,-1,pBuffer,nLength);
        pBuffer[nLength] = 0;

        delete[] szUnicodeBuff;
        delete[] szGB2312Buff;
        return pBuffer;
}

简体中文 GB2312 转换成 繁体中文BIG5
CODE:

char* GB2312ToBIG5(const char* szGBString)
{
        LCID lcid = MAKELCID(MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED),SORT_CHINESE_PRC);

        int nLength = LCMapString(lcid,LCMAP_TRADITIONAL_CHINESE,szGBString,-1,NULL,0);
        char* pBuffer=new char[nLength+1];
        LCMapString(lcid,LCMAP_TRADITIONAL_CHINESE,szGBString,-1,pBuffer,nLength);
        pBuffer[nLength]=0;

        wchar_t* pUnicodeBuff = GB2312ToUnicode(pBuffer);
        char* pBIG5Buff = UnicodeToBIG5(pUnicodeBuff);

        delete[] pBuffer;
        delete[] pUnicodeBuff;
        return pBIG5Buff;
}

忙里偷闲 - 封装汇编编写的水波特效供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

(转)WM_PAINT消息小结

  WM_PAINT是Windows窗口系统中一条重要的消息,应用程序通过处理该消息实现在窗口上的绘制工作。
  
  1. 系统何时发送WM_PAINT消息?
  系统会在多个不同的时机发送WM_PAINT消息:当第一次创建一个窗口时,当改变窗口的大小时,当把窗口从另一个窗口背后移出时,当最大化或最小化窗口时,等等,这些动作都是由 系统管理的,应用只是被动地接收该消息,在消息处理函数中进行绘制操作;大多数的时候应用也需要能够主动引发窗口中的绘制操作,比如当窗口显示的数据改变的时候,这一般是通过InvalidateRect和 InvalidateRgn函数来完成的。InvalidateRect和InvalidateRgn把指定的区域加到窗口的Update Region中,当应用的消息队列没有其他消息时,如果窗口的Update Region不为空时,系统就会自动产生WM_PAINT消息。
  
  系统为什么不在调用Invalidate时发送WM_PAINT消息呢?又为什么非要等应用消息队列为空时才发送WM_PAINT消息呢?这是因为系统把在窗口中的绘制操作当作一种低优先级的操作,于是尽 可能地推后做。不过这样也有利于提高绘制的效率:两个WM_PAINT消息之间通过InvalidateRect和InvaliateRgn使之失效的区域就会被累加起来,然后在一个WM_PAINT消息中一次得到 更新,不仅能避免多次重复地更新同一区域,也优化了应用的更新操作。像这种通过InvalidateRect和InvalidateRgn来使窗口区域无效,依赖于系统在合适的时机发送WM_PAINT消息的机 制实际上是一种异步工作方式,也就是说,在无效化窗口区域和发送WM_PAINT消息之间是有延迟的;有时候这种延迟并不是我们希望的,这时我们当然可以在无效化窗口区域后利用SendMessage 发送一条WM_PAINT消息来强制立即重画,但不如使用Windows GDI为我们提供的更方便和强大的函数:UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的Update Region,当其不为空时才发送WM_PAINT消息;RedrawWindow则给我们更多的控制:是否重画非客户区和背景,是否总是发送WM_PAINT消息而不管Update Region是否为空等。
  2. BeginPaint
  BeginPaint和WM_PAINT消息紧密相关。试一试在WM_PAINT处理函数中不写BeginPaint会怎样?程序会像进入了一个死循环一样达到惊人的CPU占用率,你会发现程序总在处理一个接 一个的WM_PAINT消息。这是因为在通常情况下,当应用收到WM_PAINT消息时,窗口的Update Region都是非空的(如果为空就不需要发送WM_PAINT消息了),BeginPaint的一个作用就是把该Update Region置为空,这样如果不调用BeginPaint,窗口的Update Region就一直不为空,如前所述,系统就会一直发送WM_PAINT消息。
  
  BeginPaint和WM_ERASEBKGND消息也有关系。当窗口的Update Region被标志为需要擦除背景时,BeginPaint会发送WM_ERASEBKGND消息来重画背景,同时在其返回信息里有一个标志表明窗口背景是否被重画过。当我们用InvalidateRect和InvalidateRgn来把指定区域加到Update Region中时,可以设置该区域是否需要被擦除背景,这样下一个BeginPaint就知道是否需要发送WM_ERASEBKGND消息了。
  
  另外要注意的一点是,BeginPaint只能在WM_PAINT处理函数中使用。

(转)内存中绘图

内存绘图的原理其实很简单!

就是将原本直接绘制到屏幕DC上的内容,绘制到内存中的兼容DC,与输出图形到显示器操作相同,只是绘图目标不同而已!

最后再将内存DC中的图形复制到显示器!

可以认为内存兼容DC是屏幕的一个缓冲,这样基本可以解决一般的由于频繁直接绘制图像到显示器而导致的界面闪烁!

下面给出一段示例代码:

CDC MemDC;   //首先定义一个显示设备对象,所有的绘制首先绘制到这块内存中
CBitmap MemBitmap; //定义一个位图对象

//随后建立与屏幕显示兼容的内存显示设备
MemDC.CreateCompatibleDC(NULL);
MemDC.SetStretchBltMode(HALFTONE);

//这时还不能绘图,因为没有地方画
//下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小
MemBitmap.CreateCompatibleBitmap(&dc /*是dc这个参数,而不是MemDC*/,rectCanvas.Width(),rectCanvas.Height());

//将位图选入到内存显示设备中
//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);

//这一句只是为了填充一个背景,
MemDC.FillSolidRect(rectCanvas.left,rectCanvas.top,rectCanvas.Width(),rectCanvas.Height(),GetBackgroundColor());   

//在MemDC上进行操作,绘制你想要绘制的东西
//...

//将MemDC的图拷贝到屏幕(dc)上进行显示
dc.BitBlt(rectCanvas.left,rectCanvas.top,rectCanvas.Width(), rectCanvas.Height(),&MemDC,0,0,SRCCOPY);

//绘图完成后的清理
MemBitmap.DeleteObject();
MemDC.DeleteDC();

(转)头文件嵌套包含

嵌套包含:两个头文件相互包含,编译出错,如下例:

//文件A.h中的代码
#include "B.h"

class A
{
public:
    B* b;
};

//文件B.h中的代码
#include "A.h"

class B
{
public:
    A* a;
};

解决办法:在其中某一个里面用上Class,如在A.h中
1)不加#include "B.h"
2) 加入Class B, 再在A的类成员里定义B *b

-->//文件A.h中的代码
class B;
class A
{
public:
    B* b;
};

一个头文件包含的原则是:尽量在CPP文件中包含头文件,而非在头文件中