From ea682b690974fb54eb14d30c8cb8459c3a94917e Mon Sep 17 00:00:00 2001 From: Rafal Harabien Date: Sun, 27 Nov 2011 19:41:52 +0000 Subject: [PATCH] [MKSHELLLINK] - Add host tool for creating .lnk files svn path=/trunk/; revision=54512 --- reactos/tools/CMakeLists.txt | 1 + reactos/tools/mkshelllink/CMakeLists.txt | 2 + reactos/tools/mkshelllink/mkshelllink.c | 333 +++++++++++++++++++ reactos/tools/mkshelllink/mkshelllink.rbuild | 5 + reactos/tools/tools.rbuild | 3 + 5 files changed, 344 insertions(+) create mode 100644 reactos/tools/mkshelllink/CMakeLists.txt create mode 100644 reactos/tools/mkshelllink/mkshelllink.c create mode 100644 reactos/tools/mkshelllink/mkshelllink.rbuild diff --git a/reactos/tools/CMakeLists.txt b/reactos/tools/CMakeLists.txt index d5821685c40..13354b748b2 100644 --- a/reactos/tools/CMakeLists.txt +++ b/reactos/tools/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(mkhive) add_subdirectory(obj2bin) add_subdirectory(spec2def) add_subdirectory(unicode) +add_subdirectory(mkshelllink) if(NOT MSVC) add_subdirectory(rsym) diff --git a/reactos/tools/mkshelllink/CMakeLists.txt b/reactos/tools/mkshelllink/CMakeLists.txt new file mode 100644 index 00000000000..1f4814cd595 --- /dev/null +++ b/reactos/tools/mkshelllink/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_executable(mkshelllink mkshelllink.c) diff --git a/reactos/tools/mkshelllink/mkshelllink.c b/reactos/tools/mkshelllink/mkshelllink.c new file mode 100644 index 00000000000..3a2b8bc028d --- /dev/null +++ b/reactos/tools/mkshelllink/mkshelllink.c @@ -0,0 +1,333 @@ +/* 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 + +#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 +{ + DWORD Signature; + GUID Guid; + DWORD Flags; + DWORD Attributes; + FILETIME CreationTime; + FILETIME ModificationTime; + FILETIME LastAccessTime; + DWORD FileSize; + DWORD IconNr; + DWORD Show; + DWORD Hotkey; + DWORD Unknown; + DWORD Unknown2; +} LNK_HEADER; + +typedef struct _LNK_LOCATOR_INFO +{ + DWORD Size; + DWORD DataOffset; + DWORD Flags; + DWORD LocalVolumeInfoOffset; + DWORD LocalBasePathnameOffset; + DWORD NetworkVolumeInfoOffset; + DWORD RemainingPathnameOffset; + char Data[0]; +} LNK_LOCATOR_INFO; + +typedef struct _LNK_LOCAL_VOLUME_INFO +{ + DWORD Size; + DWORD VolumeType; /* See GetDriveType */ + DWORD SerialNumber; + DWORD 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 +{ + WORD Size; + BYTE Type; + BYTE dummy; + DWORD dwFileSize; + WORD uFileDate; + WORD uFileTime; + WORD uFileAttribs; + char szName[0]; +} ID_LIST_FILE; + +typedef struct _ID_LIST_GUID +{ + WORD Size; + BYTE Type; + BYTE dummy; + GUID guid; +} ID_LIST_GUID; + +typedef struct _ID_LIST_DRIVE +{ + WORD Size; + BYTE Type; + CHAR szDriveName[20]; + WORD 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; + BOOL bHelp = FALSE, bMinimized = FALSE; + FILE *pFile; + LNK_HEADER Header; + USHORT uhTmp; + DWORD dwTmp; + + for (i = 1; i < argc; ++i) + { + if (argv[i][0] != '-' && argv[i][0] != '/') + pszTarget = argv[i]; + else if (!stricmp(argv[i] + 1, "h")) + bHelp = TRUE; + else if (!stricmp(argv[i] + 1, "o") && i + 1 < argc) + pszOutputPath = argv[++i]; + else if (!stricmp(argv[i] + 1, "d") && i + 1 < argc) + pszDescription = argv[++i]; + else if (!stricmp(argv[i] + 1, "w") && i + 1 < argc) + pszWorkingDir = argv[++i]; + else if (!stricmp(argv[i] + 1, "c") && i + 1 < argc) + pszCmdLineArgs = argv[++i]; + else if (!stricmp(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 (!stricmp(argv[i] + 1, "m")) + bMinimized = TRUE; + else if (!stricmp(argv[i] + 1, "g") && i + 1 < argc) + { + unsigned Data4Tmp[8], j; + + sscanf(argv[++i], "{%8lx-%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] = (BYTE)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 = (DWORD)'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(WORD), 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; +} diff --git a/reactos/tools/mkshelllink/mkshelllink.rbuild b/reactos/tools/mkshelllink/mkshelllink.rbuild new file mode 100644 index 00000000000..afa27014578 --- /dev/null +++ b/reactos/tools/mkshelllink/mkshelllink.rbuild @@ -0,0 +1,5 @@ + + + + mkshelllink.c + diff --git a/reactos/tools/tools.rbuild b/reactos/tools/tools.rbuild index 2681dbfa154..324db18f2d0 100644 --- a/reactos/tools/tools.rbuild +++ b/reactos/tools/tools.rbuild @@ -46,4 +46,7 @@ + + +