CreateProcess()

今天下午在寫程式的時候被一個奇怪的問題給卡住,我寫的程式去呼叫 CreateProcess() 但一直跳出下面的錯誤訊息:

ScreenHunter_02 Dec. 24 19.07

原來問題出在我使用 Windows 的 CreateProcess() 這個 api 的方法錯誤,先來偷看一下 MSDN 的函式原型

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

其實問題的癥結很簡單,就出在第二個參數身上,為什麼 lpCommandLine 的型別是 LPTSTR 而不是 LPCTSTR 呢?理由很簡單,因為系統會去更改這個參數,所以 MSDN 也用了 __inout_opt 來修飾這個參數,因此我們不能傳一個 read only 的記憶體區塊到這個參數來。引用一下 MSDN 的說明:

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

有趣吧?!只有 Unicode 版本的 CreateProcess 會修改這個參數,所以下面的程式碼可以 work:

wchar_t cmd[ 100 ] = L"notepad D:\\mt.txt"; 
CreateProcessW( NULL, cmd, NULL, NULL, false, 0, NULL, NULL, &si, &pi ); 
CreateProcessA( NULL, "notepad D:\\mt.txt", NULL, NULL, false, 0, NULL, NULL, &si, &pi );

但下面的程式碼是不能 work

CreateProcessW( NULL, L"notepad D:\\mt.txt", NULL, NULL, false, 0, NULL, NULL, &si, &pi );

是不是有點不 consistent 呢?

此外,眼尖的人可能會發現為什麼一個 LPTSTR (即TCHAR*) 型別可以接受一個型別為 const TCHAR array 呢?C++ standard 2.13.4 不是這樣說的嗎?

A string literal is a sequence of characters (as defined in 2.13.2) surrounded by double quotes, optionally beginning with the letter L, as in "..."or L"...".  A string literal that does not begin with L is an ordinary string literal, also referred to as a narrow string literal.  An ordinary string literal has type “array of n const char” and static storage duration (3.7), where n is the size of the string as defined below, and is initialized with the given characters. A string literal that begins with L, such as L"asdf", is a wide string literal.  A wide string literal has type “array of n const wchar_t” and has static storage duration, where n is the size of the string as defined below, and is initialized with the given characters.

怎麼 VC++ 連個 warning 都不給呢?這是因為 C++ 為了相容於 C 所做出的讓步,來看一下 4.2  Array-to-pointer conversion 的描述:

A string literal (2.13.4) that is not a wide string literal can be converted to an rvalue of type “pointer to char”; a wide string literal can be converted to an rvalue of type “pointer to wchar_t”.  In either case, the result is a pointer to the first element of the array.  This conversion is considered only when there is an explicit appropriate pointer target type, and not when there is a general need to convert from an lvalue to an rvalue.

因此,比較好的習慣是:總是用 const char/wchar_t* 去指向一塊 literal string。Scott Meyer 不就說了嗎?

Use const whenever possible

: )

#include <iostream>
#include <typeinfo>
using namespace std;

void foo( char* msg )
{
    cout << "[foo( char* msg )] " << msg << endl;
}
void foo( const char* msg )
{
    cout << "[foo( const char* msg )] " << msg << endl;
}

template<typename T>
void printType( T* x )
{
    cout << "type of T: " << typeid( T ).name() << endl;
}

void badCall()
{
    throw "Exception";
}

int main()
{
    foo( "Hello World" );
    printType( "Hello World" );

    try {
        badCall();
    }
    catch ( const char* msg ) {
        cerr << "[const char* msg] " << msg << endl;
    }
    catch ( char* msg ) {
        cerr << "[char* msg] " << msg << endl;
    }

    return 0;
}

我可沒說上面的 code 可以順利 compile 唷~

Boost.Test 的新文件

Boost 1.37.0 也出了好一陣子了,不過公司不比實驗室,總是不能隨意看有興趣的東西,指派的工作或 reading 還是得擺在第一位,更何況直屬老闆跟大頭都做我後頭和旁邊,有時還是會有點壓力!

不過最近被指派一個工作,跟 unit test 有關,趁著機會有時間可以看看新的 Boost.Test ,不過好像有點囧,怎麼 document 好像越寫越爛了…是我英文太爛嗎?還好舊電腦上還有舊版的 Boost.Test Doc !

哎呀,這個…程式設計師果然很討厭寫 document…果然古今中外都是…

Windows + Visual Studio + VSCode + CMake 的疑難雜症

Environment Windows 10 Visual Studio 2019 CMake 3.27.7 VSCode VSCode CMake Tools 1. CMAKE_BUILD_TYPE 是空的 參考一下 這篇 的處理。 大致上因為 Visual...