andy brandt's blog

automating tedious tasks in visual studio 2005 with macros and autohotkey

yes, we are still using visual studio 2005 for our development, although we hope to cut over to 2010 in the next few weeks. anyways, a customer was having problems printing reports to a particular printer, and from previous research i knew the problem was turning the “No Printer” option on in the crystal reports designer.

now we have a lot of reports, so i wanted to automate this process for all our reports. so i turned to the built-in scripting functions of visual studio, however, it turns out that you can’t control dialogs through macros. so one person recommended using autohotkey, and i had a basic knowledge of that, so I would give it a try.

so i ended up using a combination of visual studio macros & autohotkey, and it works fairly well. it still errors how during processing, but i added code so it skips any projects that have already been opened or expanded in the solution explorer. here is the macro code:

    Sub SetCrystalNoPrinter()

        ' get then instance of the solution explorer
        Dim se As UIHierarchy = DTE.Windows.Item(Constants.vsext_wk_SProjectWindow).Object()
        Dim bg As New System.ComponentModel.BackgroundWorker()

        AddHandler bg.DoWork, AddressOf OpenReports
        bg.RunWorkerAsync(se)

    End Sub

    Private Sub OpenReports(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs)

        Dim uih As UIHierarchy = e.Argument

        Try
            Dim fn As String
            Dim report As String

            uih.DTE.SuppressUI = True

            For Each p As Project In DTE.Solution.Projects
                If CheckProject("Reporting\" & p.Name, uih) Then
                    For Each pi As ProjectItem In p.ProjectItems
                        For i As Integer = 1 To pi.FileCount
                            fn = pi.FileNames(i)
                            If IO.Path.GetExtension(fn).ToLower() = ".rpt" Then
                                report = "Reporting" & pi.ContainingProject.Name & "\" & IO.Path.GetFileName(fn)
                                OpenReport(report)
                                SetPrinterSettings()
                                System.Threading.Thread.Sleep(100)
                                DTE.ActiveDocument.Close(vsSaveChanges.vsSaveChangesYes)
                                System.Threading.Thread.Sleep(200)
                            End If
                        Next
                    Next
                End If
            Next
        Catch ex As Exception
            System.Windows.Forms.MessageBox.Show(ex.ToString())
        Finally
            uih.DTE.SuppressUI = False
        End Try

    End Sub

    Private Function CheckProject(ByVal Project As String, ByVal uih As UIHierarchy) As Boolean

        DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer).Activate()
        DTE.ActiveWindow.Object.GetItem(Project).Select(vsUISelectionType.vsUISelectionTypeSelect)

        System.Threading.Thread.Sleep(200)

        For Each i As UIHierarchyItem In uih.SelectedItems
            Return Not i.UIHierarchyItems.Expanded
        Next

        Return False

    End Function

    Private Sub OpenReport(ByVal Report As String)
        DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer).Activate()
        DTE.ActiveWindow.Object.GetItem(Report).Select(vsUISelectionType.vsUISelectionTypeSelect)

        System.Threading.Thread.Sleep(200)

        DTE.ExecuteCommand("View.Open")
    End Sub

    Private Sub SetPrinterSettings()

        Dim p As System.Diagnostics.Process

        p = System.Diagnostics.Process.Start("pathtocrystalprintsetup.exe")
        p.WaitForExit()

    End Sub

yes, there is a lot going on there. i had to use a background worker because visual studio would block when waiting for the ahk application to exit.

autohotkey code:

; send key combination to get the print setup dialog open in crystal
send !rsp

WinWait, Print Setup
ControlGet chk, Checked,, No Prin&ter, Print Setup
if chk = 0
{
    send t
}
send {Enter}
WinWaitClose

it’s not pretty, but it (sort of) works! i will add more detailed explanations of what is going on soon.