1

I'm having a problem with creating a shortcut using C++.

The .lnk file is created, but the target has a nonsense path.

Can you explain why this code is not creating a correct shortcut? Can someone could help me fix my code?

Here is the code

// RepChrome.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"
#include "winnls.h"
#include "shobjidl.h"
#include "objbase.h"
#include "objidl.h"
#include "shlguid.h"
#include <shlobj.h>


HRESULT CreateLink(LPCWSTR lpszPathObj1, LPCSTR lpszPathLink, LPCWSTR lpszDesc,LPCWSTR lpszarg) 
{ 
    HRESULT hres; 
    IShellLink* psl; 

    // Get a pointer to the IShellLink interface. It is assumed that CoInitialize
    // has already been called.
    hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); 
    if (SUCCEEDED(hres)) 
    { 
        IPersistFile* ppf; 

        // Set the path to the shortcut target and add the description. 
        psl->SetPath(lpszPathObj1); 
        psl->SetArguments(lpszarg);
        psl->SetDescription(lpszDesc); 

        // Query IShellLink for the IPersistFile interface, used for saving the 
        // shortcut in persistent storage. 
        hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); 

        if (SUCCEEDED(hres)) 
        { 
            WCHAR wsz[MAX_PATH]; 

            // Ensure that the string is Unicode. 
            MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1, wsz, MAX_PATH); 

            // Add code here to check return value from MultiByteWideChar 
            // for success.

            // Save the link by calling IPersistFile::Save. 
            hres = ppf->Save(wsz, TRUE); 
            ppf->Release(); 
        } 
        psl->Release(); 
    } 
    return hres; 
}    

int _tmain(int argc, _TCHAR* argv[])
{
    char sp[MAX_PATH] = { 0 };
    WCHAR p[MAX_PATH]=  { 0 };
    
    WCHAR deskPath[MAX_PATH] = { 0 };
    SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, 0, deskPath);
    sprintf_s( sp,sizeof(deskPath),"%s\\My Program.lnk",deskPath);
    WCHAR path[MAX_PATH] = { 0 };
    SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILESX86, NULL, 0, path);    
    
    swprintf_s( p,sizeof(path),L"%s\\My Program\\start.exe",path);

    CreateLink(p, sp, L"",L""); 

    return 0;
}
1

1 Answer 1

2

When calling sprintf_s(), %s expects a char* string, but you are giving it a wchar* string instead. Also, you are passing in the wrong value for the second parameter of sprintf_s() and swprintf_s().

You really should not be using any char data in this code at all, especially since you are just converting it to wchar anyway, so you should use all wchar strings only. Change the lpszPathLink parameter to LPCWSTR, change the sp buffer to WCHAR[], and change sprintf_s() to swprintf_s().

Also, since all of the values you are using for the link are WCHAR anyway, you should use IShellLinkW directly instead of the TCHAR-based IShellLink.

Also, CreateLink() requires CoInitialize/Ex() to have been called beforehand, but it is NOT being called in this code.

Try this instead:

// RepChrome.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "windows.h"
#include "winnls.h"
#include "shobjidl.h"
#include "objbase.h"
#include "objidl.h"
#include "shlguid.h"
#include <shlobj.h>

HRESULT CreateLink(LPCWSTR lpszPathObj1, LPCWSTR lpszPathLink, LPCWSTR lpszDesc, LPCWSTR lpszarg)
{
    HRESULT hres;
    IShellLinkW* psl;

    // Get a pointer to the IShellLink interface. It is assumed that CoInitialize
    // has already been called.
    hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&psl));
    if (SUCCEEDED(hres))
    {
        IPersistFile* ppf;

        // Set the path to the shortcut target and add the description.
        psl->SetPath(lpszPathObj1);
        psl->SetArguments(lpszarg);
        psl->SetDescription(lpszDesc);

        // Query IShellLink for the IPersistFile interface, used for saving the
        // shortcut in persistent storage.
        hres = psl->QueryInterface(IID_PPV_ARGS(&ppf));
        if (SUCCEEDED(hres))
        {
            // Save the link by calling IPersistFile::Save.
            hres = ppf->Save(lpszPathLink, TRUE);
            ppf->Release();
        }
        psl->Release();
    }
    return hres;
}

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);

    WCHAR sp[MAX_PATH] = { 0 };
    WCHAR p[MAX_PATH] = { 0 };

    WCHAR deskPath[MAX_PATH] = { 0 };
    SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, 0, deskPath);
    swprintf_s( sp, _countof(sp), L"%s\\My Program.lnk", deskPath);

    WCHAR path[MAX_PATH] = { 0 };
    SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILESX86, NULL, 0, path);
    swprintf_s( p, _countof(p), L"%s\\My Program\\start.exe", path);

    CreateLink(p, sp, L"",L"");

    CoUninitialize();

    return 0;
}
2
  • IShellLink will be mapped to IShellLinkW if it is UNICODE
    – Grace
    Commented May 4, 2023 at 9:31
  • 1
    @Grace only when UNICODE is defined, yes, otherwise it will map to IShellLinkA instead. Since UNICODE is not guaranteed (depends on the user's compiler setup), as since all of the data is based on wchar_t specifically, IShellLinkW should be used directly so the code is not dependant on the compiler setup Commented May 4, 2023 at 9:43

Not the answer you're looking for? Browse other questions tagged or ask your own question.