iooiau.net

CreateProcess() の罠

CreateProcess 関数はプロセスの起動に使われますが、いやらしい罠があります。
よく API の解説などで以下のようなコードが掲載されています。


STARTUPINFO si;
PROCESS_INFORMATION pi;

ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));

if (!CreateProcess(NULL,
                   TEXT("Notepad.exe"),  // メモ帳を起動
                   NULL,
                   NULL,
                   FALSE,
                   0,
                   NULL,
                   NULL,
                   &si,
                   &pi)) {
    return;
}

CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

しかし、このコードは間違っています
それは関数の定義を見れば分かります。以下は CreateProcess() 関数の定義です。


BOOL WINAPI CreateProcess(
  LPCTSTR lpApplicationName,
  LPTSTR lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL bInheritHandles,
  DWORD dwCreationFlags,
  LPVOID lpEnvironment,
  LPCTSTR lpCurrentDirectory,
  LPSTARTUPINFO lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);

最初の二つの引数を見比べてみてください。
そう、型が違います
1番目の引数は LPCTSTR ですが、2番目の引数は LPTSTR です。
2番目の引数に C が付いていないということは、書き換え可能なバッファを渡さなければいけないのです。
文字列定数は通常、(コンパイラの設定によりますが)書き換えできない領域に置かれます。
つまり上記のようなコードを書くと、OS によってはアクセス違反で強制終了となります。

CreateProcess() がこのような妙な仕様になっているのは、その昔メモリが貴重で少しでもケチりたかった時代に、lpCommandLine で渡されたメモリ領域をそのまま起動したプロセスに渡せるようにするためだったようです。

なお、C++11 からは文字列定数を const でないポインタに入れようとするとエラーになるので、間違いが起こりにくくはなっています。