2009-12-24 1 views
3

J'essaie d'utiliser System.Runtime.InteropServices.ComTypes.IStream de C#, mais j'ai des problèmes. Selon MSDN, la définition C# ressemble à ceci:C# et IStream.Read

void Read(
    byte[] pv, 
    int cb, 
    IntPtr pcbRead 
) 

Fondamentalement, je peux lire les données du flux, mais la valeur « pcbRead » ci-dessus est toujours « 0 » (même si le tableau d'octets contient mes données) . En faisant un peu de lecture, il semble que l'argument pcbRead soit difficile à configurer correctement (bien que je sois relativement nouveau en C#).

Quoi qu'il en soit, mon code ressemble fondamentalement ceci:

myPtr = (IntPtr)0; 
int buffSize = 8192; 
byte[] buffer = new byte[buffSize]; 
while (true) 
{ 
    strm.Read(buffer, buffSize, myPtr); 
    fs.Write(buffer, 0, myPtr.ToInt32()); 
    if (myPtr.ToInt32() < buffSize) break; 
} 

Encore une fois, le problème est que « myPtr » contient toujours « 0 » après la lecture, bien que « tampon » semble contenir des données valides.

Répondre

6

Vous êtes censé transmettre un pointeur pour cet argument. La fonction IStream :: Read() écrira le nombre d'octets réellement lus dans l'emplacement pointé. Cela nécessite un code dangereux en C#, par exemple:

unsafe static int Read(System.Runtime.InteropServices.ComTypes.IStream strm, 
    byte[] buffer) { 
    int bytesRead = 0; 
    int* ptr = &bytesRead; 
    strm.Read(buffer, buffer.Length, (IntPtr)ptr); 
    return bytesRead; 
} 

Faire sans le mot clé unsafe est possible aussi:

private static IntPtr ReadBuffer; 

static int Read(System.Runtime.InteropServices.ComTypes.IStream strm, 
    byte[] buffer) { 
    if (ReadBuffer == IntPtr.Zero) ReadBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int))); 
    strm.Read(buffer, buffer.Length, ReadBuffer); 
    return Marshal.ReadInt32(ReadBuffer); 
} 

Si vous utilisez cette méthode que de temps en temps vous devez utiliser Marshal.CoTaskMemFree() libérer la mémoire.

+0

Merci nobugz - vos suggestions fonctionnent parfaitement. –

0

Je ne suis pas expérimenté avec IStream, mais en regardant votre code, je vois une erreur potentielle.
La variable myPtr est définie sur zéro au début. IntPtr fonctionne comme des pointeurs en C++, donc je pense que cette méthode s'attend à écrire de la valeur dans l'emplacement que myPtr pointe.

Pouvez-vous essayer de le faire?

unsafe 
{ 
    int pcbRead = 0; 
    int buffSize = 8192; 
    byte[] buffer = new byte[buffSize]; 
    while (true) 
    { 
     // taking address of pcbRead 
     strm.Read(buffer, buffSize, new IntPtr(&pcbRead)); 
     fs.Write(buffer, 0, pcbRead); 
     if (pcbRead < buffSize) break; 
    } 
} 
1

Voici une solution qui se base sur la réponse de Hans pour vous donner une classe que vous pouvez simplement laisser tomber dans votre projet. Il fournit une méthode d'extension pour tous les objets IStream.

Cela va transférer les données dans un flux de mémoire .net, mais vous pouvez changer cela pour être un fichier si vous préférez.

using System.IO; 
using System.Runtime.InteropServices; 
using System.Runtime.InteropServices.ComTypes; 

namespace YourProject 
{ 
    public static class IStreamExtensions 
    { 
    private const int bufferSize = 8192; 
    public static MemoryStream ReadToMemoryStream(this IStream comStream) 
    { 
     var memoryStream = new MemoryStream(); 

     var amtRead = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int))); 
     Marshal.WriteInt32(amtRead, bufferSize); 
     var buffer = new byte[bufferSize]; 
     while (Marshal.ReadInt32(amtRead) > 0) 
     { 
     comStream.Read(buffer, buffer.Length, amtRead); 
     memoryStream.Write(buffer, 0, Marshal.ReadInt32(amtRead)); 
     } 
     memoryStream.Position = 0; 

     return memoryStream; 
    } 
    } 
} 

utilisation:

IStream istream = (IStream) someCOMclass.giveMeAStream(); 
MemoryStream netStream = istream.ReadToMemoryStream();