Blog
base 36 conversion
I haven't posted any code on here in a while, so here's a quick little bit of code that might be useful to somebody.
I had to write some code this week to do conversion from base 10 to base 36 and back, in Dynamics AX. So I just took some simple code from the Wikipedia article on
base 36 and converted it to X++. Nothing fancy. The code below is all in a job, but I'll probably put it in a utility class when I actually implement it.
Labels: Dynamics AX, programming
checking user roles in AX 2012
It's been a while since I've posted anything related to Dynamics AX / X++, so I thought I'd write up something I stumbled across recently. I had created a custom form, with a number of buttons on it. Two of the buttons needed to be available only to users in a certain role.
Well, first, I should point out that this can be done without any code. See
here and
here for information on that. And there are good reasons to do it this way, in many cases.
But there are also some good reasons to do this in code. It allows you to document what you're doing and why, and it gives you more flexibility than just doing it through properties in the AOT. In my case, the business rules around this didn't really fit into the permissions available in the AOT (Read, Update, Create, and Delete), so while I could have picked one of those and used it, it wouldn't have accurately reflected the actual use case.
So I first wanted to find a method in X++ that would tell me if a given user was in a given role. I'm familiar with
Roles.IsUserInRole from the .NET Framework, and have used it frequently in the context of ASP.NET sites using custom membership providers. So I looked for something similar in AX. That led me to the
SysUserManagement Class.
I wound up writing a utility method that made use of this class:
It worked fine on my VM, when logged in under my own ID. But, after deployment, it quickly became apparent that X++ code running under a normal user account (without SYSADMIN rights) can't call methods in the SysUserManagement class. Now, there's nothing I can see in the documentation that indicates that, but I should of course have tested my code under a normal user account.
So I rewrote my code to access the appropriate role-related tables directly, and it turns out that a normal user can do that, no problem:
So I guess the lesson here is to always test your code under a normal user account, and not to assume that the MSDN page for a given AX class will tell you everything you need to know about that class.
And, as with a lot of stuff in AX, I have a feeling that I'm *still* doing this "the wrong way", even though my code works and is fairly simple. I'm guessing that, a year from now, I'll have figured out that there's a better way to do this.
Labels: Dynamics AX, programming
silliness in the Dynamics AX compare tool
I had a small issue crop up in AX a couple of weeks ago. It wasn't big enough to spend any time on, but it was a bit of an annoyance. Well, I had some spare time yesterday, so I decided to see if I could fix it. The end result was that I did indeed fix it, but the journey to that point was kind of ridiculous, so I thought I'd write it up.
AX has a built-in compare tool, for comparing different versions of code in different AX layers, or in source control. It's not a terribly great tool, and I'd rather have
WinMerge or
Beyond Compare, but it's good enough. The initial form shows the names of the two files being compared, with a red box next to one, and a blue box next to the other, to indicate the colors that will be used to highlight the differences between the two files.
Well, the color in those little boxes mysteriously disappeared a couple of weeks ago. The tool still works, and the text is highlighted in red & blue, but there's no visual indication of which text is from which file. Not a really big deal, but inconvenient.
Most of the tools built into AX are written in X++, and we have full source, so I went ahead and dug up the source for the form named "SysCompareForm." I don't think I should post any of the source here, but what I found is that those little red and blue boxes were actually HTML controls, each one displaying a web page, constructed in the code! I'd never really noticed before, but the boxes were not actually displaying solid red & blue, but rather were displaying red-to-white and blue-to-white gradients. And, of course, this being Microsoft, they were doing so in
a way that only worked in older versions of IE. And, yeah, I'd recently upgraded IE on my VM from 8 to 10. So that was the problem: each of those little squares was actually rendering a web page with IE, just to get little red & blue swatches!
The
cross-browser gradient situation has been
a bit of a mess for a long time now, and you generally need to add about 10 lines to your CSS file just to do one gradient that works well across all browsers. So, I tried to update the code so it would render out OK in IE 10. Well, I messed around for a while, and couldn't quite get it right. Then, I did some searching, and found
this thread from a Russian web site, from someone else who had the problem and solved it. So, I just copied his code and went on with my life.
Apparently, this problem was fixed by Microsoft in a recent CU, but I guess it's one that we haven't applied yet. I wonder how much other stuff in AX is being done like this, and relying on HTML/CSS that only works in IE 8. Geez.
Labels: Dynamics AX, programming
Exporting Projects From AX
After my
hard drive crash, I decided that I should really create a way to automatically back up all the code for all of my active projects. So, I've added an "exportAllProjects()" method to my "
startup projects" class. This method takes my list of active projects, iterates through them, and exports each one to a standard XPO file, in a sub-folder off the My Documents folder.
I've created a menu item for this, and attached it to the Development Tools menu. So, now I can backup all the objects in all of my active projects in one fell swoop. It would nice to be able to do this automatically, but I'm not sure I want to start messing around with doing this as a scheduled job just yet.
Labels: Dynamics AX
Remapping Keystrokes in MorphX
The shortcut keys used in MorphX, the Dynamics AX code editor, are
almost exactly the same as those used in Visual Studio. In fact, the
code editor basically *is* the editor from Visual Studio, with somewhat
reduced functionality, if I understand correctly.
The one thing that's always bugged me about it is
that the keystrokes for commenting and un-commenting code are different.
In VS (and various other editors), it's Ctrl-K,Ctrl-C and Ctrl-K,Ctrl-U.
For no obvious reason, MorphX uses Ctrl-E,Ctrl-C and Ctrl-E,Ctrl-U.
This isn't *too* bad, until you start getting used to it, then you
accidentally press Ctrl-E in SQL Management Studio, hence executing a
block of SQL instead of commenting it out. After doing *that* a few
times, I decided that I needed to fix MorphX.
Surprisingly,
I couldn't find any facility built into AX for changing keyboard
shortcuts. So, I turned to
AutoHotKey. It's very easy to remap a single
keystroke in AHK. For instance, I can just remap Ctrl-K to Ctrl-E with
"^k::^e". I went ahead and did that for awhile, since it didn't really
seem that there would be any harm in that. But, I wanted to figure out
how to create a more targeted replacement, so only the two specific
commands would get remapped.
The snippet below does that. And, or course, it limits the remapping to the AX code editor.
Labels: AutoHotKey, Dynamics AX
Opening multiple projects in MorphX at startup
The MorphX IDE used for X++ programming in Microsoft Dynamics AX is a fairly decent environment to work in, but it definitely has some shortcomings and oddities. There is a "project" abstraction in MorphX that allows you to create a named group of objects that all relate to a project that you are working on. There's little meaning to these projects, other than that. You can export all objects in a project into a single XPO file, but other than that, it's basically just a structure to help a programmer keep track of a list of related objects. You can set a single project as your "startup project", and that project will then open automatically when you open MorphX. Since I'm usually juggling three or four projects at a time, I've been thinking that it would be great if you could open a group of projects instead of just one, so I decided to try and write some code to do that.
My realization that this could be done at all mostly came from the book
Microsoft Dynamics AX 2012 Development Cookbook. The section "Modifying the right-click context menu" described a method of setting and clearing the startup project via the context menu. Prior to reading this, I hadn't realized that the development environment was as customizable as the AX front-end, though (in retrospect) it makes sense that it is.
The project I'm going to describe here will do a few, fairly simple, things:
- Maintain a list of startup projects in a simple text file, stored in my personal "documents" folder.
- Allow for adding and removing projects from this list, via the right-click context menu.
- Allow the user to open all projects in this list, via the MorphX "tools" menu.
I could probably do a lot more with this project, such as actually open the projects at MorphX startup, rather than just through the tools menu, but I'm fine with the project as-is, for now.
This blog post is going to walk through the steps necessary to implement this functionality.
First, we're going to create a class called "AjhDevLaunchStartupProjects". Our class will have methods to add and remove projects from the list, and to open all projects specified in the list. We're not going to go overboard with efficiency here. I'm assuming that the list will, at most, have three or four projects on it at a time, so we're simply going to read them from disk into an array, and write them back out from the array.
We'll start with an entirely standard static constructor:
Now let's add a method to get the file name for the text file:
Now, methods to read and write the file:
And the methods to add and remove projects from the list:
And here's the code to open all active projects:
(I probably found the code to open a project from
this blog post, or a similar one.)
Now, we will create three new "action" menu items:
- AjhDevStartupProjectAdd – to add a project.
- AjhDevStartupProjectRemove – to remove a project.
- AjhDevStartupProjectOpenAll – to open all projects.
Each one will have ObjectType set to "Class", and the object will be our class, "AjhDevLaunchStartupProjects". The static main() method in our class will use args.menuItemName() to determine which action to take.
Here's that method:
The add and remove menu items will be added to the SysContextMenu. This way, they will show in the context menu under "add-ins". The "open all" menu item will be added to the DevelopmentTools menu. This way, it will show under the "Tools" menu in MorphX.
We will also change the "verifyItem" method of the "SysContextMenu" class, so that the add & remove items will only show in the context menu for projects (and not for other objects). (If we wanted to go further with this, we would also add logic here to show only one or the other option, depending on whether or not the project is already on the startup list.)
Here's the code that we will add to "verifyItem", at the end of the large case statement there:
So I think this is all pretty straightforward. My purpose in writing this up in such detail was largely so that I could get a bit of X++ code on this blog, and in the hope that someone else might find this useful. I haven't had the chance to write much general-purpose code at my current job, so there isn't much I'm working on that would be appropriate to post here, but this seemed like a worthwhile little project to work on, and hopefully it may prove helpful to someone else.
Labels: Dynamics AX, programming
My new job, Dynamics AX, and X++
I started a new job (at
SHI) back in January. I've been wanting to post something about it for awhile now, but I've been pretty busy. Also, I kind of wanted to keep quiet about it for a bit, just in case it didn't work out. Well, I've been there for about two months now, and it seems to be going well.
Right now, I'm doing development for our
Dynamics AX system, using AX's proprietary programming language,
X++. It's a reasonably decent and relatively modern language, very similar to C# and/or Java. I do miss the more chaotic environment I'd previously been working in, where I was using a mix of ASP.NET / C#, JavaScript, and PHP / Drupal, depending on the project. SHI does have a fairly mixed environment, but there are enough programmers working here that they're not likely to need me on anything other than X++ any time soon, so I guess I'll have to get used to a bit less variety than I've had in the past.
The development environment built into AX is called MorphX. (This is also the name of a
mediocre XBox 360 game, which kind of skews Google results for MorphX, but that's OK.) Microsoft has obviously made some effort to add some nice features to MorphX since they acquired AX, but it's not quite up to the standard set by Visual Studio. They've also tried to standardize some of the keyboard shortcuts between VS and MorphX, but there are still a few annoying inconsistencies there.
I recently found a project on CodePlex called "
Microsoft Dynamics AX 2012 X++ Editor Extensions", which adds a few missing features to the X++ code editor. I tend to worry about add-ins like this slowing things down or introducing instability, but these three extensions all seem to work well. (It's funny how you don't really think of, for instance, brace matching as being a big deal, until you don't have it...)
I haven't really blogged much about programming recently, so I'd really like to get back into the habit. I have a few possible topics in mind for AX-related posts, so hopefully I can find the time to write those up soon.
Labels: Dynamics AX, programming, TFS
© 2011 Andrew Huey