找不到的msg.exe - 神奇的sysnative

某天遇到windows command - msg.exe在win7/win8找不到的問題。這問題乍看之下非常的神奇,兩套不同的軟體但一個能夠使用一個則不行。在不行的那個軟體中加入Debug訊息,卻出現msg.exe找不到的怪異現象。這兩套軟體除了實作語言不同外,另外一個是32與64 bit之差別。執行的Command一樣,但為什麼32-bit的卻不能用呢?

首先我透過了32與64位元的cmd.exe去執行msg.exe: (system32 for 32-bit;syswow64 for 64-bit)

可以發現在32位元的情況下根本找不到msg.exe,接著到C:/windows下搜尋msg.exe,確認system32與syswow64中是否都有msg.exe。事實是: 僅system32中有msg.exe。

這是系統限制,issue可不解嗎? 但問題是另外一個軟體做的到阿! 把一個軟體從32變成64位元是較難些,還是看看要如何用較簡單的方式去解決這個問題吧!

我試過直接呼叫%windir%\system32\msg.exe,也確認過%PATH%有指到system32,但事實上32-bit的service是無法直接存取system32的。還好這時候看到個一個叫sysnative的東西,它可以透過這個資料夾讓32的應用程式可以訪問到system32中的東西。

Batch

可以根據執行一次msg.exe去確認exit code,若非0代表有問題則改用sysnative的msg.exe:

@echo off
set CMD=msg.exe
 
where %CMD% >NUL 2>&1
 
if not %errorlevel% == 0 (
	set CMD=%windir%\sysnative\msg.exe
)
 
%CMD%

也可以透過判斷sysnative中的msg.exe是否存在去決定要執行的Command:
@echo off
if exist "%windir%\sysnative\msg.exe" ( 
	set CMD="%windir%\sysnative\msg.exe" 
) else (
	set CMD=msg
)
 
%CMD%

C/C++

我寫了一個intMsgCommand的function,參數為接收結果cmd與要發出的message。首先透過執行一次msg去確認exit code為0,如果不為0就用sysnative做法。這裡需注意的是: 如果採用batch中第二個做法是行不通的,因為在應用程式中判斷sysnative是否存在,將會得到false的結果。最後會把要執行的command給串到cmd參數中。當然你也可以選擇將整串的batch給塞到system中執行,只是這樣的code可讀性非常差。

void intMsgCommand(char* cmd, char *message){
	int ret = system("msg");
	if( ret == 0 ){
		sprintf(cmd, "msg * %s", message); // default command
		return;
	} 
 
	const int size = GetWindowsDirectory(NULL, 0);
	char* windir = new char[size];
 
	if( GetWindowsDirectory(windir, size) != 0){
		char msgcmd_path[50];
		sprintf(msgcmd_path, "%s\\sysnative\\msg.exe", windir);
		sprintf(cmd, "%s * %s", msgcmd_path, message); 
	} 
	delete windir;
}

友藏內心獨白: 其實這問題有點鳥。