Re: [問題] VC2005 ShellExecute();

看板C_and_CPP (C/C++)作者 (www.eJob.gov.tw)時間16年前 (2009/06/17 12:45), 編輯推噓0(001)
留言1則, 1人參與, 最新討論串2/3 (看更多)
看了你後來講的,追蹤一下大概知道你的意思了。 你用VC新增專案,選了很新奇的 Visual C++ -> CLR (Common Language Runtime) -> Windows Form 應用程式。也就是要寫 C++/CLI 的程式。 然後你以前有寫 console 介面的程式跑一些處理後,會輸出一個文字檔 readme.txt。 你新增完專案後,想要試看看能不能用函數去開啟 readme.txt。 於是你找到兩個 Windows API 函數:ShellExecute 跟 WinExec 其實你可以用 <stdlib.h> 裡簡單的 system("notepad C:\xxx.txt"); 根據msdn,WinExec 是為了跟 16位元 Windows 相容才提供,建議使用 CreateProcess() 而 ShellExecute() 則比較有趣,以 txt 來說,你用我的電腦->工具->資料夾選項-> 檔案類型->txt->進階後,可以設定,當你在檔案總管點選txt按右鍵時的行為。 比如 .txt 你可以新增一個「用madedit開啟」或簡單點新增「edit」,然後對應執行此 動作的應用程式 D:\madedit.exe。當然 edit 會被翻譯成編輯。 以後你在txt檔,按右鍵選編輯,就會用 madedit 開啟該文字檔。 同理,ShellExecute()就是把 "open" 改成 "edit" 再選擇一個 txt 檔的路徑即可。 linux 桌面 GNOME 在 終端機 下有個指令,好像是 gnome-open 123.avi 就是做同樣 的事,跑這個指令後,就會等於你在 gnome 桌面環境點兩下 123.avi 去開啟他。 -- 根據 msdn 的敘述,使用 ShellExecute() 你需要 Header shellapi.h Import library shell32.lib 上面這是最低需求,我的習慣是明知道要呼叫的是 Windows API ,那就先 #include <windows.h> 再按下 F7 先編譯一次。 然後打函數名稱,不要打完就用 alt + → 叫出自動完成。 如果叫得出名字,代表你已經 include 原型宣告成功,不用再加 shellapi.h windows.h 已經包含該檔。亦可看出此函數是否是巨集。Windows API 很多都是 有分 ANSI 跟 UNICODE 兩種版本,也就是根據專案設定是否支援 UNICODE 來 決定要呼叫 ShellExecuteA() 或 ShellExecuteW() 目前新版的 VC 預設值都是 UNICODE 支援開啟,所以你的字串常數一律都要改成 wchar_t 資料型態。 但可使用包含於 windows.h 裡的巨集 TEXT("open") ,這樣就能根據情況轉成 char 或 wchar_t。 故你應該寫 ShellExecute ( NULL, TEXT("open"), TEXT("C:\\sample.txt"), NULL, NULL,SW_SHOWNORMAL ); 然後你編譯,發生 lnk 2028 錯誤,但沒有編譯錯誤。這代表所有程式碼基本上語法正確 可通過編譯器,但連結時會錯誤。 很容易判斷出是 ShellExecute 的連結沒有成功,你要更改專案設定裡關於連結器 的選項,加入 shell32.lib 的連結設定。 或者不要更改專案設定,而是在 ShellExecute 所在的 cpp 檔假設是 main.cpp 裡, 加入編譯器前置處理指令 #pragma comment(lib, "shell32") pragmatic info: 編譯器在 main.obj 加入註解,告知連結器增加連結 shell32 的參數 不用打全名 shell32.lib,連結器會到預設目錄去搜尋此靜態連結程式庫檔案。 ※ 引述《lytn》之銘言: : 不過我發現,要是當初專案用 主控台程式建立 ,就不會有ERROR, : DEV C++ 也可以空專案直接跑, : 所以......還是卡關了. : 作者: lytn (sapphira) 看板: C_and_CPP : 標題: [問題] VC2005 ShellExecute(); : 時間: Tue Jun 16 17:29:35 2009 : 我用 學校授權的 VS2005 ,應該是 .NET 架構吧 : 專案用 Windows From 開啟的 : 網路上找很多範例 例如 MSDN : http://msdn.microsoft.com/en-us/library/bb762153(VS.85).aspx : 雖然是看不太懂,不過我照著寫,或是網路上範例碼直接COPY用 : #include<windows.h> : #include<shellapi.h> : ShellExecute ( NULL, "open", "C:\\path\\to\\readme.txt", NULL, NULL, : SW_SHOWNORMAL ); : 編譯時都會 : Error C2644:'ShellExecuteW':無法將參數2 從 'const char[5]' 轉換成 'LPCWSTR' : 這要怎麼搞阿? : ----原程式碼 : #include "stdafx.h" : #include <windows.h> : #include <shellapi.h> : #include<vector> : #include "Form1.h" : #include "GloVar.h" : #include "CivilClass.h" : #include "BasicExcel.hpp" : using namespace YExcel; : using namespace VC_Project; : [STAThreadAttribute] : int main(array<System::String ^> ^args) : { : // HWND hwnd; : // LPCWSTR filpat="open"; : WinExec("Notepad.exe", SW_SHOW); //這個會跑 : ShellExecute ( NULL, "open", "C:\\path\\to\\readme.txt", NULL, NULL, : SW_SHOWNORMAL ); //這會錯 : Application::EnableVisualStyles(); : Application::SetCompatibleTextRenderingDefault(false); : // 建立主視窗並執行 : Application::Run(gcnew Form1()); : return 0; : } : 另外想順便問, WinExec 跟 ShellExecute 有什麼差別? -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 124.8.131.252

06/17 13:36, , 1F
太棒,
06/17 13:36, 1F

06/17 13:37, , 2F
我還以為把 shell32.dll 加入參考就可以了,原來還有 linker
06/17 13:37, 2F

06/17 13:39, , 3F
推一下z大:)
06/17 13:39, 3F

06/17 13:39, , 4F
改完 可以 開 EXE 跟 TXT囉,大感謝阿,不然還不知道要拖多久
06/17 13:39, 4F
整理 Visual C++ 相對於 GNU 的 gcc 或 g++,命令列指令用的是 cl.exe,提供完整編譯 C, C++, C++/CLI 成目的檔 .obj 的編譯器功能,再加上連結成 .exe 的功能。 在 Visual C++ 選工具 -> VS 200X Command Prompt 叫出命令提示字元後 打 cl src.cpp /c 就會只做編譯器的功能輸出 src.obj。 而不加 /c 則會輸出 src.obj 跟 src.exe。 若指令 cl 加上參數 /MT,則編譯選項為 multithread support & static link /MD 同上但 dynamic link,故/MD輸出的執行檔較小,但缺所需dll的電腦就不能跑。 /MTd 與 /MDd 為等於在 debug 模式編譯出結果,會附加偵錯資訊。 而執行 link src.obj 可連結出 src.exe 但似乎無法做 /MT /MD 的指定,比較不方便。 -- obj 檔是 COFF (Common Object File Format) 格式,可用 VC 附的指令 dumpbin 解讀 或上網抓把 dumpbin 包裝成 GUI 的 wumpbin 軟體。 dumpbin 查詢 src.obj 可發現加入 #pragma comment (lib,"shell32") 後 在 .drectve section 裡有個 Linker Directive 多出一行 /DEFAULTLIB:"shell32"。 因此不管用 cl.exe 或 link.exe 都會記得在連結 src.obj 時,順便加上 shell32.lib 一起連結成 exe。 -- obj 檔做 dumpbin: .bss section 用來放沒有初始化的變數,如以下的 count #include <stdio.h> int count; //就算加了 static 使其變成內部連結,還是在.bss區 int data = 2; int main(){ int i; int j = 2; return 0; } ※ http://blog.linux.org.tw/~jserv/archives/002030.html .data 放資料,通常是有初始化的全域變數,比如上面的 int data = 2; 你在 .data 區就會看到四個16進位的Byte寫 02 00 00 00 (因為CPU是little endian,所以不是 00 00 00 02) 就算是變成 static int data = 2; 其差別也只是變成內部連結,其他都一樣。 而上面的 i 跟 j 是當程式執行到函數 main 時才隨便從屬於堆疊的那塊記憶體 區域,找 4 個 Byte 決定當成 i 或 j,一旦此函數結束,函數裡的 auto 變數所佔的區域可能就變成另外一個 auto (local) 變數拿去用。 .rdata 放唯讀資料,比如 "C:\\123.txt" 這樣的字串常數。在程式執行後 如果寫入這個區域所在的記憶體,則 Linux 上應該會回報 Segment fault, Windows 好像是跳出 Runtime error 視窗。 .text 除了資料以外的東西,應該就是指令。 symbol table 每個 obj 都有一份,有好幾筆,某筆可能寫 00000000 SECT3 External ?count@@3HA (int count) 告訴我們當初那個未初始化的全域變數 int count; 被放在 Section 3 此例也就是 .bss section,放在裡面偏移位置 00000000 的地方, 其實就是第一個 Byte 所在。 它是 external (外部連結)。因為它的宣告沒有加 static, 所以其他的 obj 檔只要透過 extern int count; 就能呼叫他。 而也因為是外部連結,所以 C++ 編譯器對 count 的符號名稱裝飾的 比較用心,變成比較複雜的 ?count@@3HA 如果當初是宣告成 static (內部連結),其他 obj 無法呼叫的話, 名稱就只會簡單的改成 _count 而已。 -- exe 檔是 PE 格式 (Portable Executable),Windows 32/64 位元用的可執行檔格式 Linux 是 ELF,DOS有個有名的com,寫組語的話會發現它很平易近人,只要用組合語言 寫完,把程式起始位址假定為 0x100 然後組譯,改成 .com 就已經是合格的執行檔。 沒有relocation顧慮也沒有header,因為點兩下 OS 就把它載入到 0x100 記憶體去, 就好像在寫 boot loader 可以直接假定BIOS會把你寫的程式載入到 0000:7c00 一樣。 exe 應該是改良自 COFF,總之用 dumpbin 就可以解讀。 打開exe後發現多出一個 Imports,裡面會寫這個執行檔需要配合哪個dll,而且 是用到這個dll的哪個函數。也可以用功能更強的 Dependcy Walker 來看。 通常都是程式寫完要拿到別台電腦執行,結果被VC專案設定預設的選項 /MD 錶了 不能跑後才想到要來看 Imports 了什麼 dll。 而那這 ShellExecute() 其實用 /MD 或 /MT 都一樣到最後還是要用到 SHELL32.dll, 而且編譯的時候一定要用到 lib,我猜 lib 裡面是定義用來呼叫dll的程式碼,不確定。 直接在 runtime 呼叫 dll 取得其函數所在,就可以躲過 Dependency Walker 等的偵測 當然 dll 還是必須存在才能執行其功能的。如下: #include <windows.h> //略 HINSTANCE hPDLL; hPDLL = LoadLibrary(TEXT("shell32.dll")); //呼叫 Windows API,故指定函數「呼叫慣例」為 __stdcall typedef HINSTANCE (__stdcall *ptr)(HWND, LPCSTR, LPCSTR, LPCSTR, LPCSTR, INT); ptr ptrFun; ptrFun = (ptr)GetProcAddress(hPDLL, "ShellExecuteA"); ptrFun(0, "edit", "C:\\windows\\winnt.bmp", 0, 0, SW_SHOWMAXIMIZED); ※ 編輯: zlw 來自: 124.8.131.252 (06/17 17:54)

06/17 18:06, , 5F
打錯,不是 boot loader 是 boot sector
06/17 18:06, 5F
文章代碼(AID): #1AE7JEOg (C_and_CPP)
文章代碼(AID): #1AE7JEOg (C_and_CPP)