熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

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

2022-06-13   來源: .NET編程 

  一個進程通常是一個運行的程序的實例它由兩個部分構成 一個內核對象操作系統用它管理進程並保存進程的統計信息 一個地址空間它包含所有的可執行或DLL模塊的代碼和數據也包含了動態分配的內存如棧和堆等

  一個進程要能完成某些功能它必須要有一個線程運行該線程負責執行包含在進程地址空間聽代碼一個進程可以包含多個線程它們可以同時運行每個線程都有自己的CPU寄存器組和自己的堆棧當一個進程創建時系統會自己創建它的第一個線程稱為基本線程(primary thread)這個線程可以創建另外的線程如果進程地址空間中沒有線程運行系統自動銷毀進程及其地址空間 編寫第一個Windows程序 Windows程序分成GUI和CUI後者雖然也包含在窗口中但窗口只能包含文本如CMDEXE用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++對象在代碼退出時被銷毀這些函數WinMainCRTStartupwWinMainCRTStartupmainCRTStartup wmainCRTStartup等(加w的是寬字符版本)這四個函數在crtexec文件中可以找到這些啟動函數完成如下功能

   取得新進程的全部命令行參數的指針

   取得新進程的環境變量的指針

   初始化C/C++運行時全局變量你的代碼如果包含StdLibh就可以訪問這些變量如_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    (StartupInfodwFlags & STARTF_USESHOWWINDOW)       ? StartupInfowShowWindow : 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<stdioh>
        #include<windowsh>
        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函數它在Shelldll中聲明於ShellAPIh調用方式如下

            int nNumArgs; PWSTR *ppArgv = CommandLineToArgvW(GetCommandLineW() &nNumArgs);
        // 使用參數 if (*ppArgv[] == Lx)
        {
           
        }
        // 釋放內存塊(那個函數是在內部分配內存塊的) 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 network 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 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(\))
                  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 = { };
         osverdwOSVersionInfoSize = sizeof(osver);
         osverdwMajorVersion = ;
         osverdwMinorVersion = ;
         osverdwPlatformId = 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系統
         }


From:http://tw.wingwit.com/Article/program/net/201311/12334.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.