关于CreateProcess 函数的两种版本问题
2011-05-16 14:10
288 查看
以下为收集资料仅供方便查看之用:
vs05里面 项目默认定义了
UNICODE 和 _UNICODE宏
在项目属性 -> C/C++ -> 预处理器 -> 预处理器定义里可以看到有从项目默认继承的UNICODE 和 _UNICODE宏
这导致 CreateProcess 调用的是 CreateProcessW
vc6里面 项目默认没有定义 UNICODE 和 _UNICODE宏
所以 CreateProcess 调用的是 CreateProcessA
而 CreateProcess 的第2个参数 lpCommandLine MSDN有如下说明:
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter
is a constant string, the function may cause an access violation.
CreateProcessW.会改变这个字符串的内容.因此,这个参数不能是一个指向只读内存的变量或者是字符串字面值.如果这个参数是一个常量字符串,函数有可能会造成存取违规.
LP 是 long pointer 的意思 (在32位机中 不再区分near pointer 和 long pointer) 就是说 是一个指针
C 是 constant 的意思 就是说是一个常量
W 是 Wide character的意思 表明是一个宽字符
STR 是 string 的意思 表示是一个字符串
T 是 Tchar 的意思 表示该类型会根据是否定义 _UNICODE UNICODE 被替换为 非W或者W类型
CreateProceesA 第2参数类型是 LPSTR 就是说一个指向 单字节字符串的指针 (现在Windows版本下 原型就是是 char *)
CreateProceesW 第2个参数类型是 LPWSTR 就是说是一个指向宽字节字符串的指针 (原型 wchar_t *)
CreateProcees 第2个参数类型是 LPTSTR 就是说 会根据 U宏 决定是 LPSTR 还是 LPTSTR (原型TCHAR*)
就是说 该函数将第2个参数视为可读写的 该函数不保证该字符串内容不被修改
而CreateProceesW 确实修改了其内容.... (关于如何修改的... 有个实验 呆会说....)
MessageBoxA 跟字符串有关的参数 都是 LPCSTR ( const char*)
MessageBoxW LPCWSTR (const wchar_t*)
MessageBox LPCTSTR (const TCHAR* )
该函数将这2个参数视为不可读写的 并且保证不会修改其内容
所以用 MessageBox(NULL,TEXT("Text"),TEXT("Caption"),0); 就不会有问题
最下面有一段代码将用一个线程 不断监视传给CreateProcess的第2个参数的内容
对CreateProcess的第2个参数 传入的是
TCHAR szCmd[] = TEXT("this is a cmd line");
首先是W版
以下是程序的输出
(每次输出内容都不一定相同 因为主线程和监视线程的调度情况不可预知 这次输出是最能反应实质的输出)
116 104 105 115 0 105 115 32 97
32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 0 97
32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97
0 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100
0 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100
32 108 105 110 请按任意键继续. ..
联想到什么了吗?
传入的参数类容是 TEXT("this is a cmd line")
't'=116
'h'=104
'i'=105
's'=115
' '=32 (空格)
...
也就是说 在CreatProcessW执行过程中
监视线程执行了5次 并且检测到 "this is a cmd line" 中的4个空格被分别替换成了 '/0'=0
再联想 cmdline 被认作是一种以空格分隔的(space-delimited ) 不定项目的字符串
所以 CreatProcessW对第2个参数干的事情就是 依次将空格替换成 '/0'(字符串终结符)
this is a cmd line
在5次监视线程插足的时候CreatProcessW将第2参数分别被当作
this
this is
this is a
this is a cmd
this is a cmd line
5个字符串处理
当然 它们都不是合法的应用程序名称
(第1参数为NULL时 将把第2参数的第一个空格前——即是第一个项目 认作应用程序名称)
所以在本例中CreateProcessW 最终会失败
但是也反应出了问题本质~~
虽然函数执行完毕后 会恢复第2参数的内容 但是它曾经是被修改过的! 所以不能传入只读存储区地址!
如果去掉UNICODE _UNICODE宏定义 重新编译 程序输出如下
(同样 只是多次输出中的一次)
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101 101 请按任意键
继续. . .
可以看出 CreateProcessA 没有对第2参数进行修改
以下是代码
#include <windows.h>
#include <process.h>
#include <tchar.h>
#include <stdio.h>
typedef struct tagUserData
{
const TCHAR * pctstr;
int len;
} UserData;
unsigned int __stdcall Monitor(void *pvParam);
int main()
{
STARTUPINFO si;
ZeroMemory(&si,sizeof(si));
si.cb = sizeof(si);
//和楼主代码有点区别
//照理说应该在ZeroMemory之后
//设置 cb 为该结构体的大小
PROCESS_INFORMATION pi;
ZeroMemory(&pi,sizeof(pi));
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = TRUE;
TCHAR szCmd[] = TEXT("this is a cmd line");
UserData param;
param.pctstr = szCmd;
param.len = static_cast<int>( _tcslen(szCmd) );
// _tcslen 是 strlen 的"T" 版本
// 建立监视线程
HANDLE hThread =
reinterpret_cast<HANDLE> ( _beginthreadex(NULL,0,Monitor,static_cast<void*>(& param),0,NULL) );
//调高其优先权 利于监视
SetThreadPriority(hThread,THREAD_PRIORITY_HIGHEST);
BOOL bRet = ::CreateProcess (
NULL,
szCmd,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi);
return 0;
}
unsigned int __stdcall Monitor(void *pvParam)
{
for (;;)
{
UserData *pData = static_cast<UserData*>(pvParam);
for (int i=0;i<pData->len;++i)
_tprintf(TEXT("%d "),pData->pctstr[ i ] );
_tprintf(TEXT("/n"));
}
return 0;
}
转自:http://hi.baidu.com/%C9%D9%C4%EA%B0%FC%C7%E0%CD%DC/blog/item/65098f10d57baf0a203f2e0a.html
vs05里面 项目默认定义了
UNICODE 和 _UNICODE宏
在项目属性 -> C/C++ -> 预处理器 -> 预处理器定义里可以看到有从项目默认继承的UNICODE 和 _UNICODE宏
这导致 CreateProcess 调用的是 CreateProcessW
vc6里面 项目默认没有定义 UNICODE 和 _UNICODE宏
所以 CreateProcess 调用的是 CreateProcessA
而 CreateProcess 的第2个参数 lpCommandLine MSDN有如下说明:
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter
is a constant string, the function may cause an access violation.
CreateProcessW.会改变这个字符串的内容.因此,这个参数不能是一个指向只读内存的变量或者是字符串字面值.如果这个参数是一个常量字符串,函数有可能会造成存取违规.
LP 是 long pointer 的意思 (在32位机中 不再区分near pointer 和 long pointer) 就是说 是一个指针
C 是 constant 的意思 就是说是一个常量
W 是 Wide character的意思 表明是一个宽字符
STR 是 string 的意思 表示是一个字符串
T 是 Tchar 的意思 表示该类型会根据是否定义 _UNICODE UNICODE 被替换为 非W或者W类型
CreateProceesA 第2参数类型是 LPSTR 就是说一个指向 单字节字符串的指针 (现在Windows版本下 原型就是是 char *)
CreateProceesW 第2个参数类型是 LPWSTR 就是说是一个指向宽字节字符串的指针 (原型 wchar_t *)
CreateProcees 第2个参数类型是 LPTSTR 就是说 会根据 U宏 决定是 LPSTR 还是 LPTSTR (原型TCHAR*)
就是说 该函数将第2个参数视为可读写的 该函数不保证该字符串内容不被修改
而CreateProceesW 确实修改了其内容.... (关于如何修改的... 有个实验 呆会说....)
MessageBoxA 跟字符串有关的参数 都是 LPCSTR ( const char*)
MessageBoxW LPCWSTR (const wchar_t*)
MessageBox LPCTSTR (const TCHAR* )
该函数将这2个参数视为不可读写的 并且保证不会修改其内容
所以用 MessageBox(NULL,TEXT("Text"),TEXT("Caption"),0); 就不会有问题
最下面有一段代码将用一个线程 不断监视传给CreateProcess的第2个参数的内容
对CreateProcess的第2个参数 传入的是
TCHAR szCmd[] = TEXT("this is a cmd line");
首先是W版
以下是程序的输出
(每次输出内容都不一定相同 因为主线程和监视线程的调度情况不可预知 这次输出是最能反应实质的输出)
116 104 105 115 0 105 115 32 97
32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 0 97
32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97
0 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100
0 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100
32 108 105 110 请按任意键继续. ..
联想到什么了吗?
传入的参数类容是 TEXT("this is a cmd line")
't'=116
'h'=104
'i'=105
's'=115
' '=32 (空格)
...
也就是说 在CreatProcessW执行过程中
监视线程执行了5次 并且检测到 "this is a cmd line" 中的4个空格被分别替换成了 '/0'=0
再联想 cmdline 被认作是一种以空格分隔的(space-delimited ) 不定项目的字符串
所以 CreatProcessW对第2个参数干的事情就是 依次将空格替换成 '/0'(字符串终结符)
this is a cmd line
在5次监视线程插足的时候CreatProcessW将第2参数分别被当作
this
this is
this is a
this is a cmd
this is a cmd line
5个字符串处理
当然 它们都不是合法的应用程序名称
(第1参数为NULL时 将把第2参数的第一个空格前——即是第一个项目 认作应用程序名称)
所以在本例中CreateProcessW 最终会失败
但是也反应出了问题本质~~
虽然函数执行完毕后 会恢复第2参数的内容 但是它曾经是被修改过的! 所以不能传入只读存储区地址!
如果去掉UNICODE _UNICODE宏定义 重新编译 程序输出如下
(同样 只是多次输出中的一次)
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101
116 104 105 115 32 105 115 32 97 32 99 109 100 32 108 105 110 101 101 请按任意键
继续. . .
可以看出 CreateProcessA 没有对第2参数进行修改
以下是代码
#include <windows.h>
#include <process.h>
#include <tchar.h>
#include <stdio.h>
typedef struct tagUserData
{
const TCHAR * pctstr;
int len;
} UserData;
unsigned int __stdcall Monitor(void *pvParam);
int main()
{
STARTUPINFO si;
ZeroMemory(&si,sizeof(si));
si.cb = sizeof(si);
//和楼主代码有点区别
//照理说应该在ZeroMemory之后
//设置 cb 为该结构体的大小
PROCESS_INFORMATION pi;
ZeroMemory(&pi,sizeof(pi));
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = TRUE;
TCHAR szCmd[] = TEXT("this is a cmd line");
UserData param;
param.pctstr = szCmd;
param.len = static_cast<int>( _tcslen(szCmd) );
// _tcslen 是 strlen 的"T" 版本
// 建立监视线程
HANDLE hThread =
reinterpret_cast<HANDLE> ( _beginthreadex(NULL,0,Monitor,static_cast<void*>(& param),0,NULL) );
//调高其优先权 利于监视
SetThreadPriority(hThread,THREAD_PRIORITY_HIGHEST);
BOOL bRet = ::CreateProcess (
NULL,
szCmd,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi);
return 0;
}
unsigned int __stdcall Monitor(void *pvParam)
{
for (;;)
{
UserData *pData = static_cast<UserData*>(pvParam);
for (int i=0;i<pData->len;++i)
_tprintf(TEXT("%d "),pData->pctstr[ i ] );
_tprintf(TEXT("/n"));
}
return 0;
}
转自:http://hi.baidu.com/%C9%D9%C4%EA%B0%FC%C7%E0%CD%DC/blog/item/65098f10d57baf0a203f2e0a.html
相关文章推荐
- 关于.net平台序列化函数相关的问题
- 关于SVN版本冲突问题
- 关于Android7.0版本及其以上系统获取本地数据(图片和文件)报系统出现异常,正在退出问题
- 关于函数指针的一个问题
- 关于Tomcat版本更换时,安装不成功的问题解决方法
- 关于fork函数返回值的问题
- 关于static修饰一个类中的成员函数的问题
- 关于android版本兼容的一些问题
- 关于release版本和Debug 版本的问题 终结
- 关于各种型号单片机delay函数不起作用问题的解决方法
- 关于arcgis版本问题的讲解例如CPG
- qt- thread —— 关于槽函数所在线程的问题
- 关于overridePendingTransition效果在1.6版本中会出现VerifyError的问题的解决
- jQuery1.9+ 废弃的函数和方法 升级Jquery版本遇到的问题
- 【标题党】记一个关于Redis-4.0.1版本下zslGetElementByRank函数的诡异问题
- 关于编写函数比较两个整数的大小的问题
- 关于水晶报表的版本问题!
- 关于SDE的版本问题总结
- 解决关于低版本的easyui (1.3.1及以下)框架 select 下拉框会自动校验的问题
- Windows入门基础:1.关于CreateWindow()函数使用中遇到的问题