Создание проекта плагина¶
Как с нуля создать проект плагина ModPlus для Revit, AutoCAD, nanoCAD или Renga: структура папок, файл решения и файл проекта (csproj) со всеми обязательными свойствами.
Именование¶
Имя плагина состоит из префикса платформы и названия функции латиницей в PascalCase.
| Платформа | Префикс | Пример |
|---|---|---|
| Revit | mpr |
mprAlignViews |
| AutoCAD | mp |
mpMultiCopy |
| nanoCAD | mnc |
mncMultiCopy |
| Renga | mr |
mrLevelsList |
Одно и то же имя используется для папки решения, файла решения, папки проекта, файла csproj и свойства RootNamespace.
Структура папок¶
Functions\<Платформа>\<Имя>\
├── <Имя>.slnx
├── ModPlus.ruleset
└── <Имя>\
├── <Имя>.csproj
├── Resources\ ← иконки, добавляются перед релизом
└── ...исходный код
ModPlus.ruleset берётся из репозитория ModPlus-Software/Ruleset и кладётся рядом с файлом решения, а не рядом с csproj: проект ссылается на него через $(SolutionDir).
Не копируйте ModPlus.ruleset из других проектов — в них встречаются устаревшие варианты.
Порядок создания¶
- В Visual Studio создайте проект Class Library (C#) с именем плагина.
- Полностью замените содержимое csproj шаблоном для своей платформы.
- Замените в шаблоне
ИМЯна имя плагина. - Положите
ModPlus.rulesetиз репозитория ModPlus-Software/Ruleset рядом с файлом решения. - Настройте конфигурации решения.
- Проверьте проект по чек-листу.
Иконки при создании проекта не нужны — обычно их добавляют в конце разработки, перед релизом (см. Иконки).
Как устроен csproj¶
Все проекты плагинов собираются по одной схеме: одна конфигурация решения — одна версия целевой программы. Конфигурации Release нет.
- Имя конфигурации — буква платформы и номер версии:
R2024(Revit),A2024(AutoCAD),N26.0(nanoCAD),R9.2(Renga). - Свойство
ExternalVersionполучается из имени конфигурации отрезанием первой буквы:$(Configuration.Substring(1)). ДляR2024это2024. - По
ExternalVersionвыбираются:- имя выходной сборки:
mprAlignViews_2024.dll; - константа условной компиляции:
R2024; - пакет API нужной версии:
ModPlus.Revit.Module.2024; - целевая платформа .NET (через условия по конфигурации).
- имя выходной сборки:
- Конфигурация
Debugнужна для разработки. В нейExternalVersionзадаётся явно: каждый разработчик указывает ту версию программы, на которой тестирует. Значения в шаблонах — только пример.
Константа Debug не нужна
Не добавляйте Debug в DefineConstants: в конфигурации Debug SDK сам определяет константу DEBUG. В коде используйте только #if DEBUG / #if !DEBUG — заглавными буквами: директивы препроцессора чувствительны к регистру, и #if Debug — это другой символ.
Обязательные свойства¶
Идентификация плагина¶
| Свойство | Значение | Назначение |
|---|---|---|
Version |
Четыре числа, например 1.0.0.0 |
Версия плагина. Правила изменения — в статье Версионирование плагинов |
RootNamespace |
Имя плагина | Корневое пространство имён; от него строится AssemblyName |
Метаданные сборки¶
Значения одинаковы для всех плагинов, менять их нельзя.
| Свойство | Значение |
|---|---|
Company |
modplus.org |
Authors |
modplus.org |
Product |
ModPlus |
AssemblyTitle |
Plugin for ModPlus software for <Платформа>, где платформа — Revit, AutoCAD, nanoCAD или Renga |
Description |
$(AssemblyTitle) |
Copyright |
Copyright © ModPlus |
AssemblyTitle должен соответствовать платформе плагина. Частая ошибка — проект nanoCAD, созданный копированием проекта AutoCAD.
Параметры сборки¶
| Свойство | Значение | Назначение |
|---|---|---|
CodeAnalysisRuleSet |
$(SolutionDir)\ModPlus.ruleset |
Правила анализа кода (StyleCop) |
Platform |
x64 |
Платформа по умолчанию, если csproj собирается без решения (например, dotnet build по csproj). При сборке из решения значение приходит из решения |
Platforms |
x64 |
Список платформ проекта. Других платформ, кроме x64, быть не должно |
PlatformTarget |
x64 |
Архитектура скомпилированной сборки. Программы бывают только 64-битными, поэтому сборка явно помечается как x64 |
AppendTargetFrameworkToOutputPath |
false |
Не добавлять к выходной папке подпапку net48 / net8.0-windows |
Optimize |
false |
Отключает оптимизацию компилятора |
Prefer32Bit |
false |
Не предпочитать 32-битный режим |
UseWpf |
true |
Поддержка WPF (XAML, ресурсы). Нужна во всех плагинах |
LangVersion |
latest |
Последняя версия C#, доступная компилятору |
Версии программы¶
| Свойство | Значение | Назначение |
|---|---|---|
Configurations |
Debug; + список версий (см. шаблоны) |
Конфигурации проекта. Каждая версия указывается один раз |
TargetFramework |
Зависит от платформы и версии (см. шаблоны) | Целевая платформа .NET |
ExternalVersion |
$(Configuration.Substring(1)), а в Debug — конкретная версия |
Номер версии программы |
DefineConstants |
Константа версии (см. шаблоны) | Условная компиляция под версию программы |
AssemblyName |
$(RootNamespace)_$(ExternalVersion) |
Имя DLL с суффиксом версии |
Порядок групп важен
Группа с AssemblyName должна стоять после группы Debug. Иначе в Debug имя сборки будет построено ещё до того, как ExternalVersion получит своё значение.
OutputPath обязательным не является и в шаблонах не задан. При необходимости его можно указать в группе Debug — путь любой удобный, например:
В других конфигурациях OutputPath не задаётся.
Пакеты¶
| Пакет | Версия | Атрибуты |
|---|---|---|
ModPlus.<Платформа>.Module.$(ExternalVersion) |
Актуальная (см. шаблоны) | ExcludeAssets="runtime" |
StyleCop.Analyzers |
1.2.0-beta.556 |
PrivateAssets=all |
ExcludeAssets="runtime" означает, что библиотеки пакета используются только при компиляции и не копируются в выходную папку: во время работы их предоставляет ModPlus.
Остальные пакеты подключаются по необходимости. Пакеты ModPlus (ModPlusAPI, ModPlusStyle и т. д.) подключаются с ExcludeAssets="runtime".
Windows Forms¶
Если плагин использует Windows Forms, добавьте <UseWindowsForms>true</UseWindowsForms> в общую группу свойств. По умолчанию это свойство не нужно.
Шаблоны csproj¶
Шаблоны полные: скопируйте целиком и замените ИМЯ на имя плагина.
Во всех проектах используется SDK Microsoft.NET.Sdk. Microsoft.NET.Sdk.WindowsDesktop не используйте: WPF включается свойством UseWpf.
- Версии: R2019–R2027.
- .NET: net48 для 2019–2024, net8.0-windows для 2025–2026, net10.0-windows для 2027.
- Константа:
R2024. - Debug: в шаблоне версия 2024, указывается своя.
Если для отладки нужна версия 2025 или новее, добавьте в группу Debug подходящий TargetFramework: net8.0-windows для 2025–2026, net10.0-windows для 2027.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.0.0</Version>
<RootNamespace>ИМЯ</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<Company>modplus.org</Company>
<Authors>modplus.org</Authors>
<Product>ModPlus</Product>
<AssemblyTitle>Plugin for ModPlus software for Revit</AssemblyTitle>
<Description>$(AssemblyTitle)</Description>
<Copyright>Copyright © ModPlus</Copyright>
<CodeAnalysisRuleSet>$(SolutionDir)\ModPlus.ruleset</CodeAnalysisRuleSet>
<Platform>x64</Platform>
<Platforms>x64</Platforms>
<PlatformTarget>x64</PlatformTarget>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Optimize>false</Optimize>
<Prefer32Bit>false</Prefer32Bit>
<UseWpf>true</UseWpf>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<Configurations>Debug;R2019;R2020;R2021;R2022;R2023;R2024;R2025;R2026;R2027</Configurations>
<TargetFramework>net48</TargetFramework>
<ExternalVersion>$(Configuration.Substring(1))</ExternalVersion>
<DefineConstants>R$(ExternalVersion)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'R2025' or '$(Configuration)' == 'R2026'">
<TargetFramework>net8.0-windows</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'R2027'">
<TargetFramework>net10.0-windows</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<ExternalVersion>2024</ExternalVersion>
<DefineConstants>R$(ExternalVersion)</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<AssemblyName>$(RootNamespace)_$(ExternalVersion)</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ModPlus.Revit.Module.$(ExternalVersion)" Version="39.0.0" ExcludeAssets="runtime" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
- Версии: A2013–A2027.
- .NET: net48 для 2013–2024, net8.0-windows для 2025–2026, net10.0-windows для 2027.
- Константа:
A2024. - Debug: в шаблоне версия 2024, указывается своя.
Если для отладки нужна версия 2025 или новее, добавьте в группу Debug подходящий TargetFramework: net8.0-windows для 2025–2026, net10.0-windows для 2027.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.0.0</Version>
<RootNamespace>ИМЯ</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<Company>modplus.org</Company>
<Authors>modplus.org</Authors>
<Product>ModPlus</Product>
<AssemblyTitle>Plugin for ModPlus software for AutoCAD</AssemblyTitle>
<Description>$(AssemblyTitle)</Description>
<Copyright>Copyright © ModPlus</Copyright>
<CodeAnalysisRuleSet>$(SolutionDir)\ModPlus.ruleset</CodeAnalysisRuleSet>
<Platform>x64</Platform>
<Platforms>x64</Platforms>
<PlatformTarget>x64</PlatformTarget>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Optimize>false</Optimize>
<Prefer32Bit>false</Prefer32Bit>
<UseWpf>true</UseWpf>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<Configurations>Debug;A2013;A2014;A2015;A2016;A2017;A2018;A2019;A2020;A2021;A2022;A2023;A2024;A2025;A2026;A2027</Configurations>
<TargetFramework>net48</TargetFramework>
<ExternalVersion>$(Configuration.Substring(1))</ExternalVersion>
<DefineConstants>A$(ExternalVersion)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'A2025' or '$(Configuration)' == 'A2026'">
<TargetFramework>net8.0-windows</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'A2027'">
<TargetFramework>net10.0-windows</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<ExternalVersion>2024</ExternalVersion>
<DefineConstants>A$(ExternalVersion)</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<AssemblyName>$(RootNamespace)_$(ExternalVersion)</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ModPlus.AutoCAD.Module.$(ExternalVersion)" Version="16.0.0" ExcludeAssets="runtime" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
- Версии: N25.0 и N26.0.
- .NET: net6-windows для всех версий.
- Константа: точка в номере версии заменяется на подчёркивание —
N26_0(идентификатор C# не может содержать точку). - Debug: в шаблоне версия 26.0, указывается своя.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.0.0</Version>
<RootNamespace>ИМЯ</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<Company>modplus.org</Company>
<Authors>modplus.org</Authors>
<Product>ModPlus</Product>
<AssemblyTitle>Plugin for ModPlus software for nanoCAD</AssemblyTitle>
<Description>$(AssemblyTitle)</Description>
<Copyright>Copyright © ModPlus</Copyright>
<CodeAnalysisRuleSet>$(SolutionDir)\ModPlus.ruleset</CodeAnalysisRuleSet>
<Platform>x64</Platform>
<Platforms>x64</Platforms>
<PlatformTarget>x64</PlatformTarget>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Optimize>false</Optimize>
<Prefer32Bit>false</Prefer32Bit>
<UseWpf>true</UseWpf>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<Configurations>Debug;N25.0;N26.0</Configurations>
<TargetFramework>net6-windows</TargetFramework>
<ExternalVersion>$(Configuration.Substring(1))</ExternalVersion>
<DefineConstants>N$(ExternalVersion.Replace('.', '_'))</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<ExternalVersion>26.0</ExternalVersion>
<DefineConstants>N$(ExternalVersion.Replace('.', '_'))</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<AssemblyName>$(RootNamespace)_$(ExternalVersion)</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ModPlus.nanoCAD.Module.$(ExternalVersion)" Version="2.0.0" ExcludeAssets="runtime" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
- Версии: R5.1–R9.2.
- .NET: net48 для всех версий.
- Константа: точка заменяется на подчёркивание —
R9_2. - Debug: в шаблоне версия 9.2, указывается своя.
Отличия от других платформ:
- обязательна ссылка
COMFileReferenceнаRengaCOMAPI.tlbиз пакетаModPlus.Renga.API.$(ExternalVersion)с параметрамиEmbedInteropTypes=FalseиPrivate=False; - обязательна ссылка
<Reference Include="Microsoft.CSharp" />; - иконки подключаются иначе (см. Иконки).
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.0.0</Version>
<RootNamespace>ИМЯ</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<Company>modplus.org</Company>
<Authors>modplus.org</Authors>
<Product>ModPlus</Product>
<AssemblyTitle>Plugin for ModPlus software for Renga</AssemblyTitle>
<Description>$(AssemblyTitle)</Description>
<Copyright>Copyright © ModPlus</Copyright>
<CodeAnalysisRuleSet>$(SolutionDir)\ModPlus.ruleset</CodeAnalysisRuleSet>
<Platform>x64</Platform>
<Platforms>x64</Platforms>
<PlatformTarget>x64</PlatformTarget>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Optimize>false</Optimize>
<Prefer32Bit>false</Prefer32Bit>
<UseWpf>true</UseWpf>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<Configurations>Debug;R5.1;R6.0;R7.0;R7.4;R8.0;R8.8;R8.9;R9.0;R9.2</Configurations>
<TargetFramework>net48</TargetFramework>
<ExternalVersion>$(Configuration.Substring(1))</ExternalVersion>
<DefineConstants>R$(ExternalVersion.Replace('.', '_'))</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<ExternalVersion>9.2</ExternalVersion>
<DefineConstants>R$(ExternalVersion.Replace('.', '_'))</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<AssemblyName>$(RootNamespace)_$(ExternalVersion)</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ModPlus.Renga.Module.$(ExternalVersion)" Version="16.0.0" ExcludeAssets="runtime" />
</ItemGroup>
<ItemGroup>
<COMFileReference Include="$(NuGetPackageRoot)\ModPlus.Renga.API.$(ExternalVersion)\2.0.0\lib\RengaCOMAPI.tlb">
<EmbedInteropTypes>False</EmbedInteropTypes>
<Private>False</Private>
</COMFileReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
</Project>
Иконки¶
На этапе разработки иконки не обязательны, поэтому в шаблонах их нет. Обычно их добавляют в конце, перед релизом, и тогда же добавляют в csproj группу элементов с иконками.
Ссылки на иконки добавляются в csproj вместе с самими файлами, не раньше.
Нужны четыре файла в папке Resources, тип сборки — Resource:
| Файл | Назначение |
|---|---|
<Имя>_16x16.png |
Маленькая иконка, светлая тема |
<Имя>_16x16_dark.png |
Маленькая иконка, тёмная тема |
<Имя>_32x32.png |
Большая иконка, светлая тема |
<Имя>_32x32_dark.png |
Большая иконка, тёмная тема |
Если в плагине несколько команд, такой набор из четырёх файлов нужен для каждой команды, с её именем.
Тип сборки — именно Resource, а не EmbeddedResource, Content или None.
Группа элементов добавляется после групп с пакетами:
Иконки лежат в двух папках, тёмных вариантов нет:
Icons\<Имя>_16x16.pngиIcons\<Имя>_24x24.png— тип сборкиEmbeddedResource;Resources\<Имя>_16x16.pngиResources\<Имя>_32x32.png— тип сборкиResource.
Решение¶
Решение создаётся только в формате .slnx. Формат .sln для новых проектов не используется.
Конфигурации решения должны совпадать с конфигурациями проекта. Платформа решения — только x64.
<Solution>
<Configurations>
<BuildType Name="Debug" />
<BuildType Name="R2019" />
<BuildType Name="R2020" />
<BuildType Name="R2021" />
<BuildType Name="R2022" />
<BuildType Name="R2023" />
<BuildType Name="R2024" />
<BuildType Name="R2025" />
<BuildType Name="R2026" />
<BuildType Name="R2027" />
<Platform Name="x64" />
</Configurations>
<Project Path="ИМЯ/ИМЯ.csproj">
<Platform Project="x64" />
</Project>
</Solution>
Для других платформ список BuildType берётся из свойства Configurations соответствующего шаблона.
Чего делать нельзя¶
- Личные конфигурации (
DebugИванов,Debug2025и т. п.). Чтобы отлаживать на другой версии, достаточно поменятьExternalVersionв группе Debug на своей машине, не коммитя это изменение. - Абсолютные пути (
D:\...,C:\Users\...) везде, кромеOutputPathв группе Debug. В остальных местах — только$(SolutionDir),$(NuGetPackageRoot)и относительные пути. - Константа
DebugвDefineConstantsи#if Debugв коде. Правильно:#if DEBUG. - Повторы в
Configurations— каждая версия указывается один раз. - Одно свойство дважды, в том числе в другом регистре (
UseWpfиUseWPF,OutputPathиoutputpath). - Конфигурация
Releaseи платформаAnyCPU. - Служебные файлы в репозитории:
*_wpftmp.csproj,* - Backup.csproj, архивы старых версий проекта.*_wpftmp.csproj— временные файлы сборки WPF; если они остались в папке проекта, их надо удалить. AssemblyTitleот другой платформы после копирования проекта.- Устаревшие версии пакетов:
StyleCop.Analyzers 1.1.118, старые версии Module.
Чек-лист¶
Перед первым коммитом:
- Имя плагина с правильным префиксом, одинаковое для папки, решения, csproj и
RootNamespace. -
ModPlus.rulesetиз репозитория Ruleset лежит рядом с файлом решения. -
AssemblyTitleсоответствует платформе. -
Platform,PlatformsиPlatformTargetравныx64и стоят в общей группе свойств. - Каждая версия в
Configurationsуказана один раз; решение содержит те же конфигурации. - В Debug:
ExternalVersionзадана,TargetFrameworkей соответствует,Debug;вDefineConstantsнет. - Группа с
AssemblyNameстоит после группы Debug. - Пакет Module актуальной версии подключён с
ExcludeAssets="runtime". -
StyleCop.Analyzers 1.2.0-beta.556подключён сPrivateAssets=all. - В csproj нет личных конфигураций и абсолютных путей (кроме
OutputPathв Debug). -
OutputPath, если задан, — только в группе Debug. - Проект собирается в конфигурации Debug и хотя бы в одной конфигурации каждой версии .NET (например, R2024, R2025, R2027).
Перед релизом дополнительно:
- Для каждой команды добавлены все иконки с нужным типом сборки (см. Иконки).