Sunday, August 21, 2011

X++ in the .NET CLR : Life in the Fast Lane

You've heard it by now. In Dynamics AX 2012, X++ can actually run in the .NET CLR. What I've often heard is "X++ running on the server runs in IL". Well, that is somewhat true. But it doesn't mean setting your classes or menu items to "RunOn Server" makes them run in IL.

So what runs in the CLR "automatically"? Well, any execution path that starts on the server definitely does. First of all that means, batch jobs... the server runs and executes batch jobs, so they all run as IL. All services run as IL, since they start on the server tier as well. Any "Business Operation" (the RunBase replacement called "Business Operation Framework" - more on that here) also runs as CLR (since it uses the service framework as a back end) So does that mean code started from the AX client never runs as IL, even when it's set to run on the server tier? Well, not automagically. But, we can do some manual magic!

Consider the following method (created on a class I called "XppIL"), it checks whether it is running in the CLR or as interpreted X++ script and outputs an infolog to show you the result. Note it accepts and returns a container. Anyone who has worked with the runAs() method in AX 2009 will recognize this pattern.



Notice the method does not require the "server" modifier (although adding won't harm anything of course). It is just public static, taking basically the default "called from". You can make this "server", but as said earlier, that won't make it run as IL necessarily.

Ok, so how do we now run this as IL? Calling this now from any method (server or client) will just return the "X++ interpreted" message... Well, let's create a Main method on our "XppIL" class and call the static method. But not the old fashioned way, we use the new function "runClassMethodIL". We don't care about passing in or getting out containers, so we'll just pass in a conNull() since we need to pass in something.



To call the runClassMethodIL function, we need to assert the XppILExecutePermission. Also as a good practice we don't use strings for the method and class name, but use the classStr() and staticMethodStr() precompiler functions.
For good measure, we'll also call the method directly, from our Main method that we make run on server, just to make the point "server" doesn't mean IL necessarily.

The last thing we need to do is make sure we generate the CIL so our X++ can run in the CLR. No need to compile the whole thing again, you can do an "incremental" compile to just compile any changes you made since the last CIL generation.



We can run our class now. And, as expected, our output looks as follows.



Great! That worked. But, how do we now debug this? Well, in Visual Studio of course! Few things on that. First, remember the IL runs on the server, so you need to debug the server process. Secondly, to debug the server service, your Visual Studio should run as administrator (elevated permissions). If it's not running as such, Visual Studio will ask you. Let me show you. Open Visual Studio. On the debug menu, select "Attach To Process".




From the list, select the Ax32Serv.exe. If it's not on the list, make sure you have the "Show processes from all users" and "Show processes in all sessions" checked. Click the "Attach" button. If your Visual Studio is not running as administrator, you will get the prompt as shown below. Once you say "restart under different credentials", and then click "yes" to allow it, Visual Studio will restart in administrator mode. You will have to repeat the above steps to attach the debugger to the Ax32Serv.exe.




Next, open the application explorer (View / Application Explorer). Navigate to Classes and find the XppIL class. Expand the class and double-click on the "RunCode" method. This will open the X++ code in Visual Studio (not editable, and unfortunately no code highlighting either).




Let's put a breakpoint in this code. Put your cursor on the line with "if (xSession" and press F9 to put a breakpoint on that line. This will put either a red dot or a red circle in front of the line. If you get a red circle (shown below), you will notice it has a little warning icon in there as well. If you hover your mouse over it, you will notice the tooltip says "The breakpoint will not currently be hit. No symbols have been loaded for this document". Basically this happens because AX will not load all assemblies at startup, but rather load them on first use. So, as soon as we start our class, you will see Visual Studio's output window (if you have that open) show assembly loading, including the IL compiled X++. So, get back into AX and run the class again...



Windows should switch you to Visual Studio and you will see the breakpoint hit there. Notice you have full capability here, including stack trace, variable watches, etc. One MAJOR point to remember here is that Visual Studio supports conditional breakpoints. That's right. The feature you've been waiting for.

As far as debugging in Visual Studio goes, the shortcuts are the same as in AX. F5 is continue, F10 is Step Over, F11, is Step Into, etc.


Have fun in the fast lane!

9 comments:

  1. Thanks for this great post.
    I wonder, can we debug if we open VS in client computer, not in AOS?

    Thanks.

    ReplyDelete
    Replies
    1. VS has this feature called "remote debugging". I guess technically it could work to debug the AOS from a client PC somewhere else, but I haven't tried that so I can't confirm.
      However, remember that debugging the AOS is holding up any other processing going on on that server, so you're holding up users. If you have a remote server that probably implies it's a central server that other people use as well, so be aware of that.

      If you do try the remote debugging, please post back here and let us all know if that worked!

      Delete
    2. Hi Joris

      I have tested the Remote Debugging option, and it works as expected. You need to have the RPC listener running on the AOS though, and obviously any firewall needs to allow the traffic. The tool is normally installed at C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\Remote Debugger\x64\msvsmon.exe and all you need to do is run it and keep it running. From the remote machine, you will have to attach a remote process and chose host name.

      Delete
    3. Awesome Tommy, thanks for letting us know!

      Delete
  2. Hi Joris,
    I have a basic question. What exactly happens when x++ code is run/generated in CIL?.

    ReplyDelete
    Replies
    1. There's a lot of ways I could answer that question, depending on what you mean exactly. Check out this really old video which actually predates AX2012 but it's essentially what ended up in AX 2012: https://channel9.msdn.com/Blogs/Charles/Peter-Villadsen-and-Gustavo-Plancarte-Inside-Ax-Translator-X-to-MSIL

      Delete
    2. Hi Joris and all,

      In Dynamics AX 2012-CU8, we have the standard functionality to import the Credit card Transaction using Csv/TXT file (Path- Travel and expense- Periodic- Credit Card- Credit card import from folder).

      I am trying to import the file and file is importing successfully. But to modify some of the fields information, I need to modify the DLL for the same. I mean we have .NET business assembly which transform csv to XML and it is a DLL file. We are using NETTcp adaptor.

      I have deployed the DLL on client using visual studio (by setting deployment target as client) and the debugger is not hitting in .NET code during this process and I have attached the AX 32.exe(not AX32Serv.exe as I am not able to see AX32Serv.exe in my process) to the process as well.

      My Visual studio version is 2013. And my Visual studio and AOS are on two different machine. I have windows 8 at my client machine and Windows server 2012 on sever machine.

      Could you or anyone please suggest me If I am missing anything ?

      Delete
    3. Hi Anuj,

      that's a very specific scenario and not entirely related to this article. I suggest you post this question on the community forums at community.dynamics.com under the AX forums. Lots of experts there and we can look at your issue there.

      Delete
    4. I had the same problem and I didn't know what I should click to solve this problem. Thanks to you, I fixed it. Thank you! Cheers!

      Delete