views:

62

answers:

1
[DllImport("kernel32.dll", SetLastError=true)]
    public static extern unsafe bool WriteFile(IntPtr hFile, void* lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped);

I am implementing this through a Write(..) method with a signature:

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

As I learned by looking at this (the use of the read counterpart is discussed) I need to implement a while loop in my code because the write/read of the buffer might not go through in one go. This is where the problem start:

If I want to keep my C# method signature as to accept a void*, unlike the linked Read example where a byte* is accepted as a parameter for the buffer.

This means that after one pass of the WriteFile, I should move my void* along to the start of the buffer that has not been written yet. I cannot apparently do this by just incrementing void* with the uint that holds the number of bytes written... I understand that void* does not have a predetermined size and that incrementing is therefore not possible but I wonder how I then should achieve what I am trying to do.

+1  A: 

You should be able to cast buffer to a byte* and then increment it. A void pointer doesn't have size associated with it so if you want to move it a certain number of bytes in any direction you can cast it to a different type of pointer (any type for that matter) and then use the casted type's size in the pointer arithmetic, like so:

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

The line above casts buffer to a byte pointer, then increments its position by wrtn number of bytes and then casts the new pointer back to a void*. Of course, casting to a byte* is the obvious choice if you are wanting to perform arbitrary pointer arithmetic.

Another possibility is to treat buffer as a byte* all along and only cast it to void* when you pass it to 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;
    }
}

And, as a last suggestion, I would consider changing the signature of Write altogether to use a byte* instead of void* because it would make it more compatible with other callers from C# and a byte* makes more sense in that case. You shouldn't have to worry about making it match the signature of the WriteFile native API since you can cast the byte* as shown above to a void* when passing it in.

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

Alas, I have to agree with one of the commenters. Why are you doing this? There are better ways to accomplish a file write in c# using many of the stream oriented classes.

Miky Dinescu
I think I can work with your answer. Of course it is easier to just use pre-cooked API stuff and I probably will do so eventually, but every now and then I like to learn new things. I have always had a strong urge to really understand things and not just use "black boxes" all the time. Every now and then I google some code online, or I look at an assembly to see how things are done by "the pros" and then I try to implement my own version. This was one of those things...
Kris
Then I hope my explanation helps you understand things better @Kris. But please make sure you read more about pointers and the .NET managed memory model before you release any code that performs pointer operation from C#. It can lead to many nasty problems if not done right!
Miky Dinescu
Continuing on this: I tried your suggestion and it works. One thing I don't get is the code the compiler spits out... something like buffer = buffer + wrtn; where buffer is of type byte* and wrtn is uint gets compiled to buffer += (byte*)wrtn; (.NET reflector). This last line, however, will never compile. How can this be so? And knowing this, how might something like bufferv += (void*) wrtn; (as output by reflector) originally have been coded (knowing void* arithmetic is apparently not possible...
Kris
@Kris, that is certainly an interesting question in itself and while I don't know the answer maybe you should post it as a separate question with a link to this one. I'm sure there is an explanation out there..
Miky Dinescu