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
14 typedef unsigned __int8
uint8_t;
15 typedef unsigned __int16
uint16_t;
16 typedef unsigned __int32
uint32_t;
19 #define SW_SHOWNORMAL 1
20 #define SW_SHOWMINNOACTIVE 7
22 typedef struct _GUID
{
29 typedef struct _FILETIME
{
30 uint32_t dwLowDateTime
;
31 uint32_t dwHighDateTime
;
32 } FILETIME
, *PFILETIME
;
34 #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 } }
35 DEFINE_GUID2(CLSID_ShellLink
,0x00021401L
,0,0,0xC0,0,0,0,0,0,0,0x46);
36 DEFINE_GUID2(CLSID_MyComputer
,0x20D04FE0,0x3AEA,0x1069,0xA2,0xD8,0x08,0x00,0x2B,0x30,0x30,0x9D);
38 #define LINK_ID_LIST 0x01
39 #define LINK_FILE 0x02
40 #define LINK_DESCRIPTION 0x04
41 #define LINK_RELATIVE_PATH 0x08
42 #define LINK_WORKING_DIR 0x10
43 #define LINK_CMD_LINE_ARGS 0x20
44 #define LINK_ICON 0x40
45 #define LINK_UNICODE 0x80
47 #define LOCATOR_LOCAL 0x1
48 #define LOCATOR_NETWORK 0x2
52 /* Specification: http://ithreats.files.wordpress.com/2009/05/lnk_the_windows_shortcut_file_format.pdf */
54 typedef struct _LNK_HEADER
60 FILETIME CreationTime
;
61 FILETIME ModificationTime
;
62 FILETIME LastAccessTime
;
71 typedef struct _LNK_LOCATOR_INFO
76 uint32_t LocalVolumeInfoOffset
;
77 uint32_t LocalBasePathnameOffset
;
78 uint32_t NetworkVolumeInfoOffset
;
79 uint32_t RemainingPathnameOffset
;
83 typedef struct _LNK_LOCAL_VOLUME_INFO
86 uint32_t VolumeType
; /* See GetDriveType */
87 uint32_t SerialNumber
;
88 uint32_t VolumeNameOffset
;
90 } LNK_LOCAL_VOLUME_INFO
;
93 #define PT_DRIVE1 0x2F
94 #define PT_FOLDER 0x31
97 typedef struct _ID_LIST_FILE
105 uint16_t uFileAttribs
;
109 typedef struct _ID_LIST_GUID
117 typedef struct _ID_LIST_DRIVE
121 char szDriveName
[20];
127 int main(int argc
, const char *argv
[])
130 const char *pszOutputPath
= "shortcut.lnk";
131 const char *pszTarget
= NULL
;
132 const char *pszDescription
= "Description";
133 const char *pszWorkingDir
= NULL
;
134 const char *pszCmdLineArgs
= NULL
;
135 const char *pszIcon
= NULL
;
137 GUID Guid
= CLSID_MyComputer
;
138 int bHelp
= 0, bMinimized
= 0;
144 for (i
= 1; i
< argc
; ++i
)
146 if (argv
[i
][0] != '-' && argv
[i
][0] != '/')
148 else if (!strcmp(argv
[i
] + 1, "h"))
150 else if (!strcmp(argv
[i
] + 1, "o") && i
+ 1 < argc
)
151 pszOutputPath
= argv
[++i
];
152 else if (!strcmp(argv
[i
] + 1, "d") && i
+ 1 < argc
)
153 pszDescription
= argv
[++i
];
154 else if (!strcmp(argv
[i
] + 1, "w") && i
+ 1 < argc
)
155 pszWorkingDir
= argv
[++i
];
156 else if (!strcmp(argv
[i
] + 1, "c") && i
+ 1 < argc
)
157 pszCmdLineArgs
= argv
[++i
];
158 else if (!strcmp(argv
[i
] + 1, "i") && i
+ 1 < argc
)
161 if (i
+ 1 < argc
&& isdigit(argv
[i
+ 1][0]))
162 IconNr
= atoi(argv
[++i
]);
164 else if (!strcmp(argv
[i
] + 1, "m"))
166 else if (!strcmp(argv
[i
] + 1, "g") && i
+ 1 < argc
)
168 unsigned Data4Tmp
[8], j
;
170 sscanf(argv
[++i
], "{%8lx-%4hx-%4hx-%2x%2x-%2x%2x%2x%2x%2x%2x}",
171 &Guid
.Data1
, &Guid
.Data2
, &Guid
.Data3
,
172 &Data4Tmp
[0], &Data4Tmp
[1], &Data4Tmp
[2], &Data4Tmp
[3],
173 &Data4Tmp
[4], &Data4Tmp
[5], &Data4Tmp
[6], &Data4Tmp
[7]);
174 for (j
= 0; j
< 8; ++j
)
175 Guid
.Data4
[j
] = (uint8_t)Data4Tmp
[j
];
178 printf("Invalid option: %s\n", argv
[i
]);
181 if (!pszTarget
|| bHelp
)
183 printf("Usage: %s [-o path][-d descr][-w path][-c cmd_line_args][-i icon_path [nr]][-h][-g guid] target\n"
184 "-o path\tSets output path\n"
185 "-d descr\tSets shortcut description\n"
186 "-w path\tSets working directory for executable\n"
187 "-c cmd_line_args\tSets command line arguments passed to program\n"
188 "-i icon_path [nr]\tSets icon file and optionally icon index\n"
189 "-m\tStart minimized\n"
190 "-g guid\tSets GUID to which target path is relative. Default value is MyComputer GUID.\n"
191 "target\tAbsolute or relative to guid specified with -g option path\n", argv
[0]);
195 pFile
= fopen(pszOutputPath
, "wb");
198 printf("Failed to open %s\n", pszOutputPath
);
203 memset(&Header
, 0, sizeof(Header
));
204 Header
.Signature
= (uint32_t)'L';
205 Header
.Guid
= CLSID_ShellLink
;
206 Header
.Flags
= LINK_ID_LIST
;
208 Header
.Flags
|= LINK_DESCRIPTION
;
210 Header
.Flags
|= LINK_WORKING_DIR
;
212 Header
.Flags
|= LINK_CMD_LINE_ARGS
;
214 Header
.Flags
|= LINK_ICON
;
215 Header
.IconNr
= IconNr
;
216 Header
.Show
= bMinimized
? SW_SHOWMINNOACTIVE
: SW_SHOWNORMAL
;
217 fwrite(&Header
, sizeof(Header
), 1, pFile
);
219 if (Header
.Flags
& LINK_ID_LIST
)
221 ID_LIST_FILE IdListFile
;
222 ID_LIST_GUID IdListGuid
;
223 ID_LIST_DRIVE IdListDrive
;
224 unsigned cbListSize
= sizeof(IdListGuid
) + sizeof(uint16_t), cchName
;
225 const char *pszName
= pszTarget
;
228 // It seems explorer does not accept links without id list. List is relative to desktop.
232 if (pszName
[0] && pszName
[1] == ':')
234 cbListSize
+= sizeof(IdListDrive
);
236 while (*pszName
== '\\' || *pszName
== '/')
243 while (pszName
[cchName
] && pszName
[cchName
] != '\\' && pszName
[cchName
] != '/')
246 if (cchName
!= 1 || pszName
[0] != '.')
247 cbListSize
+= sizeof(IdListFile
) + 2 * (cchName
+ 1);
250 while (*pszName
== '\\' || *pszName
== '/')
255 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
); // size
257 IdListGuid
.Size
= sizeof(IdListGuid
);
258 IdListGuid
.Type
= PT_GUID
;
259 IdListGuid
.dummy
= 0x50;
260 IdListGuid
.guid
= Guid
;
261 fwrite(&IdListGuid
, sizeof(IdListGuid
), 1, pFile
);
265 if (isalpha(pszName
[0]) && pszName
[1] == ':')
267 memset(&IdListDrive
, 0, sizeof(IdListDrive
));
268 IdListDrive
.Size
= sizeof(IdListDrive
);
269 IdListDrive
.Type
= PT_DRIVE1
;
270 sprintf(IdListDrive
.szDriveName
, "%c:\\", pszName
[0]);
271 fwrite(&IdListDrive
, sizeof(IdListDrive
), 1, pFile
);
273 while(*pszName
== '\\' || *pszName
== '/')
280 while (pszName
[cchName
] && pszName
[cchName
] != '\\' && pszName
[cchName
] != '/')
283 if (cchName
!= 1 || pszName
[0] != '.')
285 memset(&IdListFile
, 0, sizeof(IdListFile
));
286 IdListFile
.Size
= sizeof(IdListFile
) + 2 * (cchName
+ 1);
287 if (!pszName
[cchName
])
288 IdListFile
.Type
= PT_VALUE
; // File
290 IdListFile
.Type
= PT_FOLDER
;
291 fwrite(&IdListFile
, sizeof(IdListFile
), 1, pFile
);
292 fwrite(pszName
, cchName
, 1, pFile
);
294 fwrite(pszName
, cchName
, 1, pFile
);
299 while (*pszName
== '\\' || *pszName
== '/')
303 uhTmp
= 0; // list end
304 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
307 if (Header
.Flags
& LINK_DESCRIPTION
)
310 uhTmp
= strlen(pszDescription
);
311 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
312 fputs(pszDescription
, pFile
);
315 if (Header
.Flags
& LINK_RELATIVE_PATH
)
318 uhTmp
= strlen(pszTarget
);
319 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
320 fputs(pszTarget
, pFile
);
323 if (Header
.Flags
& LINK_WORKING_DIR
)
326 uhTmp
= strlen(pszWorkingDir
);
327 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
328 fputs(pszWorkingDir
, pFile
);
331 if (Header
.Flags
& LINK_CMD_LINE_ARGS
)
333 // Command line arguments
334 uhTmp
= strlen(pszCmdLineArgs
);
335 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
336 fputs(pszCmdLineArgs
, pFile
);
339 if (Header
.Flags
& LINK_ICON
)
341 // Command line arguments
342 uhTmp
= strlen(pszIcon
);
343 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
344 fputs(pszIcon
, pFile
);
349 fwrite(&dwTmp
, sizeof(dwTmp
), 1, pFile
);