#define UNICODE #define _INC_OLE #include #include #include #include #include void FatalError( LPSTR Message, ULONG_PTR MessageParameter1, ULONG_PTR MessageParameter2 ) { if (Message != NULL) { fprintf( stderr, "FIXLINK: " ); fprintf( stderr, Message, MessageParameter1, MessageParameter2 ); fprintf( stderr, "\n" ); } exit( 1 ); } void Usage( LPSTR Message, ULONG MessageParameter ) { fprintf( stderr, "usage: FIXLINKS [-v] [-q] [-s SystemRoot | -S searchString replaceString]\n" ); fprintf( stderr, " [-r Directory | fileNames]\n" ); fprintf( stderr, "where: -v specifies verbose output\n" ); fprintf( stderr, " -q specifies query only, no actual updating of link files\n" ); fprintf( stderr, " -s specifies the value to look for and replace with %%SystemRoot%%\n" ); fprintf( stderr, " -S specifies the value to look for and replace with replaceString\n" ); fprintf( stderr, " -r Directory specifies the root of a directory tree to\n" ); fprintf( stderr, " search for .LNK files to update.\n" ); fprintf( stderr, " fileNames specify one or more .LNK files to be updated\n" ); // // No return from FatalError // if (Message != NULL) { fprintf( stderr, "\n" ); } FatalError( Message, MessageParameter, 0 ); } PWSTR GetErrorMessage( DWORD MessageId ) { PWSTR Message, s; Message = NULL; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, MessageId, 0, (PWSTR)&Message, 128, (va_list *)NULL ); if (Message == NULL) { if (Message = (PWSTR)LocalAlloc( 0, 128 )) { swprintf( Message, L"Unable to get message for %08x", MessageId ); } } else { s = wcsrchr( Message, L'\r' ); if (s == NULL) { s = wcsrchr( Message, L'\n' ); } if (s != NULL) { *s = UNICODE_NULL; } } return Message; } PWSTR GetArgAsUnicode( LPSTR s ) { ULONG n; PWSTR ps; n = strlen( s ); ps = HeapAlloc( GetProcessHeap(), 0, (n + 1) * sizeof( WCHAR ) ); if (ps == NULL) { FatalError( "Out of memory", 0, 0 ); } if (MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, s, n, ps, n ) != (LONG)n ) { FatalError( "Unable to convert parameter '%s' to Unicode (%u)", (ULONG_PTR)s, GetLastError() ); } ps[ n ] = UNICODE_NULL; return ps; } WCHAR SearchString[ MAX_PATH ]; ULONG cchSearchString; WCHAR ReplaceString[ MAX_PATH ]; ULONG cchReplaceString; WCHAR RootOfSearch[ MAX_PATH ]; BOOL VerboseFlag; BOOL QueryFlag; HRESULT ProcessLinkFile( PWSTR FileName ); void ProcessLinkFilesInDirectoryTree( PWSTR Directory ); __cdecl main( int argc, char *argv[] ) { char *s; BOOL FileArgumentSeen = FALSE; PWSTR FileName; HRESULT Error; GetEnvironmentVariable( L"SystemRoot", SearchString, MAX_PATH ); cchSearchString = wcslen( SearchString ); if (cchSearchString == 0) { FatalError( "Unable to get value of SYSTEMROOT environment variable", 0, 0 ); } wcscpy( ReplaceString, L"%SystemRoot%" ); cchReplaceString = wcslen( ReplaceString ); while (--argc) { s = *++argv; if (*s == '-' || *s == '/') { while (*++s) { switch( tolower( *s ) ) { case 'r': if (--argc) { GetFullPathName( GetArgAsUnicode( *++argv ), MAX_PATH, RootOfSearch, &FileName ); } else { Usage( "Missing parameter to -r switch", 0 ); } break; case 's': if (--argc) { wcscpy( SearchString, GetArgAsUnicode( *++argv ) ); } else { Usage( "Missing parameter to -%c switch", (ULONG)*s); } cchSearchString = wcslen( SearchString ); if (cchSearchString == 0) { FatalError( "May not specify an empty search string", 0, 0 ); } if (*s == 'S') { if (--argc) { wcscpy( ReplaceString, GetArgAsUnicode( *++argv ) ); } else { Usage( "Missing parameter to -S switch", 0 ); } cchReplaceString = wcslen( ReplaceString ); if (cchSearchString == 0) { FatalError( "May not specify an empty replacement string", 0, 0 ); } } break; case 'q': QueryFlag = TRUE; break; case 'v': VerboseFlag = TRUE; break; default: Usage( "Invalid switch -%c'", (ULONG)*s ); } } } else { if (wcslen( RootOfSearch )) { FatalError( "May not specify file names with -r option", 0, 0 ); } FileArgumentSeen = TRUE; FileName = GetArgAsUnicode( s ); if (FileName == NULL) { Error = (HRESULT)GetLastError(); } else { Error = ProcessLinkFile( FileName ); } if (Error != NO_ERROR) { FatalError( "Failed to load from file '%s' (%ws)", (ULONG_PTR)s, (ULONG_PTR)GetErrorMessage( Error ) ); } } } if (!FileArgumentSeen) { if (wcslen( RootOfSearch )) { ProcessLinkFilesInDirectoryTree( RootOfSearch ); } else { Usage( "No textFile specified", 0 ); } } return 0; } HRESULT ProcessLinkFile( PWSTR FileName ) { HRESULT rc; IShellLink *psl; IPersistFile *ppf; WCHAR szPath[ MAX_PATH ]; WCHAR szNewPath[ MAX_PATH ]; PWSTR s; BOOL FileNameShown; BOOL FileUpdated; if (FAILED(rc = CoInitialize( NULL ))) return rc; rc = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC, &IID_IShellLink, &psl ); if (!SUCCEEDED( rc )) { FatalError( "Unable to create ShellLink instance (%ws)", (ULONG_PTR)GetErrorMessage( rc ), 0 ); } rc = (psl->lpVtbl->QueryInterface)( psl, &IID_IPersistFile, &ppf ); if (!SUCCEEDED( rc )) { FatalError( "Unable to get ShellLink PersistFile interface (%ws)", (ULONG_PTR)GetErrorMessage( rc ), 0 ); ppf->lpVtbl->Release( ppf ); } rc = (ppf->lpVtbl->Load)( ppf, FileName, STGM_READWRITE ); if (!SUCCEEDED( rc )) { printf( "%ws: unable to get load link file (%ws)\n", FileName, GetErrorMessage( rc ) ); ppf->lpVtbl->Release( ppf ); psl->lpVtbl->Release( psl ); return S_OK; } FileUpdated = FALSE; FileNameShown = FALSE; if (VerboseFlag) { printf( "%ws:\n", FileName ); FileNameShown = TRUE; } rc = (psl->lpVtbl->GetPath)( psl, szPath, MAX_PATH, NULL, 0 ); if (SUCCEEDED( rc )) { if (!_wcsnicmp( szPath, SearchString, cchSearchString )) { if (!FileNameShown) { printf( "%ws:\n", FileName ); FileNameShown = TRUE; } printf( " Path: %ws", szPath ); wcscpy( szNewPath, ReplaceString ); wcscat( szNewPath, &szPath[ cchSearchString ] ); if (QueryFlag) { printf( " - would be changed to %ws\n", szNewPath ); } else { rc = (psl->lpVtbl->SetPath)( psl, szNewPath ); if (SUCCEEDED( rc )) { printf( " - changed to %ws\n", szNewPath ); FileUpdated = TRUE; } else { printf( " - unable to modify (%ws)\n", GetErrorMessage( rc ) ); } } } else { if (VerboseFlag) { printf( " Path: %ws\n", szPath ); } } } else { ppf->lpVtbl->Release( ppf ); psl->lpVtbl->Release( psl ); printf( " Unable to get ShellLink Path (%ws)\n", GetErrorMessage( rc ) ); } rc = (psl->lpVtbl->GetWorkingDirectory)( psl, szPath, MAX_PATH ); if (SUCCEEDED( rc )) { if (!_wcsnicmp( szPath, SearchString, cchSearchString )) { if (!FileNameShown) { printf( "%ws:\n", FileName ); FileNameShown = TRUE; } printf( " Working Directory: %ws", szPath ); wcscpy( szNewPath, ReplaceString ); wcscat( szNewPath, &szPath[ cchSearchString ] ); if (QueryFlag) { printf( " - would be changed to %ws\n", szNewPath ); } else { rc = (psl->lpVtbl->SetWorkingDirectory)( psl, szNewPath ); if (SUCCEEDED( rc )) { printf( " - changed to %ws\n", szNewPath ); FileUpdated = TRUE; } else { printf( " - unable to modify (%ws)\n", GetErrorMessage( rc ) ); } } } else { if (VerboseFlag) { printf( " Working Directory: %ws\n", szPath ); } } } else { ppf->lpVtbl->Release( ppf ); psl->lpVtbl->Release( psl ); printf( " Unable to get ShellLink Working Directory (%ws)\n", GetErrorMessage( rc ) ); } if (FileUpdated) { rc = (ppf->lpVtbl->Save)( ppf, FileName, TRUE ); if (SUCCEEDED( rc )) { rc = (ppf->lpVtbl->SaveCompleted)( ppf, FileName ); if (SUCCEEDED( rc )) { printf( " Link file updated.\n" ); } else { printf( " **** unable to save changes to link file (%ws)\n", GetErrorMessage( rc ) ); } } } ppf->lpVtbl->Release( ppf ); psl->lpVtbl->Release( psl ); return S_OK; } void ProcessLinkFilesInDirectoryTree( PWSTR Directory ) { HANDLE FindHandle; WIN32_FIND_DATA FindData; WCHAR Path[ MAX_PATH ]; PWSTR FileName; ULONG n; wcscpy( Path, Directory ); FileName = &Path[ wcslen( Path ) ]; *FileName++ = L'\\'; wcscpy( FileName, L"*" ); FindHandle = FindFirstFile( Path, &FindData ); if (FindHandle == INVALID_HANDLE_VALUE) { return; } if (VerboseFlag) { printf( "%ws\n", Directory ); } SetCurrentDirectory( Directory ); do { if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (wcscmp( FindData.cFileName, L"." ) && wcscmp( FindData.cFileName, L".." )) { wcscpy( FileName, FindData.cFileName ); ProcessLinkFilesInDirectoryTree( Path ); } } else { n = wcslen( FindData.cFileName ); while (n-- && FindData.cFileName[ n ] != L'.') { } if (!_wcsicmp( &FindData.cFileName[ n ], L".lnk" )) { wcscpy( FileName, FindData.cFileName ); ProcessLinkFile( Path ); } } } while (FindNextFile( FindHandle, &FindData )); FindClose( FindHandle ); return; }