首页 > 解决方案 > 我正在尝试使用 DeviceIoControl IOCTL_STORAGE_EJECT_MEDIA 弹出我的 CD ROM 驱动器,但出现错误访问冲突写入位置

问题描述

我试图通过单击按钮弹出我的 cd ROM 驱动器。按下按钮时,CD ROM 驱动器之前正确弹出,但现在它给我一个错误:“0xC0000005:访问冲突写入位置 0x00000000。” 我不确定为什么会收到此错误。我的代码如下所示,其中我的 CD ROM 驱动器是 D 驱动器:

#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, &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;
        }
    }
}

有没有人遇到过这个错误并知道如何解决它?

标签: winapivisual-c++createfilecd-rom

解决方案


因为MessageBox一直处于阻塞状态,CloseHandle是不会调用的。当您第二次按下该按钮时,CreateFile将再次打开光驱手柄,之前的手柄尚未关闭,访问被拒绝。

您可以简单地删除此行:

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, &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)&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)&mciSetParms);
    
    // Close device ID
    mciFlags = MCI_WAIT;
    MCI_GENERIC_PARMS mciGenericParms = { 0 };
    mciError = mciSendCommand(mciOpenParms.wDeviceID, 
        MCI_CLOSE, mciFlags, (DWORD_PTR)&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;
}

推荐阅读