I'm going to be blogging about how to write a scripting engine in the CLR. As a teaser, let me show you what I can do with the scripter I've already ginned up. There's not a whole lot of effort expended in creating this scripter, and it has some warts -- some really big ones, which I won't hide, but it already can do quite a lot of things.
(define fwdir "c:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/")
That just defines a global variable (or symbol), “fwdir“, whose value is the path in the string literal.
(define syslib (load-assembly (string-append fwdir "system.dll")))
That defines a global symbol, “syslib“, whose value is the result of calling “load-assembly” on the string concatenation of (the value of) “fwdir', which we already defined, and the string literal “system.dll.“ The blue strings are part of the IEEE 1178 standard language; the red ones are my extensions for scripting in the CLR. “load-assembly“ just returns an object of type “Assembly“, which we use in the Reflection API in various ways.
(define maildll (string-append fwdir "system.web.dll"))
You should be getting the idea by now...
(define maillib (load-assembly maildll))
Ditto... let's do something new:
(define email (new maildll "System.Web.Mail.MailMessage"))
That creates a new instance of the type “System.Web.Mail.MailMessage” in the assembly “maildll” and assigns the instance to the symbol “email“.
(invoke email "set_Body" "Test Body")
That invokes the “set_Body“ method on the object “email“ with the argument “Test Body“. “set_Body“ is actually the “set“ branch of the property “Body“; Reflection exposes this as an ordinary method named “set_Body“ and my invoke routine just makes a really straightforward call to Reflection's Invoke. If we had written that line of code in C# instead of the scripter, it would look like email.Body = "TestBody";
(invoke email "set_Subject" "Test Subject")
(invoke email "set_From" "few@boar.com")
(invoke email "set_To" "brianbec@exchange.nowhere.com")
Those are just some more property “set“s. Let's get an email server and send the message!
(define server-ip
(invoke-static syslib "System.Net.Dns" "Resolve" "df-keekyoo"))
That assigns to the symbol “server-ip“ the result of invoking the static method “System.Net.Dns.Resolve“ in the assembly syslib for the exchange server “df-keekyoo“. The “invoke-static“ procedure requires us to separate the namespace of the target routine, namely “System.Net.Dns“, from the name of the target routine, namely “Resolve“. Sorry about that.
(define address (vector-ref (invoke server-ip "get_AddressList") 0))
Invoking the method “get_AddressList“, which, of course, is really the “get“ branch of the property “AddressList“, on the “server-ip“ object returned in the prior call to “System.Net.Dns.Resolve“ results in an array of addresses. We access the 0-th component of the array via the IEEE-standard routine “vector-ref“ to get the stuff we need for the next call. It all just works... nice.
(invoke-static maillib "System.Web.Mail.SmtpMail" "set_SmtpServer"
(invoke address "ToString"))
In an inner call, we invoke the .NET-standard “ToString“ method on the “address“ object to get the argument for a call of the static routine “System.Web.Mail.SmtpMail.set_SmtpServer“ in the “maillib“ assembly. This call is for side-effect, so we discard its return value.
(invoke-static maillib "System.Web.Mail.SmtpMail" "Send" email)
Finally, we send the email object. Boom! We're done. Slick, eh?