Visual Studio 2012, take two

So I managed to get VS 2012 installed. (See previous post for details on my first failed attempt.) I’d love to write up a blog post detailing some weird issue and how I worked around it, but I don’t really have anything useful to offer along those lines. I basically just installed some pending Windows Updates, had a cup of coffee, then tried again.

After the install, I was prompted to install a patch that apparently fixes some compatibility issue. Then, I was prompted to install VS 2012 update 2. I did both of those things, and now have a usable VS 2012 install. I’m still not sure why Microsoft can’t post updated installers for their products when they release patches and updates, but I’m used to the silliness now, so I just grin and bear it.

I had read a good bit of negative feedback about the UI changes in VS 2012, and I have to say that I agree with most of it, now that I’ve seem the product up close. It’s much less pleasant to look at, compared to VS 2010. First, the upper-case menus are ridiculous. Whoever thought that was a good idea has hopefully been fired by now. (Who am I kidding, he probably got promoted!) You can fix that pretty easily with this NuGet package. And the guy who put it together gets extra points for the instructions: “YOU NO LIKE NO SHOUTING?! Run Disable-AllCaps”.

The next easily-fixed interface blunder is the color scheme. The default is called “light”, and it’s kind of an all-grey mess, with a little bit of white, black, and blue.  If you switch to the “blue” theme, you get something a little like VS 2010, and much more usable.

The general flatness of the interface, though, is still pretty blah. There was really nothing wrong with the VS 2010 interface, and no reason to arbitrarily change stuff for the worse like this, and it’s so hard to believe that anybody really thought they were making things better here.

There’s a blog entry on the VS team blog that discusses the all-caps thing in specific. If you read it, you’ll get a good picture of how a very large company can make really poor decisions about specific products, based on big-picture corporate strategies and directions, and how they can be (apparently) clueless about what they’re doing. They talk about how the use of uppercase text is a “strong signature element” of MS user interfaces, including Zune and Bing. Now, really, how much thought does it take to figure out that the menu bar for a complex programming IDE has nothing to do with the user interface on a failed MP3 player or a web search engine? They end the blog post by saying that “we will enable you to customize the casing, and we are exploring options for how to expose that choice.” Well, the blog post is about a year old, VS 2012 has had two update releases, and still no option in the product itself to change the menu casing.

Alright, so that was way too much grumbling about fairly trivial user interface stuff. I guess I’m just in a bit of a cranky mood today! I still look forward to trying out VS 2012, and seeing what useful new features have been added to the product, and to C#!

Visual Studio 2012

I haven’t bothered with VS2012 yet, but today I decided to try to install VS Express 2012 for Windows Desktop on my laptop. I really only want it, at this point, for developing console apps. I wanted to take a shot at using it for the Project Euler stuff that I’ve been playing around with, and I was also interested in trying out some of the async stuff in C# 5. So nothing fancy; I just wanted to get familiar with it.

Well, no luck. The install got about halfway through (judging by the progress bars), then got no further. I know some of the VS installs in the past have been notoriously slow. (I’m looking at *you* VS 2005 SP 1! Or was it VS 2008 SP1…?) But this one just stalled at the same spot for 2 or 3 hours, with no change, so I gave up on it.

I’m starting to wonder if I need to do a fresh install of Windows 8 on that laptop, just to clear up the cruft from previous VS installs. I really wish Microsoft could make an IDE that didn’t cause so much grief just to install…

Project Euler

I started messing with Project Euler this week. It’s actually a lot of fun. I’ve only solved three problems so far, but I’ve been enjoying it, so I think I’ll keep going and see how far I get before I get tired of it, or I get pulled into something else, or the problems get too hard.
I’d been thinking that maybe I’d try a new language for this, but I decided to stick with C#, as I’m not really doing much C# at work, and I want to keep in practice with it.
I’ll also admit that I haven’t had any reason to do the kind of math problems that are found at Project Euler in a very long time, so my skills there are a bit rusty. But I’m managing to remember what Fibonacci numbers are and all that fun stuff!

Here’s my badge:

TFS Scripts

I’m definitely not a TFS genius, but I’ve written a few scripts that have proven helpful in dealing with some of the issues that come up with version control.
First, here’s a simple one. This just automates a simple TF.EXE command to show the last 50 check-ins in our project. This particular command opens a GUI window to show the output.

# https://gist.github.com/andyhuey/5471064
[string]$tf = "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\TF.exe"
pushd
cd c:\ax2012tfs
& $tf history /r /stopafter:50 *
popd

Second, here’s one to show the TFS status. This command, unlike the previous, sends output to the console, so I’m piping it to Notepad++, so I can see it there.

# https://gist.github.com/andyhuey/5471072
[string]$tf = "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\TF.exe"
[string]$npp = "C:\Program Files (x86)\Notepad++\notepad++.exe"
# [string]$tempFile = [System.IO.Path]::GetTempFileName()
[string]$tempFile = "$env:temp\tfStatus.txt"
pushd
cd c:\ax2012tfs
& $tf status > $tempFile
popd
& $npp $tempFile

And third, here’s a somewhat more complicated one. This one allows you to diff two changesets, and pipes the output to Notepad++. But, if there’s an error, it instead shows a “press any key” message, so you can see the error in the console window. Notepad++ has syntax highlighting for diff files, so the output is reasonably nice-looking.

# https://gist.github.com/andyhuey/5471084
param (
     [string]$cs1 = $( Read-Host "Enter changeset 1 (as c9999)" ),
     [string]$cs2 = $( Read-Host "Enter changeset 2 (as c9999)" )
)
[string]$tf = "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\TF.exe"
[string]$npp = "C:\Program Files (x86)\Notepad++\notepad++.exe"
[string]$tempFile = "$env:temp\tfDiff.diff"
pushd
cd c:\ax2012tfs
& $tf diff cus /v:$cs1~$cs2 /r /f:unified > $tempFile
if ($LastExitCode -eq 0)
{
     & $npp $tempFile
}
else
{
     Write-Host "Press any key to continue ..."
     $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
popd

This pretty much concludes the overview of my utility scripts that I started a few days ago. I hope it was helpful to someone. If not, at least I’ve got them documented now, so if I lose them again, I know where to look!

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.

// https://gist.github.com/andyhuey/5470950
public void exportAllProjects()
{
    // get the project list, and export all projects.
    Array projects;
    int i;
    ProjectNode sharedProjects, privateProjects, projectNode;
    str myDocsPath, filePath;

    #WinAPI

    myDocsPath = WinAPI::getFolderPath(#CSIDL_PERSONAL);
    myDocsPath += @"\xpo_backup";

    // make sure myDocsPath exists.
    if (!WinAPI::pathExists(myDocsPath))
    {
        WinAPI::createDirectoryPath(myDocsPath);
    }

    projects = this.getProjectList();

    sharedProjects = Infolog.projectRootNode().AOTfindChild('Shared');
    if (!sharedProjects)
        throw error("Error: cannot located shared project node!" );

    privateProjects = Infolog.projectRootNode().AOTfindChild('Private');
    if (!privateProjects)
        throw error("Error: cannot located private project node!" );

    for (i= 1; i <= projects.lastIndex(); i++)
    {
        // skip any line starting with a semi-colon
        if (subStr (projects.value(i), 1, 1) == ";" )
            continue;

        projectNode = sharedProjects.AOTfindChild(projects.value(i));
        if (!ProjectNode)
            projectNode = privateProjects.AOTfindChild(projects.value(i));

        if (projectNode)
        {
            filePath = myDocsPath + @"\" + projects.value(i) + ".xpo" ;
            projectNode.treeNodeExport(filePath);
        }
        else
            warning(strFmt("Project %1 cannot be found." , projects.value(i)));
    }
}

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.

; https://gist.github.com/andyhuey/5466566
; comment/uncomment, the way it was intended to be...
#IfWinActive ahk_class AxMainFrame
^k::
Transform, CtrlC, Chr, 3
Transform, CtrlU, Chr, 21
Input Key, L1 M T1
if Key = %CtrlC%
     Send ^e^c
if Key = %CtrlU%
     Send ^e^u
return
#IfWinActive

Backup Script

In my last post, I mentioned that I was going to write up some of the utility scripts I have on my VM. The first one is pretty simple. It’s a little PowerShell script to zip up the My Documents folder on the VM, and copy it to the physical machine. (I’m using 7-Zip.)

There are a few things in this script that are pretty common tasks that I need to do when using PowerShell, so this is a good thing to put up on the blog for reference. Just to point out those things:

  1. Creating a file name that contains the current date.
  2. Running a command that’s in a string variable.
  3. Prompting to “press any key” when done, so the user can see error messages, if the script is being run from a desktop icon.
  4. Giving an option to skip the “press any key” prompt, when the command is run unattended from task scheduler.
# https://gist.github.com/andyhuey/5466524
param(
     [switch]$quiet
)
$zipExe = "C:\Program Files\7-Zip\7z.exe"
$dateStr = '{0:yyyy-MM-dd}' -f (Get-Date)
$buFileName = "\\my-machine\c$\Users\me\Documents\backup\VM_MyDocBU_" + $dateStr + ".7z"
$myDocs = "C:\Users\me\Documents"
pushd
cd $myDocs
& $zipExe a -r $buFileName $myDocs
popd
if (!$quiet)
{
     Write-Host "Press any key to continue ..."
     $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}

Hard drive crash

One of the hard drives on my work PC crashed a couple of days ago. My work PC is (or rather, was) configured with an SSD for a boot drive, and two regular SATA drives, in a RAID 0 configuration, for a secondary data volume. It was one of those SATA drives that failed. Since RAID 0 doesn’t have any redundancy built in, that killed the volume.

The only data I had on that volume were the files for my VM. The way we have developer machines configured here, we have general productivity stuff (Office, etc) on the boot volume, and all the developer stuff on the VM. The setup for developing for Dynamics AX is fairly complicated, so it makes sense to do it on a VM.
Unfortunately, we don’t have any facility set up for backing up our VMs anywhere. Also, between the way AX stores source files, and the way we have TFS set up, we don’t always check in code daily, nor do we have a simple way of backing up in-progress code changes that haven’t been checked in. So, the end result is that I lost about two days worth of work on my current project.
I had, at one point, written a backup script (using PowerShell and 7-Zip) to back up the My Docs folder on the VM to the My Docs folder on the physical machine, but I hadn’t ever set it to run on a scheduled basis, so the backup file there was about a week old, which meant that I also lost a few SQL files, some test spreadsheets, and one quickie VS 2010 project that I’d written to test a web service. Oh, and I was keeping the backup script itself (plus some other scripts) in a ‘util’ folder on the root of the VM C: drive, so those didn’t get backed up either, and were lost.
So the takeaway from all of this, of course, is that I need to do what I can to get around the limitations of the environment I’m working in, and set up some automated backup procedures.
In terms of backing up the My Docs folder, I rewrote my lost PowerShell script, and set it up in task scheduler to run at 6pm daily. It ran fine last night, and I think it’ll work fine on a continuing basis.
In terms of backing up in-progress work in AX, I extended the ‘startup projects’ class that I blogged about recently to also allow me to export all of my active projects. I have it exporting them to a folder under the My Docs folder, so, if I run the export at the end of the day, prior to the file system backup, I should always have a backup of my current work, in a format that I can re-import into AX, if need be.
There are still some big holes in this system, including the fact that I have to remember to run that export daily. But it’s a good start. I’d like to add some extra stuff to this process, including daily SQL backups, and maybe a push of certain backup files to the cloud. The SQL backups are kind of hard, since the AX test database is 70 GB. And my employer, for some reason, likes to block access to cloud-based backup & storage providers, so I can’t just copy stuff into a DropBox folder, so that part’s a little tricky too. 
I’ve also considered setting up a local Mercurial or Git repo, checking in the AX export files every day, and pushing them up to a private Bitbucket repo. This would give me offsite backup, with the added benefit of increased granularity and visibility, but it would probably violate one or more corporate policies.
As a follow-up to this post, I’m going to write a few more posts, about some of the scripts I’m using now.

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:

  1. Maintain a list of startup projects in a simple text file, stored in my personal “documents” folder.
  2. Allow for adding and removing projects from this list, via the right-click context menu.
  3. 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:

// https://gist.github.com/andyhuey/5315492
public static AjhDevLaunchStartupProjects construct()
{
    return new AjhDevLaunchStartupProjects();
}

Now let’s add a method to get the file name for the text file:

// https://gist.github.com/andyhuey/5315509
private str getStartProjFileName()
{
    str myDocsPath;

    #WinAPI

    myDocsPath = WinAPI::getFolderPath(#CSIDL_PERSONAL);
    return myDocsPath + @"\axStartProjects.txt";
}

Now, methods to read and write the file:

// https://gist.github.com/andyhuey/5315524
private Array getProjectList()
{
    // get the project list from a file.
    str startProjFileName;
    TextBuffer tbProjList;
    Array projects = new Array(Types::String);
    int nProjects;

    startProjFileName = this.getStartProjFileName();
    tbProjList = new TextBuffer();

    // if it doesn't exist, create an empty file & return.
    if (!WinAPI::fileExists(startProjFileName))
    {
        WinAPI::createFile(startProjFileName);
        return projects;
    }

    // should probably assert permission...
    tbProjList.fromFile(startProjFileName);

    nProjects = 0;
    while (tbProjList.nextToken(true))
    {
        nProjects++;
        projects.value(nProjects, tbProjList.token());
    }
    return projects;
}

private boolean writeProjectList(Array projects)
{
    // write out a project list to file, overwriting existing list.
    str startProjFileName;
    TextBuffer tbProjList;
    int i;

    startProjFileName = this.getStartProjFileName();

    tbProjList = new TextBuffer();

    for (i=1; i <= projects.lastIndex(); i++)
    {
        tbProjList.appendText(projects.value(i));
        tbProjList.appendText("\n");
    }

    return tbProjList.toFile(startProjFileName);
}

And the methods to add and remove projects from the list:

// https://gist.github.com/andyhuey/5315547
private void addProject(str newProject)
{
    // add a new project to the list.
    Array projects;
    int i;

    projects = this.getProjectList();

    // make sure it's not already there...
    for (i=1; i <= projects.lastIndex(); i++)
    {
        if (projects.value(i) == newProject)
            return;
    }
    // add it and save.
    projects.value(projects.lastIndex()+1, newProject);
    this.writeProjectList(projects);
}

private void removeProject(str projectToRemove)
{
    // remove a project from the list.
    Array projectsIn, projectsOut;
    int i, j;

    projectsIn = this.getProjectList();
    projectsOut = new Array(Types::String);

    j=1;
    for (i=1; i <= projectsIn.lastIndex(); i++)
    {
        if (projectsIn.value(i) != projectToRemove)
        {
            projectsOut.value(j, projectsIn.value(i));
            j++;
        }
    }
    this.writeProjectList(projectsOut);
}

And here’s the code to open all active projects:

// https://gist.github.com/andyhuey/5315552
public void openAllProjects()
{
    // get the project list, and open all projects.
    Array projects;
    int i;
    ProjectNode sharedProjects, privateProjects, projectNode;

    projects = this.getProjectList();

    sharedProjects = Infolog.projectRootNode().AOTfindChild('Shared');
    if (!sharedProjects)
        throw error("Error: cannot locate shared project node!");

    privateProjects = Infolog.projectRootNode().AOTfindChild('Private');
    if (!privateProjects)
        throw error("Error: cannot locate private project node!");

    for (i=1; i <= projects.lastIndex(); i++)
    {
        projectNode = sharedProjects.AOTfindChild(projects.value(i));
        if (!ProjectNode)
            projectNode = privateProjects.AOTfindChild(projects.value(i));

        if (projectNode)
            projectNode.getRunNode();
        else
            warning(strFmt("Project %1 cannot be found.", projects.value(i)));
    }
}

(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:

  1. AjhDevStartupProjectAdd – to add a project.
  2. AjhDevStartupProjectRemove – to remove a project.
  3. 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:

// https://gist.github.com/andyhuey/5315559
public static void main(Args args)
{
    AjhDevLaunchStartupProjects obj = AjhDevLaunchStartupProjects::construct();
    SysContextMenu contextMenu;
    str projectName, x;
    ;

    // should always be called from a menu item.
    if (!args.menuItemName())
    {
        return;
    }

    // if called from the add-ins context menu...
    if (SysContextMenu::startedFrom(args))
    {
        contextMenu = args.parmObject();
        projectName = contextMenu.getFirstNode().treeNodeName();
    }

    switch (args.menuItemName())
    {
        case menuitemActionStr(AjhDevStartupProjectAdd):
            obj.addProject(projectName);
            break;
        case menuitemActionStr(AjhDevStartupProjectRemove):
            obj.removeProject(projectName);
            break;
        case menuitemActionStr(AjhDevStartupProjectOpenAll):
            obj.openAllProjects();
        default:
            return;
    }
}

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:

    // https://gist.github.com/andyhuey/5315566
    // ajh 2013-04-03: my own startup project thing...
    case menuitemActionStr(AjhDevStartupProjectAdd):
    case menuitemActionStr(AjhDevStartupProjectRemove):
        if (firstNode.handle() != classNum(ProjectNode) || !match(#pathProjects, firstNode.treeNodePath()))
        {
            return 0;
        }
        return 1;

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.

no more Backpack

I’ve been using 37signals’ Backpack product for several years now, since February 2007. It turns out that 37signals retired the product back in June 2012, and is no longer accepting new customers for it. It still works fine for me, and they haven’t said anything about shutting it down entirely. I don’t recall ever seeing a notice on my Backpack page letting me know that they were retiring the product, but maybe I missed it. And, when I go to my account maintenance page now, there’s no indication there either; it still shows the same plan upgrade options that it always has. I’m still on the $7/month plan, and happy with that. If they’re not actually going to shut down the service for existing users, I might as well stick with it for the time being.

I use Satchel on my iPhone and iPad to access by Backpack account. It also still works, but hasn’t been updated since 2010, and likely won’t be updated again. In fact, I don’t see the full version as available in the App Store anymore, so I guess I need to be careful not to lose the copy in my iTunes library.

Since 37signals is still charging for Backpack, and since it (likely) runs on the same platform as their other services, I imagine they’ll keep it running for the foreseeable future. There’s no reason for them to shut down a service that’s generating revenue, and probably not costing them much money to keep running.

Still, it seems prudent to look into alternatives. 37signals would probably like people to move from Backpack to Basecamp, but that wouldn’t make much sense for me. Basecamp starts at $30/month, and isn’t really meant to be used as a single-person personal organizer.

Likewise, Papyrs would probably like to grab up some of Backpack’s customer base. They actually have a blog post about Backpack’s retirement on their site that’s interesting and well-written. Papyrus isn’t a good fit for me either though. It starts at $49/month and is really meant to be used for small company intranets.

There’s a good list of Backpack alternatives at http://alternativeto.net/software/backpack/. The first item on their list is Evernote, which I do use. But I’m not sure I’d like it as a replacement for Backpack. Second on their list is OneNote, which I also use, but also isn’t quite what I would want as a Backpack replacement.

From a GTD usage standpoint, I’ve tried Nozbe, and I’m curious about Asana. Maybe a combination of Evernote and Nozbe would work out for me?

I may write up some more notes on this subject, as it’s the kind of thing I enjoy messing around with and writing about, but for now, I’ve got other stuff to do today…