2011-07-17 8 views
5

C#プロジェクトでRtlGetCompressionWorkSpaceSize関数とRtlCompressBuffer関数を使用しようとしています。C#のRtlCompressBuffer API

class Program 
{ 
    const uint COMPRESSION_FORMAT_LZNT1 = 2; 
    const uint COMPRESSION_ENGINE_MAXIMUM = 0x100; 

    [DllImport("ntdll.dll")] 
    static extern uint RtlGetCompressionWorkSpaceSize(uint CompressionFormat, out uint pNeededBufferSize, out uint Unknown); 

    [DllImport("ntdll.dll")] 
    static extern uint RtlCompressBuffer(uint CompressionFormat, byte[] SourceBuffer, uint SourceBufferLength, out byte[] DestinationBuffer, 
     uint DestinationBufferLength, uint Unknown, out uint pDestinationSize, IntPtr WorkspaceBuffer); 

    static void Main(string[] args) 
    { 
     uint dwSize = 0; 
     uint dwRet = 0; 
     uint ret = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, out dwSize, out dwRet); 

     IntPtr pMem = Marshal.AllocHGlobal((int)dwSize); 
     byte[] buffer = new byte[1024]; 
     byte[] outBuf = new byte[1024]; 
     uint destSize = 0; 
     ret = RtlCompressBuffer(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, buffer, 1024, out outBuf, 1024, 0, out destSize, pMem); 

     Console.Write(ret.ToString()); 
     Console.Read(); 
    } 
} 

それが0(NTの成功コード)を返すので、RtlGetCompressionWorkSpaceSizeは動作しますが、私はRtlCompressBufferを呼び出すとき、私はメモリアクセス違反のエラーが表示されます。ここでは

は、私がこれまで持っているものです。

編集:Davidの回答の助けを借りて問題を修正し、正しいコードを以下に示します。

const ushort COMPRESSION_FORMAT_LZNT1 = 2; 
    const ushort COMPRESSION_ENGINE_MAXIMUM = 0x100; 

    [DllImport("ntdll.dll")] 
    static extern uint RtlGetCompressionWorkSpaceSize(ushort CompressionFormat, out uint pNeededBufferSize, out uint Unknown); 

    [DllImport("ntdll.dll")] 
    static extern uint RtlCompressBuffer(ushort CompressionFormat, byte[] SourceBuffer, int SourceBufferLength, byte[] DestinationBuffer, 
     int DestinationBufferLength, uint Unknown, out int pDestinationSize, IntPtr WorkspaceBuffer); 

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    internal static extern IntPtr LocalAlloc(int uFlags, IntPtr sizetdwBytes); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    static extern IntPtr LocalFree(IntPtr hMem); 

    internal static byte[] Compress(byte[] buffer) 
    { 
     var outBuf = new byte[buffer.Length * 6]; 
     uint dwSize = 0, dwRet = 0; 
     uint ret = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, out dwSize, out dwRet); 
     if (ret != 0) 
     { 
      return null; 
     } 

     int dstSize = 0; 
     IntPtr hWork = LocalAlloc(0, new IntPtr(dwSize)); 
     ret = RtlCompressBuffer(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, buffer, 
      buffer.Length, outBuf, outBuf.Length, 0, out dstSize, hWork); 
     if (ret != 0) 
     { 
      return null; 
     } 

     LocalFree(hWork); 

     Array.Resize(ref outBuf, dstSize); 
     return outBuf; 
    } 

答えて

5

あなたは非常に近くです。問題は、あなたのPのこの部分である/ RtlCompressBufferために呼び出す:関数が戻るとき、配列の内容は、その後再び非管理対象に管理さから、両方向に整列化、およびするため

out byte[] DestinationBuffer 

byte[]のためのマーシャリングデフォルトです。 RtlCompressBufferのCの定義には__outと注釈が付けられていますが、これはポインタが__outではなく、配列の内容が__outであることを意味します。

outBufから RtlCompressBuffer変更 out outBufへの呼び出しで

byte[] DestinationBuffer 

と同様に起動/あなたのPを変更

、あなたが行くように良いことがあります。

あなたのコードは、ステータスコードSTATUS_BUFFER_ALL_ZEROSを返しますので、このゼロ以外の戻り値が失敗を示していると思うことに騙されないように注意してください。

最後に、P/invokeの両方の最初のパラメータCompressionFormatは、ushortと宣言される必要があります。