Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12 kB · 428 lines
C
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429/* COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Shell Link maker * FILE: tools/mkshelllink/mkshelllink.c * PURPOSE: Shell Link maker * PROGRAMMER: Rafal Harabien */
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>
#ifndef _MSC_VER#include <stdint.h>#elsetypedef unsigned __int8 uint8_t;typedef unsigned __int16 uint16_t;typedef unsigned __int32 uint32_t;#endif
#ifdef _WIN32#define strcasecmp _stricmp#endif
#define SW_SHOWNORMAL 1#define SW_SHOWMINNOACTIVE 7#define CSIDL_WINDOWS 0x24#define CSIDL_SYSTEM 0x25
typedef struct _GUID{ uint32_t Data1; uint16_t Data2; uint16_t Data3; uint8_t Data4[8];} GUID;
typedef struct _FILETIME { uint32_t dwLowDateTime; uint32_t dwHighDateTime;} FILETIME, *PFILETIME;
#define DEFINE_GUID2(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) const GUID name = { l,w1,w2,{ b1,b2,b3,b4,b5,b6,b7,b8 } }DEFINE_GUID2(CLSID_ShellLink,0x00021401L,0,0,0xC0,0,0,0,0,0,0,0x46);DEFINE_GUID2(CLSID_MyComputer,0x20D04FE0,0x3AEA,0x1069,0xA2,0xD8,0x08,0x00,0x2B,0x30,0x30,0x9D);
#define LINK_ID_LIST 0x01#define LINK_FILE 0x02#define LINK_DESCRIPTION 0x04#define LINK_RELATIVE_PATH 0x08#define LINK_WORKING_DIR 0x10#define LINK_CMD_LINE_ARGS 0x20#define LINK_ICON 0x40#define LINK_UNICODE 0x80
#define LOCATOR_LOCAL 0x1#define LOCATOR_NETWORK 0x2
#pragma pack(push, 1)
/* Specification: http://ithreats.files.wordpress.com/2009/05/lnk_the_windows_shortcut_file_format.pdf */
typedef struct _LNK_HEADER{ uint32_t Signature; GUID Guid; uint32_t Flags; uint32_t Attributes; FILETIME CreationTime; FILETIME ModificationTime; FILETIME LastAccessTime; uint32_t FileSize; uint32_t IconNr; uint32_t Show; uint32_t Hotkey; uint32_t Unknown; uint32_t Unknown2;} LNK_HEADER;
typedef struct _LNK_LOCATOR_INFO{ uint32_t Size; uint32_t DataOffset; uint32_t Flags; uint32_t LocalVolumeInfoOffset; uint32_t LocalBasePathnameOffset; uint32_t NetworkVolumeInfoOffset; uint32_t RemainingPathnameOffset; char Data[0];} LNK_LOCATOR_INFO;
typedef struct _LNK_LOCAL_VOLUME_INFO{ uint32_t Size; uint32_t VolumeType; /* See GetDriveType */ uint32_t SerialNumber; uint32_t VolumeNameOffset; char VolumeLabel[0];} LNK_LOCAL_VOLUME_INFO;
#define PT_GUID 0x1F#define PT_DRIVE1 0x2F#define PT_FOLDER 0x31#define PT_VALUE 0x32
typedef struct _ID_LIST_FILE{ uint16_t Size; uint8_t Type; uint8_t dummy; uint32_t dwFileSize; uint16_t uFileDate; uint16_t uFileTime; uint16_t uFileAttribs; char szName[0];} ID_LIST_FILE;
typedef struct _ID_LIST_GUID{ uint16_t Size; uint8_t Type; uint8_t dummy; GUID guid;} ID_LIST_GUID;
typedef struct _ID_LIST_DRIVE{ uint16_t Size; uint8_t Type; char szDriveName[20]; uint16_t unknown;} ID_LIST_DRIVE;
#define EXP_SPECIAL_FOLDER_SIG 0xA0000005typedef struct _EXP_SPECIAL_FOLDER{ uint32_t cbSize, dwSignature, idSpecialFolder, cbOffset;} EXP_SPECIAL_FOLDER;
#pragma pack(pop)
static const struct SPECIALFOLDER { unsigned char csidl; const char* name;} g_specialfolders[] = { { CSIDL_WINDOWS, "windows" }, { CSIDL_SYSTEM, "system" }, { 0, NULL}};
static unsigned int is_path_separator(unsigned int c){ return c == '\\' || c == '/';}
static const struct SPECIALFOLDER* get_special_folder(const char *target){ char buf[256]; strncpy(buf, target, sizeof(buf)); buf[sizeof("shell:") - 1] = '\0'; if (strcasecmp("shell:", buf)) return NULL;
target += sizeof("shell:") - 1; for (unsigned long i = 0;; ++i) { unsigned long len; const struct SPECIALFOLDER *special = &g_specialfolders[i]; if (!special->name) return NULL; len = strlen(special->name); strncpy(buf, target, sizeof(buf)); buf[len] = '\0'; if (!strcasecmp(special->name, buf) && (is_path_separator(target[len]) || !target[len])) return &g_specialfolders[i]; }}
int main(int argc, const char *argv[]){ int i; const char *pszOutputPath = "shortcut.lnk"; const char *pszTarget = NULL; const char *pszDescription = NULL; const char *pszWorkingDir = NULL; const char *pszCmdLineArgs = NULL; const char *pszIcon = NULL; char targetpath[260]; int IconNr = 0; GUID Guid = CLSID_MyComputer; int bHelp = 0, bMinimized = 0; FILE *pFile; LNK_HEADER Header; uint16_t uhTmp; uint32_t dwTmp; EXP_SPECIAL_FOLDER CsidlBlock, *pCsidlBlock = NULL;
for (i = 1; i < argc; ++i) { if (argv[i][0] != '-' && argv[i][0] != '/') pszTarget = argv[i]; else if (!strcmp(argv[i] + 1, "h")) bHelp = 1; else if (!strcmp(argv[i] + 1, "o") && i + 1 < argc) pszOutputPath = argv[++i]; else if (!strcmp(argv[i] + 1, "d") && i + 1 < argc) pszDescription = argv[++i]; else if (!strcmp(argv[i] + 1, "w") && i + 1 < argc) pszWorkingDir = argv[++i]; else if (!strcmp(argv[i] + 1, "c") && i + 1 < argc) pszCmdLineArgs = argv[++i]; else if (!strcmp(argv[i] + 1, "i") && i + 1 < argc) { pszIcon = argv[++i]; if (i + 1 < argc && isdigit(argv[i + 1][0])) IconNr = atoi(argv[++i]); } else if (!strcmp(argv[i] + 1, "m")) bMinimized = 1; else if (!strcmp(argv[i] + 1, "g") && i + 1 < argc) { unsigned Data4Tmp[8], j;
sscanf(argv[++i], "{%8x-%4hx-%4hx-%2x%2x-%2x%2x%2x%2x%2x%2x}", &Guid.Data1, &Guid.Data2, &Guid.Data3, &Data4Tmp[0], &Data4Tmp[1], &Data4Tmp[2], &Data4Tmp[3], &Data4Tmp[4], &Data4Tmp[5], &Data4Tmp[6], &Data4Tmp[7]); for (j = 0; j < 8; ++j) Guid.Data4[j] = (uint8_t)Data4Tmp[j]; } else printf("Invalid option: %s\n", argv[i]); }
if (!pszTarget || bHelp) { printf("Usage: %s [-o path][-d descr][-w path][-c cmd_line_args][-i icon_path [nr]][-h][-g guid] target\n" "-o path\tSets output path\n" "-d descr\tSets shortcut description\n" "-w path\tSets working directory for executable\n" "-c cmd_line_args\tSets command line arguments passed to program\n" "-i icon_path [nr]\tSets icon file and optionally icon index\n" "-m\tStart minimized\n" "-g guid\tSets GUID to which target path is relative. Default value is MyComputer GUID.\n" "target\tAbsolute or relative to guid specified with -g option path\n", argv[0]); return 0; }
pFile = fopen(pszOutputPath, "wb"); if (!pFile) { printf("Failed to open %s\n", pszOutputPath); return -1; }
// Header memset(&Header, 0, sizeof(Header)); Header.Signature = (uint32_t)'L'; Header.Guid = CLSID_ShellLink; Header.Flags = LINK_ID_LIST; if (pszDescription) Header.Flags |= LINK_DESCRIPTION; if (pszWorkingDir) Header.Flags |= LINK_WORKING_DIR; if (pszCmdLineArgs) Header.Flags |= LINK_CMD_LINE_ARGS; if (pszIcon) Header.Flags |= LINK_ICON; Header.IconNr = IconNr; Header.Show = bMinimized ? SW_SHOWMINNOACTIVE : SW_SHOWNORMAL; fwrite(&Header, sizeof(Header), 1, pFile);
if (Header.Flags & LINK_ID_LIST) { ID_LIST_FILE IdListFile; ID_LIST_GUID IdListGuid; ID_LIST_DRIVE IdListDrive; unsigned cbListSize = sizeof(IdListGuid) + sizeof(uint16_t), cchName; const char *pszName = pszTarget; int index = 1, specialindex = -1; const struct SPECIALFOLDER *special = get_special_folder(pszTarget);
// ID list // It seems explorer does not accept links without id list. List is relative to desktop.
if (special) { Header.Flags &= ~LINK_RELATIVE_PATH; CsidlBlock.cbSize = sizeof(CsidlBlock); CsidlBlock.dwSignature = EXP_SPECIAL_FOLDER_SIG; CsidlBlock.idSpecialFolder = special->csidl; specialindex = 3; // Skip GUID, drive and fake windows/reactos folder sprintf(targetpath, "x:\\reactos\\%s", pszTarget + sizeof("shell:") + strlen(special->name)); pszName = pszTarget = targetpath; }
if (pszName[0] && pszName[0] != ':' && pszName[1] == ':') { ++index; cbListSize += sizeof(IdListDrive); pszName += 2; while (*pszName == '\\' || *pszName == '/') ++pszName; }
while (*pszName) { cchName = 0; while (pszName[cchName] && pszName[cchName] != '\\' && pszName[cchName] != '/') ++cchName;
if (cchName != 1 || pszName[0] != '.') cbListSize += sizeof(IdListFile) + 2 * (cchName + 1);
if (++index == specialindex) { CsidlBlock.cbOffset = cbListSize - sizeof(uint16_t); pCsidlBlock = &CsidlBlock; }
pszName += cchName; while (*pszName == '\\' || *pszName == '/') ++pszName; }
uhTmp = cbListSize; fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); // size
IdListGuid.Size = sizeof(IdListGuid); IdListGuid.Type = PT_GUID; IdListGuid.dummy = 0x50; IdListGuid.guid = Guid; fwrite(&IdListGuid, sizeof(IdListGuid), 1, pFile);
pszName = pszTarget;
if (isalpha(pszName[0]) && pszName[1] == ':') { memset(&IdListDrive, 0, sizeof(IdListDrive)); IdListDrive.Size = sizeof(IdListDrive); IdListDrive.Type = PT_DRIVE1; sprintf(IdListDrive.szDriveName, "%c:\\", pszName[0]); fwrite(&IdListDrive, sizeof(IdListDrive), 1, pFile); pszName += 2; while(*pszName == '\\' || *pszName == '/') ++pszName; }
while (*pszName) { cchName = 0; while (pszName[cchName] && pszName[cchName] != '\\' && pszName[cchName] != '/') ++cchName;
if (cchName != 1 || pszName[0] != '.') { memset(&IdListFile, 0, sizeof(IdListFile)); IdListFile.Size = sizeof(IdListFile) + 2 * (cchName + 1); if (!pszName[cchName]) IdListFile.Type = PT_VALUE; // File else IdListFile.Type = PT_FOLDER; fwrite(&IdListFile, sizeof(IdListFile), 1, pFile); fwrite(pszName, cchName, 1, pFile); fputc(0, pFile); fwrite(pszName, cchName, 1, pFile); fputc(0, pFile); }
pszName += cchName; while (*pszName == '\\' || *pszName == '/') ++pszName; }
uhTmp = 0; // list end fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); }
if (Header.Flags & LINK_DESCRIPTION) { // Description uhTmp = strlen(pszDescription); fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); fputs(pszDescription, pFile); }
if (Header.Flags & LINK_RELATIVE_PATH) { // Relative Path uhTmp = strlen(pszTarget); fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); fputs(pszTarget, pFile); }
if (Header.Flags & LINK_WORKING_DIR) { // Working Dir uhTmp = strlen(pszWorkingDir); fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); fputs(pszWorkingDir, pFile); }
if (Header.Flags & LINK_CMD_LINE_ARGS) { // Command line arguments uhTmp = strlen(pszCmdLineArgs); fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); fputs(pszCmdLineArgs, pFile); }
if (Header.Flags & LINK_ICON) { // Command line arguments uhTmp = strlen(pszIcon); fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); fputs(pszIcon, pFile); }
// Extra stuff if (pCsidlBlock) fwrite(pCsidlBlock, sizeof(*pCsidlBlock), 1, pFile); dwTmp = 0; fwrite(&dwTmp, sizeof(dwTmp), 1, pFile);
fclose(pFile);
return 0;}