Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

447 lines
13 KiB

  1. #define UNICODE
  2. #define _INC_OLE
  3. #include <windows.h>
  4. #include <shellapi.h>
  5. #include <shlobj.h>
  6. #include <stdio.h>
  7. #include <string.h>
  8. void
  9. FatalError(
  10. LPSTR Message,
  11. ULONG_PTR MessageParameter1,
  12. ULONG_PTR MessageParameter2
  13. )
  14. {
  15. if (Message != NULL) {
  16. fprintf( stderr, "FIXLINK: " );
  17. fprintf( stderr, Message, MessageParameter1, MessageParameter2 );
  18. fprintf( stderr, "\n" );
  19. }
  20. exit( 1 );
  21. }
  22. void
  23. Usage(
  24. LPSTR Message,
  25. ULONG MessageParameter
  26. )
  27. {
  28. fprintf( stderr, "usage: FIXLINKS [-v] [-q] [-s SystemRoot | -S searchString replaceString]\n" );
  29. fprintf( stderr, " [-r Directory | fileNames]\n" );
  30. fprintf( stderr, "where: -v specifies verbose output\n" );
  31. fprintf( stderr, " -q specifies query only, no actual updating of link files\n" );
  32. fprintf( stderr, " -s specifies the value to look for and replace with %%SystemRoot%%\n" );
  33. fprintf( stderr, " -S specifies the value to look for and replace with replaceString\n" );
  34. fprintf( stderr, " -r Directory specifies the root of a directory tree to\n" );
  35. fprintf( stderr, " search for .LNK files to update.\n" );
  36. fprintf( stderr, " fileNames specify one or more .LNK files to be updated\n" );
  37. //
  38. // No return from FatalError
  39. //
  40. if (Message != NULL) {
  41. fprintf( stderr, "\n" );
  42. }
  43. FatalError( Message, MessageParameter, 0 );
  44. }
  45. PWSTR
  46. GetErrorMessage(
  47. DWORD MessageId
  48. )
  49. {
  50. PWSTR Message, s;
  51. Message = NULL;
  52. FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
  53. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  54. NULL,
  55. MessageId,
  56. 0,
  57. (PWSTR)&Message,
  58. 128,
  59. (va_list *)NULL
  60. );
  61. if (Message == NULL) {
  62. if (Message = (PWSTR)LocalAlloc( 0, 128 )) {
  63. swprintf( Message, L"Unable to get message for %08x", MessageId );
  64. }
  65. }
  66. else {
  67. s = wcsrchr( Message, L'\r' );
  68. if (s == NULL) {
  69. s = wcsrchr( Message, L'\n' );
  70. }
  71. if (s != NULL) {
  72. *s = UNICODE_NULL;
  73. }
  74. }
  75. return Message;
  76. }
  77. PWSTR
  78. GetArgAsUnicode(
  79. LPSTR s
  80. )
  81. {
  82. ULONG n;
  83. PWSTR ps;
  84. n = strlen( s );
  85. ps = HeapAlloc( GetProcessHeap(),
  86. 0,
  87. (n + 1) * sizeof( WCHAR )
  88. );
  89. if (ps == NULL) {
  90. FatalError( "Out of memory", 0, 0 );
  91. }
  92. if (MultiByteToWideChar( CP_ACP,
  93. MB_PRECOMPOSED,
  94. s,
  95. n,
  96. ps,
  97. n
  98. ) != (LONG)n
  99. ) {
  100. FatalError( "Unable to convert parameter '%s' to Unicode (%u)",
  101. (ULONG_PTR)s,
  102. GetLastError() );
  103. }
  104. ps[ n ] = UNICODE_NULL;
  105. return ps;
  106. }
  107. WCHAR SearchString[ MAX_PATH ];
  108. ULONG cchSearchString;
  109. WCHAR ReplaceString[ MAX_PATH ];
  110. ULONG cchReplaceString;
  111. WCHAR RootOfSearch[ MAX_PATH ];
  112. BOOL VerboseFlag;
  113. BOOL QueryFlag;
  114. HRESULT
  115. ProcessLinkFile(
  116. PWSTR FileName
  117. );
  118. void
  119. ProcessLinkFilesInDirectoryTree(
  120. PWSTR Directory
  121. );
  122. __cdecl
  123. main(
  124. int argc,
  125. char *argv[]
  126. )
  127. {
  128. char *s;
  129. BOOL FileArgumentSeen = FALSE;
  130. PWSTR FileName;
  131. HRESULT Error;
  132. GetEnvironmentVariable( L"SystemRoot", SearchString, MAX_PATH );
  133. cchSearchString = wcslen( SearchString );
  134. if (cchSearchString == 0) {
  135. FatalError( "Unable to get value of SYSTEMROOT environment variable", 0, 0 );
  136. }
  137. wcscpy( ReplaceString, L"%SystemRoot%" );
  138. cchReplaceString = wcslen( ReplaceString );
  139. while (--argc) {
  140. s = *++argv;
  141. if (*s == '-' || *s == '/') {
  142. while (*++s) {
  143. switch( tolower( *s ) ) {
  144. case 'r':
  145. if (--argc) {
  146. GetFullPathName( GetArgAsUnicode( *++argv ),
  147. MAX_PATH,
  148. RootOfSearch,
  149. &FileName
  150. );
  151. }
  152. else {
  153. Usage( "Missing parameter to -r switch", 0 );
  154. }
  155. break;
  156. case 's':
  157. if (--argc) {
  158. wcscpy( SearchString, GetArgAsUnicode( *++argv ) );
  159. }
  160. else {
  161. Usage( "Missing parameter to -%c switch", (ULONG)*s);
  162. }
  163. cchSearchString = wcslen( SearchString );
  164. if (cchSearchString == 0) {
  165. FatalError( "May not specify an empty search string", 0, 0 );
  166. }
  167. if (*s == 'S') {
  168. if (--argc) {
  169. wcscpy( ReplaceString, GetArgAsUnicode( *++argv ) );
  170. }
  171. else {
  172. Usage( "Missing parameter to -S switch", 0 );
  173. }
  174. cchReplaceString = wcslen( ReplaceString );
  175. if (cchSearchString == 0) {
  176. FatalError( "May not specify an empty replacement string", 0, 0 );
  177. }
  178. }
  179. break;
  180. case 'q':
  181. QueryFlag = TRUE;
  182. break;
  183. case 'v':
  184. VerboseFlag = TRUE;
  185. break;
  186. default:
  187. Usage( "Invalid switch -%c'", (ULONG)*s );
  188. }
  189. }
  190. }
  191. else {
  192. if (wcslen( RootOfSearch )) {
  193. FatalError( "May not specify file names with -r option", 0, 0 );
  194. }
  195. FileArgumentSeen = TRUE;
  196. FileName = GetArgAsUnicode( s );
  197. if (FileName == NULL) {
  198. Error = (HRESULT)GetLastError();
  199. }
  200. else {
  201. Error = ProcessLinkFile( FileName );
  202. }
  203. if (Error != NO_ERROR) {
  204. FatalError( "Failed to load from file '%s' (%ws)",
  205. (ULONG_PTR)s,
  206. (ULONG_PTR)GetErrorMessage( Error )
  207. );
  208. }
  209. }
  210. }
  211. if (!FileArgumentSeen) {
  212. if (wcslen( RootOfSearch )) {
  213. ProcessLinkFilesInDirectoryTree( RootOfSearch );
  214. }
  215. else {
  216. Usage( "No textFile specified", 0 );
  217. }
  218. }
  219. return 0;
  220. }
  221. HRESULT
  222. ProcessLinkFile(
  223. PWSTR FileName
  224. )
  225. {
  226. HRESULT rc;
  227. IShellLink *psl;
  228. IPersistFile *ppf;
  229. WCHAR szPath[ MAX_PATH ];
  230. WCHAR szNewPath[ MAX_PATH ];
  231. PWSTR s;
  232. BOOL FileNameShown;
  233. BOOL FileUpdated;
  234. if (FAILED(rc = CoInitialize( NULL )))
  235. return rc;
  236. rc = CoCreateInstance( &CLSID_ShellLink,
  237. NULL,
  238. CLSCTX_INPROC,
  239. &IID_IShellLink,
  240. &psl
  241. );
  242. if (!SUCCEEDED( rc )) {
  243. FatalError( "Unable to create ShellLink instance (%ws)",
  244. (ULONG_PTR)GetErrorMessage( rc ),
  245. 0
  246. );
  247. }
  248. rc = (psl->lpVtbl->QueryInterface)( psl, &IID_IPersistFile, &ppf );
  249. if (!SUCCEEDED( rc )) {
  250. FatalError( "Unable to get ShellLink PersistFile interface (%ws)",
  251. (ULONG_PTR)GetErrorMessage( rc ),
  252. 0
  253. );
  254. ppf->lpVtbl->Release( ppf );
  255. }
  256. rc = (ppf->lpVtbl->Load)( ppf, FileName, STGM_READWRITE );
  257. if (!SUCCEEDED( rc )) {
  258. printf( "%ws: unable to get load link file (%ws)\n", FileName, GetErrorMessage( rc ) );
  259. ppf->lpVtbl->Release( ppf );
  260. psl->lpVtbl->Release( psl );
  261. return S_OK;
  262. }
  263. FileUpdated = FALSE;
  264. FileNameShown = FALSE;
  265. if (VerboseFlag) {
  266. printf( "%ws:\n", FileName );
  267. FileNameShown = TRUE;
  268. }
  269. rc = (psl->lpVtbl->GetPath)( psl, szPath, MAX_PATH, NULL, 0 );
  270. if (SUCCEEDED( rc )) {
  271. if (!_wcsnicmp( szPath, SearchString, cchSearchString )) {
  272. if (!FileNameShown) {
  273. printf( "%ws:\n", FileName );
  274. FileNameShown = TRUE;
  275. }
  276. printf( " Path: %ws", szPath );
  277. wcscpy( szNewPath, ReplaceString );
  278. wcscat( szNewPath, &szPath[ cchSearchString ] );
  279. if (QueryFlag) {
  280. printf( " - would be changed to %ws\n", szNewPath );
  281. }
  282. else {
  283. rc = (psl->lpVtbl->SetPath)( psl, szNewPath );
  284. if (SUCCEEDED( rc )) {
  285. printf( " - changed to %ws\n", szNewPath );
  286. FileUpdated = TRUE;
  287. }
  288. else {
  289. printf( " - unable to modify (%ws)\n", GetErrorMessage( rc ) );
  290. }
  291. }
  292. }
  293. else {
  294. if (VerboseFlag) {
  295. printf( " Path: %ws\n", szPath );
  296. }
  297. }
  298. }
  299. else {
  300. ppf->lpVtbl->Release( ppf );
  301. psl->lpVtbl->Release( psl );
  302. printf( " Unable to get ShellLink Path (%ws)\n", GetErrorMessage( rc ) );
  303. }
  304. rc = (psl->lpVtbl->GetWorkingDirectory)( psl, szPath, MAX_PATH );
  305. if (SUCCEEDED( rc )) {
  306. if (!_wcsnicmp( szPath, SearchString, cchSearchString )) {
  307. if (!FileNameShown) {
  308. printf( "%ws:\n", FileName );
  309. FileNameShown = TRUE;
  310. }
  311. printf( " Working Directory: %ws", szPath );
  312. wcscpy( szNewPath, ReplaceString );
  313. wcscat( szNewPath, &szPath[ cchSearchString ] );
  314. if (QueryFlag) {
  315. printf( " - would be changed to %ws\n", szNewPath );
  316. }
  317. else {
  318. rc = (psl->lpVtbl->SetWorkingDirectory)( psl, szNewPath );
  319. if (SUCCEEDED( rc )) {
  320. printf( " - changed to %ws\n", szNewPath );
  321. FileUpdated = TRUE;
  322. }
  323. else {
  324. printf( " - unable to modify (%ws)\n", GetErrorMessage( rc ) );
  325. }
  326. }
  327. }
  328. else {
  329. if (VerboseFlag) {
  330. printf( " Working Directory: %ws\n", szPath );
  331. }
  332. }
  333. }
  334. else {
  335. ppf->lpVtbl->Release( ppf );
  336. psl->lpVtbl->Release( psl );
  337. printf( " Unable to get ShellLink Working Directory (%ws)\n", GetErrorMessage( rc ) );
  338. }
  339. if (FileUpdated) {
  340. rc = (ppf->lpVtbl->Save)( ppf, FileName, TRUE );
  341. if (SUCCEEDED( rc )) {
  342. rc = (ppf->lpVtbl->SaveCompleted)( ppf, FileName );
  343. if (SUCCEEDED( rc )) {
  344. printf( " Link file updated.\n" );
  345. }
  346. else {
  347. printf( " **** unable to save changes to link file (%ws)\n", GetErrorMessage( rc ) );
  348. }
  349. }
  350. }
  351. ppf->lpVtbl->Release( ppf );
  352. psl->lpVtbl->Release( psl );
  353. return S_OK;
  354. }
  355. void
  356. ProcessLinkFilesInDirectoryTree(
  357. PWSTR Directory
  358. )
  359. {
  360. HANDLE FindHandle;
  361. WIN32_FIND_DATA FindData;
  362. WCHAR Path[ MAX_PATH ];
  363. PWSTR FileName;
  364. ULONG n;
  365. wcscpy( Path, Directory );
  366. FileName = &Path[ wcslen( Path ) ];
  367. *FileName++ = L'\\';
  368. wcscpy( FileName, L"*" );
  369. FindHandle = FindFirstFile( Path, &FindData );
  370. if (FindHandle == INVALID_HANDLE_VALUE) {
  371. return;
  372. }
  373. if (VerboseFlag) {
  374. printf( "%ws\n", Directory );
  375. }
  376. SetCurrentDirectory( Directory );
  377. do {
  378. if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  379. if (wcscmp( FindData.cFileName, L"." ) && wcscmp( FindData.cFileName, L".." )) {
  380. wcscpy( FileName, FindData.cFileName );
  381. ProcessLinkFilesInDirectoryTree( Path );
  382. }
  383. }
  384. else {
  385. n = wcslen( FindData.cFileName );
  386. while (n-- && FindData.cFileName[ n ] != L'.') {
  387. }
  388. if (!_wcsicmp( &FindData.cFileName[ n ], L".lnk" )) {
  389. wcscpy( FileName, FindData.cFileName );
  390. ProcessLinkFile( Path );
  391. }
  392. }
  393. }
  394. while (FindNextFile( FindHandle, &FindData ));
  395. FindClose( FindHandle );
  396. return;
  397. }