【问题标题】:Implementing Win32 FileWrite实现 Win32 FileWrite
【发布时间】:2010-12-12 07:19:09
【问题描述】:
[DllImport("kernel32.dll", SetLastError=true)]
    public static extern unsafe bool WriteFile(IntPtr hFile, void* lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped);

我正在通过带有签名的 Write(..) 方法来实现这一点:

Write(IntPtr handleFile, void* bufferData, uint length){
    void* buffer = bufferData
    while (length > 0)
    {
      uint wrtn;
      if (!WriteFile(handle, buffer, len, out wrtn, IntPtr.Zero))
      {
         // Do some error handling
      }
      // THIS DOESNT WORK!
      // I want to move along the buffer to be able to write its remainder...
      // I tried many variations of this as well, but it seems even '+' is not valid  for a void*
      buffer += wrtn;
      len -= wrtn;
    }
}

正如我通过查看 this (the use of the read counterpart is discussed) 了解到的,我需要在我的代码中实现一个 while 循环,因为缓冲区的写入/读取可能不会一次性完成。这就是问题的开始:

如果我想保留我的 C# 方法签名以接受 void*,这与链接读取示例不同,其中字节* 被接受为缓冲区的参数。

这意味着在 WriteFile 一次通过后,我应该将我的 void* 移动到尚未写入的缓冲区的开头。我显然不能通过仅使用包含写入字节数的 uint 来增加 void* 来做到这一点......我知道 void* 没有预定的大小,因此增加是不可能的,但我想知道我应该如何实现我正在努力。

【问题讨论】:

  • 当您可以使用 System.IO 命名空间提供的内容时,您还有什么理由使用 Win32 api 不安全代码?
  • 是的,扩展了我对本地内容/互操作的了解,并了解了 .net 通常对我们隐藏的内容。我从来没有被教过我所谓的“基本”东西(指针,...),我非常想学习。
  • @Kris:如果你想了解指针和低级的东西,我会使用低级语言(C、C++、(QT、wxWidgets)),而不是尝试学习高级到低级语言。
  • ..除非他想了解 .net 上下文中的指针和内存管理内部结构。我认为这样的练习很有用,即使只是为了指出为什么你不应该在生产代码中做这样的事情。
  • 你可能不想在生产代码中做这件事,但我不喜欢 .NET 的一些事情:省略最新的文件打开/保存对话框,例如IFileDialog 公开的只是一个例子。尝试在您的高级语言项目中获得类似的东西(高级语言适用于您可能想做的 99.9% 的事情)需要了解此类内容......

标签: c# winapi void-pointers pointer-arithmetic


【解决方案1】:

您应该能够将buffer 转换为byte*,然后将其递增。一个 void 指针没有与之关联的大小,所以如果你想 move 它在任何方向上一定数量的字节,你可以将它转换为不同类型的指针(任何类型)然后在指针算术中使用转换类型的大小,如下所示:

buffer = (void *)((byte*)buffer + wrtn);

上面的行将buffer 转换为字节指针,然后将其位置增加wrtn 字节数,然后将新指针转换回void*。当然,如果您想执行任意指针运算,则转换为 byte* 是显而易见的选择。

另一种可能性是将buffer 一直视为byte*,并且仅在将其传递给WriteFile 时将其转换为void*

Write(IntPtr handleFile, void* bufferData, uint length)
{
    byte* buffer = (byte*)bufferData;
    while (length > 0)
    {
      uint wrtn;
      if (!WriteFile(handle, (void*)buffer, len, out wrtn, IntPtr.Zero))
      {
         // Do some error handling
      }
      buffer += wrtn;
      len -= wrtn;
    }
}

最后,我会考虑将Write 的签名完全更改为使用byte* 而不是void*,因为它会使其与来自C# 的其他调用者更兼容,而byte* 使得在这种情况下更有意义。您不必担心使其与 WriteFile 本机 API 的签名相匹配,因为您可以在传递 byte* 时将其转换为 void*

Write(IntPtr handleFile, byte* bufferData, uint length)
{
    while (length > 0)
    {
      uint wrtn;
      if (!WriteFile(handle, (void*)bufferData, len, out wrtn, IntPtr.Zero))
      {
         // Do some error handling
      }
      bufferData+= wrtn;
      len -= wrtn;
    }
}

唉,我不得不同意其中一位评论者的观点。你为什么做这个?那里 是使用许多面向流的类在 c# 中完成文件写入的更好方法。

【讨论】:

  • 我想我可以接受你的回答。当然,使用预先准备好的 API 更容易,我可能最终会这样做,但我时不时地喜欢学习新东西。我一直有一种强烈的冲动,想要真正理解事物,而不是一直使用“黑匣子”。我时不时地在网上搜索一些代码,或者我查看一个程序集以了解“专业人士”是如何完成的,然后我尝试实现我自己的版本。这就是其中之一......
  • 那么我希望我的解释能帮助你更好地理解@Kris。但是,在发布任何从 C# 执行指针操作的代码之前,请确保您阅读了有关指针和 .NET 托管内存模型的更多信息。如果处理不当,可能会导致许多令人讨厌的问题!
  • 继续这个:我尝试了你的建议并且它有效。我没有得到的一件事是编译器吐出的代码......类似于 buffer = buffer + wrtn;其中 buffer 是 byte* 类型, wrtn 是 uint 被编译成 buffer += (byte*)wrtn; (.NET 反射器)。然而,最后一行永远不会编译。怎么会这样?知道这一点,像 bufferv += (void*) wrtn; 这样的东西怎么可能? (作为反射器的输出)最初已经被编码(知道 void* 算术显然是不可能的......
  • @Kris,这本身当然是一个有趣的问题,虽然我不知道答案,但也许您应该将其作为一个单独的问题发布,并附上指向该问题的链接。我敢肯定那里有一个解释..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-26
  • 2013-07-22
  • 1970-01-01
  • 1970-01-01
  • 2020-07-19
  • 1970-01-01
相关资源
最近更新 更多