异常处理——异常函数之SetUnhandledExceptionFilter(子进程处理)(3)

1.解决思路和注意事项

1.1 Set顶层异常函数
1.2 建立函数
1.3 在函数里面createprocess子进程,参数传递pid给子进程,保存父进程错误信息结构体到两个二进制文件
1.4 子进程根据pid通过openprocess获得句柄,打开两个结构体保存的二进制文件,读入。minidumpwrite

2.创建子进程

//代码一
#include
#include
#include
using namespace std;
int main()
{
    //一些必备参数设置
    STARTUPINFO si;
    PROCESS_INFORMATION pi;//必备参数设置结束
    memset(&si, 0, sizeof(STARTUPINFO));//初始化si在内存块中的值(详见memset函数)
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOW;

    if(!CreateProcess(TEXT("c:\\windows\\system32\\notepad.exe"),NULL, NULL,NULL,FALSE,0,NULL,NULL,&si,&pi))
    {
        cout << "CreateFail!" << endl;
        exit(1);
    }
    else
    {
        cout << "Success!" << endl;
    }
    //不使用的句柄最好关掉
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    return 0;
}
//代码二
#include
#include
int main()
{
    STARTUPINFO si = { sizeof(STARTUPINFO) };//在产生子进程时,子进程的窗口相关信息
    PROCESS_INFORMATION pi;                  //子进程的ID/线程相关信息
    DWORD returnCode;//用于保存子程进的返回值;

    wchar_t commandLine1[] = L"finderrorsub.exe -l";  //测试命令行参数一
    wchar_t commandLine2[] = L"finderrorsub.exe";     //测试命令行参数二

    BOOL bRet = CreateProcess(              //调用失败,返回;调用成功返回非;
        NULL,                               //一般都是空;(另一种批处理情况:此参数指定"cmd.exe",下一个命令行参数"/c otherBatFile")
        commandLine1,                       //命令行参数        
        NULL,                               //_In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes,
        NULL,                               //_In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes,
        FALSE,                              //_In_        BOOL                  bInheritHandles,
        CREATE_NEW_CONSOLE,                 //新的进程使用新的窗口。
        NULL,                               //_In_opt_    LPVOID                lpEnvironment,
        NULL,                               //_In_opt_    LPCTSTR               lpCurrentDirectory,
        &si,                                //_In_        LPSTARTUPINFO         lpStartupInfo,
        &pi);                               //_Out_       LPPROCESS_INFORMATION lpProcessInformation

    if (bRet)
    {
        std::cout << "process is running..." << std::endl;
        //等待子进程结束
        WaitForSingleObject(pi.hProcess, -1);
        std::cout << "process is finished"  << std::endl;
        //获取子进程的返回值
        GetExitCodeProcess(pi.hProcess, &returnCode);
        std::cout << "process return code:" << returnCode << std::endl;
    }
    else
    {
        std::cout << "Create Process error!"<<std::endl;
        return 0;
    }

    getchar();
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    return 0;
}

#include

int main()
{
    std::cout << "this is subApp message" << std::endl;

    //if (argc>= 2)
    //{
        std::cout << "work... pass,the return code will be 0" << std::endl;
        getchar();
        return 0;
 /*   }
    else
    {
        std::cout << "work... failed,the return code will be 1" << std::endl;
        getchar();
        return 1;
    }*/
}

3.函数分析

3.1 CreateProcess函数

//简介一:
BOOL CreateProcess
(
LPCTSTR lpApplicationName,         //要运行的执行文件
LPTSTR lpCommandLine,           //执行文件的命令行语句
LPSECURITY_ATTRIBUTES lpProcessAttributes,   //决定是否返回的句柄可以被子进程继承,为NULL不能
LPSECURITY_ATTRIBUTES lpThreadAttributes,  //决定是否返回的指向线程的句柄可以被子进程继承
BOOL bInheritHandles,               //指示新进程是否从调用进程处继承了句柄
DWORD dwCreationFlags,          //指定附加的、用来控制优先类和进程的创建的标志
LPVOID lpEnvironment,             //指向一个新进程的环境块,如果此参数为空,新进程使用调用进程的环境
LPCTSTR lpCurrentDirectory,    //指向一个以NULL结尾的字符串,这个字符串用来指定子进程的工作路径。这个字符串必须是一个包含驱动器名的绝对路径。如果这个参数为空,新进程将使用与调用进程相同的驱动器和目录。
LPSTARTUPINFO lpStartupInfo,            //指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。
LPPROCESS_INFORMATION lpProcessInformation   //指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体。
);
//简介二:
BOOL CreateProcess
(
LPCTSTR lpApplicationName, 
LPTSTR lpCommandLine, 
LPSECURITY_ATTRIBUTES lpProcessAttributes。
LPSECURITY_ATTRIBUTES lpThreadAttributes, 
BOOL bInheritHandles, 
DWORD dwCreationFlags,
LPVOID lpEnvironment, 
LPCTSTR lpCurrentDirectory, 
LPSTARTUPINFO lpStartupInfo, 
LPPROCESS_INFORMATION lpProcessInformation 
);
2. 参数:
lpApplicationName:    //如果指定的话即不使用默认值,那么该参数一定要是要完整路径
指向一个NULL结尾的、用来指定可执行模块的字符串。
这个字符串可以使可执行模块的绝对路径,也可以是相对路径,在后一种情况下,函数使用当前驱动器和目录建立可执行模块的路径。
这个参数可以被设为NULL,在这种情况下,可执行模块的名字必须处于 lpCommandLine 参数的最前面并由空格符与后面的字符分开。
这个被指定的模块可以是一个Win32应用程序。如果适当的子系统在当前计算机上可用的话,它也可以是其他类型的模块(如MS-DOS 或 OS/2)。
在Windows NT中,如果可执行模块是一个16位的应用程序,那么这个参数应该被设置为NULL,并且因该在lpCommandLine参数中指定可执行模块的名称。16位的应用程序是以DOS虚拟机或Win32上的Windows(WOW) 为进程的方式运行。

lpCommandLine:
指向一个NULL结尾的、用来指定要运行的命令行。
这个参数可以为空,那么函数将使用参数指定的字符串当作要运行的程序的命令行。
如果lpApplicationName和lpCommandLine参数都不为空,那么lpApplicationName参数指定将要被运行的模块,lpCommandLine参数指定将被运行的模块的命令行。新运行的进程可以使用GetCommandLine函数获得整个命令行。C语言程序可以使用argc和argv参数。
先看一个简单的例子:
#include
int main(){
 STARTUPINFO si={sizeof(si)};
 PROCESS_INFORMATION pi;

 char *ZW="C:\\Windows\\System32\\notepad.exe";
 char *szCommandLine="C:\\Users\\Administrator\\Desktop\\readme.txt";
 ::CreateProcess(ZW,szCommandLine,NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi);
 return 0;
}
该程序在VC++6.0中运行的结果如下: 打开一个记事本。(其中给szCommandLine赋值语句中字符串第一个为空格跟没有空格意义不一样,还没搞明白。以后再研究)
如果lpApplicationName参数为空,那么这个字符串中的第一个被空格分隔的要素指定可执行模块名。如果文件名不包含扩展名,那么.exe将被假定为默认的扩展名。如果文件名以一个点(.)结尾且没有扩展名,或文件名中包含路径,.exe将不会被加到后面。如果文件名中不包含路径,Windows将按照如下顺序寻找这个可执行文件:
1.当前应用程序的目录。
2.父进程的目录。
3.Windows目录。可以使用GetWindowsDirectory函数获得这个目录。
4.列在PATH环境变量中的目录。
如果被创建的进程是一个以MS-DOS或16位Windows为基础的应用程序,lpCommandLine参数应该是一个以可执行文件的文件名作为第一个要素的绝对路径,因为这样做可以使32位Windows程序工作的很好,这样设置lpCommandLine参数是最强壮的。

lpProcessAttributes:
指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。如果lpProcessAttributes参数为空(NULL),那么句柄不能被继承。

lpThreadAttributes:
指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。如果lpThreadAttributes参数为空(NULL),那么句柄不能被继承。

bInheritHandles:
指示新进程是否从调用进程处继承了句柄。如果参数的值为真,调用进程中的每一个可继承的打开句柄都将被子进程继承。被继承的句柄与原进程拥有完全相同的值和访问权限。

dwCreationFlags:
指定附加的、用来控制优先类和进程的创建的标志。以下的创建标志可以以除下面列出的方式外的任何方式组合后指定。
下面是一些常用的标志.
值:CREATE_DEFAULT_ERROR_MODE
含义:新的进程不继承调用进程的错误模式。CreateProcess函数赋予新进程当前的默认错误模式作为替代。应用程序可以调用SetErrorMode函数设置当前的默认错误模式。
这个标志对于那些运行在没有硬件错误环境下的多线程外壳程序是十分有用的。
对于CreateProcess函数,默认的行为是为新进程继承调用者的错误模式。设置这个标志以改变默认的处理方式。
值:CREATE_NEW_CONSOLE
含义:新的进程将使用一个新的控制台,而不是继承父进程的控制台。这个标志不能与DETACHED_PROCESS标志一起使用。
值:CREATE_NEW_PROCESS_GROUP
含义:新进程将使一个进程树的根进程。进程树种的全部进程都是根进程的子进程。新进程树的用户标识符与这个进程的标识符是相同的,由lpProcessInformation参数返回。进程树经常使用GenerateConsoleCtrlEvent函数允许发送CTRL+C或CTRL+BREAK信号到一组控制台进程。
值:CREATE_SEPARATE_WOW_VDM
含义:(只适用于Windows NT)这个标志只有当运行一个16位的Windows应用程序时才是有效的。如果被设置,新进程将会在一个私有的虚拟DOS机(VDM)中运行。另外,默认情况下所有的16位Windows应用程序都会在同一个共享的VDM中以线程的方式运行。单独运行一个16位程序的优点是一个应用程序的崩溃只会结束这一个VDM的运行;其他那些在不同VDM中运行的程序会继续正常的运行。同样的,在不同VDM中运行的16位Windows应用程序拥有不同的输入队列,这意味着如果一个程序暂时失去响应,在独立的VDM中的应用程序能够继续获得输入。
值:CREATE_SHARED_WOW_VDM
含义:(只适用于Windows NT)这个标志只有当运行一个16位的Windows应用程序时才是有效的。如果WIN.INI中的Windows段的DefaultSeparateVDM选项被设置为真,这个标识使得CreateProcess函数越过这个选项并在共享的虚拟DOS机中运行新进程。
值:CREATE_SUSPENDED
含义:新进程的主线程会以暂停的状态被创建,直到调用ResumeThread函数被调用时才运行。
值:CREATE_UNICODE_ENVIRONMENT
含义:如果被设置,由lpEnvironment参数指定的环境块使用Unicode字符,如果为空,环境块使用ANSI字符。
值:DEBUG_PROCESS
含义:如果这个标志被设置,调用进程将被当作一个调试程序,并且新进程会被当作被调试的进程。系统把被调试程序发生的所有调试事件通知给调试器。
如果你使用这个标志创建进程,只有调用进程(调用CreateProcess函数的进程)可以调用WaitForDebugEvent函数。
值:DEBUG_ONLY_THIS_PROCESS
含义:如果此标志没有被设置且调用进程正在被调试,新进程将成为调试调用进程的调试器的另一个调试对象。如果调用进程没有被调试,有关调试的行为就不会产生。
值:DETACHED_PROCESS
含义:对于控制台进程,新进程没有访问父进程控制台的权限。新进程可以通过AllocConsole函数自己创建一个新的控制台。这个标志不可以与CREATE_NEW_CONSOLE标志一起使用。
dwCreationFlags参数还用来控制新进程的优先类,优先类用来决定此进程的线程调度的优先级。如果下面的优先级类标志都没有被指定,那么默认的优先类是NORMAL_PRIORITY_CLASS,除非被创建的进程是IDLE_PRIORITY_CLASS。在这种情况下子进程的默认优先类是IDLE_PRIORITY_CLASS。
可以下面的标志中的一个:
优先级:HIGH_PRIORITY_CLASS 
含义:指示这个进程将执行时间临界的任务,所以它必须被立即运行以保证正确。这个优先级的程序优先于正常优先级或空闲优先级的程序。一个例子是Windows任务列表,为了保证当用户调用时可以立刻响应,放弃了对系统负荷的考虑。确保在使用高优先级时应该足够谨慎,因为一个高优先级的CPU关联应用程序可以占用几乎全部的CPU可用时间。
优先级:IDLE_PRIORITY_CLASS 
含义:指示这个进程的线程只有在系统空闲时才会运行并且可以被任何高优先级的任务打断。例如屏幕保护程序。空闲优先级会被子进程继承。
优先级:NORMAL_PRIORITY_CLASS 
含义:指示这个进程没有特殊的任务调度要求。
优先级:REALTIME_PRIORITY_CLASS 
含义:指示这个进程拥有可用的最高优先级。一个拥有实时优先级的进程的线程可以打断所有其他进程线程的执行,包括正在执行重要任务的系统进程。例如,一个执行时间稍长一点的实时进程可能导致磁盘缓存不足或鼠标反映迟钝。

lpEnvironment:
指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境。
一个环境块存在于一个由以NULL结尾的字符串组成的块中,这个块也是以NULL结尾的。每个字符串都是name=value的形式。
因为相等标志被当作分隔符,所以它不能被环境变量当作变量名。
与其使用应用程序提供的环境块,不如直接把这个参数设为空,系统驱动器上的当前目录信息不会被自动传递给新创建的进程。对于这个情况的探讨和如何处理,请参见注释一节。
环境块可以包含Unicode或ANSI字符。如果lpEnvironment指向的环境块包含Unicode字符,那么dwCreationFlags字段的CREATE_UNICODE_ENVIRONMENT标志将被设置。如果块包含ANSI字符,该标志将被清空。
请注意一个ANSI环境块是由两个零字节结束的:一个是字符串的结尾,另一个用来结束这个快。一个Unicode环境块石油四个零字节结束的:两个代表字符串结束,另两个用来结束块。

lpCurrentDirectory:
指向一个以NULL结尾的字符串,这个字符串用来指定子进程的工作路径。这个字符串必须是一个包含驱动器名的绝对路径。如果这个参数为空,新进程将使用与调用进程相同的驱动器和目录。这个选项是一个需要启动启动应用程序并指定它们的驱动器和工作目录的外壳程序的主要条件。

lpStartupInfo:
指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。

lpProcessInformation:
指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体。

4.返回值:
如果函数执行成功,返回非零值。
如果函数执行失败,返回零,可以使用GetLastError函数获得错误的附加信息。

根据对进程创建的初步了解,已经可以完成 task1.1

我的程序如下:

parent工程位于F:\myproject\c++\Parent,程序如下:

#include 
#include 
#include 
int main( void )
{
     //创建子进程


STARTUPINFO si={sizeof(si)};              
 PROCESS_INFORMATION pi;
ZeroMemory( &pi, sizeof(pi) );
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);

TCHAR  chPath[]=TEXT("F:\\myproject\\c++\\Child\\Debug\\Child.exe");        //我的child.exe程序位于该目录下,作为第一

//个参数传给CreateProcess

if(CreateProcess(chPath, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
{
CloseHandle( pi.hProcess );              //hProcess跟hThread为pi的成员,分别为进程跟线程的句柄
CloseHandle( pi.hThread );
}
else 
{

printf("%s","Child process created faild!\n");

}
 SYSTEMTIME sys;
while(true){
GetLocalTime( &sys );

printf("%s","The parent is talking at ");
printf( "%4d/%02d/%02d %02d:%02d:%02d 星期%1d\n", sys.wYear,  
    sys.wMonth,  sys.wDay, sys.wHour, sys.wMinute, sys.wSecond,sys.wDayOfWeek);
Sleep(2000);

}
return 0;
}

child工程位于F:\myproject\c++\Child,程序如下:

#include 
#include 
int main( void )
{
SYSTEMTIME sys;
while(true){
GetLocalTime( &sys ); 

printf("%s","The child is talking at ");
printf( "%4d/%02d/%02d %02d:%02d:%02d 星期%1d\n", sys.wYear,  
    sys.wMonth,  sys.wDay, sys.wHour, sys.wMinute, sys.wSecond,sys.wDayOfWeek);
Sleep(2000);
}
return 0;
}

3.2 OpenProcess函数

HANDLE OpenProcess(DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwProcessId)
参数说明:
1、dwDesiredAccess:想拥有的该进程访问权限
PROCESS_ALL_ACCESS  //所有能获得的权限
PROCESS_CREATE_PROCESS  //需要创建一个进程
PROCESS_CREATE_THREAD   //需要创建一个线程
PROCESS_DUP_HANDLE      //重复使用DuplicateHandle句柄
PROCESS_QUERY_INFORMATION   //获得进程信息的权限,如它的退出代码、优先级
PROCESS_QUERY_LIMITED_INFORMATION  /*获得某些信息的权限,如果获得了PROCESS_QUERY_INFORMATION,也拥有PROCESS_QUERY_LIMITED_INFORMATION权限*/
PROCESS_SET_INFORMATION    //设置某些信息的权限,如进程优先级
PROCESS_SET_QUOTA          //设置内存限制的权限,使用SetProcessWorkingSetSize
PROCESS_SUSPEND_RESUME     //暂停或恢复进程的权限
PROCESS_TERMINATE          //终止一个进程的权限,使用TerminateProcess
PROCESS_VM_OPERATION       //操作进程内存空间的权限(可用VirtualProtectEx和WriteProcessMemory) 
PROCESS_VM_READ            //读取进程内存空间的权限,可使用ReadProcessMemory
PROCESS_VM_WRITE           //读取进程内存空间的权限,可使用WriteProcessMemory
SYNCHRONIZE                //等待进程终止

2、bInheritHandle:表示所得到的进程句柄是否可以被继承

3、dwProcessId:被打开进程的PID
4、
返回值:
如果函数调用成功将返回一个进程句柄值,否则将返回NULL
注意:在使用完所获得的进程句柄后一定要调用CloseHandle(handle)来关闭进程的句柄。
      在使用此函数时会发现不能成功获得有些系统进程的句柄,原因是没有权限。解决办法是在调用此函数前让我们的进程得到SeDebugPrivilege权限。

-------提升权限(不一定有效)
BOOL SetPrivilege()  
{  
    HANDLE hToken;   
    TOKEN_PRIVILEGES NewState;   
    LUID luidPrivilegeLUID;   

    //获取进程令牌  
    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)||!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidPrivilegeLUID))   
    {   
        printf("SetPrivilege Error\n");  
        return FALSE;   
    }   
    NewState.PrivilegeCount = 1;   
    NewState.Privileges[0].Luid = luidPrivilegeLUID;   
    NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;   
    //提示进程权限,注意该函数也可以改变线程的权限,如果hToken指向一个线程的令牌   
    if(!AdjustTokenPrivileges(hToken, FALSE, &NewState, NULL, NULL, NULL))  
    {  
        printf("AdjustTokenPrivilege Errro\n");  
        return FALSE;  
    }  
    return TRUE;  
}

4.进程分析

3.1 进程的创建过程
1、系统创建进程内核对象(PCB进程控制块)。
2、系统为新进程创建虚拟地址空间,帮将可执行文件或任何必要的DLL文件的代码和数据加载到该进程的地址空间。
3、系统为新进程的主线程创建一个线程内核对象(TCB线程控制块)。
4、通过执行C/C++运行期启动代码,该主线程开始运行。
注:在Windows环境下,尽量用多线程而不是多进程。

5.其他与进程相关的API(选读)


1、创建进程
            BOOL CreateProcess(
                    PCTSTR      psApplicationName, //可执行文件的名字
                    PTSTR      pszCommandLine, //命令行字符串
                    PSECURITY_ATTRIBUTES psaProcess,   //进程对象的安全性
                    PSECURITY_ATTRIBUTES psaThread,   //线程对象的安全性
                    BOOL      bInheritHandles, //句柄可继承性
                    DWORD      fdwCreate,   //标识符(优先级)
                    PVOID      pvEnvironment, //指向环境字符串
                    PCTSTR      pszCurDir,   //子进程当前目录
                    PSTARTUPINFO    psiStartInfo,
                    PPROCESS_INFORMATION ppiProcInfo);   //进程线程句柄及ID
2、打开进程
            HANDLE OpenProcess(
                    DWORD dwDesiredAccess, //访问安全属性
                    BOOL bInheritHandle, //继承属性
                    DWORD hProcessId);   //进程ID
            注:获取hPronessId指定的进程的内核对象的句柄
3、终止进程
(1)、主线程的进入点函数返回
(2)、进程自己终止自己
              VOID ExitProcess(
                        UINT fuExitCode); //退出代码3)、终止自身进程或其他进程
              BOOL TerminateProcess(
                      HANDLE hProcess, //进程句柄
                      UINT fuExitCode); //退出代码
三、与进程相关的API
4、获取进程的可执行文件或DLL对应的句柄
              HMODULE GetModuleHandle(
                      PCTSTR pszModule); //模块名称
              注:当参数传NULL时获取的是进程的地址空间中可执行文件的基地址。
5、获取与指定窗口关联在一起的一个进程和线程标识符
              HANDLE GetWindowThreadProcessId(
                      HWND hWnd,    //窗口句柄
                      LPDWORD lpdwProcessId); //与该窗口相关的进程ID
6、获取进程的运行时间
              Bool GetProcessTimes(
                      HANDLE hProcess,   //进程句柄
                      PFILETIME pftCreationTime, //创建时间
                      PFILETIME pftExitTime, //退出时间
                      PFILETIME pftKernelTime, //内核时间
                      PFILETIME pftUserTime); //用户时间
              注:返回的时间适用于某个进程中的所有线程(甚至已经终止运行的线程)。
7、获取当前进程的一个伪句柄
              HANDLE GetCurrentProcess();
               注:该函数获取当前进程的伪句柄,通常情况值为-1,只能标识当前进程内核对象,
                      可以复制,但不可继承。不必调用CloseHandle()函数来关闭这个句柄。
8、将进程的伪句柄转换为实句柄
              HANDLE DuplicateHandle(
                      GetCurrentProcess(),
                      GetCurrentProcess(),
                      GetCurrentProcess(),
                      &hProcess,
                      0,
                      FALSE ,
                      DUPLICATE_SAME_ACCESS);
9、获取当前进程ID
              DWORD GetCurrentProcessId();
10、获取进程优先级
              DWORD GetPriorityClass(
                   HANDLE hProcess);
11、修改进程的优先级类
              BOOL SetPriorityClass(
                     HANDLE hProcess, //进程句柄
                     DWORD fdwPriority); //相对进程优先级1:相对线程优先级
                     实时: REALTIME_PRIORITY_CLASS
                     高: HIGH_PRIORITY_CLASS
                     高于正常; ABOVE_NORMAL_PRIORITY_CLASS
                     正常: NORMAL_PRIORITY_CLASS
                     低于正常: BELOW_NORMAL_PRIORITY_CLASS
                     空闲: IDLE_PRIORITY_CLASS
              注2:只要拥有进程的句柄和足够的权限,就能够修改系统中运行的任何进程的优
                       先级类。
12、获取进程句柄计数
              BOOL GetProcessHandleCount(
                      HANDLE hProcess,   //句柄
                      PDWORD pdwHandleCount); //句柄计数
13、获取环境块
              DWORD GetEnvironmentVariable(
                      LPCTSTR lpName, //环境变量的名字
                      LPTSTR lpValue, //存放返回字符串的缓冲区
                      DWORD cchValue); //缓冲区的大小
              注:返回值为返回字符串的长度,当缓存不足时返回所需字符串的长度
14、设置环境块
              BOOL SetEnvironmentVariable(
                      LPCTSTR lpName, //环境变量的名字
                      LPCTSTR lpValue); //存放变量值字符串的缓冲区

你可能感兴趣的:(#,C++,#,Windows开发,#,软件调试,函数,异常处理,子进程,minidump)