2010-07-30

Find Authenticode Signed Assemblies

Mono.Security can be used to check for the presence of an Authenticode signature on a assembly. On my system, only one assembly isn’t signed in the .NET 4 GAC. The other 364 of 365 are signed. In the .NET 2 GAC, 936 are signed while 1269 are not. In Windows Explorer, you can see the details on the “Digital Signature” tab in the file’s Properties window.

open System
open System.IO
 
type Authenticode = Mono.Security.Authenticode.AuthenticodeDeformatter
 
let windir = Environment.GetFolderPath Environment.SpecialFolder.Windows
let pathGac4 = Path.Combine(windir, @"assembly")
 
let assemblies = Directory.EnumerateFiles(pathGac4, "*.dll", SearchOption.AllDirectories)
 
let isSigned (assembly:string) =
  let a = Authenticode assembly
  a.Signature <> null
 
let main() =
  assemblies
  |> Seq.filter isSigned
  |> Seq.length |> printfn "%d"
  //|> Seq.iter (printfn "%s")
  ()
 
main()

I’m using “C:\Program Files (x86)\Mono-2.6.7\lib\mono\2.0\Mono.Security.dll”. Install Mono 2.6.7 for Windows and you’ll have one too.

The next step is to actually validate the assembly. This can be done by using both Mono.Security and System.Security assemblies. You can use AuthenticodeDeformatter to get the RawData bytes for both the SingingCertificate and Certificates that make up the entire chain. Then you can instantiate an X509Certificate2 from System.Security.Cryptography.X509Certificates with the RawData bytes. A few slightly tricky validation steps are left, but I’ll leave that for a future post.

2010-07-28

Exploring the GAC with F# and Mono.Cecil

Mono.Cecil can be used to look inside assemblies. May be you want to search GAC to find types that implement a certain interface. In this post, I’ll show how to find all the types that implement IDisposable as an example.

The output of this code on my machine:

There are 1620 DLLs in C:\Windows\assembly.
There are 365 DLLs in C:\Windows\Microsoft.NET\assembly.
# of public types: 30071
# IDisposable: 733

The code:

open System
open System.IO
open Mono.Cecil
 
let windir = Environment.GetFolderPath Environment.SpecialFolder.Windows
let pathGac2 = Path.Combine(windir, "assembly")
let pathGac4 = Path.Combine(windir, @"Microsoft.NET\assembly")
 
let main() =
  let dlls2 = Directory.EnumerateFiles(pathGac2, "*.dll", SearchOption.AllDirectories)
  let dlls4 = Directory.EnumerateFiles(pathGac4, "*.dll", SearchOption.AllDirectories)
 
  printfn "There are %d DLLs in %s." (Seq.length dlls2) pathGac2
  printfn "There are %d DLLs in %s." (Seq.length dlls4) pathGac4
 
  let readModule (dll:string) = ModuleDefinition.ReadModule dll
 
  let publicTypes =
    dlls4
    |> Seq.map readModule
    |> Seq.map (fun m -> m.Types)
    |> Seq.concat
    |> Seq.filter (fun t -> t.IsPublic)
 
  // How many public types in all .NET 4 assemblies?
  publicTypes |> Seq.length |> printfn "# of public types: %d"
 
  // How many of those implement the IDisposable interface?
  let isFullNameEqual (t:Type) (tr:TypeReference) = t.FullName = tr.FullName
  publicTypes
  |> Seq.filter (fun t -> t.Interfaces |> Seq.exists (isFullNameEqual typeof<IDisposable>))
  |> Seq.length |> printfn "# IDisposable: %d"
  ()
 
main()

A source release of the latest version of Mono.Cecil can be downloaded here. I built 0.9.3 for .NET 4 Client Profile in the Release configuration without issue.

2010-02-01

Microsoft’s Extensible Storage Engine with F#

The Extensible Storage Engine (ESE) that comes with Microsoft Windows is a data storage technology that is a good match for F# and domains like technical computing.  ESE has a really impressive set of features [1] [2].  I like that it provides a “highly concurrent database access”, can scale from a 1 MB database to a 16 TB one, and the types map well to .NET.

The ESE native library can be found on your Windows systems at %systemroot%\system32\esent.dll.  A .NET assembly, Esent.Interop.dll, wraps the native library to provides a managed interface.  It was first released to CodePlex [3] on 2008-12-22 by Laurion Burchall [4] and is at version 1.5 release on 2009-09-12.  I wrapped it and exposed the functions I needed for this blog in a F# module.

ESE is not a relational database management system, it is a “indexed sequential access method” (ISAM) [5] system.  I like to think of it as storing sequences of records and that makes it a good match for F#.  I created an F# record type for storing a Product based on the Product table from the Microsoft SQL Server sample database AdventureWorksLT2008.  I made all the fields in the record be of the option type in order to make the code safer and more reusable. 

type Product =
  {
    ID: Int32 option; 
    Name: String option;
    Number: String option;
    Color: String option;
    StandardCost: Single option;
    ListPrice: Single option;
    Size: String option;
    Weight: Single option;
    CategoryID: Int32 option;
    ModelID: Int32 option;
    SellStartDate: DateTime option;
    SellEndDate: DateTime option;
    DiscontinuedDate: DateTime option;
    ThumbNailPhoto: Byte[] option;
    ThumbnailPhotoFileName: String option;
    RowGuid: Guid option;
    ModifiedDate: DateTime option;
  }

With the help of a small MsSqlReader.fs module, the 255 products in the SQL Server database can be read into a lazy sequence of products records like so:

let readAll() =
  seq {
    use cmd = createCommand cn "select ProductID, Name, ProductNumber, Color, StandardCost, ListPrice, Size, Weight, ProductCategoryID, ProductModelID, SellStartDate, SellEndDate, DiscontinuedDate, ThumbNailPhoto, ThumbnailPhotoFileName, rowguid, ModifiedDate from SalesLT.Product"
    use r = cmd.ExecuteReader()
    while r.Read() do
      yield
        {
          ID = readInt32 r 0;
          Name = readString r 1;
          Number = readString r 2;
          Color = readNullableString r 3;
          StandardCost = readNullableDecimalAsSingle r 4;
          ListPrice = readNullableDecimalAsSingle r 5;
          Size = readNullableString r 6;
          Weight = readNullableDecimalAsSingle r 7;
          CategoryID = readNullableInt32 r 8;
          ModelID = readNullableInt32 r 9;
          SellStartDate = readDateTime r 10;
          SellEndDate = readNullableDateTime r 11;
          DiscontinuedDate = readNullableDateTime r 12;
          ThumbNailPhoto = readNullableBytes r 13;
          ThumbnailPhotoFileName = readNullableString r 14;
          RowGuid = readGuid r 15;
          ModifiedDate = readDateTime r 16;
        }
  }

I create the schema for the ESE database file in the AdventureWorksEse class type:

type AdventureWorksEse(file) =
  let fileExists = File.Exists file
 
  let ese = getInstance file
  do 
    ese.Parameters.Recovery <- false // useful to turn off during development
    ese.Init()
 
  // create table and columns for a new file
  do
    if fileExists <> true then
      use sn = openSession ese
      let db = createDatabase sn file
      let t = createTable sn db Names.Product
      addColumnInt32NotNull sn t Names.ID
      addColumnTextUnicodeNotNull sn t Names.Name 100 // bytes, 50 Unicode characters
      addColumnTextUnicode sn t Names.Number 50 // bytes, 25 Unicode characters
      addColumnTextUnicode sn t Names.Color 30 // bytes, 15 Unicode characters
      addColumnSingleNotNull sn t Names.StandardCost
      addColumnSingleNotNull sn t Names.ListPrice
      addColumnTextUnicode sn t Names.Size 10 // bytes, 5 Unicode characters
      addColumnSingle sn t Names.Weight
      addColumnInt32 sn t Names.CategoryID
      addColumnInt32 sn t Names.ModelID
      addColumnDateTimeNotNull sn t Names.SellStartDate
      addColumnDateTime sn t Names.SellEndDate
      addColumnDateTime sn t Names.DiscontinuedDate
      addColumnBytes sn t Names.ThumbNailPhoto 8000 // SQL Server varbinary max
      addColumnTextUnicode sn t Names.ThumbnailPhotoFileName 100 // bytes, 50 Unicode characters
      addColumnGuidNotNull sn t Names.RowGuid
      addColumnDateTimeNotNull sn t Names.ModifiedDate
 
      createIndexPrimary sn t Names.ID
      createIndexUnique sn t Names.Name
      createIndexUnique sn t Names.Number
      createIndexUnique sn t Names.RowGuid
 
  member x.File with get() = file
  member x.Ese with get() = ese
 
  interface IDisposable with
    member x.Dispose() =
      ese.Dispose()

The ProductServiceMsEse class type can be used to insert or read products from the ESE database.

// wraps a ESE session, so create a new instance for each thread
// http://msdn.microsoft.com/en-us/library/aa964734(EXCHG.10).aspx
type ProductServiceMsEse(ese:AdventureWorksEse) =
  let sn = openSession ese.Ese
 
  let table =
    let db =
      attachDatabase sn ese.File
      openDatabase sn ese.File
    getTable sn db Names.Product
 
  // get columns
  let getColumn name =
    MsEse.getColumn sn table name
  let columnID = getColumn Names.ID
  let columnName = getColumn Names.Name
  let columnNumber = getColumn Names.Number
  let columnColor = getColumn Names.Color
  let columnStandardCost = getColumn Names.StandardCost
  let columnListPrice = getColumn Names.ListPrice
  let columnSize = getColumn Names.Size
  let columnWeight = getColumn Names.Weight
  let columnCategoryID = getColumn Names.CategoryID
  let columnModelID = getColumn Names.ModelID
  let columnSellStartDate = getColumn Names.SellStartDate
  let columnSellEndDate = getColumn Names.SellEndDate
  let columnDiscontinuedDate = getColumn Names.DiscontinuedDate
  let columnThumbNailPhoto = getColumn Names.ThumbNailPhoto
  let columnThumbnailPhotoFileName = getColumn Names.ThumbnailPhotoFileName
  let columnRowGuid = getColumn Names.RowGuid
  let columnModifiedDate = getColumn Names.ModifiedDate
 
  let insert sn (p:Product) =
    let t = table
    let setColumns() =
      writeInt32 sn t columnID p.ID
      writeString sn t columnName p.Name
      writeString sn t columnNumber p.Number
      writeString sn t columnColor p.Color
      writeSingle sn t columnStandardCost p.StandardCost
      writeSingle sn t columnListPrice p.ListPrice
      writeString sn t columnSize p.Size
      writeSingle sn t columnWeight p.Weight
      writeInt32 sn t columnCategoryID p.CategoryID
      writeInt32 sn t columnModelID p.ModelID
      writeDateTime sn t columnSellStartDate p.SellStartDate
      writeDateTime sn t columnDiscontinuedDate p.DiscontinuedDate
      writeBytes sn t columnThumbNailPhoto p.ThumbNailPhoto
      writeString sn t columnThumbnailPhotoFileName p.ThumbnailPhotoFileName
      writeGuid sn t columnRowGuid p.RowGuid
      writeDateTime sn t columnModifiedDate p.ModifiedDate
    MsEse.insert sn t setColumns
 
  let read() =
    let t = table
    {
      ID = readInt32 sn t columnID;
      Name = readString sn t columnName;
      Number = readString sn t columnNumber;
      Color = readString sn t columnColor;
      StandardCost = readSingle sn t columnStandardCost;
      ListPrice = readSingle sn t columnListPrice;
      Size = readString sn t columnSize;
      Weight = readSingle sn t columnWeight;
      CategoryID = readInt32 sn t columnCategoryID;
      ModelID = readInt32 sn t columnModelID;
      SellStartDate = readDateTime sn t columnSellStartDate;
      SellEndDate = readDateTime sn t columnSellEndDate;
      DiscontinuedDate = readDateTime sn t columnDiscontinuedDate;
      ThumbNailPhoto = readBytes sn t columnThumbNailPhoto;
      ThumbnailPhotoFileName = readString sn t columnThumbNailPhoto;
      RowGuid = readGuid sn t columnRowGuid;
      ModifiedDate = readDateTime sn t columnModifiedDate;
    }
 
  member x.Insert product =
    let fn() = insert sn product
    transact sn fn
 
  member x.InsertAll products =
    let fn() = products |> Seq.iter (insert sn)
    transact sn fn
 
  member x.ReadAll() =
    setCurrentIndexToPrimary sn table
    readAll sn table read
 
  member x.ReadAllByName() =
    setCurrentIndex sn table Names.Name
    readAll sn table read
 
  member x.ReadAllByNumber() =
    setCurrentIndex sn table Names.Number
    readAll sn table read
 
  interface IDisposable with
    member x.Dispose() =
      table.Dispose()
      sn.Dispose()

The demo application runs this code:

/// Establishes a connection to SQL Server. Creates the ESE database file. Fills the ESE database.
let transferProducts() =
  use ese = new AdventureWorksEse("AdventureWorks.db")
  use svcEse = new ProductServiceMsEse(ese)
  use cn = MsSqlReader.openConnection @"server=.\sqlexpress; Integrated Security=true; Initial Catalog=AdventureWorksLT2008R2"
  let svcSql = ProductServiceMsSql cn
  svcSql.ReadAll() |> svcEse.InsertAll
 
/// print the first n products indexed by name
let printProducts n =
  let print (p:Product) =
    printfn "%s %s" p.Number.Value p.Name.Value
  use ese = new AdventureWorksEse("AdventureWorks.db")
  use svc = new ProductServiceMsEse(ese)
  svc.ReadAllByName()
  |> Seq.take n
  |> Seq.iter print
 
let main() =
  transferProducts()
  printProducts 10
  Console.ReadKey false |> ignore

You can browse the source code or check out the source and build the program using Visual Studio 2010 (beta 2).  A couple of things like the GUID column type require Windows Vista or above.  Modifying it to run on Windows XP shouldn’t be very difficult.

svn checkout http://ctaggart.googlecode.com/svn/tags/blog-2010/2010-02-01/fese/

References & Links

  1. 2008-10-23 “ESENT (Extensible Storage Engine) API in the Windows SDK”
  2. MSDN - Extensible Storage Engine
  3. CodePlex - ESENT Managed Interop
  4. Blog - “ESE/ESENT Database Stuff” by Laurion Burchall
  5. Wikipedia - ISAM
  6. Book - F# for Technical Computing by Jon Harrop

2010-01-13

List Transactional WCF Bindings in F#

Scott Seely just posted how “F# is Changing My Style”.  He gave example code of how he now approaches coding in C# in a more functional way.  The code uses LINQ to print a list of WCF binding that have a default constructor and support transactions.  I think it actually is a whole lot more readable in F#:

open System
open System.ServiceModel
open System.ServiceModel.Channels
 
let hasDefaultConstructor (t:Type) =
  t.GetConstructors()
  |> Seq.exists(fun c -> c.GetParameters().Length = 0)
 
let getType (o:Object) =
  o.GetType()
 
let isTransactional (b:Binding) =
  b.CreateBindingElements()
  |> Seq.map getType
  |> Seq.exists typeof<TransactionFlowBindingElement>.Equals
 
let main() =
  typeof<Binding>.Assembly.GetTypes()
  |> Seq.filter typeof<Binding>.IsAssignableFrom
  |> Seq.filter hasDefaultConstructor
  |> Seq.map Activator.CreateInstance
  |> Seq.cast<Binding>
  |> Seq.filter isTransactional
  |> Seq.iter(printfn "%A")
 
  Console.ReadKey(false) |> ignore
  ()
 
main()

I built up the sequence a line or two at a time, printing the results as I went, then piping them to the next filter.  Simply comment out a few lines and you can print out all types in the assembly, all Binding types, or only those with default constructors.  It also feels a lot more reusable.  For example, hasDefaultConstructor is a function I can use elsewhere.

I do get the same answer as him as long as I use .NET 4:

System.ServiceModel.NetTcpBinding
System.ServiceModel.NetTcpContextBinding
System.ServiceModel.WSHttpBinding
System.ServiceModel.WSHttpContextBinding
System.ServiceModel.NetNamedPipeBinding
System.ServiceModel.WSFederationHttpBinding
System.ServiceModel.WS2007FederationHttpBinding
System.ServiceModel.WS2007HttpBinding
System.ServiceModel.WSDualHttpBinding

It looks like in .NET 3.5, NetTcpContextBinding and WsHttpContextBinding are in System.WorkflowServices.dll, but they are included in System.ServiceModel.dll in .NET 4.

2009-12-21

Locate Me

I updated “funslae” to center the map on your location if Google can figure out your location.  The Google AJAX API is used to get your location based on your IP address.  There are many other options for geocoding an IP address.  The updated example demonstrates how to communicate between JavaScript and Silverlight in F#.  The approach is based on a 2008-07 blog post from Mike Snow.

Most of the coding was done in JavaScript in index.html.  I updated it to load the Google AJAX API, then the Silverlight control via JavaScript, then center the map by calling the .NET code from JavavScript.  In App.fs, I extended Map to create a ScriptableMap that is exposed to the JavaScript.

Let me know if doesn’t work for you.