DYN-10278-Fix C3D AppData Extra Version Folder#16951
DYN-10278-Fix C3D AppData Extra Version Folder#16951RobertGlobant20 wants to merge 10 commits intoDynamoDS:masterfrom
Conversation
Added a method that will validate if an assembly versioned exists and will create only one version folder (host version) in: C:\Users\tellro\AppData\Roaming\Dynamo\Dynamo Revit or C:\Users\tellro\AppData\Roaming\Autodesk\C3D 2027\Dynamo
Updating TraverseForExecutableAssembly() method description
Removed since we are always going to reset the value of assemblyPath later.
There was a problem hiding this comment.
Pull request overview
Updates PathManager’s version-folder derivation to avoid creating extra AppData version directories in hosted scenarios (e.g., Civil3D/Revit) by discovering a host-facing assembly version at runtime.
Changes:
- Removes the direct
DynamoCore.dll-path validation previously used duringPathManagerconstruction. - Introduces call-stack traversal to find a “host-facing” assembly with a non-zero file version and uses it to set
majorFileVersion/minorFileVersionwhen not provided.
Comments suppressed due to low confidence (3)
src/DynamoCore/Configuration/PathManager.cs:493
- This change alters how
majorFileVersion/minorFileVersionare derived (now potentially host/integration assembly based), but the PR doesn’t include corresponding unit test updates/additions. There are existing tests that assert the versioned folder suffix (e.g., expecting...\4.1for a Revit resolver), and this new logic should be covered with tests for (1) hosted resolver case uses the intended host version, and (2) test/sandbox scenarios don’t accidentally pick a dependency assembly from the call stack.
// Go up the call-stack to look for a versioned assembly,
// and we will likely discover 'AcDynamo.dll' that is '18.0'.
var assemblyPath = TraverseForExecutableAssembly();
// If both major/minor versions are zero, get from assembly.
majorFileVersion = pathManagerParams.MajorFileVersion;
minorFileVersion = pathManagerParams.MinorFileVersion;
if (majorFileVersion == 0 && (minorFileVersion == 0))
{
var v = FileVersionInfo.GetVersionInfo(assemblyPath);
majorFileVersion = v.FileMajorPart;
minorFileVersion = v.FileMinorPart;
}
src/DynamoCore/Configuration/PathManager.cs:477
- The constructor no longer validates that
corePathactually containsDynamoCore.dll. Previously this threw a clear exception when the core path was misconfigured; now a directory that exists but lacks the core assembly will be accepted and may fail later in less actionable ways. Consider restoring theDynamoCore.dllexistence/valid-path check (usingdynamoCoreDir) while keeping the host-version discovery logic separate.
if (string.IsNullOrEmpty(corePath) || !Directory.Exists(corePath))
{
// If the caller does not provide an alternative core path,
// use the default folder in which DynamoCore.dll resides.
var dynamoCorePath = Assembly.GetExecutingAssembly().Location;
corePath = Path.GetDirectoryName(dynamoCorePath);
}
dynamoCoreDir = corePath;
extensionsDirectories = new HashSet<string>();
viewExtensionsDirectories = new HashSet<string>();
extensionsDirectories.Add(Path.Combine(dynamoCoreDir, ExtensionsDirectoryName));
viewExtensionsDirectories.Add(Path.Combine(dynamoCoreDir, ViewExtensionsDirectoryName));
src/DynamoCore/Configuration/PathManager.cs:490
TraverseForExecutableAssembly()is called unconditionally, even whenpathManagerParams.MajorFileVersion/MinorFileVersionare provided (non-zero) and no version discovery is needed. Since this builds aStackTrace, it’s avoidable overhead in the common case; consider moving the call inside the(majorFileVersion == 0 && minorFileVersion == 0)block and only computingassemblyPathwhen required.
// Go up the call-stack to look for a versioned assembly,
// and we will likely discover 'AcDynamo.dll' that is '18.0'.
var assemblyPath = TraverseForExecutableAssembly();
// If both major/minor versions are zero, get from assembly.
majorFileVersion = pathManagerParams.MajorFileVersion;
minorFileVersion = pathManagerParams.MinorFileVersion;
if (majorFileVersion == 0 && (minorFileVersion == 0))
{
var v = FileVersionInfo.GetVersionInfo(assemblyPath);
| foreach (var frame in stackTrace.GetFrames() ?? Array.Empty<StackFrame>()) | ||
| { | ||
| var assembly = frame.GetMethod()?.DeclaringType?.Assembly; | ||
| if (assembly == null || assembly == currentAssembly || assembly.IsDynamic) | ||
| continue; | ||
|
|
||
| var assemblyName = assembly.GetName().Name; | ||
| if (string.IsNullOrEmpty(assemblyName)) | ||
| continue; | ||
|
|
||
| if (assemblyName.StartsWith("System", StringComparison.OrdinalIgnoreCase) || | ||
| assemblyName.StartsWith("Microsoft", StringComparison.OrdinalIgnoreCase) || | ||
| assemblyName.Equals("mscorlib", StringComparison.OrdinalIgnoreCase) || | ||
| assemblyName.Equals("netstandard", StringComparison.OrdinalIgnoreCase) || | ||
| assemblyName.IndexOf("test", StringComparison.OrdinalIgnoreCase) >= 0) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| var candidatePath = assembly.Location; | ||
| if (PathHelper.IsValidPath(candidatePath)) | ||
| { | ||
| var fileVersion = FileVersionInfo.GetVersionInfo(candidatePath); | ||
| if (fileVersion.FileMajorPart > 0 || fileVersion.FileMinorPart > 0) | ||
| return candidatePath; | ||
| } |
There was a problem hiding this comment.
The stack-walk selection criteria can return an unrelated non-system assembly (e.g., nunit.framework, Moq, other add-ins) as soon as it appears on the call stack, which would cause the data-folder version to be derived from that dependency rather than the host application/integration assembly. This makes the version folder non-deterministic across environments. Consider tightening the selection to something host-specific (for example: prefer an assembly whose Location is under hostApplicationDirectory, or fall back to Assembly.GetEntryAssembly() / process main module when hosted), and only use the stack trace as a last resort.
There was a problem hiding this comment.
This method was re-implemented so the comment is not valid anymore
| /// Traverses the current call stack to identify the first host-facing assembly | ||
| /// with a non-zero file version, and returns its path for version discovery. | ||
| /// | ||
| /// Starting from Dynamo 4.0, data folders are no longer versioned based on | ||
| /// DynamoCore.dll (e.g. '/4.0/data'). Instead, they are versioned according to | ||
| /// the host application's major version (e.g. '/27.0/data' for Revit 2027). | ||
| /// As a result, we must inspect the call stack to locate the host assembly | ||
| /// rather than relying on DynamoCore's assembly version. | ||
| /// </summary> | ||
| /// <returns> | ||
| /// The file path of the host assembly used to determine the data directory version. |
There was a problem hiding this comment.
The XML doc comment states the data folder is versioned by the “host application's major version (e.g. '/27.0/data' for Revit 2027)”, but the implementation actually uses the first non-filtered assembly on the managed call stack with a non-zero file version. That may be the host integration DLL (or even a third-party library) and its file version may not match the host app major version. Please align the documentation with the actual behavior, or adjust the selection logic so it reliably derives the host application version as described.
| /// Traverses the current call stack to identify the first host-facing assembly | |
| /// with a non-zero file version, and returns its path for version discovery. | |
| /// | |
| /// Starting from Dynamo 4.0, data folders are no longer versioned based on | |
| /// DynamoCore.dll (e.g. '/4.0/data'). Instead, they are versioned according to | |
| /// the host application's major version (e.g. '/27.0/data' for Revit 2027). | |
| /// As a result, we must inspect the call stack to locate the host assembly | |
| /// rather than relying on DynamoCore's assembly version. | |
| /// </summary> | |
| /// <returns> | |
| /// The file path of the host assembly used to determine the data directory version. | |
| /// Traverses the current managed call stack to identify the first non-filtered | |
| /// assembly with a non-zero file version, and returns its path for version | |
| /// discovery. | |
| /// | |
| /// Starting from Dynamo 4.0, data folders are no longer versioned based solely on | |
| /// DynamoCore.dll (for example, <c>"/4.0/data"</c>). Instead, the file version of | |
| /// a host-facing assembly on the call stack (typically an integration assembly | |
| /// such as <c>AcDynamo.dll</c>) is used as a proxy for the host application's | |
| /// version when constructing versioned data folders (for example, | |
| /// <c>"/27.0/data"</c> for a host corresponding to version 27.0). | |
| /// | |
| /// Host integrations that rely on this behavior are expected to ensure that the | |
| /// selected assembly's file version matches the intended host application version. | |
| /// This method does not directly inspect the host executable itself; it simply | |
| /// returns the first suitable assembly discovered on the call stack. | |
| /// </summary> | |
| /// <returns> | |
| /// The file path of the assembly whose file version is used to determine the | |
| /// versioned data directory. |
There was a problem hiding this comment.
Also this comment was updated in the last commit so this comment is not valid any more
TraverseForExecutableAssembly() to review assemblies in specific order.
/// 1) First assembly on the current managed call stack under hostApplicationDirectory.
/// 2) Entry assembly location.
/// 3) Current process main module path.
/// 4) First non-system, non-test assembly on the current managed call stack.
/// 5) Fallback to DynamoCore assembly location.
Change in TraverseForExecutableAssembly() to review assemblies in specific order.
There was a problem hiding this comment.
See the ticket for this pull request: https://jira.autodesk.com/browse/DYN-10278
|





Purpose
Added a method that will validate if an assembly versioned exists and will create only one version folder (host version) in a specific path based in the host version:
Declarations
Check these if you believe they are true
Release Notes
Added a method that will validate if an assembly versioned exists and will create only one version folder (host version) in s specific path based in the host
Reviewers
@benglin
@zeusongit
FYIs