一种解决启动进程传递参数过长的方法
工作中,QA同学在测试我们程序的时候,发现在XP下,我们的A进程无法启动我们的B进程。而在Win7 64bit系统下功能正常。RD同学调试后,发现我们A进程中使用ShellExcute去启动了B进程(转载请指明出于breaksoftware的csdn博客)
HINSTANCE ShellExecute(_In_opt_ HWND hwnd,_In_opt_ LPCTSTR lpOperation,_In_ LPCTSTR lpFile,_In_opt_ LPCTSTR lpParameters,_In_opt_ LPCTSTR lpDirectory,_In_ INT nShowCmd
);
其中不成功的场景是:我们给lpParameters传递了大概32K字节长度的参数。
我当时就觉得这个是因为ShellExcute中参数长度限制问题。我决定将这个逻辑使用CreateProcess去实现,这样我将会有更多的控制权力。但是最后我们发现问题还是依旧的,因为我们查看MSDN关于CreateProcess的lpCommandLine说明:
lpCommandLine [in, out, optional]
The command line to be executed. The maximum length of this string is 32,768 characters, including the Unicode terminating null character.
它最长只可以穿32768个字符(而我之后测试结果却是32766)。看来简单的使用CreateProcess还是不能解决我们的问题。
为了解决这个问题,我们首先分析问题出现的场景:
- A进程去启动B进程
- A进程启动B进程时要传递一个很长的数据
- A进程不关心B进程执行结果和生命周期
- B进程不关心A进程的生命周期
遇到这类问题,首先肯定先想到,使用管道(Pipe)或者Socket这类进程间通信手段。这个方法可以解决上述特点中的1、2两个问题。但是管道和Socket给人最直观的映像就是:双方交互式通信。即A要关心B的存在与否,B也要关心A的存在与否。任何一方断了,都会影响另一方的流程。这个和我们上述特点中的3、4是相背的。那么怎么解决呢?我想到了另一个进程间通信的方法——内存映射文件。
内存映射文件分为两种,一种是“命名”文件,一种是“匿名”内存映射文件。“命名”文件一般用于安全性要求不高的进程间通信,而“匿名”内存映射文件一般是用于安全性较高的进程间通信。我们肯定优先考虑安全性更高的“匿名”内存映射文件。我举一个之前我写得工程的例子解释如何使用“匿名”内存映射文件进行进程间通信的:
- A和B进程建立管道连接
- A创建一个“匿名”内存映射文件
- A打开B进程句柄
- A将“匿名”内存映射文件Handle Duplicate给B进程,生成B进程可以使用的HandleB
- A将HandleB通过管道传递给进程B
- 进程B使用HandleB访问数据
这个流程给出了一个使用匿名管道进行进程间通信的一个必要的条件:B进程的已经存在,并且可以通知B进程去使用Duplicate后的HandleB。
在我们的场景中,就是不希望使用除了文件映射之外的通信方式。而且,我们要在B进程创建时,就将文件映射传给B进程,所以无法使用“匿名”内存映射文件。
目前只剩下“命名”内存映射文件一条路可以走了。虽然这种方式存在种种不安全性,但是它是目前场景下唯一可以选择的方向。
为了不存在“名称”的冲突问题。我选择了随机生成“名称”的方案
VOID CTransmitParam::GenerateFileMappingName()
{time_t t;srand((unsigned)time(&t));WCHAR wchName[MAX_PATH] = {0};wsprintf( wchName, L"%d", rand());m_wstrFileMappingName.clear();m_wstrFileMappingName.append(wchName);
}
虽然每次都是随机的,但是我还是不放心这个“随机”碰撞的概率。于是我在创建内存映射文件时判断了下当前创建的“名字”是否在系统中已经存在。如果存在,我会重新随机生成名字并创建该名字的内存映射文件。
BOOL CTransmitParam::CreateFileMappingEx(DWORD dwNewBufferSize)
{BOOL bSuc = FALSE;int nMaxLoopCount = 32;do {m_hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, dwNewBufferSize, m_wstrFileMappingName.c_str());if ( NULL == m_hFileMapping ) {break;}if ( ERROR_ALREADY_EXISTS == ::GetLastError() ) {::CloseHandle(m_hFileMapping);m_hFileMapping = NULL;if ( 0 >= --nMaxLoopCount ) {break;}else {GenerateFileMappingName();continue;}}else {bSuc = TRUE;break;}}while (TRUE);return TRUE;
}
待内存映射文件创建成功后,我们往该“文件”中写入数据,其数据格式是:前sizeof(DWORD)保存的是要传递给子进程的数据长度,其后跟着数据内容。
struct StData {DWORD dwBufferSize; // 从BufferFirst开始的数据长度BYTE BufferFirst;
};
具体的数据填充代码是
BOOL CTransmitParam::PackData( LPVOID lpMem,DWORD dwNewBufferSize,LPCBYTE lpBuffer, DWORD dwBufferSize )
{BOOL bSuc = FALSE;do {LPBYTE lpFilePointer = (LPBYTE)lpMem;OVERLAPPED op;memset(&op, 0, sizeof(op));DWORD dwRead = 0;DWORD dwBufferSizeSize = sizeof(dwNewBufferSize);errno_t e = memcpy_s( lpFilePointer, dwNewBufferSize, &dwNewBufferSize, dwBufferSizeSize);if ( 0 != e ) {std::cerr<<"Memcpy_s Failed.The error code is"<<e<<std::endl;break;}lpFilePointer += sizeof(dwNewBufferSize);e = memcpy_s( lpFilePointer, dwNewBufferSize - dwBufferSizeSize, lpBuffer, dwBufferSize );if ( 0 != e ) {std::cerr<<"Memcpy_s Failed.The error code is"<<e<<std::endl;break;}bSuc = TRUE;} while (0);return bSuc;
}
下一步就是我们要使用挂起的方式创建子进程B。之所以要使用挂起方式创建,是因为我们要获取其进程的句柄,并且使用该进程句柄去Duplicate出内存映射文件句柄HandleB。之所以要这么做,所因为,我们要在此时让该内存映射文件和子进程B的生命周期相关联。因为从父进程角度来说,我们CreateFileMapping后,要进行对应的CloseHandle,从而不会造成资源泄露。如果我们不让父进程创建的内存映射文件和子进程B相关联,在父进程CloseHandle后,内存映射文件的引用计数将降为0,从而被释放掉。此时,子进程可能还没有时机去读取到内存映射文件。
BOOL CTransmitParam::CreateProcess_TransmitParam( LPCWSTR lpChildProcssPath )
{BOOL bSuc = FALSE;do {STARTUPINFO st;memset(&st, 0, sizeof(st));st.cb = sizeof(st);PROCESS_INFORMATION pi;memset(&pi, 0, sizeof(pi));std::wstring wstrCmd = GenerateCommandLine();BOOL bCreateSuc = CreateProcess( lpChildProcssPath, (LPWSTR) wstrCmd.c_str(), NULL, NULL, FALSE, /*CREATE_NO_WINDOW |*/ CREATE_SUSPENDED, NULL, NULL, &st, &pi );if ( FALSE == bCreateSuc ) {std::cerr<<"CreateProcess Error.The error code is"<<::GetLastError()<<std::endl;break;}HANDLE hTargetHandle = NULL;if ( FALSE == DuplicateHandle( GetCurrentProcess(), m_hFileMapping, pi.hProcess, &hTargetHandle, DUPLICATE_SAME_ACCESS, FALSE, DUPLICATE_SAME_ACCESS ) ) {std::cerr<<"DuplicateHandle Failed.The error code is"<<::GetLastError()<<std::endl;break;}if ( NULL != pi.hThread ) {::ResumeThread( pi.hThread );}CloseHandle(pi.hThread);CloseHandle(pi.hProcess);bSuc = TRUE;} while (0);return bSuc;
}
在父进程CloseHandle后,父进程的逻辑就此走完。我们再看下子进程的数据接收过程。
子进程接收一个以“FM”为Key的参数,该参数中保存了“命名”内存映射文件的名字,通过该名字,我们可以获取父进程传送过来的数据内容。
BOOL CTransmitParam::UnPackData(LPVOID lpMem)
{BOOL bSuc = FALSE;do {m_dwRecvBufferLength = 0;errno_t e = memcpy_s( &m_dwRecvBufferLength, sizeof(m_dwRecvBufferLength), lpMem, sizeof(DWORD));if ( 0 != e ) {std::cerr<<"Memcpy_s Failed.The error code is"<<e<<std::endl;break;}if ( 0 == m_dwRecvBufferLength ) {std::cerr<<"FileMapping's size is 0.\n"<<::GetLastError()<<std::endl;break;}m_lpRecvBuffer = new BYTE[m_dwRecvBufferLength];memset( m_lpRecvBuffer, 0, sizeof(m_lpRecvBuffer));e = memcpy_s( m_lpRecvBuffer, m_dwRecvBufferLength, (LPBYTE)lpMem + sizeof(DWORD), m_dwRecvBufferLength );if ( 0 != e ) {std::cerr<<"Memcpy_s Failed.The error code is"<<e<<std::endl;break;}bSuc = TRUE;} while (0);return bSuc;
}BOOL CTransmitParam::GetRecvBuffer(const std::wstring& wstrFileMappingName)
{if ( NULL != m_lpRecvBuffer ) {return TRUE;}BOOL bSuc = FALSE;do { HANDLE hFileMapping = OpenFileMapping( FILE_MAP_READ, FALSE, wstrFileMappingName.c_str());if ( NULL == hFileMapping ) {std::cerr<<"OpenFileMapping Failed.The error code is"<<::GetLastError()<<std::endl;break;}LPVOID lpMem = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);if ( NULL == lpMem ) {std::cerr<<"MapViewOfFile Failed.The error code is"<<::GetLastError()<<std::endl;break;}if ( FALSE == UnPackData(lpMem) ) {break;}UnmapViewOfFile(lpMem);CloseHandle(hFileMapping);bSuc = TRUE;} while (0);return bSuc;
}
工程下载地址
相关文章:

Ubuntu“无法获得锁\加锁”解决方案
2019独角兽企业重金招聘Python工程师标准>>> 当你添加了源,更新源的时候,如果中途中断了更新,安装软件或者再次更新的时候就是出现如下提示, E: 无法获得锁 /var/lib/apt/lists/lock – open (11: 资源暂时不可用) E: …

一步一步学Silverlight 2系列(3):界面布局
概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, Ironpython,对JSON、Web Service、WCF以及Sockets的支持等一系列新的特性。《一步一步学Silverlig…

一种准标准CSV格式的介绍和分析以及解析算法
CSV是一种古老的数据传输格式,它的全称是Comma-Separated Values(逗号分隔值)。出生在那个标准缺失的蛮荒年代,CSV的标准一直(到2005年)是NULL——世间存在着N种CSV格式,它们自成体系࿰…
新战场路在何方——详解360金融数据中台之旅
作者 |360金融架构总监黄建庭出品 | AI科技大本营(ID:rgznai100)本文为CSDN即将推出的《新战场:决胜中台》专刊的第 4 篇文章。自阿里巴巴引入中台概念后,市场对中台的关注度持续“高烧”不退。作为企业的基础平台,数据…

oracle中的exists 和not exists 用法详解
有两个简单例子,以说明 “exists”和“in”的效率问题 1) select * from T1 where exists(select 1 from T2 where T1.aT2.a) ; T1数据量小而T2数据量非常大时,T1<<T2 时,1) 的查询效率高。 2) select * from T1 where T1.a in (select…

现代内存编号解读(转)
现代SDRAM、DDR SDRAM、DDR2 SDRAM三种主流内存颗粒的编号一、DDR SDRAM:HYNIX DDR SDRAM颗粒编号:HY XX X XX XX X X X X X X X — XX X1 2 3 4 5 6 7 8 9 10 11 12 — 13 14整个DDR SDRAM颗粒的编号,一共是由14…
被追捧为“圣杯”的深度强化学习已走进死胡同
作者 | 朱仲光编译 | 夕颜出品 | AI科技大本营(ID:rgznai1100)【导读】近年来,深度强化学习成为一个被业界和学术界追捧的热门技术,社区甚至将它视为金光闪闪的通向 AGI 的圣杯,大多数人都看好它未来发展的巨大潜力。但…
一种清除windows通知区域“僵尸”图标的方案——问题分析
通知区域名称有趣的历史 假如说到windows通知区域,可能很多人还是不清楚它是什么。如果改称Tray区域,可能有人就懂了。如果再白话点,叫它“托盘”或者“系统托盘”,可能会有更多的人猜到它是windows什么部位。现在我们揭开…

Apache2.4+Tomcat7集群搭建
一、安装jdk、Tomcat、Apache1.安装jdk1.7cd /home/java/software #把软件下载到/home/java/software目录下,将应用安装到/home/java目录下。 wget http://download.oracle.com/otn/java/jdk/7u80-b15/jdk-7u80-linux-x64.tar.gz tar -zxvf jdk-7u80-linux-x64.tar…
一种清除windows通知区域“僵尸”图标的方案——XP系统解决方案
XP下“僵尸”图标的解决方案 从《一种清除windows通知区域“僵尸”图标的方案——问题分析》(以后简称《问题分析》)一文中分析的通知区域结构可以看出,XP的通知区域结构是相对简单的。如果我们解决了XP下的问题,那么Win7上的问题…

《评人工智能如何走向新阶段》后记(再续12)
由AI科技大本营下载自视觉中国151. 新一代人工智能研究方向: (1)研究新一代人工智能基础理论(机理、模型和算法);(2)研发面向需求的共性技术(以神经网络和算法为核心、数据和硬件为基…

正则表达式测试工具 Regex Tester 的使用方法
2019独角兽企业重金招聘Python工程师标准>>> 正则表达式测试工具“RegexTester”,下载地址:http://www.oschina.net/p/regextester 一、关于本文 今天的工作中遇到了一些正则表达式,我需要检验它们是否正确,不过我对自…
一种清除windows通知区域“僵尸”图标的方案——Windows7系统解决方案
Windows7下“僵尸”图标的解决方案 从《一种清除windows通知区域“僵尸”图标的方案——问题分析》(以后简称《问题分析》)一文中分析的通知区域结构可以看出,Windows7的通知区域比XP通知区域多出了一个“临时”系统通知区域(转载…

《评人工智能如何走向新阶段》后记(再续13)
由AI科技大本营下载自视觉中国161. 引自美国科技媒体TNW记者对美欧企业主管与AI专家的访谈录摘要,谈到2020年AI的八大趋势: ①人工智能将使医疗保健更准确、成本更低; ②可解释性和信托及AI伦理将受到更多关注; ③在人工智能领…

在特定情况下的简单SSO实现方案
最近需要实现类似单点登录的功能。情况是这样的,最初在做网站A,做着做着,要做网站B了,要求与网站A完全分开作为两个应用,但用户数据要求与网站A保持一致,也要求用户在网站A登录后,转到网站B时不…

为创业者保驾护航 “无安全 不创业” 安全狗全国路演北京站
2019独角兽企业重金招聘Python工程师标准>>> 2015年上半年,网络安全问题毫无疑问已经成为了互联网行业关注的重点。在短短一年多的时间里,网络安全问题就从隐患转而呈现出爆发之势,即使是网易、支付宝、携程这样的互联网行业巨头也…
一种将快捷方式从开始菜单“常用应用”的中去除的方法
当我们安装一款软件的时候,这款软件的一些快捷方式可能被设置到开始菜单的“常用应用”区域。但是,如果是“卸载”快捷方式被“钉”到该区域,就会造成非常不好的体验。毕竟把“卸载”接口暴露得如此醒目,如同把该款软件的地狱大门…

ISA---不能访问网址或是多次刷新才能访问的解决方法一则
当你安装ISA2006在WINDOWS 2003 SERVER上,并打上SP2补订时。遇SNAT客户端不能访问WEB,但能PING通,能TELNET通,也能访问QQ或是MSN的问题时可以利用以下方法解决。同时,如果你遇到在此环境下,客户端访问外部网…

《评人工智能如何走向新阶段》后记(深谈人工智能发展前沿)
由AI科技大本营下载自视觉中国来自国内外的跟贴留言 深谈人工智能发展前沿 自从我们发表《评人工智能如何走向新阶段》一文以来,至今约5个月,引来了中外专家、草根们的大量跟贴留言(也有人转录他人的公开言论作为跟贴来发表的)。…

URAL 2027 URCAPL, Episode 1 (模拟)
题意:给你一个HxW的矩阵,每个点是一个指令,根据指令进行一系列操作。 题解:模拟 #include<cstdio> #include<algorithm> using namespace std;const int maxn 101; char G[maxn][maxn];int dx[] {-1,0,1, 0}; int d…

使用WinHttp接口实现HTTP协议Get、Post和文件上传功能
我实现了一个最新版本的接口,详见《实现HTTP协议Get、Post和文件上传功能——使用WinHttp接口实现》。还有基于libcurl实现的版本《实现HTTP协议Get、Post和文件上传功能——使用libcurl接口实现》。以下是原博文: 我们在做项目开发时,往往会…
收藏 | 一文带你总览知识蒸馏,详解经典论文
「免费学习 60 节公开课:投票页面,点击讲师头像」作者:凉爽的安迪来源 | 深度传送门(ID:deep_deliver)【导读】这是一篇关于【知识蒸馏】简述的文章,目的是想对自己对于知识蒸馏学习的内容和问题…

[工具推荐]用了TrueCrypt 再无难掩之隐
缘起:混在网络n多年了,手头总有些东西不想被别人看到的东西,由于小弟人品好,相貌佳,总有很多朋友喜欢用我的电脑玩啊玩啊……。 近日,冠希、柏芝等前辈以身示法,为我等上了很好一堂关于隐私保护…

利用phpmailer类邮件发送
<?phprequire("class.phpmailer.php"); //下载的文件必须放在该文件所在目录$mail new PHPMailer(); //建立邮件发送类$address "接收方邮箱"; //接收方地址$mail->IsSMTP(); //使用SMTP方式发送$…
据说这是大多数人【减肥】的真实写照
有句诗说得好 “冬天不减肥,夏天徒伤悲” 在这个人人储存脂肪的季节绝对是你甩掉脂肪的好时机(毕竟这是一个拼颜值的时代颜值是天生的,可是身材绝不能输)但是 据说大多数人的减肥经历其实是这样的减肥第一步管住嘴,迈开…
PE文件和COFF文件格式分析——导出表的应用——一种摘掉Inline钩子(Unhook)的方法
在日常应用中,某些程序往往会被第三方程序下钩子(hook)。如果被下钩子的进程是我们的进程,并且第三方钩子严重影响了我们的逻辑和流程,我们就需要把这些钩子摘掉(Unhook)。本件讲述一种在32位系统上,如何摘掉API钩子的思路和方法。…

设置列表字段为主键
转贴:Sample event handler to set a field as a pr imary key (enforce no duplicates) Got this as a request from a reader- how to prevent users from adding items with same titles as ones that already exist in the list. Codeusing System;using System.Collectio…

谁登录了你的linux
最近有一台数据库服务器自动重启。查了一下相关登录信息:查看linux下的用户登录日志,包括用户登录时所用的主机的ip:more /var/log/secure who /var/log/wtmp干了些什么? root账户下输入su - username 切换到username下输入 histo…
一种使用GDI+对图片尺寸和质量的压缩方法
今天同事向我询问图片压缩的算法,我想起大概两三年前做过的一个项目。其中包含了尺寸和质量两种压缩算法,并且支持JPEG、bmp、PNG等格式。今天把这段逻辑贴出来,供大家参考。(转载请指明来源于breaksoftware的CSDN博客)…

.NET企业级应用架构设计系列之应用服务器
本文属spanzhang(张友邦)原创,发布地址为:http://blog.csdn.net/spanzhang。转载或引用请注明原文之出处,谢谢! .NET企业级应用架构设计系列之开场白 .NET企业级应用架构设计系列之技术选型 这里要说到的…