Windows via CC++ —进程(一)

Windows via CC++ —进程(一),第1张

一个进程通常是一个运行的程序的实例 它由两个部分构成 一个内核对象 *** 作系统用它管理进程 并保存进程的统计信息 一个地址空间 它包含所有的可执行或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。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12367131.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-24
下一篇 2023-05-24

发表评论

登录后才能评论

评论列表(0条)

保存