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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
    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
    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.