Custom DotNet and Open XML SDK


Using the .Net API, I hat developped a custom DotNet module that was creating a MsWord DOCX using the Microsoft.Interop.Word for a complex Merge situation, creating a documents with many tables.
It was working for small documents, but it was very slow (3 min for a 3 page document).
Timed out after 20 minutes for an expected 19 pages document.

I read many posts on-line recommending to use the "Open XML SDK" which is supposed to be much much faster (6 seconds instead of 5 minutes for example), but I can't get it to work with SageCRM .Net API.

1) I used NuGet in VS 2019 to install the latest version of "Open XML SDK" (DocumentFormat.OpenXml - v2.18 dated 2022-09-06)
2) Added the reference to my project and set "Copy Local = True"
3) Created a simple Test project
4) When deploying, the files DocumentFormat.OpenXml.dll and DocumentFormat.OpenXml.xml are copied to the "Sage\CRM\CRM\CustomDotNet" folder with the Project's DLL

I get this error at runtime:

Sep 12 2022 16:52:58.063 6884 1148 1 Class: CRMWrapper - Method: ExecWebPageNew - Error: An error has occurred on PreBuildContents or BuildContents
Sep 12 2022 16:52:58.070 6884 1148 5 StackTrace:
Error: Could not load file or assembly 'DocumentFormat.OpenXml, Version=, Culture=neutral, PublicKeyToken=8fb06cb64d019a17' or one of its dependencies. The system cannot find the file specified.
at ExportDOCX.MyCustomPage.BuildContents()
at Sage.CRM.Wrapper.CRMWrapper.ExecWebPageNew(String fullFileName, String callFunction)

There is no dependencies for OpenXml with .Net 3.5.

I lost a lot of time this week trying to make this work.
Has anyone been able to work with the "Open XML SDK" in SageCRM ?


  • You're getting the problem because your custom CRM .Net assembly is only able to resolve dependencies from the GAC. This is why you didn't have an issue using the Word Interop - because that's installed into the GAC. Dependencies that you add via NuGet won't be GAC-ed.

    This is all because your custom CRM pages are loaded into their own AppDomain. It's possible to set the ApplicationPath and PrivateBinPath when creating a new ApDomain, and this tells the runtime where to start probing for private assemblies, but unfortunately these don't get set by SageCRMBase - so it'll only probe the GAC.

    Fortunately there's a simple fix : you just create a type initialiser for your AppFactory class and add a handler for the AppDomain's AssemblyResolve event in there. Then in the handler you can tell the runtime exactly where to find the dependency. There's a variety of ways to implement this, but see the post here for one way to do it :

  • Thank you very much for the Hint !

    All I had to do is run the following command to put the OpenXML SDK 2.18 in the GAC and it's working.
    "c:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\gacutil.exe" /I DocumentFormat.OpenXml.dll

    I'll read more in detail the link you sent me, but your explanation was very clear to help me understand.