@@ -1157,145 +1157,62 @@ private static string GetHomeOrCreateTempHome()
11571157 }
11581158
11591159 private readonly static Version PSVersion6 = new Version ( 6 , 0 ) ;
1160+ private readonly static Version PSVersion7_7 = new Version ( 7 , 7 , 0 ) ;
11601161
11611162 /// <summary>
1162- /// Gets the user content directory path based on PSContentPath experimental feature settings .
1163- /// Checks if PSContentPath is enabled and returns the appropriate path (custom, default, or legacy) .
1163+ /// Gets the user content directory path using PowerShell's GetPSModulePath API .
1164+ /// Falls back to legacy path if the API is not available or PowerShell version is below 7.7.0 .
11641165 /// </summary>
1165- private static string GetUserContentPath ( PSCmdlet psCmdlet , string defaultPSContentPath , string legacyPath )
1166+ private static string GetUserContentPath ( PSCmdlet psCmdlet , string legacyPath )
11661167 {
1167- bool usePSContentPath = IsExperimentalFeatureEnabled ( psCmdlet , "PSContentPath" ) ;
1168-
1169- if ( usePSContentPath )
1170- {
1171- psCmdlet . WriteVerbose ( "PSContentPath experimental feature is enabled" ) ;
1172-
1173- // Check environment variable and config file for custom PSUserContentPath
1174- string customPSUserContentPath = GetPSUserContentPath ( psCmdlet ) ;
1175-
1176- if ( ! string . IsNullOrEmpty ( customPSUserContentPath ) && Directory . Exists ( customPSUserContentPath ) )
1177- {
1178- // Use custom configured path
1179- psCmdlet . WriteVerbose ( $ "Using custom PSUserContentPath: { customPSUserContentPath } ") ;
1180- return customPSUserContentPath ;
1181- }
1182- else
1183- {
1184- // Use default PSContentPath location when feature is enabled
1185- psCmdlet . WriteVerbose ( $ "Using default PSContentPath location: { defaultPSContentPath } ") ;
1186- return defaultPSContentPath ;
1187- }
1188- }
1189- else
1190- {
1191- // PSContentPath not enabled, use legacy location
1192- psCmdlet . WriteVerbose ( $ "Using legacy location: { legacyPath } ") ;
1193- return legacyPath ;
1194- }
1195- }
1168+ // Get PowerShell engine version from $PSVersionTable.PSVersion
1169+ Version psVersion = psCmdlet . SessionState . PSVariable . GetValue ( "PSVersionTable" ) is Hashtable versionTable
1170+ && versionTable [ "PSVersion" ] is Version version
1171+ ? version
1172+ : new Version ( 5 , 1 ) ;
11961173
1197- /// <summary>
1198- /// Checks if a PowerShell experimental feature is enabled by reading the PowerShell configuration file.
1199- /// Returns false if the configuration file doesn't exist or if the feature is not enabled.
1200- /// </summary>
1201- private static bool IsExperimentalFeatureEnabled ( PSCmdlet psCmdlet , string featureName )
1202- {
1203- try
1174+ // Only use GetPSModulePath API if PowerShell version is 7.7.0 or greater (when PSContentPath feature is available)
1175+ if ( psVersion >= PSVersion7_7 )
12041176 {
1205- // PowerShell configuration file location
1206- string configPath = Path . Combine (
1207- Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) ,
1208- "powershell" ,
1209- "powershell.config.json"
1210- ) ;
1211-
1212- if ( ! File . Exists ( configPath ) )
1213- {
1214- psCmdlet . WriteVerbose ( "PowerShell configuration file not found, experimental features not enabled" ) ;
1215- return false ;
1216- }
1217-
1218- string jsonContent = File . ReadAllText ( configPath ) ;
1219- using ( var jsonDoc = JsonDocument . Parse ( jsonContent ) )
1177+ // Try to use PowerShell's GetPSModulePath API via reflection
1178+ // This API automatically respects PSContentPath settings
1179+ try
12201180 {
1221- // Look for "ExperimentalFeatures": ["FeatureName"] in the config
1222- if ( jsonDoc . RootElement . TryGetProperty ( "ExperimentalFeatures" , out var experimentalFeatures ) &&
1223- experimentalFeatures . ValueKind == JsonValueKind . Array )
1181+ var moduleIntrinsicsType = typeof ( PSModuleInfo ) . Assembly . GetType ( "System.Management.Automation.ModuleIntrinsics" ) ;
1182+ var scopeEnumType = typeof ( PSModuleInfo ) . Assembly . GetType ( "System.Management.Automation.PSModulePathScope" ) ;
1183+
1184+ if ( moduleIntrinsicsType != null && scopeEnumType != null )
12241185 {
1225- foreach ( var feature in experimentalFeatures . EnumerateArray ( ) )
1186+ var getPSModulePathMethod = moduleIntrinsicsType . GetMethod ( "GetPSModulePath" , System . Reflection . BindingFlags . Public | System . Reflection . BindingFlags . Static ) ;
1187+
1188+ if ( getPSModulePathMethod != null )
12261189 {
1227- if ( string . Equals ( feature . GetString ( ) , featureName , StringComparison . OrdinalIgnoreCase ) )
1190+ // PSModulePathScope.User = 0
1191+ object userScope = Enum . ToObject ( scopeEnumType , 0 ) ;
1192+ string userModulePath = ( string ) getPSModulePathMethod . Invoke ( null , new object [ ] { userScope } ) ;
1193+
1194+ if ( ! string . IsNullOrEmpty ( userModulePath ) )
12281195 {
1229- psCmdlet . WriteVerbose ( string . Format ( "Experimental feature '{0}' found in configuration file" , featureName ) ) ;
1230- return true ;
1196+ string userContentPath = Path . GetDirectoryName ( userModulePath ) ;
1197+ psCmdlet . WriteVerbose ( $ "User content path from GetPSModulePath API: { userContentPath } ") ;
1198+ return userContentPath ;
12311199 }
12321200 }
12331201 }
12341202 }
1235-
1236- psCmdlet . WriteVerbose ( string . Format ( "Experimental feature '{0}' not found in configuration file" , featureName ) ) ;
1237- return false ;
1238- }
1239- catch ( Exception ex )
1240- {
1241- psCmdlet . WriteVerbose ( string . Format ( "Error reading PowerShell configuration file: {0}" , ex . Message ) ) ;
1242- return false ;
1243- }
1244- }
1245-
1246- /// <summary>
1247- /// Gets the custom PSUserContentPath from environment variable or PowerShell configuration file.
1248- /// Environment variable takes precedence over the configuration file setting.
1249- /// Returns null if neither is set or configured.
1250- /// </summary>
1251- private static string GetPSUserContentPath ( PSCmdlet psCmdlet )
1252- {
1253- try
1254- {
1255- // First check the environment variable (takes precedence)
1256- string envPSUserContentPath = Environment . GetEnvironmentVariable ( "PSUserContentPath" ) ;
1257- if ( ! string . IsNullOrEmpty ( envPSUserContentPath ) )
1258- {
1259- psCmdlet . WriteVerbose ( string . Format ( "Found PSUserContentPath from environment variable: {0}" , envPSUserContentPath ) ) ;
1260- return envPSUserContentPath ;
1261- }
1262-
1263- // If environment variable not set, check the configuration file
1264- string configPath = Path . Combine (
1265- Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) ,
1266- "powershell" ,
1267- "powershell.config.json"
1268- ) ;
1269-
1270- if ( ! File . Exists ( configPath ) )
1271- {
1272- psCmdlet . WriteVerbose ( "PowerShell configuration file not found" ) ;
1273- return null ;
1274- }
1275-
1276- string jsonContent = File . ReadAllText ( configPath ) ;
1277- using ( var jsonDoc = JsonDocument . Parse ( jsonContent ) )
1203+ catch ( Exception ex )
12781204 {
1279- // Look for PSUserContentPath in the config
1280- if ( jsonDoc . RootElement . TryGetProperty ( "PSUserContentPath" , out var pathElement ) )
1281- {
1282- string psUserContentPath = pathElement . GetString ( ) ;
1283- if ( ! string . IsNullOrEmpty ( psUserContentPath ) )
1284- {
1285- psCmdlet . WriteVerbose ( string . Format ( "Found PSUserContentPath in config file: {0}" , psUserContentPath ) ) ;
1286- return psUserContentPath ;
1287- }
1288- }
1205+ psCmdlet . WriteVerbose ( $ "GetPSModulePath API not available: { ex . Message } ") ;
12891206 }
1290-
1291- psCmdlet . WriteVerbose ( "PSUserContentPath not configured in PowerShell configuration file or environment variable" ) ;
1292- return null ;
12931207 }
1294- catch ( Exception ex )
1208+ else
12951209 {
1296- psCmdlet . WriteVerbose ( string . Format ( "Error reading PSUserContentPath: {0}" , ex . Message ) ) ;
1297- return null ;
1210+ psCmdlet . WriteVerbose ( $ "PowerShell version { psVersion } is below 7.7.0, using legacy location") ;
12981211 }
1212+
1213+ // Fallback to legacy location
1214+ psCmdlet . WriteVerbose ( $ "Using legacy location: { legacyPath } ") ;
1215+ return legacyPath ;
12991216 }
13001217
13011218 private static void GetStandardPlatformPaths (
@@ -1305,7 +1222,13 @@ private static void GetStandardPlatformPaths(
13051222 {
13061223 if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
13071224 {
1308- string powerShellType = ( psCmdlet . Host . Version >= PSVersion6 ) ? "PowerShell" : "WindowsPowerShell" ;
1225+ // Get PowerShell engine version from $PSVersionTable.PSVersion
1226+ Version psVersion = psCmdlet . SessionState . PSVariable . GetValue ( "PSVersionTable" ) is Hashtable versionTable
1227+ && versionTable [ "PSVersion" ] is Version version
1228+ ? version
1229+ : new Version ( 5 , 1 ) ; // Default to Windows PowerShell version if unable to determine
1230+
1231+ string powerShellType = ( psVersion >= PSVersion6 ) ? "PowerShell" : "WindowsPowerShell" ;
13091232
13101233 // Windows PowerShell doesn't support experimental features or PSContentPath
13111234 if ( powerShellType == "WindowsPowerShell" )
@@ -1316,33 +1239,22 @@ private static void GetStandardPlatformPaths(
13161239 }
13171240 else
13181241 {
1319- string defaultPSContentPath = Path . Combine (
1320- Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) ,
1321- powerShellType
1322- ) ;
13231242 string legacyPath = Path . Combine (
13241243 Environment . GetFolderPath ( Environment . SpecialFolder . MyDocuments ) ,
13251244 powerShellType
13261245 ) ;
13271246
1328- localUserDir = GetUserContentPath ( psCmdlet , defaultPSContentPath , legacyPath ) ;
1247+ localUserDir = GetUserContentPath ( psCmdlet , legacyPath ) ;
13291248 }
13301249
13311250 allUsersDir = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) , powerShellType ) ;
13321251 }
13331252 else
13341253 {
13351254 // paths are the same for both Linux and macOS
1336- string xdgDataHome = Environment . GetEnvironmentVariable ( "XDG_DATA_HOME" ) ;
1337- if ( string . IsNullOrEmpty ( xdgDataHome ) )
1338- {
1339- xdgDataHome = Path . Combine ( GetHomeOrCreateTempHome ( ) , ".local" , "share" ) ;
1340- }
1341-
1342- string defaultPSContentPath = Path . Combine ( xdgDataHome , "powershell" ) ;
13431255 string legacyPath = Path . Combine ( GetHomeOrCreateTempHome ( ) , ".local" , "share" , "powershell" ) ;
13441256
1345- localUserDir = GetUserContentPath ( psCmdlet , defaultPSContentPath , legacyPath ) ;
1257+ localUserDir = GetUserContentPath ( psCmdlet , legacyPath ) ;
13461258
13471259 // Create the default data directory if it doesn't exist
13481260 if ( ! Directory . Exists ( localUserDir ) )
0 commit comments