一个程序,我们通过输入不同的命令行参数,就可以实现一个可执行文件,多种功能,通过命令行来控制它的行为,例如,我们在控制台的时候,就是遇到最多的,如一个exe程序,加入为test..exe,我们可以设置一些命令行参数,如/t,/q,在我们输入不同的命令行的时候,实现不同的行为,但是一般在控制台见得普遍,在窗口化程序中,是否没有怎么看到,之所以我们没有看到,是因为使用的方式不同,对于窗口化的程序,我们通常根据功能的不同,我们把这些参数都写入了注册表,然后在右键快捷菜单中设置相应的菜单项,当我们选择不同的菜单项目的时候,其实就是使用了通过不同的命令行来达到不同的程序行为,另外,我们还可以在我们编写的mfc程序中,通过参数来控制行为以后,我们不在继续执行,创建窗口等操作,而是直接退出。如此一来,我们就可以通过一个exe文件实现不同的功能,如一个基于对话框的程序,我们通过输入不同的参数以后,可以实现一些功能,但是呢,我们不启动对话框。那么,下面就来说说如何mfc是如何使用命令行的。
在文档类的InitInstance中会看到这样的一句话,前面是一个类,这个类只有一个方法,就是CCommandLineInfo::ParseParam,别的全是数据成员和构造函数,ParseCommandLine函数是CWinapp的一个方法,它以cmdInfo对象为参数,它的作用就是重复的调用ParseParam方法来解析传入程序的命令行,根据标记和参数来填充CCommandLineInfo的数据成员,它是重复调用的,直到将所有的参数解析完毕。然后我们在这段代码的下面,找到一句:
if (!ProcessShellCommand(cmdInfo))
return FALSE;
这个就是通过参数,执行相应的命令,如打开文件,新建文件等等命令,这些都可以在MSDN的CWinapp类中找到,但是呢,这些都是默认的,MFC内置的命令,如果我们要实现我们自己的命令参数呢,应该如何做呢,那么,我们应该自定义一个类,这个类继承了CCommandLineInfo类,这里顺便说一下如何添加类到MFC到我们的项目中,通过类向导,选择添加MFC类,如下图:
这中添加方式,就可以直接将相应的头文件,源文件关联然后加入项目中,而且类的基本框架页实现了,但是有的类,如我们现在要添加一个继承自CCommandLineInfo类的自定义类,我们发现,没有这个基类,那么应该如何做呢,我们可以选择继承自CObject类,这个类是所有mfc类的基类,它是一个很重要的类,同学们可以到msdn中查阅学习。继续刚才的,我们继承了CObject这个类,但是我们不是要继承CCommandLineInfo类吗,其实这不要紧,看下图:
我们只要将CObject换成我们的CCommandLineInfo类,这样就可以了,大家可以看到,用这种方式添加的类,构造函数,析构函数都有了,所以建议大家用这中方式添加自定义MFC类。在这里,我添加了两个数据成员,分别为TCHAR类型的常量指针,之所以为常量,是为了和我们后面要写的ParseParam相对应。我用这两个变量来保存我们自定义的命令行标记和参数。命令行里面的标记呢,是指如/t,-t,这样的就是标记,在解析的时候,自动将/和-符号移除。加/和-的效果是一样的,关键是标记后面的值。参数,如我们打开一个文件,“C:\Test.txt”,用双引号引起来的,就是参数。在解析的时候,引号也自动去掉。如果我们的参数中必须要有引号,那么在最外面的引号里面通过转义字符来表示如\",这样一来,就可以将双引号包含如命令中。最后,我重写了CCommandLineInfo类唯一的方法ParseParam方法,如下图:
由上面可以看到,bFlag是一个布尔值,如果是TURE,说明这个命令行是一个标记,那么就将它存入F中,否则就是参数,存入P中。最后在调用基类的方法,主要是响应MFC默认的一些命令,当然,这里呢,对于是否调用基类的判断不是很好,大家根据自己的实际情况,来设定。如此一来,我们就可以获取我们定义的命令行了。下面是如何处理我们自定义的命令行
在最初的基础之上,我先处理了自己的自定义的命令行,然后根据需要来看是否调用处理默认的MFC命令。这是在mfc的文档类中,我们充分利用了mfc框架,实现了自定义命令行的实现,那么我们还有一种方式,这种方式和SDK的WinMain函数中处理由WinMain函数传递进来的命令行处理是一样的,如下:
int CALLBACK WinMain( _In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow );其中lpCmdLine就是命令行,下面我就在基于对话框的MFC程序中说明一下。
WinMain的这个参数传递进来以后,就把值赋给了app类的m_lpCmdLine数据成员中,那么我们只要解析它就可以了,如下图:
在这里我们在输出窗口中可以看到m_lpCmdLine中的值,就是我调试的时候设置的命令行。然后我通过一个while语句,将字符指针中的字符复制到一个字符数组中,因为字符数组通过索引来查找字符,便于我们解析。但是这个要麻烦一点,因为不会像MFC提供的CCommandLine类一样为我们自动移除引号和/,-标记。这些全部要我们自己来操作。那么,我们默认调试运行的时候,参数是空,我们如何在调试的时候加入参数呢,见下图:
在属性当中,调试,命令参数中,我们就可以输入参数,在运行的时候,就可以传入参数,来帮助我们调试不同命令行下的参数运行情况。那么,第二种方式,就完全和SDK总的WinMain中的参数解析一样的,如此一来,就完全没有使用MFC提供给我们的方法,就是麻烦一点,但是有大的灵活性。
最后,总结一下,通过命令行,控制我们程序启动时的行为,实现一个exe文件,多个功能,如在上面的例子中,我们可以在处理我们自己的命令之后,可以返回,退出,而不继续执行,建立窗口。但是大家要记住处理命令行的地方,那就是在app类的InitInstance方法中,应该在调用基类的CWinApp::InitInstance();方法之前调用,因为基类的这个方法就开始创建实例了,如果再创建实例之后,处理我们自己的命令行就直接返回,会造成内存泄露的情况。