@import url(http://www.cppblog.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
PNG2BMP.EXE 可以将png图片转换成bmp图片。
下载地址
示例:PNG2BMP.EXE -X C:\\1.PNG -O 1.bmp -D c:\\
命令参数如下:
png2bmp, a PNG-to-BMP converter - version 1.62 (Sep 4, 2005)
Copyright (C) 1999-2005 MIYASAKA Masaru
Compiled with libpng 1.2.8 and zlib 1.2.3.
Usage: png2bmp.exe [-switches] inputfile(s)
or:
| png2bmp.exe [-switches] |
List of input files may use wildcards (* and ?)
Output filename is same as input filename, but extension .bmp
Switches (case-insensitive) :
-A Preserve alpha channel (save in 32bit ARGB BMP format)
-B Preserve alpha channel (save in 32bit Bitfield BMP format)
-R Convert transparent color to alpha channel (use with -A or -B)
-O name Specify name for output file
-D dir Output files into dir
-E Delete input files after successful conversion
-T Set the timestamp of input file on output file
-Q Quiet mode
-L Log errors to .\P2BERROR.LOG file
-X Disable conversion through standard input/output
posted @
2012-11-19 17:24 王海光 阅读(1758) |
评论 (0) |
编辑 收藏
摘要: 以下实现适用于24位BMP图片: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 //单击一个Button,保存位图 2 void CDlgDlg::OnButton1() ...
阅读全文
posted @
2012-11-19 17:19 王海光 阅读(7028) |
评论 (2) |
编辑 收藏
1.介绍
gSOAP编译工具提供了一个SOAP/XML 关于C/C++ 语言的实现,从而让C/C++语言开发web服务或客户端程序的工作变得轻松了很多。绝大多数的C++web服务工具包提供一组API函数类库来处理特定的SOAP数据结构,这样就使得用户必须改变程序结构来适应相关的类库。与之相反,gSOAP利用编译器技术提供了一组透明化的SOAP API,并将与开发无关的SOAP实现细节相关的内容对用户隐藏起来。gSOAP的编译器能够自动的将用户定义的本地化的C或C++数据类型转变为符合XML语法的数据结构,反之亦然。这样,只用一组简单的API就将用户从SOAP细节实现工作中解脱了出来,可以专注与应用程序逻辑的实现工作了。gSOAP编译器可以集成C/C++和Fortran代码(通过一个Fortran到C的接口),嵌入式系统,其他SOAP程序提供的实时软件的资源和信息;可以跨越多个操作系统,语言环境以及在防火墙后的不同组织。
gSOAP使编写web服务的工作最小化了。gSOAP编译器生成SOAP的代码来序列化或反序列化C/C++的数据结构。gSOAP包含一个WSDL生成器,用它来为你的web服务生成web服务的解释。gSOAP的解释器及导入器可以使用户不需要分析web服务的细节就可以实现一个客户端或服务端程序。下面是gSOAP的一些特点:
l gSOAP编译器可以根据用户定义的C和C++数据结构自动生成符合SOAP的实例化代码。
l gSOAP支持WSDL 1.1, SOAP 1.1, SOAP 1.2, SOAP RPC 编码方式以及 literal/document 方式.
l gSOAP是少数完全支持SOAP1.1 RPC编码功能的工具包,包括多维数组及动态类型。比如,一个包含一个基类参数的远程方法可以接收客户端传来的子类实例。子类实例通过动态绑定技术来保持一致性。
l gSOAP 支持 MIME (SwA) 和 DIME 附件包。
l gSOAP是唯一支持DIME附件传输的工具包。它允许你在保证XML可用性的同时能够以最快的方式(流方式)传递近乎无大小限制的二进制数据。
l gSOAP 支持 SOAP-over-UDP。
l gSOAP 支持 IPv4 and IPv6.
l gSOAP 支持 Zlib deflate and gzip compression (for HTTP, TCP/IP, and XML file storage)。
l gSOAP 支持 SSL (HTTPS)。
l gSOAP 支持 HTTP/1.0, HTTP/1.1 保持连接, 分块传输及基本验证。
l gSOAP 支持 SOAP 单向消息。
l gSOAP 包含一个 WSDL 生成器,便于web服务的发布。
l gSOAP 包含一个WSDL解析器 (将WSDL转换为gSOAP头文件),可以自动化用户客户端及服务端的开发。
l 生成可以单独运行的web服务及客户端程序。
l 因为只需要很少内存空间,所以可以运行在类似Palm OS, Symbian, Pocket PC的小型设备中。
l 适用于以C或C++开发的web服务中。
l 跨平台:Windows, Unix, Linux, Mac OS X, Pocket PC, Palm OS, Symbian等。
l 支持序列化程序中的本地化C/C++数据结构。
l 可以使用输入和输出缓冲区来提高效率,但是不用完全消息缓冲来确定HTTP消息的长度。取而代之的是一个三相序列化方法。这样,像64位编码的图像就可以在小内存设备(如PDA)中以DIME附件或其他方式传输。
l 支持C++单继承,动态绑定,重载,指针结构(列表、树、图、循环图,定长数组,动态数组,枚举,64位2进制编码及16进制编码)。
l 不需要重写现有的C/C++应用。但是,不能用unions,指针和空指针来作为远程方法调用参数的数据结构中元素。
l 三相编组:1)分析指针,引用,循环数据结构;2)确定HTTP消息长度;3)将数据序列化位SOAP1.1编码方式或用户定义的数据编码方式。
l 双相编组:1)SOAP解释及编码;2)分解“forward”指针(例如:分解SOAP中的href属性)。
l 完整可定制的SOAP错误处理机制。
l 可定制的SOAP消息头处理机制,可以用来保持状态信息
gsoap通常带有两个工具: wsdl2h 和 soapcpp2。 wsdl2h主要是用来生成头文件的,而soapcpp2主要是利用wsdl2h生成的头文件来生成C文件或C++文件。 以下是README.txt中示例:
Example translation of WSDL to code in two steps:
$ wsdl2h -s -o calc.h http://www.cs.fsu.edu/~engelen/calc.wsdl
posted @
2012-11-16 17:14 王海光 阅读(2943) |
评论 (0) |
编辑 收藏
微软在XP下提供了一个软件程序,SYSTEM32\msiexec.exe
在命令行下输入:msiexec /? 显示帮助信息,如下:
Windows (R) Installer. V 5.0.7600.16385
msiexec /Option <Required Parameter> [Optional Parameter]
安装选项
</package | /i> <Product.msi>
安装或配置产品
/a <Product.msi>
管理安装 - 在网络上安装产品
/j<u|m> <Product.msi> [/t <Transform List>] [/g <Language ID>]
公布产品 - m 公布到所有用户,u 公布到当前用户
</uninstall | /x> <Product.msi | ProductCode>
卸载产品
显示选项
/quiet
安静模式,无用户交互
/passive
无人参与模式 - 只显示进度栏
/q[n|b|r|f]
设置用户界面级别
n - 无用户界面
b - 基本界面
r - 精简界面
f - 完整界面(默认值)
/help
帮助信息
重新启动选项
/norestart
安装完成后不重新启动
/promptrestart
必要时提示用户重新启动
/forcerestart
安装后始终重新启动计算机
日志选项
/l[i|w|e|a|r|u|c|m|o|p|v|x|+|!|*] <LogFile>
i - 状态消息
w - 非致命警告
e - 所有错误消息
a - 操作的启动
r - 操作特定记录
u - 用户请求
c - 初始用户界面参数
m - 内存不足或致命退出信息
o - 磁盘空间不足消息
p - 终端属性
v - 详细输出
x - 额外调试信息
+ - 扩展到现有日志文件
! - 每一行刷新到日志
* - 记录所有信息,除了 v 和 x 选项
/log <LogFile>
与 /l* <LogFile> 相同
更新选项
/update <Update1.msp>[;Update2.msp]
应用更新
/uninstall <PatchCodeGuid>[;Update2.msp] /package <Product.msi | ProductCode>
删除产品的更新
修复选项
/f[p|e|c|m|s|o|d|a|u|v] <Product.msi | ProductCode>
修复产品
p - 仅当文件丢失时
o - 如果文件丢失或安装了更旧的版本(默认值)
e - 如果文件丢失或安装了相同或更旧的版本
d - 如果文件丢失或安装了不同版本
c - 如果文件丢失或较验和与计算的值不匹配
a - 强制重新安装所有文件
u - 所有必要的用户特定注册表项(默认值)
m - 所有必要的计算机特定注册表项(默认值)
s - 所有现有的快捷键方式(默认值)
v - 从源运行并重新缓存本地安装包
设置公共属性
[PROPERTY=PropertyValue]
请查阅 Windows (R) Installer SDK 获得有关命令行语法的其他文档。
版权所有 (C) Microsoft Corporation. 保留所有权利。
此软件的部分内容系基于 Independent JPEG Group 的工作。
运行CMD后,输入msiexec /i Product.msi /q 即可安装msi程序。
运行CMD后,输入msiexec /x
ProductCode /q 即可卸载msi安装过的程序。
ProductCode的值可在注册表中查找,注册表位置:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall 根据DisplayName键值来确定安装的msi程序,UninstallString的值中包含ProductCode 如:{BC12972C-BF15-4607-A745-948DA0DE63FF}
卸载命令可以写成如下所示:msiexec /x "{BC12972C-BF15-4607-A745-948DA0DE63FF}" /q
posted @
2012-11-15 11:21 王海光 阅读(2686) |
评论 (0) |
编辑 收藏
基数排序(Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。基数排序的发明可以追溯到1887年赫尔曼·何乐礼在打孔卡片制表机(Tabulation Machine)上的贡献。
它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
基数排序时对每一维进行调用子排序算法时要求这个子排序算法必须是稳定的。
基数排序与直觉相反:它是按照从底位到高位的顺序排序的。
伪代码:
RADIX-SORT(A,d)
1 for i <-- 1 to d
2 do use a stable sort to sort array A on digit i
引理8.3: 给定n个d位数,每一个数位可以取k种可能的值,如果所用稳定排序需要Θ(n+k)时间,基数排序能以Θ(d(n+k))的时间完成。
证明:如果采用计数排序这种稳定排序,那么每一遍处理需要时间Θ(n+k),一共需处理d遍,于是基数排序的运行时间为Θ(d(n+k))。当d为常数,k=O(n)时,有线性运行时间。
注:将关键字分成若干位方面,可以有一定的灵活性。
引理8.4:给定n个b位数和任何正整数r<=b,如果采用的稳定排序需要Θ(d(n+k))时间,则RADIX-SORT能在Θ((b/r)(n+2r))时间内正确地对这些数进行排序。
证明:对于一个r<=b,将每个关键字看成由d=b/r位组成的数,每一个数字都是(0~ -1)之间的一个整数,这样就可以采取计数排序。K= ,d=b/r,总的运行时间为Θ((b/r)(n+ ))。
对于给定的n值和b值,如何选择r值使得最小化表达式(b/r)(n+2r)。如果b< lgn,对于任何r<=b的值,都有(n+2r)=Θ(n),于是选择r=b,使计数排序的时间为Θ((b/b)(n+2b)) = Θ(n)。 如果b>lgn,则选择r=lgn,可以给出在某一常数因子内的最佳时间:当r=lgn时,算法复杂度为Θ(bn/lgn),当r增大到lgn以上时,分子 增大比分母r快,于是运行时间复杂度为Ω(bn/lgn);反之当r减小到lgn以下的时候,b/r增大,而n+ 仍然是Θ(n)。
以下引自麻省理工学院算法导论——笔记:
![](/images/cppblog_com/wanghaiguang/RadixSort1.jpg)
![](/images/cppblog_com/wanghaiguang/RadixSort2.jpg)
![](/images/cppblog_com/wanghaiguang/RadixSort3.jpg)
![](/images/cppblog_com/wanghaiguang/RadixSort4.jpg)
![](/images/cppblog_com/wanghaiguang/RadixSort5.jpg)
![](/images/cppblog_com/wanghaiguang/RadixSort6.jpg)
![](/images/cppblog_com/wanghaiguang/RadixSort7.jpg)
![](/images/cppblog_com/wanghaiguang/RadixSort8.jpg)
以下代码实例转自:http://www.docin.com/p-449224125.html
1 以下是用C++实现的基数排序代码:
2 #include <cstdio>
3 #include <cstdlib>
4 // 这个是基数排序用到的计数排序,是稳定的。
5 // pDigit是基数数组,nMax是基数的上限,pData是待排序的数组, nLen是待排序数组的元素个数
6 // 必须pDigit和pData的下标相对应的,即pDigit[1]对应pData[1]
7 int RadixCountingSort(int *pDigit, int nMax,int *pData,int nLen){
8 // 以下是计数排序
9 int *pCount = new int[nMax];
10 int *pSorted = new int[nLen];
11 int i,j;
12 for(i=0; i<nMax; ++i)
13 pCount[i] = 0;
14 for(i=0; i<nLen; ++i)
15 ++pCount[pDigit[i]];
16 for(i=1; i<nMax; ++i)
17 pCount[i] += pCount[i-1];
18 for(i=nLen-1; i>=0; --i){
19 --pCount[pDigit[i]];
20 pSorted[pCount[pDigit[i]]] = pData[i]; // z这里注意,是把待排序的数组赋值
21 }
22 for(i=0; i<nLen; ++i)
23 pData[i] = pSorted[i];
24 delete [] pCount;
25 delete [] pSorted;
26 return 1;
27 }
28 int RadixSort(int *pData, int nLen){
29 int *pDigit = new int[nLen]; // 申请存放基数(某个位数)的空间
30 int nRadixBase = 1;
31 bool flag = false;
32 while(!flag){
33 flag = true;
34 nRadixBase *= 10;
35 for(int i=0; i<nLen; ++i){
36 pDigit[i] = pData[i]%nRadixBase; // 求出某位上的数当做基数
37 pDigit[i] /= nRadixBase/10;
38 if(pDigit[i] > 0)
39 flag = false;
40 }
41 if(flag)
42 break;
43 RadixCountingSort(pDigit,10,pData,nLen);
44 }
45 delete[] pDigit;
46 return 1;
47 }
48 main()
49 {
50 int nData[10]={43,65,34,5,8,34,23,0,45,34};;
51 RadixSort(nData, 10);
52 printf("经排序后的数列是:\n");
53 for (int i = 0; i < 10; ++i)
54 printf("%d ", nData[i]);
55 printf("\n");
56 return 0;
57 }
posted @
2012-11-13 16:52 王海光 阅读(660) |
评论 (0) |
编辑 收藏
算法介绍
计数排序是一个类似于桶排序的排序算法,其优势是对已知数量范围的数组进行排序。它创建一个长度为这个数据范围的数组C,C中每个元素记录要排序数组中对应记录的出现个数。这个算法于1954年由 Harold H. Seward 提出。
当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。
由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序中的算法来排序数据范围很大的数组。计数排序之所以能够突破前面所述的Ω(nlgn)极限,是因为它不是基于元素比较的。计数排序适合所需排序的数组元素取值范围不大的情况(范围太大的话辅助空间很大)。
定理:任意一个比较排序算法在最坏情况下,都需要做Ω(nlgn)次比较。
算法的步骤如下:
- 找出待排序的数组中最大和最小的元素
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
以下引自麻省理工学院算法导论——笔记:
Counting sort: No comparisons between elements.
• Input: A[1 . . n], where A[ j]{1, 2, …, k} .
• Output: B[1 . . n], sorted.
• Auxiliary storage: C[1 . . k] .
Counting sort
for i ← 1 to k
do C[i] ← 0
for j ←1 to n
do C[A[ j]] ← C[A[ j]] + 1 —> C[i] = |{key = i}|
for i ← 2 to k
do C[i] ← C[i] + C[i–1] —>C[i] = |{key ← i}|
for j ← n downto 1
do B[C[A[ j]]] ← A[ j]
C[A[ j]] ← C[A[ j]] – 1
实例:
![](/images/cppblog_com/wanghaiguang/CountingSort1.jpg)
对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
![](/images/cppblog_com/wanghaiguang/CountingSort2.jpg)
反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
![](/images/cppblog_com/wanghaiguang/CountingSort3.jpg)
![](/images/cppblog_com/wanghaiguang/CountingSort4.jpg)
![](/images/cppblog_com/wanghaiguang/CountingSort5.jpg)
注:基于比较的排序算法的最佳平均时间复杂度为 O(nlogn)
稳定性:算法是稳定的。
如果k小于nlogn可以用计数排序,如果k大于nlogn可以用归并排序。
代码实例:
1 C++实现:
2 #include<cstdio>
3 #include<algorithm>
4 using namespace std;
5 //n为数组元素个数,k是最大的那个元素
6 void CountingSort(int *input, int size, int k){
7 int i;
8 int *result = new int[size]; //开辟一个保存结果的临时数组
9 int *count = new int[k+1]; //开辟一个临时数组
10 for(i=0; i<=k; ++i)
11 count[i]=0;
12 //使count[i]等于等于i的元素的个数
13 for(i=0; i<size; ++i)
14 ++count[input[i]]; //count数组中坐标为元素input[i]的增加1,即该元素出现的次数加1
15 for(i=1; i<=k; ++i)
16 count[i] += count[i-1];
17 for(i=size-1; i>=0; --i){ //正序来也行,但是到这来可以使排序是稳定的
18 --count[input[i]]; //因为数组下标从0开始,所以这个放在前面
19 result[count[input[i]]] = input[i]; //这个比较绕, count[input[i]-1] 就代表小于等于元素
}
20 copy(result,result+size,input); //调用copy函数把结果存回原数组
21 delete [] result; //记得释放空间
22 delete [] count;
23 }
24 int main()
25 {
26 int input[11]={2,7,4,9,8,5,7,8,2,0,7};
27 CountingSort(input,11,9);
28 for(int i=0; i<11; ++i)
29 printf("%d ",input[i]);
30 putchar('\n');
31 return 0;
32 }
posted @
2012-11-13 11:07 王海光 阅读(677) |
评论 (1) |
编辑 收藏
摘要: 1 #============================================================================= 2 本文转自: http://blog.csdn.net/tge7618291
下载地址1:http:/...
阅读全文
posted @
2012-11-09 13:59 王海光 阅读(940) |
评论 (0) |
编辑 收藏
摘要: 为了编译一个简单的源文件main.c,需要自动生成一个makefile,以下是步骤:第一步:----------在/root/project/main目录下创建一个文件main.c,其内容如下:------------------------------------------------#include <stdio.h> int main(int argc, char** argv...
阅读全文
posted @
2012-11-08 15:37 王海光 阅读(1471) |
评论 (0) |
编辑 收藏
目的:
基本掌握了 make 的用法,能在Linux系统上编程。
环境:
Linux系统,或者有一台Linux服务器,通过终端连接。一句话:有Linux编译环境。
准备:
准备三个文件:file1.c, file2.c, file2.h
file1.c:
#include <stdio.h>
#include "file2.h"
int main()
{
printf("print file1$$$$$$$$$$$$$$$$$$$$$$$$\n");
File2Print();
return 0;
}
file2.h:
#ifndef FILE2_H_
#define FILE2_H_
#ifdef __cplusplus
extern "C" {
#endif
void File2Print();
#ifdef __cplusplus
}
#endif
#endif
file2.c:
#include "file2.h"
void File2Print()
{
printf("Print file2**********************\n");
}
基础:
先来个例子:
有这么个Makefile文件。(文件和Makefile在同一目录)
=== makefile 开始 ===
helloworld:file1.o file2.o
gcc file1.o file2.o -o helloworld
file1.o:file1.c file2.h
gcc -c file1.c -o file1.o
file2.o:file2.c file2.h
gcc -c file2.c -o file2.o
clean:
rm -rf *.o helloworld
=== makefile 结束 ===
一个 makefile 主要含有一系列的规则,如下:
A: B
(tab)<command>
(tab)<command>
每个命令行前都必须有tab符号。
上面的makefile文件目的就是要编译一个helloworld的可执行文件。让我们一句一句来解释:
helloworld : file1.o file2.o: helloworld依赖file1.o file2.o两个目标文件。
gcc File1.o File2.o -o helloworld: 编译出helloworld可执行文件。-o表示你指定 的目标文件名。
file1.o : file1.c: file1.o依赖file1.c文件。
gcc -c file1.c -o file1.o: 编译出file1.o文件。-c表示gcc 只把给它的文件编译成目标文件, 用源码文件的文件名命名但把其后缀由“.c”或“.cc”变成“.o”。在这句中,可以省略-o file1.o,编译器默认生成file1.o文件,这就是-c的作用。
file2.o : file2.c file2.h
gcc -c file2.c -o file2.o
这两句和上两句相同。
clean:
rm -rf *.o helloworld
当用户键入make clean命令时,会删除*.o 和helloworld文件。
如果要编译cpp文件,只要把gcc改成g++就行了。
写好Makefile文件,在命令行中直接键入make命令,就会执行Makefile中的内容了。
到这步我想你能编一个Helloworld程序了。
上一层楼:使用变量
上面提到一句,如果要编译cpp文件,只要把gcc改成g++就行了。但如果Makefile中有很多gcc,那不就很麻烦了。
第二个例子:
=== makefile 开始 ===
OBJS = file1.o file2.o
CC = gcc
CFLAGS = -Wall -O -g
helloworld : $(OBJS)
$(CC) $(OBJS) -o helloworld
file1.o : file1.c file2.h
$(CC) $(CFLAGS) -c file1.c -o file1.o
file2.o : file2.c file2.h
$(CC) $(CFLAGS) -c file2.c -o file2.o
clean:
rm -rf *.o helloworld
=== makefile 结束 ===
这里我们应用到了变量。要设定一个变量,你只要在一行的开始写下这个变量的名字,后 面跟一个 = 号,后面跟你要设定的这个变量的值。以后你要引用 这个变量,写一个 $ 符号,后面是围在括号里的变量名。
CFLAGS = -Wall -O –g,解释一下。这是配置编译器设置,并把它赋值给CFFLAGS变量。
-Wall: 输出所有的警告信息。
-O: 在编译时进行优化。
-g: 表示编译debug版本。
这样写的Makefile文件比较简单,但很容易就会发现缺点,那就是要列出所有的c文件。如果你添加一个c文件,那就需要修改Makefile文件,这在项目开发中还是比较麻烦的。
再上一层楼:使用函数
学到这里,你也许会说,这就好像编程序吗?有变量,也有函数。其实这就是编程序,只不过用的语言不同而已。
第三个例子:
=== makefile 开始 ===
CC = gcc
XX = g++
CFLAGS = -Wall -O –g
TARGET = ./helloworld
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
%.o:%.cpp
$(XX) $(CFLAGS) -c $< -o $@
SOURCES = $(wildcard *.c *.cpp)
OBJS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))
$(TARGET) : $(OBJS)
$(XX) $(OBJS) -o $(TARGET)
chmod a+x $(TARGET)
clean:
rm -rf *.o helloworld
=== makefile 结束 ===
函数1:wildcard
产生一个所有以 '.c' 结尾的文件的列表。
SOURCES = $(wildcard *.c *.cpp)表示产生一个所有以 .c,.cpp结尾的文件的列表,然后存入变量 SOURCES 里。
函数2:patsubst
匹配替换,有三个参数。第一个是一个需要匹配的式样,第二个表示用什么来替换它,第三个是一个需要被处理的由空格分隔的列表。
OBJS = $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCES)))表示把文件列表中所有的.c,.cpp字符变成.o,形成一个新的文件列表,然后存入OBJS变量中。
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
%.o:%.cpp
$(XX) $(CFLAGS) -c $< -o $@
这几句命令表示把所有的.c,.cpp编译成.o文件。
这里有三个比较有用的内部变量。$@ 扩展成当前规则的目的文件名, $< 扩展成依靠 列表中的第一个依靠文件,而 $^ 扩展成整个依靠的列表(除掉了里面所有重 复的文件名)。
chmod a+x $(TARGET)表示把helloworld强制变成可执行文件。
到这里,我想你已经能够编写一个比较简单也比较通用的Makefile文件了,上面所有的例子都假定所有的文件都在同一个目录下,不包括子目录。
那么文件不在一个目录可以吗?
怎么编写Makefile生成静态库?
你还想更上一层楼吗?
请听下回分解。
本文转自:http://goodcandle.cnblogs.com/archive/2006/03/30/278702.html
posted @
2012-11-08 13:59 王海光 阅读(442) |
评论 (0) |
编辑 收藏
一、基本概念:
钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。二、运行机制:
1、钩子链表和钩子子程:每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针指向指定的,应用程序定义的,被Hook子程调用的回调函数,也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。钩子子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。钩子子程必须按照以下的语法:
1 LRESULT CALLBACK HookProc
2 (
3 int nCode,
4 WPARAM wParam,
5 LPARAM lParam
6 );
HookProc是应用程序定义的名字。nCode参数是Hook代码,Hook子程使用这个参数来确定任务。这个参数的值依赖于Hook类型,每一种Hook都有自己的Hook代码特征字符集。wParam和lParam参数的值依赖于Hook代码,但是它们的典型值是包含了关于发送或者接收消息的信息。2、钩子的安装与释放:使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时,系统就调用与这个Hook关联的Hook链的开头的Hook子程。每一个Hook链中的Hook子程都决定是否把这个事件传递到下一个Hook子程。Hook子程传递事件到下一个Hook子程需要调用CallNextHookEx函数。
1 HHOOK SetWindowsHookEx(
2 int idHook, // 钩子的类型,即它处理的消息类型
3 HOOKPROC lpfn, // 钩子子程的地址指针。如果dwThreadId参数为0
4 // 或是一个由别的进程创建的线程的标识,
5 // lpfn必须指向DLL中的钩子子程。
6 // 除此以外,lpfn可以指向当前进程的一段钩子子程代码。
7 // 钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。
8 HINSTANCE hMod, // 应用程序实例的句柄。标识包含lpfn所指的子程的DLL。
10 // 如果dwThreadId 标识当前进程创建的一个线程,
11 // 而且子程代码位于当前进程,hMod必须为NULL。
12 // 可以很简单的设定其为本应用程序的实例句柄。
13 DWORD dwThreadId // 与安装的钩子子程相关联的线程的标识符。
14 // 如果为0,钩子子程与所有的线程关联,即为全局钩子。
15 );
函数成功则返回钩子子程的句柄,失败返回NULL。以上所说的钩子子程与线程相关联是指在一钩子链表中发给该线程的消息同时发送给钩子子程,且被钩子子程先处理。在钩子子程中调用得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外一个SDK中的API函数CallNextHookEx来传递它,以执行钩子链表所指的下一个钩子子程。这个函数成功时返回钩子链中下一个钩子过程的返回值,返回值的类型依赖于钩子的类型。这个函数的原型如下:
1 LRESULT CallNextHookEx
2 (
3 HHOOK hhk;
4 int nCode;
5 WPARAM wParam;
6 LPARAM lParam;
7 );
hhk为当前钩子的句柄,由SetWindowsHookEx()函数返回。NCode为传给钩子过程的事件代码。wParam和lParam 分别是传给钩子子程的wParam值,其具体含义与钩子类型有关。 钩子函数也可以通过直接返回TRUE来丢弃该消息,并阻止该消息的传递。否则的话,其他安装了钩子的应用程序将不会接收到钩子的通知而且还有可能产生不正确的结果。钩子在使用完之后需要用UnHookWindowsHookEx()卸载,否则会造成麻烦。释放钩子比较简单,UnHookWindowsHookEx()只有一个参数。函数原型如下:
1 UnHookWindowsHookEx
2 (
3 HHOOK hhk;
4 );
函数成功返回TRUE,否则返回FALSE。3、一些运行机制:在Win16环境中,DLL的全局数据对每个载入它的进程来说都是相同的;而在Win32环境中,情况却发生了变化,DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入DLL时,操作系统自动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。#pragma data_seg预处理指令用于设置共享数据段。例如:
1 #pragma data_seg("SharedDataName")
2 HHOOK hHook=NULL;
3 #pragma data_seg()
在#pragma data_seg("SharedDataName")和#pragma data_seg()之间的所有变量 将被访问该Dll的所有进程看到和共享。当进程隐式或显式调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里(以下简称"地址空间")。这使得DLL成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。4、系统钩子与线程钩子:SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。 线程勾子用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内。 系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。几点说明:(1)如果对于同一事件(如鼠标消息)既安装了线程勾子又安装了系统勾子,那么系统会自动先调用线程勾子,然后调用系统勾子。 (2)对同一事件消息可安装多个勾子处理过程,这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。 (3)勾子特别是系统勾子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾子,在使用完毕后要及时卸载。三、钩子类型
每一种类型的Hook可以使应用程序能够监视不同类型的系统消息处理机制。下面描述所有可以利用的Hook类型。1、WH_CALLWNDPROC和WH_CALLWNDPROCRET HooksWH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以监视发送到窗口过程的消息。系统在消息发送到接收窗口过程之前WH_CALLWNDPROCHook子程,并且在窗口过程处理完消息之后调用WH_CALLWNDPROCRET Hook子程。WH_CALLWNDPROCRET Hook传递指针到CWPRETSTRUCT结构,再传递到Hook子程。CWPRETSTRUCT结构包含了来自处理消息的窗口过程的返回值,同样也包括了与这个消息关联的消息参数。2、WH_CBT Hook在以下事件之前,系统都会调用WH_CBT Hook子程,这些事件包括:1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件;2. 完成系统指令;3. 来自系统消息队列中的移动鼠标,键盘事件;4. 设置输入焦点事件;5. 同步系统消息队列事件。 Hook子程的返回值确定系统是否允许或者防止这些操作中的一个。3、WH_DEBUG Hook在系统调用系统中与其他Hook关联的Hook子程之前,系统会调用WH_DEBUG Hook子程。你可以使用这个Hook来决定是否允许系统调用与其他Hook关联的Hook子程。4、WH_FOREGROUNDIDLE Hook当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就会调用WH_FOREGROUNDIDLE Hook子程。5、WH_GETMESSAGE Hook应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及其他发送到消息队列中的消息。6、WH_JOURNALPLAYBACK HookWH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它們不會被注射到任何行程位址空間。7、WH_JOURNALRECORD HookWH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样使用。WH_JOURNALRECORD是system-wide local hooks,它們不會被注射到任何行程位址空間。8、WH_KEYBOARD Hook在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使用这个Hook来监视输入到消息队列中的键盘消息。9、WH_KEYBOARD_LL HookWH_KEYBOARD_LL Hook监视输入到线程消息队列中的键盘消息。10、WH_MOUSE HookWH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。使用这个Hook监视输入到消息队列中的鼠标消息。11、WH_MOUSE_LL HookWH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。12、WH_MSGFILTER 和 WH_SYSMSGFILTER HooksWH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通过安装了Hook子程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook监视所有应用程序消息。 WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间过滤消息,这等价于在主消息循环中过滤消息。 通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循环里一样。13、WH_SHELL Hook外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子程。WH_SHELL 共有5钟情況:1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁;2. 当Taskbar需要重画某个按钮;3. 当系统需要显示关于Taskbar的一个程序的最小化形式;4. 当目前的键盘布局状态改变;5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。 按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自己。以上转自:http://www.microsoft.com/china/community/program/originalarticles/techdoc/hook.mspx以下转自:http://topic.csdn.net/t/20030513/03/1774836.htmlCallNextHookEx 作用Hook 串鏈(Hook Chains) 當許多程式都安裝了某種型態的hook 時,就會形成一個filter-function chain。一旦特定 的event 發生,Windows 會呼叫該型態中最新掛上的hook filter function。舉個例,如果 程式A 掛上了一個system-wide WH_KEYBOARD hook,每當有任何執行緒取得鍵盤訊 息,Windows 就會呼叫這個filter function。如果程式B 也掛上了一個system-wide WH_KEYBOARD hook,那麼當event 發生,Windows 不再呼叫程式A 的filter function, 改呼叫程式B的filter function。這也意味每一個filter function 有責任確保先前掛上的filter Windows 95 程式設計指南(Windows 95 : A Developer’s Guide) 394 function 被呼叫(也就是維護串鏈的完整性)。 SetWindowsHookEx 函式會將新掛上的hook filter function 的代碼傳回。任何程式只要掛 上一個新的filter function 就必須儲存這個代碼(通常存放在全域變數中): static HHOOK g_hhook = NULL; . . . g_hhook = SetWindowsHookEx(WH_KEYBOARD, Example_kybdHook, hinst, NULL); . . . 如果有錯誤發生,SetWindowsHookEx 函式會傳回NULL。 如果你希望hook chain 中的其它filter functions 也能夠執行,你可以在你的filter function 中呼叫CallNextHookEx 函式(或許你已經在先前的Example_KybdHook 函式片段中注意 到了) LRESULT CallNextHookEx(HHOOK hhook, int nCode, WPARAM wParam, LPARAM lParam); 這個函式會呼叫filter-function chain 的下一個filter function,並傳入相同的nCode、 wParam 和lParam。下一個filter function 結束之前,應該也遵循這個規則去呼叫 CallNextHookEx 函式,並再次將hook 代碼(通常那是被放在全域變數中)傳入。 CallNextHookEx 函式利用這個hook 代碼,走訪整個串鏈,決定哪一個filter function 是 下一個呼叫目標。如果CallNextHookEx 函式發現已經沒有下一個filter function 可以呼叫 (走到串鏈盡頭了),它會傳回0;否則它就傳回「下一個filter function 執行後的傳回值」。 你可能會在許多文件(包括SDK 文件)中發現一個有關CallNextHookEx 函式的過氣警告:「如 果nCode 小於0,則hook 函式應該不做任何處理,直接將它交給CallNextHookEx 函式,並傳回 CallNextHookEx 函式的回返值」。這並不是真的,而且自Windows 3.0 以來(那時還在使用舊版的 SetWindowsHook 函式)就已經不是真的了!撰寫程式時,你可以完全不理會這項警告。 第6章訊息攔截(Hooks) 395 有些時候你可能不希望呼叫其他的filter functions,這種情況下你只要不在你的filter function 中呼叫CallNextHookEx 函式即可。只要不將CallNextHookEx 函式放到你的filter function 中,你就不會呼叫其他的filter functions,而你也因此可以指定你自己的傳回值。 不幸的是,這裡埋伏著一個陷阱:另一個執行緒可能也為你安裝了一個hook,新的filter function 於是比你的filter function 更早被喚起,而它可能不呼叫你的filter function,完蛋 了!這個問題沒有一般性的解決方案,如果你先將自己的hook 卸除,然後再重新掛上, 那麼你的filter function 就成為最新的一個,會最先被呼叫。沒錯,但你不能夠保證其他 人不會依樣畫葫蘆。簡言之,hooks 是一個合作機制,沒有任何保障。
posted @
2012-11-05 11:27 王海光 阅读(535) |
评论 (0) |
编辑 收藏