I'm sure this has been addressed in the past. My apologies.
I want to understand why I'm getting error LNK2019.
And what direction to take, to get this resolved.
Errors are LNK2019:
unresolved external symbol __imp__ReportError referenced in function _wmain
unresolved external symbol __imp__Options referenced in function _wmain
This occurs when I build solution in Visual Studio 2010 and up.
Contents of header file for the above methods are described like so:
LIBSPEC DWORD Options (int, LPCTSTR *, LPCTSTR, ...);
LIBSPEC DWORD Options (int, LPCTSTR *, LPCTSTR, ...);
Main code:
#include "Everything.h"
BOOL TraverseDirectory(LPTSTR, LPTSTR, DWORD, LPBOOL);
DWORD FileType(LPWIN32_FIND_DATA);
BOOL ProcessItem(LPWIN32_FIND_DATA, DWORD, LPBOOL);
int _tmain(int argc, LPTSTR argv[])
{
BOOL flags[MAX_OPTIONS], ok = TRUE;
TCHAR searchPattern[MAX_PATH + 1], currPath[MAX_PATH_LONG+1], parentPath[MAX_PATH_LONG+1];
LPTSTR pSlash, pSearchPattern;
int i, fileIndex;
DWORD pathLength;
fileIndex = Options(argc, argv, _T("Rl"), &flags[0], &flags[1], NULL);
pathLength = GetCurrentDirectory(MAX_PATH_LONG, currPath);
if (pathLength == 0 || pathLength >= MAX_PATH_LONG) { /* pathLength >= MAX_PATH_LONG (32780) should be impossible */
ReportError(_T("GetCurrentDirectory failed"), 1, TRUE);
}
if (argc < fileIndex + 1)
ok = TraverseDirectory(currPath, _T("*"), MAX_OPTIONS, flags);
else for (i = fileIndex; i < argc; i++) {
if (_tcslen(argv[i]) >= MAX_PATH) {
ReportError(_T("The command line argument is longer than the maximum this program supports"), 2, FALSE);
}
_tcscpy(searchPattern, argv[i]);
_tcscpy(parentPath, argv[i]);
pSlash = _tstrrchr(parentPath, _T('\\'));
if (pSlash != NULL) {
*pSlash = _T('\0');
_tcscat(parentPath, _T("\\"));
SetCurrentDirectory(parentPath);
pSlash = _tstrrchr(searchPattern, _T('\\'));
pSearchPattern = pSlash + 1;
} else {
_tcscpy(parentPath, _T(".\\"));
pSearchPattern = searchPattern;
}
ok = TraverseDirectory(parentPath, pSearchPattern, MAX_OPTIONS, flags) && ok;
SetCurrentDirectory(currPath);
}
return ok ? 0 : 1;
}
static BOOL TraverseDirectory(LPTSTR parentPath, LPTSTR searchPattern, DWORD numFlags, LPBOOL flags)
{
HANDLE searchHandle;
WIN32_FIND_DATA findData;
BOOL recursive = flags[0];
DWORD fType, iPass, lenParentPath;
TCHAR subdirectoryPath[MAX_PATH + 1];
/* Open up the directory search handle and get the
first file name to satisfy the path name.
Make two passes. The first processes the files
and the second processes the directories. */
if ( _tcslen(searchPattern) == 0 ) {
_tcscat(searchPattern, _T("*"));
}
/* Add a backslash, if needed, at the end of the parent path */
if (parentPath[_tcslen(parentPath)-1] != _T('\\') ) { /* Add a \ to the end of the parent path, unless there already is one */
_tcscat (parentPath, _T("\\"));
}
/* Open up the directory search handle and get the
first file name to satisfy the path name. Make two passes.
The first processes the files and the second processes the directories. */
for (iPass = 1; iPass <= 2; iPass++) {
searchHandle = FindFirstFile(searchPattern, &findData);
if (searchHandle == INVALID_HANDLE_VALUE) {
ReportError(_T("Error opening Search Handle."), 0, TRUE);
return FALSE;
}
do {
fType = FileType(&findData);
if (iPass == 1) /* ProcessItem is "print attributes". */
ProcessItem(&findData, MAX_OPTIONS, flags);
lenParentPath = (DWORD)_tcslen(parentPath);
/* Traverse the subdirectory on the second pass. */
if (fType == TYPE_DIR && iPass == 2 && recursive) {
_tprintf(_T("\n%s%s:"), parentPath, findData.cFileName);
SetCurrentDirectory(findData.cFileName);
if (_tcslen(parentPath) + _tcslen(findData.cFileName) >= MAX_PATH_LONG-1) {
ReportError(_T("Path Name is too long"), 10, FALSE);
}
_tcscpy(subdirectoryPath, parentPath);
_tcscat (subdirectoryPath, findData.cFileName); /* The parent path terminates with \ before the _tcscat call */
TraverseDirectory(subdirectoryPath, _T("*"), numFlags, flags);
SetCurrentDirectory(_T("..")); /* Restore the current directory */
}
/* Get the next file or directory name. */
} while (FindNextFile(searchHandle, &findData));
FindClose(searchHandle);
}
return TRUE;
}
static DWORD FileType(LPWIN32_FIND_DATA pFileData)
{
BOOL isDir;
DWORD fType;
fType = TYPE_FILE;
isDir =(pFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
if (isDir)
if (lstrcmp(pFileData->cFileName, _T(".")) == 0
|| lstrcmp(pFileData->cFileName, _T("..")) == 0)
fType = TYPE_DOT;
else fType = TYPE_DIR;
return fType;
}
static BOOL ProcessItem(LPWIN32_FIND_DATA pFileData, DWORD numFlags, LPBOOL flags)
{
const TCHAR fileTypeChar[] = {_T(' '), _T('d')};
DWORD fType = FileType(pFileData);
BOOL longList = flags[1];
SYSTEMTIME lastWrite;
if (fType != TYPE_FILE && fType != TYPE_DIR) return FALSE;
_tprintf(_T("\n"));
if (longList) {
_tprintf(_T("%c"), fileTypeChar[fType - 1]);
_tprintf(_T("%10d"), pFileData->nFileSizeLow);
FileTimeToSystemTime(&(pFileData->ftLastWriteTime), &lastWrite);
_tprintf(_T(" %02d/%02d/%04d %02d:%02d:%02d"),
lastWrite.wMonth, lastWrite.wDay,
lastWrite.wYear, lastWrite.wHour,
lastWrite.wMinute, lastWrite.wSecond);
}
_tprintf(_T(" %s"), pFileData->cFileName);
return TRUE;
}
The error you're getting is from the linker, and it's telling you that it cannot find the definitions of the ReportError and Options functions (both of which are referenced from your main function).
You say that you have included a header file that contains these functions, but that header contains only the declarations of these functions. You know, like their signature. It doesn't have the body (implementation) of the functions. You need the definition for that.
Definitions for functions that you've written are usually located in *.cpp files. If you've written these functions, make sure that the code file that contains their definitions has been added to your project.
For functions that you have not written (i.e. are part of a reusable library of code), you usually provide the linker with a *.lib file that contains what it needs to call the functions. If these functions are from a library, make sure that you've added the *.lib file that came with them to the list of files that the linker will search. To do that in Visual Studio, follow these steps:
Right-click on your project in the Solution Explorer, and open its Properties.
Expand the "Linker" category, and select the "Input" node.
Click in the "Additional Dependencies" field, and press the ... button.
Enter the name of your *.lib file on a new line in the textbox that appears.
Just because you declared a function in a header file, doesn't mean it has a body anywhere.
The link error says the function doesn't have a body.
This is likely because you didn't link to the proper Library (a .lib file).
Related
I've been looking around for methods by which a directory can be monitored for file creation/modification etc. however all the previous posts I've found for Windows are C++ specific.
Microsoft does list ReadDirectoryChangesW, but this too is for C++ (I haven't the knowledge to assess whether these are compatible for C). I've only knowledge with inotify for Linux, which is fairly straightforward, and wondered if there are any simple examples of the Windows equivalent? (I do not want to use inotify on Windows despite it technically being achievable).
If you are just looking for methods, maybe this will help a bit:
https://www.geeksforgeeks.org/c-program-list-files-sub-directories-directory/
(just copy-pasted the code in case)
Tested it on linux machine and it seems to work. Not recursive though.
int main(void)
{
struct dirent *de; /* Pointer for directory entry */
/* opendir() returns a pointer of DIR type. */
DIR *dr = opendir(".");
if (dr == NULL) /* opendir returns NULL if couldn't open directory */
{
printf("Could not open current directory" );
return 0;
}
/* Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
for readdir() */
while ((de = readdir(dr)) != NULL)
printf("%s\n", de->d_name);
closedir(dr);
return 0;
}
Also, see this question if you need to check if a listed file is a directory:
Checking if a dir. entry returned by readdir is a directory, link or file
This method may not be as portable as it seems, but worth a try.
Cheers!
Using FindFirstFile to hit the first node of certain directory, then to call FindNextFile to iterate files one by one inside one directory layer.
Here is my sample code for your reference, there is a recursive funcion.
#include "stdafx.h"
#include <string>
static void iterate_dir(std::string dir) {
WIN32_FIND_DATA fd;
HANDLE hFind;
std::wstring fn_ws;
std::string fn;
int pos = 0;
int count_bg = 0;
int count_fg = 0;
std::string dir_bkp = dir;
std::string dir_sub;
std::string str_wide_char_for_any = "*.*";
std::string str_folder_node = "..";
if (dir.length() - dir.find_last_of("\\") > 1) //dir ends without "\\"
dir += "\\";
dir += str_wide_char_for_any;
std::wstring dir_wstr = std::wstring(dir.begin(), dir.end());
LPCWSTR dir_wc = dir_wstr.c_str();
hFind = FindFirstFile(dir_wc, &fd);
if (hFind == INVALID_HANDLE_VALUE) {
FindClose(hFind);
return;
}
while(true) {
if (!FindNextFile(hFind, &fd)) {
break;
}
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
fn_ws = std::wstring(fd.cFileName);
fn = std::string(fn_ws.begin(), fn_ws.end());
if (fn.compare(str_folder_node) == 0) {
continue;
}
else {
if ((pos = dir.rfind(str_wide_char_for_any)) != std::string::npos) {
dir_sub = dir;
dir_sub = dir_sub.replace(dir_sub.begin()+pos, dir_sub.end(), fn.begin(), fn.end());
}
else if (dir.length() - (pos = dir.rfind("\\")) > 1) {
dir_sub = dir;
dir_sub += "\\";
dir_sub += fn;
}
else {
dir_sub = dir;
dir_sub += fn;
}
printf("[%s][directory]:%s\n", __func__, dir.c_str());
iterate_dir(dir_sub);
continue;
}
}
else if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
fn_ws = std::wstring(fd.cFileName);
fn = std::string(fn_ws.begin(), fn_ws.end());
printf("[%s][file]:%s\n", __func__, fn.c_str());
}
else {
fn_ws = std::wstring(fd.cFileName);
fn = std::string(fn_ws.begin(), fn_ws.end());
printf("[%s][unspecified attribute file]:%s\n", __func__, fn.c_str());
}
}
FindClose(hFind);
return;
}
You can have a main.cpp like:
int main() {
std::string dir_name("C:\\test");
iterate_dir(dir);
return 0;
}
I'm running the following from a 32-bit process on a 64-bit Windows 10:
#ifndef _DEBUG
WCHAR buffPath[MAX_PATH] = {0};
FARPROC pfn = (FARPROC)::GetModuleHandleEx;
HMODULE hMod = NULL;
::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)pfn, &hMod);
PVOID pOldVal = NULL;
if(::Wow64DisableWow64FsRedirection(&pOldVal))
{
::GetModuleFileNameEx(::GetCurrentProcess(), hMod, buffPath, _countof(buffPath));
::Wow64RevertWow64FsRedirection(pOldVal);
wprintf(L"Path=%s\n", buffPath);
}
#else
#error run_in_release_mode
#endif
and I'm expecting to receive the path as c:\windows\syswow64\KERNEL32.DLL, but it gives me:
Path=C:\Windows\System32\KERNEL32.DLL
Any idea why?
when we load dll via LoadLibrary[Ex] or LdrLoadDll - first some pre-process transmitted dll name (say convert api-* to actual dll name or redirect dll name based on manifest - well known example comctl32.dll) and then use this (possible modified) dll name to load file as dll. but wow fs redirection - not preprocessed at this stage. if dll was successfully loaded - system allocate LDR_DATA_TABLE_ENTRY structure and save transmitted (after pre-process) dll name here as is.
the GetModuleFileNameEx simply walk throughout LDR_DATA_TABLE_ENTRY double linked list and search entry where DllBase == hModule - if found - copy FullDllName to lpFilename (if buffer big enough). so it simply return dll path used during load dll. the Wow64DisableWow64FsRedirection have no any effect to this call.
if we want get real (canonical) full path to dll - need use GetMappedFileNameW or ZwQueryVirtualMemory with MemoryMappedFilenameInformation
so code can be (if we hope that MAX_PATH is enough)
WCHAR path[MAX_PATH];
GetMappedFileNameW(NtCurrentProcess(), hmod, path, RTL_NUMBER_OF(path));
or if use ntapi and correct handle any path length:
NTSTATUS GetDllName(PVOID AddressInDll, PUNICODE_STRING NtImageName)
{
NTSTATUS status;
union {
PVOID buf;
PUNICODE_STRING ImageName;
};
static volatile UCHAR guz;
PVOID stack = alloca(guz);
SIZE_T cb = 0, rcb = 0x200;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (0 <= (status = ZwQueryVirtualMemory(NtCurrentProcess(), AddressInDll,
MemoryMappedFilenameInformation, buf, cb, &rcb)))
{
return RtlDuplicateUnicodeString(
RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
ImageName, NtImageName);
}
} while (status == STATUS_BUFFER_OVERFLOW);
return status;
}
UNICODE_STRING NtImageName;
if (0 <= GetDllName(hmod, &NtImageName))
{
RtlFreeUnicodeString(&NtImageName);
}
about question "way to convert it to the win32 form" - there is a counter question - for what ?
at first we can use it as is with NtOpenFile (well documented api), at second - the simplest way convert to win32 form, accepted by CreateFileW - add \\?\globalroot prefix to nt path. but not all win32 api (primary shell api) accept this form. if we want exactly dos-device form path (aka X:) need use IOCTL_MOUNTMGR_QUERY_POINTS - got the array of MOUNTMGR_MOUNT_POINT inside MOUNTMGR_MOUNT_POINTS structure and search for DeviceName which is prefix for our nt path and SymbolicLinkName have driver letter form. code can be ~
#include <mountmgr.h>
ULONG NtToDosPath(HANDLE hMM, PUNICODE_STRING ImageName, PWSTR* ppsz)
{
static MOUNTMGR_MOUNT_POINT MountPoint;
static volatile UCHAR guz;
PVOID stack = alloca(guz);
PMOUNTMGR_MOUNT_POINTS pmmp = 0;
DWORD cb = 0, rcb = 0x200, BytesReturned;
ULONG err = NOERROR;
do
{
if (cb < rcb) cb = RtlPointerToOffset(pmmp = (PMOUNTMGR_MOUNT_POINTS)alloca(rcb - cb), stack);
if (DeviceIoControl(hMM, IOCTL_MOUNTMGR_QUERY_POINTS,
&MountPoint, sizeof(MOUNTMGR_MOUNT_POINT),
pmmp, cb, &BytesReturned, 0))
{
if (ULONG NumberOfMountPoints = pmmp->NumberOfMountPoints)
{
PMOUNTMGR_MOUNT_POINT MountPoints = pmmp->MountPoints;
do
{
UNICODE_STRING SymbolicLinkName = {
MountPoints->SymbolicLinkNameLength,
SymbolicLinkName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->SymbolicLinkNameOffset)
};
UNICODE_STRING DeviceName = {
MountPoints->DeviceNameLength,
DeviceName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->DeviceNameOffset)
};
PWSTR FsPath;
if (RtlPrefixUnicodeString(&DeviceName, ImageName, TRUE) &&
DeviceName.Length < ImageName->Length &&
*(FsPath = (PWSTR)RtlOffsetToPointer(ImageName->Buffer, DeviceName.Length)) == '\\' &&
MOUNTMGR_IS_DRIVE_LETTER(&SymbolicLinkName))
{
cb = ImageName->Length - DeviceName.Length;
if (PWSTR psz = new WCHAR[3 + cb/sizeof(WCHAR)])
{
*ppsz = psz;
psz[0] = SymbolicLinkName.Buffer[12];
psz[1] = ':';
memcpy(psz + 2, FsPath, cb + sizeof(WCHAR));
return NOERROR;
}
return ERROR_NO_SYSTEM_RESOURCES;
}
} while (MountPoints++, --NumberOfMountPoints);
}
return ERROR_NOT_FOUND;
}
rcb = pmmp->Size;
} while ((err = GetLastError()) == ERROR_MORE_DATA);
return err;
}
ULONG NtToDosPath(PWSTR lpFilename, PWSTR* ppsz)
{
HANDLE hMM = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, FILE_GENERIC_READ,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hMM == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
UNICODE_STRING us;
RtlInitUnicodeString(&us, lpFilename);
ULONG err = NtToDosPath(hMM, &us, ppsz);
CloseHandle(hMM);
return err;
}
PWSTR psz;
if (NtToDosPath(path, &psz) == NOERROR)
{
DbgPrint("%S\n", psz);
delete [] psz;
}
also we can change in code to MOUNTMGR_IS_VOLUME_NAME(&SymbolicLinkName) for get volume (persistent) name form instead driver letter form
I am writing a utility function for a c program which creates directories, but will also create any intermediate directories which do not exist. I have that code working, but I wanted to turn my attention to improve user input formatting. Namely, I want to regularize the input path before I process it, especially to remove // which will break my current implementation.
I am aware of realpath(3), but my main issue is that it will fail if any of the directories on the path do not already exist. The command line function realpath(1) has the -m option, which seems to do what I want, but I don't want to invoke a shell if I can avoid it (Otherwise I could do mkdir -p and be done with it). Gnu findutils/canonicalize.h has canonicalize_filename_mode, but I don't know how to reference that short of copying the source directly (not out of the question). Thoughts, suggestions?
I am tied to my development environment gcc 4.7.7and rhel 6.6.
Below is my current implementation.
static int do_mkdir(const char *path, mode_t mode) {
struct stat st;
int status = 0;
if ( stat(path, &st) != 0 ) {
errno = 0;
if (mkdir(path, mode) != 0 && errno != EEXIST)
status = -1;
} else if ( !S_ISDIR(st.st_mode) ) {
errno = ENOTDIR;
status = -1;
}
return(status);
}
int mkpath(const char *path, mode_t mode) {
char *pp;
char *sp;
int status;
char copypath[PATH_MAX+1];
copypath = realpath(path, copypath);
status = 0;
pp = copypath;
if(copypath == NULL)
status = -1;
while ( status == 0 && (sp = strchr(pp, '/')) != 0 ) {
if (sp != pp) {
*sp = '\0';
status = do_mkdir(copypath, mode);
*sp = '/';
}
pp = sp + 1;
}
if ( status == 0 )
status = do_mkdir(path, mode);
return (status);
}
It might not fully answer your issue but here is an alternative:
You could process the path string with strtok, setting delim argument to '/'.
By doing so, strtok will return you intermediate directory names (null-terminated: you can get rid off the \0 tweak) and it will gracefully ignore //.
You can then add your "sanitize" checks (look for invalid characters, path name is too long, etc...)
I found a workaround that I believe covers all cases. What I did, was to move the call to realpath into the do_mkdir function so that way it will only have at most one non-existent element, replacing it with a strdup in the mkpath function. I can't think of a situation where performing the canonicalization on a partial path will break anything either.
I am trying to list all devices attached to my system and after searching found this code which throws up error local function definations are illegal can someone explain what its means please.
Or is my issue because I am trying to use code that was from in C++. Thanks
WORKING CODE
#include <windows.h>
#include <setupapi.h>
#include <stdio.h>
#pragma comment(lib,"SetupAPI")
void print_property
(
__in HDEVINFO hDevInfo,
__in SP_DEVINFO_DATA DeviceInfoData,
__in PCWSTR Label,
__in DWORD Property
)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
//
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
Property,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
// Change the buffer size.
if (buffer)
{
LocalFree(buffer);
}
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (LPTSTR)LocalAlloc(LPTR, buffersize * 2);
}
else
{
break;
}
}
wprintf(L"%s %s\n",Label, buffer);
if (buffer)
{
LocalFree(buffer);
}
}
int main()
{
//int setupdi_version()
//{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(
NULL,
0, // Enumerator
0,
DIGCF_PRESENT | DIGCF_ALLCLASSES);
if (INVALID_HANDLE_VALUE == hDevInfo)
{
// Insert error handling here.
return 1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); i++)
{
LPTSTR buffer = NULL;
DWORD buffersize = 0;
print_property(hDevInfo, DeviceInfoData, L"Friendly name :", SPDRP_FRIENDLYNAME);
while (!SetupDiGetDeviceInstanceId(
hDevInfo,
&DeviceInfoData,
buffer,
buffersize,
&buffersize))
{
if (buffer)
{
LocalFree(buffer);
}
if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
// Change the buffer size.
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (LPTSTR)LocalAlloc(LPTR, buffersize * 2);
}
else
{
wprintf(L"error: could not get device instance id (0x%x)\n", GetLastError());
break;
}
}
if (buffer)
{
wprintf(L"\tDeviceInstanceId : %s\n", buffer);
}
print_property(hDevInfo, DeviceInfoData, L"\tClass :", SPDRP_CLASS);
print_property(hDevInfo, DeviceInfoData, L"\tClass GUID :", SPDRP_CLASSGUID);
}
if (NO_ERROR != GetLastError() && ERROR_NO_MORE_ITEMS != GetLastError())
{
// Insert error handling here.
return 1;
}
// Cleanup
SetupDiDestroyDeviceInfoList(hDevInfo);
system ("pause");
return 0;
}
You have another function defined inside the body of main; this is invalid C. Move it outside of main.
The code will compile and run if you comment out the following two lines as shown:
// int setupdi_version()
// {
I think the original code is from a function named setupdi_version() and it got mangled a bit when you tried to change it to main(). Note: it looks like the original source code is from here.
To answer your follow-on problem. Those are linker errors. You need to tell Visual Studio which .lib file(s) to link against. You can do that in the Visual Studio project dependencies or just add the following to the top of the source code.
#pragma comment(lib,"SetupAPI")
On opening a directory via zwopenfile(open directory option), it returns a handle for the directory path. Now I need to get the directory path from the handle. Its my requirement.
I could see an example(please see the code below) where file name can be fetched from file handle. But the below example does not help for directory. Is there any possibilities in fetching directory name from its opened handle.
CHAR* GetFileNameFromHandle(HANDLE hFile)
{
BOOL bSuccess = FALSE;
TCHAR pszFilename[MAX_PATH+1];
HANDLE hFileMap;
// Get the file size.
DWORD dwFileSizeHi = 0;
DWORD dwFileSizeLo = GetFileSize(hFile, &dwFileSizeHi);
if( dwFileSizeLo == 0 && dwFileSizeHi == 0 )
{
printf("Cannot map a file with a length of zero.\n");
return FALSE;
}
// Create a file mapping object.
hFileMap = CreateFileMappingW(hFile,
NULL,
PAGE_READONLY,
0,
1,
NULL);
if (hFileMap)
{
// Create a file mapping to get the file name.
void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
if (pMem)
{
if (GetMappedFileNameW (GetCurrentProcess(),
pMem,
pszFilename,
MAX_PATH))
{
// Translate path with device name to drive letters.
TCHAR szTemp[1024];
szTemp[0] = '\0';
if (GetLogicalDriveStrings(1024-1, szTemp))
{
TCHAR szName[MAX_PATH];
TCHAR szDrive[3] = TEXT(" :");
BOOL bFound = FALSE;
TCHAR* p = szTemp;
do
{
// Copy the drive letter to the template string
*szDrive = *p;
// Look up each device name
if (QueryDosDevice(szDrive, szName, MAX_PATH))
{
UINT uNameLen = _tcslen(szName);
if (uNameLen < MAX_PATH)
{
bFound = _tcsnicmp(pszFilename, szName, uNameLen) == 0;
if (bFound && *(pszFilename + uNameLen) == _T('\\'))
{
// Reconstruct pszFilename using szTempFile
// Replace device path with DOS path
TCHAR szTempFile[MAX_PATH];
StringCchPrintf(szTempFile,
MAX_PATH,
TEXT("%s%s"),
szDrive,
pszFilename+uNameLen);
StringCchCopyN(pszFilename, MAX_PATH+1, szTempFile, _tcslen(szTempFile));
}
}
}
// Go to the next NULL character.
while (*p++);
} while (!bFound && *p); // end of string
}
}
bSuccess = TRUE;
UnmapViewOfFile(pMem);
}
CloseHandle(hFileMap);
}
_tprintf(TEXT("File name is %s\n"), pszFilename);
return( pszFilename);
}
NtQueryObject did it.
The example from MSDN, which you posted, gives a 'fully qualified' file name, i.e. with the drive letter and the full path.
With that, it should be easy to get the directory name: just strip off everything after the last \.