第4章プロセス
- 一般に、プログラムの実行中のインスタンスのプロセスとして定義され、それは2つの部分から構成:カーネルアドレス空間とオブジェクト。
- 単一CPUコンピュータ、オペレーティングシステム上で、ポーリングモードに割り当てられたスロット内の個々のスレッド。
4.1あなたの最初のWindowsアプリケーションを作成
- Windowsは、アプリケーションの2種類のサポート:GUIプログラム(グラフィカル・ユーザー・インターフェース)プログラムとCUI(コンソールユーザーインターフェイス)。
- VSを使用してプロジェクトを作成する場合は、統合開発環境は、接続スイッチを設定し、接続スイッチGUIプログラムは、/ SUBSYSTEMです:CONSOLEは、CUIの手順は/ SUBSYSTEM:WINDOWS; VS間違った選択項目のタイプを使用してプロジェクトを作成する場合は、することができプロジェクトのプロパティ項目の種類を変更するスイッチの接続を変更することにより、
- Windowsアプリケーションは、エントリポイント関数を持っている必要があり、プログラムが実行を開始し、この関数が呼び出され、C / C ++プログラマは、以下の2つのエントリポイント関数を使用することができます。
int WINAPI _tWinMain(
HINSTANCE hInstanceExe, //可执行文件的实例,实际值是一个内存基地址
HINSTANCE, //无参数名,编译器不会发出“参数没有被引用到”警告
PTSTR,
int nCmdShow);
int _tmain(
int argc,
TCHAR *argv[],
TCHAR *envp[]);
アプリケーションの種類と対応するエントリの機能:
アプリケーションタイプ | エントリポイント関数(入口) | スタート機能は、実行可能ファイルを組み込み |
ANSI文字列を処理するGUIアプリケーション | _tWinMain(WinMain関数) | WinMainCRTStartup |
Unicode文字列を処理するGUIアプリケーション | _tWinMain(wWinMain) | wWinMainCRTStartup |
CUIのANSI文字列処理アプリケーション | _tmain(メイン) | mainCRTStartup |
Unicode文字列を処理するCUIアプリケーション | _tmain(WMain) | wmainCRTStartup |
単純にC / C ++ランタイムライブラリ関数は次のように要約起動を使用します。
- 完全なコマンドラインの新プロセスへのポインタを取得します。
- プロセスの新しい環境変数にポイントへのポインタを取得します。
- それはSTDLIB.Hが含まれている場合は、グローバル変数C / C ++ランタイムライブラリを初期化し、我々のコードは、これらの変数にアクセスすることができ、次のように、変数が要約されています。
変数名 | タイプ | 説明とWindowsの機能を使用することをお勧めします |
_osver | unsigned int型 | OSのビルド(ビルド)のバージョンは、GetVersionExを使用します |
_winmajor | unsigned int型 | Windowsシステムの16進表現でのメジャーバージョン番号、GetVersionExを使用します |
_winminor | unsigned int型 | Windowsシステムの16進表現でのマイナーバージョン番号、GetVersionExを使用します |
_winver | unsigned int型 | (_winmajor << 8)+ _winmajor、请换用GetVersionEx |
_argc | unsigned int型 | コマンドラインに渡されたパラメータの数は、GetCommandLineを使用します |
_argv _wargv |
CHAR wchar_t型 |
アレイ_argcの長さ、GetCommandLine使用 |
_environ _wenviron |
CHAR wchar_t型 |
ポインタの配列、またはスワップGetEnvironmentStrings GetEnvironmentVarible |
_pgmptr _wpgmptr |
CHAR wchar_t型 |
名前やプログラムを実行しているANSI / Unicodeのフル・パス、GetModuleFileNameはを使用します |
- ヒープ(ヒープ)を用いCランタイムメモリ割り当て函数(mallocとのcalloc)および他の低レベルのI / Oルーチンを初期化します。
- C ++クラスオブジェクトのすべてのグローバルおよび静的コンストラクタを呼び出します。
- これらの初期化が完了すると、C / C ++関数は、アプリケーションの開始エントリポイント関数を呼び出し
4.1.1プロセスのインスタンスハンドル
- 実行可能ファイルまたはDLLプロセスのアドレス空間にロードされ、それぞれが独自のインスタンスハンドルを与えられています。
- HINSTANCE HMODULEとまったく同じ、16ビットWindowsでは、異なるタイプのデータを言いました。
//获取可执行文件/DLL文件被加载到进程地址空间的位置,函数返回一个句柄/基地址
HMODULE GetModuleHandle(PCTSTR pszModule);
4.1.2プロセスハンドルの例
C / C ++ランタイムライブラリのスタートアップコードは常にhPrevInstanceパラメータWinMain関数、および16ビットのWindowsシステム用のパラメータ(w)は、このパラメータは、あなたのコードで参照されていないにNULLを渡します。
4.1.3プロセスのコマンドライン
プロセスで新しいシステムを作成する場合は、コマンドラインはほとんど常に非空で、それにコマンドラインを通過します。
環境変数4.1.4プロセス
//每个进程都有环境块,环境块是在进程地址空间内分配的一块内存
=::=::\ ...
VarName1 = VarValue2\0 //环境变量的名称 = 赋予此变量的值
VarName2 = VarValue1\0
VarName3 = VarValue3\0 ...
VarNameX = VarValueX\0
\0
- メモリブロックGetEnvironmentStringsがリターンを機能したときにコールGetEnvironmentStringsは、もはや必要とされ、完全な環境のブロックを得るために機能しない、FreeEnvironmentStringsはそれを解放する関数と呼ばれるべきです。
BOOL FreeEnvironmentStrings(PTSTR pszEnvironmentBlock);
- メインアプリケーションのエントリポイントで受信されたTCHARは* ENV []取得した環境パラメータブロック(のみCUIプログラム):スペースは、等号が考慮されるスペースの前後に、重要です。
- 次のように更新されたレジストリエントリ、およびアプリケーションは、すぐに自分の環境ブロックを更新するには、呼び出すことができる場合:
SendMessage(HWND _BROADCASR,WM_SETTINGCHANGE,0,(LPARAM) TEXT("Environment"));
//成功找到变量名,返回字符数;未找到就返回0;
DWORD GetEnvironmentVariable(
PCTSTR pszName, //指向预期的变量名称
PTSTR pszValue, //指向保存变量值的缓冲区
DWORD cchValue); //指出缓冲区大小(字符数),可传入0
- %のUSERPROFILEの%\ドキュメントした後、コンテンツは二つの「交換可能な文字列」、ここで環境変数の値のパーセントと文字列置換を実行する間にある:文字列は内部の「交換可能な文字列」の数を持っている、と、拡張された文字列を生成します。
//Windows提供了相应的函数,返回值是保存扩展字符串所需的缓冲区大小
//如果参数chSize小于返回值,%%变量不会扩展,会被替换成空字符串
//通常要2次调用ExpandEnvironmentStrings函数
DWORD ExpandEnvironmentStrings(
PTCSTR pszSrc, //"可替换环境变量字符串"的字符串地址
PTSTR pszSrc, //接收扩展字符串的缓冲区地址
DWORD chSize); //缓冲区的最大大小(用字符数来表示)
//可添加、删除、修改一个变量的值
//如果指定的变量不存在就创建它,如果pszValue为NULL则从环境快中删除该变量
BOOL SetEnvironmentVarible(
PCTSTR pszName, //标识一个变量
PCTSTR pszValue); //想要修改成的值
4.1.5関連付けプロセス
いずれかのCPUホストスレッドで実行できる処理スレッドが利用可能なCPUのサブセット上で実行するように強制することができ、「相関処理」と呼ばれます。
エラーモード4.1.6プロセス
//进程调用该函数来告诉系统如何处理错误
UINT SetErrorMode(UINT fuErrorMode);
4.1.7プロセスは、現在のドライブとディレクトリを常駐します
//线程调用以下函数来获取和设置其所在进程的当前驱动器和目录
DWORD GetCurrentDirectory(DWORD cchCurDir,PTSTR pszCurDir);
BOOL SetCurrentDirectory(PCTSTR pszCurDir);
4.1.8プロセスのカレントディレクトリ
- Windowsのファイル機能は、ドライブ文字の環境変数を追加または変更しない、彼らはただ、この変数を読んでいます。
- 現在のディレクトリを取得するためにGetFullPathNameプロセスを呼び出します。
システムバージョン4.1.9
OSVERSIONINFOEX構造。
//能比较主机系统的版本和应用程序要求的版本
BOOL VerifyVersionInfo(
POSVERSIONINFOEX pVersionInformation,
DWORD dwTypeMask,
DWORDLONG dwlConditionMask); //一个64位的值,决定着比较方式
4.2 CreateProcess関数
//创建一个进程,它注意不到任何初始化问题
BOOL CreateProcess(
PCTSTR pszApplicationName, //新进程要使用的可执行文件名称
PTSTR pszCommandLine, //传给新进程的命令行字符串
PSECURITY_ATTRIBUTES psaProcess, //
PSECURITY_ATTRIBUTES psaThread,
BOOL bInheritHandles, //新进程对内核对象的继承
DWORD fdwCreate, //标识影响新进程创建方式的标志
PVOID pvEnvironment, //指向一块内存地址
PCTSTR pszCurDir,
PSTARTUPINFO psiStartInfo,
pPROCESS_INFORMATION ppiProcInfo);
4.2.1 pszApplicationNameパラメータとpszCommandLine
4.2.2 psaProcess、psaThreadパラメータとbInheritHandles
- 新しいプロセスを作成するには、システムが作成する必要があり、プロセスのカーネルオブジェクトやスレッドカーネルオブジェクト(プロセスのメインスレッドを)。
- 割り当てられた2つのSECURITY_ATTRIBUTES構造、セキュリティ権限を作成するために、オブジェクトハンドルの継承。
4.2.3 fdwCreateパラメータ
パラメータとしてfdwCreate使用可能フラグは、優先クラス(優先クラス)を指定することを可能にします。
DEBUG_PROCESS | 親プロセスの子プロセスをデバッグしたいと子プロセスがすべてのプロセスを生成し、イベントの任意の子プロセスの発生、あなたは親プロセスに通知する必要があります |
DEBUG_ONLY_PROCESS | 唯一のイベントの最近の発生、子プロセスを親プロセスの子プロセスをデバッグ、それは親プロセスに通知します |
CREATE_SUSPENDED | メインスレッドがハングアップすると同時に、新しいプロセスを作成します。 |
DETACHED_PROCESS | CUI親プロセスのコンソールウィンドウにアクセスするプロセスを停止し、新しいコンソールウィンドウに出力を送信します |
CREATE_NEW_CONSOLE | 新しいプロセスのための新しいコンソールウィンドウを作成します。 |
CREATE_NO_WINDOW | アプリケーションのための任意のコンソールウィンドウを作成しないでください |
CREATE_NEW_PROCESS_GROUP | 新しいプロセスグループを作成し、ときブレイク+ +リストを通知を受けるために、ユーザを押すCtrlキーCまたはCtrlプロセスを変更 |
CREATE_DEFAULT_ERROR_MODE | 新しいプロセスは、親プロセスで使用されるエラーモード(SetErrorMode関数)を継承しません。 |
CREATE_SEPARATE_WOW_VDM | 16ユニークなシステムは、別々のVDMで別の仮想DOSマシン(仮想DOSマシン、VDM)、各ランニングを作成します |
CREATE_SHARED_WOW_VDM | すべてのアプリケーションが共有VDMで実行する16のユニークなシステムで、 |
CREATE_UNICODE_ENVIRONMENT | フラグは、プロセスの環境ブロックは、デフォルトのANSI文字列が含まれ、システムのサブプロセスの環境ブロックがUnicode文字を含む伝えます |
CREATE_FORCEDOS | MS-DOSアプリケーションでOS / 2アプリケーションに埋め込まれた16ビットを実行することを余儀なく |
CREATE_BREAKAWAY_FROM_JOB | これは、プロセスは、ジョブとジョブ・独立したプロセスを生成することができます |
EXTENDED_STARTUPINFO_PRESENT | パラメータはpsiStartInfo構造を通過していることをシステムにSTARTUPINFOEX |
4.2.4 pvEnvironmentパラメータ
このメモリが不要になったときpvEnvironmentパラメータに使用する新しいプロセスの環境文字列を含むメモリブロックへのポイントは、環境GetEnvironmentStringsの使用は、リターンアドレス文字列のデータ・ブロックを機能しない、それはFreeEnvironmentStringsを解放する関数を呼び出します。
4.2.5 pszCurDirパラメータ
パラメータは、親が現在のドライブの子ディレクトリを設定することができます
4.2.6 psiStartInfoパラメータ
構造やSTARTUPINFOEX構造にSTARTUPINFOパラメータポイントは、使用されていないこの構造体のメンバーは、初期化構造体のメンバは、CreateProcessをを呼び出す前に完了しなければならない、クリアしなければなりません。
STARTUPINFO结构和STARTUPINFOEX结构的成员:
typedef struct _STARTUPINFO{
DWORD cb; //STARTUPINFO结构中的字节数
PSTR IpReserved; //保留,必须初始化为NULL
PSTR IpDesktop; //在哪个桌面上启动应用程序
PSTR IpTitle; //控制台窗口的窗口标题
DWORD dwX; //应用程序窗口在屏幕上的位置(x,y坐标)
DWORD dwY;
DWORD dwXSize; //应用程序窗口的高度和宽度
DWORD dwYSize;
DWORD dwXCountChars; //指定子进程的控制台窗口的高度和宽度(用字符数表示)
DWORD dwYCountChars;
DWORD dwFillAttribute; //指定子进程的控制台窗口所用的文本和背景色
DWORD dwFlags;
WORD wShowWindow; //指定应用程序主窗口如何显示
WORD cbReserved2; //保留,必须初始化为0
PBYTE IpREserved2; //保留,必须初始化为NULL
HANDLE hStdInput; //指定到控制台输入缓冲区的句柄和输出缓冲区的句柄
HANDLE hStdOutput;
HANDLE hStdError;
}STARTUPINFO, *LPSTARTUPINFO;
typedef struct _STARTUPINFOEX{
STARTUPINFO StartupInfo;
struct _PROC_THREAD_ATTRIBUTE_LIST *IpAttributeList;
}STARTUPINFOEX, *LPSTARTUPINFOEX;
dwFlags成员有一组标志,用于告诉CreateProcess函数:PSTARTUPINFO结构中的其他成员包含的信息是否有用,或者忽略一些成员;
4.2.7 ppiProcInfo参数
参数指向一个PROCESS_INFORMATION结构,Create函数返回之前会初始化结构成员:
typedef struct _PROCESS_INFORMATION{
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
}PROCESS_INFORMATION:
GetCurrentProcessId 获得当前进程ID
GetCurrentThreadId 获得当前正在运行线程的ID
GetProcessId 获得与指定句柄对应的一个进程ID
GetThreadId 获得与指定句柄对应的一个线程ID
GetProcessIdOfThread 根据线程句柄,获得其所在进程的ID
应用程序要与它的创建者通信,最好不要使用ID,因为ID会被重用,此时ID标识的可能是另一个进程了,应该使用内核对象、窗口句柄来进行通信;
要使进程/线程ID不被重用,就是保证进程或线程对象不被销毁,即不关闭这些对象的句柄,直到应用程序不再使用ID时,再调用CloseHandle来释放内核对象;
4.3 终止进程
进程可通过以下4种方式终止:
4.3.1 主线程的入口点函数返回(强烈推荐的方式)
设计应用程序时,应保证主线程的入口点函数返回后,应用程序的进程才终止;
4.3.2 进程中的一个线程调用ExitProcess函数(避免这种方式)
VOID ExitProcess(UINT fuExitCode);
//ExitThread将使应用程序的主线程停止执行,进程只要还有其他线程在运行,进程就不会终止
4.3.3 进程中的一个线程调用TerminateProcess函数(避免这种方式)
//任何线程都能调用它来终止另一个进程或自己的进程
BOOL TerminateProcess(
HANDLE hProcess, //要终止的进程句柄
UINT fuExitCode); //进程终止时的退出代码,传入fuExitCode参数的值
4.4.4 进程中所有线程自然死亡(几乎不会发生)
BOOL GetExitCodeProcess(HANDLE hProcess,PDWORD pdwExitCode);
4.4 子进程
Windows提供了几种在不同进程之间传递数据的方式:动态数据交换(DDE),OLE,管道,邮件槽;
//创建新线程,让它执行工作,然后等候结果
PROCESS_INFORMATION pi;
DWORD dwExitCode;
//创建子进程
BOOL fSuccess = CreateProcess(...., &pi);
if(fSuccess){
//当不再需要就关闭线程句柄
CloseHandle(pi.hThread);
//暂停执行父进程的线程,直到子进程终止
WaitForSingleObject(pi.hProcess,INFINITE);
//子进程结束,获取退出代码
GetExitCodeProcess(pi.hProcess,&dwExitCode);
//当不再需要就关闭进程句柄
CloseHandle(pi.hProcess);
}
4.5 管理员以标准用户权限运行时
用户账户控制(UAC)
4.5.1 自动提升进程的权限
可执行文件中嵌入了特殊的资源(RT_MANIFEST),系统会检查清单文件的<trustInfo>段;
可以将清单保存到可执行文件所在目录,扩展名使用.manifest;
4.5.2 手动提升进程的权限
调用ShellExecuteEx函数来提升权限
4.5.3 何为当前权限上下文
GetProcessElevation函数能返回:提升类型和指出进程是否正在以管理员身份运行的布尔值;
4.5.4 枚举系统中正在运行的进程
“性能数据”数据库
4.5.5 Process Information示例程序