Форум программистов, компьютерный форум, киберфорум
Наши страницы
Dragokas
Войти
Регистрация
Восстановить пароль
Оценить эту запись

Быстрое отключение всех флешек с обесточиванием (VB, C++, exe+src)

Запись от Dragokas размещена 14.08.2012 в 15:20
Обновил(-а) Dragokas 18.03.2017 в 12:39 (Утилита RemoveDrive заменена новой версией)

Последняя версия утилиты доступна в конце темы по ссылке: AntiHidden - Удаление последствий вируса (поддельные "фейковые" папки на флешке)

Преимущества:
  • В OS Vista, Windows 7 не только отключает, но и обесточивает Flash Drive.
  • Показывает имя приложения, которое не дает безопасно отключить уст-во (блокирует его файловую систему).
  • Ожидает до тех пор, пока блокировка не будет снята, затем самостоятельно повторяет попытку безопасного отключения.
  • Двойной клик - флешка отключена (никаких сообщений, ничего лишнего )
  • Возможность отключить отдельное уст-во, если их более 1 (отображает информацию о них).
  • Имеет исходный код.
Итак, если устройств подключено несколько, отображается диалоговое окно с информацией о:
1. Метках флешек.
2. Общем объеме.
3. Имени раздела.
Нажатие "Ок" отключает все съемные накопители.
Ввод порядкового номера устройства отключает конкретный драйв.

Авто-определение букв накопителей, диалоговое окно и вызов консольной утилиты написано на Visual Basic.
Основная часть: freeware-программа RemoveDrive - на С++ (Автор: Uwe Sieber - www.uwe-sieber.de)
Ее упрощенный Source-код.
C++ Demo (щелк^_^)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
//
// RemoveDriveByLetter.cpp by Uwe Sieber - [url]www.uwe-sieber.de[/url]
//
// Simple demonstration how to prepare a disk drive for save removal
//
// Works with removable and fixed drives under W2K, XP, W2K3, Vista
//
// Console application - expects the drive letter of the drive to remove as parameter
//
// you are free to use this code in your projects
//
 
 
#include "stdafx.h"
#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(int argc, char* argv[])
{
 
    if ( argc != 2 ) {
        return 1;       
    }
 
    char DriveLetter = argv[1][0];
    DriveLetter &= ~0x20; // uppercase
 
    if ( DriveLetter < 'A' || DriveLetter > 'Z' ) {
        return 1;
    }
 
    char szRootPath[] = "X:\";   // "X:"  -> for GetDriveType
    szRootPath[0] = DriveLetter;
 
    char szDevicePath[] = "X:";   // "X:"   -> for QueryDosDevice
    szDevicePath[0] = DriveLetter;
 
    char szVolumeAccessPath[] = "\\\\.\\X:";   // "\\.\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;
}
//-----------------------------------------------------------


Скомпилированный EXE и исходник VB в архиве.

Обновление 15.12.2012
Исправлен баг с определением кардридера (или дисковода), как флешки.

___________________
В нашей орг-ции часто приходится передавать результаты работы через флешку, поэтому актуально.
Кроме того, когда-то моя флешка не выдержала необдуманного отключения (во времена, когда интернет был плохой - пользовались в интернет-клубах. А там бац - "Ваше время истекло". Нечего делать - выдернул флеху. Домой - а она не работает.
Вложения
Тип файла: rar FlashEject.rar (31.3 Кб, 327 просмотров)
Размещено в Без категории
Просмотров 2693 Комментарии 8
Всего комментариев 8
Комментарии
  1. Старый комментарий
    Аватар для Dragokas
    Да, прога весьма удобна, когда к компу подключено куча флешек и ты в судоргах ищешь мой компьютер, чтобы понять какая буква у отключаемой.
    Или когда по нажатию ЛКМ надпись "Безопасно извлечь" выскакивает только через 5 сек. (как у меня на работе), а потом видешь "Не могу извлечь" н/з по какой причине (а это оказывается антивирус решил в неподходящий момент проверить Autorun.inf) и приходится снова повторять.

    Я тестировал несколько утилит и эта (от sieber-а) мне понравилась больше всего по названым в топике причинам. А наличие исходного кода дает уверенность в том, что там не окажится какой-нибуть гадости.

    Eastman предложил протестировать на съемных ЖД.
    По описанию утилита должна уметь это делать.
    У кого есть возможность, протестируйте, плиз.
    Если дос-овское окно попросту не появится, значит тип носителя определяется не как "съемный.",
    тогда просто пропишите в ком. строке
    Bash
    1
    
    c:\removedrive.exe x: -L -E -i -H
    , где c:\ - путь к программе removedrive, x: - имя съемного ЖД и отпишитесь - получилось ли...

    Если у кого есть предложение об увеличении кол-ва необходимой для отображения инфы при отключении 2-х и более устройств, пожалуйста, спрашивайте об этом.
    Также можно оформить в виде формы с динамическим кол-вом кнопочек, но я это принципиально не делал, так как считал излишеством.
    Запись от Dragokas размещена 21.08.2012 в 23:40 Dragokas вне форума
  2. Старый комментарий
    Аватар для Dragokas
    Вчера после нескольких экспериментов случайно закрыл программу крестиком, не дождавшись завершения работы. Получил перманентный фатальный крэш программы RemoveDrive.
    Как ни пытался, заставить ее снова работать не смог.
    Связался с разработчиком и на удивление сразу получил ответ с Debug-версией утилиты.
    Сейчас ожидаю исправления Uwe Sieber-ом этой проблемы.
    А пока рекомендую всем: не закрывать принудительно утилиту. Этого Вам делать никогда не понадобится.
    Запись от Dragokas размещена 23.08.2012 в 00:26 Dragokas вне форума
  3. Старый комментарий
    Аватар для Dragokas
    Сегодня получил обновленную Debug-версию утилиты от разработчика.
    Как
    ответил Uwe:
    It crashed because I had coded some part more for having nice looking
    and readable code than for safe code. I knew the risk but
    decided that nothing can go wrong...

    Архив ТС обновлен.
    Запись от Dragokas размещена 23.08.2012 в 13:07 Dragokas вне форума
  4. Старый комментарий
    ...хочу напомнить, что управление питанием хаба допускают не все материнки и от ОС это не зависит. Так что размонтирование флешки и деактивация root-ветки USB (через PCI-контроллер) - не гарантия пропадания питания, на моей машине и всех рабочих питание остается все равно. Доказано мультиметром.

    Приведенные исходники лишь подверждают, что принципиального отличия в методе отключения от того же devcon (тоже с исходниками) c cfgmgr32 и SetupAPI - нет.
    Запись от raxp размещена 23.08.2012 в 13:34 raxp вне форума
  5. Старый комментарий
    Аватар для Dragokas
    Это относится только к старым моделям материнок или на новых тоже может не поддерживаться управление питанием?
    Запись от Dragokas размещена 23.08.2012 в 15:53 Dragokas вне форума
  6. Старый комментарий
    ...а что считать старыми? На работе разные года от 99 до 09, дома нетбук посовременней, 11 года.
    Запись от raxp размещена 23.08.2012 в 16:17 raxp вне форума
  7. Старый комментарий
    Аватар для Dragokas
    Ясно. Если не поддерживает, значит никаким кодом и не выкрутишься.

    Ну тогда только какое-нить техническое приспособление в разрыв цепи питания, скажем, применимо ко внешнему USB-хабу.
    Запись от Dragokas размещена 23.08.2012 в 18:28 Dragokas вне форума
  8. Старый комментарий
    ...дык, на эту тему, но не применительно к флешкам, есть темы http://www.cyberforum.ru/delphi-begi...ead632757.html, http://www.cyberforum.ru/cpp-beginne...ead373323.html.

    Началось еще все с USB-ракетницы. Есть такие игрушки-гаджеты с запуском
    Запись от raxp размещена 23.08.2012 в 18:41 raxp вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru