Программно монтируем диски в директорию

В этой статье рассматривается, как программно монтировать жесткие диски в директории. По умолчанию Windows монтирует несъемные носители автоматически, то есть если вы подключаете новый жесткий диск к компьютеру и после загрузки операционной системы, и установки необходимых драйверов, вы можете увидеть его в проводнике. В операционных системах UNIX существует возможность монтирования жестких дисков в директории, так называемые точки монтирования. Начиная с Windows NT 4.0 в поставку операционной системы входит утилита mountvol.exe, которая через интерфейс командной строки позволяет создавать, удалять и выводит список точек подключения дисков. Результат работы которой вы видите на рисунке:
Программно монтируем диски в директорию
После не продолжительной работы с данной утилитой, мне захотелось реализовать предоставленные возможности самому, программно. Первым шагом, нужно было, выяснить какие API функции использует mountvol.exe, для этого использовалась программа Dependency Walker входящая в состав поставки среды разработки, ниже на рисунке предоставлено основное окно программы с загруженной в нее утилиты mountvol.exe:
Программно монтируем диски в директорию
Из результата работы Dependency Walker мы видим какие API функции использует mountvol.exe. Для создания программы-примера использовалась среда разработки MS Visual C++ 6.0, тип приложения «Win 32 Console Application».Для начала рассмотрим функций, которые будут использоваться в приложении, и так по порядку:

HANDLE FindFirstVolume(LPTSTR lpszVolumeName,DWORD cchBufferLength);

Данная функция используется для начала сканирования компьютера на наличие томов, возвращаемое значение хендел который используется для последующего поиска, в переменную lpszVolumeName возвращается указатель на буфер, содержащий уникальный индефикатор(GUID) первого найденного тома.

Для дальнейшего поиска используется функция:

BOOL FindNextVolume(
HANDLE hFindVolume,
LPTSTR lpszVolumeName,
DWORD cchBufferLength
);

Первым параметром, которой является хендел, полученный при помощи вызова функции FindFirstVolume, в переменную lpszVolumeName возвращаются последующие найденные индефикаторы(GUID) томов.

Для завершения поиска необходимо осуществить вызов функции:

BOOL FindVolumeClose(HANDLE hFindVolume);

Единственным параметром функции, которой является хендел поиска.

Для определения типа тома использовалась функция:

UINT GetDriveType( LPCTSTR lpRootPathName);

Параметром которой является индефикатор тома, возвращаемое значение:

DRIVE_UNKNOWN Не известный тип
DRIVE_NO_ROOT_DIR Не точек подключения
DRIVE_REMOVABLE Съёмный диск
DRIVE_FIXED Фиксированный диск
DRIVE_REMOTE Удалённый или network диск
DRIVE_CDROM CD-ROM диск
DRIVE_RAMDISK RAM диск

Для определения файловой системы и метки тома использовалась функция:

BOOL GetVolumeInformation(
LPCTSTR lpRootPathName,
LPTSTR lpVolumeNameBuffer,
DWORD nVolumeNameSize,
LPDWORD lpVolumeSerialNumber,
LPDWORD lpMaximumComponentLength,
LPDWORD lpFileSystemFlags,
LPTSTR lpFileSystemNameBuffer,
DWORD nFileSystemNameSize);

Первый параметр которой является уникальный индефикатор тома, в параметра lpVolumeNameBuffer возвращается метка тома, в параметр lpFileSystemNameBuffer возвращается указатель на буфер содержащий имя файловой системы.

Мною также был найден еще один способ получения уникальных индефикаторов томов, для этого использовалась связка функций GetLogicalDrives и GetVolumeNameForVolumeMountPoint, подробней о их параметрах:

DWORD GetLogicalDrives(void);

Функция возвращает битовую маску, которая содержит все доступные тома в системе.

BOOL GetVolumeNameForVolumeMountPoint(
LPCTSTR lpszVolumeMountPoint,
LPTSTR lpszVolumeName,
DWORD cchBufferLength);

Функция возвращает уникальный индефикатор тома по его точке монтирования.
На рисунке представлен результат работы этих двух способов:
Программно монтируем диски в директорию
Для удаления точки монтирования использовалась функция:

BOOL DeleteVolumeMountPoint(LPCTSTR lpszVolumeMountPoint);

Параметром которой является точка монтирования.

И самая главная функция по монтированию томов:

BOOL SetVolumeMountPoint(
LPCTSTR lpszVolumeMountPoint,
LPCTSTR lpszVolumeName);

Первый параметр которой является точка последующего монтирования, в нашем случае это директория, например “C:\\mnt\\”, второй параметр это уникальный индефикатор тома.

Предупреждение:
Точка куда будет монтироваться том, в нашем случае директория должна быть пустой!!!
На содержимое монтируемого тома это условие не распространяется.

А теперь пробуем все вместе, далее приведен исходный текст программы, которая сначала выводит двумя способами уникальные индефикаторы томов, так же файловую систему, тип тома и метку, дальше она производит демонтирование заданного диска и последующее его монтирование в точку C:\\mnt\\ после паузы возвращает первоначальную точку.

#include <iostream.h>
#include <windows.h>
#include <conio.h>
 
HANDLE (WINAPI * FindFirstVolume)(LPTSTR,DWORD);
BOOL   (WINAPI * FindNextVolume)(HANDLE,LPTSTR,DWORD);
BOOL   (WINAPI * FindVolumeClose)(HANDLE);
BOOL   (WINAPI * GetVolumeNameForVolumeMountPoint)(LPCTSTR,LPTSTR,DWORD);
BOOL   (WINAPI * DeleteVolumeMountPoint)(LPCTSTR);
BOOL   (WINAPI * SetVolumeMountPoint)(LPCTSTR,LPCTSTR);
 
void ErrorView();
 
int main()
{
        HANDLE hMount;
 
        char szPath[MAX_PATH],
                 szRecvBuff[MAX_PATH+1],
                 szFileSystem[MAX_PATH+1];
 
        HMODULE hmod;
        if((hmod = LoadLibrary("Kernel32.dll")))
        {
                if(!(FindFirstVolume  = (HANDLE(WINAPI *)
                        (LPTSTR,DWORD))GetProcAddress(hmod,"FindFirstVolumeA")))
                {cout<<"Error GetProcAddress...1"<<endl;return -1;}
                if(!(FindNextVolume   = (BOOL(WINAPI *)
                        (HANDLE,LPTSTR,DWORD))GetProcAddress(hmod,"FindNextVolumeA")))
                {cout<<"Error GetProcAddress...2"<<endl;return -1;}
                if(!(FindVolumeClose  = (BOOL(WINAPI *)
                        (HANDLE))GetProcAddress(hmod,"FindVolumeClose")))
                {cout<<"Error GetProcAddress...3"<<endl;return -1;}
                if(!(GetVolumeNameForVolumeMountPoint  = (BOOL(WINAPI *)
                        (LPCTSTR,LPTSTR,DWORD))GetProcAddress(hmod,
                        "GetVolumeNameForVolumeMountPointA")))
                {cout<<"Error GetProcAddress...4"<<endl;return -1;}
                if(!(DeleteVolumeMountPoint  = (BOOL(WINAPI *)
                        (LPCTSTR))GetProcAddress(hmod,"DeleteVolumeMountPointA")))
                {cout<<"Error GetProcAddress...5"<<endl;return -1;}
                if(!(SetVolumeMountPoint  = (BOOL(WINAPI *)
                        (LPCTSTR,LPCTSTR))GetProcAddress(hmod,"SetVolumeMountPointA")))
                {cout<<"Error GetProcAddress...6"<<endl;return -1;}
 
                hMount = FindFirstVolume(szPath,sizeof(szPath));
 
                UINT uin;
 
                if(hMount!=INVALID_HANDLE_VALUE)
                {
                        do
                        {
                                uin = GetDriveType(szPath);
                                if(uin==DRIVE_CDROM)      {cout<<"CDROM: ";}
                                if(uin==DRIVE_UNKNOWN)    {cout<<"UNKNOWN: ";}
                                if(uin==DRIVE_NO_ROOT_DIR){cout<<"NO_ROOT_DIR: ";}
                                if(uin==DRIVE_REMOVABLE)  {cout<<"REMOV: ";}
                                if(uin==DRIVE_FIXED)      {cout<<"FIXED: ";}
                                if(uin==DRIVE_REMOTE)     {cout<<"REMOTE: ";}
                                if(uin==DRIVE_RAMDISK)    {cout<<"RAMDISK: ";}
 
                                GetVolumeInformation(szPath,
                                                                szRecvBuff,
                                                                sizeof(szRecvBuff),
                                                                NULL,NULL,NULL,
                                                                szFileSystem,
                                                                sizeof(szFileSystem));
 
                                cout<<"("<<szRecvBuff
                                      <<"|"<<szFileSystem
                                      <<") "<<szPath<<endl<<endl;  
        
                                ZeroMemory(szRecvBuff,sizeof(szRecvBuff));
                                ZeroMemory(szFileSystem,sizeof(szFileSystem));
 
                        }while(FindNextVolume(hMount,szPath,sizeof(szPath))!=0);
                        
                        FindVolumeClose(hMount);
 
                }else{cout<<"Error FindFirstVolume"<<endl;}
 
                FreeLibrary(hmod);
                
        }else{cout<<"Error LoadLibrary"<<endl;}
                
        ZeroMemory(szRecvBuff,sizeof(szRecvBuff));
        char szDrive[4];DWORD dwID = GetLogicalDrives();
 
        for(int i = 0;i < 26;i++)
        {
           if(((dwID>>i)&0x00000001)==1)
           {
                 szDrive[0] =  char(65+i);
                 szDrive[1] = ':';
                 szDrive[2] = '\\';
                 szDrive[3] = 0;
                 GetVolumeNameForVolumeMountPoint(szDrive,
                                                                        szRecvBuff,
                                                                        sizeof(szRecvBuff));
                 cout <<szDrive<<" "<<szRecvBuff<<endl;
 
           }
        }
 
/************************************/
        if(DeleteVolumeMountPoint("G:\\"))
        {cout<<"DeleteVolumeMountPoint status:OK"<<endl;}
        else
        {cout<<"DeleteVolumeMountPoint status:ERROR"<<endl;ErrorView();}
        if(SetVolumeMountPoint("C:\\mnt\\",
                "\\\\?\\Volume{e7aaf23a-8560-11db-97d5-806d6172696f}\\"))
        {cout<<"SetVolumeMountPoint status:OK"<<endl;}
        else
        {cout<<"SetVolumeMountPoint status:ERROR"<<endl;ErrorView();}
/////////////////
        cout<<"(PAUSE)Press any key..."<<endl;
        while(!getch());
/////////////////
        if(DeleteVolumeMountPoint("C:\\mnt\\"))
        {cout<<"DeleteVolumeMountPoint status:OK"<<endl;}
        else
        {cout<<"DeleteVolumeMountPoint status:ERROR"<<endl;ErrorView();}
        if(SetVolumeMountPoint("G:\\",
                "\\\\?\\Volume{e7aaf23a-8560-11db-97d5-806d6172696f}\\"))
        {cout<<"SetVolumeMountPoint status:OK"<<endl;}
        else
        {cout<<"SetVolumeMountPoint status:ERROR"<<endl;ErrorView();}
/************************************/
 
        cout<<"Press any key to exit..."<<endl;
        while(!getch());
        return 0;
 
}
 
void ErrorView()
{
        LPVOID lpMsgBuf = NULL;
        FormatMessage
        (FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,GetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf, 0, NULL);
        CharToOem((LPTSTR)lpMsgBuf,(LPTSTR)lpMsgBuf);
        cout<<(LPCTSTR)lpMsgBuf<<endl;
        LocalFree(lpMsgBuf);
}

А теперь подробней, по шагам:

HANDLE (WINAPI * FindFirstVolume) (LPTSTR,DWORD);
BOOL (WINAPI * FindNextVolume) (HANDLE,LPTSTR,DWORD);
BOOL (WINAPI * FindVolumeClose)(HANDLE);
BOOL (WINAPI * GetVolumeNameForVolumeMountPoint) (LPCTSTR,LPTSTR,DWORD);
BOOL (WINAPI * DeleteVolumeMountPoint) (LPCTSTR);
BOOL (WINAPI * SetVolumeMountPoint) (LPCTSTR,LPCTSTR);

Создаются указатели на прототипы функций для последующей загрузки в них адресов функций.

hmod = LoadLibrary("Kernel32.dll"));

Загружаем необходимую библиотеку.

if(!(FindFirstVolume = (HANDLE(WINAPI *)
(LPTSTR,DWORD))GetProcAddress(hmod,"FindFirstVolumeA")))
{cout<<"Error GetProcAddress...1"<

Далее получаем адреса необходимых нам функций.

hMount = FindFirstVolume(szPath,sizeof(szPath));
UINT uin;
if(hMount!=INVALID_HANDLE_VALUE)
{
do
{
uin = GetDriveType(szPath);
if(uin==DRIVE_CDROM) {cout<<"CDROM: ";} if(uin==DRIVE_UNKNOWN) {cout<<"UNKNOWN: ";} if(uin==DRIVE_NO_ROOT_DIR){cout<<"NO_ROOT_DIR: ";} if(uin==DRIVE_REMOVABLE) {cout<<"REMOV: ";} if(uin==DRIVE_FIXED) {cout<<"FIXED: ";} if(uin==DRIVE_REMOTE) {cout<<"REMOTE: ";} if(uin==DRIVE_RAMDISK) {cout<<"RAMDISK: ";} GetVolumeInformation(szPath, szRecvBuff, sizeof(szRecvBuff), NULL,NULL,NULL, szFileSystem,sizeof(szFileSystem)); cout<<"("<

Производится поиск томов и вывод информации о них.

ZeroMemory(szRecvBuff,sizeof(szRecvBuff));
char szDrive[4];DWORD dwID = GetLogicalDrives();
for(int i = 0;i < 26;i++) { if(((dwID>>i)&0x00000001)==1)
{
szDrive[0] = char(65+i);
szDrive[1] = ':';
szDrive[2] = '\\';
szDrive[3] = 0;
GetVolumeNameForVolumeMountPoint(szDrive,
szRecvBuff,
sizeof(szRecvBuff));
cout <

Здесь идет вывод информации вторым способом.

if(DeleteVolumeMountPoint("G:\\"))
{cout<<"DeleteVolumeMountPoint status:OK"<

Удаление точки монтирования, после выполнения данного участка кода, утилита mountvol покажет вам следующее:
Программно монтируем диски в директорию
Далее устанавливаем новую точку монтирования:

if(SetVolumeMountPoint("C:\\mnt\\",
"\\\\?\\Volume{e7aaf23a-8560-11db-97d5-806d6172696f}\\"))
{cout<<"SetVolumeMountPoint status:OK"<

и смотрим результат работы mountvol а также заходим в точку монтирования C:\mnt\ и видим там содержимое жесткого диска:
Программно монтируем диски в директорию
Ну а теперь возвращаем все как было, для этого следующий участок кода:

if(DeleteVolumeMountPoint("C:\\mnt\\"))
{cout<<"DeleteVolumeMountPoint status:OK"<

Результат работы программы вы можете видеть на рисунке, в последующем мне хотелось бы создать программу, которая будет для удобства иметь графический интерфейс....
Программно монтируем диски в директорию