Tuesday, 8 April 2014

C# Property by reference

I wanted to write a generic function to test a validation method of my object, but I wanted the function to be able to manipulate specific properties, like turning boolean values on or off.

Now one possibility is to pass the value by reference which is often something I don't like very much, but won't get into that too much right now, but since we are dealing with properties here the usual more elegant way is to create a wrapper or lambda function of some kind.

Now natrually before coding anything a quick google/SO search reveals some basic solutions and as one might expect involves some reflection as one CodeProject article here.

However I usually use dynamic reflection as a last resort as there is too much room for error here and referring to fields as strings just doesn't seem like the cleanest way. Also a super simple solution is by an SO post made here  (scroll down a bit) which simply just uses a small wrapper class you specifiy the getter and setter using 2 lambda expressions. This is certainly clean and simple, which I like, however I tried to create a function even simpler and more concise which also uses the compiler to ensure correctness, so I came up with this.

So much like the first version to use some reflection except no strings but using expressions. First extracting the property info from the expression, a nice clean wrapper object to help with this from this SO post:

public static class PropertyHelper<T>
 public static PropertyInfo GetProperty<TValue>(
  Expression<Func<T, TValue>> selector)
  {
  Expression body = selector;
  if (body is LambdaExpression)
  {
   body = ((LambdaExpression)body).Body;
  }
  
  switch (body.NodeType)
  {
   case ExpressionType.MemberAccess:
    return (PropertyInfo)((MemberExpression)body).Member;
    break;
   default:
    throw new InvalidOperationException();
  }
  }
}

So taking that a little further, creating a helper class to generate a lambda getter and setter

public static class PropertyHelper
{
 public static Func<TValue> GetPropertyGetter<T, TValue>(T value, Expression<Func<T, TValue>> selector)
 {
  var propInfo = (PropertyInfo)((MemberExpression)(selector).Body).Member;
  return () => (TValue)propInfo.GetValue(value, null);
 }

 public static Action<TValue> GetPropertySetter<T, TValue>(T value, Expression<Func<T, TValue>> selector)
 {
  var propInfo = (PropertyInfo)((MemberExpression)(selector).Body).Member;
  return v => propInfo.SetValue(value, v, null);
 }
}

Small note: This requires you to use a lambda expression for the property any other expression will break it, I didn't add all the checks to make it more terse. So Then the usage would simply be:
var getter = PropertyHelper.GetPropertyGetter(world, w => w.Hello);
var setter = PropertyHelper.GetPropertySetter(world, w => w.Hello);

And then you can get the property by calling:

getter();
setter("Hello");

Extending that a little further by adding a nice wrapper class:

public class PropertyWrapper<TValue> : IPropertyWrapper<TValue>
{
 protected PropertyInfo _propInfo;
 protected object _instance;

 public PropertyWrapper(PropertyInfo propinfo, object instance)
 {
  _instance = instance;
  _propInfo = propinfo;
 }

 public object Instance
 {
  get { return _instance; }
 }

 public TValue Value
 {
  get { return (TValue)_propInfo.GetValue(_instance, null); }

  set
  {
   _propInfo.SetValue(_instance, value, null);
  }
 }
}

Then you can pass around the wrappers:
var wrapper = PropertyHelper.GetPropertyWrapper(world, w => w.Hello)
Now you can easily use this in your generic method and manipulate any property:
  
private static void TestCondition(IPropertyWrapper condition)
{
 condition.Value = false;
 Console.WriteLine(condition.Instance.Validate());
 condition.Value = true;
 Console.WriteLine(condition.Instance.Validate());
}
Dynamic objects are of course another way to do this however you then still are missing out on the compile time checking, so I haven't coded out this solution, but I think I'd still rather use this approach then reflection by string name method. So there you have it, this seems like a nice clean way to create property references, you can take it one more step further by creating an object extension method for this, but I usually try to avoid this as it can clutter up the code completion just a little bit too much. But if you can think of an even better cleaner way in C#

Monday, 4 November 2013

Abstractions

After reading this interesting article: Abstracting away and Abstracting into it made me think of an argument discussion with a friend regarding this very topic. I was under the belief that when I was implementing logging for my application I would be best off to create my own ILog interface. And then creating wrappers that would encapsulate either Log4Net or NLog. My motivation was that Log4Net for example provides 50+ methods that I didn't need and that I wanted something for my application with just 3 methods:
LogMessage, LogWarning, LogError
I also wanted something easy to mock and unit test. The fact that this is logging is interesting because it is so ubiquitous, meaning whichever way I go could by coupled to nearly every single class.

One way we can address the problem by merely keeping the usage on the outskirts of the application. A good example of this are IOC containers. It doesn't really matter which library we use as the client code will not use it directly. This might seem impossible with the logging example but there are techniques such as creating logging aspects (AOP) but we won't go into that here.

In hindsight I feel that I was wrong about some things but right about some other. I didn't need to abstract Log4Net due to testing needs. Log4Net does already have an ILog interface and this does make it mockable and testable. And even though it has 50+ methods to mock do you even need to bother? In a unit testing scenario your config is set up to just print to the console, no mocking needed. However addressing the 2nd problem of swapping our libraries, do you even need to? Even if you have a project that you plan will live for 5-10+ years Log4Net will probably out live it. Also this particular example the library is proven and well established.

Also by creating my own interface I really thought that this is all I would need now, but in future I end up needing more, and in time I will just duplicate the effort of the library I am abstracting. This will unfortunately create the same inevitability which is the more abstractions I leak the more I am implicitly tying myself to that library anyway and ultimately I will introduce a piece of functionality that only the one library will support and switching will be impossible.

Of course I have seen many times where companies and projects have heavily embraced and invested in library X and then due to fading support or features were forced to move to Y and requiring large portions of the code base to be re written. In the cases I mention even if we created an abstraction to either X or Y there were such fundamental differences between what these graphics libraries supported from each other that a common abstraction wasn't possible and would more than likely just water down our interfaces leaving very little point of using either of them.

In this case though it is important that we use certain patterns for development for instance if we were using a graphics library and using the MVC or MVP pattern, in this unlikelihood we certainly could just replace our views and keep all business logic in tact. Also patterns like AOP mentioned above.

So as long as the library provides ways to mock and unit test and unless you are using a new very experimental library you are probably better to abstract into.

This is my honest advice for best business value, of course if I was writing something for myself I would certainly add layers and abstractions, because its more fun, but that's another story...

Tuesday, 21 May 2013

AutoFac Dynamic Factories

AutoFac like many Ioc containers make it easy to declare your dependencies and just get them magically injected for you. The problem comes in when trying to control the lifetime of certain objects, especially if you are trying to create some shorter lifetime objects. Once you declare your dependency you don't have any more control over the lifetime of it. Also sometimes you might specifically need 2 different instances of an object. AutoFac just like many other containers has lifetime options when registering objects:
builder.RegisterType<TestClass1>().InstancePerDependency();
builder.RegisterType<TestClass2>().InstancePerLifetimeScope();
The first indicates that each dependency or call to the Resolve() method will give you a new instance. The 2nd indicates that each dependency or call to the Resolve() within the same lifetime scope will give you a new instance. This is sort of special as it actually shares the instance in the same call graph, lifetime scope or call to container.BeginLifetimeScope(). So this registration of specific to when sharing instances is preferred.

So even if you change all your objects to InstancePerDependency lifetime (which would be hugely limiting anyway) you still need a way to create these on-the-fly instances. The AutoFac solution is to resolve an object of type Owned<T>

However this means having the reference to the IContainer in your objects which is very bad and IOC 101 dont's. So just create a simple interface for your framework that you can use to inject abstract factories instead something like:

public interface IFactory<T>
{
    T Create();
}

This is very simple and easy to generate mocks for testing. You can also easily create a generic wrapper using lambda expressions to generate on the fly even when not using AutoFac or a different container. But for the autofac version, you can call it anything you like AutoFacFactory if you like, but it will function something like this:

public class Factory<T> : IFactory<T>
{
 private IContainer _container;

 public Factory(IContainer container)
 {
  _container = container;
 }

 public T Create()
 {
  var owned = _container.Resolve<Owned<T>>();
  return owned.Value;
 }
}
In order to get this to work there's a few simple steps to configure AutoFac:

First register the open generic type:

var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(Factory<>)).As(typeof(IFactory<>)).InstancePerDependency();

Next is that you have to actually register the container instance itself back to the container, as it seems its not automatic. This can actually be done easily but you need to construct a builder to update the container:

var postContainerBuilder = new ContainerBuilder();
postContainerBuilder.Register(c => container);
postContainerBuilder.Update(container);

Now you can declare dependencies to IFactory of any type that is registered and be able to create unique instances each time:

var builder = new ContainerBuilder();
builder.RegisterType<TestClass1>().InstancePerDependency();
builder.RegisterType<TestClass2>().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Factory<>)).As(typeof(IFactory<>)).InstancePerDependency();

var container = builder.Build();

var postContainerBuilder = new ContainerBuilder();
postContainerBuilder.Register(c => container);
postContainerBuilder.Update(container);

var factory1 = container.Resolve<IFactory<TestClass1>>();
var factory2 = container.Resolve<IFactory<TestClass2>>();

var t11 = factory1.Create();
var t12 = factory1.Create();

Assert.AreNotEqual(t11, t12);

var t21 = factory2.Create();
var t22 = factory2.Create();

Assert.AreNotEqual(t21, t22);

Tuesday, 22 January 2013

PowerShell Pitfalls (Non-Fatal Exceptions, Test-Path, Drives, Jobs)

So if you are trying to use PowerShell for some significant real stuff you will run into many pitfalls just like me. So much so I could dedicate a few blog posts just to get some functions out, but in trying to mount a TrueCrypt drive and checking its existence afterwards I ran into many annoying problems. So in C# I would just use Directory.Exists() and be done with it. But the PowerShell way is Test-Path. You can still use the C# version [System.IO.Directory]::Exists() but since using Test-Path and wanting to be more PowerShelly I stuck to it and forgot about Directory.Exists() I mean its the same thing anyway, right? No way! It seems like there are some other checks which I'm not sure what perhaps permissions etc the function tests for both existence and accessibility (permissions). All i really wanted to test was if the drive exists. So one other quick way is to useGet-ChildItem "C:\" and wrap this in a try except and check for exception of type System.Management.Automation.DriveNotFoundException

So much like:


function DriveExists($path)
{ 
 try
 {

  Get-ChildItem $path | Out-Null
 }
 catch
 {
  if ($_.Exception.gettype() -eq [System.Type]::GetType("System.Management.Automation.DriveNotFoundException"))
  {
   Write-Host "FAIL!"
   Write-Host $Error[0].exception
   return $false   
  }  
 }
 return $true
}

But this doesn't work either because the catch block is never executed, WTF? So it turns out that in powershell only fatal exceptions will invoke the catch block: To get around this you have to check the return value from the last command as suggested and then check the error:
   function DriveExists($path)
{ 
 Get-ChildItem $path -ErrorAction SilentlyContinue | Out-Null
 if (-not $?) # http://www.neolisk.com/techblog/powershell-specialcharactersandtokens
 {
  if ($Error[0].exception.gettype() -eq [System.Type]::GetType("System.Management.Automation.DriveNotFoundException"))
  {
   return $false   
  }    
 }  

 return $true
}
Now that I have this handy function again i found that for some reason a new drive mounted while executing the script cannot be seen, again WTF? It's as if it is cached in the session and can't be accessed. Then I think hey let me just test the existence by using Directory.Exists() and what do you know it works. In order to get Test-Path to work I had to run this out of process in a new session using a job, so something like this:
function DriveExistUsingJob($path)
{
 $ScriptBlock = {  Write-Output (Test-Path $args[0]) }
 $Job = Start-Job -ScriptBlock $ScriptBlock -ArgumentList @($path)
 Wait-Job $job | Out-Null
 $jobResult = [string](Receive-Job -Job $Job )
 while ($job.HasMoreData)
 {
  $jobResult = [string](Receive-Job -Job $Job)
 }
 
 return $jobResult
}
So why did I use PowerShell's Test-Path function one in the first place? I don't know I think I'll stick to the C# functions where I can, at least I know what that does. Remember: All I really wanted to do was to say Directory.Exists(), but no... It really makes me feel like Windows Powers Hell or at least mine.

Wednesday, 7 November 2012

Git Stash

Sometimes you want to just mess around with some code, maybe write some data buffers to a file to examine or whatever else. This leaves the code in a messy state and whenever you want to switch to a different branch you don't want to commit this code but you also don't want to leave your working directory dirty. For this I am loving Git Stash: Just go to a bash prompt (This is very nice and easy from Git Extensions):

The code must be tracked, if not already then simply:

git add .
Save:
git stash save "MyStash"
Coming back to it later:
git stash list
git stash apply stash@{0}
Find your stash with the list command, that will give you the stash number. Then apply Undoing the stash:
git stash show -p | git apply --reverse

Tuesday, 6 November 2012

Singleton and Dependency Injection

Ayende wrote this great blog post on the Singleton pattern, I always enjoy these kinds of posts from him because he looks at them from a real world perspective. It reminded me to make a specific subtle point on Singleton and that is:
Most of the issues that people have with the Singleton aren’t with the notion of the single instance, but with the notion of a global static gateway, which means that it becomes very hard to modify for things like tests, and it is easy to create code that is very brittle in its dependencies on its environment.
This has often lead to statements about statics/singletons are evil. But they don't have to be. You should continue to create objects that utilize dependency injection and abstractions and make sure they are decoupled from such statics or instances. The way you do this is very simple: Here is some typical Singleton (Implementations may vary): So when using this you have a choice, you can either use it this way: The above usage is very hard to test and doesn't allow me to change the implementation of the GetRaise method. Another big problem is the Dishonest API, there is no indication when I create the object or call the method that there is a hidden dependency to Manager which is a typical problem with using Singletons and statics like this. The solution is to rather ask for the instance and not let your class know about the singleton at all: Now I can test and change the implementation as I like, for example create a new derivative: If you don't like the manual approach, these days IoC containers are everywhere and the setup for example StructureMap is this easy: The above will just use the default Singleton instance to resolve. But you can also override it as follows: These techniques are indeed very basic but so often overlooked. The singletons are not evil when used correctly but must remain living in the root layer of your application where all the calls are made initially.

Thursday, 25 October 2012

Convert Your Physical Machine to VMWare or VirtualBox

This is very handy when you want to install a new OS or machine but want to keep your old machine running in case you need to boot / or get some settings you forgot about.

Disk2Vhd

First get Disk2Vhd. This is a super small utility written by the guys from Sysinternals (so it must be good). Just run it, choose drive C: (OS) and save the file. It runs while your OS is running. Once done you will have a VHD of your hard drive.

Virtual Box

Now you can simply create the Machine in Virtual Box and add the drive as the primary and boot from it. If you have any issues with BSOD (Blue screen of death) such as 0x0000007B it could be because the SATA drivers are not compatible. What I found works for this is to add the drive as an IDE instead. In virtual box this is easy:
Other issues may be: Make sure you create the machine as the correct platform (64bit) or (32Bit) else it wont boot either.

VMWare

I found the same to apply for VMWare, however VMWare doesn't allow you to choose IDE when adding the existing VHD. For this just add a new hard disk and choose the IDE option:



Now simply remove these disks, close VMWare and edit the .vmx file 

You should now be able to boot from either and not worry about exporting all your settings.

Simple BDD - Take 2

A while back I wrote the post Simple BDD where the idea was to show how you can write a self documenting, human readable test in BDD style without the need of any fancy frameworks using fluent syntax. I was literally inundated by feedback from one person. The complaint was that it is weird to write statements such as:

Given
Given
When
When
When
Then
Then

The argument is that this doesn't really read like a story and just seems like I am stuttering.

So I would like to make a small modification to this to show 2 points.

The test name

The test name will really be something descriptive that is most logical:

GivenARectangleWithWidthOf10AndHeightOf10TheAreaShouldCalculate100

I am sure I don't have to add the virtual spaces to make it more readable at this point as you would get the idea. The wording here is completely logical.

Implementation

To actually write this implementation that satisfies the test I am going write a Shape and Calculator and throw in a Visitor just to make it more interesting.

Now to write the grammar in a little bit more like a human, I thought to simply add an And method. Here is the full test and test class implementation:

Note

I DO recommend that you do not use too many evaluations (Asserts/Then) in one test and to rather break them up. The fact that I have shown it is just an example of what the syntax can do. Also the grammar is not set in stone and the idea is to adapt it to what you prefer.

Sunday, 29 July 2012

Simple BDD

I have spoken before about how much I like the fluent-interface. I use it a lot expecially with object builders. I was looking at the Bddify framework. I have known about this style of testing and BDD for a while, but was really inspired about how Bddify write tests.

I realized that I could do this so easily with everyday tests as well without the need for any fancy frameworks etc. And just make my own tests easier to write and understand.

I was doing a very simple schedule for an import process and heeding the warning by Ayende about scheduling. I tried to consider the wise words and established that my requirement is very simple and to the point and most of these won't be a problem for me. With this in mind I wanted to make sure there are no bugs in the schedule logic so this is what I came up with. First I could express my requirements in the test:

The implementation of this is very simple and I have just done so directly in my test class. It's just about the grammar Given a set of inputs When certain conditions are met, Then we can expect a specific result.

  • Given - Construction of the objects I am testing.
  • When - Setting some criterea on the objects I have created.
  • Then - Run a method and (Act and Assert)

Obviously to have a good grammar you first need to understand a bit more about your domain and tests. And what you will be testing, so when starting I still write the first test or 2 using a few variables and seeing how the test logic fits together and create my grammar from there.

Bugs with these kinds of things could be a nightmare in a live environment. So this style of testing helps you build all of the scenarios that you can think of and make sure those work through the lifetime of the solution.

Wednesday, 25 July 2012

PowerShell - Using Credentials

Send-MailMessage

In PowerShell you can use the Send-MailMessage command to simply send an email through SMTP. This is easy enough and the command is simply to call this with some arguments as follows:

Send-MailMessage
1
Send-MailMessage -SmtpServer "mail.yourserver.com" -Subject "Error" -From "user@domain.com" -To "user@domain.com" -Body "test"

The problem is that you may get an authentication error:

The SMTP server requires a secure connection or the client was not authenticated.
The server response was: 5.5.1 Authentication Required.

The issue is that you need to specify a credential by using the -Credential parameter

You can use the Get-Credential Cmdlet, but this will show a prompt unless you specify the username and password in your script file. This is not good for anyone viewing or copying our script file. Turns out we can easily encrypt this information using the ConvertTo-SecureString:

First we need to get the password from user input:
Read-Host
1
Read-Host -AsSecureString
Output
************
System.Security.SecureString
PS C:\>

We can also read it from a UI prompt:

Get-Credential
1
(Get-Credential -Credential "user").Password
Send-MailMessage_1

You can also save this password to a file like this:

Get-Credential
1
(Get-Credential -Credential "user").Password | ConvertFrom-SecureString | Out-File "C:\temp\mycredential.cred"

Which will produce output that looks something like this:

Securestring output
01000000d08c9ddf0115d1118c7a00c04fc297eb0100000064ff74aec2ba6d428b22782a6f91faac000000000200000000001066000000010000200000005b03f7a95e78f996e0a25c5b50fc3f481cf275092131e7fc45eb7553db7d1220000000000e800000000200002000000060b480288abe198159022376076f3f6663478790e547238af5d8550b65a9b6f810000000f536330ee4f0a2a2cd37da4472c4ed17400000008d65be8e08d68ce5472c25f70b60a252d9380b7e950a5780b253e1e20bccbd491bb65d5db6513b0e8925eb6e70f730e21b09223981f43f96dc2b1de12a3407fe

Now these secure strings cannot be decoded by anyone else but your own logged on user, if you try to take this file and decrypt it on a different machine you will get this error:

SecureString error
ConvertTo-SecureString : Key not valid for use in specified state.
 
At line:1 char:58
+ cat ("C:\temp\mycredential.cred") | convertto-securestring <<<<
    + CategoryInfo          : InvalidArgument: (:) [ConvertTo-SecureString], CryptographicException
    + FullyQualifiedErrorId : ImportSecureString_InvalidArgument_CryptographicError,Microsoft.PowerShell.Commands.ConvertToSecureStringCommand

Note: Initially i thought that I could make this more secure by providing an additional security key something like this:

ConvertFrom-SecureString
1
ConvertFrom-SecureString -Key ([System.Text.Encoding]::ASCII.GetBytes("0e5c436f3993432590f12c0b2a48dafa"))

The Key i would use I would simply generate as follows:

NewGuid
1
[System.Guid]::NewGuid().ToString().Replace('-', '')
Output
0e5c436f3993432590f12c0b2a48dafa
PS C:\>

But this actually circumvents the Data Protection API. This page has a good explanation and more information.

So which way is more secure? Well that depends on your needs and environment but I think when the key is in your script file it is easier for an attacker to copy the file and script and then decrypt the information. So as always it is a good idea to apply as many security features as possible and keep the file safe and private, apply permissions and possibly use a different or lower access account.

The simplest way to save the credential to a file would be as follows:

Get-Credential
1
(Get-Credential -Credential "user").Password | ConvertFrom-SecureString | Out-File c:\temp\mycredential.cred
c:\temp\mycredential.cred
01000000d08c9ddf0115d1118c7a00c04fc297eb0100000021edac733db042498f873cbbdc18c766...
And we could save this to a file. However when running this same script say off a flash drive could be a problem when you create the encrypted file and load it again as they will be different for each logged on session. So I thought to create a reusable function that rather takes a store folder and saves a different file for each users unique account which is done by getting the AccountDomainSid:
GetFile
1
2
3
4
5
function GetFile($storeFolder)
{
     return [System.IO.Path]::Combine($storeFolder,
        [System.Security.Principal.WindowsIdentity]::GetCurrent().User.AccountDomainSid.ToString())
}
CreatePassword
1
2
3
4
function CreatePassword($storeFolder)
{
    (Get-Credential -Credential "user").Password | ConvertFrom-SecureString | Out-File (GetFile $storeFolder)
}

And use it:

Output
PS C:\> CreatePassword c:\temp

Creates a file S-X-X-XX-XXXXXXX-XXXXXXX-XXXXXXXXX

c:\temp\S-X-X-XX-XXXXXXX-XXXXXXX-XXXXXXXXX
01000000d08c9ddf0115d1118c7a00c04fc297eb0100000064ff74aec2ba6d428b22782a...

Reading the password:

GetPassword
1
2
3
4
function GetPassword($storeFolder)
{
    return cat ((GetFile $storeFolder)) | ConvertTo-SecureString
}

And use it:

Output
PS C:\> GetPassword c:\temp
System.Security.SecureString

Now as you can see we can't actually see the password because it is securely stored in the System.Security.SecureString But we can use this object to create Credentials.

Creating a credential:

ReadCredential
1
2
3
4
function ReadCredential($userName, $storeFolder)
{
    return new-object -typename System.Management.Automation.PSCredential -argumentlist $userName, (GetPassword $storeFolder)
}

And use it:

Output
PS C:\> ReadCredential "DOMAIN\user" "C:\temp"
 
UserName       Password
--------       --------
DOMAIN\user    System.Security.SecureString
 
PS C:\>

Putting it all together

As always you can create a reusable module from the functions above and use it whenever you need to save or load credentials. Just pick a filename such as CredentialFunctions.psm1 and then import it using:
Import-Module
1
Import-Module  -Name "c:\PowerShell\CredentialFunctions.psm1"

And now you can simply use this in credential functions such as Send-MailMessage

Send-MailMessage
1
Send-MailMessage -SmtpServer "mail.yourserver.com" -Subject "Error" -From "user@domain.com" -To "user@domain.com" -Body "test" -Credential (ReadCredential "DOMAIN\user" "C:\temp")

Powershell has a number of functions that may require Credentials and you could use this to do things like start processes etc.

Final note:

Even though the password is encrypted securely and with a key don't leave the file lying around and still protect it for instance on a flash disk. An attacker could still having access to your machine and your script key decrypt it. Lastly don't abuse the SMTP server.

Wednesday, 18 July 2012

MVC 4.0 on IIS 7, 404, 403 errors.

It seems that every time I deploy a new site to IIS there is some problem this time it is an MVC 4.0 App to a machine that does not have visual studio or ASP.NET MVC Installed.

First I got error 404 not found, so I tried a few things that didn’t work including the usual suspects

Registering .NET 4.0 with IIS (which it wasn’t):

image

Then I started to get 403 – Forbidden.

In the end enabling the HTTP Redirection fixed it:

image

This post was particularly useful and contained the answer.

Saturday, 7 July 2012

PowerShell–Zip

Whenever writing automation scripts you often need to zip some files, either to upload them or just for back-up purposes.

I wanted a really simple and easy way I can do this repeatedly so I thought of creating a PowerShell script that I can reuse to do this easily. I thought that PowerShell's pipeline would be a perfect tool for this. So I wanted to use the dir command with all its powerful filters etc and then just pipe this out to a zip file something like this:

PowerShell Zip Usage
1
2
3
4
5
6
Push-Location c:\temp
# create new zip file
dir -Recurse c:\temp\stufftoZip1 | ToZip -relativeBaseDirectory (Get-Location).Path  -fileName "c:\temp\test.zip"
# append to zip file
dir -Recurse c:\temp\stufftoZip2 | ToZip -relativeBaseDirectory (Get-Location).Path -appendToZip -fileName "c:\temp\test.zip"
Pop-Location

And that is all you need to make use of it. You can use all the query operators like select, where, select, first, etc.

This also introduces the concept of Pipeline functions.

Pipeline

Each pipeline function has a begin, process and end. In the begin block is where we initialize our function. In the process block this is where you handle each item from the pipeline. The end block is where we clean-up like close files etc.

PowerShell Pipeline function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function ToZip($fileName, $relativeBaseDirectory=$null, [switch] $appendToZip=$false, $verbose=$true)
{
    begin
    {      
        # initialization
        $zipFile = [System.IO.Packaging.Package]::Open($fileName, $mode)
    }
    process
    {      
        # $_.FullName is the current item (in this case file) in the pipeline.
    }
    end
    {      
        # finalization
        $zipFile.Close();
    }
}

Implementation

In order to use the System.IO.Packaging.Package namespace and create Zip files you have to import the assembly WindowsBase as follows:

1
[Reflection.Assembly]::LoadWithPartialName("WindowsBase") | Out-Null

Save the following to a file and call it Zip.psm1

Zip.psm1
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
Set-StrictMode -V 1.0
[Reflection.Assembly]::LoadWithPartialName("WindowsBase") | Out-Null
function ToZip($fileName, $relativeBaseDirectory=$null, [switch] $appendToZip=$false, $verbose=$true)
{
    begin
    {
        $zipCreated = { (Get-Variable -ErrorAction SilentlyContinue -Name zipFile) -ne $null }
        $logMessage = {
            param ($message)
            if ($verbose)
            {
                Write-Host  $message
            }
        }
        $mode = [System.IO.FileMode]::Create
        if ($appendToZip)
        {
            $mode = [System.IO.FileMode]::Open
        }
        $zipFile = [System.IO.Packaging.Package]::Open($fileName, $mode)
    }
    process
    {
        if  ((&$zipCreated) -and ([System.IO.File]::Exists($_.FullName) -eq $true))
        {
             
            $zipFileName = $_.FullName
            if ($relativeBaseDirectory -ne $null)          
            {
                #$directoryName = [System.IO.Path]::GetDirectoryName($_.FullName)
                $zipFileName = $_.FullName.SubString($relativeBaseDirectory.Length, $_.FullName.Length-$relativeBaseDirectory.Length)              
            }
             
             
            $destFilename = [System.IO.Path]::Combine(".\\", $zipFileName)
            #$destFilename = $destFilename.Replace(" ", "_")
            $uri = New-Object Uri -ArgumentList ($destFilename, [UriKind]::Relative)
            $uri = [System.IO.Packaging.PackUriHelper]::CreatePartUri($uri)
             
            &$logMessage ("Adding: {0}" -f $destFileName)
             
            if ($zipFile.PartExists($uri))
            {
                $zipFile.DeletePart($uri);
            }
             
            $part = $zipFile.CreatePart($uri, [string]::Empty, [System.IO.Packaging.CompressionOption]::Normal)
            $dest = $part.GetStream()
             
            $srcStream = New-Object System.IO.FileStream -ArgumentList ($_.FullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
            try
            {
                $srcStream.CopyTo($dest)
            }
            finally
            {
                $srcStream.Close()
            }          
        }
    }
    end
    {
        if  (&$zipCreated)
        {
            $zipFile.Close()
        }
        &$logMessage "Done"
    }
}

Import & Usage

You can easily reuse the above module anywhere in your PowerShell scripts by importing it as follows:

PowerShell Zip Usage
1
2
3
4
5
6
7
8
9
10
Import-Module c:\Development\PowerShell\Zip.psm1
 
# Example 1
dir | ToZip c:\temp\test.zip
 
# Example 2
dir | ToZip -fileName c:\temp\test.zip -relativeBaseDirectory (Get-Location).Path
 
# Example 3
dir | ToZip -fileName c:\temp\test.zip -relativeBaseDirectory (Get-Location).Path -appendToZip

Example 1

Zip all the content in the current directory to c:\temp\test.zip

Example 2

Zip all the content in the current directory to c:\temp\test.zip. Using the folder structure starting at the current directory as relative path.

Example 3

Zip all the content in the current directory to c:\temp\test.zip. Using the folder structure starting at the current directory as relative path, and appending to the Zip.

Caveats

Actually there are none from PowerShell side, however the build in .net packaging library places a [Content_Types].xml in the zip file. You can't get rid of this. Also files with spaces will be escaped. This obviously can affect anything you zip and unzip where the file names are required. But again this is not PowerShell but the packaging library.

I will try and make a future post on a version that uses SharpZipLib so stay tuned...