/* 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 #include #include #include #ifndef _MSC_VER #include #else typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif #define SW_SHOWNORMAL 1 #define SW_SHOWMINNOACTIVE 7 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; #pragma pack(pop) int main(int argc, const char *argv[]) { unsigned i; const char *pszOutputPath = "shortcut.lnk"; const char *pszTarget = NULL; const char *pszDescription = "Description"; const char *pszWorkingDir = NULL; const char *pszCmdLineArgs = NULL; const char *pszIcon = NULL; int IconNr = 0; GUID Guid = CLSID_MyComputer; int bHelp = 0, bMinimized = 0; FILE *pFile; LNK_HEADER Header; uint16_t uhTmp; uint32_t dwTmp; 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; // ID list // It seems explorer does not accept links without id list. List is relative to desktop. pszName = pszTarget; if (pszName[0] && pszName[1] == ':') { 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); 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) { // Dscription 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 dwTmp = 0; fwrite(&dwTmp, sizeof(dwTmp), 1, pFile); fclose(pFile); return 0; }