实现外部程序根据参数调用、控制目标程序——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

相关文章

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

JAVA系统中Spring Boot应用程序的配置文件application.yml使用详解

《JAVA系统中SpringBoot应用程序的配置文件application.yml使用详解》:本文主要介绍JAVA系统中SpringBoot应用程序的配置文件application.yml的... 目录文件路径文件内容解释1. Server 配置2. Spring 配置3. Logging 配置4. Ma

MySQL中时区参数time_zone解读

《MySQL中时区参数time_zone解读》MySQL时区参数time_zone用于控制系统函数和字段的DEFAULTCURRENT_TIMESTAMP属性,修改时区可能会影响timestamp类型... 目录前言1.时区参数影响2.如何设置3.字段类型选择总结前言mysql 时区参数 time_zon

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

Python xmltodict实现简化XML数据处理

《Pythonxmltodict实现简化XML数据处理》Python社区为提供了xmltodict库,它专为简化XML与Python数据结构的转换而设计,本文主要来为大家介绍一下如何使用xmltod... 目录一、引言二、XMLtodict介绍设计理念适用场景三、功能参数与属性1、parse函数2、unpa

mac中资源库在哪? macOS资源库文件夹详解

《mac中资源库在哪?macOS资源库文件夹详解》经常使用Mac电脑的用户会发现,找不到Mac电脑的资源库,我们怎么打开资源库并使用呢?下面我们就来看看macOS资源库文件夹详解... 在 MACOS 系统中,「资源库」文件夹是用来存放操作系统和 App 设置的核心位置。虽然平时我们很少直接跟它打交道,但了