分类 Windows编程 下的文章

静态链接库和动态链接库

以前的时候写过这方面的博客,当时写的时候都觉得已经懂了。今天闲着没事干,和咚哥他们去大华校招笔试遇到了这个问题。我突然间发现SDK没写三个月,把Dll都忘记了。回来看了下,复习下。

以前写过程序的装载和链接的博客,那是OS上面比较理论的。实际用函数库的时候主要分为,静态库和动态库,这里就简简单单地介绍下了。

静态库


静态库(Static Libary)就是平时的 .lib 文件,静态库里面包含的是所有的代码和数据,是完整的!程序在编译生成.obj文件后,连接器将.obj文件和.lib文件整合到.exe里面,程序运行时候所需的函数代码和数据都已经在.exe中了,静态库很霸道,不管你愿不愿意,它就一股脑儿把所有需要的东西都给你,所以就没有导出函数这个概念,缺点就是整合出来的产品比较大,而且很不利于下次的升级,每次升级都得把lib文件修改,然后重新编译链接整合出来新的程序,而且静态链接库不能包含其他的链接库,这一点和动态链接库有很大的区别

使用方式:


在生成的.h文件中做如下声明

extern "C" function()......

在调用函数的.cpp文件里面

#include "XXX.h"

#pragma comment(lib,"XXX.lib")

动态链接库


动态链接库是在运行的时候动态加载的,可以人为也可以非人为,看你怎么用,总之就是很灵活。下面直接说调用方式。

1.隐式加载


隐式加载把你的动态链接库的代码生成.lib文件+.dll,隐式加载的时候需要的就是.h,.dll,.lib文件。说的简单点,.dll文件里面是函数的定义和数据,只是一部分,那么声明呢?声明就在.lib,.lib文件就包含了.dll文件中函数的声明和一些其他的数据。奇怪了,为什么上面的静态链接库里面的.lib文件不是涵盖了所有的函数信息吗?我以前百思不得其解,查了下网上的资料,此.lib非彼.lib。若是编译链接你的dll至少输出一个函数的话,.lib文件就当作一种引导函数的输出表来用了,里面包含了函数的“声明”。在隐式加载里面的.lib文件编译链接的时候整合到程序里面,但是dll里面的代码和数据却没有整合进去,让exe文件所占空间更小。但是加载程序加载exe,dll就被加载到内存中了,加载程序先为新的进程创建一个虚拟地址空间,并将可执行模块映射到新进程的地址空间中。加载程序接着解析可执行模块的导入段。对导入段中列出的每个DLL,加载程序会在用户的系统中对该DLL模块进行定位(差不多就是和lib中进行核实),并将该DLL映射到进程的地址空间中。注意,由于DLL模块可以从其它DLL模块中导入函数和变量,因此DLL模块可能有自己的导入段并需要将它所需的DLL模块映射到进程的地址空间中。

详细参考http://www.cnblogs.com/forlina/archive/2011/08/08/2131011.html

隐式加载使用方式:


#include "XXX.h"

#pragma comment(lib,XXX.lib)

并把dll放到环境变量或者执行目录下

2.显示加载


采用LoadLibrary,FreeLibrary来手动加载,这种加载是在程序运行的时候加载所需要的Dll,和隐式链接不一样,显示加载随时可以在需要的时候加载指定的Dll模块,也可以随时卸载。而隐式加载实际上存在一个Dll计数器,当计数器为0的时候系统就自动帮你卸载了。函数会在用户的系统中对DLL的文件映像进行定位,并试图将该文件映像映射到调用进程的地址空间中。LoadLibrary返回的HMODULE表示文件映像被映 射到的虚拟内存地址。  

显示加载使用方式:


只需要Dll即可

详细参考http://www.cnblogs.com/forlina/archive/2011/08/08/2131042.html

 

 

进程快照获取

进程快照主要存在于PROCESSENTRY32这个结构体中,下面是这个结构体的信息。

typedef struct tagPROCESSENTRY32 {
DWORD dwSize; //结构体大小
DWORD cntUsage; //0
DWORD th32ProcessID; //进程ID:PID
ULONG_PTR th32DefaultHeapID; //0
DWORD th32ModuleID; //0
DWORD cntThreads; //进程计数
DWORD th32ParentProcessID; //父进程ID
LONG pcPriClassBase; //线程优先权
DWORD dwFlags; //0
TCHAR szExeFile[MAX_PATH]; //进程的可执行文件名称
} PROCESSENTRY32, *PPROCESSENTRY32;

- 阅读剩余部分 -

YCrCb 模型

不久前做的激光笔项目里面有一块内容是,查找一张图片中的红点,算法之一就是通过YCrCb 模型模型来寻找最亮的点。

YCrCb 模型更适合图形压缩. 因为人眼对图片上的亮度 Y 的变化远比色度 C 的变化敏感. 我们完全可以每个点保存一个 8bit 的亮度值, 每 2x2 个点保存一个 Cr Cb 值, 而图象在肉眼中的感觉不会起太大的变化.。
所以, 原来用 RGB 模型, 4 个点需要 4x3=12 字节. 而现在仅需要 4+2=6 字节; 平
均每个点占 12bit. 当然 JPEG 格式里允许每个点的 C 值都记录下来; 不过 MPEG 里
都是按 12bit 一个点来存放的, 我们简写为 YUV12。

[R G B] -> [Y Cb Cr] 转换
-------------------------


(R,G,B 都是 8bit unsigned)

       | Y  |     |  0.299       0.587       0.114 |   | R |     | 0 |

       | Cb |  =  |- 0.1687    - 0.3313      0.5   | * | G |   + |128|

        | Cr |     |  0.5       - 0.4187    - 0.0813|   | B |     |128|

Y = 0.299*R + 0.587*G + 0.114*B  (亮度)
Cb =  - 0.1687*R - 0.3313*G + 0.5   *B + 128
Cr =    0.5   *R - 0.4187*G - 0.0813*B + 128

[Y,Cb,Cr] -> [R,G,B] 转换
-------------------------


R = Y + 1.402  *(Cr-128)
G = Y - 0.34414*(Cb-128) - 0.71414*(Cr-128)
B = Y + 1.772  *(Cb-128)

窗口透明和边框隐藏设置

上次做激光笔项目的时候想到了窗口变透明,就相当于屏幕的最上层是一个透明窗口,并且没有边框。然后今天看醒哥的屏保数字雨的时候突然间记起来上次的实现,于是又写了下。

关于窗口的透明处理首先设置下窗口属性

 SetWindowLong(hwnd,GWL_EXSTYLE,WS_EX_LAYERED);

然后用下面这个函数
BOOL SetLayeredWindowAttributes(
HWND hwnd, // handle to the layered window 透明窗体的句柄
COLORREF crKey, // specifies the color key 颜色值
BYTE bAlpha, // value for the blend function 透明度,取值范围是[0,255]
DWORD dwFlags // action 透明方式,可以取两个值:
        // 当取值为LWA_ALPHA时,crKey参数无效,bAlpha参数有效; 
 // 当取值为LWA_COLORKEY时,bAlpha参数有效,而窗体中的所有颜色为crKey的地方将变为透明。
        LWA_ALPHA = 0x2
        LWA_COLORKEY=0x1
);

其中dwFlags有LWA_ALPHA(值为2)和LWA_COLORKEY(值为1)两种设置,如果LWA_ALPHA被设置的话,通过bAlpha参数指定窗体的透明度;如果LWA_COLORKEY标志被设置的话,则指定关键色为crKey,该颜色所覆盖的区域将从窗体去除,去除后的区域将不再进行点击检测,其他颜色则正常显示。如果阿尔法混合值设置为0,其窗口区域同样不进行点击检测。

至于窗口的无边框可以在创建窗口时设置参数即可。

	cx = GetSystemMetrics(SM_CXSCREEN);
	cy = GetSystemMetrics(SM_CYSCREEN);
     hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("The Hello Program"), // window caption
                          WS_DLGFRAME | WS_THICKFRAME | WS_POPUP,        // window style
                          0,              // initial x position
                          0,              // initial y position
                          cx,              // initial x size
                          cy,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters

 

(转)VS2008中编译通过,但调试时出现“未使用调试信息生成二进制文件”的问题

现象:在vs2008中,建立一个空项目,添加一个c++文件main.cc

#include<iostream> using namespace std; int main() { cout << "hello"<<end; return 0; }

 

该程序能够编译得过,但是执行,无论是debug还是release,都很出现

“无法找到“xxx.exe”的调试信息,或者调试信息不匹配。未使用调试信息生成二进制文件。”

- 阅读剩余部分 -

<转>GTK+ VS MFC

今天看到一篇关于GTK+和MFC对比的文章,学GTK+编程的来看看 

MFC已经江河日下,日渐式微,而GTK+可谓欣欣向荣,如日中天。这里无意于落井下石,痛打落水狗,贬MFC而尊GTK+。自己即在使用MFC也在使用 GTK+,不会偏袒其中之任何一方。这个对比完全出于个人对两者的理解,说它是不完全对比,一方面只是一时兴起想做个笔记而已,另外一方面我对两者的理解 也是有限的。

1. 两者都是基于面向对象设计的。尽管MFC是用C++写的,而GTK+是用C写的,但思想都是面向对象的。GTK+使用glib的对象机制,由于用C写的,其实现相对有点繁琐。

- 阅读剩余部分 -

基本调试命令

OD基本调试命令


F2:设置断点
F8:执行下一条指令
F7:单步步入,功能和F8类似,区别是遇到cll指令调试子程序会跟进子程序
F4:运行到选定位置
F9:运行程序,若是没有断点就一直处于运行状态
F12:停止运行

 

Debug基本调试命令


debug filename 进入调试
-R 查看,修改寄存器的内容,比如R CS,可以查看CS寄存器内容,并且可以修改
-D 查看内存中的内容。比如 D 0010,查看内存是0010的内容
-E 修改内存中的内容,和D使用一样
-U 将内存中的机器指令翻译成汇编指令
-T 执行下一条机器指令
-A 以汇编指令的格式在内存中写入一条机器指令

 

TC基本调试命令


F7:Trace into 单步跟踪,进入函数内部。
F8:Step over 单步跟踪,跳过函数。
Ctr+F7:查看变量
Ctr+F8:设置断点

Ctr+F9:执行到下一断点

一些基本的IDE调试命令就不说了,上面写的都是就是当作记事本了···

windows程序设计之DIB操作

主要介绍下与设备无关的位图DIB(Device Independent Bitmap)
位图的基本格式
1.文件头
typedef struct tagBITMAPFILEHEADER {
WORD bfType;//bmp图类型为'BM'或者0X4D42
DWORD bfSize;//整个文件大小
WORD bfReserved1;//0
WORD bfReserved2;//0
DWORD bfOffBits;//DIB像素数据偏移
} BITMAPFILEHEADER
2.信息头
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;//结构体大小 = 40
LONG biWidth;//文件宽度(像素)
LONG biHeight;//文件高度(像素)
WORD biPlanes;//1
WORD biBitCount;//每像素位数(1,4,8,16,24,32)
DWORD biCompression;//压缩方式
DWORD biSizeImage;//实际位图数据占用的字节数
LONG biXPelsPerMeter;//X方向分辨率
LONG biYPelsPerMeter;//Y方向分辨率
DWORD biClrUsed;//使用的颜色数
DWORD biClrImportant;//重要颜色数
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
biBitCount字段
$ 1(2色DIB)
$ 4(16色DIB)
$ 8(256色DIB)
$ 24(全色DIB)

3.RGB色彩表(不一定有)
对于像素位(biBitCount字段)是1,4,和8时,BITMAPINFOHEADER后面跟着的是色彩表。
typedef struct tagRGBQUAD {
BYTE rgbBlue;//蓝色
BYTE rgbGreen;//绿色
BYTE rgbRed;//红色
BYTE rgbReserved;//保留,0
} RGBQUAD;

每个像素点都是一个RGB,三种色彩组成颜色。要注意的就是里面的元素顺序是BGR。

4.位图像素位
这里才是图像数据的真实存放处,可以在这里读取图像的数据进行操作。每个像素点都是由RGB数组构成。要注意的是,在DIB中,图像的底行是文件的第一行,图像的顶行是文件的最后一行。但是对于同一行来说,还是从左到右存放数据的。windows程序设计上是这样说的:从下到上DIB的原点是位图图像的左下角,它是图像的第一行的第一个像素。从上到下DIB的原点也是位图图像的左下角,但是这种情况下,左下角是位图数据的最后一行的第一个像素。
DIB中的行数是BITMAPINFOHEADER结构中的biHeight字段,每一行的像素是该结构中biWidth字段,每一行从左边开始,向右数,每个像素位数由bcBitCount确定。
每行的长度必须是4的倍数。
RowLength = 4 * ((bmch.bcWidth * bmch.bcBitCount + 31) / 32)计算;

总的像素位数据大小 = RowLength *bmch.biHeight计算。
下面是对DIB文件的读写:

/****************************************************************************
*函数名称: ReadBmp()
*函数参数: const char *bmpName 写入bmp格式文件的名称及路径
***************************************************************************/
bool ReadBmp(const char *strFile)
{
BITMAPFILEHEADER bitHead;
BITMAPINFOHEADER bitInfoHead;
FILE* pfile;

pfile = fopen(strFile,"rb");//打开文件

if(pfile!=NULL)
{
printf("file bkwood.bmp open success.n");
//读取位图文件头信息
fread(&bitHead,1,sizeof(BITMAPFILEHEADER),pfile);
//fseek(pfile,2,SEEK_CUR); // "BM"
if(bitHead.bfType != 0x4d42)
{
printf("file is not .bmp file!");
return false;
}
//读取位图信息头信息
fread(&bitInfoHead,1,sizeof(BITMAPINFOHEADER),pfile);
}
tagRGBQUAD *pRgb ;
int width = bitInfoHead.biWidth;
int height = bitInfoHead.biHeight;
//分配内存空间把源图存入内存
int l_width = 4 * ((width* bitInfoHead.biBitCount+31) /32);//计算位图的实际宽度并确保它为32的倍数
BYTE *pColorData=(BYTE *)malloc(height*l_width);
memset(pColorData,0,height*l_width);
long nData = height*l_width;

//把位图数据信息读到数组里
fread(pColorData,1,nData,pfile);
fclose(pfile);
return true;
}

/****************************************************************************
*函数名称: saveBmp()

unsigned char *imgBuf 待存盘的位图数据
int width, 以像素为单位待存盘的位图宽
int height, 以像素为单位待存盘的位图高
int biBitCount, 每个像素占的位数
RGBQUAD *pColorTable 颜色表指针
*函数返回值:0为失败 1为成功
*函数描述:给定写入bmp文件的名称和路径 要写入图像的位图数据,宽,高,写进文件中
*
***************************************************************************/
bool saveBmp(const char* bmpName,unsigned char *imgBuf,int width,int height,int biBitCount,RGBQUAD *pColorTable)
{
if(!imgBuf)//imgBuf 待存盘的位图数据
return 0;

int colorTablesize = 0;
if(biBitCount == 8)
colorTablesize =1024;

int lineByte = (width * biBitCount/8+3)/4*4; //行的长度。以字节为单位的每行长度始终是4的倍数

FILE *fp = fopen(bmpName,"wb");
if(fp == 0) return 0;

BITMAPFILEHEADER fileHead; //文件头
fileHead.bfType= 0x4d42; //确保是bmp图像:"BM"
fileHead.bfSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) + colorTablesize + lineByte *height; //图像大小
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;
fileHead.bfOffBits = 54 +colorTablesize;

fwrite(&fileHead,sizeof(BITMAPFILEHEADER),1,fp); //写图像文件头

BITMAPINFOHEADER head; //信息头
head.biBitCount = biBitCount; //每像素的位数
head.biClrImportant = 0;
head.biClrUsed = 0;
head.biCompression = 0;
head.biHeight = height; //图像高度
head.biPlanes =1;
head.biSize = 40;
head.biSizeImage = lineByte *height; //图像字节数
head.biWidth = width; //图像高度
head.biXPelsPerMeter = 0;
head.biYPelsPerMeter = 0;

fwrite(&head,sizeof(BITMAPINFOHEADER),1,fp); //写图像文件信息头

if(biBitCount == 8) //如果是8bits,写入色彩表
fwrite(pColorTable,sizeof(RGBQUAD),256,fp);

fwrite(imgBuf,height * lineByte,1,fp); //写图像的数据内容
fclose(fp);
return 1;
}

windows程序设计之GDI简介

最近在做一个有关激光笔的小项目,和学长们一起做的。里面还有硬件成份,现在因为才开始做,我主要做软件上面的东西,当然技术含量是很少的抓狂···诶,学了一年左右了,水平还是没很大长进刚刚C入门,SDK也只能说勉强算是入门了吧。项目里面有一部分是要绘制出来鼠标的移动轨迹的,我就去重新看了下GDI,发现自己以前没写过这些东西,印象就是不深刻啊,于是就温习了下。重新看了下<>和<>,ctrl+c和ctrl+v出来下面个实验品,然后做了些笔记,我还是记得学长讲得那句话:博客就是用来记录自己成长的地方。无论有什么学到的,都可以写上来,无论含量高不高,总能在自己以后有一天需要的时候想起来,并且找到,这大概和老师说的好记性不如让笔头差不多吧.

下面是自己一些简单的总结:

windows GDI绘图
Graphics Device Interface:图形设备接口
GDI是GraphicsDeviceInterface的缩写,含义是图形设备接口,它的主要任务是负责系统与绘图程序之间的信息交换,处理所有Windows程序的图形输出。
在Windows操作系统下,绝大多数具备图形界面的应用程序都离不开GDI,我们利用GDI所提供的众多函数就可以方便的在屏幕、打印机及其它输出设备上输出图形,文本等操作。GDI的出现使程序员无需要关心硬件设备及设备驱动,就可以将应用程序的输出转化为硬件设备上的输出,实现了程序开发者与硬件设备的隔离,大大方便了开发工作。

1.资源使用
// 初始的颜色
BYTE bRed = 0;
BYTE bGreen = 0;
BYTE bBlue = 0;
// 笔的颜色 正黑
COLORREF cPen = RGB(bRed, bGreen, bBlue);
//画笔的设置
HPEN CreatePen(
__in int fnPenStyle,
__in int nWidth,
__in COLORREF crColor
);
HPEN hpen;
hpen = CreatePen(PS_SOLID, 10, cPen); //画笔基本属性

//画刷的设置
HBRUSH hbrush;
COLORREF cBrush = RGB(233, GetGValue(cPen),255);
hbrush = CreateSolidBrush(cBrush);
// 释放资源
DeleteObject(hpen);
DeleteObject(hbrush);

2.获取DC。获取了DC就相当于获取了绘画的资格。
HDC hDC = GetDC(hWnd);//参数是NULL时获取的是整个显示器的DC,若是句柄就是指定窗口的DC。

3.将DC设置成对象目标句柄。将图形对象选入DC中,这样DC中原有的类型图形对象就会被新的对象替换。
SelectObject(hDC, hpen);
SelectObject(hDC, hbrush);
这样绘画的时候就可以改变默认的DC属性。

4.绘画区域的变化
GetClientRect(hWnd, &rect);这里获取的是客户区域的大小,是以客户区左上角为坐标原点进行创建的。因此比如在处理WM_MOUSEMOVE等鼠标消息的时候,传进来的lParam参数中包含的坐标也是相对于客户端的。要想转化成屏幕坐标可以用下面的方法:
//转换窗口有效区为屏幕座标
POINT point;
point.x = 0, point.y = 0;
ClientToScreen(hWnd,&point);
你要是想把光标限制在客户区内,可以这样:
//将光标限定在窗口有效区内
rect.top = point.y;
rect.left = point.x;
rect.bottom += rect.top;
rect.right += rect.left;
ClipCursor(&rect); //rect一定要是屏幕座标
下面是获取屏幕坐标的函数
POINT pt;
GetCursorPos(&pt);
这里获取的坐标就是相对屏幕左上角的坐标。若是要转化成客户区坐标可以用
ScreenToClient(hWnd,&point);

5.绘制线条
移到指定的点

下面是我修改的一个绘制鼠标轨迹的小程序

#define MAXGUIDESEGMENTS 1000
#define MyAlloc(dwSize) HeapAlloc(GetProcessHeap(),0,dwSize)
#define MyFree(lpMem) HeapFree(GetProcessHeap(),0,lpMem);

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
BOOL GetGuideLine(HWND hWnd, LPPOINT *lpPoint, LPDWORD lpdwNumPts);

LONG clsCur;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("LineDemo") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;

wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;

if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("Program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}

hwnd = CreateWindow (szAppName, TEXT ("Line Demonstration"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC = NULL ;
static LPPOINT lpRed = NULL;
static DWORD dwRed = 0;
switch (message)
{
case WM_CREATE:
hDC = GetDC(hwnd);
break;

case WM_LBUTTONDOWN:
//取得窗口有效区
RECT rect;
GetClientRect(hwnd,&rect);

//转换窗口有效区为屏幕座标
POINT point;
point.x = 0, point.y = 0;
ClientToScreen(hwnd,&point);
rect.top = point.y;
rect.left = point.x;
rect.bottom += rect.top;
rect.right += rect.left;

ClipCursor(&rect); //rect一定要是屏幕座标

if (!clsCur) {
clsCur = GetClassLong(hwnd,GCL_HCURSOR); //取当前窗口的光标
}
SetClassLong(hwnd,GCL_HCURSOR,NULL); //设置当前窗口的光标为NULL
SetCursor(LoadCursor(NULL, IDC_CROSS)); //设置当前光标为十字形

GetGuideLine(hwnd, &lpRed, &dwRed);

break;
case WM_DESTROY:
if (lpRed)
MyFree(lpRed);
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}

BOOL GetGuideLine(HWND hWnd, LPPOINT *lpPoint, LPDWORD lpdwNumPts)
{
MSG msg;
HDC hDC = GetDC(hWnd);
BOOL bFirstTime = TRUE;
DWORD dwPos = 0;
RECT rect;
static HPEN hpen;

hpen = CreatePen(PS_SOLID,3, RGB(0,255,0));
SelectObject(hDC,hpen);

GetClientRect(hWnd, &rect);
// 为点数组分配空间
*lpPoint = (LPPOINT)MyAlloc(MAXGUIDESEGMENTS * sizeof(POINT));
SetCapture(hWnd); // 设置鼠标捕获器
// 将客户区填充为白色
GetClientRect(hWnd, &rect);
PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);

while(1)
{
// 过滤所有鼠标消息
WaitMessage();
if (PeekMessage(&msg,NULL,WM_MOUSEFIRST,WM_MOUSELAST,PM_REMOVE))
{
// 判断是否在客户区中
if ((LOWORD(msg.lParam) < rect.right) && (HIWORD(msg.lParam) <
rect.bottom))
{
// 是否第一次收到消息
if (bFirstTime)
{
bFirstTime = FALSE;
// 如果是第一次将笔的起点移动到鼠标点击的位置
MoveToEx(hDC, LOWORD(msg.lParam), HIWORD(msg.lParam),
NULL);
}
// 是否达到了最大点数
if (dwPos < MAXGUIDESEGMENTS)
{
// 鼠标的移动会产生鼠标消息,每收到一次消息保存一个点
(*lpPoint)[dwPos].x = LOWORD(msg.lParam);
(*lpPoint)[dwPos].y = HIWORD(msg.lParam);
// 绘制到鼠标所在的点
LineTo(hDC, (*lpPoint)[dwPos].x, (*lpPoint)
[dwPos].y);
dwPos++;
}
}
}
if (msg.message == WM_RBUTTONDOWN)
{
ClipCursor(NULL);
SetClassLong(hWnd,GCL_HCURSOR, clsCur);
SetCursor((HCURSOR)clsCur);
break;
}
else
continue;
}

*lpdwNumPts = dwPos;
ReleaseDC(hWnd, hDC);
ReleaseCapture();
DeleteDC( hDC );

return TRUE;
}

最近的时间越来越紧啊···专业课越来越难,自己课余学计算机的时间也越来越少,真是后后悔自己没去计算机专业额,不过事在人为,努力就好!昨天看到一句比较好的话:一个优秀的程序员:勤奋+执着!我看着很有感触,有时觉得自己花那么多时间面对代码,是否值得···不过我想有付出就会有回报的!!

windows下的错误代码处理

看了下《windows核心编程》的错误代码处理部分。我这才知道错误代码处理的重要性,以前的时候有错误就埋头苦调,现在发现当初好傻。埋头苦调根本就是又费力又费时,而且错误代码还不一定能调的出来。以后就用Windows下面的错误代码处理额···

下面是一个例子

#include 
DWORD GetLastErrorBox(HWND hWnd, LPSTR lpTitle);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

- 阅读剩余部分 -

WM_PAINT消息的异常情况

碰到了一个很纠结的事情,结果我找了很久才找到问题所在,竟然出在了WM_PAINT消息里面。我本来是想写些GDI的东西的,想设个定时器每隔点时间画个点。结果纠结的事情就是定时器不起作用了,就是画不出点。后来我做了一个精简,写了个小程序,在按下左键的时候触发定时器,然后每1S钟触发WM_TIMER消息,弹出一个框。奇怪的事情就是在这里,老弹不出框。

先看代码:

#include 
#include 

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

#define ID_TIMER 1

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("LineDemo") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;

wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;

if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("Program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}

hwnd = CreateWindow (szAppName, TEXT ("Line Demonstration"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HANDLE h_Handle;
HDC hdc ;
static HPEN hPen;
static COLORREF colorrrefRGB =RGB(0,0,0);
static POINT pt;
switch (message)
{
case WM_TIMER:
MessageBox(NULL,"","",MB_OK);
return 0;
case WM_PAINT:

return 0;
case WM_LBUTTONDOWN:
SetTimer(hwnd,ID_TIMER,1000,NULL);
break;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}

难道是没有收到WM_TIMER消息?于是我就在WM_LBUTTONDOWN消息里面SendMessage(hwnd,WM_TIMER,NULL,1);
调试的时候发现消息是传到了WM_TIMER里面的,但是就是卡在了MessageBox函数那。这可纠结了···

过了一两个小时,我终于发现问题出在哪里了,就是在WM_PAINT那里!我重新去找了WM_PAINT消息的资料,总算了解了些。
下面是我参考http://www.examda.com/ncre2/cpp/jichu/20090613/082508824-2.html的。

首先,什么时候会触发WM_PAINT消息?在以下情况下,Windows可能发送WM_PAINT消息:
Windows擦除覆盖了部分窗口的对话框或消息框。
菜单下拉出来,然后被释放。
显示工具提示消息。
在某些情况下,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函数的作用就是将窗口需要重绘的区域设置为空(也就是Update Region置空)。在正常情况下,我们接收到了WM_PAINT消息后,窗口的Update
Region都是非空的(如果为空就不需要发送WM_PAINT消息了)。而当你响应这个消息的时候又不调用BeginPaint来清空,窗口的Update Region就一直是非空的,系统就会一直发送WM_PAINT消息。这样就形成了一个处理WM_PAINT消息的死循环。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_PAINT处理函数中使用,并且在调用了BeginPaint函数后,不要忘记了调用EndPaint函数,他们可是一对的。

再回来上面的代码,这下解决起来就很轻松了。
下面有两种解决方法

1. case WM_PAINT:

hdc = BeginPaint (hwnd, &ps) ;
EndPaint(hwnd, &ps);
return 0 ;
这样就windows就会把无效区域重画了,就不会一直产生WM_PAINT消息,阻塞队列了。这样就自然能弹出对话框了。

2. case WM_PAINT:

break;
直接用break就能跳出switch结构,调用DefWindowProc (hwnd, message, wParam, lParam) ; 交还给Windows自己处理。
Windows自己会处理WM_PAINT消息。

今天就到这里了(*^__^*)

windows程序设计之动态链接库

今天很高兴大笑,因为军训结束了,真是累啊,变得黑人一样。。。(泪奔)委屈

切入正题,最近使用到了HOOK技术,就顺便学习了下DLL的知识,不过只是大概地看了下而已,概念性的东西很多,也就只能了解下,还有很多涉及到操作系统层面的,所以看的云里雾里快哭了。最后东拼西凑写了点,便于以后自己学习。

- 阅读剩余部分 -

Hook技术简介

钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。
钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。这和前面我博客的窗口子类化都异曲同工,但是Hook可强大多了,我是这两天才开始看的,所以略知皮毛。

- 阅读剩余部分 -

windows下安装,配置gcc编译器

在Windows下使用gcc编译器:


1.首先介绍下MinGW


MinGW是指只用自由软件来生成纯粹的Win32可执行文件的编译环境,它是Minimalist GNU on Windows的略称。

  实际上 MinGW 并不是一个 单纯的C/C++ 编译器,而是一套 GNU 工具集合。除开 GCC 以外,MinGW 还包含有一些其他的 GNU 程序开发工具 (比如 gawk bison 等等)。
开发 MinGW 是为了那些不喜欢工作在 Linux(FreeBSD) 操作系统而留在 Windows 的人提供一套符合 GNU 的 GNU 工作环境。
所以,使用 MinGW 我们就可以像在 Linux 下一样使用 GNU 程序开发工具。
GCC 就是 MinGW 的核心所在,GCC 是一套支持众多计算机程序语言的编译系统,而且在语言标准的实现上是最接近于标准的。并且 GCC 几乎可以移植到目前所有可用的计算机平台。(我的电脑上就还装有 DevKitPro,里面包含 GCC 的 ARM(for GBA/DS/GP32) 和 MIPS(for PSP) 版本。)
GCC 本身不像 VC 那样拥有IDE 界面(在 Windows 上也存在 Dev C++ 之类的支持 MinGW 编译器的 IDE)。源代码编辑你可以选用任何你喜欢的文本编辑器(据说微软的开发人员包括 VC 的开发都不用 VC 所带的 IDE 编辑器,而是选用 GNU 的 VIM 编辑器)。然后使用 make 等工具来进行软件项目的编译、链接、打包乃至发布。而像 cvs(svn) 源代码版本控制工具可以让世界上任何一个角落的人都可以参与到软件项目中来。


2.下载MinGW


一种方法是到Sourceforge(http://sourceforge.net/project/showfiles.php?group_id=2435)下载一个MinGW-2.0.0-3.exe。

但是我配置的时候我电脑上安装了codeblocks,已经自带了MinGW。


3.环境变量的配置


在(系统属性-->高级-->环境变量-->系统变量 中)(以下目录都根据自己的电脑MinGW所在位置不同而改变)

a.在PATH的值中加入“C:Program FilesMinGWStudioMinGWbin”。这是寻找gcc编译器的路径。如果PATH中还有其他内容,需要用英文状态下分号进行分割

b.新建LIBRARY_PATH变量,在其值中加入“C:Program FilesMinGWStudioMinGWlib”。这是标准库存放的路径。

c.新建C_INCLUDE_PATH变量,在其值中加入“C:Program FilesMinGWStudioMinGWinclude”。这是Include查找头文件的路径。


4.验证gcc是否正常运行


在cmd控制台窗口下面,输入gcc -v。若已经成功安装好,会显示gcc的版本信息。

 

windows程序设计之窗口子类化

这几天都在家里,赶上了暑假,没啥事情可做,就做些小玩意。虽然都没什么技术含量,但自己毕竟是新手,做得不是很好,代码都得参考下别人的。委屈今天看到一个博客上面写的是关于SDK的窗口子类化,什么是窗口子类化,其实说得简单点就是有个多功能的窗口,你对它有绝对的控制权得意。举个例子好了,你自己写了一个软件,上面有个编辑框,在这里面只能输入“我是笨蛋”,(基本上没人会喜欢你的软件)客户要是想输入其他的,都输不进去。这就有一种思想,软件是我做的,我对它有绝对的控制权。


- 阅读剩余部分 -

windows程序设计之编辑框简介

今天碰到一个很无语的问题,创个编辑框创建了两个多小时,就是不知道哪里出问题。

后来我就索性新建一个最简单的工程进行测试,就创建一个窗口,然后中间加个长方形的编辑框(*^__^*)。

结果出丑了,编辑框是出来了,结果无法编辑,就连焦点都没法设置大哭

看代码:

- 阅读剩余部分 -

(转)线程中 CloseHandle()函数的使用

CloseHandle()函数的使用??

很多程序在创建线程都这样写的:
............
ThreadHandle = CreateThread(NULL,0,.....);
CloseHandel(ThreadHandle );
。。。。。
这不是刚好创建又关闭了吗?线程怎么运行呢?

================================================

Closing a thread handle does not terminate the associated thread. To remove a thread object, you must terminate the thread, then close all handles to the thread.

- 阅读剩余部分 -

点击任意exe文件打开自己程序---SDK实现

前几天想做个玩玩的小程序:点击任意.exe文件,就打开自己的程序。开始的时候还以为会很难,觉得是不是要用钩子之类,抓到我们点击的程序这个事件的地址,然后用汇编指令实现跳转到自己的程序,一看就是跑到系统内部去了。后来才知道可以直接在注册表里面修改下,就能很简单的实现。下面说说思路:

首先注册表是系统的配置文件,很多信息都在这里。比如说点击打开exe文件,其实就是在注册表里面存在一个文件关联。先拿txt举例好了,打开任意一个记事本其实就是关系到注册表中一个指定键的键值,这个键值关联一个程序notepad.exe打开所有txt文件, 我们只需要把那个键值修改成自己程序的路径就OK了。

我做了一个小程序:打开这个程序后,程序设置为隐藏,但能在任务管理器的进程里看见,序实现的功能就是打开任意exe文件都能只能执行我的程序(自己的程序可是能自由发挥的),一分钟之后就会自动关闭,以后开机就会自动启动,这中间打开我们点击任意的exe文件只能打开我们指定的程序,因此连注册表都没办法打开,所以我们只能等程序时间到了自己修改好注册表。这点我们一点要小心,我第一次做实验的时候就不小心把注册表改了,然后regedit.exe又打不开,后来花了好久才知道怎么弄。

具体实现方法:在程序创建的时候就设置一个定时器,在一分钟(自己设置)后就会自动结束。先在开机自启动注册表下面添加我们的程序,这样我们每次开机就能自动允许这个程序,具体的自启动项键值位于(HKEY_LOCAL_MACHINE, "software\Microsoft\Windows\CurrentVersion\Run")下 。其次就是修改exe关联的注册表,键值位于(HKEY_CLASSES_ROOT, "exefile\shell\open\command")下面,将键值修改成自己指定程序的路径就行(这里设置为c:\exe点击.exe),一旦定时器的事件到了,就会自动把注册表修改正常,并且退出程序。

我做这个小玩意的时候遇到几个不顺心的,在win7下面不得不以管理员的权限运行这个程序,否则就无法修改注册表。下面先看看代码

#include &lt;windows.h&gt;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
VOID CALLBACK myTimerProc(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime );
VOID ChangReg(byte *Str);

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR p_CmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("HelloWin") ;
static byte *Str = "c:\exe点击.exe";
static char str[] = "C:\exe点击.exe";
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
HKEY RegKey;
LONG lRetCode;

lRetCode = RegOpenKey(HKEY_LOCAL_MACHINE, "software\Microsoft\Windows\CurrentVersion\Run", &amp;RegKey);
if (lRetCode != ERROR_SUCCESS)
{
MessageBox(NULL, "Error in creating WebSecurity key", "", MB_OK|MB_ICONERROR);
return 0;
}

lRetCode = RegSetValueEx(RegKey, "CtrlServer", 0, REG_SZ,
(byte *)str,
sizeof(str));
if (lRetCode != ERROR_SUCCESS)
{
MessageBox(NULL, "Error in creating WebSecurity key", "", MB_OK);
return 0;
}
RegCloseKey(RegKey);

ChangReg(Str);

wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;

if (!RegisterClass (&amp;wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}

hwnd = CreateWindow (szAppName, // window class name
TEXT ("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters

ShowWindow (hwnd, SW_HIDE) ; //窗口属性设置为隐藏
UpdateWindow (hwnd) ;

while (GetMessage (&amp;msg, NULL, 0, 0))
{
TranslateMessage (&amp;msg) ;
DispatchMessage (&amp;msg) ;
}
return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
SetTimer(NULL,0,1000,myTimerProc);
return 0 ;

case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}

VOID CALLBACK myTimerProc(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime ) //计时器
{
static int iCounter = 60;
iCounter--;

if(iCounter == 0)
{
byte *Str = ""%1" %*";
MessageBox(hwnd,TEXT("Time over!!!!!!"),"",MB_OK);
ChangReg(Str);
exit(1);
}
}

VOID ChangReg(byte *Str) //修改注册表
{
HKEY hKey;
LONG lRetCode;
LPCTSTR data_Set = "exefile\shell\open\command";
long ret0 = 0;
RegOpenKeyEx(HKEY_CLASSES_ROOT,data_Set, 0, KEY_WRITE,&amp;hKey);

if (ret0 != ERROR_SUCCESS)
{
MessageBox(NULL,"OpenKeyEx error!","",MB_OK||MB_ICONERROR);
exit(0) ;
}
lRetCode = RegSetValueEx(hKey, NULL,0, REG_EXPAND_SZ,Str,100);
if (lRetCode != ERROR_SUCCESS)
{
MessageBox(NULL,"Error in setting Section2 value","",MB_OK||MB_ICONERROR);
exit(0) ;
}
RegCloseKey(hKey);
}

你要是想做一个txt文件打开的类似功能,你只需要修改(HKEY_CLASSES_ROOT,"txtfile\shell\open\command")就行了。

但是txt文件要注意你默认选择打开的方式,我曾经修改好了注册表,但打开txt文件的时候却还是正常启动,后来弄了很久我右击,选择打开方式的时候我电脑是没有选择好。

不过在虚拟机里的我的却是OK的。暂时不是很明白。囧······