4

Our 32-Bit application launches Windows LNK files (Shell Links) via ShellExecute. When it tries to "launch" a link to a 64-Bit binary (such as the "Internet Explorer (64-Bit)" shortcut in Start Menu) it always ends up launching the 32-Bit binary. Internally, ShellExecute incorrectly resolves the link target: There's a hidden field inside the LNK which holds FOLDERID_ProgramFiles. A 64-Bit app resolves this to the 64-Bit Program Files directory, but a 32-Bit app won't.

Wow64DisableWow64FsRedirection does not change this behavior of ShellExecute.

Besides going through a 64-Bit "trampoline" process (which is not an option due to how our plugin architecture works), Is there any way for a 32-Bit app to launch shell links exactly the way a 64-Bit app would?

5 Answers 5

1

Please read Raymond Chen's new article, "How can I get the original target of a shortcut without applying any 32-bit adjustments?" It describes how to disable known-folder-relative tracking. See the SLDF_DISABLE_KNOWNFOLDER_RELATIVE_TRACKING flag.

https://devblogs.microsoft.com/oldnewthing/?p=107807

Here is a rewritten sample from the link above (by using ATL):

#include <atlbase.h>

#define RETURN_IF_FAILED(hr)                \
do                                          \
{                                           \
    const auto hRes = HRESULT{ hr };        \
    if (FAILED(hRes))                       \
        return { };                         \
} while (false)

std::wstring shellLinkRawTargetPath(const std::wstring& linkPath)
{
    ATL::CComPtr< IShellLinkW > link;
    RETURN_IF_FAILED(link.CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER));

    ATL::CComPtr< IPersistFile > persistFile;
    RETURN_IF_FAILED(link->QueryInterface(IID_PPV_ARGS(&persistFile)));

    RETURN_IF_FAILED(persistFile->Load(linkPath.c_str(), STGM_READ));

    ATL::CComPtr< IShellLinkDataList > dataList;
    RETURN_IF_FAILED(link->QueryInterface(IID_PPV_ARGS(&dataList)));

    DWORD flags{ };
    RETURN_IF_FAILED(dataList->GetFlags(&flags));

    flags |= SLDF_DISABLE_KNOWNFOLDER_RELATIVE_TRACKING;
    RETURN_IF_FAILED(dataList->SetFlags(flags));

    ATL::CComPtr< IStream > stream;
    RETURN_IF_FAILED(::CreateStreamOnHGlobal(nullptr, TRUE, &stream));

    ATL::CComPtr< IPersistStream > persistStream;
    RETURN_IF_FAILED(link->QueryInterface(IID_PPV_ARGS(&persistStream)));

    RETURN_IF_FAILED(persistStream->Save(stream, TRUE));

    LARGE_INTEGER zero = { };
    RETURN_IF_FAILED(stream->Seek(zero, STREAM_SEEK_SET, nullptr));

    RETURN_IF_FAILED(persistStream->Load(stream));

    WIN32_FIND_DATA wfd{ };
    std::vector< WCHAR > buffer(MAX_PATH);

    HRESULT hr = link->GetPath(
        buffer.data(), static_cast< int >(buffer.size()), &wfd, 0);

    while (hr == S_FALSE)
    {
        buffer.resize(buffer.size() * 2);

        hr = link->GetPath(
            buffer.data(), static_cast< int >(buffer.size()), &wfd, 0);
    }

    return SUCCEEDED(hr) ?
        std::wstring{ buffer.data() } : std::wstring{ };
}
Sign up to request clarification or add additional context in comments.

Comments

0

Reading this article from Raymond Chen I don't think what you're asking is possible. I would still consider making a small "trampoline" application, who's only job was to launch the given application/link, and compiling a different one for use on 32bit and 64bit systems. Either that or build two versions of your application, a 32bit and 64bit one.

Comments

0

You could spawn an explorer.exe process which calls on the LNK.

Is there a particular reason you can't compile your program as a 64bit application?

Comments

0

Anytime you here something is impossible on a computer, think again... The key is to utilize the c:\windows\sysnative\ path to shut off the redirection.

Here is very simple code that will do what you want:

#include <windows.h>
#include <ShellAPI.h>
#include <stdio.h>

int main(int iArgc, const char *pArgv[])
{
    ShellExecute(NULL, L"open", L"C:\\windows\\sysnative\\..\\..\\Program Files\\Internet Explorer\\iexplore.exe", NULL, NULL, SW_SHOWNORMAL);
    BOOL bIAmWow64 = FALSE;
    IsWow64Process(GetCurrentProcess(), &bIAmWow64);
    printf("I am a wow64 process: %hs\n", bIAmWow64 ? "Yes": "No");
    return 0;
}

I hope that is helpful.

Comments

0

Andrew: I gave it a shot, and the sysnative folder does not do anything that Wow64DisableWow64FsRedirection doesn't already do. The problem is that ShellExecute mistakenly assumes that the link is pointing to %programfiles(x86)%, when it is in fact pointing to %programfiles% (Even when there is no such file in %programfiles(x86)%).

Opening 64bit programs already works perfectly fine. It's .lnk files pointing to the %programfiles% directory that are the problem.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.