Part 1: Get Set Up
Part 2: Interop Bliss
Part 3: Unfortunate Limitations
In part 1 of this series, we setup some base code in AX, and set up Visual Studio with a project containing some AX proxy classes, ready to deploy and run code from Visual Studio. This article (part 2) builds on top of that code.
Ready for some action? I thought so! First, we'll create a pre/post event handler and hook it up to our Run() method as a managed pre handler. So, with the code from the previous article in place, let's add some code in Visual Studio. If you didn't save a solution you can re-open, you can open the Visual Studio project form the AOT. Open AX, and in the AOT go to Visual Studio Projects > C Sharp Projects and find the DAXMusings project. Right-click the project and select "edit". This will open the project in Visual Studio for you.
So, in the HandlerClass.cs file, we will add a new method to the HandlerClass. Handler methods (whether they are X++ or managed) that you will add in the AOT, have to be static and public of course, so they can be called from the event sender. We will use a "standard" pre/post handler, not a lightweight handler, so the only argument we are expecting is XppPrePostArgs. For more information on XppPrePostArgs and how to use it, also see my models and events series (specifically the second part). Just to see our code working I retrieve the calling object (getThis()) from the XppPrePostArgs class and output the type to the infolog using our Global class proxy. The interesting piece here is that getThis() returns an "object". We need to get this into our proxy. Remember the proxy is a "wrapper" around the AX object, and that means we can't CAST the getThis() returned object to our DAXMusingsEvents class, but we need to create a new proxy class and pass it the AX object. It's important you understand why and how this works, because it can lead to some confusion if you're not careful.
Let's build and deploy this to our client so we can add it as a handler in the AOT. Just press F5 in Visual Studio. In AX, open the AOT and find our DAXMusingsEvents class. Right-click on the Run() method and select "New Event Handler Subscription". Open the properties for the handler. Give it a proper name (this is important, check this post so see why, also check the models and events series), and set the "EventHandlerType" property to "Managed". For the class name, we need to include the full namespace handle to the class. So the class property will be set to "DAXMusings.ManagedHandlers.HandlerClass". As soon as you do that, the dropdown for the "Method" property will show you the "compatible" methods on the class. Just select the "PrePostHandler" method and save and compile the class.
If you now run your class, you should see the follow infolog:
If you started AX using F5 in Visual Studio, you can put a breakpoint in your C# method and run the class in AX again, to see how the debugging works.
So, this works perfectly. You could re-implement the code from the models and events articles to use a managed handler for the validateField instead of the X++ handler.
Let's try some regular interop. How about we make a C# method which we will call from X++ code? We'll create one method here which we can later re-use for other purposes. Close the AX client and go back into Visual Studio. In the HandlerClass, we will add the following method:
Hit F5 to build, deploy and start AX. Find your DAXMusingsEvents class in the AOT, and right-click > New > Method. We'll call the method "CallManagedCode". Inside, we will create an instance of an XppPrePostArgs class, set an argument "string", and call the static C# method we created previously.
Next, we'll call this method from the existing Run() method. Open up Run() and add the call to "CallManagedCode". When you now run the class, you should see the PrePost handler fire, but also our call to our method.
Let's review what this means. The "CallManagedCode" method, passes "this" (DAXMusingsEvents instance) and an instance of the XppPrePostArgs. Our C# managed method, accepts our PROXY classes for DAXMusingsEvents, and our proxy for XppPrePostArgs. As you can see from the output in the infolog, this works perfectly! You get the proxies in your C# code as expected. I encourage you to play around with this. Debug, try to create a new instance of an AX class in C# and return it from a method, etc. Interop is great. These were basically the missing features in previous versions of AX. Instances of CLR objects and/or AX objects crossing the boundaries back and forth.
As cool as this is, we'll have to end on a sad note in our part 3 of the series, showing you the unfortunate limitations of these new features.