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{ };
}