一个进程通常是一个运行的程序的实例 它由两个部分构成 一个内核对象 *** 作系统用它管理进程 并保存进程的统计信息 一个地址空间 它包含所有的可执行或DLL模块的代码和数据 也包含了动态分配的内存如栈和堆等
一个进程要能完成某些功能 它必须要有一个线程运行 该线程负责执行包含在进程地址空间听代码 一个进程可以包含多个线程 它们可以同时运行 每个线程都有自己的CPU寄存器组和自己的堆栈 当一个进程创建时 系统会自己创建它的第一个线程 称为基本线程(primary thread) 这个线程可以创建另外的线程 如果进程地址空间中没有线程运行 系统自动销毁进程及其地址空间 一 编写第一个Windows程序 Windows程序分成GUI和CUI 后者虽然也包含在窗口中 但窗口只能包含文本 如CMD EXE 用vs创建程序时 链接器使用/SUBSYSTEM来决定程序类型 生成的类型信息会放在执行映像的头部 当系统加载执行映像时 它会查找这个信息 如果是控制台程序 系统会启动一个控制台窗口 而如果是GUI程序 则系统不会启动控制台窗口而只是加载该映像 一旦程序启动 系统便不会关心程序类型了 Windows程序的进入点函数有下面两个
int WINAPI _tWinMain ( HINSTANCE hInstanceExe HINSTANCE PTSTR pszCmdLine int nCmdShow )
int _tmain ( int argc TCHAR *argv[] TCHAR *envp[] )
但是 *** 作系统不会调用你写的进入点函数!它是调用C/C++启动函数 后者由链接时的 entry:XXX设置 这个函数会初始化C/C++运行库 这样你可以调用如malloc或free等 你还确保你声明的任何全局或静态C++对象在代码退出时被销毁 这些函数 WinMainCRTStartup wWinMainCRTStartup mainCRTStartup wmainCRTStartup等(加w的是宽字符版本) 这四个函数在crtexe c文件中可以找到 这些启动函数完成如下功能
取得新进程的全部命令行参数的指针
取得新进程的环境变量的指针
初始化C/C++运行时全局变量 你的代码如果包含StdLib h就可以访问这些变量 如_osver _winmajor _winminor _winver __argc __argv __wargv _environ _wenviron _pgmptr _wpgmptr等
用C进行时函数malloct和calloc和底层I/O函数初始化堆
为所有全局和静态C++类对象调用构造函数 当然这些初始化完成后便调用你的启动函数 代码如下(UNICODE版本) GetStartupInfo(&StartupInfo)int nMainRetVal = WinMain((HINSTANCE)&__ImageBase NULL pszCommandLineAnsi (StartupInfo dwFlags &STARTF_USESHOWWINDOW) ? StartupInfo wShowWindow : SW_SHOWDEFAULT)int nMainRetVal = wmain(argc argv envp)如果要在_tmain的入口访问环境变量 则_tmain应写成 int _tmain(int argc TCHAR* argv[] TCHAR* env[])
当你的进入点函数退出时 启动搭野函数调用C运行时的exit函数 它传递返回值能它并完成如下功能
调用由_onexit函数调用的任何函数
为所有全局和静态C++类对象调用析构函数
如果以DEBUG方式生成 则C/C++运行时内存的任何泄漏都会由知念喊 _CrtDumpMemoryLeaks函数列出
调用 *** 作系统的ExitProcess函数 把返回值传递给它
但是由于安全原因 这些变量都已经变成过时的 你应该调用相应的Windows API函高差数来获取这些变量
二 进程实例句柄装载到进程地址空间的每个可执行的或DLL文件都会被赋予一个唯一的实例句柄 你的可执行文件的实例是作为WinMain的第一个参数传递hInstance 该句柄值通常用于装载资源等 如 HICON LoadIcon(HINSTANCE hInstance PCTSTR pszIcon)在上面的调用中 第一个参数就表示哪个文件(哪个DLL)包含了你要加载的资源 许多程序把hInstance保存作为一个全局变量 SDK文档中有些函数需要HMODULE类型的参数 实际上它们是一样的 如 DWORD GetModuleFileName( HMODULE hInstModule PTSTR pszPath DWORD cchPath)hInstance的真实值是可执行文件映像装载的基址 vs链接器默认是 x 你可通过/BASE:address进行更改 函数GetModuleHandle可取得一个可执行或DLL文件的装载句柄 它用文件名作为参数 如果参数是NULL 则返回调用者的句柄 但如果代码中DLL中运行 要获取句柄有两种办法
使用链接器提供的__ImageBase伪变量(C运行时代码就以这种方式)
调用GetModuleHandleEx
下面是这些代码 #include<stdio h> #include<windows h> extern C const IMAGE_DOS_HEADER __ImageBase void DumpModule() { HMODULE hModule = GetModuleHandle(NULL) printf( 用GetModuleHandle(NULL) = x%x\r\n hModule) printf( 用__ImageBase = x%x\r\n (HINSTANCE)&__ImageBase) hModule = NULL GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS (PCTSTR)DumpModule &hModule) printf( 用GetModuleHandleEx = x%x\r\n hModule) } int main(int argc TCHAR* argv[]) { DumpModule() return }
上面代码可以看出 不管在哪调用GetModuleHandle 它总返回可执行文件的基址 另外 在生成的WinMain函数的第二个参数也是HINSTANCE 它是指前一程序的实例句柄 这个参数现在不应该使用
三 进程命令行当新进程创建时 它会传递一个命令行 它从来不会为空(至少有可执行文件名) 当C运行时执行GUI程序时 它是用GetCommandLine函数获取命令行参数 跳过可执行文件名 把剩下的传递给pszCommandLine 这块缓冲不应该写入任何东西 要取得命令行参数 可以使用CommandLineToArgvW函数 它在Shell dll中 声明于ShellAPI h 调用方式如下
int nNumArgsPWSTR *ppArgv = CommandLineToArgvW(GetCommandLineW() &nNumArgs) // 使用参数 if (*ppArgv[ ] == L x ) { } // 释放内存块(那个函数是在内部分配内存块的) HeapFree(GetProcessHeap() ppArgv)
四 进程的环境变量环境变量块分配于进程的地址空间中 形式如下 =::=::\ VarName =VarValue \ VarName =VarValue \ VarName =VarValue \ VarNameX=VarValueX\ \ 注意=::=::\ 有些字符串可能以=开始 所以这些字符不能用于环境变量中 有两种方式可以读取这些环境变量
用GetEnvironmentStrings函数
void DumpEnvStrings() { PTSTR pEnvBlock = GetEnvironmentStrings()
// Parse the block with the following format: // =::=::\ // = // var=value\ // // var=value\ \ // Note that some other strings might begin with = // Here is an example when the application is started from a neork share // [ ] =::=::\ // [ ] =C:=C:\Windows\System // [ ] =ExitCode= // TCHAR szName[MAX_PATH] TCHAR szValue[MAX_PATH] PTSTR pszCurrent = pEnvBlock HRESULT hr = S_OK PCTSTR pszPos = NULL int current = while (pszCurrent != NULL) { // 跳过无意义字符串如 =::=::\ if (*pszCurrent != TEXT( = )) { // 查找 = 分隔符 pszPos = _tcschr(pszCurrent TEXT( = )) // 现在指针指向值的第一个字符 pszPos++ // 复制变量名 size_t cbNameLength = // 不包含 = (size_t)pszPos (size_t)pszCurrent sizeof(TCHAR) hr = StringCbCopyN(szName MAX_PATH pszCurrent cbNameLength) if (FAILED(hr)) { break }
// Copy the variable value with the last NULL character // and allow truncation because this is for UI only hr = StringCchCopyN(szValue MAX_PATH pszPos _tcslen(pszPos)+ ) if (SUCCEEDED(hr)) { _tprintf(TEXT( [%u] %s=%s\r\n ) current szName szValue) } else // something wrong happenedcheck for truncation if (hr == STRSAFE_E_INSUFFICIENT_BUFFER) { _tprintf(TEXT( [%u] %s=%s \r\n ) current szName szValue) } else { // This should never occur _tprintf( TEXT( [%u] %s=???\r\n ) current szName ) break } } else { _tprintf(TEXT( [%u] %s\r\n ) current pszCurrent) } // Next variable please current++ // Move to the end of the string while (*pszCurrent != TEXT( \ )) pszCurrent++ pszCurrent++ // Check if it was not the last string if (*pszCurrent == TEXT( \ )) break } // 不要忘记了要释放内存 FreeEnvironmentStrings(pEnvBlock) }
第二种获取环境变量的方法只用于CUI程序 它是通过main函数的第三个参数传入的
void DumpEnvVariables(PTSTR pEnvBlock[]) { int current = PTSTR* pElement = (PTSTR*)pEnvBlock PTSTR pCurrent = NULL while (pElement != NULL) { pCurrent = if (pCurrent == NULL) { // 没有环境变量了 pElement = NULL } else { _tprintf(TEXT( [%u] %s\r\n ) current pCurrent) current++ pElement++ } } }
在环境变量的这种表示中 可以预见它是把空格计算在内的 例如下面是两个环境变量 XYZ =Home (在XYZ后面有一空格 环境变量的名字就是XYZ加一空格) XYZ=Home 当用户登录系统时 系统从注册表的两个地方获取环境 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment HKEY_CURRENT_USER\Environment 这些值可以在系统属性中进行修改 当然程序也可以通过注册表函数对这些值进行修改 一般这些修改后的值需要重新登录后才会对所有程序产生影响 但有些程序可以通过接收WM_SETTINGCHANGE消息立即获取新值 如Explorer 任务管理器和控制面板等 这种你如果更新了这些值 就可以通过下面的方式对这些程序进行通知 SendMessage(HWND_BROADCAST WM_SETTINGCHANGE (LPARAM) TEXT( Environment ))
如果您的程序还需要获取环境变量的功能 现在可以使用新函数 GetEnvironmentVariable void PrintEnvironmentVariable(PCTSTR pszVariableName) { PTSTR pszValue = NULL // 获取存储值所需要的缓冲区大小 DWORD dwResult = GetEnvironmentVariable(pszVariableName pszValue ) if (dwResult != ) { DWORD size = dwResult * sizeof(TCHAR) pszValue = (PTSTR)malloc(size) GetEnvironmentVariable(pszVariableName pszValue size) _tprintf(TEXT( %s=%s\n ) pszVariableName pszValue) free(pszValue) } else { _tprintf(TEXT( %s =<unknown value>\n ) pszVariableName) } }
如果在环境变量中包含一些其它的环境变量值 如%USERPROFILE%\Documents 这里USERPROFILE变量便是其它定义的 要获取完整的变量值(展开的变量值) 可用ExpandEnvironmentStrings函数 如 DWORD chValue = ExpandEnvironmentStrings(TEXT( PATH= %PATH% ) NULL )PTSTR pszBuffer = new TCHAR[chValue]chValue = ExpandEnvironmentStrings(TEXT( PATH= %PATH% ) pszBuffer chValue)_tprintf(TEXT( %s\r\n ) pszBuffer)delete[] pszBuffer最后要添加 删除和修改一个环境变量值 可用SetEnvironmentVariable函数
五 进程的其它一些属性
错误模式 每个进程都关联一个标识 它指示系统如何报告错误 可用函数SetErrorMode进行设置
当前驱动器和目录 每个进程也都有自己的当前驱动器和目录 它由系统跟踪 下面API可获取或设置这两个值 GetCurrentDirectory和SetCurrentDirectory
进程当前目录 系统通过环境变量跟踪进程的每个驱动器的当前目录 如 =C:=C:\Utility\Bin =D:=D:\Program Files
系统版本 原来Microsoft用GetVersion来获取版本串 后来改成GetVersionEx 现在前者几乎不用 现在是检测系统是否是Vista的代码
// 准备OSVERSIONINFOEX结构 OSVERSIONINFOEX osver = { } osver dwOSVersionInfoSize = sizeof(osver) osver dwMajorVersion = osver dwMinorVersion = osver dwPlatformId = VER_PLATFORM_WIN _NT
//准备条件掩码 DWORDLONG dwlConditionMask = // 必须初始化成 VER_SET_CONDITION(dwlConditionMask VER_MAJORVERSION VER_EQUAL)VER_SET_CONDITION(dwlConditionMask VER_MINORVERSION VER_EQUAL) VER_SET_CONDITION(dwlConditionMask VER_PLATFORMID VER_EQUAL)
//进行版本测试 if (VerifyVersionInfo(&osver VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID dwlConditionMask)) { //该版本是Vista } else { //该系统不是Vista系统 }
lishixinzhi/Article/program/net/201311/12334该程序的输出结蠢银果为:7。
程亏档码序中定义了变量销哪i、a1、a2、f和sum,其中a1和a2分别初始化为1和2。在for循环中,循环变量i从1到4,每次循环计算a1和a2的乘积,并将结果累加到sum中,然后将a1赋值为a2,将a2赋值为乘积f。因此,在循环结束时,a1的值为3,a2的值为6,sum的值为12+23+36+618=7。
最后,使用printf函数输出sum的值为7。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)