我正在尝试使用 C# 中的 System.Runtime.InteropServices.ComTypes.IStream,但我遇到了一些麻烦。根据 MSDN,C# 的定义如下:
void Read(
byte[] pv,
int cb,
IntPtr pcbRead
)
基本上,我可以从流中读取数据,但上面的“pcbRead”值始终为“0”(即使字节数组包含我的数据)。读了一些书,似乎 pcbRead 参数的正确设置有点棘手(尽管我对 C# 还很陌生)。
不管怎样,我的代码基本上是这样的:
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;
}
同样,问题是“myPtr”在读取后仍然包含“0”,尽管“buffer”似乎包含有效数据。
您应该为该参数传递一个指针。
IStream::Read()
函数会将实际读取的字节数写入指向的位置。这需要 C# 中的不安全代码,例如:
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;
}
不使用 unsafe 关键字也是可以的:
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);
}
如果您只是偶尔使用此方法,则应该使用
Marshal.FreeCoTaskMem()
来释放内存。
这是一个基于汉斯答案的解决方案,为您提供一个可以直接放入项目中的课程。它为所有 IStream 对象提供了扩展方法。
这会将数据传输到 .net 内存流,但如果您愿意,您可以将其更改为文件。
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;
}
}
}
用途:
IStream istream = (IStream) someCOMclass.giveMeAStream();
MemoryStream netStream = istream.ReadToMemoryStream();
我对 IStream 没有经验,但查看你的代码我发现一些潜在的错误。
变量 myPtr 在开始时设置为零。 IntPtr 的工作方式类似于 C++ 中的指针,因此我认为该方法希望将值写入 myPtr 指向的位置。
你能尝试这样做吗?
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;
}
}