mirror of
https://github.com/reactos/reactos.git
synced 2026-06-21 02:16:05 +08:00
[MKSHELLLINK] Support creating Unicode-aware shortcuts (#8936)
Support the creation of shortcuts that can use Unicode versions of their name (description), relative path, working directory, command- line arguments, and icon location. This option can be selected at runtime with a switch. Additionally: - Ensure that `wchar_t` is 16-bit wide. - Introduce and use a "poor-man" ANSI-to-UTF16LE `my_mbstowcs()` routine to convert the ANSI strings, for the Unicode scenario mentioned above. We cannot use the host mbstowcs() routine, since on *nix systems the iconv library being used may have been compiled with a 32-bit `wchar_t` (even if the tool is compiled with: `-fshort-wchar -fwide-exec-charset=UTF-16LE`), as this is the case with the GitHub actions build bots.
This commit is contained in:
@@ -20,7 +20,7 @@ endif()
|
||||
add_host_tool(bin2c bin2c.c)
|
||||
add_host_tool(gendib gendib/gendib.c)
|
||||
add_host_tool(geninc geninc/geninc.c)
|
||||
add_host_tool(mkshelllink mkshelllink/mkshelllink.c)
|
||||
|
||||
add_host_tool(obj2bin obj2bin/obj2bin.c)
|
||||
target_link_libraries(obj2bin PRIVATE host_includes)
|
||||
|
||||
@@ -39,6 +39,7 @@ if(ARCH STREQUAL "i386")
|
||||
endif()
|
||||
add_subdirectory(mkhive)
|
||||
add_subdirectory(mkisofs)
|
||||
add_subdirectory(mkshelllink)
|
||||
if(ARCH STREQUAL "i386")
|
||||
add_subdirectory(rsym)
|
||||
endif()
|
||||
|
||||
5
sdk/tools/mkshelllink/CMakeLists.txt
Normal file
5
sdk/tools/mkshelllink/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
add_host_tool(mkshelllink mkshelllink.c)
|
||||
if(NOT MSVC)
|
||||
target_compile_options(mkshelllink PRIVATE "-fshort-wchar")
|
||||
endif()
|
||||
@@ -4,6 +4,7 @@
|
||||
* PURPOSE: Shell Link maker
|
||||
* COPYRIGHT: Copyright 2011 Rafal Harabien <rafalh@reactos.org>
|
||||
* Copyright 2024 Whindmar Saksit <whindsaks@proton.me>
|
||||
* Copyright 2026 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
|
||||
*/
|
||||
|
||||
/* INCLUDES ******************************************************************/
|
||||
@@ -27,6 +28,15 @@ typedef unsigned __int32 uint32_t;
|
||||
typedef __int32 int32_t;
|
||||
#endif
|
||||
|
||||
C_ASSERT(sizeof(wchar_t) == sizeof(uint16_t)); // Ensure wchar_t is 16-bit (UTF16 character)
|
||||
|
||||
#ifndef _countof
|
||||
#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
|
||||
#endif
|
||||
#ifndef min
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define strcasecmp _stricmp
|
||||
#endif
|
||||
@@ -206,6 +216,38 @@ static const struct SPECIALFOLDER
|
||||
|
||||
/* FUNCTIONS *****************************************************************/
|
||||
|
||||
/*
|
||||
* Poor-man ANSI-to-UTF16LE conversion.
|
||||
* We cannot use the host mbstowcs() routine, since on *nix systems the iconv
|
||||
* library being used may have been compiled with a 32-bit wchar_t (even if
|
||||
* the tool is compiled with: `-fshort-wchar -fwide-exec-charset=UTF-16LE`).
|
||||
*/
|
||||
size_t
|
||||
my_mbstowcs(
|
||||
wchar_t *wcstr,
|
||||
const char *mbstr,
|
||||
size_t count)
|
||||
{
|
||||
size_t i;
|
||||
size_t len = strlen(mbstr) + 1; // Count the NUL-terminator.
|
||||
|
||||
/* Convert up to the NUL-terminator (included) and at most 'count' characters */
|
||||
count = min(count, len);
|
||||
|
||||
/* POSIX extension: if wcstr is NULL, return the number of wide
|
||||
* characters that would be written to wcstr, if converted. */
|
||||
if (!wcstr)
|
||||
return count;
|
||||
|
||||
if (count == 0)
|
||||
return (size_t)0; // dest string exists, but 0 bytes converted.
|
||||
for (i = 0; i < count; ++i)
|
||||
wcstr[i] = (wchar_t)mbstr[i];
|
||||
/* Return the number of wide characters, excluding the NUL-terminator, written to wcstr */
|
||||
return (i - 1);
|
||||
}
|
||||
#define mbstowcs my_mbstowcs
|
||||
|
||||
static bool is_path_separator(char c)
|
||||
{
|
||||
return (c == '\\' || c == '/');
|
||||
@@ -235,6 +277,31 @@ static const struct SPECIALFOLDER* get_special_folder(const char *target)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write an ANSI string buffer to the shell link,
|
||||
* converting it to UTF16LE Unicode if necessary.
|
||||
*/
|
||||
static void write_string(FILE *pFile, bool bUnicode, const char *str)
|
||||
{
|
||||
uint16_t uhTmp;
|
||||
|
||||
/* String length in number of ANSI or Unicode characters */
|
||||
uhTmp = strlen(str);
|
||||
fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
|
||||
|
||||
/* String data (ANSI or Unicode) */
|
||||
if (!bUnicode)
|
||||
{
|
||||
fwrite(str, uhTmp, 1, pFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t tmpBufferU[1024]; // Large-enough buffer for string conversions.
|
||||
uhTmp = (uint16_t)mbstowcs(tmpBufferU, str, _countof(tmpBufferU));
|
||||
fwrite(tmpBufferU, uhTmp * sizeof(wchar_t), 1, pFile);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int i;
|
||||
@@ -247,10 +314,9 @@ int main(int argc, const char *argv[])
|
||||
char targetpath[260];
|
||||
int32_t IconIndex = 0;
|
||||
GUID Guid = CLSID_MyComputer;
|
||||
bool bHelp = false, bMinimized = false;
|
||||
bool bHelp = false, bUnicode = false, bMinimized = false;
|
||||
FILE *pFile;
|
||||
LNK_HEADER Header;
|
||||
uint16_t uhTmp;
|
||||
uint32_t dwTmp;
|
||||
EXP_SPECIAL_FOLDER CsidlBlock, *pCsidlBlock = NULL;
|
||||
|
||||
@@ -263,6 +329,8 @@ int main(int argc, const char *argv[])
|
||||
bHelp = true;
|
||||
else if (!strcmp(argv[i] + 1, "o") && (i + 1 < argc))
|
||||
pszOutputPath = argv[++i];
|
||||
else if (!strcmp(argv[i] + 1, "u"))
|
||||
bUnicode = true;
|
||||
else if (!strcmp(argv[i] + 1, "d") && (i + 1 < argc))
|
||||
pszDescription = argv[++i];
|
||||
else if (!strcmp(argv[i] + 1, "w") && (i + 1 < argc))
|
||||
@@ -297,9 +365,10 @@ int main(int argc, const char *argv[])
|
||||
|
||||
if (!pszTarget || bHelp)
|
||||
{
|
||||
printf("Usage: %s [-h][-o path][-d descr][-w path][-c cmdline_args][-i icon_path [nr]][-g guid] target\n"
|
||||
printf("Usage: %s [-h][-o path][-u][-d descr][-w path][-c cmdline_args][-i icon_path [nr]][-g guid] target\n"
|
||||
"-h\tShows this help.\n"
|
||||
"-o path\tSets the output path.\n"
|
||||
"-u\tCreates a Unicode-aware shortcut.\n"
|
||||
"-d descr\tSets the shortcut description.\n"
|
||||
"-w path\tSets the working directory for the executable.\n"
|
||||
"-c cmdline_args\tSets the command-line arguments passed to the program.\n"
|
||||
@@ -322,6 +391,8 @@ int main(int argc, const char *argv[])
|
||||
Header.Size = sizeof(Header);
|
||||
Header.Guid = CLSID_ShellLink;
|
||||
Header.Flags = LINK_ID_LIST;
|
||||
if (bUnicode)
|
||||
Header.Flags |= LINK_UNICODE;
|
||||
if (pszDescription)
|
||||
Header.Flags |= LINK_DESCRIPTION;
|
||||
if (pszWorkingDir)
|
||||
@@ -343,6 +414,7 @@ int main(int argc, const char *argv[])
|
||||
unsigned int cchName;
|
||||
const char *pszName = pszTarget;
|
||||
size_t specialPathLen = 0;
|
||||
uint16_t uhTmp;
|
||||
const struct SPECIALFOLDER *special = get_special_folder(pszTarget);
|
||||
|
||||
/* ID list. It appears explorer does not accept links
|
||||
@@ -449,45 +521,25 @@ int main(int argc, const char *argv[])
|
||||
fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
|
||||
}
|
||||
|
||||
/* Description */
|
||||
if (Header.Flags & LINK_DESCRIPTION)
|
||||
{
|
||||
/* Description */
|
||||
uhTmp = strlen(pszDescription);
|
||||
fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
|
||||
fputs(pszDescription, pFile);
|
||||
}
|
||||
write_string(pFile, bUnicode, pszDescription);
|
||||
|
||||
/* Relative path */
|
||||
if (Header.Flags & LINK_RELATIVE_PATH)
|
||||
{
|
||||
/* Relative path */
|
||||
uhTmp = strlen(pszTarget);
|
||||
fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
|
||||
fputs(pszTarget, pFile);
|
||||
}
|
||||
write_string(pFile, bUnicode, pszTarget);
|
||||
|
||||
/* Working directory */
|
||||
if (Header.Flags & LINK_WORKING_DIR)
|
||||
{
|
||||
/* Working directory */
|
||||
uhTmp = strlen(pszWorkingDir);
|
||||
fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
|
||||
fputs(pszWorkingDir, pFile);
|
||||
}
|
||||
write_string(pFile, bUnicode, pszWorkingDir);
|
||||
|
||||
/* Command-line arguments */
|
||||
if (Header.Flags & LINK_CMDLINE_ARGS)
|
||||
{
|
||||
/* Command-line arguments */
|
||||
uhTmp = strlen(pszCmdLineArgs);
|
||||
fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
|
||||
fputs(pszCmdLineArgs, pFile);
|
||||
}
|
||||
write_string(pFile, bUnicode, pszCmdLineArgs);
|
||||
|
||||
/* Icon path */
|
||||
if (Header.Flags & LINK_ICON)
|
||||
{
|
||||
/* Icon path */
|
||||
uhTmp = strlen(pszIcon);
|
||||
fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
|
||||
fputs(pszIcon, pFile);
|
||||
}
|
||||
write_string(pFile, bUnicode, pszIcon);
|
||||
|
||||
/* Write the data block list */
|
||||
if (pCsidlBlock)
|
||||
|
||||
Reference in New Issue
Block a user