Extracting a Shared Parameter GUID from an InternalDefinition
Learn how to extract the GUID of shared parameters in Autodesk Revit without relying on the shared parameter file or scanning millions of elements. This guide explains how to use InternalDefinition and ForgeTypeId, distinguish shared vs. project parameters, and implement a fast, reliable solution for your add-ins.
Introduction
If you're building Revit add-ins and working with shared parameters, you've probably asked yourself: how can I get the GUID of a shared parameter directly from the model—without relying on the shared parameter file or scanning tons of elements?
Good news: by using the InternalDefinition API together with ForgeTypeId, you can grab that GUID efficiently and reliably. In this post, we'll walk through:
- How to collect all non–built-in parameter definitions from the document
- How to extract the GUID from ForgeTypeId
- How to tell shared and project parameters apart
- Why this method is fast and scalable
Why ExternalDefinition Isn’t Enough
Shared parameters live in a shared parameter file you can attach to Revit. Each entry in that file defines a parameter (name, data type, etc.) and includes a GUID. Reading the GUID is straightforward if you have the ExternalDefinition instances from the currently attached shared parameter file.
However, this approach is fragile in real-world projects:
- Different teams or phases may use different parameter files.
- The correct file may not be attached at runtime.
- Some projects use custom or client-specific parameter files.
- File access may be restricted in automated environments.
So relying on the shared parameter file isn't always a safe bet.
Why Not Scan All Elements?
Another idea is to iterate all elements in the project, collect their parameters, and extract GUIDs where available. This might work for small models but quickly becomes a performance issue:
- Large projects can contain hundreds of thousands of elements and millions of parameters.
- Scanning everything increases memory usage and processing time.
- You might not even have an element that uses the shared parameter you're looking for.
We need a fast, document-level approach that doesn’t depend on element instances or the shared parameter file.
The Efficient Approach: Read InternalDefinition from ParameterBindings
Revit maintains a ParameterBindings map on the Document, linking Definition objects to their bindings. You can iterate this map and collect all parameter definitions that are not built-in:
var definitions = new HashSet<InternalDefinition>();
var bindings = document.ParameterBindings;
var iterator = bindings.ForwardIterator();
iterator.Reset();
while (iterator.MoveNext())
{
if (iterator.Key is not InternalDefinition definition)
{
continue;
}
if (definition.BuiltInParameter == BuiltInParameter.INVALID)
{
definitions.Add(definition);
}
}
Enter ForgeTypeId: Extracting the GUID
Revit exposes a ForgeTypeId for an InternalDefinition via InternalDefinition.GetTypeId(). The TypeId string encodes both the parameter origin and the identifier. Examples:
- Shared parameter:
revit.local.shared:15a75d930ff84e9484f3cb1c7af1d1e3-1.0.0
- Project parameter:
revit.local.project:8b2d1a2b1b9c4d8e9f0a1b2c3d4e5f6a-1.0.0
The 32-character segment after the last colon is the GUID.
public static Guid? ExtractGuid(this InternalDefinition definition)
{
const int guidLength = 32;
var forgeTypeId = definition.GetTypeId();
if (forgeTypeId.Empty())
{
return null;
}
var typeId = forgeTypeId.TypeId;
var position = typeId.LastIndexOf(':');
if (position < 0)
{
return null;
}
return Guid.TryParseExact(typeId.Substring(position + 1, guidLength), "N", out var guid)
? guid
: null;
}
Distinguishing Shared vs. Project Parameters
Both shared and project parameters include a GUID in their ForgeTypeId. To determine the type, check the prefix:
revit.local.shared
→ Shared parameterrevit.local.project
→ Project parameter
public static bool IsShared(this InternalDefinition definition)
{
var ftid = definition.GetTypeId();
return !ftid.Empty()
&& ftid.TypeId.StartsWith("revit.local.shared", StringComparison.OrdinalIgnoreCase);
}
public static bool IsProject(this InternalDefinition definition)
{
var ftid = definition.GetTypeId();
return !ftid.Empty()
&& ftid.TypeId.StartsWith("revit.local.project", StringComparison.OrdinalIgnoreCase);
}
Putting It Together: Get All Shared Parameter GUIDs
public static Dictionary<string, Guid> Build(Document document)
{
var defs = ParameterDefinitionCollector.GetNonBuiltInDefinitions(document);
var sharedDefs = defs.Where(d => d.IsShared());
var result = new Dictionary<string, Guid>(StringComparer.OrdinalIgnoreCase);
foreach (var d in sharedDefs)
{
var guid = d.ExtractGuid();
if (guid.HasValue)
{
result[d.Name] = guid.Value;
}
}
return result;
}
Performance Notes
- Iterating Document.ParameterBindings scales with the number of definitions, not elements.
- Avoids creating Parameter objects for every element.
- Fast and lightweight compared to scanning the entire model.
Availability and Refresh Strategy
ParameterBindings are document-specific. You can't build the GUID map once at application startup and reuse it globally. Instead:
- Build the map when a document is opened or on demand.
- Bindings may change during a session if parameters are added or removed.
- Rebuild the map each time you use it or implement a cache with invalidation.
Troubleshooting & Edge Cases
- Always check forgeTypeId.Empty() and handle malformed strings.
- Built-in parameters don’t have shared GUIDs.
- Definition.Name isn’t guaranteed to be unique.
Conclusion
You don’t need the shared parameter file or a slow element scan to get shared parameter GUIDs. By iterating Document.ParameterBindings, filtering to InternalDefinition, and parsing the ForgeTypeId:
- You'll collect all user-defined parameter definitions.
- You'll distinguish shared vs. project parameters using the prefix.
- You'll extract the shared parameter GUID for mapping, analytics, or interoperability.
This approach is fast, robust, and file-agnostic—perfect for production add-ins using the Revit API.