运行环境

  如果要写一个生成100个随机序列号的小程序,你会使用哪类语言?

  相比传统语言要先创建一个工程项目,我们直接在桌面新建个文本文件可以写脚本了。

  虽然用文本编辑器写代码没任何优势,但对于简单的程序足矣。之后程序交给其他人使用时,脚本优势淋漓尽致的体现出来了:当他们自己想简单修改一些逻辑规则时,只需用记事本打开可以,而记事本每台电脑上都有。

  相反,传统语言写的程序,即使有源代码,用户想简单的修改下也无法生效,还需安装并配置好相应的开发环境才行,这对不熟悉的人来说颇费周折。

  所以脚本必须足够简单 —— 简单到用户只管修改和运行可以,其他步骤都交给脚本宿主自动完成。

  如果想用C++写脚本,那么代码的编译和链接当然必须是全自动的,这并不复杂。

  但仅仅依靠CL.exe等几个命令还是不够的,因为在其他的电脑上并没有相应的开发环境 —— Include和Lib文件夹,因此无法通过编译和链接了。

  而这些头文件库文件,一共多达上千个,全都带上则有近百兆!显然,我们的脚本只用到几个基本功能可以了,那些复杂的windows头文件没必要了。

  事实上,程序的头文件只是函数和结构的定义,仅仅用来给编译器分析而已,终并不生成实际的指令。所以,我们把常用的头文件,事先生成一个.pch预编译头文件可以。以后编译时,将他对应到某个头文件可以了,例如stdafx.h。这样无需使用任何头文件了。即使stdafx.h也不在,编译仍然能通过,因为这一切都打包在.pch里面了。并且大量的头文件经过事先的分析,编译时无需再编译它们了,速度大幅提升。

  至于Lib文件,里面都是库函数的内容。除非整个程序不使用任何C运行时库,那么我们可以不带上任何lib,但那样只能写基本的代码了。对于一般的简单脚本程序,只需几个必要的lib即可:KERNEL32.LIB,LIBCMT.LIB,LIBCPMT.LIB,OLDNAMES.LIB。总共才1M多。

  我们把这几个lib文件以及.pch文件,放在cl.exe同个目录下,这样无需指定INCLUDE和LIB环境变量。

  至此,我们有了一个精简版的VC6编译器。通过上述10个文件,我们可以不依赖任何环境,独立编译C++程序了。

cl /Yu"stdafx.h" /Fp"MyDLL.pch" test.cpp

  实际运行

  现在,我们可以动态产生C++代码文件,并且自动编译的能力了。但是如何将终的二进制文件与脚本宿主交互呢?

  由于exe只能运行在独立的进程里,数据交互只能通过匿名管道,要实现回调什么的非常困难。

  但若换成dll可以大显身手了,不仅运行在同一进程空间内,更重要的是dll是可以动态加载卸载的,这一点太符合脚本程序的特性了。并且当某个模块更新了之后,可以把先前的模块释放掉,加载新的。而这一切都是动态的,无需重启宿主即可完成!

  而且dll可以导出内部的函数,宿主用GetProcAddress()可以轻松获得某个函数地址;至于回调,传递一个宿主的函数指针给脚本可以了。只要约定好函数声明,双方都可以用简单原始的方法互相调用,甚至共享同一块内存空间。

  为了让函数导出更简洁,本例中定义了个叫function的宏:

#define function extern "C" __declspec(dllexport) void