最新消息:ww12345678 的部落格重装上线,希望大家继续支持。

AX2012 DLL Deployment and how AX binds DLL’s at runtime

网络文摘 Kenny Saelen 4172浏览

In an earlier post I have showed how to use custom WPF controls with Dynamics AX 2012.

Well when using this type of fuctionality, you will often come into troubles when using it on other environments because of the corresponding DLL files not being deployed on their machine.

For that, you can use the automatic deployment feature of Ax when adding projects to the AOT. But it seems that there is an issue there. You can find lots of information on this post made by Joris De Gruyter:

http://daxmusings.codecrib.com/2011/09/ax-2012-net-assembly-deployment.html

And there is also information to be found here:

http://msdn.microsoft.com/en-us/library/gg889192.aspx

In short you have the following options to deploy your DLL files:

  • Automatic deployment feature of Visual Studio projects in the AOT
  • Using the SysFileDeployer framework (Joris mentioned an upcoming blog post on that, so that will be cool and interesting)
  • Copy them into the clientbin or serverbin directories
  • Use the Global Assembly Cache to store assemblies

While opinions might be devided, I tend to like the latter option. Simply because the GAC is intended to manage just that. Some of the benefits of the GAC:

  • No polution of the file system with DLL files being copied all around
  • Assemblies must be signed and the PublicKeyToken can be used to differentiate versions of your DLL file (Think of disallowing an assembly in production if it is coming from development environments and the wrong version was accidentally added)
  • Due to the previous fact, you can also have multiple versions of the same DLL file in your GAC.
  • GAC Redirection can be used to point to the right assembly if older versions are searched for

So let’s look back at the initial problem that I was facing. Dynamics AX threw up an error mentioning that it could not find the DLL file it needed. Strange because it was present in the GAC so it should always find it there.

DLLLoadingError

Well to find out why it did not find the DLL I needed to check where AX was actually looking for it. And to do that you can use a .net framework tool called fuslogvw. This little helpfull tool is actually an assembly binding logging tool. So if DLL files are searched for, this little guy will give you the details about it.

So open up a Visual Studio Command Prompt and type fuslogvw and hit enter. First we need to do some minor setup to get it to log stuff. So go to Settings and select the log all binds to disk option. Then select a path to store the log in and you’re set to go.



Fuslogvw Settings

At that point I fired up the AX client and openened up the form that uses the assembly that appeared to be missing. Fuslogvw caught the information that is displayed below.

Fuslogvw_CapturedRecord

*** Assembly Binder Log Entry  (17/05/2013 @ 9:03:38) ***
 
The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.
 
Assembly manager loaded from:  C:WindowsMicrosoft.NETFrameworkv4.0.30319clr.dll
Running under executable  C:Program Files (x86)Microsoft Dynamics AX60ClientBinAx32.exe
--- A detailed error log follows.
 
=== Pre-bind state information ===
LOG: User = EPSlab rd wdpan59
LOG: DisplayName = EPS.AX.WPFControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
(Fully-specified)
LOG: Appbase = file:///C:/Program Files (x86)/Microsoft Dynamics AX/60/Client/Bin/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = Ax32.exe
Calling assembly : Microsoft.Dynamics.AX.ManagedInterop, Version=6.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:Program Files (x86)Microsoft Dynamics AX60ClientBinAx32.exe.Config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:WindowsMicrosoft.NETFrameworkv4.0.30319configmachine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: The same bind was seen before, and was failed with hr = 0x80070002.
ERR: Unrecoverable error occurred during pre-download check (hr = 0x80070002).

The following line shows what was wrong at the time:

LOG: DisplayName = EPS.AX.WPFControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

The assembly manager was actually looking for an unsigned version of my assembly in the GAC. But that version is not present since I had signed my assembly… So why was it looking for an unsigned version?? Well the answer to this was the order in which I had done things.

For development purposes I did the following in the exact same order:

  • Created a Visual Studio solution and added it to the AOT.
  • Added a ManagedHost control on a form and pointed out to the assembly that I needed. (Filedbased) (Until now an unsigned one)
  • When all was working well, I tried to do stuff the BP way and I signed my assembly and put it into the GAC.

As that seems to be enough, environments other than my machine were still facing the issue even though the DLL was in the GAC there too. And when looking at the ManagedHost control in AX, I saw that it was keeping a reference to the unsigned assembly and not the signed one!! So to solve it, you need to do things in a different order:

  • Create the Visual studio project.
  • Sign and build your assembly.
  • Add the solution to the AOT.
  • Add the ManagedHost control now and point to the assembly (that is now in your GAC already)

When this is done, you will have a reference in AX that also takes the right PublicKeyToken in to account and it works on the other machines also. To be sure I checked with fuslogvw again and this is the output.

 

*** Assembly Binder Log Entry  (17/05/2013 @ 9:03:42) ***
 
The operation was successful.
Bind result: hr = 0x0. The operation completed successfully.
 
Assembly manager loaded from:  C:WindowsMicrosoft.NETFrameworkv4.0.30319clr.dll
Running under executable  C:Program Files (x86)Microsoft Dynamics AX60ClientBinAx32.exe
--- A detailed error log follows.
 
=== Pre-bind state information ===
LOG: User = EPSlab rd wdpan59
LOG: DisplayName = EPS.AX.WPFControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=14cd416a5dbff93c, processorArchitecture=x86
(Fully-specified)
LOG: Appbase = file:///C:/Program Files (x86)/Microsoft Dynamics AX/60/Client/Bin/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = Ax32.exe
Calling assembly : Microsoft.Dynamics.AX.ManagedInterop, Version=6.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:Program Files (x86)Microsoft Dynamics AX60ClientBinAx32.exe.Config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:WindowsMicrosoft.NETFrameworkv4.0.30319configmachine.config.
LOG: Post-policy reference: EPS.AX.WPFControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=14cd416a5dbff93c, processorArchitecture=x86
LOG: Found assembly by looking in the GAC.
LOG: Binding succeeds. Returns assembly from C:WindowsMicrosoft.NetassemblyGAC_32EPS.AX.WPFControlsv4.0_1.0.0.0__14cd416a5dbff93cEPS.AX.WPFControls.dll.
LOG: Assembly is loaded in default load context.

And as we can see, Dynamics AX now takes the key into account and mentioned that it indeed found the assembly in the GAC.

LOG: DisplayName = EPS.AX.WPFControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=14cd416a5dbff93c, processorArchitecture=x86

So for now, we have stored all the assemblies that we use in a folder on the network and every time there is a new version available, simple scripts are run to update the GAC and that is the only thing that needs to be done.