This past week I managed to:
All of this fun activity didn’t leave much time for:
This evening I finally got to jump back into a Reporting Services frame of mind in preparation for San Fran next week. Reporting Services ships with a utility to run “scripts” from the command line: the rs (rs.exe) utility. Most Microsoft server applications expose a COM object model that admins can script against using VBScript, but Reporting Services exposes only a web service API. The “scripts” that rs.exe executes are actually little hunks of VB.NET code that invoke methods on the web service API.
Reporting Services only ships two sample scripts. One script cancels running jobs on the server (CancelRunningJobs.rss), the second script (PublishSampleReports.rss) demonstrates how to deploy reports to the report server (yes, the default extension is unfortunately “rss”). A really short script might look like the following:
Sub Main() Dim permissions As String() permissions = rs.GetSystemPermissions() For Each permission As String in permissions Console.WriteLine(permission) Next End Sub
I can save the above snippet in a text file and execute the instructions from the command line like so:
rs -i scriptname.rss -s http://localhost/reportserver
The rs utility compiles the above snippet (without option strict, so there is no need to declare permissions with a specific type) and executes the code. Obviously, there is some additional infrastructure in place to let the variable rs invoke web service methods. A little poking around with reflector reveals the utility sets up our code with imports for the System, System.Web, System.WebServices, System.WebServices.Protocols, System.IO, and Microsoft.SqlServer.ReportingServices namespaces. The utility wraps the code chunk shown above inside of a class declaration (____ScriptClass) and adds a member variable (rs) defined as type ReportingService.
What’s also interesting is an embedded resource in rs.exe by the name of StartupClass.vb. The StartupClass, stripped down to essentials (I removed the code for exception handling and for batching commands in the script.), looks like the following:
Public Module MainModule Public Sub Main(ByVal args as string()) Dim rs as new WebServiceWrapper(args(0), args(1), args(2), args(3)) Dim clientScript as new ____ScriptClass() clientScript.rs = rs clientScript.Main() End Sub End Module
What intrigues me about rs.exe is how easy it is to pull off this “scripting” technique in .NET. Think about what it would take for an application to pick up a hunk of C++ source code from the disk and just execute it in-process. It is possible, of course, but fraught with peril.
The tricks you can pull off safely in .NET just never cease to amaze me.