2009-11-02 69 views
0
[DllImport("kernel32.dll", SetLastError=true)] 
    public static extern unsafe bool WriteFile(IntPtr hFile, void* lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped); 

我通过写(..)方法的签名实现这个:实施的Win32 FILEWRITE

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 *,与链接的Read示例中的byte *被接受为缓冲区的参数不同。

这意味着在WriteFile的一次传递之后,我应该将void *移动到尚未写入的缓冲区的开始处。我不明白这是通过增加void *来保存写入字节数的uint来实现的......我明白void *没有预先确定的大小,因此增量是不可能的,但是我不知道我应该如何实现我正在努力。

+3

当您可以使用System.IO命名空间提供的内容时,是否有任何理由使用Win32 api不安全代码? – 2009-11-02 14:23:16

+0

是的,扩展我对原生东西/互操作的了解,并理解.net通常隐藏的东西。我从来没有被教过什么,我称之为“基本”的东西(指针,...),我非常想学习。 – Kris 2009-11-02 14:32:58

+0

@Kris:如果您想了解指针和低级别的东西,我会使用低级语言(C,C++,(QT,wxWidgets)),而不是尝试从高级到低级别语言。 – Bobby 2009-11-02 14:49:11

回答

1

你应该能够将buffer投到byte*然后增加它。一个void指针没有与它相关的大小,所以如果你想移动它在一定数量的字节在任何方向上,你可以将它转换为不同类型的指针(任何类型的指针),然后使用铸造在指针运算,像这样类型的规模:

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

的线的上方投射buffer到一个字节的指针,然后通过wrtn字节数递增其位置,然后注塑新指针回一个void *。当然,如果你想执行任意指针算术,铸造到byte*是一个明显的选择。

另一种可能性是治疗buffer作为byte*一直以来,只有将它转换为void*当你将它传递给WriteFile

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的签名,因为你可以投在传递时,它上面显示的void*byte*

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#中完成文件写入的更好方法。

+0

我想我可以和你的答案一起工作。当然,使用预先熟悉的API更容易,我可能会最终这样做,但我偶尔偶尔会学习新的东西。我一直强烈渴望真正理解事物,而不是一直使用“黑盒子”。我偶尔会在网上搜索一些代码,或者看一个程序集,看看“专业人员”是如何完成的,然后尝试实现我自己的版本。这是其中的一件... – Kris 2009-11-02 14:39:45

+0

然后我希望我的解释能帮助你更好地理解事情@Kris。但是请确保您在发布任何从C#执行指针操作的代码之前,先阅读有关指针和.NET托管内存模型的更多信息。如果做得不对,它会导致很多讨厌的问题! – 2009-11-02 14:44:17

+0

继续这一点:我试过你的建议,它的工作原理。有一件事我没有得到的是编译器吐出的代码......像buffer = buffer + wrtn;其中缓冲区的类型为byte *,而wrtn为uint则被编译为缓冲区+ =(byte *)wrtn; (.NET反射器)。然而,这最后一行永远不会编译。这怎么可能呢?知道这一点,怎么可能会像bufferv + =(void *)wrtn; (作为反射器的输出)原本已被编码(知道无效*算术显然是不可能的... – Kris 2009-11-02 14:48:45