Recently, with a Microsoft Dynamics NAV 2015 implementation, we ran into an issue in which some functionality that had been created for Secure FTP integration did not work in the same fashion as it did in a prior version of Dynamics NAV (2009 R2) specifically. When trying to compile the codeunit that managed the DotNET variables after upgrading the content that had been created, we received the following error:
After validating that the add-in was installed in the proper “Add-in” folders of the Service Tier and RoleTailored Client, we went ahead and tried to browse to the DotNET variable and were able to successfully see it, but none of the methods or types were exposed:
Mixed Mode Architecture – 32-bit Client/Development Environment, 64-bit Service Tier
When we originally developed this application, it was for a customer that was running Dynamics NAV 2009 R2. We saw an advantage into designing the components so that we could easily transport them to the newer versions of NAV as the older Automation/ActiveX type variables were something that long-term were going to be a deprecated feature. Sadly, at the older version, this type of issue did not occur and all of the methods were exposed when adding the .NET DLL file into the Add-ins folder:
What we then determined, which is key to why this failed in the newer version of NAV but not in the older versions, revolves around how you compile and leverage your .NET assemblies. When looking at the .NET component, we noticed that it was compiled specifically for a processor architecture and was not compiled into MSIL (which is architecture independent):
The developer of these components created the DLL file with a native implementation – meaning that the outer-layer of the DLL is considered managed code, but the internal aspect of it is still native code that is processor dependent. Because we live in a mixed mode environment (even at NAV 2016 – the development environment is still 32-bit), the methods and classes exposed do not allow us to add this assembly for use – even the 32-bit implementation does not expose due to possible serialization restrictions on either the assembly, or potentially a requirement of the actual NAV .NET bridge components as well.
How Did We Fix It?
Our first instinct was to go down the path of leveraging ActiveX again – we weren’t excited at this prospect, so we contacted the developer of the solution that originally created the assemblies to see if MSIL was an option. The developer pointed us to the direction of a different implementation of the assemblies and we went ahead and created a new assembly that was MSIL equivalent (using Visual Studio 2015):
The methodology used in this new implementation of the product uses pure C# and leverages call-outs using P/Invoke to accomplish this task:
For the RTC Add-in Folder, we copied the newly created “pure C#” DLL into the add-ins along with the x86 native DLL that is called with P/Invoke – for the Service Tier, we copied the x64 native DLL (and our pure C# DLL) into it – that way, if we decided to run the component on the client, or on the server, it would invoke the correct architecture when processing:
After successfully installing the component and doing a couple of updates on the DotNET assembly, our functionality worked as expected:
Conclusion on Assemblies
The big lesson learned throughout this process is that you must be cognizant of the architectural differences between a 32-bit and 64-bit environment, as they have an effect on the results of how the DotNET variables work within Dynamics NAV. Moving forward, we will be ensuring that any .NET assembly that we use will require to be MSIL compatible, and if not compatible, that a MSIL shell is created with P/Invoke being used to call specific application functionality as needed within the assembly.
I wanted to give special thanks to Matt Fausey to Chilkat Software, Inc. for pointing us in the right direction with this issue – while Matt has never used Dynamics NAV, he was able to point us to the Chilkat Mono offering which offered a pure C# implementation of the product (basically a wrapper) that then called the necessary native methods through P/Invoke – thanks again Matt for your help!