2011-07-17 17 views
5

Estoy tratando de usar las funciones RtlGetCompressionWorkSpaceSize y RtlCompressBuffer en un proyecto de C#.RtlCompressBuffer API en C#

Esto es lo que tengo hasta ahora:

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(); 
    } 
} 

RtlGetCompressionWorkSpaceSize funciona ya que devuelve 0 (código de éxito NT), pero cuando llamo RtlCompressBuffer me sale un error de memoria de acceso Violación.

EDITAR: Con la ayuda de la respuesta de David he solucionado el problema y el código correcto está a continuación.

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; 
    } 

Respuesta

5

Eres muy cerca de allí. El problema es que esta parte de su P/Invoke para RtlCompressBuffer:

out byte[] DestinationBuffer 

el de clasificación por defecto para byte[] es para contenido de la matriz a marshalled en ambas direcciones, desde conseguido no administrado, y luego de vuelta cuando se devuelve la función. La definición de C de RtlCompressBuffer está anotada con __out pero eso significa que los contenidos de la matriz son __out en lugar de que el puntero sea __out.

Cambiar la P/invocar a

byte[] DestinationBuffer 

y de manera similar en la llamada a RtlCompressBuffer cambio out outBuf a outBuf y usted debe ser bueno para ir.

Tenga en cuenta que su código en su forma actual devolverá un código de estado de STATUS_BUFFER_ALL_ZEROS así que no se deje engañar por pensar que este valor de retorno distinto de cero indica falla.

Un último punto, el primer parámetro para ambas P/invocaciones, CompressionFormat, debe declararse como ushort.