Related
Developing a game ("jogo" in PT),
the server can host up to 5 simultaneous games,
which the client will access via mapped memory.
So here's what I have:
Server:
#define MAX_JOGOS 5
typedef struct{
...
} sJogo;
typedef struct{
sJogo * pS;
} sGlobals;
sJogo jogo[MAX_JOGOS]; //global
sGlobals globals[MAX_JOGOS]; //global
HANDLE hMapFile; //global
int _tmain(int argc, LPTSTR argv[]) {
...
hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(sJogo)*MAX_JOGOS, szName);
//create map for all games
....
}
//called when new game is created
void createView(int index){
//create view for 1 game and store pointer
//### need to apply offset here ###
globals.pS[index] = (sJogo * )MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(sJogo);
}
//called by thread on event set
void CopyJogo(int index){
//use stored pointer to update jogo
CopyMemory((PVOID)globals[index].pS, &jogo[index], sizeof(sJogo));
}
Client:
HANDLE hMapFile; //global
sJogo * pS; //global
int _tmain(int argc, LPTSTR argv[]) {
...
hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, szName);
pS = (sJogo *)MapViewOfFile(cdata.hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(sJogo));
//### need respective offset here ###
}
I have tried creating a view of "sizeof(sJogo)*MAX_JOGOS" and then incrementing the pointer+=sizeof(sJogo) * index; but didn't manage to succeed, so now I turn to you, can you help me learn to use the offset?
I have searched quite persistently and found a good example here on stackoverflow but it's C++ and I couldn't adapt it.
The high-order DWORD offset would be sizeof(sJogo) correct?
But I don't know what granularity is or how to apply it to the low-order DWORD...
Can you help me?
Thank you.
EDIT:
The code below is returning when i = 1 (NULL), what am I doing wrong?
int _tmain(int argc, LPTSTR argv[]) {
....
hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(sJogo)*MAX_JOGOS, szName);
if (hMapFile == NULL)
{...}
DWORD offset = 0;
for (i = 0; i < MAX_JOGOS; i++) {
if (MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, offset, sizeof(sJogo)) == NULL)
{
_tprintf(TEXT("Erro MapViewOfFile I: %d\n"), i);
CloseHandle(hMapFile);
return;
}
offset += sizeof(sJogo);
}
}
EDIT 2:
Solved the problem above, found the solution here:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa366548%28v=vs.85%29.aspx
I wasn't taking into account the allocation granularity on the offset, which was causing MapViewOfFile to return NULL on the second attempt.
The link above shows a clear example on how to apply it to the offset.
The example in MSDN should fit for you.
You can find it here
Basically in the CreateFileMapping you declare the amount of memory to share, in the MapViewOfFile you create a 'viewport' on the memory. It can have dimensions equal or a smaller than the quantity requested.
This memory can now be read or write.
On the client side you can open the mapping with OpenFileMapping then you should map the quantity of memory that you want access, the size of a single structure. Then you can iterate while MapViewOfFile returns a valid address to access all elements of the array:
sJogo *pS = NULL;
DWORD ofset = 0;
while((pS= (sJogo *)MapViewOfFile(cdata.hMapFile, FILE_MAP_ALL_ACCESS, 0, offset, sizeof(sJogo)))
{
//Do something with the current element pointed by pS
offset += sizeof(sJogo);
}
Here we start at offset zero in the mapped memory, than we increment offset by the size of our structure. This moves the mapping to the next element of the array of structures.
When we exceed the number of elements the file mapping will return NULL.
This is not the most efficient way to handle file mapping, you want consider to create a super structure holding the number of entries available and the entries coded as VLA (Variable Lenght Array) at the end of structure:
typedef struct{
int nEntries; //Holds the number of entries
sJogo pS[];
} sGlobals;
sGlobals globals;
This is my first attempt at using a resource file. I have seen a lot of answers that apply to C# but not C. Any suggestions?
Assuming you mean the method used by Sysinternals and others to carry the drivers or needed DLLs (or even the x64 version of the program itself) in the resource section of a program (e.g. Sysinternals' Process Explorer), using Microsoft Visual C you can use this code:
BOOL ExtractResTo(HINSTANCE Instance, LPCTSTR BinResName, LPCTSTR NewPath, LPCTSTR ResType)
{
BOOL bResult = FALSE;
HRSRC hRsrc;
if(hRsrc = FindResource(HMODULE(Instance), BinResName, ResType))
{
HGLOBAL hGlob
if(HGLOBAL hGlob = LoadResource(Instance, hRsrc))
{
DWORD dwResSize = SizeofResource(Instance, hRsrc);
HANDLE hFileWrite = CreateFile(NewPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, 0);
if(hFileWrite != INVALID_HANDLE_VALUE)
__try
{
DWORD dwSizeWritten = 0;
bResult = (WriteFile(hFileWrite, LockResource(hGlob), dwResSize, &dwSizeWritten, NULL) && (dwSizeWritten == dwResSize));
}
__finally
{
CloseHandle(hFileWrite);
}
}
}
return bResult;
}
This saves the given resource (BinResName) of resource type (ResType) from the module (e.g. a DLL) Instance to file NewPath. Obviously if your C doesn't understand __try and __finally you'll have to adjust the code accordingly.
Taken from here (in SIDT.rar) and adjusted for C. The code is under the liberal BSD license according to the website.
Now if you wanted to get the pointer to the data (ppRes) and its size (pwdResSize):
BOOL GetResourcePointer(HINSTANCE Instance, LPCTSTR ResName, LPCTSTR ResType, LPVOID* ppRes, DWORD* pdwResSize)
{
// Check the pointers to which we want to write
if(ppRes && pdwResSize)
{
HRSRC hRsrc;
// Find the resource ResName of type ResType in the DLL/EXE described by Instance
if(hRsrc = FindResource((HMODULE)Instance, ResName, ResType))
{
HGLOBAL hGlob;
// Make sure it's in memory ...
if(hGlob = LoadResource(Instance, hRsrc))
{
// Now lock it to get a pointer
*ppRes = LockResource(hGlob);
// Also retrieve the size of the resource
*pdwResSize = SizeofResource(Instance, hRsrc);
// Return TRUE only if both succeeded
return (*ppRes && *pdwResSize);
}
}
}
// Failure means don't use the values in *ppRes and *pdwResSize
return FALSE;
}
Call like this:
LPVOID pResource;
DWORD pResourceSize;
if(GetResourcePointer(hInstance, _T("somename"), _T("sometype"), &pResource, &pResourceSize))
{
// use pResource and pResourceSize
// e.g. store into a string buffer or whatever you want to do with it ...
}
Note, this also works for resources with integer IDs. You can detect these by using the macro IS_INTRESOURCE.
The resource script, e.g. myresources.rc, itself is trivial:
#include <winnt.rh>
"somename" "sometype" "path\to\file\to\embed"
RCDATA instead of sometype is a reasonable choice
#include <winnt.rh> // for RCDATA to be known to rc.exe
"somename" RCDATA "path\to\file\to\embed"
... in which case you would adjust the above call to be:
GetResourcePointer(hInstance, _T("somename"), RT_RCDATA, &pResource, &pResourceSize)
Complete example making use of GetResourcePointer above. Let's say you have a pointer variable char* buf and you know your resource is actual text, then you need to make sure that it is zero-terminated when used as a string, that's all.
Resource script:
#include <winnt.rh>
"test" RCDATA "Test.txt"
The code accessing it
char* buf = NULL;
LPVOID pResource;
DWORD pResourceSize;
if(GetResourcePointer(hInstance, _T("test"), RT_RCDATA, &pResource, &pResourceSize))
{
if(buf = calloc(pResourceSize+1, sizeof(char)))
{
memcpy(buf, pResource, pResourceSize);
// Now use buf
free(buf);
}
}
Unless, of course you meant to simply link a .res file which works by passing it to the linker command line.
I need to get a list of all processes on a windows system including names and PID.
EnumProcess can obtain a list of pids, but how do I get the process name from the pid? I don't want to call OpenProcess on the process as that doesn't always work (like if the other process is run by a different user).
You have different option which you can use to receive exe names of currently running processes (process names like you wrote). The best way depends a little from the programming language which you use and from other requirements. For example you can use WMI. One other more old way is the usage of Performance Counters (see also An Introduction To Performance Counters). To get the counters values you can just use registry query operations from the HKEY_PERFORMANCE_DATA base key (see Retrieving Counter Data)
One more way which can be also good used is the NtQuerySystemInformation function with SystemProcessInformation as a parameter. EnumProcess and many other Windows API use the function internally. The struct SYSTEM_PROCESS_INFORMATION defined in the documentation of NtQuerySystemInformation has many "undocumented" but since many many years well-known fields. If you search in the Internet for the definition of the structure you will fined full documentation. I wonder that the function hat status not full documented. The function was at least in NT 3.5 (probably also before) and can be good used now on Windows 7 32- or 64-bit. To be exact below you will find a small C test program which print all process ids with the corresponding exe names (not full exe path, just the file name):
#include <Windows.h>
// one can also use Winternl.h if needed
//#include <Winternl.h> // for UNICODE_STRING and SYSTEM_INFORMATION_CLASS
#include <stdio.h>
#include <tchar.h>
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemProcessInformation = 5
} SYSTEM_INFORMATION_CLASS;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef LONG KPRIORITY; // Thread priority
typedef struct _SYSTEM_PROCESS_INFORMATION_DETAILD {
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER SpareLi1;
LARGE_INTEGER SpareLi2;
LARGE_INTEGER SpareLi3;
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
KPRIORITY BasePriority;
HANDLE UniqueProcessId;
ULONG InheritedFromUniqueProcessId;
ULONG HandleCount;
BYTE Reserved4[4];
PVOID Reserved5[11];
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION_DETAILD, *PSYSTEM_PROCESS_INFORMATION_DETAILD;
typedef NTSTATUS (WINAPI *PFN_NT_QUERY_SYSTEM_INFORMATION)(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT OPTIONAL PULONG ReturnLength
);
int main()
{
size_t bufferSize = 102400;
PSYSTEM_PROCESS_INFORMATION_DETAILD pspid=
(PSYSTEM_PROCESS_INFORMATION_DETAILD) malloc (bufferSize);
ULONG ReturnLength;
PFN_NT_QUERY_SYSTEM_INFORMATION pfnNtQuerySystemInformation = (PFN_NT_QUERY_SYSTEM_INFORMATION)
GetProcAddress (GetModuleHandle(TEXT("ntdll.dll")), "NtQuerySystemInformation");
NTSTATUS status;
while (TRUE) {
status = pfnNtQuerySystemInformation (SystemProcessInformation, (PVOID)pspid,
bufferSize, &ReturnLength);
if (status == STATUS_SUCCESS)
break;
else if (status != STATUS_INFO_LENGTH_MISMATCH) { // 0xC0000004L
_tprintf (TEXT("ERROR 0x%X\n"), status);
return 1; // error
}
bufferSize *= 2;
pspid = (PSYSTEM_PROCESS_INFORMATION_DETAILD) realloc ((PVOID)pspid, bufferSize);
}
for (;;
pspid=(PSYSTEM_PROCESS_INFORMATION_DETAILD)(pspid->NextEntryOffset + (PBYTE)pspid)) {
_tprintf (TEXT("ProcessId: %d, ImageFileName: %ls\n"), pspid->UniqueProcessId,
(pspid->ImageName.Length && pspid->ImageName.Buffer)? pspid->ImageName.Buffer: L"");
if (pspid->NextEntryOffset == 0) break;
}
return 0;
}
Ýou can get the process identifier and name for all running processes using the ToolHelp API.
The following code will display the pid and name for each process.
void showProcessInformation() {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hSnapshot) {
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
if(Process32First(hSnapshot, &pe32)) {
do {
printf("pid %d %s\n", pe32.th32ProcessID, pe32.szExeFile);
} while(Process32Next(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
}
}
CreateToolhelp32Snapshot() will give you the process name (but not the path); other than that, you will have to call OpenProcess(). If your code is running in an administrative context you could enable the SE_DEBUG_NAME privilege to get access to processes running under other contexts.
I am getting an error when I try to run a c file which does some basic writes to a serial port. I am trying to run it asynchronously because the writes sometimes take a long time to transfer. My original version had it running synchronously with WriteFile() commands which worked fine. I am new to using OVERLAPPED and would appreciate and input concerning it.
The error I am getting is:
Debug Assertion Failed!
<path to dbgheap.c>
Line: 1317
Expression: _CrtIsValidHeapPointer(pUserData)
when the second write function is called.
In main:
{
//initialized port (with overlapped), DBC, and timeouts
result = write_port(outPortHandle, 128);
result = write_port(outPortHandle, 131);
}
static void CALLBACK write_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) {
//write completed. check for errors? if so throw an exception maybe?
printf("write completed--and made it to callback function\n");
}
int write_port(HANDLE hComm,BYTE* lpBuf) {
OVERLAPPED osWrite = {0};
// Create this write operation's OVERLAPPED structure's hEvent.
osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osWrite.hEvent == NULL)
// error creating overlapped event handle
return 0;
// Issue write.
if (!WriteFileEx(hComm, &lpBuf, 1, &osWrite, &write_compl )) {
if (GetLastError() != ERROR_IO_PENDING) {
// WriteFile failed, but isn't delayed. Report error and abort.
printf("last error: %ld",GetLastError());
return 0; //failed, return false;
}
else {
// Write is pending.
WaitForSingleObjectEx(osWrite.hEvent, 50, TRUE); //50 ms timeout
return -1; //pending
}
}
else {
return 1; //finished
}
}
That was not the full code, sorry. I was using an array of BYTEs as well, not constants. But system("pause")'s were causing my debug assertion failed errors, and after carefully looking through my code, when the WriteFileEx() was successful, it was never setting an alert/timeout on the event in the overlapped structure, so the callback function would never get called. I fixed these problems though.
I just need help with the handling/accessing a single BYTE in a structure which is allocated when a ReadFileEx() function is called (for storing the BYTE that is read so it can be handled). I need to know how to access that BYTE storage using an offset and make the overlapped structure null. Would making the overlapped structure null be as simple as setting the handle in it to INVALID_HANDLE_VALUE?
I think you have a couple of issues:
You are passing an integer as a pointer (your compiler should warn against this or preferably refuse to compile the code):
result = write_port(outPortHandle, 128);
Compare this to the definition of write_port:
int write_port(HANDLE hComm,BYTE* lpBuf) {
The above statements doesn't match. Later on you then pass a pointer to the lpBuf pointer to the WriteFileEx function by taking the address of the BYTE* -> "&lpBuf". This will not result in what you think it will do.
Even if you fix this, you will still have potential lifetime issues whenever the write is successfully queued but won't complete within the 50 ms timeout.
When using overlapped I/O, you need to make sure that the read/write buffer and the overlapped structure remain valid until the I/O is completed, cancelled or the associated device is closed. In your code above you use a pointer to an OVERLAPPED struct that lives on the stack in your call to WriteFileEx. If WriteFileEx does not complete within 50 ms, the pending I/O will have a reference to a non-existing OVERLAPPED struct and you will (hopefully) have an access violation (or worse, silently corrupted stack data somewhere in your app).
The canonical way of handling these lifetime issues (if performance is not a big issue), is to use a custom struct that includes an OVERLAPPED struct and some storage for the data to be read/written. Allocate the struct when posting the write and deallocate the struct from the I/O completion routine. Pass the address of the included OVERLAPPED struct to WriteFileEx, and use e.g. offsetof to get the address to the custom struct from the OVERLAPPED address in the completion routine.
Also note that WriteFileEx does not actually use the hEvent member, IIRC.
EDIT: Added code sample, please note:
I haven't actually tried to compile the code, there might be typos or other problems with the code.
It's not the most efficient way of sending data (allocating/deallocating a memory block for each byte that is sent). It should be easy to improve, though.
#include <stddef.h>
#include <assert.h>
#include <windows.h>
// ...
typedef struct _MYOVERLAPPED
{
OVERLAPPED ol;
BYTE buffer;
} MYOVERLAPPED, *LPMYOVERLAPPED;
// ...
static void CALLBACK write_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
if (NULL == lpOverlapped)
{
assert(!"Should never happen");
return;
}
LPBYTE pOlAsBytes = (LPBYTE)lpOverlapped;
LPBYTE pMyOlAsBytes = pOlAsBytes - offsetof(MYOVERLAPPED, ol);
LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)pOlAsBytes;
if ((ERROR_SUCCESS == dwErrorCode) &&
(sizeof(BYTE) == dwNumberOfBytesTransfered))
{
printf("written %uc\n", pMyOl->buffer);
}
else
{
// handle error
}
free(pMyOl);
}
int write_port(HANDLE hComm, BYTE byte) {
LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)malloc(sizeof(MYOVERLAPPED));
ZeroMemory(pMyOl, sizeof(MYOVERLAPPED));
pMyOl->buffer = byte;
// Issue write.
if (!WriteFileEx(hComm, &pMyOl->buffer, sizeof(BYTE), pMyOl, &write_compl )) {
if (GetLastError() != ERROR_IO_PENDING) {
// WriteFile failed, but isn't delayed. Report error and abort.
free(pMyOl);
printf("last error: %ld",GetLastError());
return 0; //failed, return false;
}
else {
return -1; //pending
}
}
else {
free(pMyOl);
return 1; //finished
}
}
result = write_port(outPortHandle, 128);
result = write_port(outPortHandle, 131);
The lpBuf argument have to be pointers to buffers, not constants.
e.g.
char buffer;
buffer = 128;
result = write_port(outPortHandle, &buffer);
buffer = 131;
result = write_port(outPortHandle, &buffer);
What you really want to do is also pass a buffer length.
e.g.
char buffer[] = { 128, 131 };
result = write_port(outPortHandle, &buffer, sizeof(buffer));
int write_port(HANDLE hComm,BYTE* lpBuf, size_t length) {
...
// Issue write.
if (!WriteFileEx(hComm, &lpBuf, length, &osWrite, &write_compl )) {
...
What's the easiest way to get the filename associated with an open HANDLE in Win32?
I tried the code posted by Mehrdad here. It works, but with limitations:
It should not be used for network shares because the MountPointManager may hang for a very long time.
It uses undocumented API (IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH) I don't like that very much
It does not support USB devices that create virtual COM ports (I need that in my project)
I also studied other approaches like GetFileInformationByHandleEx() and GetFinalPathNameByHandle(), but these are useless as they return only Path + Filename but without drive. Additionally GetFinalPathNameByHandle() also has the hanging bug.
The GetMappedFileName() approach in the MSDN (posted by Max here) is also very limited:
It works only with real files
The file size must not be zero bytes
Directories, Network and COM ports are not supported
The code is clumsy
So I wrote my own code. I tested it on Win XP and on Win 7, 8, and 10. It works perfectly.
NOTE: You do NOT need any additional LIB file to compile this code!
CPP FILE:
t_NtQueryObject NtQueryObject()
{
static t_NtQueryObject f_NtQueryObject = NULL;
if (!f_NtQueryObject)
{
HMODULE h_NtDll = GetModuleHandle(L"Ntdll.dll"); // Ntdll is loaded into EVERY process!
f_NtQueryObject = (t_NtQueryObject)GetProcAddress(h_NtDll, "NtQueryObject");
}
return f_NtQueryObject;
}
// returns
// "\Device\HarddiskVolume3" (Harddisk Drive)
// "\Device\HarddiskVolume3\Temp" (Harddisk Directory)
// "\Device\HarddiskVolume3\Temp\transparent.jpeg" (Harddisk File)
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg" (USB stick)
// "\Device\TrueCryptVolumeP\Data\Passwords.txt" (Truecrypt Volume)
// "\Device\Floppy0\Autoexec.bat" (Floppy disk)
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB" (DVD drive)
// "\Device\Serial1" (real COM port)
// "\Device\USBSER000" (virtual COM port)
// "\Device\Mup\ComputerName\C$\Boot.ini" (network drive share, Windows 7)
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini" (network drive share, Windwos XP)
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" (network folder share, Windwos XP)
// "\Device\Afd" (internet socket)
// "\Device\Console000F" (unique name for any Console handle)
// "\Device\NamedPipe\Pipename" (named pipe)
// "\BaseNamedObjects\Objectname" (named mutex, named event, named semaphore)
// "\REGISTRY\MACHINE\SOFTWARE\Classes\.txt" (HKEY_CLASSES_ROOT\.txt)
DWORD GetNtPathFromHandle(HANDLE h_File, CString* ps_NTPath)
{
if (h_File == 0 || h_File == INVALID_HANDLE_VALUE)
return ERROR_INVALID_HANDLE;
// NtQueryObject() returns STATUS_INVALID_HANDLE for Console handles
if (IsConsoleHandle(h_File))
{
ps_NTPath->Format(L"\\Device\\Console%04X", (DWORD)(DWORD_PTR)h_File);
return 0;
}
BYTE u8_Buffer[2000];
DWORD u32_ReqLength = 0;
UNICODE_STRING* pk_Info = &((OBJECT_NAME_INFORMATION*)u8_Buffer)->Name;
pk_Info->Buffer = 0;
pk_Info->Length = 0;
// IMPORTANT: The return value from NtQueryObject is bullshit! (driver bug?)
// - The function may return STATUS_NOT_SUPPORTED although it has successfully written to the buffer.
// - The function returns STATUS_SUCCESS although h_File == 0xFFFFFFFF
NtQueryObject()(h_File, ObjectNameInformation, u8_Buffer, sizeof(u8_Buffer), &u32_ReqLength);
// On error pk_Info->Buffer is NULL
if (!pk_Info->Buffer || !pk_Info->Length)
return ERROR_FILE_NOT_FOUND;
pk_Info->Buffer[pk_Info->Length /2] = 0; // Length in Bytes!
*ps_NTPath = pk_Info->Buffer;
return 0;
}
// converts
// "\Device\HarddiskVolume3" -> "E:"
// "\Device\HarddiskVolume3\Temp" -> "E:\Temp"
// "\Device\HarddiskVolume3\Temp\transparent.jpeg" -> "E:\Temp\transparent.jpeg"
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg" -> "I:\foto.jpg"
// "\Device\TrueCryptVolumeP\Data\Passwords.txt" -> "P:\Data\Passwords.txt"
// "\Device\Floppy0\Autoexec.bat" -> "A:\Autoexec.bat"
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB" -> "H:\VIDEO_TS\VTS_01_0.VOB"
// "\Device\Serial1" -> "COM1"
// "\Device\USBSER000" -> "COM4"
// "\Device\Mup\ComputerName\C$\Boot.ini" -> "\\ComputerName\C$\Boot.ini"
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini" -> "\\ComputerName\C$\Boot.ini"
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" -> "\\ComputerName\Shares\Dance.m3u"
// returns an error for any other device type
DWORD GetDosPathFromNtPath(const WCHAR* u16_NTPath, CString* ps_DosPath)
{
DWORD u32_Error;
if (wcsnicmp(u16_NTPath, L"\\Device\\Serial", 14) == 0 || // e.g. "Serial1"
wcsnicmp(u16_NTPath, L"\\Device\\UsbSer", 14) == 0) // e.g. "USBSER000"
{
HKEY h_Key;
if (u32_Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Hardware\\DeviceMap\\SerialComm", 0, KEY_QUERY_VALUE, &h_Key))
return u32_Error;
WCHAR u16_ComPort[50];
DWORD u32_Type;
DWORD u32_Size = sizeof(u16_ComPort);
if (u32_Error = RegQueryValueEx(h_Key, u16_NTPath, 0, &u32_Type, (BYTE*)u16_ComPort, &u32_Size))
{
RegCloseKey(h_Key);
return ERROR_UNKNOWN_PORT;
}
*ps_DosPath = u16_ComPort;
RegCloseKey(h_Key);
return 0;
}
if (wcsnicmp(u16_NTPath, L"\\Device\\LanmanRedirector\\", 25) == 0) // Win XP
{
*ps_DosPath = L"\\\\";
*ps_DosPath += (u16_NTPath + 25);
return 0;
}
if (wcsnicmp(u16_NTPath, L"\\Device\\Mup\\", 12) == 0) // Win 7
{
*ps_DosPath = L"\\\\";
*ps_DosPath += (u16_NTPath + 12);
return 0;
}
WCHAR u16_Drives[300];
if (!GetLogicalDriveStrings(300, u16_Drives))
return GetLastError();
WCHAR* u16_Drv = u16_Drives;
while (u16_Drv[0])
{
WCHAR* u16_Next = u16_Drv +wcslen(u16_Drv) +1;
u16_Drv[2] = 0; // the backslash is not allowed for QueryDosDevice()
WCHAR u16_NtVolume[1000];
u16_NtVolume[0] = 0;
// may return multiple strings!
// returns very weird strings for network shares
if (!QueryDosDevice(u16_Drv, u16_NtVolume, sizeof(u16_NtVolume) /2))
return GetLastError();
int s32_Len = (int)wcslen(u16_NtVolume);
if (s32_Len > 0 && wcsnicmp(u16_NTPath, u16_NtVolume, s32_Len) == 0)
{
*ps_DosPath = u16_Drv;
*ps_DosPath += (u16_NTPath + s32_Len);
return 0;
}
u16_Drv = u16_Next;
}
return ERROR_BAD_PATHNAME;
}
HEADER FILE:
#pragma warning(disable: 4996) // wcsnicmp deprecated
#include <winternl.h>
// This makro assures that INVALID_HANDLE_VALUE (0xFFFFFFFF) returns FALSE
#define IsConsoleHandle(h) (((((ULONG_PTR)h) & 0x10000003) == 0x3) ? TRUE : FALSE)
enum OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectAllInformation,
ObjectDataInformation
};
struct OBJECT_NAME_INFORMATION
{
UNICODE_STRING Name; // defined in winternl.h
WCHAR NameBuffer;
};
typedef NTSTATUS (NTAPI* t_NtQueryObject)(HANDLE Handle, OBJECT_INFORMATION_CLASS Info, PVOID Buffer, ULONG BufferSize, PULONG ReturnLength);
There is a correct (although undocumented) way to do this on Windows XP which also works with directories -- the same method GetFinalPathNameByHandle uses on Windows Vista and later.
Here are the eneded declarations. Some of these are already in WInternl.h and MountMgr.h but I just put them here anyway:
#include "stdafx.h"
#include <Windows.h>
#include <assert.h>
enum OBJECT_INFORMATION_CLASS { ObjectNameInformation = 1 };
enum FILE_INFORMATION_CLASS { FileNameInformation = 9 };
struct FILE_NAME_INFORMATION { ULONG FileNameLength; WCHAR FileName[1]; };
struct IO_STATUS_BLOCK { PVOID Dummy; ULONG_PTR Information; };
struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; };
struct MOUNTMGR_TARGET_NAME { USHORT DeviceNameLength; WCHAR DeviceName[1]; };
struct MOUNTMGR_VOLUME_PATHS { ULONG MultiSzLength; WCHAR MultiSz[1]; };
extern "C" NTSYSAPI NTSTATUS NTAPI NtQueryObject(IN HANDLE Handle OPTIONAL,
IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
OUT PVOID ObjectInformation OPTIONAL, IN ULONG ObjectInformationLength,
OUT PULONG ReturnLength OPTIONAL);
extern "C" NTSYSAPI NTSTATUS NTAPI NtQueryInformationFile(IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation,
IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass);
#define MOUNTMGRCONTROLTYPE ((ULONG) 'm')
#define IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH \
CTL_CODE(MOUNTMGRCONTROLTYPE, 12, METHOD_BUFFERED, FILE_ANY_ACCESS)
union ANY_BUFFER {
MOUNTMGR_TARGET_NAME TargetName;
MOUNTMGR_VOLUME_PATHS TargetPaths;
FILE_NAME_INFORMATION NameInfo;
UNICODE_STRING UnicodeString;
WCHAR Buffer[USHRT_MAX];
};
Here's the core function:
LPWSTR GetFilePath(HANDLE hFile)
{
static ANY_BUFFER nameFull, nameRel, nameMnt;
ULONG returnedLength; IO_STATUS_BLOCK iosb; NTSTATUS status;
status = NtQueryObject(hFile, ObjectNameInformation,
nameFull.Buffer, sizeof(nameFull.Buffer), &returnedLength);
assert(status == 0);
status = NtQueryInformationFile(hFile, &iosb, nameRel.Buffer,
sizeof(nameRel.Buffer), FileNameInformation);
assert(status == 0);
//I'm not sure how this works with network paths...
assert(nameFull.UnicodeString.Length >= nameRel.NameInfo.FileNameLength);
nameMnt.TargetName.DeviceNameLength = (USHORT)(
nameFull.UnicodeString.Length - nameRel.NameInfo.FileNameLength);
wcsncpy(nameMnt.TargetName.DeviceName, nameFull.UnicodeString.Buffer,
nameMnt.TargetName.DeviceNameLength / sizeof(WCHAR));
HANDLE hMountPointMgr = CreateFile(_T("\\\\.\\MountPointManager"),
0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, 0, NULL);
__try
{
DWORD bytesReturned;
BOOL success = DeviceIoControl(hMountPointMgr,
IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH, &nameMnt,
sizeof(nameMnt), &nameMnt, sizeof(nameMnt),
&bytesReturned, NULL);
assert(success && nameMnt.TargetPaths.MultiSzLength > 0);
wcsncat(nameMnt.TargetPaths.MultiSz, nameRel.NameInfo.FileName,
nameRel.NameInfo.FileNameLength / sizeof(WCHAR));
return nameMnt.TargetPaths.MultiSz;
}
__finally { CloseHandle(hMountPointMgr); }
}
and here's an example usage:
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hFile = CreateFile(_T("\\\\.\\C:\\Windows\\Notepad.exe"),
0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
assert(hFile != NULL && hFile != INVALID_HANDLE_VALUE);
__try
{
wprintf(L"%s\n", GetFilePath(hFile));
// Prints:
// C:\Windows\notepad.exe
}
__finally { CloseHandle(hFile); }
return 0;
}
edit Thanks for the comments about this being Vista or Server 2008 only. I missed that in the page. Guess I should have read the whole article ;)
It looks like you can use GetFileInformationByHandleEx() to get this information.
You'll likely want to do something like:
GetFileInformationByHandleEx( fileHandle, FILE_NAME_INFO, lpFileInformation, sizeof(FILE_NAME_INFO));
Double check the MSDN page to make sure I haven't misled you too badly :)
Cheers,
Taylor
FWIW, here's the same solution from the MSDN article suggested by Prakash in Python using the wonderful ctypes:
from ctypes import *
# get handle to c:\boot.ini to test
handle = windll.kernel32.CreateFileA("c:\\boot.ini", 0x80000000, 3, 0, 3, 0x80, 0)
hfilemap = windll.kernel32.CreateFileMappingA(handle, 0, 2, 0, 1, 0)
pmem = windll.kernel32.MapViewOfFile(hfilemap, 4, 0, 0, 1)
name = create_string_buffer(1024)
windll.psapi.GetMappedFileNameA(windll.kernel32.GetCurrentProcess(), pmem, name, 1024)
print "The name for the handle 0x%08x is %s" % (handle, name.value)
# convert device name to drive letter
buf = create_string_buffer(512)
size = windll.kernel32.GetLogicalDriveStringsA(511, buf)
names = buf.raw[0:size-1].split("\0")
for drive in names:
windll.kernel32.QueryDosDeviceA(drive[0:2], buf, 512)
if name.value.startswith(buf.value):
print "%s%s" % (drive[0:2], name.value[len(buf.value):])
break
For Windows Vista and later I prefer to use
GetFinalPathNameByHandle()
char buf[MAX_PATH];
GetFinalPathNameByHandleA(fileHandle, buf, sizeof(buf), VOLUME_NAME_DOS)
For Windows XP I prefer the solution by Mehrdad.
So I load GetFinalPathNameByHandle() dynamically via GetProcAddress() and if this fails (because it's Windows XP) I go for Mehrdad's solution with NtQueryObject()
If you need to do this on Win32 pre-Vista or Server 2008, look at the GetMappedFileName(...) function, which is one of the best kept secrets in Win32. WIth a little C/C++-fu, you can memory map a small portion of the file in question, and then pass that handle to this function.
Also, on Win32, you cannot really delete a file that is open (the open/unlink issue mentioned on another answer) - you can mark it for deletion on close, but it will still hang around until its last open handle is closed. Dunno if mapping (via mmap(...)) the file in this case would help, because it has to point back to a physical file...
-=- James.
On unixes there is no real way of reliably doing this. In unix with the traditional unix filesystem, you can open a file and then unlink it (remove its entry from the directory) and use it, at which point the name isn't stored anywhere. In addition, because a file may have multiple hardlinks into the filesystem, each of the names are equivalent, so once you've got just the open handle it wouldn't be clear which filename you should map back towards.
So, you may be able to do this on Win32 using the other answers, but should you ever need to port the application to a unix enviornment, you'll be out of luck. My advice to you is to refactor your program, if possible, so that you don't need the OS to be able to maintain an open resource to filename connection.