Daniel
Daniel

Reputation: 13

Creating a shortcut .lnk using Windows API

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

Upvotes: 1

Views: 970

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 597941

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

Upvotes: 2

Related Questions