1 /* COPYRIGHT: See COPYING in the top level directory 2 * PROJECT: ReactOS Shell Link maker 3 * FILE: tools/mkshelllink/mkshelllink.c 4 * PURPOSE: Shell Link maker 5 * PROGRAMMER: Rafal Harabien 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <ctype.h> 12 13 #ifndef _MSC_VER 14 #include <stdint.h> 15 #else 16 typedef unsigned __int8 uint8_t; 17 typedef unsigned __int16 uint16_t; 18 typedef unsigned __int32 uint32_t; 19 #endif 20 21 #define SW_SHOWNORMAL 1 22 #define SW_SHOWMINNOACTIVE 7 23 24 typedef struct _GUID 25 { 26 uint32_t Data1; 27 uint16_t Data2; 28 uint16_t Data3; 29 uint8_t Data4[8]; 30 } GUID; 31 32 typedef struct _FILETIME { 33 uint32_t dwLowDateTime; 34 uint32_t dwHighDateTime; 35 } FILETIME, *PFILETIME; 36 37 #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 } } 38 DEFINE_GUID2(CLSID_ShellLink,0x00021401L,0,0,0xC0,0,0,0,0,0,0,0x46); 39 DEFINE_GUID2(CLSID_MyComputer,0x20D04FE0,0x3AEA,0x1069,0xA2,0xD8,0x08,0x00,0x2B,0x30,0x30,0x9D); 40 41 #define LINK_ID_LIST 0x01 42 #define LINK_FILE 0x02 43 #define LINK_DESCRIPTION 0x04 44 #define LINK_RELATIVE_PATH 0x08 45 #define LINK_WORKING_DIR 0x10 46 #define LINK_CMD_LINE_ARGS 0x20 47 #define LINK_ICON 0x40 48 #define LINK_UNICODE 0x80 49 50 #define LOCATOR_LOCAL 0x1 51 #define LOCATOR_NETWORK 0x2 52 53 #pragma pack(push, 1) 54 55 /* Specification: http://ithreats.files.wordpress.com/2009/05/lnk_the_windows_shortcut_file_format.pdf */ 56 57 typedef struct _LNK_HEADER 58 { 59 uint32_t Signature; 60 GUID Guid; 61 uint32_t Flags; 62 uint32_t Attributes; 63 FILETIME CreationTime; 64 FILETIME ModificationTime; 65 FILETIME LastAccessTime; 66 uint32_t FileSize; 67 uint32_t IconNr; 68 uint32_t Show; 69 uint32_t Hotkey; 70 uint32_t Unknown; 71 uint32_t Unknown2; 72 } LNK_HEADER; 73 74 typedef struct _LNK_LOCATOR_INFO 75 { 76 uint32_t Size; 77 uint32_t DataOffset; 78 uint32_t Flags; 79 uint32_t LocalVolumeInfoOffset; 80 uint32_t LocalBasePathnameOffset; 81 uint32_t NetworkVolumeInfoOffset; 82 uint32_t RemainingPathnameOffset; 83 char Data[0]; 84 } LNK_LOCATOR_INFO; 85 86 typedef struct _LNK_LOCAL_VOLUME_INFO 87 { 88 uint32_t Size; 89 uint32_t VolumeType; /* See GetDriveType */ 90 uint32_t SerialNumber; 91 uint32_t VolumeNameOffset; 92 char VolumeLabel[0]; 93 } LNK_LOCAL_VOLUME_INFO; 94 95 #define PT_GUID 0x1F 96 #define PT_DRIVE1 0x2F 97 #define PT_FOLDER 0x31 98 #define PT_VALUE 0x32 99 100 typedef struct _ID_LIST_FILE 101 { 102 uint16_t Size; 103 uint8_t Type; 104 uint8_t dummy; 105 uint32_t dwFileSize; 106 uint16_t uFileDate; 107 uint16_t uFileTime; 108 uint16_t uFileAttribs; 109 char szName[0]; 110 } ID_LIST_FILE; 111 112 typedef struct _ID_LIST_GUID 113 { 114 uint16_t Size; 115 uint8_t Type; 116 uint8_t dummy; 117 GUID guid; 118 } ID_LIST_GUID; 119 120 typedef struct _ID_LIST_DRIVE 121 { 122 uint16_t Size; 123 uint8_t Type; 124 char szDriveName[20]; 125 uint16_t unknown; 126 } ID_LIST_DRIVE; 127 128 #pragma pack(pop) 129 130 int main(int argc, const char *argv[]) 131 { 132 int i; 133 const char *pszOutputPath = "shortcut.lnk"; 134 const char *pszTarget = NULL; 135 const char *pszDescription = "Description"; 136 const char *pszWorkingDir = NULL; 137 const char *pszCmdLineArgs = NULL; 138 const char *pszIcon = NULL; 139 int IconNr = 0; 140 GUID Guid = CLSID_MyComputer; 141 int bHelp = 0, bMinimized = 0; 142 FILE *pFile; 143 LNK_HEADER Header; 144 uint16_t uhTmp; 145 uint32_t dwTmp; 146 147 for (i = 1; i < argc; ++i) 148 { 149 if (argv[i][0] != '-' && argv[i][0] != '/') 150 pszTarget = argv[i]; 151 else if (!strcmp(argv[i] + 1, "h")) 152 bHelp = 1; 153 else if (!strcmp(argv[i] + 1, "o") && i + 1 < argc) 154 pszOutputPath = argv[++i]; 155 else if (!strcmp(argv[i] + 1, "d") && i + 1 < argc) 156 pszDescription = argv[++i]; 157 else if (!strcmp(argv[i] + 1, "w") && i + 1 < argc) 158 pszWorkingDir = argv[++i]; 159 else if (!strcmp(argv[i] + 1, "c") && i + 1 < argc) 160 pszCmdLineArgs = argv[++i]; 161 else if (!strcmp(argv[i] + 1, "i") && i + 1 < argc) 162 { 163 pszIcon = argv[++i]; 164 if (i + 1 < argc && isdigit(argv[i + 1][0])) 165 IconNr = atoi(argv[++i]); 166 } 167 else if (!strcmp(argv[i] + 1, "m")) 168 bMinimized = 1; 169 else if (!strcmp(argv[i] + 1, "g") && i + 1 < argc) 170 { 171 unsigned Data4Tmp[8], j; 172 173 sscanf(argv[++i], "{%8x-%4hx-%4hx-%2x%2x-%2x%2x%2x%2x%2x%2x}", 174 &Guid.Data1, &Guid.Data2, &Guid.Data3, 175 &Data4Tmp[0], &Data4Tmp[1], &Data4Tmp[2], &Data4Tmp[3], 176 &Data4Tmp[4], &Data4Tmp[5], &Data4Tmp[6], &Data4Tmp[7]); 177 for (j = 0; j < 8; ++j) 178 Guid.Data4[j] = (uint8_t)Data4Tmp[j]; 179 } 180 else 181 printf("Invalid option: %s\n", argv[i]); 182 } 183 184 if (!pszTarget || bHelp) 185 { 186 printf("Usage: %s [-o path][-d descr][-w path][-c cmd_line_args][-i icon_path [nr]][-h][-g guid] target\n" 187 "-o path\tSets output path\n" 188 "-d descr\tSets shortcut description\n" 189 "-w path\tSets working directory for executable\n" 190 "-c cmd_line_args\tSets command line arguments passed to program\n" 191 "-i icon_path [nr]\tSets icon file and optionally icon index\n" 192 "-m\tStart minimized\n" 193 "-g guid\tSets GUID to which target path is relative. Default value is MyComputer GUID.\n" 194 "target\tAbsolute or relative to guid specified with -g option path\n", argv[0]); 195 return 0; 196 } 197 198 pFile = fopen(pszOutputPath, "wb"); 199 if (!pFile) 200 { 201 printf("Failed to open %s\n", pszOutputPath); 202 return -1; 203 } 204 205 // Header 206 memset(&Header, 0, sizeof(Header)); 207 Header.Signature = (uint32_t)'L'; 208 Header.Guid = CLSID_ShellLink; 209 Header.Flags = LINK_ID_LIST; 210 if (pszDescription) 211 Header.Flags |= LINK_DESCRIPTION; 212 if (pszWorkingDir) 213 Header.Flags |= LINK_WORKING_DIR; 214 if (pszCmdLineArgs) 215 Header.Flags |= LINK_CMD_LINE_ARGS; 216 if (pszIcon) 217 Header.Flags |= LINK_ICON; 218 Header.IconNr = IconNr; 219 Header.Show = bMinimized ? SW_SHOWMINNOACTIVE : SW_SHOWNORMAL; 220 fwrite(&Header, sizeof(Header), 1, pFile); 221 222 if (Header.Flags & LINK_ID_LIST) 223 { 224 ID_LIST_FILE IdListFile; 225 ID_LIST_GUID IdListGuid; 226 ID_LIST_DRIVE IdListDrive; 227 unsigned cbListSize = sizeof(IdListGuid) + sizeof(uint16_t), cchName; 228 const char *pszName = pszTarget; 229 230 // ID list 231 // It seems explorer does not accept links without id list. List is relative to desktop. 232 233 pszName = pszTarget; 234 235 if (pszName[0] && pszName[1] == ':') 236 { 237 cbListSize += sizeof(IdListDrive); 238 pszName += 2; 239 while (*pszName == '\\' || *pszName == '/') 240 ++pszName; 241 } 242 243 while (*pszName) 244 { 245 cchName = 0; 246 while (pszName[cchName] && pszName[cchName] != '\\' && pszName[cchName] != '/') 247 ++cchName; 248 249 if (cchName != 1 || pszName[0] != '.') 250 cbListSize += sizeof(IdListFile) + 2 * (cchName + 1); 251 252 pszName += cchName; 253 while (*pszName == '\\' || *pszName == '/') 254 ++pszName; 255 } 256 257 uhTmp = cbListSize; 258 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); // size 259 260 IdListGuid.Size = sizeof(IdListGuid); 261 IdListGuid.Type = PT_GUID; 262 IdListGuid.dummy = 0x50; 263 IdListGuid.guid = Guid; 264 fwrite(&IdListGuid, sizeof(IdListGuid), 1, pFile); 265 266 pszName = pszTarget; 267 268 if (isalpha(pszName[0]) && pszName[1] == ':') 269 { 270 memset(&IdListDrive, 0, sizeof(IdListDrive)); 271 IdListDrive.Size = sizeof(IdListDrive); 272 IdListDrive.Type = PT_DRIVE1; 273 sprintf(IdListDrive.szDriveName, "%c:\\", pszName[0]); 274 fwrite(&IdListDrive, sizeof(IdListDrive), 1, pFile); 275 pszName += 2; 276 while(*pszName == '\\' || *pszName == '/') 277 ++pszName; 278 } 279 280 while (*pszName) 281 { 282 cchName = 0; 283 while (pszName[cchName] && pszName[cchName] != '\\' && pszName[cchName] != '/') 284 ++cchName; 285 286 if (cchName != 1 || pszName[0] != '.') 287 { 288 memset(&IdListFile, 0, sizeof(IdListFile)); 289 IdListFile.Size = sizeof(IdListFile) + 2 * (cchName + 1); 290 if (!pszName[cchName]) 291 IdListFile.Type = PT_VALUE; // File 292 else 293 IdListFile.Type = PT_FOLDER; 294 fwrite(&IdListFile, sizeof(IdListFile), 1, pFile); 295 fwrite(pszName, cchName, 1, pFile); 296 fputc(0, pFile); 297 fwrite(pszName, cchName, 1, pFile); 298 fputc(0, pFile); 299 } 300 301 pszName += cchName; 302 while (*pszName == '\\' || *pszName == '/') 303 ++pszName; 304 } 305 306 uhTmp = 0; // list end 307 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); 308 } 309 310 if (Header.Flags & LINK_DESCRIPTION) 311 { 312 // Dscription 313 uhTmp = strlen(pszDescription); 314 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); 315 fputs(pszDescription, pFile); 316 } 317 318 if (Header.Flags & LINK_RELATIVE_PATH) 319 { 320 // Relative Path 321 uhTmp = strlen(pszTarget); 322 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); 323 fputs(pszTarget, pFile); 324 } 325 326 if (Header.Flags & LINK_WORKING_DIR) 327 { 328 // Working Dir 329 uhTmp = strlen(pszWorkingDir); 330 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); 331 fputs(pszWorkingDir, pFile); 332 } 333 334 if (Header.Flags & LINK_CMD_LINE_ARGS) 335 { 336 // Command line arguments 337 uhTmp = strlen(pszCmdLineArgs); 338 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); 339 fputs(pszCmdLineArgs, pFile); 340 } 341 342 if (Header.Flags & LINK_ICON) 343 { 344 // Command line arguments 345 uhTmp = strlen(pszIcon); 346 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); 347 fputs(pszIcon, pFile); 348 } 349 350 // Extra stuff 351 dwTmp = 0; 352 fwrite(&dwTmp, sizeof(dwTmp), 1, pFile); 353 354 fclose(pFile); 355 356 return 0; 357 } 358