#include <stdio.h>
#include <shlobj.h>
#include <shlwapi.h>

bool __fastcall CreateShortCut(LPWSTR pwzShortCutFileName,  LPCITEMIDLIST pidl,
							   LPTSTR pszWorkingDirectory, WORD wHotKey, int iCmdShow);
int short_cut_startup(char *connection_name, LPWSTR link_name);
LPITEMIDLIST GetNextItemID(LPCITEMIDLIST pidl);
UINT GetSize(LPCITEMIDLIST pidl);
LPITEMIDLIST Append(LPCITEMIDLIST pidlBase, LPCITEMIDLIST pidlAdd);

LPMALLOC pMalloc;

int main(void)
{
	if(short_cut_startup("demo", L"demo - connection.lnk"))
		printf("Success!\n");
	else printf("Can't create shortcut!\n");
	return 0;
}

/* Main function which creating shortcut on desktop */
int short_cut_startup(char *connection_name, LPWSTR link_name)
{
    LPITEMIDLIST pidConnections = NULL;
    LPITEMIDLIST pidlItems = NULL;
    LPITEMIDLIST pidlDesk = NULL;
    IShellFolder *psfFirstFolder = NULL;
    IShellFolder *psfDeskTop = NULL;
    IShellFolder *pConnections = NULL;
    LPENUMIDLIST ppenum = NULL;
    ULONG celtFetched;
    HRESULT hr;
    STRRET str_curr_connection_name;
    TCHAR curr_connection_name[MAX_PATH] = "";	/* Connection point name */
	TCHAR desktop_path[MAX_PATH]="";			/* Path to desktop */
	TCHAR full_link_name[MAX_PATH]="";			/* Full shortcut name */
	LPITEMIDLIST full_pid;						/* Full shortcut pid */

   
    CoInitialize( NULL );
	/* Allocating memory for Namespace objects */
    hr = SHGetMalloc(&pMalloc);
    hr = SHGetFolderLocation(NULL, CSIDL_CONNECTIONS, NULL, NULL, &pidConnections);

	/* Get full path to desktop */
	SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktop_path);

	hr = SHGetDesktopFolder(&psfDeskTop);
    hr = psfDeskTop->BindToObject(pidConnections, NULL, IID_IShellFolder, (LPVOID *) &pConnections);
    hr = pConnections->EnumObjects(NULL,SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &ppenum);

	/* Loop for searching our connection */
    while(hr = ppenum->Next(1,&pidlItems, &celtFetched) == S_OK && (celtFetched) == 1)
	{
		pConnections->GetDisplayNameOf(pidlItems, SHGDN_INFOLDER, &str_curr_connection_name);
		StrRetToBuf(&str_curr_connection_name, pidlItems, curr_connection_name, MAX_PATH);
		if(!strcmp(curr_connection_name,connection_name))
			goto found;
	}
	printf("Connection not found in \"Network connections\" folder.\n");
	return 0;
found:
	/* Append PIDLs */
	full_pid=Append(pidConnections,pidlItems);
	SetCurrentDirectory(desktop_path);
	if(!CreateShortCut(link_name, full_pid, "C:\\windows", 0, SW_SHOWNORMAL))
		return 0;

	ppenum->Release();
    pMalloc->Free(pidlItems);
    pMalloc->Free(pidConnections);
    pMalloc->Release();
    pConnections->Release();
    CoUninitialize();
	return 1;
}

bool __fastcall CreateShortCut(LPWSTR pwzShortCutFileName,  LPCITEMIDLIST pidl,
							   LPTSTR pszWorkingDirectory, WORD wHotKey, int iCmdShow)
{ 
	IShellLink * pSL; 
	IPersistFile * pPF; 
	HRESULT hRes; 
	// Получение экземпляра компонента "Ярлык" 
	hRes = CoCreateInstance(CLSID_ShellLink, 0,CLSCTX_INPROC_SERVER, 
							IID_IShellLink, (LPVOID *)&pSL); 
	if( SUCCEEDED(hRes) ) 
	{ 
		hRes=pSL->SetIDList(pidl);
		if(SUCCEEDED(hRes))
		{ 
			hRes = pSL->SetHotkey(wHotKey); 
			if( SUCCEEDED(hRes) ) 
			{ 
				hRes = pSL->SetShowCmd(iCmdShow); 
				if( SUCCEEDED(hRes) ) 
				{ 
					// Получение компонента хранилища параметров 
					hRes = pSL->QueryInterface(IID_IPersistFile,(LPVOID *)&pPF); 
					if( SUCCEEDED(hRes) ) 
					{ 
						// Сохранение созданного ярлыка 
						hRes = pPF->Save(pwzShortCutFileName,TRUE); 
						pPF->Release(); 
					}
				}
			}
		}
		pSL->Release(); 
	}
	return SUCCEEDED(hRes); 
}

/******************************************************************************/
/* Functions copied from http://msdn.microsoft.com */

LPITEMIDLIST GetNextItemID(LPCITEMIDLIST pidl) 
{ 
   // Check for valid pidl.
   if(pidl == NULL)
      return NULL;

   // Get the size of the specified item identifier. 
   int cb = pidl->mkid.cb; 

   // If the size is zero, it is the end of the list. 
   if (cb == 0) 
      return NULL; 

   // Add cb to pidl (casting to increment by bytes). 
   pidl = (LPITEMIDLIST) (((LPBYTE) pidl) + cb); 

   // Return NULL if it is null-terminating, or a pidl otherwise. 
   return (pidl->mkid.cb == 0) ? NULL : (LPITEMIDLIST) pidl; 
} 

/* Get size of PIDL */
UINT GetSize(LPCITEMIDLIST pidl)
{
    UINT cbTotal = 0;
    if (pidl)
    {
        cbTotal += sizeof(pidl->mkid.cb);    // Terminating null character
        while (pidl)
        {
            cbTotal += pidl->mkid.cb;
            pidl = GetNextItemID(pidl);
        }
    }
    return cbTotal;
}

/* Appending PIDLs */
LPITEMIDLIST Append(LPCITEMIDLIST pidlBase, LPCITEMIDLIST pidlAdd)
{
    if(pidlBase == NULL)
        return NULL;
    if(pidlAdd == NULL)
        return (LPITEMIDLIST)pidlBase;
    
    LPITEMIDLIST pidlNew;

    UINT cb1 = GetSize(pidlBase) - sizeof(pidlBase->mkid.cb);
    UINT cb2 = GetSize(pidlAdd);

    pidlNew = (LPITEMIDLIST)pMalloc->Alloc(cb1 + cb2);
    if (pidlNew)
    {
        CopyMemory(pidlNew, pidlBase, cb1);
        CopyMemory(((LPSTR)pidlNew) + cb1, pidlAdd, cb2);
    }
    return pidlNew;
}
