
{"id":233,"date":"2017-05-08T14:17:33","date_gmt":"2017-05-08T04:17:33","guid":{"rendered":"http:\/\/bakke.online\/?p=233"},"modified":"2017-05-08T14:17:33","modified_gmt":"2017-05-08T04:17:33","slug":"debugging-net-code-called-from-dynamics-ax","status":"publish","type":"post","link":"https:\/\/www.bakke.online\/index.php\/2017\/05\/08\/debugging-net-code-called-from-dynamics-ax\/","title":{"rendered":"Debugging .NET code called from Dynamics AX"},"content":{"rendered":"<p>From time to time it is necessary to develop custom .NET code and call it from Dynamics AX. \u00a0It could be that you already have the .NET code to do what you need and you don&#8217;t want to reimplement the functionality in X++, or what you are trying to do is too complex to do directly in X++.<\/p>\n<p>One common situation is where you need to write a wrapper around some operating system functionality or web service to expose a much simpler API to AX.<\/p>\n<p>This works, and AX has good support for calling .NET code through it&#8217;s .NET interop framework. \u00a0One thing that does become more complicated is debugging, but once you know how it is actually quite easy.<\/p>\n<p><!--more--><\/p>\n<h3>Visual Studio<\/h3>\n<p>In order to debug .NET assemblies you will need Visual Studio. \u00a0If you&#8217;re developing your own .NET code you will most likely have this already, and the steps shown here should work with any recent version of Visual Studio. \u00a0I have done this with Visual Studio 2013 as well as 2015.<\/p>\n<h3>Compiling your assembly<\/h3>\n<p>First of all, make sure you have selected a debug configuration and then compile your .NET assembly. \u00a0In your output folder you&#8217;ll find your DLL along with a PDB file. \u00a0These need to be copied to your Dynamics AX client bin folder, typically located at C:\\Program Files (x86)\\Microsoft Dynamics AX\\60\\Client\\bin.<\/p>\n<p>If your project has references to other assemblies, such as Microsoft Exchange Managed API or similar, the required DLLs and their PDB files will also be copied to your output folder. \u00a0In that case, make sure that these are also copied to the AX client folder.<a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/project-output-folder.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-234\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/project-output-folder.png\" alt=\"\" width=\"722\" height=\"275\" \/><\/a><\/p>\n<p>For a redeployment, you would only need to copy the assembly DLL and PDB for what has actually changed. \u00a0The referenced Exchange API assemblies would not have changed and can be ignored when you redeploy.<\/p>\n<h3>Adding reference and code to the AX side<\/h3>\n<p>In the AX development environment, add a reference to your assembly. \u00a0Right-click on the References node in the AOT and select Add reference:<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/addreference.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-235\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/addreference.png\" alt=\"\" width=\"225\" height=\"201\" \/><\/a><\/p>\n<p>Click Browse and select the DLL you copied to the AX client folder. \u00a0The assembly will show up in the bottom half of the form:<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/addreference_2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-236\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/addreference_2.png\" alt=\"\" width=\"753\" height=\"621\" \/><\/a><\/p>\n<p>It is not necessary to add references to assemblies referenced by your DLL. \u00a0The .NET runtime will take care of that automatically, the DLLs just need to be in the same folder or in the Global Assembly Cache.<\/p>\n<p>After you have done this, restart the AX client.<\/p>\n<h3>Write some code to test the assembly<\/h3>\n<p>For this demonstration, let&#8217;s just write a small job to test the assembly:<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>static void<\/strong><\/span> EB_TestDebugger( Args _args )\n{\n  AXExchangeInterface.ExchangeInterface exchangeInterface;\n  <span style=\"color: #0000ff;\"><strong>int<\/strong>      <\/span>numEmails;\n  <span style=\"color: #0000ff;\"><strong>int<\/strong>      <\/span>i;\n  Filename tempFileName;\n\n<span style=\"color: #0000ff;\"><strong>  new<\/strong> <\/span>InteropPermission( InteropKind::ClrInterop ).assert();\n\n  tempFileName      = WinAPI::getTempFilename( WinAPI::getTempPath(), <span style=\"color: #993300;\">\"AXPDF\"<\/span> );\n  exchangeInterface = <span style=\"color: #0000ff;\"><strong>new<\/strong> <\/span>AXExchangeInterface.ExchangeInterface( <span style=\"color: #993300;\">\"user@domain.com\"<\/span> );\n  numEmails         = exchangeInterface.LoadEmails();\n\n<span style=\"color: #0000ff;\"><strong>  for<\/strong><\/span>( i = <span style=\"color: #ff0000;\"><strong>0<\/strong><\/span>; i &lt; numEmails; i++ ) <span style=\"color: #008000;\"><em>\/\/ .NET is using zero-based indexes<\/em><\/span>\n  {\n    <span style=\"color: #0000ff;\"><strong>if<\/strong><\/span>( exchangeInterface.GetAttachment( i, tempFileName ) )\n    {\n      <span style=\"color: #008000;\"><em>\/\/ Do something with it<\/em><\/span>\n    }\n  }\n\n  CodeAccessPermission::revertAssert();\n}<\/pre>\n<p>Nothing fancy, just asserting CLR interop permissions, creating a CLR object from a class in the assembly, then call some simple API methods on it before we revert the CLR interop permissions assert.<\/p>\n<p>Don&#8217;t run the job just yet. \u00a0We&#8217;ll need to attach the debugger first, but before that I strongly recommend to run with only a single instance of the Dynamics AX client. \u00a0It will make it a lot easier to select the correct process to attach to.<\/p>\n<h3>Attaching the debugger<\/h3>\n<p>Back in Visual Studio, with your .NET assembly project open, set breakpoints in your code where you want them. \u00a0When you are ready to start debugging, click DEBUG and then Attach to Process.<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/attach.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-237\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/attach.png\" alt=\"\" width=\"656\" height=\"422\" \/><\/a><\/p>\n<p>In the form that opens, find and select your Dynamics AX client process. \u00a0It will be named ax32.exe.<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/attach_2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-238\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/attach_2.png\" alt=\"\" width=\"870\" height=\"586\" \/><\/a><\/p>\n<p>It is possible to use Visual Studio to debug processes running on remote computers as well, but let&#8217;s leave that complexity out of it for now. \u00a0Click the Attach button to attach to the AX client process.<\/p>\n<p>Have a look at the breakpoints you set earlier. \u00a0If they appear red like normal, everything is good and debugging should work well. \u00a0If they are just a red ring with white center, the AX client has not loaded symbols for your assembly which usually means that the assembly has not been loaded, either.<\/p>\n<p>Good:\u00a0<a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/breakpoint_good.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-240\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/breakpoint_good.png\" alt=\"\" width=\"480\" height=\"84\" \/><\/a>Bad:<a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/breakpoint_bad.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-239\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/breakpoint_bad.png\" alt=\"\" width=\"468\" height=\"83\" \/><\/a><\/p>\n<p>If the breakpoints are not lighting up correctly, detach from the AX client process again (Don&#8217;t choose terminate&#8230;) and start troubleshooting what could be wrong:<\/p>\n<ul>\n<li>The assembly has not been built with debug information. \u00a0Are you sure the assembly was built in a debug configuration?<\/li>\n<li>There is another version of the same assembly in the GAC. \u00a0Use GACUtil.exe to search for and remove the assembly if present.<\/li>\n<li>The current version of the DLL and PDB (you&#8217;ll need both&#8230;) files have not been copied to the AX client folder, so AX loaded up an old version.<\/li>\n<li>Did you connect to the correct ax32.exe process? \u00a0On some development workstations there could be several of them. \u00a0I recommend running with only a single client process when debugging with Visual Studio.<\/li>\n<\/ul>\n<p>Anyway, assuming that the breakpoints did indeed light up as they should, you can now run the job from earlier.<\/p>\n<p>When execution hits your breakpoint, the Visual Studio debugger will fire up and you can debug and inspect as with any other .NET program. \u00a0You can even use the AX debugger to step through your code, and it will seamlessly hand over to Visual Studio whenever a breakpoint is hit within the .NET code.<\/p>\n<p>Sweet as&#8230;<\/p>\n<h3>Changing the assembly and trying again<\/h3>\n<p>If you need to make changes to the .NET code and try again, you&#8217;ll need to stop the AX client before you copy the new version of the DLL and PDB file across to the client bin folder. \u00a0Otherwise AX will keep the assembly loaded, both preventing you from overwriting the existing files, and from loading a new version.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>From time to time it is necessary to develop custom .NET code and call it from Dynamics AX. \u00a0It could be that you already have the .NET code to do what you need and you don&#8217;t want to reimplement the functionality in X++, or what you are trying to do is too complex to do &hellip; <a href=\"https:\/\/www.bakke.online\/index.php\/2017\/05\/08\/debugging-net-code-called-from-dynamics-ax\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Debugging .NET code called from Dynamics AX&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19,4,1],"tags":[20,2,21,3],"class_list":["post-233","post","type-post","status-publish","format-standard","hentry","category-net","category-dynamics-ax","category-uncategorized","tag-net","tag-ax2012r3","tag-debugging","tag-dynamics-ax"],"_links":{"self":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts\/233","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/comments?post=233"}],"version-history":[{"count":0,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts\/233\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/media?parent=233"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/categories?post=233"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/tags?post=233"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}