实现外部程序根据参数调用、控制目标程序——ShellExecute()函数的详解及相关知识的拓展

本文主要是介绍实现外部程序根据参数调用、控制目标程序——ShellExecute()函数的详解及相关知识的拓展,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

喵哥最近在做一个需要远程控制相机的项目,其中一个环节就是需要用一个控制端的exe去调用相机运行的exe,还需要根据控制端提供的参数对相机作出相应的控制动作。另外,如果有做类似项目的朋友,希望可以交流一下经验,共同进步。

目录

WinExec

ShellExecute

接收参数


根据以往的经验,需要在外部执行文件开启一个程序的方法就是使用WinExec()函数,这个函数的参数简单,用起来也十分方便:

UINT WINAPI WinExec(_In_  LPCSTR lpCmdLine,_In_  UINT uCmdShow
);

WinExec

WinExec()的第一个参数是执行文件的路径,第二个参数是执行文件打开后的显示方式。在使用WinExec()时需要注意的事情:

************返回值**************** 
大于 31                    {调用成功}
等于 0 {内存不足}
ERROR_FILE_NOT_FOUND = 2;  {文件名错误}
ERROR_PATH_NOT_FOUND = 3;  {路径名错误}
ERROR_BAD_FORMAT     = 11; {EXE 文件无效}
(请参考FindExecutable函数) *************uCmdShow 参数可选值**************
SW_HIDE            = 0; {隐藏, 并且任务栏也没有最小化图标} SW_SHOWNORMAL = 1; {用最近的大小和位置显示, 激活}
SW_NORMAL          = 1; {同 SW_SHOWNORMAL}
SW_SHOWMINIMIZED   = 2; {最小化, 激活}
SW_SHOWMAXIMIZED   = 3; {最大化, 激活}
SW_MAXIMIZE        = 3; {同 SW_SHOWMAXIMIZED}
SW_SHOWNOACTIVATE  = 4; {用最近的大小和位置显示, 不激活}
SW_SHOW            = 5; {同 SW_SHOWNORMAL}
SW_MINIMIZE        = 6; {最小化, 不激活}
SW_SHOWMINNOACTIVE = 7; {同 SW_MINIMIZE}
SW_SHOWNA          = 8; {同 SW_SHOWNOACTIVATE}
SW_RESTORE         = 9; {同 SW_SHOWNORMAL}
SW_SHOWDEFAULT     = 10; {同 SW_SHOWNORMAL}
SW_MAX             = 10; {同 SW_SHOWNORMAL} 

另外,winexec() 必须有GetMessage或超时之后才返回!


ShellExecute

WinExec的一大缺点就是没有参数的传递机制,其实如果目标执行文件的参数不多,并且调用目标函数时使用的参数是有规律的,那么其实可以把参数放在一个文件中保存,然后顺序读取参数即可,也可以实现根据参数来控制目标执行文件的启动,但是喵哥的项目需要不按参数顺序来启动执行文件,或者说考虑到以后程序的扩展性,所以放弃了这个函数,然后在网上寻找合适的替代品。然后找到一说WinExec就不得不说的ShellExecute。

ShellExecute()函数的参数表要比WinExec大,有6个参数:

HINSTANCE ShellExecute(_In_opt_  HWND hwnd,  _In_opt_  LPCTSTR lpOperation, _In_      LPCTSTR lpFile,  _In_opt_  LPCTSTR lpParameters, _In_opt_  LPCTSTR lpDirectory, _In_      INT nShowCmd
);

各个参数的说明如下:

hWnd:用于指定父窗口句柄。当函数调用过程出现错误时,它将作为Windows消息窗口的父窗口。例如,可以将其设置为应用程序主窗口句柄,即Application.Handle,也可以将其设置为桌面窗口句柄(用GetDesktopWindow函数获得)。 
     Operation:用于指定要进行的操作。其中“open”操作表示执行由FileName参数指定的程序,或打开由FileName参数指定的文件或文件夹;“print”操作表示打印由FileName参数指定的文件;“explore”操作表示浏览由FileName参数指定的文件夹。当参数设为nil时,表示执行默认操作“open”。 
      FileName:用于指定要打开的文件名、要执行的程序文件名或要浏览的文件夹名。 
      Parameters:若FileName参数是一个可执行程序,则此参数指定命令行参数,否则此参数应为nil或PChar(0)。 
      Directory:用于指定文件开始运行的默认目录,即FileName为NULL时,打开该文件夹,若不对默认目录做出设置(NULL),默认打开文档(环境为Windows 10)。 
      ShowCmd:若FileName参数是一个可执行程序,则此参数指定程序窗口的初始显示方式,否则此参数应设置为0。这个参数可用的值如下所列:

1 SW_HIDE 隐藏这个窗体,并激活其他窗体。
2 SW_MAXIMIZE 最大化指定的窗体。
3 SW_MINIMIZE 最小化指定的窗体,并按顺序激活最上层的窗体。
4 SW_RESTORE 激活并显示窗体。如果窗体为最小化或者最大化,窗体恢复到原始大小和位置。应用程序当恢复一个最小化的窗体时将指定标记。
5 SW_SHOW 以当前的大小和位置激活并显示窗体。
6 SW_SHOWDEFAULT 
7 SW_SHOWMAXIMIZED 激活并最大化显示窗体。
8 SW_SHOWMINIMIZED 激活并最小化现实窗体。
9 SW_SHOWMINNOACTIVE 最小化窗体,保持其激活状态。
10 SW_SHOWNA 以当前状态显示窗体,保持其激活状态。
11 SW_SHOWNOACTIVATE 以当前的大小和位置显示窗体,并保持其激活状态。
12 SW_SHOWNORMAL 激活并显示一个窗体。如果窗体为最大化或者最小化,窗体恢复到原始的大小和位置。当窗体第一次显示的时候,应用程序记录标记。

若ShellExecute函数调用成功,则返回值为被执行程序的实例句柄。若返回值小于32,则表示出现错误。


ShellExecute的功能远不止这些,它还可以做一些特别酷的事情:

1. 如果将FileName参数设置为“http:”协议格式,那么该函数将打开默认浏览器并链接到指定的URL地址。若用户机器中安装了多个浏览器 ,则该函数将根据Windows 9x/NT注册表中http协议处理程序(Protocols Handler)的设置确定启动哪个浏览器。 

格式一:http://网站域名。  如:ShellExecute(handle, ‘open’, http://www.neu.edu.cn’, nil, nil, SW_SHOWNORMAL); 
      格式二:http://网站域名/网页文件名。 
       如:ShellExecute(handle, ‘open’, http://www.neu.edu.cn/default.htm’,nil,nil,SW_SHOWNORMAL); 
      2.如果将FileName参数设置为“mailto:”协议格式,那么该函数将启动默认邮件客户程序,如Microsoft Outlook(也包括Microsoft Outlook Express)或Netscape Messanger。若用户机器中安装了多个邮件客户程序,则该函数将根据Windows 9x/NT注册表中mailto协议处理 程序的设置确定启动哪个邮件客户程序。 
      格式一:mailto: 
      如:ShellExecute(handle,‘open’, ‘mailto:’, nil, nil, SW_SHOWNORMAL);打开新邮件窗口。 
      格式二:mailto:用户账号@邮件服务器地址 
      如:ShellExecute(handle, ‘open’,‘ mailto:who@mail.neu.edu.cn’, nil, nil, SW_SHOWNORMAL);打开新邮件窗口,并自动填入收件人地址。若指定多个收件人地址,则收件人地址之间必须用分号或逗号分隔开(下同)。 
      格式三:mailto:用户账号@邮件服务器地址?subject=邮件主题&body=邮件正文 
      如:ShellExecute(handle, ‘open’, ‘ mailto:who@mail.neu.edu.cn?subject=Hello&Body=This is a test’, nil, nil, 
SW_SHOWNORMAL);打开新邮件窗口,并自动填入收件人地址、邮件主题和邮件正文。若邮件正文包括多行文本,则必须在每行文本之间加入换行转义字符%0a。 

还有其他一些有意思的操作可以参考:深入浅出ShellExecute(总结)。

除了ShellExecute()之外,还有一个比较复杂、又强大的函数——CreateProcess(),其函数原型如下:

BOOL WINAPI CreateProcess(_In_opt_     LPCTSTR lpApplicationName,_Inout_opt_  LPTSTR lpCommandLine,_In_opt_     LPSECURITY_ATTRIBUTES lpProcessAttributes,_In_opt_     LPSECURITY_ATTRIBUTES lpThreadAttributes,_In_         BOOL bInheritHandles,_In_         DWORD dwCreationFlags,_In_opt_     LPVOID lpEnvironment,_In_opt_     LPCTSTR lpCurrentDirectory,_In_         LPSTARTUPINFO lpStartupInfo,_Out_        LPPROCESS_INFORMATION lpProcessInformation
);

由于CreateProcess比较复杂,而ShellExecute还可以满足需求,所以暂时没有认真去了解它。有兴趣的朋友可以去看:https://blog.csdn.net/duck04551/article/details/4312260。


接收参数

所以喵哥最终选用了ShellExecute()函数来控制目标执行文件,但是还有一个问题需要解决,那就是目标执行文件怎么接收ShellExecute发过去的参数呢,为此喵哥又去研究了一下main函数的参数传递规律。

先从最简单的 int main(int argc, char * argv)开始,喵哥最开始接触C/C++时,对于这个主函数的参数定义十分不屑(主要是无知...)觉得我用main()不一样用得挺好嘛,也一直没用到过这两个参数,直到最近几天,我突然意识到外部跟程序的参数传递可以通过这俩参数,真是惊醒梦中人。

为了测试传递参数的规律,我写了如下简单的测试代码:

int main(int argc, char *argv[])
{int i;for (i = 0; i < argc; i++)printf("Argument %d is %s.\n", i, argv[i]);getchar();return 0;
}

然后在cmd中执行生成的exe文件:

根据cmd中的运行结果,可以得出一些结论。第一个参数argc,指明有多少个参数将被传递给主函数main(),真正的参数以字符串数组(即第2个参数argv[])的形式来传递。需要注意的是,argc代表参数的数量, main()函数本身是在索引0为的第一参数。 所以, argc总是至少为1,它的数值是argv列阵的元素数目。 所以, argv[0]的值是至关重要的。 如果用户在控制台环境中程序名称后键入含参数的指令, 那么随后的参数将传递给argv[1]。


熟悉MFC的朋友都知道,APP源代码中的InitInstance()相当于C++main函数,即程序的入口,在这里就可以接收命令传递过来的参数,并作出相应的操作。不过在MFC中argc和argv分别变成__argc和__argv,以下是我的部分代码。

/*****************接收ShellExecute的消息************************/CString str;for (int i = 1; i < __argc; i++){str += __argv[i];     }/***************************设置INDEX以选择需要的相机**********************/if (str == "openCamera1")       INDEX = 0;else if (str == "openCamera2")	INDEX = 1;else if (str == "openCamera3")	INDEX = 2;else if (str == "openCamera4")	INDEX = 3;else if (str == "openCamera5")	INDEX = 4;else if (str == "openCamera6")	INDEX = 5;else                            INDEX = 0;

 

这篇关于实现外部程序根据参数调用、控制目标程序——ShellExecute()函数的详解及相关知识的拓展的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/626299

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Debezium 与 Apache Kafka 的集成方式步骤详解

《Debezium与ApacheKafka的集成方式步骤详解》本文详细介绍了如何将Debezium与ApacheKafka集成,包括集成概述、步骤、注意事项等,通过KafkaConnect,D... 目录一、集成概述二、集成步骤1. 准备 Kafka 环境2. 配置 Kafka Connect3. 安装 D

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本