Создание ярлыка
Есть еще ссылка: http://www.sources.ru/builder/faq/015.html - но там не выполняется самое первое условие и соответственно весь код ни к чему не ведет. :(
Хочется: что бы кто-нибудь растолковал, как все же можно сделать ярлык на WIN API, пусть с COM (без него ведь ни как?). Необходимый минимум. Пожалуйста не предлагать самому разобраться с COM - всему свое время, а время COM для меня еще не настало. Времени как раз не хватает, а ярлык сделать очень нужно. Но т.к. пока работаю совсем в другом направлении, то не успеваю разобраться сам. В общем - буду благодарен за любой дельный совет.
ЗЫ: Сюда: http://www.codenet.ru/progr/delphi/quest036.php - то же желательно не посылать, т.к. там вообще темный лес для меня. Но если уж посылать, то с комментариями по поводу кода.
#include <shlobj.h>
#include <windows.h>
#define HOTKEY(modifier,key) ((((modifier)&0xff)<<8)|((key)&0xff))
// Создание ярлыка
// Входные параметры:
// pwzShortCutFileName - путь и имя ярлыка, например, "C:\\Блокнот.lnk"
// Если не указан путь, ярлык будет создан в папке, указанной в следующем параметре.
// Прим.: Windows сама НЕ добавляет к имени расширение .lnk
// pszPathAndFileName - путь и имя exe-файла, например, "C:\\Windows\\NotePad.Exe"
// pszWorkingDirectory - рабочий каталог, например, "C:\\Windows"
// pszArguments - аргументы командной строки, например, "C:\\Doc\\Text.Txt"
// wHotKey - горячая клавиша, например, для Ctrl+Alt+A HOTKEY(HOTKEYF_ALT|HOTKEYF_CONTROL,'A')
// iCmdShow - начальный вид, например, SW_SHOWNORMAL
// pszIconFileName - путь и имя файла, содержащего иконку, например, "C:\\Windows\\NotePad.Exe"
// int iIconIndex - индекс иконки в файле, нумеруется с 0
bool __fastcall CreateShortCut(
LPWSTR pwzShortCutFileName,
LPTSTR pszPathAndFileName,
LPTSTR pszWorkingDirectory,
LPTSTR pszArguments,
WORD wHotKey,
int iCmdShow,
LPTSTR pszIconFileName,
int iIconIndex)
{
IShellLink * pSL;
IPersistFile * pPF;
HRESULT hRes;
if( CoInitialize(NULL) != S_OK)
return false;
// Получение экземпляра компонента "Ярлык"
hRes = CoCreateInstance(CLSID_ShellLink, 0, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&pSL);
if( SUCCEEDED(hRes) )
{
hRes = pSL->SetPath(pszPathAndFileName);
if( SUCCEEDED(hRes) )
{
//hRes = pSL->SetArguments(pszArguments);
//if( SUCCEEDED(hRes) )
{
hRes = pSL->SetWorkingDirectory(pszWorkingDirectory);
if( SUCCEEDED(hRes) )
{
hRes = pSL->SetIconLocation(pszIconFileName,iIconIndex);
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);
} //bool __fastcall CreateShortCut
void main(void)
{
//WSTR
bool b = CreateShortCut(L"C:\\Note2.lnk", _T("D:\\Windows\\NotePad.Exe"), _T("D:\\Windows"), _T(""), 0,
SW_SHOWNORMAL, _T("D:\\Windows\\NotePad.Exe"), 0);
}
Чуть переделаный пример
Благодарю! Всего-то у нас не хватало функции CoInitialize(). Не мудрено, что ничего не работало. Я как только ее название увидел - сразу понял что она делает. Все понятно стало, во всяком случае для данного примера.
Теперь осталось научиться делать то же самое для VPN-подключения. Но это я постараюсь сам.
Значит так, что бы сделать ярлык на не файловый обект нам надо привязать его к объекту через IShellLink::SetIDList() метод. Я сделал перечисление сетевых подключений, дело осталось за малым - получить параметр для SetIDList(). Как я понял, получить его можно при помощи IShellFolder::BindToObject(). Вот тут-то и загвоздка! Не получается - в последний параметр BindToObject возвращается NULL, что означает ошибку. Значит опять что-то неправильно понял. В приведенном коде удален за ненадобностью цикл перечисления всех подключений. Т.е. мы останавливаемся на первом найденом.
Делаем так:
LPITEMIDLIST pidConnections = NULL;
LPITEMIDLIST pidlItems = NULL;
IShellFolder *psfFirstFolder = NULL;
IShellFolder *psfDeskTop = NULL;
IShellFolder *pConnections = NULL;
LPENUMIDLIST ppenum = NULL;
ULONG celtFetched;
HRESULT hr;
STRRET strDispName;
TCHAR pszDisplayName[MAX_PATH];
ULONG uAttr;
CoInitialize( NULL );
hr = SHGetMalloc(&pMalloc);
hr = SHGetFolderLocation(NULL, CSIDL_CONNECTIONS, NULL, NULL, &pidConnections);//ВЫБИРАЕМ ПАПКУ "СЕТЕВЫЕ ПОДКЛЮЧЕНИЯ"
hr = SHGetDesktopFolder(&psfDeskTop);
hr = psfDeskTop->BindToObject(pidConnections, NULL, IID_IShellFolder, (LPVOID *) &pConnections);
hr = pConnections->EnumObjects(NULL,SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &ppenum);
hr = ppenum->Next(1,&pidlItems, &celtFetched);// == S_OK && (celtFetched) == 1)
pConnections->GetDisplayNameOf(pidlItems, SHGDN_INFOLDER, &strDispName);
StrRetToBuf(&strDispName, pidlItems, pszDisplayName, MAX_PATH);
rus_out(pszDisplayName);//ВЫВОДИМ ИМЯ ПОДКЛЮЧЕНИЯ
if(!psfFirstFolder)
{
uAttr = SFGAO_FOLDER;//МОЖЕТ ТУТ ЧТО НАДО ПОСТАВИТЬ ДРУГОЕ?
pConnections->GetAttributesOf(1, (LPCITEMIDLIST *) &pidlItems, &uAttr);
hr = pConnections->BindToObject(pidlItems, NULL, IID_IUnknown, (LPVOID *) &psfFirstFolder);//КАК МНЕ КАЖЕТСЯ ДОЛЖНО РАБОТАТ, НО...
if(!psfFirstFolder)printf("Error!\n");//ВОТ ТУТ ПОЛУЧАЕМ ОШИБКУ
else printf("Ok!\n");
}
pMalloc->Free(pidlItems);
printf("\n");
ppenum->Release();
pMalloc->Free(pidConnections);
pMalloc->Release();
pConnections->Release();
// psfFirstFolder->Release();//ПОЧЕМУ ТО ВЫСКАКИВАЕТ ОШИБКА ПРИ РАБОТЕ С НЕФАЙЛОВЫМИ ОБЪЕКТАМИ
CoUninitialize();
Где я снова накосячил?
Блин, ниасилил. :(
Значит так, что бы сделать ярлык на не файловый обект нам надо привязать его к объекту через IShellLink::SetIDList() метод. Я сделал перечисление сетевых подключений, дело осталось за малым - получить параметр для SetIDList(). Как я понял, получить его можно при помощи IShellFolder::BindToObject(). Вот тут-то и загвоздка! Не получается - в последний параметр BindToObject возвращается NULL, что означает ошибку. Значит опять что-то неправильно понял. В приведенном коде удален за ненадобностью цикл перечисления всех подключений. Т.е. мы останавливаемся на первом найденом.
Делаем так:
LPITEMIDLIST pidConnections = NULL;
LPITEMIDLIST pidlItems = NULL;
IShellFolder *psfFirstFolder = NULL;
IShellFolder *psfDeskTop = NULL;
IShellFolder *pConnections = NULL;
LPENUMIDLIST ppenum = NULL;
ULONG celtFetched;
HRESULT hr;
STRRET strDispName;
TCHAR pszDisplayName[MAX_PATH];
ULONG uAttr;
CoInitialize( NULL );
hr = SHGetMalloc(&pMalloc);
hr = SHGetFolderLocation(NULL, CSIDL_CONNECTIONS, NULL, NULL, &pidConnections);//ВЫБИРАЕМ ПАПКУ "СЕТЕВЫЕ ПОДКЛЮЧЕНИЯ"
hr = SHGetDesktopFolder(&psfDeskTop);
hr = psfDeskTop->BindToObject(pidConnections, NULL, IID_IShellFolder, (LPVOID *) &pConnections);
hr = pConnections->EnumObjects(NULL,SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &ppenum);
hr = ppenum->Next(1,&pidlItems, &celtFetched);// == S_OK && (celtFetched) == 1)
pConnections->GetDisplayNameOf(pidlItems, SHGDN_INFOLDER, &strDispName);
StrRetToBuf(&strDispName, pidlItems, pszDisplayName, MAX_PATH);
rus_out(pszDisplayName);//ВЫВОДИМ ИМЯ ПОДКЛЮЧЕНИЯ
if(!psfFirstFolder)
{
uAttr = SFGAO_FOLDER;//МОЖЕТ ТУТ ЧТО НАДО ПОСТАВИТЬ ДРУГОЕ?
pConnections->GetAttributesOf(1, (LPCITEMIDLIST *) &pidlItems, &uAttr);
hr = pConnections->BindToObject(pidlItems, NULL, IID_IUnknown, (LPVOID *) &psfFirstFolder);//КАК МНЕ КАЖЕТСЯ ДОЛЖНО РАБОТАТ, НО...
if(!psfFirstFolder)printf("Error!\n");//ВОТ ТУТ ПОЛУЧАЕМ ОШИБКУ
else printf("Ok!\n");
}
pMalloc->Free(pidlItems);
printf("\n");
ppenum->Release();
pMalloc->Free(pidConnections);
pMalloc->Release();
pConnections->Release();
// psfFirstFolder->Release();//ПОЧЕМУ ТО ВЫСКАКИВАЕТ ОШИБКА ПРИ РАБОТЕ С НЕФАЙЛОВЫМИ ОБЪЕКТАМИ
CoUninitialize();
Где я снова накосячил?
Ну во первых у тебя нужный PIDL уже есть после
hr = ppenum->Next(1,&pidlItems, &celtFetched);
В принципе pidlItems это и есть то что нужно то есть идентификатор нужного тебе обекта
а дальнейшие твои действия с
if(!psfFirstFolder)
{
uAttr = SFGAO_FOLDER;//МОЖЕТ ТУТ ЧТО НАДО ПОСТАВИТЬ ДРУГОЕ?
pConnections->GetAttributesOf(1, (LPCITEMIDLIST *) &pidlItems, &uAttr);
hr = pConnections->BindToObject(pidlItems, NULL, IID_IUnknown, (LPVOID *) &psfFirstFolder);//КАК МНЕ КАЖЕТСЯ ДОЛЖНО РАБОТАТ, НО...
if(!psfFirstFolder)printf("Error!\n");//ВОТ ТУТ ПОЛУЧАЕМ ОШИБКУ
else printf("Ok!\n");
}
Нужны были бы только в случае наличия подпапок в Сетевых подключениях,чего не может быть,соответсвенно и все это действовать не будет,да и ктомуже ты в
hr = pConnections->BindToObject(pidlItems, NULL, IID_IUnknown, (LPVOID *) &psfFirstFolder);
указываеш IID_IUnknown хотя должен был бы указать IID_IShellFolder.
Вобщем после перечеслинея и нахождения нужного PIDL ты можеш получить путь к обекту
hr = ppenum->Next(1,&pidlItems, &celtFetched);
pConnections-GetDisplayNameOf(pidlItems,SHGDN_FORPARSING, &strDispName);
StrRetToBuf(&strDispName, pidlItems,szDisplayName, MAX_PATH);
SFGAOF sfg=0;
int u=MultiByteToWideChar(CP_ACP,MB_COMPOSITE,pszDisplayName,-1,lpchw,MAX_PATH);
hr=SHParseDisplayName(lpchw,0,&pidlItems2,SFGAO_LINK,&sfg);//по идее здесь должны мы получить полный PIDL но почему то это не работает
vitaly2003s - ты был не совсем прав, надо не путь получить, или имя, а именно PIDL объекта, при чем полный. GetDisplayNameOf дает нам не путь, а только имя которое выводится на обозрение пользователю. Т.е. в оптимальном варианте - это лишь часть пути, как например для Program Files будет "Program Files", а не "C:\Program Files". А если мы имеем дело с виртуальными объектами вроде подключений, то это даже не часть пути.
Полный PIDL в моем случае получается при сложении PIDL папки "Сетевые подключения" и PIDL искомого подключения.
Но все равно большое спасибо, а то я долго наверное еще тупил бы пытаясь получить то, что у меня уже есть :).
Вот код:
#include <shlwapi.h>
#include <stdio.h>
void rus_out(char *str)
{
char out_str[255];
CharToOem(str,out_str);
printf("%s\n",out_str);
return;
}
LPMALLOC pMalloc;
bool __fastcall CreateShortCut(LPWSTR pwzShortCutFileName, LPCITEMIDLIST pidl,
LPTSTR pszWorkingDirectory, WORD wHotKey, int iCmdShow);
LPITEMIDLIST Append(LPCITEMIDLIST pidlBase, LPCITEMIDLIST pidlAdd);
void short_cut_startup(char *connection_name, LPWSTR link_name);
main()
{
short_cut_startup("connection_name",L"shortcut_file_name.lnk");
return 0;
}
//ОСНОВНАЯ ФУНКЦИЯ СОЗДАНИЯ ЯРЛЫЧКА НА РАБОЧЕМ СТОЛЕ
void 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]="";//ИМЯ ПОДКЛЮЧЕНИЯ
TCHAR desktop_path[MAX_PATH]="";//ПУТЬ К РАБОЧЕМУ СТОЛУ
TCHAR full_link_name[MAX_PATH]="";
LPITEMIDLIST full_pid;
CoInitialize( NULL );
//ВЫДЕЛЯЕМ ПАМЯТЬ ДЛЯ РАБОТЫ С ОБЕКТАМИ Namespace
hr = SHGetMalloc(&pMalloc);
hr = SHGetFolderLocation(NULL, CSIDL_CONNECTIONS, NULL, NULL, &pidConnections);
//УЗНАЕМ ПУТЬ К РАБОЧЕМУ СТОЛУ
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);
//ВОТ ТУТ МОЖНО ЗАМУТИТ ЦИКЛ
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))break;
}
//СКЛАДЫВАЕМ ПОЛУЧЕННЫЕ PIDLs
full_pid=Append(pidConnections,pidlItems);
SetCurrentDirectory(desktop_path);
CreateShortCut(link_name,full_pid,"C:\\windows",0,SW_SHOWNORMAL);
printf("\n");
ppenum->Release();
pMalloc->Free(pidlItems);
pMalloc->Free(pidConnections);
pMalloc->Release();
pConnections->Release();
CoUninitialize();
}
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);
if( SUCCEEDED(hRes) ) printf("Save successed!\n");
else printf("Save error!\n");
pPF->Release();
}// else printf("Error 4\n");
}// else printf("Error 3\n");
}// else printf("Error 2\n");
}// else printf("Error 1\n");
pSL->Release();
}// else printf("Error 0\n");
return SUCCEEDED(hRes);
}
//************************************************************
//ФУНКЦИИ ДЛЯ РАБОТЫ С PIDLs - тупо скопированы с 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;
}
//УЗНАЕМ РАЗМЕР ЗАДАННОЙ 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;
}
//СКЛАДЫВАЕМ 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;
}
Если в нем что криво и неправильно, и не лень разбираться, то любые предложения и исправления будут восприняты с благодарностью. Я щас уже не в состоянии.
Главное сейчас - он работает!
А так у тебя щя код ниче,вполне приемлем в данной ситуации. Мог бы впринципе ты выделить все это в класс,думаю позднее он бы те еще не раз пригодился,и сделать его более универсальным.
Да наверное, надо причесать его и попросить в FAQ выложить, или еще куда, а то я весь инет перерыл (по крайней мере русский) пытаясь найти как это делается. В итоге пришлось самому делать :)
Сразу скажу, что этот код не работает на Visual Studio 6 SP5.
Ну, ХЗ, я в VC 7.0 делал и XP SP2. Хотя работает нормально во всех виндах начиная с 2000-ной - точно. Может и просто в NT нормально. Не знаю, не где проверить, да и не актуально уже.