Расследование показало, что возникла проблема с запуском 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).
ОтветитьУдалить