Я пытаюсь извлечь свой дисковод CD-Rom с помощью DeviceIoControl IOCTL_STORAGE_EJECT_MEDIA, но получаю ошибку access violation местоположение записи

#winapi #visual-c #createfile #cd-rom

#winapi #visual-c #создать файл #cd-rom

Вопрос:

Я пытаюсь извлечь свой дисковод cd Rom, нажав кнопку. При нажатии кнопки привод CD-Rom раньше извлекался правильно, но теперь он выдает ошибку: «0xC0000005: место записи 0x00000000 с нарушением доступа». Я не уверен, почему я получаю эту ошибку. Мой код показан ниже, где мой дисковод CD-Rom является общим дисководом:

 #include <windows.h>
#include <tchar.h>
#include <stdio.h>

#define BUTTON                  3456
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
DWORD dwBytes;
HANDLE hCdRom;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
    {
        HWND hwndButton = CreateWindow(
            L"BUTTON",  // Predefined class; Unicode assumed 
            L"EJECT",   // Button text 
            WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,  // Styles 
            180,         // x position 
            200,        // y position 
            100,        // Button width
            100,        // Button height
            hWnd,     // Parent window
            (HMENU)BUTTON,       // No menu.
            (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
            NULL);      // Pointer not needed.
    }
    
    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
        case BUTTON:
            hCdRom = CreateFile(L"\\.\D:",
                GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

            if (hCdRom == INVALID_HANDLE_VALUE)
            {
                wsprintf(NULL, L"Error: %d", GetLastError());    //Getting error: Exception thrown at 0x746FFA6F (user32.dll) in Project.exe: 0xC0000005: Access violation writing location 0x00000000.
                return 1;
            }

            DeviceIoControl(hCdRom, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, amp;dwBytes, NULL);

            if (hCdRom == 0)
            {
                wsprintfW(NULL, L"Error: %d", GetLastError());
                return 1;
            }
            MessageBox(NULL, L"Please insert a CD ROM in the CD tray.", L"CD ROM Drive", 0);

            CloseHandle(hCdRom);
    
            break;
        }
    }
}
 

Кто-нибудь сталкивался с этой ошибкой и знает, как ее исправить?

Ответ №1:

Поскольку MessageBox он находится в состоянии блокировки, CloseHandle не вызывается. Когда вы нажмете кнопку во второй раз, CreateFile снова откроется дескриптор привода CD Rom, предыдущий дескриптор не был закрыт, и доступ запрещен.

Вы можете просто удалить эту строку:

MessageBox(NULL, L"Please insert a CD ROM in the CD tray.", L"CD ROM Drive", 0);

Но правильный способ — добавить функцию DefWindowProcA, которая гарантирует, что все сообщения обрабатываются.

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

Измените следующим образом,

 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
    {
        HWND hwndButton = CreateWindow(
            L"BUTTON",  // Predefined class; Unicode assumed 
            L"EJECT",   // Button text 
            WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,  // Styles 
            180,         // x position 
            200,        // y position 
            100,        // Button width
            100,        // Button height
            hWnd,     // Parent window
            (HMENU)BUTTON,       // No menu.
            (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
            NULL);      // Pointer not needed.
    }

    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
        case BUTTON:
            hCdRom = CreateFile(L"\\.\D:",
                GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

            if (hCdRom == INVALID_HANDLE_VALUE)
            {
                wsprintf(NULL, L"Error: %d", GetLastError());    //Getting error: Exception thrown at 0x746FFA6F (user32.dll) in Project.exe: 0xC0000005: Access violation writing location 0x00000000.
                return 1;
            }

            DeviceIoControl(hCdRom, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, amp;dwBytes, NULL);

            if (hCdRom == 0)
            {
                wsprintfW(NULL, L"Error: %d", GetLastError());
                return 1;
            }
            MessageBox(NULL, L"Please insert a CD ROM in the CD tray.", L"CD ROM Drive", 0);

            CloseHandle(hCdRom);

            break;
        }
    }
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
 

Обновленный:

 #include <tchar.h>
#include <windows.h>
#include <mmsystem.h> // for MCI functions

// Link to winmm.lib (usually included in project settings)
#pragma comment(lib, "winmm")

void ControlCdTray(TCHAR drive, DWORD command)
{
    // Not used here, only for debug
    MCIERROR mciError = 0;

    // Flags for MCI command
    DWORD mciFlags = MCI_WAIT | MCI_OPEN_SHAREABLE | 
        MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | MCI_OPEN_ELEMENT;

    // Open drive device and get device ID
    TCHAR elementName[] = { drive };
    MCI_OPEN_PARMS mciOpenParms = { 0 };
    mciOpenParms.lpstrDeviceType = (LPCTSTR)MCI_DEVTYPE_CD_AUDIO;
    mciOpenParms.lpstrElementName = elementName;    
    mciError = mciSendCommand(0, 
        MCI_OPEN, mciFlags, (DWORD_PTR)amp;mciOpenParms);

    // Eject or close tray using device ID
    MCI_SET_PARMS mciSetParms = { 0 };
    mciFlags = MCI_WAIT | command; // command is sent by caller
    mciError = mciSendCommand(mciOpenParms.wDeviceID, 
        MCI_SET, mciFlags, (DWORD_PTR)amp;mciSetParms);
    
    // Close device ID
    mciFlags = MCI_WAIT;
    MCI_GENERIC_PARMS mciGenericParms = { 0 };
    mciError = mciSendCommand(mciOpenParms.wDeviceID, 
        MCI_CLOSE, mciFlags, (DWORD_PTR)amp;mciGenericParms);
}

// Eject drive tray
void EjectCdTray(TCHAR drive)
{
    ControlCdTray(drive, MCI_SET_DOOR_OPEN);
}

// Retract drive tray
void CloseCdTray(TCHAR drive)
{
    ControlCdTray(drive, MCI_SET_DOOR_CLOSED);
}

int _tmain(int argc, _TCHAR* argv[])
{
    EjectCdTray(TEXT('D')); // drive letter hardcoded
    //CloseCdTray(TEXT('D'));

    return 0;
}
 

Комментарии:

1. У меня есть ‘default: return DefWindowProc (hWnd, message, wParam, lParam);’ в моем коде, я не включил его в онлайн. Я удалил ‘MessageBox (NULL, L»Пожалуйста, вставьте компакт-диск в лоток для компакт-дисков.», L»Привод компакт-дисков», 0);’ как вы и предлагали, но он все еще не извлекается.

2. @Alyssa Здесь также необходимо изменить: CreateFile(L"\\.\D:",

3. У меня это тоже есть, забыл кавычки при вводе его в Интернете.

4. @Alyssa будет ли компакт-диск извлечен при первом нажатии кнопки?

5. Я разместил функции в начале, и это сработало для меня. Большое вам спасибо!