以前用的是.net平台下的C#语言,做过几年网站,也做过winform项目,觉得虽然自己水平相对可以,可是对windows平台的系统级知识了解的却非常少,总感觉浮在水面.用C#写的程序绝对是托管的,.net平台下唯一不受托管的只有vc++,vc++开发编译的程序都是本地代码.说到这里,想起一个朋友用C#给一家公司做了一个winform软件,项目交付时,这位朋友给人家演示时,先准备在人家机器上装了一个.netframework,结果被人家阻止了,客户说道:”我们只要双击软件就要运行,要像绿色版一样,不要给我们装任何其他东西,我们公司不允许这样.”后来,这位朋友很无语.只能用其他开发语言重新开发.
C#开发的东西并不是不好,只是依赖性太强了,没有.netfromwork,那他就死翘翘了.没有CLR,没有JIT,.net平台下的托管程序都是跑步起来的.我们先看这些托管代码是如何运行的.再次之前,我们必须搞清楚windows下可执行文件的格式.在linux下,是elf格式.在windows下,可执行文件是pe格式的,PE格式是有固定格式的,不然系统的加载器是不能识别的,就没有办法加载到内存中,那便自然没有办法执行.
虽然CSC(C#编译器)和 CL(VC编译器)编译的可执行程序都是exe后缀的.但是他们大有不同,CL编译的可执行程序没有CLR头信息,而CSC编译的程序有CLR头,并且CLR头后边的编译好的二进制代码数据和本地程序的二进制数据是不一样的,当双击exe文件时,加载器会去先读取exe文件的头,通过头来判断是本地程序还是托管程序,有CLR头,自然是托管的,这个时候CLR和JIT就会配合工作,将代码加载到内存中,然后JIT将代码即时编译成本地代码,然后缓存下来,为了下次执行效率更高(调用到的模块方法会即时编译),如果没有CLR和JIT,那自然不能执行,二本地代码则直接可执行.不需要这些外部依赖.
申明:虽然C#代码执行要CLR和JIT,但是他不是解释执行的,初次有CLR解释,和JIT编译,第二次执行那么他理论上和本地代码效率一样高的.
以上是个人的理解,也不是详细到位,望大家见谅.也正是由于以上原因,我本人并不是很喜欢使用托管代码.我更喜欢本地代码.好了,不说了,直接win32 API编程开始吧,直接先上一段代码.(我使用的VC++ 6.0,编译器是CL)
VC编译工具 CL编译器 LINK连接器 RC资源编译
下边我的第一win32程序
#include <windows.h>
//开发window程序要备好MSDN,由于API庞大复杂,所以MSDN手册不可缺少.
//玩游戏就要遵守游戏规则,windows程序入口函数就是这样设计的,我们必须遵守,没有什么可以解释的
int WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR pszCmd,int nShowCmd)
{
//我们调用第一个系统API函数,开发第一最简单win32程序,
MessageBox(NULL,"我的第一个win32程序","提示!",MB_OK);
return 0;
}
// 编译 CL /C first.c /C 表示只编译不链接
// 链接 LINK first.obj user32.lib 链接编译的 first.obj 和系统的user32.lib 生成 first.exe
以上程序用记事本便可以完成,使用命令行编译时需要配置环境变量,负责 CL ,LINK这些命令不能执行.下边是一些编译和连接工具的具体使用方法
2.编译器与连接器
编译连接过程如下:
2.1.编译器工具cl.exe
cl.exe的选项参数主要分成如下几类:
a. -OPTIMIZATION- 优化选项
b. -CODE GENERATION-代码生成选项
c. -OUTPUT FILES-输出文件选项
d. -PREPROCESSOR-预处理选项
e. -LANGUAGE-语言选项
f. -MISCELLANEOUS-杂项选项
g. -LINKING-连接相关选项
2.1.1.-OPTIMIZATION-优化选项
/O1 优化空间
/O2 优化速度
/Og 全局优化
/Od 关闭优化(缺省编译选项)
2.1.2.-LANGUAGE-语言选项
/Zi 产生调试信息
/ZI 产生可以Edit和Continue的调试信息
/Ze 使用扩展C/C++语言,就是可以支持MS自己提供的语法(缺省选项)
/Za 使用标准ANSI C/C++语言
2.1.3.-PREPROCESSOR-预处理选项
/P 预处理到文件
/E 预处理到标准输出stdout,就是直接输出到控制台
/u 删除预处理宏
/D 定义一个宏
/I 指定头文件的搜索路径
/C 预编译的时候不要去除注视
2.1.4.-MISCELLANEOUS-杂项
/c 只编译不连接
/TC 把原代码做为C编译
/TP 把原代码做为CPP编译(MFC必须这个选项)
/w 关闭警告
/W<n> 开启并设置警告级别
/Wx 把警告做为错误处理
/Yc 产生.pch文件(预处理头)
/Yu 使用.pch文件(某些文件就不再不预处理,可以加快编译速度)
/FD 保证编译使用最可靠的依赖信息
2.1.5.-CODE GENERATION-代码产生选项
/GA 优化Window应用
/GD 优化DLL库
/Gd __cdecl调用惯例
/Gr __fastcall调用惯例
/Gz __stdcall调用惯例
/GR 运行时类型检查,这通过在预编译时候添加_CPPRTTI宏来实现
关闭使用/GR-(缺省编译选项)
/GX 等价于/EHsc,MFC必须这个选项
/EHs C++同步异常处理,对C++有效,只要使用catch都要使用该选项
/EHa C++异步异常处理
/EHc 该选项必须要求/EHs或者/EHa、/GX,编译器假定extern C函数
不throw异常
/Gi 增量编译,使用/Gi-关闭。
增量编译就是不生成新的编译文件,只是在原来基础上修改增加
新改变的函数。
/Gm 最小代码重构(rebuild)
2.1.6.-OUTPUT FILES-输出文件选项
/Fo 指定输出的目标文件*.obj
/Fe 指定输出的执行文件*.exe
/Fp 指定输出的与处理头文件*.pch
/Fa 指定输出的汇编文件
2.1.7.-LINKING-连接选项
/MD 使用MSVCRT.LIB编译多线程DLL库
/ML 使用LIBC.LIB编译单线程执行文件
/MT 使用LIBCMT.LIB编译单线程执行文件(MFC必须这个选项)
/MDd 使用MSVCRTD.LIB编译多线程DLL可调试库
/MLd 使用LIBCD.LIB编译单线程可调试执行文件
/MTd 使用LIBCMTD.LIB编译单线程可调试执行文件