Расследование показало, что возникла проблема с запуском 64-битных приложений из 32-битного FAR. В NF запуск приложений реализован через функцию ShellExecute - через нее запускается ярлык (LNK-файл) приложения, хранящийся в меню "Пуск". Выяснилось, что ShellExecute, вызванная из 32-битного приложения, ярлыки 64-битных приложений обрабатывает неправильно.
Эта проблема описана здесь и здесь. ShellExecute неверно раскрывает путь "%Program files%", который прописан в lnk-файле. 64-битные приложения на 64-битных системах устанавливаются в "C:\Program files", 32-битные в "C:\Program files (x86)". Функция ShellExecute, при запуске из 32-битного приложения, всегда интерпретирует "%Program files%" как "C:\Program files (x86)". 64-битные приложения, естественно, не запускаются.
Я обошел проблему следующим образом. Открываю lnk-файл, через IShellLink получаю полный путь к приложению, заменяю в нем "Program files (x86)" на "Program files" и затем запускаю измененый путь через тот же ShellExecute. Вот пример кода, взятый из исходных кодов Named Folders (слегка упрощенный и отвязанный от проекта).
#include "stdafx.h" #include <Windows.h> #include <WinBase.h> #include <boost/scope_exit.hpp> #include <boost/algorithm/string.hpp> #include <string> #include <vector> #include <ShlObj.h> #include <shellapi.h> #include <comdef.h> #include <Shlwapi.h> #pragma comment(lib, "shlwapi.lib") typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; BOOL IsWow64() { BOOL bIsWow64 = FALSE; //IsWow64Process is not available on all supported versions of Windows. //Use GetModuleHandle to get a handle to the DLL that contains the function //and GetProcAddress to get a pointer to the function if available. fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); if (NULL != fnIsWow64Process) { if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64)) { //handle error } } return bIsWow64; } bool GetShortcutProgramPath(std::wstring const& PathToShortcut, std::wstring &destPath) { wchar_t wbuffer[MAX_PATH]; lstrcpy((reinterpret_cast<wchar_t*>(&wbuffer[0])), PathToShortcut.c_str()); PathUnquoteSpaces((reinterpret_cast<wchar_t*>(&wbuffer[0]))); //remove quotes from path if they exist ::CoInitialize(0); BOOST_SCOPE_EXIT ( (&wbuffer) ) { //wbuffer is used because BOOST_SCOPE_EXIT doesn't support empty list params CoUninitialize(); } BOOST_SCOPE_EXIT_END; IPersistFile* ppf = 0; IShellLink* psh = 0; HRESULT hr = ::CoCreateInstance(::CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER , IID_IPersistFile, reinterpret_cast<void**>(&ppf)); if (FAILED(hr)) return false; BOOST_SCOPE_EXIT( (&ppf) ) { ppf->Release(); } BOOST_SCOPE_EXIT_END; hr = ppf->Load((reinterpret_cast<wchar_t*>(&wbuffer[0])), STGM_READ); if (FAILED(hr)) return false; hr = ppf->QueryInterface(IID_IShellLink, reinterpret_cast<void**>(&psh)); if (FAILED(hr)) return false; BOOST_SCOPE_EXIT( (&psh) ) { psh->Release(); } BOOST_SCOPE_EXIT_END; hr = psh->GetPath(&wbuffer[0], MAX_PATH, NULL, SLGP_UNCPRIORITY); if (FAILED(hr)) return false; destPath = &wbuffer[0]; return true; } void execute_selected_program64_under_w32(std::wstring const& path, std::wstring const& params) { std::wstring dest_path; if (GetShortcutProgramPath(path, dest_path)) { wchar_t buffer[MAX_PATH]; ::ExpandEnvironmentStringsW(L"%ProgramFiles%", reinterpret_cast<wchar_t*>(&buffer[0]), MAX_PATH); std::wstring pf32 = reinterpret_cast<wchar_t*>(&buffer[0]); ::ExpandEnvironmentStringsW(L"%ProgramW6432%", reinterpret_cast<wchar_t*>(&buffer[0]), MAX_PATH); std::wstring pf64 = reinterpret_cast<wchar_t*>(&buffer[0]); if (pf32 != pf64) { boost::replace_all(dest_path, pf32, pf64); HINSTANCE value = ShellExecuteW(0, NULL , dest_path.c_str(), params.c_str(), NULL, SW_SHOWNORMAL); } } } void ExecuteProgram(std::wstring const &path, std::wstring const ¶ms) { HINSTANCE value = ShellExecuteW(0, NULL , path.c_str(), params.c_str(), NULL, SW_SHOWNORMAL); if ((int)(intptr_t)value < 32) { if (IsWow64()) { //workaround for #6 execute_selected_program64_under_w32(path, params); } } } int _tmain(int argc, _TCHAR* argv[]) { ExecuteProgram(L"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Paint.NET.lnk", L""); return 0; }
Здорово. А если есть и "c:\Program Files\SomeProgram.exe", и "c:\Program Files (x86)\SomeProgram.exe", а надо запустить именно тот, что указан в ярлыке? (А там может быть указан x86).
ОтветитьУдалить