2

I have tried several ways to get MoveFileEx working with the DELAY_UNTIL_REBOOT flag without success. The FileRenameOperations key in the registry also shows that the method did not execute properly. What could be the cause?

I call my MoveFileEx function like this:

MoveFileEx(localFile, oldFile, MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT);

My WINAPI code is as follows:

[DllImport("kernel32.dll", EntryPoint = "MoveFileEx")]
internal static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);

internal enum MoveFileFlags
{
    MOVEFILE_REPLACE_EXISTING = 1,
    MOVEFILE_COPY_ALLOWED = 2,
    MOVEFILE_DELAY_UNTIL_REBOOT = 4,
    MOVEFILE_WRITE_THROUGH = 8
}

This application is run under admin account. Could this be because I'm using 4 instead of 0x4 or is it some 64bit problem? Thanks!

EDIT: The Operation returns false and error code of 3.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Chibueze Opata
  • 9,856
  • 7
  • 42
  • 65
  • 1
    Well, `4 == 0x4`. This pinvoke will be interpreted identically on x64 and x86. Are you implying that your code runs fine as a 32 bit process? What does the call to MoveFileEx look like? – David Heffernan Mar 08 '12 at 21:35
  • From Microsoft, the C++ code is: `BOOL WINAPI MoveFileEx( __in LPCTSTR lpExistingFileName, __in_opt LPCTSTR lpNewFileName, __in DWORD dwFlags );` – Chibueze Opata Mar 08 '12 at 21:38
  • 4
    That's not the call. That's the declaration, and we already know that part because it's in MSDN. You need to show *your* code, please. – Rob Kennedy Mar 08 '12 at 21:39
  • Sorry, I see the call to `MoveFileEx` now. Clearly I am blind. I wonder, is your process 32 or 64 bit? Could it be that you are running a 32 bit process on a 64 bit system. When you call `MoveFileEx` it works fine, but writes to the 32 bit view of the registry (HKLM\Software\Wow6432Node\...). You then check the 64 bit view of the registry and conclude that the function failed. Another question, what does MoveFileEx return, true or false? In summary, please give us more information. – David Heffernan Mar 08 '12 at 21:49
  • My code is actually shown on top, the `localFile` and `oldFile` are both string parameters. The lengths are 94 and 126 respectively. – Chibueze Opata Mar 08 '12 at 21:51
  • In addition to Steve's answer, MoveFileFlags misses [Flags] attribute. – Nikola Markovinović Mar 08 '12 at 21:54
  • @DavidHeffernan the process is 64 bit and even if it was 32, I think rename operations are actually written to HKLM\SYSTEM which doesn't have a separate key for 32bits... – Chibueze Opata Mar 08 '12 at 21:55
  • OK, now we are getting somewhere. Now, what does MoveFileEx return? And if it returns false then add `SetLastError = true` to the `DllImport` and call `Marshal.GetLastWin32Error()` to find out the error code. – David Heffernan Mar 08 '12 at 21:59
  • The operation returns false. And lastError returns as 3. – Chibueze Opata Mar 08 '12 at 22:00
  • Right, now do the rest of what I said and let us know the error code. – David Heffernan Mar 08 '12 at 22:02

2 Answers2

5

Error code 3 is ERROR_PATH_NOT_FOUND. It seems that you made a simple mistake in your file names: one of the directories in one of your file names does not exist.

In a comment, you state that the call that fails is of the form:

MoveFileEx(newFile, "", 4);

This will certainly fail. What you are trying to do is pass NULL as the destination filename in order to delete the file. But you are not passing NULL, you are passing the empty string. In order to pass NULL to the native API, call it like this:

bool res = MoveFileEx(filename, null,
    MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT);

I would make sure that you include SetLastError = true in your pinvoke so that you can diagnose errors.

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool MoveFileEx(string lpExistingFileName, 
    string lpNewFileName, MoveFileFlags dwFlags);
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I've double checked this, that's not the problem. I even copied and pasted in run window just now and it executed. – Chibueze Opata Mar 08 '12 at 22:07
  • At this point we can't really help much. Windows says one thing, you say something else. Clearly one of you is wrong, but I don't think we can do much more. – David Heffernan Mar 08 '12 at 22:08
  • I feel like my PC just committed a crime and sent me to jail for it! – Chibueze Opata Mar 08 '12 at 22:09
  • Hmmm, just noticed one of the MoveFileEx actually returns true, the one that fails precisely is the one that uses parameter (newFile, "", 4) – Chibueze Opata Mar 08 '12 at 22:12
  • @opatachibueze, just to add a little salt on your wounds. Just tried on my pc (win7x64) and I have the right entries on PendingFileRenameOperations. – Steve Mar 08 '12 at 22:14
  • 1
    Yes that's it. I've updated my answer with an explanation. The lesson here is to look at the return value from the API and check the error codes in case of failure. – David Heffernan Mar 08 '12 at 22:18
  • 1
    Ok, thanks, finally tested, and it works! and yes, I never really understood the good of setting LastError to true. Thanks – Chibueze Opata Mar 08 '12 at 22:20
  • Or you could use the old form and do MoveFileEx(filename, null, MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT). You don't need an IntPtr to say null in C#. – SecurityMatt Mar 09 '12 at 00:03
  • @SecurityMatt thanks a lot for that correction. Minor brain freeze on my part. Answer updated. – David Heffernan Mar 09 '12 at 03:43
  • @DavidHeffernan & SecurityMatt I actually tried null first and it worked, I just assumed your IntPtr.Zero was the proper way. – Chibueze Opata Mar 09 '12 at 05:54
0

Your process should be ran with administrative rights.

AndreiM
  • 815
  • 9
  • 17