Search code examples
c#.net-4.0pinvokedllimportsignature

Using SetFilePointer in C# has unblanced the stack


Ok, I am using the SetFilePointer function in C# with .NET 4.0. Below are the dllimports I used to call this function:

[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
static extern uint SetFilePointer([In] SafeFileHandle hFile, [In] long lDistanceToMove, [Out] out int lpDistanceToMoveHigh, [In] EMoveMethod dwMoveMethod);

Whenever I run the code that uses the SetFilePointer function in the debugger I get this exception:

PInvokeStackImbalance was detected Message: A call to PInvoke function 'MyDiskStuff!MyDiskStuff.HardDisk::SetFilePointer' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

Whenever I run the same code outside of the debugger I do not get the above exception.

Below is the code I am using to make the calls to SetFilePointer:

public enum EMoveMethod : uint
    {
        Begin = 0,
        Current = 1,
        End = 2
    }

uint retvalUint = SetFilePointer(mySafeFileHandle, myLong, out myInt, EMoveMethod.Begin);

Is there something wrong with my dllimport signatures?


Solution

  • Your P/Invoke signature is a little off:

    Here's the Win32 definition:

    DWORD WINAPI SetFilePointer(
      _In_         HANDLE hFile,
      _In_         LONG lDistanceToMove,
      _Inout_opt_  PLONG lpDistanceToMoveHigh,
      _In_         DWORD dwMoveMethod
    );
    

    And here's the P/Invoke with your enum specified:

    [DllImport("kernel32.dll", EntryPoint="SetFilePointer")]
    static extern uint SetFilePointer(
          [In] Microsoft.Win32.SafeHandles.SafeFileHandle hFile, 
          [In] int lDistanceToMove, 
          [In, Out] ref int lpDistanceToMoveHigh, 
          [In] EMoveMethod dwMoveMethod) ;
    

    EDIT: Oh, and some test code:

    var text = "Here is some text to put in the test file";
    File.WriteAllText(@"c:\temp\test.txt", text);
    var file = File.Open(@"c:\temp\test.txt", FileMode.OpenOrCreate);
    int moveDistance = 10;
    int moveDistanceHighBits = 0;
    uint retvalUint = SetFilePointer(file.SafeFileHandle, moveDistance, ref moveDistanceHighBits, EMoveMethod.Begin);
    Debug.Assert(Encoding.ASCII.GetBytes(text)[moveDistance] == file.ReadByte());
    

    Also note from the docs:

    lDistanceToMove [in]

    The low order 32-bits of a signed value that specifies the number of bytes to move the file pointer. If lpDistanceToMoveHigh is not NULL, lpDistanceToMoveHigh and lDistanceToMove form a single 64-bit signed value that specifies the distance to move. If lpDistanceToMoveHigh is NULL, lDistanceToMove is a 32-bit signed value. A positive value for lDistanceToMove moves the file pointer forward in the file, and a negative value moves the file pointer back.

    lpDistanceToMoveHigh [in, out, optional]

    A pointer to the high order 32-bits of the signed 64-bit distance to move. If you do not need the high order 32-bits, this pointer must be set to NULL. When not NULL, this parameter also receives the high order DWORD of the new value of the file pointer. For more information, see the Remarks section in this topic.