c++builder6使用ShellExecuteEx遇到诡异问题
CB6的程序是很多年前的一个东西,现在要给它增加新功能,用QT写一个摄像头控制让他来调用。试过system()函数,功能完全可以实现但那个黑窗口太丑了。于是改用ShellExecuteEx。于是为了这一点点的美化,一天时间就扔进去了。
一开始代码是这样的,网上类似代码有很多,也没有什么特殊操作。series是个变量,作为参数送给被执行的takePic。
.
.
.
AnsiString cmd = "takePic_32\\takePic.exe ";
AnsiString series = var;
.
.
.
SHELLEXECUTEINFO sei;
ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));
sei.cbSize = sizeof(SHELLEXECUTEINFO);
sei.lpFile = cmd.c_str();
sei.lpParameters = series.c_str();
sei.nShow = SW_SHOW;
sei.fMask = SEE_MASK_NOCLOSEPROCESS;//使用 SEE_MASK_NOCLOSEPROCESS 参数
sei.lpVerb = NULL;//("open");
if (ShellExecuteEx(&sei))//执行成功
{
if (sei.hProcess)//指定 SEE_MASK_NOCLOSEPROCESS 并其成功执行,则 hProcess 将会返回执行成功的进程句柄
WaitForSingleObject(sei.hProcess, INFINITE);//等待执行完毕
}else{
ShowMessage("无法启动摄像头!");
return;
}
takePic单独运行时一切正常,被调用之后虽然程序可以运行,可以拍照,但拍出来的照片无法存储。
一开始以为是权限问题,试了各种方法都无法解决。做了很多试验总结出的规律是,被调用的程序可以复制已经存在的文件,但不能建立新的文件。
又以为是takePic有问题,在QT里找了很久也没有线索。
各种查资料,各种试验,没头绪。
就在已经准备放弃这个方案时,无意发现不传 lpParameters 运行时一切正常,或者把 lpParameters 用裸字串写死也一切正常。
太奇怪了,按文档资料来看,
SHELLEXECUTEINFO定义:
typedef struct _SHELLEXECUTEINFO {
DWORD cbSize;//结构大小,sizeof(SHELLEXECUTEINFO)
ULONG fMask;//指定结构成员的有效性
HWND hwnd;//父窗口句柄或出错时显示错误父窗口的句柄,可以为 NULL
LPCTSTR lpVerb;//指定该函数的执行动作
LPCTSTR lpFile;//操作对象路径
LPCTSTR lpParameters;//执行参数,可以为 ULL
LPCTSTR lpDirectory;//工作目录,可以为 NULL
int nShow;//显示方式
HINSTANCE hInstApp;//如果设置了 SEE_MASK_NOCLOSEPROCESS ,并且调用成功则该值大于32,调用失败者被设置错误值
LPVOID lpIDList;//ITEMIDLIST结构的地址,存储成员的特别标识符,当fMask不包括SEE_MASK_IDLIST或SEE_MASK_INVOKEIDLIST时该项被忽略
LPCTSTR lpClass;//指明文件类别的名字或GUID,当fMask不包括SEE_MASK_CLASSNAME时该项被忽略
HKEY hkeyClass;//获得已在系统注册的文件类型的Handle,当fMask不包括SEE_MASK_HOTKEY时该项被忽略
DWORD dwHotKey;//程序的热键关联,低位存储虚拟关键码(Key Code),高位存储修改标志位(HOTKEYF_),当fmask不包括SEE_MASK_HOTKEY时该项被忽略
union {
HANDLE hIcon;//取得对应文件类型的图标的Handle,当fMask不包括SEE_MASK_ICON时该项被忽略
HANDLE hMonitor;//将文档显示在显示器上的Handle,当fMask不包括SEE_MASK_HMONITOR时该项被忽略
} DUMMYUNIONNAME;
HANDLE hProcess;//指向新启动的程序的句柄。若fMask不设为SEE_MASK_NOCLOSEPROCESS则该项值为NULL。
//但若程序没有启动,即使fMask设为SEE_MASK_NOCLOSEPROCESS,该值也仍为NULL。
//如果没有新创建进程,也会为空
} SHELLEXECUTEINFO, *LPSHELLEXECUTEINFO;
可执行文件和参数都是LPCTSTR类型的,我在调用时虽然没特意进行LPCTSTR的转换,但传送char * 进去,编译运行都没有任何出错提示。
于是把参数相关的地方改了个写法:
char p[256];
strcpy(p,bzh->Text.Trim().c_str());
LPCSTR pa = (LPCSTR) p;
sei.lpParameters = pa;
这么罗嗦一圈之后,程序运行一切正常了。
总结一下,不同开发环境下,类似类型的变量之间还是有很多差异的,使用时应当加以注意,不能随便相信编译环境的默认转换。
但我遇到的问题也很解释不通,同一个函数调用,同样要求LPCSTR变量,为什么有的参数行,有的参数就不行?而且引发这么诡异的连带后果,被调程序运行正常,可以读写已经存在的文件,却失去了创建新文件的权限。