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
16 typedef unsigned __int8
uint8_t;
17 typedef unsigned __int16
uint16_t;
18 typedef unsigned __int32
uint32_t;
21 #define SW_SHOWNORMAL 1
22 #define SW_SHOWMINNOACTIVE 7
32 typedef struct _FILETIME
{
33 uint32_t dwLowDateTime
;
34 uint32_t dwHighDateTime
;
35 } FILETIME
, *PFILETIME
;
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);
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
50 #define LOCATOR_LOCAL 0x1
51 #define LOCATOR_NETWORK 0x2
55 /* Specification: http://ithreats.files.wordpress.com/2009/05/lnk_the_windows_shortcut_file_format.pdf */
57 typedef struct _LNK_HEADER
63 FILETIME CreationTime
;
64 FILETIME ModificationTime
;
65 FILETIME LastAccessTime
;
74 typedef struct _LNK_LOCATOR_INFO
79 uint32_t LocalVolumeInfoOffset
;
80 uint32_t LocalBasePathnameOffset
;
81 uint32_t NetworkVolumeInfoOffset
;
82 uint32_t RemainingPathnameOffset
;
86 typedef struct _LNK_LOCAL_VOLUME_INFO
89 uint32_t VolumeType
; /* See GetDriveType */
90 uint32_t SerialNumber
;
91 uint32_t VolumeNameOffset
;
93 } LNK_LOCAL_VOLUME_INFO
;
96 #define PT_DRIVE1 0x2F
97 #define PT_FOLDER 0x31
100 typedef struct _ID_LIST_FILE
108 uint16_t uFileAttribs
;
112 typedef struct _ID_LIST_GUID
120 typedef struct _ID_LIST_DRIVE
124 char szDriveName
[20];
130 int main(int argc
, const char *argv
[])
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
;
140 GUID Guid
= CLSID_MyComputer
;
141 int bHelp
= 0, bMinimized
= 0;
147 for (i
= 1; i
< argc
; ++i
)
149 if (argv
[i
][0] != '-' && argv
[i
][0] != '/')
151 else if (!strcmp(argv
[i
] + 1, "h"))
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
)
164 if (i
+ 1 < argc
&& isdigit(argv
[i
+ 1][0]))
165 IconNr
= atoi(argv
[++i
]);
167 else if (!strcmp(argv
[i
] + 1, "m"))
169 else if (!strcmp(argv
[i
] + 1, "g") && i
+ 1 < argc
)
171 unsigned Data4Tmp
[8], j
;
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
];
181 printf("Invalid option: %s\n", argv
[i
]);
184 if (!pszTarget
|| bHelp
)
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]);
198 pFile
= fopen(pszOutputPath
, "wb");
201 printf("Failed to open %s\n", pszOutputPath
);
206 memset(&Header
, 0, sizeof(Header
));
207 Header
.Signature
= (uint32_t)'L';
208 Header
.Guid
= CLSID_ShellLink
;
209 Header
.Flags
= LINK_ID_LIST
;
211 Header
.Flags
|= LINK_DESCRIPTION
;
213 Header
.Flags
|= LINK_WORKING_DIR
;
215 Header
.Flags
|= LINK_CMD_LINE_ARGS
;
217 Header
.Flags
|= LINK_ICON
;
218 Header
.IconNr
= IconNr
;
219 Header
.Show
= bMinimized
? SW_SHOWMINNOACTIVE
: SW_SHOWNORMAL
;
220 fwrite(&Header
, sizeof(Header
), 1, pFile
);
222 if (Header
.Flags
& LINK_ID_LIST
)
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
;
231 // It seems explorer does not accept links without id list. List is relative to desktop.
235 if (pszName
[0] && pszName
[1] == ':')
237 cbListSize
+= sizeof(IdListDrive
);
239 while (*pszName
== '\\' || *pszName
== '/')
246 while (pszName
[cchName
] && pszName
[cchName
] != '\\' && pszName
[cchName
] != '/')
249 if (cchName
!= 1 || pszName
[0] != '.')
250 cbListSize
+= sizeof(IdListFile
) + 2 * (cchName
+ 1);
253 while (*pszName
== '\\' || *pszName
== '/')
258 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
); // size
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
);
268 if (isalpha(pszName
[0]) && pszName
[1] == ':')
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
);
276 while(*pszName
== '\\' || *pszName
== '/')
283 while (pszName
[cchName
] && pszName
[cchName
] != '\\' && pszName
[cchName
] != '/')
286 if (cchName
!= 1 || pszName
[0] != '.')
288 memset(&IdListFile
, 0, sizeof(IdListFile
));
289 IdListFile
.Size
= sizeof(IdListFile
) + 2 * (cchName
+ 1);
290 if (!pszName
[cchName
])
291 IdListFile
.Type
= PT_VALUE
; // File
293 IdListFile
.Type
= PT_FOLDER
;
294 fwrite(&IdListFile
, sizeof(IdListFile
), 1, pFile
);
295 fwrite(pszName
, cchName
, 1, pFile
);
297 fwrite(pszName
, cchName
, 1, pFile
);
302 while (*pszName
== '\\' || *pszName
== '/')
306 uhTmp
= 0; // list end
307 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
310 if (Header
.Flags
& LINK_DESCRIPTION
)
313 uhTmp
= strlen(pszDescription
);
314 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
315 fputs(pszDescription
, pFile
);
318 if (Header
.Flags
& LINK_RELATIVE_PATH
)
321 uhTmp
= strlen(pszTarget
);
322 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
323 fputs(pszTarget
, pFile
);
326 if (Header
.Flags
& LINK_WORKING_DIR
)
329 uhTmp
= strlen(pszWorkingDir
);
330 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
331 fputs(pszWorkingDir
, pFile
);
334 if (Header
.Flags
& LINK_CMD_LINE_ARGS
)
336 // Command line arguments
337 uhTmp
= strlen(pszCmdLineArgs
);
338 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
339 fputs(pszCmdLineArgs
, pFile
);
342 if (Header
.Flags
& LINK_ICON
)
344 // Command line arguments
345 uhTmp
= strlen(pszIcon
);
346 fwrite(&uhTmp
, sizeof(uhTmp
), 1, pFile
);
347 fputs(pszIcon
, pFile
);
352 fwrite(&dwTmp
, sizeof(dwTmp
), 1, pFile
);