注册本站  论坛  繁體中文

电脑技巧
手机 | MP3 | MP4 | 显卡 | 主板 | 显示器 | 光存储 | 笔记本 | 网络设备 | 移动存储 | 数码相机
键鼠 | CPU | 音箱 | GPS | 电视 | 服务器 | 投影机 | 机箱电源 | 品牌电脑 | 办公打印 |
| 网站首页 | Cisco | Windows | Linux | Java | Dotnet | Oracle | 网页设计 | 平面设计 | 安全 | 软件应用 | 电脑维修 | 办公维修 |
您现在的位置: 电脑技巧 >> Dotnet >> C# >> Dotnet正文

Windows via C/C++ —进程(一)

文章来源:blog.csdn.net 作者:黄祥东 更新时间:2008-6-28 20:39:13 【 】 【加入收藏

        一个进程通常是一个运行的程序的实例,它由两个部分构成: 1. 一个内核对象,操作系统用它管理进程,并保存进程的统计信息。 2. 一个地址空间,它包含所有的可执行或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文件中可以找到。这些启动函数完成如下功能:

        1. 取得新进程的全部命令行参数的指针

        2. 取得新进程的环境变量的指针

        3. 初始化C/C++运行时全局变量。你的代码如果包含StdLib.h就可以访问这些变量,如_osver, _winmajor, _winminor, _winver, __argc, __argv, __wargv, _environ, _wenviron, _pgmptr, _wpgmptr等。

        4. 用C进行时函数malloct和calloc和底层I/O函数初始化堆

        5. 为所有全局和静态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函数,它传递返回值能它并完成如下功能:

        1. 调用由_onexit函数调用的任何函数

        2. 为所有全局和静态C++类对象调用析构函数

        3. 如果以DEBUG方式生成,则C/C++运行时内存的任何泄漏都会由 _CrtDumpMemoryLeaks函数列出

        4. 调用操作系统的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链接器默认是0x00400000,你可通过/BASE:address进行更改。函数GetModuleHandle可取得一个可执行或DLL文件的装载句柄,它用文件名作为参数,如果参数是NULL,则返回调用者的句柄。但如果代码中DLL中运行,要获取句柄有两种办法:

        1. 使用链接器提供的__ImageBase伪变量(C运行时代码就以这种方式)

        2. 调用GetModuleHandleEx

        下面是这些代码:

         #include<stdio.h>
        #include<windows.h>
        extern "C" const IMAGE_DOS_HEADER __ImageBase;
        void DumpModule()
        {
             HMODULE hModule = GetModuleHandle(NULL);
             printf("用GetModuleHandle(NULL) = 0x%x\r\n", hModule);
                 printf("用__ImageBase = 0x%x\r\n", (HINSTANCE)&__ImageBase);
                 hModule = NULL;
             GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
                 (PCTSTR)DumpModule, &hModule);
             printf("用GetModuleHandleEx = 0x%x\r\n", hModule);
        }
        int main(int argc, TCHAR* argv[])
        {
             DumpModule();
             return 0;
        }

        上面代码可以看出,不管在哪调用GetModuleHandle,它总返回可执行文件的基址。另外,在生成的WinMain函数的第二个参数也是HINSTANCE,它是指前一程序的实例句柄,这个参数现在不应该使用。

        三. 进程命令行当新进程创建时,它会传递一个命令行。它从来不会为空(至少有可执行文件名)。当C运行时执行GUI程序时,它是用GetCommandLine函数获取命令行参数, 跳过可执行文件名,把剩下的传递给pszCommandLine。这块缓冲不应该写入任何东西。要取得命令行参数,可以使用CommandLineToArgvW函数,它在Shell32.dll中,声明于ShellAPI.h。调用方式如下:

         int nNumArgs; PWSTR *ppArgv = CommandLineToArgvW(GetCommandLineW(), &nNumArgs);
        // 使用参数 if (*ppArgv[1] == L'x')
        {
            ...
        }
        // 释放内存块(那个函数是在内部分配内存块的) HeapFree(GetProcessHeap(), 0, ppArgv);

        四. 进程的环境变量环境变量块分配于进程的地址空间中, 形式如下: =::=::\ ... VarName1=VarValue1\0 VarName2=VarValue2\0 VarName3=VarValue3\0 ... VarNameX=VarValueX\0 \0 注意=::=::\,有些字符串可能以=开始,所以这些字符不能用于环境变量中。有两种方式可以读取这些环境变量:

        1. 用GetEnvironmentStrings函数:

        void DumpEnvStrings()
        {
            PTSTR pEnvBlock = GetEnvironmentStrings();

            // Parse the block with the following format:
            //    =::=::\
            //    =...
            //    var=value\0
            //    ...
            //    var=value\0\0
            // Note that some other strings might begin with '='.
            // Here is an example when the application is started from a network share.
            //    [0] =::=::\
            //    [1] =C:=C:\Windows\System32
            //    [2] =ExitCode=00000000
            //    TCHAR szName[MAX_PATH];
            TCHAR szValue[MAX_PATH];
            PTSTR pszCurrent = pEnvBlock;
            HRESULT hr = S_OK;
            PCTSTR pszPos = NULL;
            int current = 0;
            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)+1);
                  if (SUCCEEDED(hr))
                  {
                     _tprintf(TEXT("[%u] %s=%s\r\n"), current, szName, szValue);
                  }
                 else
                 // something wrong happened; check 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('\0'))
                  pszCurrent++;
               pszCurrent++;
              // Check if it was not the last string.
               if (*pszCurrent == TEXT('\0'))
                  break;
            };
           // 不要忘记了要释放内存
            FreeEnvironmentStrings(pEnvBlock);
        }

        第二种获取环境变量的方法只用于CUI程序,它是通过main函数的第三个参数传入的。

         void DumpEnvVariables(PTSTR pEnvBlock[])
        {
            int current = 0;
            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, 0, (LPARAM) TEXT("Environment"));

        如果您的程序还需要获取环境变量的功能,现在可以使用新函数:

         GetEnvironmentVariable void PrintEnvironmentVariable(PCTSTR pszVariableName)
         {
            PTSTR pszValue = NULL;
            // 获取存储值所需要的缓冲区大小
            DWORD dwResult = GetEnvironmentVariable(pszVariableName, pszValue, 0);
           if (dwResult != 0)
         {
               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, 0); PTSTR pszBuffer = new TCHAR[chValue]; chValue = ExpandEnvironmentStrings(TEXT("PATH='%PATH%'"), pszBuffer, chValue); _tprintf(TEXT("%s\r\n"), pszBuffer); delete[] pszBuffer; 最后要添加,删除和修改一个环境变量值,可用SetEnvironmentVariable函数。

        五. 进程的其它一些属性

        1. 错误模式:每个进程都关联一个标识,它指示系统如何报告错误。可用函数SetErrorMode进行设置。

        2. 当前驱动器和目录:每个进程也都有自己的当前驱动器和目录,它由系统跟踪,下面API可获取或设置这两个值  GetCurrentDirectory和SetCurrentDirectory

        3. 进程当前目录:系统通过环境变量跟踪进程的每个驱动器的当前目录。如 =C:=C:\Utility\Bin =D:=D:\Program Files

        4. 系统版本:原来Microsoft用GetVersion来获取版本串,后来改成GetVersionEx。现在前者几乎不用。现在是检测系统是否是Vista的代码:

        // 准备OSVERSIONINFOEX结构 OSVERSIONINFOEX osver = { 0 };
         osver.dwOSVersionInfoSize = sizeof(osver);
         osver.dwMajorVersion = 6;
         osver.dwMinorVersion = 0;
         osver.dwPlatformId = VER_PLATFORM_WIN32_NT;

        //准备条件掩码 DWORDLONG dwlConditionMask = 0;
        // 必须初始化成0 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系统
         }

  • 上一篇Dotnet:

  • 下一篇Dotnet: 没有了
  • 最 新 热 门
     web.config配置文件中的 元素
     为网站添加业务层
     用SqlDataSource实现DataList嵌套DataList
     Visual Studio 2003插件的编写
     千条DOS命令收藏
     IIS 常见问题
     IIS需要的最小NTFS权限
     优化 .NET的性能
     设计模式与VB .net代码 外观模式,合成模式
     VB.net中介者模式
    最 新 推 荐
     Windows via C/C++ —进程(一)
     C#邮件发送程序
     扩展 ASP.NET 的客户端验证
     实现DataGridView中行的上下移动
     C#中的委托和事件
     与IDE相关的Attribute属性
     C#中using关键字的使用介绍
     C#FileStream复制大文件
     C#实用技巧:轻松实现对文件的操作
     C#实现所有经典排序算法
    相 关 文 章

    vb.net中用GetPrivateProfileString访问INI…
    LinkButton的数据绑定问题
    Win32 DLL的一个调试心得
    win 2003 server的一些优化设置
    Page_Load Page_Init方法使用原理
    C#中using关键字的使用介绍
    C# String StringBuider 解惑
    .Net Framework下安装Windows服务
    VB.net(Win)录入验证的办法
    VC.NET制作Windows游戏窗口

    | 设为首页 | 加入收藏 | 联系站长 | 友情链接 | 版权申明 | 网站公告

     

    Copyright 2006-2008 pcjx.com All Rights Reserved
    电脑技巧 版权所有 粤ICP备06059145号 地图
    本网站所有内容未经许可不得转载或做其他使用