Wednesday, September 12, 2007

Working With LZ-Compressed Files in .NET

I’m needing to deal with some files compressed with the COMPRESS.EXE utility which packs targeted files in the Lev-Zimpel algorithm via the lz32.dll system library.  That means playing with PInvoke which is something I’d rather not do since it’s generally a huge PITA. 

To uncompress files you’ll need to open the zipped file, copy it to another file (expanding it enroute), then close your source and target files.  Three different calls from lz32.dll: LZOpenFile, LZCopy, and LZClose.  PInvoke means you need to reference those calls with DllImport statements:

[DllImport("lz32.dll", EntryPoint = "LZOpenFile", SetLastError = true,

    CharSet = CharSet.Ansi)]

public static extern int LZOpenFileA(

    string lpszFile,

    [Out, MarshalAs(UnmanagedType.LPStruct)] OFSTRUCT lpOf,

    int mode);

 

[DllImport("lz32.dll")]

public static extern int LZCopy(int hfSource, int hfDest);

 

[DllImport("lz32.dll")]

public static extern int LZClose(int hfFile);

A couple things about the first call, LZOpenFileA: The CharSet declaration makes sure that the unmanaged code gets things correctly marshalled from the CLR’s native Unicode over to the unmanaged world’s ANSI.  Secondly, the OFSTRUCT structure in the Win32 world needs defining as a .NET class in our world:

[StructLayout(LayoutKind.Sequential)]

public class OFSTRUCT

{

    public const int OFS_MAXPATHNAME = 128;

    public byte cBytes;

    public byte fFixedDisc;

    public UInt16 nErrCode;

    public UInt16 Reserved1;

    public UInt16 Reserved2;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=OFS_MAXPATHNAME)]

    public string szPathName;

}

The string szPathName gets some MarshalAs love to deal with the transition from managed to unmanaged code.

Finally, we’ll set a couple constants for the mode we’re opening the files in:

private const int OF_READ = 0x0;

private const int OF_CREATE = 0x1000;

Now the code to actually deal with the calls:

public static string ExpandLZCompressedToTempFile(FileInfo file)

{

    OFSTRUCT sourceStruct = new OFSTRUCT();

 

    int handleSource = 0;

    int handleTarget = 0;

    string tmpFile = "";

 

    handleSource = LZOpenFileA(file.FullName, sourceStruct, OF_READ);

    OFSTRUCT targetStruct = new OFSTRUCT();

    tmpFile = FileSystem.GetTempFileName();

    handleTarget = LZOpenFileA(tmpFile, targetStruct, OF_CREATE);

 

    LZCopy(handleSource, handleTarget);

 

    LZClose(handleSource);

    LZClose(handleTarget);

 

    return tmpFile;

}

I’ll use another method to read the expanded file and stream that into a GZip-compressed format since that’s supported in .NET’s managed world.

Check out Natty Gur’s article on Code Project if you’re interested in some very, very deep PInvoke details.  His article solved a couple issues for me, but is written about sharing and caching data between processes using some interesting trickery. 

4 comments:

Hristo Deshev said...

Jim, I'm not sure if that's a pun on your end, but it is not Lev-Zimpel, but rather Lempel-Ziv:
LZW

Hristo

Jim Holmes said...

Bah!

No, no pun, just a silly error after a long day.

Thanks for the correction!

Monish Nagisetty said...

I really love your VS color scheme. I need to adopt this scheme when I get some time. I also ran into this other VS scheme from one of the fellow BizTalk bloggers. Take a look:
http://www.winterdom.com/weblog/2007/09/11/NightingaleAVS2005ColorScheme.aspx

Jim Holmes said...

@Monish: I use Scott Hansleman's VS settings which you can find at http://www.hanselman.com/blog/content/binary/HanselmanColorSettings.zip.

The Nightingale looks interesting, but I'm not sure if I can handle those colors or not. I'll give it a shot and see how it works!

Subscribe (RSS)

The Leadership Journey