提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档Windows
前言
许多应用程序都需要使用无损的数据压缩和解压缩。 Windows压缩 API 通过公共 API 公开 Windows 压缩算法来简化此功能。C#可以调用这些API实现流和文件的加压和解压。不支持Windows XP/Windows 7, 支持Windows 8/10/11.
一、支持算法如下:
XPRESS
XPRESS Huffman
MSZIP
LZMS
主要函数:
BOOL CreateCompressor(
[in] DWORD Algorithm,
[in, optional] PCOMPRESS_ALLOCATION_ROUTINES AllocationRoutines,
[out] PCOMPRESSOR_HANDLE CompressorHandle
);
BOOL Compress(
[in] COMPRESSOR_HANDLE CompressorHandle,
[in] LPCVOID UncompressedData,
[in] SIZE_T UncompressedDataSize,
[out] PVOID CompressedBuffer,
[in] SIZE_T CompressedBufferSize,
[out] PSIZE_T CompressedDataSize
);
BOOL CloseCompressor(
[in] COMPRESSOR_HANDLE CompressorHandle
);
其他详情参阅微软:http://msdn.microsoft.com/en-us/library/hh968104(v=vs.85).aspx
二、C# 实现代码
1.压缩
int compressXpress(byte[] InputBuffer, int size, ref IntPtr CompressedBuffer, out string err)
// CompressedBuffer需要提前分配内存空间(非托管), 可为size的两倍。
{
System.IntPtr Compressor = System.IntPtr.Zero;
err = "";
try
{
unsafe
{
System.Int32 Success;
System.UInt32 CompressedBufferSize;
System.UInt32 CompressedDataSize = 0;
// Create an XpressHuff compressor.
Success = CreateCompressor(
COMPRESS_ALGORITHM_XPRESS, // Compression Algorithm
System.IntPtr.Zero,
out Compressor);
if (Success == 0)
{
err = "Create fail! ";
int errCode = Marshal.GetLastWin32Error();
err += GetSysErrMsg(errCode);
return -2;
}
// byte* memBytePtr = (byte*)CompressedBuffer.ToPointer();
// for (int i = 0; i < 100; i++) testBytes[i] = memBytePtr[i];
// Query compressed buffer size.
Success = Compress(
Compressor, // Compressor Handle
InputBuffer, // Input buffer, Uncompressed data
(uint)size, // Uncompressed data size
CompressedBuffer, // Compressed Buffer
0, // Compressed Buffer size
out CompressedBufferSize); // Compressed Data size
if (CompressedBufferSize == 0)
{
err = "Query fail! BufferSize = 0";
return -3;
}
// Call Compress() again to do real compression and output the compressed
// data to CompressedBuffer.
Success = Compress(
Compressor, // Compressor Handle
InputBuffer, // Input buffer, Uncompressed data
(uint)size, // Uncompressed data size
CompressedBuffer, // Compressed Buffer
CompressedBufferSize, // Compressed Buffer size
out CompressedDataSize); // Compressed Data size
if (Success == 0)
{
err = "Execution fail! ";
int errCode = Marshal.GetLastWin32Error();
err += GetSysErrMsg(errCode);
return -4;
}
//memBytePtr = (byte*)CompressedBuffer.ToPointer();
//for (int i = 0; i < 100; i++) testBytes[i] = memBytePtr[i];
return (int)CompressedDataSize;
}
}
catch (Exception ex)
{
err = "Function exception: " + ex.Message;
return -1;
}
finally
{
try
{
if (Compressor != System.IntPtr.Zero)
{
CloseCompressor(Compressor);
}
}
catch { }
}
}
2.解压缩
int decompressXpress(byte[] InputBuffer, int size, out byte[] outdata, out string err)
{
System.IntPtr deCompressor = System.IntPtr.Zero;
IntPtr deCompressedBuffer = System.IntPtr.Zero;
err = "";
outdata = new byte[0];
if (InputBuffer.Length == 0) return 0;
try
{
unsafe
{
System.Int32 Success;
System.UInt32 deCompressedBufferSize;
System.UInt32 deCompressedDataSize = 0;
// Create an XpressHuff compressor.
Success = CreateDecompressor(
COMPRESS_ALGORITHM_XPRESS, // Compression Algorithm
System.IntPtr.Zero,
out deCompressor);
if (Success == 0)
{
err = "Create fail! ";
int errCode = Marshal.GetLastWin32Error();
err += GetSysErrMsg(errCode);
return -2;
}
// byte* memBytePtr = (byte*)CompressedBuffer.ToPointer();
// for (int i = 0; i < 100; i++) testBytes[i] = memBytePtr[i];
// Query compressed buffer size.
Success = Decompress(
deCompressor, // Compressor Handle
InputBuffer, // Input buffer, Uncompressed data
(uint)size, // Uncompressed data size
deCompressedBuffer, // Compressed Buffer
0, // Compressed Buffer size
out deCompressedBufferSize); // Compressed Data size
if (deCompressedBufferSize == 0)
{
err = "Query fail! BufferSize = 0";
return -3;
}
/ 分配内存
try
{
deCompressedBuffer = System.Runtime.InteropServices.Marshal.AllocHGlobal((int)deCompressedBufferSize);
}
catch (Exception ex)
{
this.Invoke(new EventHandler(delegate
{
MessageBox.Show(deCompressedBufferSize + " - AllocHGlobal失败:" + ex.Message);
throw new Exception(ex.Message);
}));
}
// Call Compress() again to do real compression and output the compressed
// data to CompressedBuffer.
Success = Decompress(
deCompressor, // Compressor Handle
InputBuffer, // Input buffer, Uncompressed data
(uint)size, // Uncompressed data size
deCompressedBuffer, // Compressed Buffer
deCompressedBufferSize, // Compressed Buffer size
out deCompressedDataSize); // Compressed Data size
if (Success == 0 || deCompressedDataSize == 0)
{
err = "Execution fail! ";
if (Success == 0)
{
int errCode = Marshal.GetLastWin32Error();
err += GetSysErrMsg(errCode);
}
else
{
err += "解压返回数据长度为0.";
}
return -4;
}
outdata = new byte[deCompressedDataSize];
byte* memBytePtr = (byte*)deCompressedBuffer.ToPointer();
for (int i = 0; i < deCompressedDataSize; i++) outdata[i] = memBytePtr[i];
return (int)deCompressedDataSize;
}
}
catch (Exception ex)
{
err = "Function exception: " + ex.Message;
return -1;
}
finally
{
try
{
unsafe // free mem
{
if (deCompressedBuffer != null && deCompressedBuffer != IntPtr.Zero) System.Runtime.InteropServices.Marshal.FreeHGlobal(deCompressedBuffer);
}
}
catch { }
try
{
if (deCompressor != System.IntPtr.Zero)
{
CloseDecompressor(deCompressor);
}
}
catch { }
}
}
3.取错误代码描述
[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
public extern static int FormatMessage(int flag, ref IntPtr source, int msgid, int langid, ref string buf, int size, ref IntPtr args);
/// <summary>
/// 获取系统错误信息描述
/// </summary>
/// <param name="errCode">系统错误码</param>
/// <returns></returns>
public static string GetSysErrMsg(int errCode)
{
IntPtr tempptr = IntPtr.Zero;
string msg = null;
FormatMessage(0x1300, ref tempptr, errCode, 0, ref msg, 255, ref tempptr);
return msg;
}
4.函数c#说明
/*
#define COMPRESS_ALGORITHM_INVALID 0
#define COMPRESS_ALGORITHM_NULL 1
#define COMPRESS_ALGORITHM_MSZIP 2
#define COMPRESS_ALGORITHM_XPRESS 3
#define COMPRESS_ALGORITHM_XPRESS_HUFF 4
#define COMPRESS_ALGORITHM_LZMS 5
#define COMPRESS_ALGORITHM_MAX 6
*/
const int COMPRESS_ALGORITHM_XPRESS = 3; //COMPRESS_ALGORITHM_XPRESS_HUFF
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate System.UInt32 PFN_COMPRESS_ALLOCATE(System.UInt32 UserContext, System.UInt32 Size);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void PFN_COMPRESS_FREE(System.UInt32 UserContext, System.UInt32 Memory);
[StructLayout(LayoutKind.Sequential)]
public struct COMPRESS_ALLOCATION_ROUTINES
{
public PFN_COMPRESS_ALLOCATE Allocate;
public PFN_COMPRESS_FREE Free;
public System.UInt32 UserContext;
}
[DllImport("Cabinet.dll", SetLastError = true)]
public static extern System.Int32 CreateCompressor(
System.UInt32 Algorithm,
System.IntPtr AllocationRoutines,
out System.IntPtr CompressorHandle
);
[DllImport("Cabinet.dll", SetLastError = true)]
public static extern System.Int32 CreateDecompressor(
System.UInt32 Algorithm,
System.IntPtr AllocationRoutines,
out System.IntPtr DecompressorHandle
);
[DllImport("Cabinet.dll", SetLastError = true)]
public static extern System.Int32 Compress(
System.IntPtr CompressorHandle,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]byte[] UncompressedData,
System.UInt32 UncompressedDataSize,
IntPtr CompressedBuffer,
System.UInt32 CompressedBufferSize,
out System.UInt32 CompressedDataSize
);
[DllImport("Cabinet.dll", SetLastError = true)]
public static extern System.Int32 Decompress(
System.IntPtr DecompressorHandle,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]byte[] CompressedData,
System.UInt32 CompressedDataSize,
IntPtr UncompressedBuffer,
System.UInt32 UncompressedBufferSize,
out System.UInt32 UncompressedDataSize
);
[DllImport("Cabinet.dll", SetLastError = true)]
public static extern System.Int32 CloseCompressor(
System.IntPtr CompressorHandle
);
[DllImport("Cabinet.dll", SetLastError = true)]
public static extern System.Int32 CloseDecompressor(
System.IntPtr DecompressorHandle
);
总结
Xpress压缩率和Windows的NTFS文件压缩率差不多, 速度很快, 我的机器上有300MB/s,
XPRESS Huffman压缩率要高10%的样子,速度大概150MB/s.
C#也有包装好的GZIP压缩类,压缩率稍微高于XPRESS Huffman(3%-5%),速度较慢,我的机器上快速模式大概60MB/s.