- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #include <wchar.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdint.h>
- #include <stddef.h>
- #ifdef _MSC_VER
- #pragma comment(lib, "ntdll.lib")
- #endif
- typedef LONG NTSTATUS;
- #ifndef NT_SUCCESS
- #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
- #endif
- typedef struct _IO_STATUS_BLOCK {
- union { NTSTATUS Status; PVOID Pointer; } DUMMYUNIONNAME;
- ULONG_PTR Information;
- } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
- typedef enum _FILE_INFORMATION_CLASS {
- FileRenameInformation = 10
- } FILE_INFORMATION_CLASS;
- typedef struct _FILE_RENAME_INFORMATION {
- BOOLEAN ReplaceIfExists;
- HANDLE RootDirectory;
- ULONG FileNameLength;
- WCHAR FileName[1];
- } FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
- #ifdef __cplusplus
- extern "C" {
- #endif
- NTSTATUS NTAPI NtSetInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
- ULONG NTAPI RtlNtStatusToDosError(NTSTATUS);
- #ifdef __cplusplus
- }
- #endif
- static BOOL ToFullDosPathW(const wchar_t* inPath, wchar_t** outFull)
- {
- if (!inPath || !outFull) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
- if (wcslen(inPath) >= 4 && inPath[0] == L'\\' && inPath[1] == L'\\' && inPath[2] == L'?' && inPath[3] == L'\\') {
- size_t len = wcslen(inPath) + 1;
- wchar_t* dup = (wchar_t*)HeapAlloc(GetProcessHeap(), 0, len * sizeof(wchar_t));
- if (!dup) { SetLastError(ERROR_OUTOFMEMORY); return FALSE; }
- wcscpy(dup, inPath);
- *outFull = dup;
- return TRUE;
- }
- DWORD need = GetFullPathNameW(inPath, 0, NULL, NULL);
- if (need == 0) return FALSE;
- wchar_t* buf = (wchar_t*)HeapAlloc(GetProcessHeap(), 0, need * sizeof(wchar_t));
- if (!buf) { SetLastError(ERROR_OUTOFMEMORY); return FALSE; }
- if (GetFullPathNameW(inPath, need, buf, NULL) == 0) {
- DWORD err = GetLastError();
- HeapFree(GetProcessHeap(), 0, buf);
- SetLastError(err);
- return FALSE;
- }
- *outFull = buf;
- return TRUE;
- }
- static HANDLE OpenForRenameW(const wchar_t* fullDosPath)
- {
- DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
- DWORD flags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
- return CreateFileW(fullDosPath, DELETE | SYNCHRONIZE, share, NULL, OPEN_EXISTING, flags, NULL);
- }
- static BOOL SplitParentAndLeafW(const wchar_t* fullPath, wchar_t** parentOut, const wchar_t** leafOut)
- {
- if (!fullPath || !parentOut || !leafOut) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
- size_t len = wcslen(fullPath);
- if (len == 0) { SetLastError(ERROR_INVALID_NAME); return FALSE; }
- const wchar_t* last = wcsrchr(fullPath, L'\\');
- if (!last || last == fullPath) { SetLastError(ERROR_INVALID_NAME); return FALSE; }
- size_t parentLen = (size_t)(last - fullPath);
- const wchar_t* leaf = last + 1;
- if (*leaf == L'\0') { SetLastError(ERROR_INVALID_NAME); return FALSE; }
- wchar_t* parent = (wchar_t*)HeapAlloc(GetProcessHeap(), 0, (parentLen + 1) * sizeof(wchar_t));
- if (!parent) { SetLastError(ERROR_OUTOFMEMORY); return FALSE; }
- wmemcpy(parent, fullPath, parentLen);
- parent[parentLen] = L'\0';
- *parentOut = parent;
- *leafOut = leaf;
- return TRUE;
- }
- static BOOL IsLeafOnlyTarget(const wchar_t* target)
- {
- if (!target || !*target) return FALSE;
- if (wcschr(target, L'\\') != NULL) return FALSE;
- if (((target[0] >= L'A' && target[0] <= L'Z') || (target[0] >= L'a' && target[0] <= L'z')) && target[1] == L':')
- return FALSE;
- return TRUE;
- }
- static BOOL ExtractBaseFilePathW(const wchar_t* fullPath, wchar_t** baseOut, BOOL* hadStreamOut)
- {
- if (!fullPath || !baseOut || !hadStreamOut) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
- const wchar_t* afterSlash = wcsrchr(fullPath, L'\\');
- const wchar_t* searchStart = afterSlash ? afterSlash + 1 : fullPath;
- const wchar_t* colon = wcschr(searchStart, L':');
- if (!colon) {
- size_t n = wcslen(fullPath) + 1;
- wchar_t* dup = (wchar_t*)HeapAlloc(GetProcessHeap(), 0, n * sizeof(wchar_t));
- if (!dup) { SetLastError(ERROR_OUTOFMEMORY); return FALSE; }
- wcscpy(dup, fullPath);
- *baseOut = dup;
- *hadStreamOut = FALSE;
- return TRUE;
- }
- size_t baseLen = (size_t)(colon - fullPath);
- wchar_t* base = (wchar_t*)HeapAlloc(GetProcessHeap(), 0, (baseLen + 1) * sizeof(wchar_t));
- if (!base) { SetLastError(ERROR_OUTOFMEMORY); return FALSE; }
- wmemcpy(base, fullPath, baseLen);
- base[baseLen] = L'\0';
- *baseOut = base;
- *hadStreamOut = TRUE;
- return TRUE;
- }
- BOOL RenameFileW(const wchar_t* fromPathW, const wchar_t* toPathW)
- {
- if (!fromPathW || !toPathW) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
- wchar_t *fromFull = NULL;
- if (!ToFullDosPathW(fromPathW, &fromFull)) return FALSE;
- HANDLE hSrc = OpenForRenameW(fromFull);
- if (hSrc == INVALID_HANDLE_VALUE) {
- DWORD err = GetLastError();
- HeapFree(GetProcessHeap(), 0, fromFull);
- SetLastError(err);
- return FALSE;
- }
- HANDLE hRoot = INVALID_HANDLE_VALUE;
- const wchar_t* fileNamePtr = NULL;
- wchar_t *destParent = NULL, *toFull = NULL;
- if (IsLeafOnlyTarget(toPathW)) {
- if (toPathW[0] == L':') {
- BOOL hadStream = FALSE;
- wchar_t* baseFull = NULL;
- if (!ExtractBaseFilePathW(fromFull, &baseFull, &hadStream)) {
- DWORD err = GetLastError();
- CloseHandle(hSrc);
- HeapFree(GetProcessHeap(), 0, fromFull);
- SetLastError(err);
- return FALSE;
- }
- if (!hadStream) {
- HeapFree(GetProcessHeap(), 0, baseFull);
- CloseHandle(hSrc);
- HeapFree(GetProcessHeap(), 0, fromFull);
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
- }
- hRoot = CreateFileW(baseFull,
- FILE_READ_ATTRIBUTES | SYNCHRONIZE,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL, OPEN_EXISTING, 0, NULL);
- HeapFree(GetProcessHeap(), 0, baseFull);
- if (hRoot == INVALID_HANDLE_VALUE) {
- DWORD err = GetLastError();
- CloseHandle(hSrc);
- HeapFree(GetProcessHeap(), 0, fromFull);
- SetLastError(err);
- return FALSE;
- }
- fileNamePtr = toPathW;
- } else {
- const wchar_t* srcLeaf = NULL;
- if (!SplitParentAndLeafW(fromFull, &destParent, &srcLeaf)) {
- DWORD err = GetLastError();
- CloseHandle(hSrc);
- HeapFree(GetProcessHeap(), 0, fromFull);
- SetLastError(err);
- return FALSE;
- }
- hRoot = CreateFileW(destParent,
- FILE_READ_ATTRIBUTES | SYNCHRONIZE,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- if (hRoot == INVALID_HANDLE_VALUE) {
- DWORD err = GetLastError();
- HeapFree(GetProcessHeap(), 0, destParent);
- CloseHandle(hSrc);
- HeapFree(GetProcessHeap(), 0, fromFull);
- SetLastError(err);
- return FALSE;
- }
- fileNamePtr = toPathW;
- }
- } else {
- if (!ToFullDosPathW(toPathW, &toFull)) {
- DWORD err = GetLastError();
- CloseHandle(hSrc);
- HeapFree(GetProcessHeap(), 0, fromFull);
- SetLastError(err);
- return FALSE;
- }
- const wchar_t* destLeaf = NULL;
- if (!SplitParentAndLeafW(toFull, &destParent, &destLeaf)) {
- DWORD err = GetLastError();
- CloseHandle(hSrc);
- HeapFree(GetProcessHeap(), 0, fromFull);
- HeapFree(GetProcessHeap(), 0, toFull);
- SetLastError(err);
- return FALSE;
- }
- hRoot = CreateFileW(destParent,
- FILE_READ_ATTRIBUTES | SYNCHRONIZE,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- if (hRoot == INVALID_HANDLE_VALUE) {
- DWORD err = GetLastError();
- HeapFree(GetProcessHeap(), 0, destParent);
- CloseHandle(hSrc);
- HeapFree(GetProcessHeap(), 0, fromFull);
- HeapFree(GetProcessHeap(), 0, toFull);
- SetLastError(err);
- return FALSE;
- }
- fileNamePtr = destLeaf;
- }
- size_t nameChars = wcslen(fileNamePtr);
- if (nameChars == 0) {
- if (toFull) HeapFree(GetProcessHeap(), 0, toFull);
- if (destParent) HeapFree(GetProcessHeap(), 0, destParent);
- if (hRoot != INVALID_HANDLE_VALUE) CloseHandle(hRoot);
- CloseHandle(hSrc);
- HeapFree(GetProcessHeap(), 0, fromFull);
- SetLastError(ERROR_INVALID_NAME);
- return FALSE;
- }
- ULONG nameBytes = (ULONG)(nameChars * sizeof(WCHAR));
- ULONG infoSize = (ULONG)(offsetof(FILE_RENAME_INFORMATION, FileName) + nameBytes);
- PFILE_RENAME_INFORMATION fri = (PFILE_RENAME_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, infoSize);
- if (!fri) {
- if (toFull) HeapFree(GetProcessHeap(), 0, toFull);
- if (destParent) HeapFree(GetProcessHeap(), 0, destParent);
- if (hRoot != INVALID_HANDLE_VALUE) CloseHandle(hRoot);
- CloseHandle(hSrc);
- HeapFree(GetProcessHeap(), 0, fromFull);
- SetLastError(ERROR_OUTOFMEMORY);
- return FALSE;
- }
- fri->ReplaceIfExists = FALSE;
- fri->RootDirectory = NULL/*hRoot*/;
- fri->FileNameLength = nameBytes;
- memcpy(fri->FileName, fileNamePtr, nameBytes);
- IO_STATUS_BLOCK iosb = { 0 };
- NTSTATUS status = NtSetInformationFile(hSrc, &iosb, fri, infoSize, FileRenameInformation);
- BOOL ok = NT_SUCCESS(status);
- if (!ok) SetLastError(RtlNtStatusToDosError(status)); else SetLastError(ERROR_SUCCESS);
- HeapFree(GetProcessHeap(), 0, fri);
- if (toFull) HeapFree(GetProcessHeap(), 0, toFull);
- if (destParent) HeapFree(GetProcessHeap(), 0, destParent);
- if (hRoot != INVALID_HANDLE_VALUE) CloseHandle(hRoot);
- CloseHandle(hSrc);
- HeapFree(GetProcessHeap(), 0, fromFull);
- return ok;
- }
- static void PrintLastErrorMessageW(DWORD err)
- {
- wchar_t* msg = NULL;
- DWORD n = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, err, 0, (LPWSTR)&msg, 0, NULL);
- if (n && msg) {
- for (wchar_t* p = msg; *p; ++p) { if (*p == L'\r' || *p == L'\n') { *p = L'\0'; break; } }
- fwprintf(stderr, L"Win32 error %lu: %ls\n", err, msg);
- LocalFree(msg);
- } else {
- fwprintf(stderr, L"Win32 error %lu\n", err);
- }
- }
- int wmain(int argc, wchar_t* argv[])
- {
- if (argc != 3) {
- fwprintf(stderr, L"Usage:\n %ls <fromPath> <toPath>\n", argv[0]);
- return 2;
- }
- if (!RenameFileW(argv[1], argv[2])) {
- DWORD err = GetLastError();
- fwprintf(stderr, L"Rename failed.\n");
- PrintLastErrorMessageW(err);
- return 1;
- }
- wprintf(L"Rename succeeded.\n");
- return 0;
- }
Untitled
Posted by Anonymous on Wed 21st Jan 2026 02:20
raw | new post
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.
nrubsig.kpaste.net RSS