Fix lib/ folder DLL resolution for InvokeButton/LinkButton and merge per-user extension paths in all-users installs#3511
Conversation
|
Unable to trigger custom agent "Code Reviewer". You have run out of credits 😔 |
|
@jmcouffin I'm getting back from vacation tomorrow and will take a look on that and others PRs this weekend |
romangolev
left a comment
There was a problem hiding this comment.
Two configuration-scope issues need attention before this fix fully addresses all-users installations.
| // For all-users installs, the active config is in ProgramData. Individual users may | ||
| // also have their own extension paths stored in their per-user config (AppData). Merge | ||
| // both lists so that paths added via the Settings UI or CLI at user scope are honoured. | ||
| if (PyRevitInstallScope.IsAllUsersInstall()) |
There was a problem hiding this comment.
[P1] Respect the read-only machine-config policy before merging user paths. GetActiveConfig() deliberately treats a ProgramData config with the ReadOnly attribute as an admin lock, but this condition checks only the installation scope. It therefore still loads extensions from a user-writable AppData config, bypassing that policy. Please skip this merge when the active config is read-only.
|
|
||
| var activeConfigPath = GetConfig().ConfigPath ?? string.Empty; | ||
| if (File.Exists(perUserConfigPath) && | ||
| !string.Equals(perUserConfigPath, activeConfigPath, StringComparison.OrdinalIgnoreCase)) |
There was a problem hiding this comment.
[P2] Merge machine paths when the active config has fallen back to the per-user config. In a normal non-elevated all-users session, GetActiveConfig() uses AppData whenever ProgramData is not writable. perUserConfigPath then equals activeConfigPath, so this block is skipped and machine-scope userextensions added after the initial seed are never discovered—the admin-scope case from #3312 remains. Consider merging both configs based on ActiveConfigInfo while still honoring the read-only lock.
Two regressions in the v6 new loader affecting extensions that use custom DLLs and all-users deployments.
Issue #5 —
lib/folder no longer searched for extension DLLsInvokeButtonandLinkButtonassembly resolution only searchedbin/subdirectories. Extensions that followed the pre-v6 convention of placing DLLs underlib/silently failed to load.CommandTypeGenerator.ResolveInvokeAssemblyPath(): addsCollectLibraryPaths()alongside the existingCollectBinaryPaths()callLinkButtonBuilder.ResolveAssemblyPath(): same change for LinkButton target assembly resolutionIssue #2 — Per-user extension search paths ignored in all-users installs
When pyRevit is installed machine-wide,
ExtensionParser.GetExtensionRoots()readuserextensionsexclusively from the ProgramData config. Paths written to the per-user AppData config (by the Settings UI or a non-admin CLI invocation) were never discovered by the C# loader.ExtensionParser.GetExtensionRoots(): whenIsAllUsersInstall()is true and a separate per-user config exists at%AppData%\pyRevit, itsuserextensionsentries are merged into the roots list using aHashSet<string>for O(1) case-insensitive deduplication.