I'm trying to create a plugin for D365. I have package dependencies including
- Azure.DigitalTwins.Core
- Azure.Core
- Azure.Identity
These .dll dependencies must be merged into a single .dll to register a plugin. I'm using an ILMerge batch file script to combine these dlls into a single merged.dll output.
Here's the script I'm using:
@echo off
:: this script needs https://www.nuget.org/packages/ilmerge
:: Set build, used for directory. Typically Release or Debug
SET ILMERGE_BUILD=bin\Debug\ilmerge
:: set your NuGet ILMerge Version, this is the number from the package manager install, for example:
:: PM> Install-Package ilmerge -Version 3.0.29
:: to confirm it is installed for a given project, see the packages.config file
SET ILMERGE_VERSION=3.0.41
:: the full ILMerge should be found here:
SET ILMERGE_PATH=%USERPROFILE%\.nuget\packages\ilmerge\%ILMERGE_VERSION%\tools\net452
:: dir "%ILMERGE_PATH%"\ILMerge.exe
SET PROJECT_ROOT=%CD%
SET ASSEMBLY_DIR=%PROJECT_ROOT%\bin\Debug\net462
::/targetplatform:v4,"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0" ^
::/target:library ^
::/copyattrs ^
::/closed ^
::/allowDup ^
echo Merging adtplugin...
:: add project DLL's starting with replacing the FirstLib with this project's DLL
"%ILMERGE_PATH%"\ILMerge.exe ^
/keyfile:%PROJECT_ROOT%\adtplugin.snk ^
/out:%ILMERGE_BUILD%\merged.dll ^
/target:library ^
/nDebug ^
/closed ^
/allowDup ^
/copyattrs ^
/targetplatform:v4,"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2" ^
%ASSEMBLY_DIR%\adtplugin.dll ^
%ASSEMBLY_DIR%\Azure.Core.dll ^
%ASSEMBLY_DIR%\Azure.DigitalTwins.Core.dll ^
%ASSEMBLY_DIR%\Azure.Identity.dll ^
%ASSEMBLY_DIR%\Microsoft.Bcl.AsyncInterfaces.dll ^
%ASSEMBLY_DIR%\Microsoft.Identity.Client.dll ^
%ASSEMBLY_DIR%\Microsoft.Identity.Client.Extensions.Msal.dll ^
%ASSEMBLY_DIR%\Microsoft.Win32.Primitives.dll ^
%ASSEMBLY_DIR%\netstandard.dll ^
%ASSEMBLY_DIR%\System.Memory.dll ^
%ASSEMBLY_DIR%\System.Threading.Tasks.Extensions.dll
pause
:Done
I've played around quite a bit with the /option flags passed to ILMerge.exe. Currently with the config shown, I'm able to merge the .dlls listed into a single merged.dll output.
Next, I'm trying to use the Plug-in registration tool to register a new assembly using the merged.dll output file. When I try to register the plugin I'm getting errors:
Here's the error dump:
Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Unable to load plug-in assembly.
Detail: <OrganizationServiceFault xmlns="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ActivityId>9e6ed737-8374-464e-956a-d70b46a2c686</ActivityId>
<ErrorCode>-2147204719</ErrorCode>
<ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
<KeyValuePairOfstringanyType>
<a:key>ApiExceptionSourceKey</a:key>
<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">Plugin/Microsoft.Crm.ObjectModel.PluginAssemblyService</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>ApiSourceActivityKey</a:key>
<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">Microsoft.Crm.Extensibility.PipelineStep</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>ApiExceptionOwnerKey</a:key>
<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">COMMONDATASERVICECDS\CDSAPI-Reliability</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>ApiOriginalExceptionKey</a:key>
<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">Microsoft.Crm.CrmException: Unable to load plug-in assembly. ---> Microsoft.Crm.CrmException: Unable to load plug-in assembly.
at Microsoft.Crm.ObjectModel.PluginAssemblyServiceInternal`1.LoadCrmPluginAssemblyMetadata(IBusinessEntity pluginAssembly, IExecutionContext context, Boolean loadAllMetadata)
at Microsoft.Crm.ObjectModel.PluginAssemblyServiceInternal`1.RetrieveAssemblyMetadata(IBusinessEntity pluginAssembly, ExecutionContext context, Boolean retrieveFromExisting, Boolean forSystemAssembly)
at Microsoft.Crm.ObjectModel.PluginAssemblyServiceInternal`1.ValidateOperation(String operationName, IBusinessEntity entity, ExecutionContext context)
at Microsoft.Crm.ObjectModel.SdkEntityServiceBase.CreateInternal(IBusinessEntity entity, ExecutionContext context, Boolean verifyAction)
--- End of inner exception stack trace ---
at Microsoft.Crm.Extensibility.VersionedPluginProxyStepBase.Execute(PipelineExecutionContext context)
at Microsoft.Crm.Extensibility.PipelineInstrumentationHelper.Execute(Boolean instrumentationEnabled, String stopwatchName, ExecuteWithInstrumentation action, PipelineExecutionContext context)
at Microsoft.Crm.Extensibility.Pipeline.<>c__DisplayClass7_0.<RunStep>b__0()</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>ApiStepKey</a:key>
<a:value i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">3ecabb1b-ea3e-db11-86a7-000a3a5473e8</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>ApiDepthKey</a:key>
<a:value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">1</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>ApiActivityIdKey</a:key>
<a:value i:type="b:guid" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/">9e6ed737-8374-464e-956a-d70b46a2c686</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>ApiPluginSolutionNameKey</a:key>
<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">System</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>ApiStepSolutionNameKey</a:key>
<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">System</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>ApiExceptionCategory</a:key>
<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">SystemFailure</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>ApiExceptionMessageName</a:key>
<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">UnableToLoadPluginAssembly</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>ApiExceptionHttpStatusCode</a:key>
<a:value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">500</a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>0</a:key>
<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">Failed to load plugin assembly with exception Microsoft.Crm.CrmException: GetAssemblyMetadata: expected mdtAssemblyRef or mdtAssembly
at Microsoft.Crm.CrmException.Assert(Boolean condition, String message)
at Microsoft.Crm.CrmPluginAssemblyMetadata.GetAssemblyMetadata(UInt32 token)
at Microsoft.Crm.CrmPluginAssemblyMetadata.ProcessTypeRef(UInt32 typeRef)
at Microsoft.Crm.CrmPluginAssemblyMetadata.ProcessType(UInt32 token)
at Microsoft.Crm.CrmPluginAssemblyMetadata.ProcessTypeDef(UInt32 typeDef)
at Microsoft.Crm.CrmPluginAssemblyMetadata.ProcessType(UInt32 token)
at Microsoft.Crm.CrmPluginAssemblyMetadata.LoadMetadata()
at Microsoft.Crm.CrmPluginAssemblyMetadata.LoadMetadataFromAssemblyContent(ArraySegment`1 assemblyContents, Boolean loadAllMetadata)
at Microsoft.Crm.CrmPluginAssemblyMetadata.LoadMetadataFromAssemblyContent(String content, Boolean loadAllMetadata)
at Microsoft.Crm.ObjectModel.PluginAssemblyServiceInternal`1.LoadCrmPluginAssemblyMetadata(IBusinessEntity pluginAssembly, IExecutionContext context, Boolean loadAllMetadata). PluginInfo => Crm plugin assembly info : sourcetype = 0, description = , ismanaged = False, pluginassemblyid = b77380cb-8676-45b6-9e5c-63a5960ed2ac, ispasswordset = False, publickeytoken = 7F66FA42F6885FDC, name = merged, culture = neutral, isolationmode = 2, version = 1.0.0.0, </a:value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType>
<a:key>1</a:key>
<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">Microsoft.Crm.CrmException: GetAssemblyMetadata: expected mdtAssemblyRef or mdtAssembly
at Microsoft.Crm.CrmException.Assert(Boolean condition, String message)
at Microsoft.Crm.CrmPluginAssemblyMetadata.GetAssemblyMetadata(UInt32 token)
at Microsoft.Crm.CrmPluginAssemblyMetadata.ProcessTypeRef(UInt32 typeRef)
at Microsoft.Crm.CrmPluginAssemblyMetadata.ProcessType(UInt32 token)
at Microsoft.Crm.CrmPluginAssemblyMetadata.ProcessTypeDef(UInt32 typeDef)
at Microsoft.Crm.CrmPluginAssemblyMetadata.ProcessType(UInt32 token)
at Microsoft.Crm.CrmPluginAssemblyMetadata.LoadMetadata()
at Microsoft.Crm.CrmPluginAssemblyMetadata.LoadMetadataFromAssemblyContent(ArraySegment`1 assemblyContents, Boolean loadAllMetadata)
at Microsoft.Crm.CrmPluginAssemblyMetadata.LoadMetadataFromAssemblyContent(String content, Boolean loadAllMetadata)
at Microsoft.Crm.ObjectModel.PluginAssemblyServiceInternal`1.LoadCrmPluginAssemblyMetadata(IBusinessEntity pluginAssembly, IExecutionContext context, Boolean loadAllMetadata)</a:value>
</KeyValuePairOfstringanyType>
</ErrorDetails>
<HelpLink>http://go.microsoft.com/fwlink/?LinkID=398563&error=Microsoft.Crm.CrmException%3a80044191&client=platform</HelpLink>
<Message>Unable to load plug-in assembly.</Message>
<Timestamp>2021-04-29T17:46:08.9958593Z</Timestamp>
<ExceptionRetriable>false</ExceptionRetriable>
<ExceptionSource i:nil="true" />
<InnerFault>
<ActivityId>9e6ed737-8374-464e-956a-d70b46a2c686</ActivityId>
<ErrorCode>-2147204719</ErrorCode>
<ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
<HelpLink i:nil="true" />
<Message>Unable to load plug-in assembly.</Message>
<Timestamp>2021-04-29T17:46:08.9958593Z</Timestamp>
<ExceptionRetriable>false</ExceptionRetriable>
<ExceptionSource i:nil="true" />
<InnerFault i:nil="true" />
<OriginalException i:nil="true" />
<TraceText i:nil="true" />
</InnerFault>
<OriginalException i:nil="true" />
<TraceText i:nil="true" />
</OrganizationServiceFault>
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at Microsoft.Crm.Tools.Libraries.RegistrationHelper.RegisterAssembly(CrmOrganization org, String pathToAssembly, CrmPluginAssembly assembly) in D:\a\1\s\src\GeneralTools\PluginRegistrationV3\PluginRegistration\CrmLibraries\RegistrationHelper.cs:line 723
at Microsoft.Crm.Tools.AssemblyRegistration.PluginRegistrationViewModel.RegisterAssembly() in D:\a\1\s\src\GeneralTools\PluginRegistrationV3\RegistrationTools\AssemblyRegistration\ViewModels\PluginRegistrationViewModel.cs:line 649
If anyone has had success using a .bat script to run ILMerge, merging .dll files, and using the output to register a D365 Plugin, please let me know! (extremely specific... I know haha)
Update
I've now switched to ILRepack and ILRepack.Lib.MSBuild.Task. I'm using the following config in a ILRepack.targets file:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="ILRepacker" AfterTargets="Build">
<ItemGroup>
<InputAssemblies Include="$(OutputPath)$(TargetName)$(TargetExt)" />
<!--<InputAssemblies Include="$(OutputPath)\*.dll" />-->
<InputAssemblies Include="$(OutputPath)adtplugin.dll" />
<InputAssemblies Include="$(OutputPath)Azure.Core.dll" />
<InputAssemblies Include="$(OutputPath)Azure.DigitalTwins.Core.dll"/>
<InputAssemblies Include="$(OutputPath)Azure.Identity.dll"/>
<InputAssemblies Include="$(OutputPath)Microsoft.Bcl.AsyncInterfaces.dll"/>
<InputAssemblies Include="$(OutputPath)System.Memory.dll"/>
<InputAssemblies Include="$(OutputPath)System.Threading.Tasks.Extensions.dll"/>
<!--<InputAssemblies Include="$(OutputPath)Microsoft.Identity.Client.dll"/>
<InputAssemblies Include="$(OutputPath)Microsoft.Identity.Client.Extensions.Msal.dll"/>
<InputAssemblies Include="$(OutputPath)Microsoft.Win32.Primitives.dll"/>
<InputAssemblies Include="$(OutputPath)netstandard.dll"/>-->
</ItemGroup>
<ILRepack
Parallel="true"
Internalize="false"
InternalizeExclude="@(DoNotInternalizeAssemblies)"
InputAssemblies="@(InputAssemblies)"
LibraryPath="$(OutputPath)"
Wildcards="false"
TargetKind="SameAsPrimaryAssembly"
DebugInfo="false"
KeyFile="adtplugin.snk"
OutputFile="$(OutputPath)Merged\$(AssemblyName).dll"
LogFile="$(OutputPath)Merged\ILRepack.log"
/>
</Target>
</Project>
The merge succeeds, but the plugin registration is still failing with the same error dump shown above.




Try to use Fody package to merge your .dll-s after build action. First, you need to install Fody and Costura.Fody via nuget. It will add some references and the file FodyWeavers.xml which you can fill this way
Then in the Solution Explorer select the references for the assemblies which you want to merge together and in the Properties Window change Copy Local to True. For those assemblies you don't want to include in the merge process select Copy Local - False
Rebuild your solution and in the output you'll got merged dll.