@@ -35,6 +35,10 @@ public static SessionValidationResult ValidateSession (SessionData sessionData,
3535 // Cache drive letters once to avoid repeated expensive DriveInfo.GetDrives() calls
3636 var cachedDriveLetters = GetFixedDriveLetters ( ) ;
3737
38+ // Enumerate the session directory and its immediate subdirectories once, instead of
39+ // re-enumerating for every missing file. This is shared across all alternative-path lookups.
40+ var sessionDirectories = GetSessionSearchDirectories ( sessionData . SessionFilePath ) ;
41+
3842 foreach ( var fileName in sessionData . FileNames )
3943 {
4044 var normalizedPath = NormalizeFilePath ( fileName ) ;
@@ -60,7 +64,7 @@ public static SessionValidationResult ValidateSession (SessionData sessionData,
6064 {
6165 result . MissingFiles . Add ( fileName ) ;
6266
63- var alternativePaths = FindAlternativePaths ( fileName , sessionData . SessionFilePath , cachedDriveLetters ) ;
67+ var alternativePaths = FindAlternativePaths ( fileName , sessionData . SessionFilePath , sessionDirectories , cachedDriveLetters ) ;
6468 result . PossibleAlternatives [ fileName ] = alternativePaths ;
6569 }
6670 }
@@ -104,6 +108,43 @@ private static bool IsUri (string fileName)
104108 ! uri . Scheme . Equals ( "file" , StringComparison . OrdinalIgnoreCase ) ;
105109 }
106110
111+ /// <summary>
112+ /// Returns the directories to search for alternative file locations: the session file's directory
113+ /// followed by its immediate subdirectories. Enumerated once per session so that per-missing-file
114+ /// lookups do not repeatedly hit the file system with the same <see cref="Directory.GetDirectories(string)"/> call.
115+ /// </summary>
116+ /// <param name="sessionFilePath">The full path to the session/project file. May be null or empty.</param>
117+ /// <returns>The session directory and its immediate subdirectories, or an empty list if unavailable.</returns>
118+ private static List < string > GetSessionSearchDirectories ( string sessionFilePath )
119+ {
120+ if ( string . IsNullOrWhiteSpace ( sessionFilePath ) )
121+ {
122+ return [ ] ;
123+ }
124+
125+ try
126+ {
127+ var sessionDir = Path . GetDirectoryName ( sessionFilePath ) ;
128+ if ( string . IsNullOrEmpty ( sessionDir ) || ! Directory . Exists ( sessionDir ) )
129+ {
130+ return [ ] ;
131+ }
132+
133+ var directories = new List < string > { sessionDir } ;
134+ directories . AddRange ( Directory . GetDirectories ( sessionDir ) ) ;
135+ return directories ;
136+ }
137+ catch ( Exception ex ) when ( ex is ArgumentException or
138+ ArgumentNullException or
139+ PathTooLongException or
140+ UnauthorizedAccessException or
141+ IOException )
142+ {
143+ // Ignore errors when enumerating the session directory
144+ return [ ] ;
145+ }
146+ }
147+
107148 /// <summary>
108149 /// Gets the list of fixed drive letters that are ready.
109150 /// Extracted to avoid repeated expensive DriveInfo.GetDrives() calls.
@@ -116,12 +157,11 @@ private static List<char> GetFixedDriveLetters ()
116157 . Where ( d => d . IsReady && d . DriveType == DriveType . Fixed )
117158 . Select ( d => d . Name [ 0 ] ) ] ;
118159 }
119- catch ( Exception ex ) when (
120- ex is IOException
121- or UnauthorizedAccessException
122- or SecurityException
123- or DriveNotFoundException
124- or ArgumentNullException )
160+ catch ( Exception ex ) when ( ex is IOException or
161+ UnauthorizedAccessException or
162+ SecurityException or
163+ DriveNotFoundException or
164+ ArgumentNullException )
125165 {
126166 return [ ] ;
127167 }
@@ -140,10 +180,11 @@ or DriveNotFoundException
140180 /// whitespace.</param>
141181 /// <param name="sessionFilePath">The full path to the project file used as a reference for searching related directories. Can be null or empty if
142182 /// project context is not available.</param>
183+ /// <param name="sessionDirectories">Pre-enumerated session directory and its immediate subdirectories, shared across all missing files.</param>
143184 /// <param name="cachedDriveLetters">Pre-computed list of fixed drive letters to avoid repeated DriveInfo.GetDrives() calls.</param>
144185 /// <returns>A list of strings containing the full paths of files found that match the specified file name in alternative
145186 /// locations. The list will be empty if no matching files are found.</returns>
146- private static List < string > FindAlternativePaths ( string fileName , string sessionFilePath , List < char > cachedDriveLetters )
187+ private static List < string > FindAlternativePaths ( string fileName , string sessionFilePath , List < string > sessionDirectories , List < char > cachedDriveLetters )
147188 {
148189 var alternatives = new List < string > ( ) ;
149190
@@ -159,37 +200,11 @@ private static List<string> FindAlternativePaths (string fileName, string sessio
159200 return alternatives ;
160201 }
161202
162- // Search in directory of .lxj project file
163- if ( ! string . IsNullOrWhiteSpace ( sessionFilePath ) )
164- {
165- try
166- {
167- var sessionDir = Path . GetDirectoryName ( sessionFilePath ) ;
168- if ( ! string . IsNullOrEmpty ( sessionDir ) && Directory . Exists ( sessionDir ) )
169- {
170- var candidatePath = Path . Join ( sessionDir , baseName ) ;
171- if ( File . Exists ( candidatePath ) )
172- {
173- alternatives . Add ( candidatePath ) ;
174- }
175-
176- // Also check subdirectories (one level deep)
177- var subdirs = Directory . GetDirectories ( sessionDir ) ;
178- alternatives . AddRange (
179- subdirs
180- . Select ( subdir => Path . Join ( subdir , baseName ) )
181- . Where ( File . Exists ) ) ;
182- }
183- }
184- catch ( Exception ex ) when ( ex is ArgumentException or
185- ArgumentNullException or
186- PathTooLongException or
187- UnauthorizedAccessException or
188- IOException )
189- {
190- // Ignore errors when searching in project directory
191- }
192- }
203+ // Search in directory of .lxj project file and its immediate subdirectories (pre-enumerated once per session)
204+ alternatives . AddRange (
205+ sessionDirectories
206+ . Select ( dir => Path . Join ( dir , baseName ) )
207+ . Where ( File . Exists ) ) ;
193208
194209 // Search in Documents/LogExpert folder
195210 try
0 commit comments