I want to get Drive Letter and Name.
I used "DeviceIoControl" and "IOCTL_DISK_GET_DRIVE_LAYOUT_EX" for this reason. I am using Microsoft Visual C++ ultimate Edition.
#define wszDrive L"\\\\.\\PhysicalDrive0"
BOOL GetDriveParition(LPWSTR wszPath, DRIVE_LAYOUT_INFORMATION_EX *pdg)
{
HANDLE hDevice = INVALID_HANDLE_VALUE; // handle to the drive to be examined
BOOL bResult = FALSE; // results flag
DWORD junk = 0; // discard results
hDevice = CreateFileW(wszPath, // drive to open
0, // no access to the drive
FILE_SHARE_READ | // share mode
FILE_SHARE_WRITE,
NULL, // default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL); // do not copy file attributes
if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
{
return (FALSE);
}
bResult = DeviceIoControl(hDevice, // device to be queried
IOCTL_DISK_GET_DRIVE_LAYOUT_EX, // operation to perform
NULL,
0, // no input buffer
pdg,
sizeof(*pdg), // output buffer
&junk, // # bytes returned
NULL); // synchronous I/O
CloseHandle(hDevice);
return (bResult);
}
int wmain(int argc, wchar_t *argv[])
{
DRIVE_LAYOUT_INFORMATION_EX pdg; // disk drive partition structure
BOOL bResult = FALSE; // generic results flag
bResult = GetDriveParition (wszDrive, &pdg);
if (bResult)
{
wprintf(L"Drive path = %ws\n", wszDrive);
wprintf(L"Partition Style = %I64d\n", pdg.PartitionStyle);
wprintf(L"Partition Count = %ld\n", pdg.PartitionCount);
}
else
{
wprintf (L"GetDrivePartition failed. Error %ld.\n", GetLastError ());
}
getch();
}
but when I was performing I confronted to an error which was "error 122".
I think that you meant to say error code 122 rather than 22. That error is ERROR_INSUFFICIENT_BUFFER. As documented, you will need to allocate a larger buffer and try again.
The point here is that the struct is a variable sized struct. You need to allocate dynamic memory large enough to hold information for all the partitions.
Something like this should get you going in the right direction:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define wszDrive L"\\\\.\\PhysicalDrive0"
BOOL GetDriveParition(LPWSTR wszPath, DRIVE_LAYOUT_INFORMATION_EX *pdg, size_t size)
{
HANDLE hDevice = INVALID_HANDLE_VALUE; // handle to the drive to be examined
BOOL bResult = FALSE; // results flag
DWORD junk = 0; // discard results
hDevice = CreateFileW(wszPath, // drive to open
0, // no access to the drive
FILE_SHARE_READ | // share mode
FILE_SHARE_WRITE,
NULL, // default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL); // do not copy file attributes
if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
{
return (FALSE);
}
bResult = DeviceIoControl(hDevice, // device to be queried
IOCTL_DISK_GET_DRIVE_LAYOUT_EX, // operation to perform
NULL,
0, // no input buffer
pdg,
size, // output buffer
&junk, // # bytes returned
NULL); // synchronous I/O
CloseHandle(hDevice);
return (bResult);
}
int wmain(int argc, wchar_t *argv[])
{
DRIVE_LAYOUT_INFORMATION_EX* pdg; // disk drive partition structure
BOOL bResult = FALSE; // generic results flag
size_t size = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + 10*sizeof(PARTITION_INFORMATION_EX);
pdg = (DRIVE_LAYOUT_INFORMATION_EX*) malloc(size);
bResult = GetDriveParition (wszDrive, pdg, size);
if (bResult)
{
wprintf(L"Drive path = %ws\n", wszDrive);
wprintf(L"Partition Style = %I64d\n", pdg->PartitionStyle);
wprintf(L"Partition Count = %ld\n", pdg->PartitionCount);
}
else
{
wprintf (L"GetDrivePartition failed. Error %ld.\n", GetLastError ());
}
free(pdg);
}
I've cast the return value of malloc since you state that you are using a C++ compiler.
Related
In order to write to HD at max. performance I'm using overlapped I/O.
It works.
Upon acquiring 4MB of data (from sensor) I'm writing it to disk.
Then, upon getting the next 4MB I first ask if the previous writing completed.
How can I know what is the optimal block size (4MB ?) that is best for my disk ?
// AsyncFile.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "AsyncFile.h"
/****************************************************************************/
CAsyncFile::CAsyncFile()
{
}
/****************************************************************************/
CAsyncFile::~CAsyncFile()
{
}
/****************************************************************************/
int CAsyncFile::OpenFile(char *pcFileName,
bool bAsync, // Whether async read/write is required
bool bWrite) // True is file is used for writing to
{
DWORD dwAsyncMask = bAsync ? (FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING) : 0;
DWORD dwCreation = bWrite ? CREATE_ALWAYS : OPEN_EXISTING;
DWORD dwAccess = bWrite ? GENERIC_WRITE : GENERIC_READ;
DWORD dwShareMode = bWrite ? FILE_SHARE_READ : FILE_SHARE_WRITE;
if (strlen(pcFileName) < sizeof(m_cFileName))
strcpy_s(m_cFileName, 256, pcFileName);
else
m_cFileName[0] = 0; // NULL (error - file name is too long)
// Calling openFile() sets a valid value to the file handle
m_hFileHandle = INVALID_HANDLE_VALUE;
// Auto reset (manual reset=false), init state = false, no name
m_hIoCompleted = CreateEvent(NULL, FALSE, FALSE, NULL);
// Init OVERLAPPED structure, for async read
m_tOverlapped.Offset = 0;
m_tOverlapped.OffsetHigh = 0;
m_tOverlapped.hEvent = m_hIoCompleted;
m_Event = m_tOverlapped.hEvent;
if (m_hFileHandle != INVALID_HANDLE_VALUE)
{
// File is already openned; check open mode
if ((bAsync == m_bAsync) && (bWrite == m_bWrite))
return (ASYNCFILE_OK);
// File is already openned, but in other mode; Should close file
// before using it again
return ASYNCFILE_FILE_IS_NOT_IN_WRITE_MODE;
}
m_hFileHandle =
CreateFile((LPCTSTR)m_cFileName,
dwAccess, // Open for read or write
dwShareMode, //
NULL, // No SECURITY_ATTRBUTES
dwCreation, // Open exisiting file (if read) \ create new (if write)
dwAsyncMask, // For asynchronous operations, for maximum asynchronous performence
0);
if (m_hFileHandle == INVALID_HANDLE_VALUE)
{
DWORD dwError = GetLastError();
return ASYNCFILE_FAILED_TO_OPEN_FILE;
}
//In case file opened for reading, get its size
if (bWrite == false)
{
GetFileSizeEx(m_hFileHandle, &m_FileSize);
}
// Save open mode
m_bAsync = bAsync;
m_bWrite = bWrite;
return ASYNCFILE_OK;
}
/****************************************************************************/
int CAsyncFile::CloseFile()
{
//BOOL Status;
if (!CloseHandle(m_hFileHandle))
return ASYNCFILE_FAILED_TO_CLOSE_FILE;
if (!CloseHandle(m_hIoCompleted))
return ASYNCFILE_FAILED_TO_CLOSE_FILE;
return ASYNCFILE_OK;
}
/****************************************************************************/
int CAsyncFile::StartAsyncRead(void* pBuffer,
DWORD dwReadSize,
bool* pbEof)
{
*pbEof = false; // By default, EOF is false
int iError;
if (m_hFileHandle == INVALID_HANDLE_VALUE)
return (false);
if (!ReadFile(m_hFileHandle,
pBuffer,
dwReadSize,
NULL, // actual bytes read is not valid now
&m_tOverlapped))
{
if ((iError = GetLastError()) == ERROR_HANDLE_EOF)
{
*pbEof = true;
return ASYNCFILE_OK;
}
else if (!(m_bAsync && (iError == ERROR_IO_PENDING)))
{
return ASYNCFILE_START_READ_FAILED;
}
}
return ASYNCFILE_OK;
}
/****************************************************************************/
int CAsyncFile::WaitAsyncOperationEnd(DWORD* pdwActualBytesTransferred)
{
if (m_hFileHandle == INVALID_HANDLE_VALUE)
return ASYNCFILE_WAIT_FOR_COMPLETION_FAILED;
// Wait for read operation to complete
if (!GetOverlappedResult(m_hFileHandle,
&m_tOverlapped,
pdwActualBytesTransferred,
true))
return ASYNCFILE_WAIT_FOR_COMPLETION_FAILED;
return ASYNCFILE_OK;
}
/****************************************************************************/
int CAsyncFile::StartAsyncWrite(void* pSrcBuf,
DWORD dwSize) // In bytes
{
int iError;
if (!WriteFile(m_hFileHandle,
pSrcBuf,
dwSize,
NULL, // actual bytes written is not valid now
&m_tOverlapped))
{
iError = GetLastError();
if (iError != ERROR_IO_PENDING)
return ASYNCFILE_START_WRITE_FAILED;
}
return ASYNCFILE_OK;
}
/****************************************************************************/
void CAsyncFile::SetFilePosition(UINT64 Position)
{
m_tOverlapped.Offset = Position & 0xFFFFFFFF;
m_tOverlapped.OffsetHigh = Position >> 32;
}
/****************************************************************************/
UINT64 CAsyncFile::GetFilePosition()
{
UINT64 Position;
Position = (m_tOverlapped.Offset) | ((UINT64)m_tOverlapped.OffsetHigh << 32);
return (Position);
}
/****************************************************************************/
UINT64 CAsyncFile::GetFileSize()
{
return (m_FileSize.QuadPart);
}
Well it depends on the average size of your files as well. If your file sizes are constantly ranging near 7 to 8 MB, then there would be some benefit in increasing the buffer size to 8192KB. How old is the drive? How often have you used it, many other factors come into play. We are able to learn more about this topic from an excellent piece of software called FastCopy. Hope this helps.
Convention wisdom to eject a USB drive on Windows is the following sequence:
CreateFile (drive letter, with read/write rights, file share read and write)
DeviceIoControl(FSCTL_LOCK_VOLUME)
DeviceIoControl(FSCTL_DISMOUNT_VOLUME)
DeviceIoControl(IOCTL_STORAGE_MEDIA_REMOVAL) PreventMediaRemoval = FALSE
DeviceIoControl(IOCTL_STORAGE_EJECT_MEDIA)
This worked fine until a recent change in Windows 10 (not sure when). Now the drive is still properly ejected, but then Windows immediately remounts the drive.
What needs to be done to eject the drive until the user removes it and puts it in again?
Using CM_Request_Device_EjectW API works for me. You can have a try.
The following is the complete code I tested and it from "How to Prepare a USB Drive for Safe Removal" at codeproject.
(Here the "F" is my USB drive letter. Replace it using your own one.)
#include <stdio.h>
#include <windows.h>
#include <Setupapi.h>
#include <winioctl.h>
#include <winioctl.h>
#include <cfgmgr32.h>
//-------------------------------------------------
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber, UINT DriveType, char* szDosDeviceName);
//-------------------------------------------------
//-------------------------------------------------
int main()
{
char DriveLetter = 'F';
DriveLetter &= ~0x20; // uppercase
if (DriveLetter < 'A' || DriveLetter > 'Z') {
return 1;
}
char szRootPath[] = "F:\\"; // "X:\" -> for GetDriveType
szRootPath[0] = DriveLetter;
char szDevicePath[] = "F:"; // "X:" -> for QueryDosDevice
szDevicePath[0] = DriveLetter;
char szVolumeAccessPath[] = "\\\\.\\F:"; // "\\.\X:" -> to open the volume
szVolumeAccessPath[4] = DriveLetter;
long DeviceNumber = -1;
// open the storage volume
HANDLE hVolume = CreateFile(szVolumeAccessPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
if (hVolume == INVALID_HANDLE_VALUE) {
return 1;
}
// get the volume's device number
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned = 0;
long res = DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL);
if (res) {
DeviceNumber = sdn.DeviceNumber;
}
CloseHandle(hVolume);
if (DeviceNumber == -1) {
return 1;
}
// get the drive type which is required to match the device numbers correctely
UINT DriveType = GetDriveType(szRootPath);
// get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
char szDosDeviceName[MAX_PATH];
res = QueryDosDevice(szDevicePath, szDosDeviceName, MAX_PATH);
if (!res) {
return 1;
}
// get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
DEVINST DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber, DriveType, szDosDeviceName);
if (DevInst == 0) {
return 1;
}
PNP_VETO_TYPE VetoType = PNP_VetoTypeUnknown;
WCHAR VetoNameW[MAX_PATH];
VetoNameW[0] = 0;
bool bSuccess = false;
// get drives's parent, e.g. the USB bridge, the SATA port, an IDE channel with two drives!
DEVINST DevInstParent = 0;
res = CM_Get_Parent(&DevInstParent, DevInst, 0);
for (long tries = 1; tries <= 3; tries++) { // sometimes we need some tries...
VetoNameW[0] = 0;
// CM_Query_And_Remove_SubTree doesn't work for restricted users
//res = CM_Query_And_Remove_SubTreeW(DevInstParent, &VetoType, VetoNameW, MAX_PATH, CM_REMOVE_NO_RESTART); // CM_Query_And_Remove_SubTreeA is not implemented under W2K!
//res = CM_Query_And_Remove_SubTreeW(DevInstParent, NULL, NULL, 0, CM_REMOVE_NO_RESTART); // with messagebox (W2K, Vista) or balloon (XP)
res = CM_Request_Device_EjectW(DevInstParent, &VetoType, VetoNameW, MAX_PATH, 0);
//res = CM_Request_Device_EjectW(DevInstParent, NULL, NULL, 0, 0); // with messagebox (W2K, Vista) or balloon (XP)
bSuccess = (res == CR_SUCCESS && VetoType == PNP_VetoTypeUnknown);
if (bSuccess) {
break;
}
Sleep(500); // required to give the next tries a chance!
}
if (bSuccess) {
printf("Success\n\n");
return 0;
}
printf("failed\n");
printf("Result=0x%2X\n", res);
if (VetoNameW[0]) {
printf("VetoName=%ws)\n\n", VetoNameW);
}
return 1;
}
//-----------------------------------------------------------
//----------------------------------------------------------------------
// returns the device instance handle of a storage volume or 0 on error
//----------------------------------------------------------------------
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber, UINT DriveType, char* szDosDeviceName)
{
bool IsFloppy = (strstr(szDosDeviceName, "\\Floppy") != NULL); // who knows a better way?
GUID* guid;
switch (DriveType) {
case DRIVE_REMOVABLE:
if (IsFloppy) {
guid = (GUID*)&GUID_DEVINTERFACE_FLOPPY;
}
else {
guid = (GUID*)&GUID_DEVINTERFACE_DISK;
}
break;
case DRIVE_FIXED:
guid = (GUID*)&GUID_DEVINTERFACE_DISK;
break;
case DRIVE_CDROM:
guid = (GUID*)&GUID_DEVINTERFACE_CDROM;
break;
default:
return 0;
}
// Get device interface info set handle for all devices attached to system
HDEVINFO hDevInfo = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) {
return 0;
}
// Retrieve a context structure for a device interface of a device information set
DWORD dwIndex = 0;
long res;
BYTE Buf[1024];
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
DWORD dwSize;
spdid.cbSize = sizeof(spdid);
while (true) {
res = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, guid, dwIndex, &spdid);
if (!res) {
break;
}
dwSize = 0;
SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &dwSize, NULL); // check the buffer size
if (dwSize != 0 && dwSize <= sizeof(Buf)) {
pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!
ZeroMemory(&spdd, sizeof(spdd));
spdd.cbSize = sizeof(spdd);
long res = SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, pspdidd, dwSize, &dwSize, &spdd);
if (res) {
// in case you are interested in the USB serial number:
// the device id string contains the serial number if the device has one,
// otherwise a generated id that contains the '&' char...
/*
DEVINST DevInstParent = 0;
CM_Get_Parent(&DevInstParent, spdd.DevInst, 0);
char szDeviceIdString[MAX_PATH];
CM_Get_Device_ID(DevInstParent, szDeviceIdString, MAX_PATH, 0);
printf("DeviceId=%s\n", szDeviceIdString);
*/
// open the disk or cdrom or floppy
HANDLE hDrive = CreateFile(pspdidd->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hDrive != INVALID_HANDLE_VALUE) {
// get its device number
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned = 0;
res = DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL);
if (res) {
if (DeviceNumber == (long)sdn.DeviceNumber) { // match the given device number with the one of the current device
CloseHandle(hDrive);
SetupDiDestroyDeviceInfoList(hDevInfo);
return spdd.DevInst;
}
}
CloseHandle(hDrive);
}
}
}
dwIndex++;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return 0;
}
I'm trying to find a way to let the system tell me whenever there's a new entry in the USN Change Journal to track modifications made to files and directories on an NTFS volume (Server 2008/2012).
This way I don't have to constantly poll the journal and can just let my thread sleep until I get notified when there's a new change-event.
However, is there even such an interrupt?
The FSCTL_QUERY_USN_JOURNAL function doesn't specifically mention interrupts (events, notifications), nor have I been able to find another way to achieve this with less intensive poll-and-compare techniques.
I'm not a hard-core programmer so there may be simpler ways to tie these functions to interrupts that I'm not aware of.
Could I perhaps find out where the USN Change Journal is stored and watch that file with another process that can generate and interrupt on change?
https://msdn.microsoft.com/en-us/library/aa365729(v=vs.85).aspx
The code posted here blocks the executing thread till the new USN record is created in the Journal. When new records arrive, the thread awakens and you can process changes and/or notify listeners via a callback that filesystem has changed (in the example it just prints message to the console). Then the thread blocks again. This example uses one thread per volume (so for each volume, separate NTFSChangesWatcher class instance needed).
It is not specified which tools or language you use, so I will write as I did it. To run this code, create a Visual Studio C++ Win32 Console Application.
Create NTFSChangesWatcher class. Paste this code in NTFSChangesWatcher.h file (replacing auto-generated one):
#pragma once
#include <windows.h>
#include <memory>
class NTFSChangesWatcher
{
public:
NTFSChangesWatcher(char drive_letter);
~NTFSChangesWatcher() = default;
// Method which runs an infinite loop and waits for new update sequence number in a journal.
// The thread is blocked till the new USN record created in the journal.
void WatchChanges();
private:
HANDLE OpenVolume(char drive_letter);
bool CreateJournal(HANDLE volume);
bool LoadJournal(HANDLE volume, USN_JOURNAL_DATA* journal_data);
bool NTFSChangesWatcher::WaitForNextUsn(PREAD_USN_JOURNAL_DATA read_journal_data) const;
std::unique_ptr<READ_USN_JOURNAL_DATA> GetWaitForNextUsnQuery(USN start_usn);
bool NTFSChangesWatcher::ReadJournalRecords(PREAD_USN_JOURNAL_DATA journal_query, LPVOID buffer,
DWORD& byte_count) const;
std::unique_ptr<READ_USN_JOURNAL_DATA> NTFSChangesWatcher::GetReadJournalQuery(USN low_usn);
char drive_letter_;
HANDLE volume_;
std::unique_ptr<USN_JOURNAL_DATA> journal_;
DWORDLONG journal_id_;
USN last_usn_;
// Flags, which indicate which types of changes you want to listen.
static const int FILE_CHANGE_BITMASK;
static const int kBufferSize;
};
and this code in NTFSChangesWatcher.cpp file:
#include "NTFSChangesWatcher.h"
#include <iostream>
using namespace std;
const int NTFSChangesWatcher::kBufferSize = 1024 * 1024 / 2;
const int NTFSChangesWatcher::FILE_CHANGE_BITMASK =
USN_REASON_RENAME_NEW_NAME | USN_REASON_SECURITY_CHANGE | USN_REASON_BASIC_INFO_CHANGE | USN_REASON_DATA_OVERWRITE |
USN_REASON_DATA_TRUNCATION | USN_REASON_DATA_EXTEND | USN_REASON_CLOSE;
NTFSChangesWatcher::NTFSChangesWatcher(char drive_letter) :
drive_letter_(drive_letter)
{
volume_ = OpenVolume(drive_letter_);
journal_ = make_unique<USN_JOURNAL_DATA>();
bool res = LoadJournal(volume_, journal_.get());
if (!res) {
cout << "Failed to load journal" << endl;
return;
}
journal_id_ = journal_->UsnJournalID;
last_usn_ = journal_->NextUsn;
}
HANDLE NTFSChangesWatcher::OpenVolume(char drive_letter) {
wchar_t pattern[10] = L"\\\\?\\a:";
pattern[4] = static_cast<wchar_t>(drive_letter);
HANDLE volume = nullptr;
volume = CreateFile(
pattern, // lpFileName
// also could be | FILE_READ_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, // dwDesiredAccess
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // share mode
NULL, // default security attributes
OPEN_EXISTING, // disposition
// It is always set, no matter whether you explicitly specify it or not. This means, that access
// must be aligned with sector size so we can only read a number of bytes that is a multiple of the sector size.
FILE_FLAG_NO_BUFFERING, // file attributes
NULL // do not copy file attributes
);
if (volume == INVALID_HANDLE_VALUE) {
// An error occurred!
cout << "Failed to open volume" << endl;
return nullptr;
}
return volume;
}
bool NTFSChangesWatcher::CreateJournal(HANDLE volume) {
DWORD byte_count;
CREATE_USN_JOURNAL_DATA create_journal_data;
bool ok = DeviceIoControl(volume, // handle to volume
FSCTL_CREATE_USN_JOURNAL, // dwIoControlCode
&create_journal_data, // input buffer
sizeof(create_journal_data), // size of input buffer
NULL, // lpOutBuffer
0, // nOutBufferSize
&byte_count, // number of bytes returned
NULL) != 0; // OVERLAPPED structure
if (!ok) {
// An error occurred!
}
return ok;
}
bool NTFSChangesWatcher::LoadJournal(HANDLE volume, USN_JOURNAL_DATA* journal_data) {
DWORD byte_count;
// Try to open journal.
if (!DeviceIoControl(volume, FSCTL_QUERY_USN_JOURNAL, NULL, 0, journal_data, sizeof(*journal_data), &byte_count,
NULL)) {
// If failed (for example, in case journaling is disabled), create journal and retry.
if (CreateJournal(volume)) {
return LoadJournal(volume, journal_data);
}
return false;
}
return true;
}
void NTFSChangesWatcher::WatchChanges() {
auto u_buffer = make_unique<char[]>(kBufferSize);
auto read_journal_query = GetWaitForNextUsnQuery(last_usn_);
while (true) {
// This function does not return until new USN record created.
WaitForNextUsn(read_journal_query.get());
cout << "New entry created in the journal!" << endl;
auto journal_query = GetReadJournalQuery(read_journal_query->StartUsn);
DWORD byte_count;
if (!ReadJournalRecords(journal_query.get(), u_buffer.get(), byte_count)) {
// An error occurred.
cout << "Failed to read journal records" << endl;
}
last_usn_ = *(USN*)u_buffer.get();
read_journal_query->StartUsn = last_usn_;
// If you need here you can:
// Read and parse Journal records from the buffer.
// Notify an NTFSChangeObservers about journal changes.
}
}
bool NTFSChangesWatcher::WaitForNextUsn(PREAD_USN_JOURNAL_DATA read_journal_data) const {
DWORD bytes_read;
bool ok = true;
// This function does not return until new USN record created.
ok = DeviceIoControl(volume_, FSCTL_READ_USN_JOURNAL, read_journal_data, sizeof(*read_journal_data),
&read_journal_data->StartUsn, sizeof(read_journal_data->StartUsn), &bytes_read,
nullptr) != 0;
return ok;
}
unique_ptr<READ_USN_JOURNAL_DATA> NTFSChangesWatcher::GetWaitForNextUsnQuery(USN start_usn) {
auto query = make_unique<READ_USN_JOURNAL_DATA>();
query->StartUsn = start_usn;
query->ReasonMask = 0xFFFFFFFF; // All bits.
query->ReturnOnlyOnClose = FALSE; // All entries.
query->Timeout = 0; // No timeout.
query->BytesToWaitFor = 1; // Wait for this.
query->UsnJournalID = journal_id_; // The journal.
query->MinMajorVersion = 2;
query->MaxMajorVersion = 2;
return query;
}
bool NTFSChangesWatcher::ReadJournalRecords(PREAD_USN_JOURNAL_DATA journal_query, LPVOID buffer,
DWORD& byte_count) const {
return DeviceIoControl(volume_, FSCTL_READ_USN_JOURNAL, journal_query, sizeof(*journal_query), buffer, kBufferSize,
&byte_count, nullptr) != 0;
}
unique_ptr<READ_USN_JOURNAL_DATA> NTFSChangesWatcher::GetReadJournalQuery(USN low_usn) {
auto query = make_unique<READ_USN_JOURNAL_DATA>();
query->StartUsn = low_usn;
query->ReasonMask = 0xFFFFFFFF; // All bits.
query->ReturnOnlyOnClose = FALSE;
query->Timeout = 0; // No timeout.
query->BytesToWaitFor = 0;
query->UsnJournalID = journal_id_;
query->MinMajorVersion = 2;
query->MaxMajorVersion = 2;
return query;
}
Now you can use it (for example in the main function for testing):
#include "NTFSChangesWatcher.h"
int _tmain(int argc, _TCHAR* argv[])
{
auto watcher = new NTFSChangesWatcher('z');
watcher->WatchChanges();
return 0;
}
And console output should be like this on every change in the filesystem:
This code was slightly reworked to remove unrelated details and is a part of the Indexer++ project. So for more details, you can refer to the original code.
You can use Journal, but in this case I'd use easier method via registering a directory notification by calling the FindFirstChangeNotification or ReadDirectoryChangesW functions, see https://msdn.microsoft.com/en-us/library/aa364417.aspx
If you'd prefer to use Journal, this is - I think - the best introductory article with many examples. It is written for W2K, but those concepts are still valid: https://www.microsoft.com/msj/0999/journal/journal.aspx
I'm trying to make DuplicateHandle() for a file that another process writes. I succeeded, but I get the position of the owner process. After I seek to the beginning it seeks also in the owner process. Can I somehow seek without changing the first process's progress?
EDIT:
Another application opens this file without CreateFile. Is thare a way to read the file form the begining with ReadFile, without seeking manually?
EDIT again:
There isn't a way to read only from one side with duplicated handle. Thanks for helping.
From MSDN:
The duplicate handle refers to the same object as the original handle. Therefore, any changes to the object are reflected through both handles. For example, if you duplicate a file handle, the current file position is always the same for both handles. For file handles to have different file positions, use the CreateFile function to create file handles that share access to the same file.
Instead of DuplicateHandle, you must call CreateFile in both process, with the right combination of access mode and sharing flag. MSDN has the full set of rules, here is a combination that works :
Writer process :
HANDLE file = CreateFile(..., GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, ...);
Reader process :
HANDLE file = CreateFile(..., GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, ...);
If you need to play with the flags, here is the (crude) test application I wrote to answer your question :
// 2process1file.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#define NUMBER_OF_LINES 100
#define IO_PERIOD 250
static const char message[] = "The quick brown fox jumps over the lazy dog.\n";
HANDLE file = INVALID_HANDLE_VALUE;
BOOL CtrlHandler(DWORD ctltype)
{
if(file != INVALID_HANDLE_VALUE)
{
CloseHandle(file);
file = INVALID_HANDLE_VALUE;
}
return FALSE;
}
int _tmain(int argc, _TCHAR* argv[])
{
if(argc == 3)
{
DWORD access = GENERIC_READ;
DWORD share = FILE_SHARE_READ;
bool is_writer = false;
if((*argv[1]|' ') == 'w')
{
access |= GENERIC_WRITE;
is_writer = true;
}
else
{
share |= FILE_SHARE_WRITE;
}
file = CreateFile(argv[2], access, share, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if(file != INVALID_HANDLE_VALUE)
{
DWORD nbytes = 1;
SetFilePointer(file, 0, 0, FILE_BEGIN); //Redundant when writing
for(int i=0; (i<NUMBER_OF_LINES) && nbytes; ++i) {
if(is_writer) {
if(WriteFile(file, message, sizeof(message)-1, &nbytes, 0) == 0)
{
//Write failed somehow
break;
}
//Sleep(INFINITE);
if(i%25 == 0) printf("%d\n", i);
} else {
char buffer[sizeof message] = "";
if(ReadFile(file, buffer, sizeof(buffer)-1, &nbytes, 0) && nbytes) {
buffer[sizeof(buffer)-1] = 0;
printf(buffer);
} else {
//Read failed somehow
break;
}
}
Sleep(IO_PERIOD);
}
CloseHandle(file);
file = INVALID_HANDLE_VALUE;
}
}
else
{
wprintf(L"Usage : %s [w|r] filename\n");
}
return 0;
}
I'm trying to look up the physical drive number (as in, I need the N in \\.\PhysicalDriveN to open the block device for reading) from the drive letter of a CDROM drive on Windows 7. This page indicates that IOCTL_STORAGE_GET_DEVICE_NUMBER should work, but it returns 0 for the drive number for both C: and D: (where D: is the removable drive), so that can't be right. IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS is also suggested, but that fails with an ERROR_INVALID_FUNCTION on D:.
I can't help but feel that I've missed a crucial concept somewhere.
Here's my code:
#include "stdafx.h"
#include "Windows.h"
void printLastError(){
DWORD lastError;
DWORD bytesReturned;
WCHAR outbuf[2048];
lastError = GetLastError();
bytesReturned = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL, lastError, LANG_USER_DEFAULT, outbuf, 2048, NULL);
if (bytesReturned > 0){
wprintf(outbuf);
} else {
printf("Error %d while formatting error %d\n", GetLastError(), lastError);
}
}
void readDeviceNumberByExtents(HANDLE hFile){
BOOL ioctlSuccess;
DWORD bytesReturned;
VOLUME_DISK_EXTENTS vde;
ioctlSuccess = DeviceIoControl(hFile,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
NULL, 0, &vde, sizeof(vde), &bytesReturned, NULL);
if (ioctlSuccess != 0){
printf("%d\n", vde.Extents->DiskNumber );
} else {
printLastError();
}
}
void readDeviceNumberByStorage(HANDLE hFile){
BOOL ioctlSuccess;
DWORD bytesReturned;
STORAGE_DEVICE_NUMBER sdn;
ioctlSuccess = DeviceIoControl(hFile,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL, 0, &sdn, sizeof(sdn), &bytesReturned, NULL);
if (ioctlSuccess != 0){
printf("%d\n", sdn.DeviceNumber );
} else {
printLastError();
}
}
void runTest(WCHAR* driveName){
HANDLE driveHandle;
DWORD diskNumber;
driveHandle = CreateFile(driveName,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (driveHandle != INVALID_HANDLE_VALUE){
wprintf(L"Opened %s\n", driveName);
printf("Device number by extents: ");
readDeviceNumberByExtents(driveHandle);
printf("Device number by storage: ");
readDeviceNumberByStorage(driveHandle);
CloseHandle(driveHandle);
} else {
printf("Failure!\n");
}
}
int _tmain(int argc, _TCHAR* argv[])
{
runTest(L"\\\\.\\C:");
printf("\n");
runTest(L"\\\\.\\D:");
getc(stdin);
return 0;
}
...and the output when I run it, either as Administrator or not:
Opened \\.\C:
Device number by extents: 0
Device number by storage: 0
Opened \\.\D:
Device number by extents: Incorrect function.
Device number by storage: 0
The "\\.\PhysicalDriveN" only works for (things that act like) hard drives, not removable disks. If something acts like a removable disk (or floppy, CD-ROM, etc.), "\\.\X:" opens the raw drive (other drives don't support partitions, so the distinction between "\\.\x:" and "\\.\PhysicalDiskN" doesn't exist for them). You normally want to use GetDriveType to figure out what kind of disk you have, and only if that says it's a DRIVE_FIXED do you attempt to find the drive number and use "\\.\PhysicalDriveN" with it.
It's C#.Net code, but this is what i've written to do the job:
using System.Management; //Add in a reference to this as well in the project settings
public static string GetPhysicalDevicePath(char DriveLetter)
{
ManagementClass devs = new ManagementClass( #"Win32_Diskdrive");
{
ManagementObjectCollection moc = devs.GetInstances();
foreach(ManagementObject mo in moc)
{
foreach (ManagementObject b in mo.GetRelated("Win32_DiskPartition"))
{
foreach (ManagementBaseObject c in b.GetRelated("Win32_LogicalDisk"))
{
string DevName = string.Format("{0}", c["Name"]);
if (DevName[0] == DriveLetter)
return string.Format("{0}", mo["DeviceId"]);
}
}
}
}
return "";
}