diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_disc/drivemngr.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/Plugins/Library/ml_disc/drivemngr.cpp')
-rw-r--r-- | Src/Plugins/Library/ml_disc/drivemngr.cpp | 1910 |
1 files changed, 1910 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_disc/drivemngr.cpp b/Src/Plugins/Library/ml_disc/drivemngr.cpp new file mode 100644 index 00000000..b2d87fcc --- /dev/null +++ b/Src/Plugins/Library/ml_disc/drivemngr.cpp @@ -0,0 +1,1910 @@ +#include "main.h" +#include "./drivemngr.h" +#include "./primosdk_helper.h" +#include "./resource.h" +#include "../nu/trace.h" +#include "./dbt.h" +#include "./spti.h" +#include <setupapi.h> + +#include <imapi.h> +#include <strsafe.h> + +#define LISTENER_CLASSNAME L"MLDISCLISTENER" +#define LISTENER_WINDOWNAME L"" + +#define POLLMEDIUMCHANGE_INTERVAL 2000 +#define POLLMEDIUMVALIDATE_INTERVAL 6000 + +#define DMS_SUSPENDED 0x0000 +#define DMS_ACTIVE 0x0001 + +#define WM_EX_QUIT (WM_APP + 1) + +typedef struct _MEDIUMINFO_I +{ + UINT msLastPolled; // last time medium info was polled + UINT serialNumber; // medium serialnumber +} MEDIUMINFO_I; + +typedef struct _DRIVEINFO_I +{ + char cLetter; // drive letter + INT deviceNumber; // system assigned device number (unique till next reboot) + BOOL bMediumInserted; // if TRUE mediumInfo contains valid data + CHAR cMode; // drive mode + DWORD dwType; // drive type + LPWSTR pszDevName; // device name + HANDLE hThread; // device info thread + DWORD dwThreadId; + MEDIUMINFO_I mediumInfo; +} DRIVEINFO_I; + +typedef struct _DRIVEMNGR +{ + HWND hwndListener; + DMNPROC callback; + UINT fState; + CRITICAL_SECTION csLock; + + DRIVEINFO_I *pDrives; + INT nCount; + INT nAlloc; + + HANDLE hPollingThread; + DWORD dwPollingThread; +} DRIVEMNGR; + +typedef struct _DEVICEINFO +{ + CHAR cLetter; + LPWSTR pszDevName; + WCHAR szTargetPath[128]; + WCHAR szVolumeName[64]; + DWORD dwType; + INT deviceNumber; + INT opCode; +} DEVICEINFO; + +static DRIVEMNGR *pMngr = NULL; + + +static void CALLBACK APC_CheckDrives(ULONG_PTR param); +static void CALLBACK APC_IsMediumChanged(ULONG_PTR param); + +static void CALLBACK APC_GetUnitInfo(ULONG_PTR param); +static void CALLBACK APC_GetUnitInfo2(ULONG_PTR param); +static void CALLBACK APC_GetDiscInfoEx(ULONG_PTR param); +static void CALLBACK APC_GetDiscInfo2(ULONG_PTR param); +static void CALLBACK APC_GetTitle(ULONG_PTR param); +static void CALLBACK APC_DriveScan(ULONG_PTR param); +static void CALLBACK APC_GetMCIInfo(ULONG_PTR param); +static void CALLBACK APC_GetIMAPIInfo(ULONG_PTR param); +static void CALLBACK APC_Eject(ULONG_PTR param); + +static DWORD CALLBACK InfoThread(LPVOID param); + +static DWORD CALLBACK PollingThread(LPVOID param); + +static void CALLBACK PollMediumInfo(ULONG_PTR param); + +static LRESULT WINAPI ListenerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +static CHAR CheckLetter(CHAR cLetter) +{ + if (cLetter < 'A' || cLetter > 'Z') + { + if (cLetter >= 'a' && cLetter <= 'z') return (cLetter - 0x20); + return 0; + } + return cLetter; +} + +static LPCWSTR GetDeviceName(CHAR cLetter) +{ + LPCWSTR pszDevName; + if (!pMngr) return NULL; + pszDevName = NULL; + + EnterCriticalSection(&pMngr->csLock); + for (int i = 0; i < pMngr->nCount; i++) + { + if (pMngr->pDrives[i].cLetter == cLetter) + { + pszDevName = pMngr->pDrives[i].pszDevName; + break; + } + } + LeaveCriticalSection(&pMngr->csLock); + + return pszDevName; +} + +static BOOL IsPollingRequired(void) +{ + HKEY hKey; + LONG result; + BOOL bAutoRunEnabled; + + bAutoRunEnabled = FALSE; + + result = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\Cdrom"), &hKey); + if (ERROR_SUCCESS == result) + { + DWORD value; + DWORD size; + size = sizeof(DWORD); + result = RegQueryValueEx(hKey, TEXT("AutoRun"), NULL, NULL, (LPBYTE)&value, &size); + if (ERROR_SUCCESS == result) bAutoRunEnabled = (0 != value); + + RegCloseKey(hKey); + } + return !bAutoRunEnabled; + +} +static CHAR Drive_LetterFromMask(ULONG unitmask) +{ + char i; + for (i = 0; i < 26; ++i) + { + if (unitmask & 0x1) break; + unitmask = unitmask >> 1; + } + return (i + 'A'); +} + +static BOOL Drive_Add(DEVICEINFO *pDevInfo) +{ + DRIVEINFO_I *pDrive; + if (!pMngr) return FALSE; + + EnterCriticalSection(&pMngr->csLock); + + INT index, opCode; + + opCode = 0; + for (index = 0; index < pMngr->nCount && pMngr->pDrives[index].cLetter != pDevInfo->cLetter; index++); + if (index != pMngr->nCount) + { + pDrive = &pMngr->pDrives[index]; + if (pDrive->deviceNumber != pDevInfo->deviceNumber || pDrive->dwType != pDevInfo->dwType) opCode = 1; + } + else + { + if (pMngr->nCount == pMngr->nAlloc) + { + LPVOID data; + data = realloc(pMngr->pDrives, sizeof(DRIVEINFO_I)*(pMngr->nCount + 2)); + if (!data) + { + LeaveCriticalSection(&pMngr->csLock); + return FALSE; + } + pMngr->pDrives = (DRIVEINFO_I*)data; + pMngr->nAlloc += 2; + } + pDrive = &pMngr->pDrives[pMngr->nCount]; + pMngr->nCount++; + + ZeroMemory(pDrive, sizeof(DRIVEINFO_I)); + pDrive->cLetter = pDevInfo->cLetter; + opCode = 2; + } + + if (opCode) + { + pDrive->deviceNumber = pDevInfo->deviceNumber; + pDrive->dwType = pDevInfo->dwType; + if (pDrive->pszDevName) free(pDrive->pszDevName); + pDrive->pszDevName = _wcsdup(pDevInfo->pszDevName); + } + + LeaveCriticalSection(&pMngr->csLock); + + if (opCode && pMngr->callback) pMngr->callback((2 == opCode) ? DMW_DRIVEADDED : DMW_DRIVECHANGED, pDevInfo->cLetter); + + return TRUE; +} + +static BOOL Drive_Remove(CHAR cLetter) +{ + INT index; + BOOL bReportChanges; + if (!pMngr) return FALSE; + + EnterCriticalSection(&pMngr->csLock); + + bReportChanges = FALSE; + index = pMngr->nCount; + while (index-- && pMngr->pDrives[index].cLetter != cLetter); + + if (-1 != index) + { + if (pMngr->pDrives[index].pszDevName) free(pMngr->pDrives[index].pszDevName); + if (index != pMngr->nCount - 1) MoveMemory(&pMngr->pDrives[index], &pMngr->pDrives[index + 1], sizeof(DRIVEINFO_I)*(pMngr->nCount - index)); + pMngr->nCount--; + bReportChanges = TRUE; + } + LeaveCriticalSection(&pMngr->csLock); + + if (bReportChanges && pMngr->callback) pMngr->callback(DMW_DRIVEREMOVED, cLetter); + return TRUE; +} + +static HRESULT QueueInfoAPC(CHAR cLetter, PAPCFUNC pfnAPC, ULONG_PTR param) +{ + DWORD *pdwThreadId(NULL); + HRESULT hr(S_FALSE); + HANDLE *phThread(NULL); + static HANDLE hDrivesInfoThread = NULL; + + if (NULL == pMngr) return E_FAIL; + + EnterCriticalSection(&pMngr->csLock); + if (cLetter) + { + INT index = pMngr->nCount; + while (index-- && pMngr->pDrives[index].cLetter != cLetter); + if (-1 != index) + { + phThread = &pMngr->pDrives[index].hThread; + pdwThreadId = &pMngr->pDrives[index].dwThreadId; + } + } + else + { + phThread = &hDrivesInfoThread; + } + + if (phThread) + { + if (!*phThread) + { + DWORD tid; + *phThread = CreateThread(NULL, 0, InfoThread, NULL, 0, &tid); + if (pdwThreadId) *pdwThreadId = tid; + Sleep(100); + } + if (*phThread) + { + if (0 == QueueUserAPC(pfnAPC, *phThread, param)) + { + TRACE_LINE(TEXT("queue user apc failed")); + } + else hr = S_OK; + } + } + + LeaveCriticalSection(&pMngr->csLock); + + return hr; +} + +static BOOL Medium_Add(CHAR cLetter, DWORD serial) +{ + INT index; + if (!pMngr) return FALSE; + + EnterCriticalSection(&pMngr->csLock); + + index = pMngr->nCount; + while (index-- && pMngr->pDrives[index].cLetter != cLetter); + if (-1 != index) + { + pMngr->pDrives[index].bMediumInserted = TRUE; + pMngr->pDrives[index].mediumInfo.msLastPolled = 0; + pMngr->pDrives[index].mediumInfo.serialNumber = serial; + } + + LeaveCriticalSection(&pMngr->csLock); + + if (-1 != index) + { + if (-1 == serial) QueueInfoAPC(cLetter, APC_IsMediumChanged, (ULONG_PTR)cLetter); + if (pMngr->callback) pMngr->callback(DMW_MEDIUMARRIVED, cLetter); + } + + return TRUE; +} + +static BOOL Medium_Remove(CHAR cLetter) +{ + INT index; + if (!pMngr) return FALSE; + + EnterCriticalSection(&pMngr->csLock); + + index = pMngr->nCount; + while (index-- && pMngr->pDrives[index].cLetter != cLetter); + if (-1 != index) pMngr->pDrives[index].bMediumInserted = FALSE; + + LeaveCriticalSection(&pMngr->csLock); + + if (-1 != index && pMngr->callback) pMngr->callback(DMW_MEDIUMREMOVED, cLetter); + + return TRUE; +} + +BOOL DriveManager_Initialize(DMNPROC DMNProc, BOOL bSuspended) +{ + WNDCLASSW wc = {0}; + HINSTANCE hInstance; + + if (pMngr || !DMNProc) return FALSE; + + pMngr = (DRIVEMNGR*)calloc(1, sizeof(DRIVEMNGR)); + if (!pMngr) return FALSE; + + hInstance = GetModuleHandle(NULL); + + if (!GetClassInfoW(hInstance, LISTENER_CLASSNAME, &wc)) + { + wc.hInstance = hInstance; + wc.lpfnWndProc = ListenerWndProc; + wc.lpszClassName = LISTENER_CLASSNAME; + if (!RegisterClassW(&wc)) + { + DriveManager_Uninitialize(0); + return FALSE; + } + } + pMngr->hwndListener = CreateWindowW(LISTENER_CLASSNAME, LISTENER_WINDOWNAME, WS_DISABLED, 0,0,0,0, HWND_DESKTOP, NULL, hInstance, 0L); + if (!pMngr->hwndListener) + { + DriveManager_Uninitialize(0); + return FALSE; + } + InitializeCriticalSection(&pMngr->csLock); + pMngr->callback = DMNProc; + + return TRUE; +} + +BOOL DriveManager_Uninitialize(INT msExitWaitTime) +{ + if (pMngr) + { + WNDCLASSW wc; + HINSTANCE hInstance; + + if (pMngr->hwndListener) DestroyWindow(pMngr->hwndListener); + + hInstance = GetModuleHandle(NULL); + if (GetClassInfoW(hInstance, LISTENER_CLASSNAME, &wc)) UnregisterClassW(LISTENER_CLASSNAME, hInstance); + + EnterCriticalSection(&pMngr->csLock); + + for (int index =0; index < pMngr->nCount; index++) + { + if (pMngr->pDrives[index].hThread) + { + PostThreadMessage(pMngr->pDrives[index].dwThreadId, WM_EX_QUIT, 1, 0); + INT result = WaitForSingleObject(pMngr->pDrives[index].hThread, msExitWaitTime); + if (WAIT_TIMEOUT == result) TerminateThread(pMngr->pDrives[index].hThread, 1); + CloseHandle(pMngr->pDrives[index].hThread); + pMngr->pDrives[index].hThread = NULL; + } + } + LeaveCriticalSection(&pMngr->csLock); + + if (pMngr->hPollingThread) + { + PostThreadMessage(pMngr->dwPollingThread, WM_EX_QUIT, 1, 0); + INT result = WaitForSingleObject(pMngr->hPollingThread, msExitWaitTime); + if (WAIT_TIMEOUT == result) TerminateThread(pMngr->hPollingThread, 1); + CloseHandle(pMngr->hPollingThread); + pMngr->hPollingThread = NULL; + } + + EnterCriticalSection(&pMngr->csLock); + + if (pMngr->pDrives) + { + free(pMngr->pDrives); + } + + DRIVEMNGR *managerInstance = pMngr; + pMngr = NULL; + + LeaveCriticalSection(&managerInstance->csLock); + DeleteCriticalSection(&managerInstance->csLock); + + free(managerInstance); + + PrimoSDKHelper_Uninitialize(); + + } + return TRUE; +} + +BOOL DriveManager_Suspend(void) +{ + if (!pMngr) return FALSE; + + pMngr->fState = DMS_SUSPENDED; + if (pMngr->hPollingThread) + { + PostThreadMessage(pMngr->dwPollingThread, WM_EX_QUIT, 1, 0); + pMngr->hPollingThread = NULL; + } + return TRUE; +} + +BOOL DriveManager_Update(BOOL bAsync) +{ + if (bAsync) return (QueueInfoAPC(0, APC_DriveScan, 0) && QueueInfoAPC(0, PollMediumInfo, 0)); + else + { + APC_DriveScan(0); + QueueInfoAPC(0, PollMediumInfo, 0); + } + return TRUE; +} + +BOOL DriveManager_Resume(BOOL bUpdate) +{ + if (!pMngr) return FALSE; + pMngr->fState = DMS_ACTIVE; + + EnterCriticalSection(&pMngr->csLock); + for (int index =0; index < pMngr->nCount; index++) pMngr->pDrives[index].mediumInfo.msLastPolled = 0; + LeaveCriticalSection(&pMngr->csLock); + + APC_DriveScan(0); + QueueInfoAPC(0, PollMediumInfo, 0); + + if (NULL == pMngr->hPollingThread && IsPollingRequired()) + { + pMngr->hPollingThread = CreateThread(NULL, 0, PollingThread, NULL, 0, &pMngr->dwPollingThread); + } + + return TRUE; +} + +BOOL DriveManager_SetDriveMode(CHAR cLetter, CHAR cMode) +{ + BOOL report; + INT index; + + index = -1; + report = FALSE; + cLetter = CheckLetter(cLetter); + + if (pMngr && cLetter) + { + EnterCriticalSection(&pMngr->csLock); + for (index =0; index < pMngr->nCount; index++) + { + if (pMngr->pDrives[index].cLetter == cLetter) + { + if (pMngr->pDrives[index].cMode != cMode) + { + pMngr->pDrives[index].cMode = cMode; + report = TRUE; + } + break; + } + } + if (index == pMngr->nCount) index = -1; + LeaveCriticalSection(&pMngr->csLock); + if (report && pMngr->callback) pMngr->callback(DMW_MODECHANGED, MAKEWORD(cLetter, cMode)); + } + + return (-1 != index); +} + +CHAR DriveManager_GetDriveMode(CHAR cLetter) +{ + CHAR result; + + result = DM_MODE_ERROR; + cLetter = CheckLetter(cLetter); + + if (pMngr && cLetter) + { + INT index; + EnterCriticalSection(&pMngr->csLock); + for (index =0; index < pMngr->nCount; index++) + { + if (pMngr->pDrives[index].cLetter == cLetter) + { + result = pMngr->pDrives[index].cMode; + break; + } + } + LeaveCriticalSection(&pMngr->csLock); + } + return result; +} + +DWORD DriveManager_GetDriveType(CHAR cLetter) +{ + DWORD type; + + type = DRIVE_TYPE_UNKNOWN | DRIVE_CAP_UNKNOWN; + cLetter = CheckLetter(cLetter); + + if (pMngr && cLetter) + { + INT index; + EnterCriticalSection(&pMngr->csLock); + for (index =0; index < pMngr->nCount; index++) + { + if (pMngr->pDrives[index].cLetter == cLetter) + { + type = pMngr->pDrives[index].dwType; + break; + } + } + LeaveCriticalSection(&pMngr->csLock); + } + return type; +} + +BOOL DriveManager_IsMediumInserted(CHAR cLetter) +{ + BOOL result; + + result = FALSE; + cLetter = CheckLetter(cLetter); + + if (pMngr && cLetter) + { + INT index; + EnterCriticalSection(&pMngr->csLock); + for (index =0; index < pMngr->nCount; index++) + { + if (pMngr->pDrives[index].cLetter == cLetter) + { + result = pMngr->pDrives[index].bMediumInserted; + break; + } + } + LeaveCriticalSection(&pMngr->csLock); + } + return result; +} + +INT DriveManager_GetDriveList(CHAR *pLetters, INT cchSize) +{ + INT r = 0; + if (!pLetters || !pMngr) return -1; + EnterCriticalSection(&pMngr->csLock); + for (int index =0; index < pMngr->nCount; index++) + { + *pLetters = pMngr->pDrives[index].cLetter; + pLetters++; + cchSize--; + r++; + if (0 == cchSize) break; + } + LeaveCriticalSection(&pMngr->csLock); + return r; +} + +static BOOL QueueInfoJob(PAPCFUNC pfnAPC, DM_NOTIFY_PARAM *pHeader) +{ + BOOL result(TRUE); + if (!pMngr || !pHeader) result = FALSE; + + if (result) + { + HANDLE hProc = GetCurrentProcess(); + pHeader->hReserved = 0; + result = (BOOL)DuplicateHandle(hProc, GetCurrentThread(), hProc, &pHeader->hReserved, + 0, FALSE, DUPLICATE_SAME_ACCESS); + if (!result) pHeader->hReserved = 0; + } + + if (result) + { + CHAR cLetter = CheckLetter(pHeader->cLetter); + result = (cLetter && S_OK == QueueInfoAPC(cLetter, pfnAPC, (ULONG_PTR)pHeader)); + } + + if(!result && pHeader && pHeader->fnFree) + { + if (pHeader->hReserved) CloseHandle(pHeader->hReserved); + pHeader->fnFree(pHeader); + } + return result; +} + +BOOL DriveManager_GetUnitInfo(DM_UNITINFO_PARAM *puip) +{ + return QueueInfoJob(APC_GetUnitInfo, (DM_NOTIFY_PARAM*)puip); +} + +BOOL DriveManager_GetUnitInfo2(DM_UNITINFO2_PARAM *puip) +{ + return QueueInfoJob(APC_GetUnitInfo2, (DM_NOTIFY_PARAM*)puip); +} + +BOOL DriveManager_GetDiscInfoEx(DM_DISCINFOEX_PARAM *pdip) +{ + return QueueInfoJob(APC_GetDiscInfoEx, (DM_NOTIFY_PARAM*)pdip); +} +BOOL DriveManager_GetDiscInfo2(DM_DISCINFO2_PARAM *pdip) +{ + return QueueInfoJob(APC_GetDiscInfo2, (DM_NOTIFY_PARAM*)pdip); +} + +BOOL DriveManager_QueryTitle(DM_TITLE_PARAM *pdtp) +{ + return QueueInfoJob(APC_GetTitle, (DM_NOTIFY_PARAM*)pdtp); +} + +BOOL DriveManager_GetMCIInfo(DM_MCI_PARAM *pmcip) +{ + return QueueInfoJob(APC_GetMCIInfo, (DM_NOTIFY_PARAM*)pmcip); +} + +BOOL DriveManager_GetIMAPIInfo(DM_IMAPI_PARAM *pIMAPI) +{ + return QueueInfoJob(APC_GetIMAPIInfo, (DM_NOTIFY_PARAM*)pIMAPI); +} +BOOL DriveManager_Eject(CHAR cLetter, INT nCmd) +{ + if (!pMngr) return FALSE; + CHAR cLetter1 = CheckLetter(cLetter); + + return (cLetter1 && QueueInfoAPC(cLetter1, APC_Eject, (ULONG_PTR)MAKELONG(cLetter, nCmd))); +} + +BOOL DriveManager_IsUnitReady(CHAR cLetter, BOOL *pbReady) +{ + BYTE sc, asc, ascq; + + BOOL bSuccess; + HANDLE hDevice; + + *pbReady = FALSE; + hDevice = CreateFileW(GetDeviceName(cLetter), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + + if (INVALID_HANDLE_VALUE == hDevice) return FALSE; + + bSuccess = SPTI_TestUnitReady(hDevice, &sc, &asc, &ascq, 3); + if (!bSuccess) + { + if (ERROR_SEM_TIMEOUT == GetLastError()) bSuccess = TRUE; + } + else if (0x00 == sc || (0x02 == sc && 0x3A == asc)) *pbReady = TRUE; + + CloseHandle(hDevice); + + return bSuccess; +} + +static BOOL GetVolumeNameForVolumeMountPoint_DL(LPCWSTR lpszVolumeMountPoint, LPWSTR lpszVolumeName, DWORD cchBufferLength) +{ + static BOOL (WINAPI *func)(LPCWSTR, LPWSTR, DWORD) = NULL; + if (!func) + { + UINT prevErrorMode; + HMODULE hModule; + prevErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + hModule = LoadLibraryW(L"Kernel32.dll"); + SetErrorMode(prevErrorMode); + if (hModule) + { + func = (BOOL (WINAPI*)(LPCWSTR, LPWSTR, DWORD))GetProcAddress(hModule, "GetVolumeNameForVolumeMountPointW"); + FreeLibrary(hModule); + } + } + return (func) ? func(lpszVolumeMountPoint, lpszVolumeName, cchBufferLength) : FALSE; +} + +static DWORD GetDeviceNames(DEVICEINFO *pDevInfo, INT count) +{ + HANDLE hDevInfo; + SP_DEVICE_INTERFACE_DATA spiData; + SP_DEVICE_INTERFACE_DETAIL_DATA_W *pspiDetailData; + DWORD dwErrorCode, dwReqSize, dwDetailSize; + wchar_t volume[128], szDosName[] = L"X:\\", szDosName1[] = L"X:\\"; + + if (!pDevInfo || !count) return ERROR_INVALID_DATA; + + for (int i = 0; i < count; i++) + { + szDosName[0] = pDevInfo[i].cLetter; + GetVolumeNameForVolumeMountPoint_DL(szDosName, pDevInfo[i].szVolumeName, sizeof(pDevInfo[i].szVolumeName)/sizeof(wchar_t)); + szDosName1[0] = pDevInfo[i].cLetter; + QueryDosDeviceW(szDosName1, pDevInfo[i].szTargetPath, sizeof(pDevInfo[i].szTargetPath)/sizeof(wchar_t)); + } + + hDevInfo = SetupDiGetClassDevs((LPGUID)&CdRomClassGuid, NULL, NULL, (DIGCF_PRESENT | DIGCF_INTERFACEDEVICE)); + if (INVALID_HANDLE_VALUE == hDevInfo) return GetLastError(); + + dwDetailSize = 0; + pspiDetailData = NULL; + spiData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + dwErrorCode = 0; + + for (int index = 0; !dwErrorCode; index++) + { + BOOL bResult = SetupDiEnumDeviceInterfaces(hDevInfo, 0, (LPGUID)&CdRomClassGuid, index, &spiData); + if (!bResult) + { + dwErrorCode = GetLastError(); + break; + } + + bResult = SetupDiGetDeviceInterfaceDetailW(hDevInfo, &spiData, NULL, 0, &dwReqSize, NULL); + if (!bResult) + { + dwErrorCode = GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER != dwErrorCode) break; + dwErrorCode = 0; + } + dwReqSize += 2*sizeof(wchar_t); + if (dwReqSize > dwDetailSize) + { + LPVOID data; + data = realloc(pspiDetailData, dwReqSize); + if (!data) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; break; } + pspiDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA*)data; + dwDetailSize = dwReqSize; + } + + pspiDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + bResult = SetupDiGetDeviceInterfaceDetailW(hDevInfo, &spiData, pspiDetailData, dwDetailSize, NULL, NULL); + if (!bResult) + { + dwErrorCode = GetLastError(); + break; + } + + INT cchName; + cchName = lstrlenW(pspiDetailData->DevicePath); + pspiDetailData->DevicePath[cchName] = L'\\'; + pspiDetailData->DevicePath[cchName + 1] = 0x00; + + if(GetVolumeNameForVolumeMountPoint_DL(pspiDetailData->DevicePath, volume, sizeof(volume)/sizeof(wchar_t))) + { + for (int i = 0; i < count; i++) + { + if (!pDevInfo[i].pszDevName && 0 == lstrcmpW(volume, pDevInfo[i].szVolumeName)) + { + pDevInfo[i].pszDevName = (LPWSTR)calloc((cchName + 1), sizeof(wchar_t)); + if (pDevInfo[i].pszDevName) StringCchCopyNW(pDevInfo[i].pszDevName, cchName + 1, pspiDetailData->DevicePath, cchName); + break; + } + } + } + } + if (pspiDetailData) free(pspiDetailData); + SetupDiDestroyDeviceInfoList(hDevInfo); + + for (int i = 0; i < count; i++) + { + if (!pDevInfo[i].pszDevName) + { + wchar_t szDevName[] = L"\\\\.\\x:"; + szDevName[4] = pDevInfo[i].cLetter; + pDevInfo[i].pszDevName = (LPWSTR)calloc(sizeof(szDevName) + 2, sizeof(wchar_t)); + if (pDevInfo[i].pszDevName) StringCbCopyW(pDevInfo[i].pszDevName, sizeof(szDevName) + 2, szDevName); + } + } + + return dwErrorCode; +} + +static void GetDeviceCaps(DEVICEINFO *pDevInfo, INT count) +{ + for( int i = 0; i < count; i++) + { + pDevInfo[i].dwType = ((pDevInfo[i].dwType & 0x0000FFFF) | DRIVE_CAP_UNKNOWN); + } + + // TODO come back to this later on, but for the moment not seeing any noticeable issues + // with disabling this and instead and instead it helps prevent random trk****.tmp + // files being generated and also seems to fix the crash on start people have here + /*IDiscMaster *pdm; + IDiscRecorder *pdr; + IEnumDiscRecorders *per; + ULONG nActual; + HRESULT hr = CoCreateInstance(CLSID_MSDiscMasterObj, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IDiscMaster, (void**)&pdm); + if (SUCCEEDED(hr)) + { + // TODO determine why this is causing trk*.tmp files to be created when called + // which ends up spamming the %temp% folder everytime Winamp starts :o( + hr = pdm->Open(); + if (SUCCEEDED(hr)) + { + IEnumDiscMasterFormats *pef; + hr = pdm->EnumDiscMasterFormats(&pef); + if (SUCCEEDED(hr)) + { + IID pFormats[2]; + hr = pef->Next(sizeof(pFormats)/sizeof(IID), pFormats, &nActual); + if (SUCCEEDED(hr)) + { + while(nActual--) { if (IID_IRedbookDiscMaster == pFormats[nActual]) break; } + if (nActual != ((ULONG)-1)) + { + IRedbookDiscMaster *pdf; + hr = pdm->SetActiveDiscMasterFormat(IID_IRedbookDiscMaster, (void**)&pdf); + if (SUCCEEDED(hr)) + { + pdf->Release(); + hr = pdm->EnumDiscRecorders(&per); + if (SUCCEEDED(hr)) + { + while (S_OK== per->Next(1, &pdr, &nActual) && nActual > 0) + { + BSTR bstrPath; + hr = pdr->GetPath(&bstrPath); + if (SUCCEEDED(hr)) + { + for (int i = 0; i < count; i++) + { + if (0 == lstrcmpW(pDevInfo[i].szTargetPath, bstrPath)) + { + LONG type; + if (SUCCEEDED(pdr->GetRecorderType(&type))) + { + pDevInfo[i].dwType &= 0x0000FFFF; + switch(type) + { + case RECORDER_CDR: pDevInfo[i].dwType |= DRIVE_CAP_R; break; + case RECORDER_CDRW: pDevInfo[i].dwType |= DRIVE_CAP_RW; break; + } + } + break; + } + } + if (bstrPath) SysFreeString(bstrPath); + } + pdr->Release(); + } + per->Release(); + } + } + } + } + pef->Release(); + } + pdm->Close(); + } + pdm->Release(); + } + else + { + }*/ +} + +static void Listener_OnDeviceChange(HWND hwnd, UINT nType, DWORD_PTR dwData) +{ + DEV_BROADCAST_HDR *phdr; + + switch(nType) + { + case DBT_DEVICEARRIVAL: + phdr = (DEV_BROADCAST_HDR*)dwData; + if (DBT_DEVTYP_VOLUME == phdr->dbch_devicetype) + { + DEV_BROADCAST_VOLUME *pvol = (DEV_BROADCAST_VOLUME*)phdr; + if (DBTF_MEDIA == pvol->dbcv_flags) Medium_Add(Drive_LetterFromMask(pvol->dbcv_unitmask), (DWORD)-1); + else if (0 == pvol->dbcv_flags) + { + char root[] = "X:\\"; + root[0] = Drive_LetterFromMask(pvol->dbcv_unitmask); + if (DRIVE_CDROM == GetDriveTypeA(root)) QueueInfoAPC(0, APC_CheckDrives, (ULONG_PTR)root[0]); + } + } + break; + case DBT_DEVICEREMOVECOMPLETE: + phdr = (DEV_BROADCAST_HDR*)dwData; + if (DBT_DEVTYP_VOLUME == phdr->dbch_devicetype) + { + DEV_BROADCAST_VOLUME *pvol = (DEV_BROADCAST_VOLUME*)phdr; + if (DBTF_MEDIA == pvol->dbcv_flags) Medium_Remove(Drive_LetterFromMask(pvol->dbcv_unitmask)); + else if (0 == pvol->dbcv_flags) + { + char root[] = "X:\\"; + root[0] = Drive_LetterFromMask(pvol->dbcv_unitmask); + if (DRIVE_CDROM == GetDriveTypeA(root)) Drive_Remove(root[0]); + } + } + break; + } +} + +static DWORD CALLBACK InfoThread(LPVOID param) +{ + MSG msg; + DWORD start, status, timeout, result(0); + BOOL bComInit, run(TRUE); + HANDLE hTemp(NULL); + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); + bComInit = ( S_OK == CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); + + timeout = 20000; // 20 seconds delay + start = GetTickCount(); + + while(run) + { + DWORD elapsed = GetTickCount() - start; + if (elapsed < timeout) + status = MsgWaitForMultipleObjectsEx(0, NULL, timeout - elapsed, + QS_ALLINPUT, MWMO_WAITALL | MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); + else status = WAIT_TIMEOUT; + + switch(status) + { + case WAIT_FAILED: + if (bComInit) CoUninitialize(); + return (DWORD)-1; + case WAIT_TIMEOUT: + if (NULL != pMngr) + { + EnterCriticalSection(&pMngr->csLock); + start = GetCurrentThreadId(); + hTemp = NULL; + + for (int i = pMngr->nCount - 1; i >= 0; i--) + { + if (pMngr->pDrives[i].dwThreadId == start) + { + pMngr->pDrives[i].dwThreadId = 0; + hTemp = pMngr->pDrives[i].hThread; + pMngr->pDrives[i].hThread = NULL; + } + } + LeaveCriticalSection(&pMngr->csLock); + + //while (WAIT_IO_COMPLETION == WaitForMultipleObjectsEx(0, NULL, TRUE, 0, TRUE)); + while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION); + } + result = 2; + run = FALSE; + break; + case WAIT_IO_COMPLETION: start = GetTickCount(); break; + case WAIT_OBJECT_0: + while (run && PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) + { + switch(msg.message) + { + case WM_QUIT: + result = (DWORD)msg.wParam; + run = FALSE; + break; + case WM_EX_QUIT: + PostQuitMessage((INT)msg.wParam); + break; + default: + TranslateMessage(&msg); + DispatchMessageW(&msg); + break; + } + } + break; + } + } + + if (bComInit) CoUninitialize(); + if (2 == result && hTemp) CloseHandle(hTemp); + hTemp = NULL; + return result; +} + +static DWORD CALLBACK PollingThread(LPVOID param) +{ + MSG msg; + DWORD status, timeout; + BOOL bComInit; + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); + bComInit = ( S_OK == CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); + + timeout = POLLMEDIUMCHANGE_INTERVAL; + + for(;;) + { + DWORD elapsed, start = GetTickCount(); + while ((elapsed = GetTickCount() - start) < timeout) + { + status = MsgWaitForMultipleObjectsEx(0, NULL, timeout - elapsed, + QS_ALLINPUT, MWMO_WAITALL | MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); + switch(status) + { + case WAIT_FAILED: + if (bComInit) CoUninitialize(); + return (DWORD)-1; + case WAIT_TIMEOUT: PollMediumInfo(0); break; + case WAIT_OBJECT_0: + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) + { + switch(msg.message) + { + case WM_QUIT: + if (bComInit) CoUninitialize(); + return (DWORD)msg.wParam; + case WM_EX_QUIT: + PostQuitMessage((INT)msg.wParam); + break; + default: + TranslateMessage(&msg); + DispatchMessageW(&msg); + break; + } + } + break; + } + } + } +} + +static void CALLBACK PollMediumInfo(ULONG_PTR param) +{ + char letters[32] = {0}; + LPCWSTR pszDevName[32] = {0}; + INT index, count; + if (!pMngr) return; + + count = 0; + EnterCriticalSection(&pMngr->csLock); + for (index =0; index < pMngr->nCount; index++) + { + if (DM_MODE_BURNING != pMngr->pDrives[index].cMode && DM_MODE_RIPPING != pMngr->pDrives[index].cMode) + { + letters[count] = pMngr->pDrives[index].cLetter; + pszDevName[count] = pMngr->pDrives[index].pszDevName; + count++; + } + } + + LeaveCriticalSection(&pMngr->csLock); + + while(count--) + { + HANDLE hDevice = CreateFileW(pszDevName[count], GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + + if (INVALID_HANDLE_VALUE != hDevice) + { + BYTE sc, asc, ascq; + BOOL bReady, bReportChanges, bNeedRecheck; + DWORD ticks; + + bReportChanges = FALSE; + bNeedRecheck = FALSE; + + if(!SPTI_TestUnitReady(hDevice, &sc, &asc, &ascq, 2)) + { + bReady = FALSE; + } + else bReady = (0x00 == sc || (0x02 == sc && 0x3A == asc)); + + CloseHandle(hDevice); + + EnterCriticalSection(&pMngr->csLock); + for (index =0; index < pMngr->nCount; index++) + { + if (pMngr->pDrives[index].cLetter == letters[count]) + { + ticks = GetTickCount(); + if (pMngr->pDrives[index].bMediumInserted && + (ticks - pMngr->pDrives[index].mediumInfo.msLastPolled) > POLLMEDIUMVALIDATE_INTERVAL) bNeedRecheck = TRUE; + pMngr->pDrives[index].mediumInfo.msLastPolled = ticks; + + if (bReady && ((0x00 == sc) != pMngr->pDrives[index].bMediumInserted)) bReportChanges = TRUE; + break; + } + } + LeaveCriticalSection(&pMngr->csLock); + + if (bReportChanges) + { + if (0 == sc) Medium_Add(letters[count], (DWORD)-1); + else Medium_Remove(letters[count]); + } + else if (bNeedRecheck) + { + QueueInfoAPC(letters[count], APC_IsMediumChanged, (DWORD_PTR)letters[count]); + } + + } + } +} + + +static LRESULT WINAPI ListenerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_DEVICECHANGE: + Listener_OnDeviceChange(hwnd, (UINT)wParam, (DWORD_PTR)lParam); + break; + } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); +} + +static void CALLBACK APC_CheckDrives(ULONG_PTR param) +{ + INT index, result, count; + DWORD unitmask, dwOutput; + STORAGE_DEVICE_NUMBER sdn; + DEVICEINFO *pDevInfo; + BYTE buffer[4096] = {0}; + + if (!pMngr) return; + + unitmask = (DWORD)param; + count = 0; + for (int i = 0; i < 26; i++) {if (0x1 & (unitmask >> i)) count++;} + if (!count) return; + + pDevInfo = (DEVICEINFO*)calloc(count, sizeof(DEVICEINFO)); + if (!pDevInfo) return; + + index = 0; + for (int i = 0; i < 26; i++) + { + if (0x1 & unitmask) + { + pDevInfo[index].cLetter = (CHAR)(('A' + i)); + index++; + if (index == count) break; + } + unitmask = unitmask >> 1; + } + + GetDeviceNames(pDevInfo, count); + + for (int i = 0; i < count; i++) + { + HANDLE hDevice = CreateFileW(pDevInfo[i].pszDevName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + + if (INVALID_HANDLE_VALUE != hDevice) + { + ZeroMemory(&sdn, sizeof(STORAGE_DEVICE_NUMBER)); + result = DeviceIoControl(hDevice, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(STORAGE_DEVICE_NUMBER), &dwOutput, NULL); + pDevInfo->deviceNumber = (result) ? sdn.DeviceNumber : -1; + + ZeroMemory(&buffer, sizeof(buffer)/sizeof(BYTE)); + result = DeviceIoControl(hDevice, IOCTL_STORAGE_GET_MEDIA_TYPES_EX, NULL, 0, buffer, sizeof(buffer)/sizeof(BYTE), &dwOutput, NULL); + if (result && buffer && (FILE_DEVICE_DVD & ((GET_MEDIA_TYPES*)buffer)->DeviceType)) pDevInfo[i].dwType = DRIVE_TYPE_DVD; + else pDevInfo[i].dwType = DRIVE_TYPE_CD; + + CloseHandle(hDevice); + } + } + + GetDeviceCaps(pDevInfo, count); + + EnterCriticalSection(&pMngr->csLock); + + for (int i = 0; i < count; i++) + { + pDevInfo[i].opCode = 0; + for (index =0; index < pMngr->nCount; index++) + { + if (pMngr->pDrives[index].cLetter == pDevInfo[i].cLetter) + { + if (-1 == pMngr->pDrives[index].deviceNumber || pMngr->pDrives[index].deviceNumber != pDevInfo[i].deviceNumber) + pDevInfo[i].opCode = 1; + break; + } + } + if (pMngr->nCount == index) pDevInfo[i].opCode = 2; + } + LeaveCriticalSection(&pMngr->csLock); + + for (int i = 0; i < count; i++) + { + if (pDevInfo[i].opCode) Drive_Add(&pDevInfo[i]); + } + + if (pDevInfo) + { + for (int i = 0; i<count; i++) + { + if (pDevInfo[i].pszDevName) free(pDevInfo[i].pszDevName); + } + free(pDevInfo); + } +} + +static void CALLBACK APC_IsMediumChanged(ULONG_PTR param) +{ + INT opCode; + DWORD serial; + + wchar_t devname[4] = L"X:\\"; + + if (!pMngr) return; + + opCode = 0; + devname[0] = (char)(0xFF & param); + if (devname[0]) + { + BOOL result; + + serial = 0; + result = GetVolumeInformationW(devname, NULL, 0, &serial, NULL, NULL, NULL, 0); + if (!result) serial = 0; // perhaps this is empty recordable disc + + EnterCriticalSection(&pMngr->csLock); + + for (INT index =0; index < pMngr->nCount; index++) + { + if (pMngr->pDrives[index].cLetter == (char)param ) + { + pMngr->pDrives[index].mediumInfo.msLastPolled = GetTickCount(); + if (!pMngr->pDrives[index].bMediumInserted && result) opCode = 0x02; + else if (pMngr->pDrives[index].mediumInfo.serialNumber != serial) + { + if (-1 == pMngr->pDrives[index].mediumInfo.serialNumber) pMngr->pDrives[index].mediumInfo.serialNumber = serial; + else opCode = 0x03; + } + break; + } + } + LeaveCriticalSection(&pMngr->csLock); + + if (0x01 & opCode) Medium_Remove((char)param); + if (0x02 & opCode) Medium_Add((char)param, serial); + } +} + +static void CALLBACK APC_AsyncOp_Complete(ULONG_PTR param) +{ + DM_NOTIFY_PARAM *phdr = (DM_NOTIFY_PARAM*)param; + if (phdr->hReserved) + { + CloseHandle(phdr->hReserved); + phdr->hReserved = NULL; + } + + if (phdr->callback) + { + if (phdr->uMsg) + { + if (IsWindow((HWND)phdr->callback)) SendMessageW((HWND)phdr->callback, phdr->uMsg, (WPARAM)DMW_OPCOMPLETED, (LPARAM)phdr); + } + else ((DMNPROC)phdr->callback)(DMW_OPCOMPLETED, (INT_PTR)param); + } + + if (phdr->fnFree) + { + phdr->fnFree(phdr); + } +} + +static void AsycOp_Complete(DM_NOTIFY_PARAM *param) +{ + if (param) QueueUserAPC(APC_AsyncOp_Complete, param->hReserved, (ULONG_PTR)param); +} + +static void CALLBACK APC_GetUnitInfo(ULONG_PTR param) +{ + DWORD unit; + DM_UNITINFO_PARAM *puip; + puip = (DM_UNITINFO_PARAM*)param; + + puip->header.opCode = DMOP_UNITINFO; + + unit = CheckLetter(puip->header.cLetter); + + if (!unit || (!PrimoSDKHelper_IsInitialized() && !PrimoSDKHelper_Initialize())) puip->header.result = PRIMOSDK_CMDSEQUENCE; + else + { + DWORD ready = 0; + CHAR buffer[512] = {0}; + + puip->header.result = PrimoSDKHelper_UnitInfo(&unit, &puip->dwType, + ((DMF_DESCRIPTION & puip->header.fFlags) || (DMF_FIRMWARE & puip->header.fFlags)) ? (BYTE*)buffer: NULL, + (DMF_READY & puip->header.fFlags) ? &ready : NULL); + + if (PRIMOSDK_OK == puip->header.result) + { + if (DMF_READY & puip->header.fFlags) puip->bReady = (0 != ready); + + if (DMF_DESCRIPTION & puip->header.fFlags) + { + INT len = lstrlenA(buffer); + if (len > 5) len -= 5; + if (!puip->pszDesc || puip->cchDesc < (len + 1)) puip->cchDesc = -(len + 1); + else + { + StringCchCopyNA(puip->pszDesc, puip->cchDesc, buffer, len); + puip->cchDesc = len; + } + } + if (DMF_FIRMWARE & puip->header.fFlags) + { + LPSTR p; + INT len = lstrlenA(buffer); + p = buffer + (len - ((len > 5) ? 4 : 0)); + if (!puip->pszFirmware || puip->cchFirmware < 4) puip->cchFirmware = -4; + else + { + StringCchCopyA(puip->pszFirmware, puip->cchFirmware, p); + puip->cchFirmware = 4; + } + } + } + } + AsycOp_Complete(&puip->header); +} + +static void CALLBACK APC_GetUnitInfo2(ULONG_PTR param) +{ + DWORD unit; + DM_UNITINFO2_PARAM *puip; + + puip = (DM_UNITINFO2_PARAM*)param; + puip->header.opCode = DMOP_UNITINFO2; + unit = CheckLetter(puip->header.cLetter); + + if (!unit || (!PrimoSDKHelper_IsInitialized() && !PrimoSDKHelper_Initialize())) puip->header.result = PRIMOSDK_CMDSEQUENCE; + else + { + BOOL bReady; + DWORD szTypes[32], rfu; + + if (DriveManager_IsUnitReady((char)unit, &bReady) && !bReady) + { + SleepEx(1000, TRUE); + QueueUserAPC(APC_GetUnitInfo2, GetCurrentThread(), param); + return; + } + + puip->header.result = PrimoSDKHelper_UnitInfo2(&unit, szTypes, &puip->dwClassId, &puip->dwBusType, &rfu); + if (PRIMOSDK_OK == puip->header.result) + { + if (DMF_TYPES & puip->header.fFlags) + { + INT len; + for (len = 0; szTypes[len] != 0xFFFFFFFF; len++); + + if (!puip->pdwTypes || puip->nTypes < len) puip->nTypes = -len; + else + { + puip->nTypes = len; + if (len) CopyMemory(puip->pdwTypes, szTypes, sizeof(DWORD)*len); + } + } + } + } + AsycOp_Complete(&puip->header); +} +static void CALLBACK APC_GetDiscInfoEx(ULONG_PTR param) +{ + DWORD unit; + DM_DISCINFOEX_PARAM *pdip; + + pdip = (DM_DISCINFOEX_PARAM*)param; + pdip->header.opCode = DMOP_DISCINFO; + unit = CheckLetter(pdip->header.cLetter); + + if (!unit || (!PrimoSDKHelper_IsInitialized() && !PrimoSDKHelper_Initialize())) pdip->header.result = PRIMOSDK_CMDSEQUENCE; + else + { + BOOL bReady; + DWORD dwFlags, dwErasable; + + if (DriveManager_IsUnitReady((char)unit, &bReady) && !bReady) + { + SleepEx(1000, TRUE); + QueueUserAPC(APC_GetDiscInfoEx, GetCurrentThread(), param); + return; + } + + dwFlags = (DMF_DRIVEMODE_TAO & pdip->header.fFlags); + pdip->header.result = PrimoSDKHelper_DiscInfoEx(&unit, dwFlags, + (DMF_MEDIUMTYPE & pdip->header.fFlags) ? &pdip->dwMediumType : NULL, + (DMF_MEDIUMFORMAT & pdip->header.fFlags) ? &pdip->dwMediumFormat : NULL, + &dwErasable, + (DMF_TRACKS & pdip->header.fFlags) ? &pdip->dwTracks: NULL, + (DMF_USED & pdip->header.fFlags) ? &pdip->dwUsed : NULL, + (DMF_FREE & pdip->header.fFlags) ? &pdip->dwFree : NULL); + + if (PRIMOSDK_OK == pdip->header.result) pdip->bErasable = (0 != dwErasable); + } + + AsycOp_Complete(&pdip->header); +} + +static void CALLBACK APC_GetDiscInfo2(ULONG_PTR param) +{ + DWORD unit; + DM_DISCINFO2_PARAM *pdip; + + pdip = (DM_DISCINFO2_PARAM*)param; + pdip->header.opCode = DMOP_DISCINFO2; + unit = CheckLetter(pdip->header.cLetter); + + if (!unit || (!PrimoSDKHelper_IsInitialized() && !PrimoSDKHelper_Initialize())) pdip->header.result = PRIMOSDK_CMDSEQUENCE; + else + { + DWORD rfu, medium, protectedDVD, flags; + + BOOL bReady; + if (DriveManager_IsUnitReady((char)unit, &bReady) && !bReady) + { + SleepEx(1000, TRUE); + QueueUserAPC(APC_GetDiscInfo2, GetCurrentThread(), param); + return; + } + + pdip->header.result = PrimoSDKHelper_DiscInfo2(&unit, + (DMF_MEDIUM & pdip->header.fFlags) ? &pdip->dwMedium : (DMF_MEDIUMEX & pdip->header.fFlags) ? &medium : NULL, + (DMF_PROTECTEDDVD & pdip->header.fFlags) ? &protectedDVD : NULL, + (DMF_PACKETWRITTEN & pdip->header.fFlags) ? &flags : NULL, + (DMF_MEDIUMEX & pdip->header.fFlags) ? &pdip->dwMediumEx : NULL, + &rfu); + if (PRIMOSDK_OK == pdip->header.result) + { + if (DMF_PROTECTEDDVD & pdip->header.fFlags) pdip->bProtectedDVD = (0 != protectedDVD); + if (DMF_PACKETWRITTEN & pdip->header.fFlags) pdip->bPacketWritten = (0 != (PRIMOSDK_PACKETWRITTEN & protectedDVD)); + } + + } + AsycOp_Complete(&pdip->header); +} + +static void CALLBACK APC_GetTitle(ULONG_PTR param) +{ + CHAR cLetter; + DM_TITLE_PARAM *pdtp; + + pdtp = (DM_TITLE_PARAM*)param; + pdtp->header.opCode = DMOP_TITLE; + cLetter = CheckLetter(pdtp->header.cLetter); + + pdtp->header.result = PRIMOSDK_CMDSEQUENCE; + if (cLetter && pdtp->pszTitle) + { + wchar_t name[] = L"X:\\"; + MCIDEVICEID devId; + MCI_OPEN_PARMS op = {0}; + MCI_GENERIC_PARMS gp = {0}; + MCI_STATUS_PARMS sp = {0}; + + name[0] = cLetter; + + op.lpstrDeviceType = (LPWSTR)MCI_DEVTYPE_CD_AUDIO; + op.lpstrElementName = name; + + if (!mciSendCommandW(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID | MCI_OPEN_ELEMENT, (DWORD_PTR)&op)) + { + HRESULT hr; + + devId = op.wDeviceID; + sp.dwItem = MCI_STATUS_MEDIA_PRESENT; + INT present = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (BOOL)sp.dwReturn : 0; + + if (present) + { + INT nTracks; + BOOL bAudio; + wchar_t szVolume[256] = {0}; + // check if we have at least one audio track + sp.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + nTracks = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (INT)sp.dwReturn : -1; + bAudio = FALSE; + + if (nTracks > 0) + { + sp.dwItem = MCI_CDA_STATUS_TYPE_TRACK; + for (sp.dwTrack = 1; sp.dwTrack <= (UINT)nTracks && !bAudio; sp.dwTrack++) + { + mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR)&sp); + bAudio = (MCI_CDA_TRACK_AUDIO == sp.dwReturn); + } + if (bAudio) WASABI_API_LNGSTRINGW_BUF(IDS_CD_AUDIO, szVolume, sizeof(szVolume)/sizeof(wchar_t)); + else + { + INT result; + wchar_t devname[4] = L"X:\\"; + devname[0] = cLetter; + result = GetVolumeInformationW(devname, szVolume, sizeof(szVolume)/sizeof(wchar_t), NULL, NULL, NULL, NULL, 0); + if (!result) WASABI_API_LNGSTRINGW_BUF(IDS_DISC_DATA, szVolume, sizeof(szVolume)/sizeof(wchar_t)); + } + } + else WASABI_API_LNGSTRINGW_BUF(IDS_DISC_BLANK, szVolume, sizeof(szVolume)/sizeof(wchar_t)); + + hr = StringCchPrintfW(pdtp->pszTitle, pdtp->cchTitle, L"%s (%c:)", szVolume, cLetter); + } + else + { + INT nDriveType, nDriveCap; + DWORD type; + wchar_t szDriveType[32] = {0}, szDriveCap[64] = {0}; + + type = DriveManager_GetDriveType(cLetter); + if ((DRIVE_TYPE_UNKNOWN | DRIVE_CAP_UNKNOWN) == type) type = DRIVE_TYPE_CD; + + nDriveCap = ((DRIVE_CAP_R | DRIVE_CAP_RW) & type) ? IDS_RECORDER_CAP : IDS_DRIVE_CAP; + nDriveType = (IDS_DRIVE_CAP == nDriveCap && (DRIVE_TYPE_DVD & type)) ? IDS_DVD : IDS_CD; + + WASABI_API_LNGSTRINGW_BUF(nDriveType, szDriveType, sizeof(szDriveType)/sizeof(wchar_t)); + WASABI_API_LNGSTRINGW_BUF(nDriveCap, szDriveCap, sizeof(szDriveCap)/sizeof(wchar_t)); + hr = StringCchPrintfW(pdtp->pszTitle, pdtp->cchTitle, L"%s %s (%C:)", szDriveType, szDriveCap, cLetter); + } + pdtp->header.result = hr; + mciSendCommandW(devId, MCI_CLOSE, MCI_WAIT, (DWORD_PTR)&gp); + } + } + AsycOp_Complete(&pdtp->header); +} + +static void CALLBACK APC_DriveScan(ULONG_PTR param) +{ + char i; + char root[] = "A:\\"; + DWORD unitmask; + DEVICEINFO di = {0}; + + /// detect drives + unitmask = GetLogicalDrives(); + + di.deviceNumber = -1; + di.dwType = DRIVE_TYPE_CD; + + for (i = 0; i < 26; ++i) + { + if (0x1 & (unitmask >> i)) + { + root[0] = ('A' + i); + if(DRIVE_CDROM != GetDriveTypeA(root)) unitmask &= ~(1 << i); + else + { + di.cLetter = root[0]; + Drive_Add(&di); + } + } + } + APC_CheckDrives((ULONG_PTR)unitmask); +} + +#define MAX_TEST_ATTEMPT 20 + +static void CALLBACK APC_Eject(ULONG_PTR param) +{ + INT nCmd; + CHAR cLetter; + BYTE sc(0), asc(0), ascq(0); + + nCmd = HIWORD(param); + cLetter = CheckLetter((CHAR)param); + + if (cLetter && DM_MODE_READY == DriveManager_GetDriveMode(cLetter)) + { + BOOL bSuccess; + HANDLE hDevice; + + hDevice = CreateFileW(GetDeviceName(cLetter), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + if (INVALID_HANDLE_VALUE != hDevice) + { + DWORD dwOutput; + LARGE_INTEGER start, finish; + + bSuccess = SPTI_TestUnitReady(hDevice, &sc, &asc, &ascq, 3); + if (!bSuccess && ERROR_SEM_TIMEOUT == GetLastError()) + { + bSuccess = TRUE; + sc = 0xFF; + } + + if (bSuccess && (0 == sc || (0x02 == sc && 0x3A == asc))) + { + INT opCode; + opCode = (DM_EJECT_REMOVE == nCmd || 0x00 == sc || (0x3A == asc && 0x01 == ascq)) ? + IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA; + + QueryPerformanceCounter(&start); + bSuccess = DeviceIoControl(hDevice, opCode, NULL, 0, NULL, 0, &dwOutput, NULL); + QueryPerformanceCounter(&finish); + + if (bSuccess && DM_EJECT_CHANGE == nCmd && 0x00 != sc && 0x00 == ascq) + { + finish.QuadPart -= start.QuadPart; + + if (finish.QuadPart < freq.QuadPart && (finish.QuadPart*100000 / freq.QuadPart) < 200) + { + // test unit redy + INT i; + sc = 0x02; asc = 0x04; ascq = 0x01; + for (i = 0; i < MAX_TEST_ATTEMPT && 0x02 == sc && 0x04 == asc && 0x01 == ascq; i++) + { + Sleep(50); + if (!SPTI_TestUnitReady(hDevice, &sc, &asc, &ascq, 3) && ERROR_SEM_TIMEOUT == GetLastError()) + i = MAX_TEST_ATTEMPT; + } + if (i < MAX_TEST_ATTEMPT && 0x02 == sc && 0x3A ==asc) + { + DeviceIoControl(hDevice, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &dwOutput, NULL); + } + sc = 0x00; + } + } + } + CloseHandle(hDevice); + } + else bSuccess = FALSE; + + if (!bSuccess) + { // we can try MCI + + } + else if (0x00 != sc && !(0x02 == sc && 0x3A == asc)) + { + SleepEx(200, TRUE); + QueueUserAPC(APC_Eject, GetCurrentThread(), param); + return; + } + } +} + +static void CALLBACK APC_GetMCIInfo(ULONG_PTR param) +{ + CHAR cLetter; + MCI_OPEN_PARMS op = {0}; + DM_MCI_PARAM *pmcip; + + pmcip = (DM_MCI_PARAM*)param; + pmcip->header.opCode = DMOP_MCIINFO; + cLetter = CheckLetter(pmcip->header.cLetter); + + pmcip->header.result = PRIMOSDK_CMDSEQUENCE; + if (cLetter) + { + wchar_t name[] = L"X:\\"; + MCIDEVICEID devId; + MCI_INFO_PARMS ip = {0}; + MCI_GENERIC_PARMS gp = {0}; + MCI_STATUS_PARMS sp = {0}; + + name[0] = cLetter; + + op.lpstrDeviceType = (LPWSTR)MCI_DEVTYPE_CD_AUDIO; + op.lpstrElementName = name; + + if (!mciSendCommandW(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID | MCI_OPEN_ELEMENT, (DWORD_PTR)&op)) + { + WCHAR buffer[512] = {0}; + INT nMaxTracks = pmcip->nTracks; + + devId = op.wDeviceID; + + if ((DMF_TRACKCOUNT | DMF_TRACKSINFO) & pmcip->header.fFlags) + { + sp.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + pmcip->nTracks = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (INT)sp.dwReturn : -1; + } + if (DMF_READY & pmcip->header.fFlags) + { + sp.dwItem = MCI_STATUS_READY; + pmcip->bReady = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (BOOL)sp.dwReturn : 0; + } + if (DMF_MODE & pmcip->header.fFlags) + { + sp.dwItem = MCI_STATUS_MODE; + pmcip->uMode = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (UINT)sp.dwReturn : 0; + } + if (DMF_MEDIUMPRESENT & pmcip->header.fFlags) + { + sp.dwItem = MCI_STATUS_MEDIA_PRESENT; + pmcip->bMediumPresent = (!mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&sp)) ? (BOOL)sp.dwReturn : 0; + } + if (DMF_MEDIUMUID & pmcip->header.fFlags) + { + ip.dwRetSize = sizeof(buffer)/sizeof(wchar_t); + ip.lpstrReturn= buffer; + if (!mciSendCommandW(devId, MCI_INFO, MCI_WAIT | MCI_INFO_MEDIA_IDENTITY, (DWORD_PTR)&ip)) + { + INT len; + len = lstrlenW(ip.lpstrReturn); + if (S_OK == StringCchCopyW(pmcip->pszMediumUID, pmcip->cchMediumUID, ip.lpstrReturn)) + { + pmcip->cchMediumUID = len; + } + else pmcip->cchMediumUID = 0 - (len + 1); + } + else pmcip->cchMediumUID = -1; + } + if (DMF_MEDIUMUPC & pmcip->header.fFlags) + { + ip.dwCallback = NULL; + ip.dwRetSize = sizeof(buffer)/sizeof(wchar_t); + ip.lpstrReturn = buffer; + if (!mciSendCommandW(devId, MCI_INFO, MCI_WAIT | MCI_INFO_MEDIA_UPC, (DWORD_PTR)&ip)) + { + INT len; + len = lstrlenW(ip.lpstrReturn); + if (S_OK == StringCchCopyW(pmcip->pszMediumUPC, pmcip->cchMediumUPC, ip.lpstrReturn)) + { + pmcip->cchMediumUPC = len; + } + else pmcip->cchMediumUPC = 0 - (len + 1); + } + else pmcip->cchMediumUPC = -1; + } + + if (DMF_TRACKSINFO & pmcip->header.fFlags) + { + MCI_SET_PARMS setp; + + if (nMaxTracks < pmcip->nTracks) pmcip->nTracks = (0 - pmcip->nTracks); + else + { + INT prevPos(0), length(0); + setp.dwTimeFormat = MCI_FORMAT_MILLISECONDS; + mciSendCommandW(devId, MCI_SET, MCI_WAIT | MCI_SET_TIME_FORMAT, (DWORD_PTR)&setp); + + for (int i = pmcip->nTracks; i > 0; i--) + { + sp.dwItem = MCI_CDA_STATUS_TYPE_TRACK; + sp.dwTrack = i; + mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR)&sp); + BOOL bAudio = (MCI_CDA_TRACK_AUDIO == sp.dwReturn); + + sp.dwItem = MCI_STATUS_POSITION; + sp.dwTrack = i; + mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR)&sp); + + if (i != pmcip->nTracks) length = prevPos - (INT)sp.dwReturn; + prevPos = (INT)sp.dwReturn; + + if (i == pmcip->nTracks) + { + sp.dwItem = MCI_STATUS_LENGTH; + sp.dwTrack = i; + mciSendCommandW(devId, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR)&sp); + length = (INT)sp.dwReturn; + } + + pmcip->pTracks[i- 1] = (0x7FFFFFF & length) | ((bAudio) ? 0x80000000 : 0); + } + + setp.dwTimeFormat = MCI_FORMAT_TMSF; + mciSendCommandW(devId, MCI_SET, MCI_WAIT | MCI_SET_TIME_FORMAT, (DWORD_PTR)&setp); + } + } + + mciSendCommandW(devId, MCI_CLOSE, MCI_WAIT, (DWORD_PTR)&gp); + pmcip->header.result = PRIMOSDK_OK; + } + } + + AsycOp_Complete(&pmcip->header); +} + +static void CALLBACK APC_GetIMAPIInfo(ULONG_PTR param) +{ + CHAR cLetter; + BOOL bReady; + HRESULT hr(S_FALSE); + IDiscMaster *pdm; + IDiscRecorder *pdr; + IEnumDiscRecorders *per; + ULONG nActual; + + wchar_t szDevName[] = L"X:\\"; + wchar_t szTargetName[128] = {0}; + DM_IMAPI_PARAM *pIMAPI; + + pIMAPI = (DM_IMAPI_PARAM*)param; + cLetter = CheckLetter(pIMAPI->header.cLetter); + + if (DriveManager_IsUnitReady(cLetter, &bReady) && !bReady) + { + SleepEx(1000, TRUE); + QueueUserAPC(APC_GetIMAPIInfo, GetCurrentThread(), param); + return; + } + + pIMAPI->header.opCode = DMOP_IMAPIINFO; + + pIMAPI->bRecorder = FALSE; + pIMAPI->header.result = (DWORD)E_INVALIDARG; + + szDevName[0] = cLetter; + if (cLetter && QueryDosDeviceW(szDevName, szTargetName, sizeof(szTargetName)/sizeof(wchar_t))) + { + hr = CoCreateInstance(CLSID_MSDiscMasterObj, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IDiscMaster, (void**)&pdm); + if (SUCCEEDED(hr)) + { + hr = pdm->Open(); + if (SUCCEEDED(hr)) + { + IEnumDiscMasterFormats *pef; + hr = pdm->EnumDiscMasterFormats(&pef); + if (SUCCEEDED(hr)) + { + IID pFormats[2]; + hr = pef->Next(sizeof(pFormats)/sizeof(IID), pFormats, &nActual); + if (SUCCEEDED(hr)) + { + while(nActual--) { if (IID_IRedbookDiscMaster == pFormats[nActual]) break; } + if (nActual != ((ULONG)-1)) + { + IRedbookDiscMaster *pdf; + hr = pdm->SetActiveDiscMasterFormat(IID_IRedbookDiscMaster, (void**)&pdf); + if (SUCCEEDED(hr)) + { + pdf->Release(); + hr = pdm->EnumDiscRecorders(&per); + if (SUCCEEDED(hr)) + { + while (S_OK== per->Next(1, &pdr, &nActual) && nActual > 0) + { + BSTR bstrPath; + hr = pdr->GetPath(&bstrPath); + if (SUCCEEDED(hr)) + { + if (0 == lstrcmp(szTargetName, bstrPath)) + { + pIMAPI->bRecorder = TRUE; + if ((DMF_BASEPNPID & pIMAPI->header.fFlags) && FAILED(pdr->GetBasePnPID(&pIMAPI->bstrBasePnPID))) pIMAPI->bstrBasePnPID = NULL; + if ((DMF_DISPLAYNAMES & pIMAPI->header.fFlags) && FAILED(pdr->GetDisplayNames(&pIMAPI->bstrVendorID, &pIMAPI->bstrProductID, &pIMAPI->bstrRevision))) + { + pIMAPI->bstrVendorID = NULL; + pIMAPI->bstrProductID = NULL; + pIMAPI->bstrRevision = NULL; + } + if (DMF_PATH & pIMAPI->header.fFlags) + { + pIMAPI->bstrPath = bstrPath; + bstrPath = NULL; + } + if ((DMF_DRIVESTATE & pIMAPI->header.fFlags) && FAILED(pdr->GetRecorderState(&pIMAPI->ulDriveState))) pIMAPI->ulDriveState = (ULONG)-1; + if ((DMF_DRIVETYPE & pIMAPI->header.fFlags) && FAILED(pdr->GetRecorderType(&pIMAPI->fDriveType))) pIMAPI->fDriveType = 0; + if ((DMF_QUERYMEDIATYPE | DMF_QUERYMEDIAINFO) & pIMAPI->header.fFlags) + { + BOOL bTypeOk(FALSE), bInfoOk(FALSE); + if (SUCCEEDED(pdr->OpenExclusive())) + { + if (0 == (DMF_QUERYMEDIATYPE & pIMAPI->header.fFlags) || + SUCCEEDED(pdr->QueryMediaType(&pIMAPI->fMediaType, &pIMAPI->fMediaFlags))) bTypeOk = TRUE; + if (0 == (DMF_QUERYMEDIAINFO & pIMAPI->header.fFlags) || + SUCCEEDED(pdr->QueryMediaInfo(&pIMAPI->bSessions, &pIMAPI->bLastTrack, &pIMAPI->ulStartAddress, + &pIMAPI->ulNextWritable, &pIMAPI->ulFreeBlocks))) bInfoOk = TRUE; + pdr->Close(); + } + + + if (!bTypeOk) + { + pIMAPI->fMediaType = -1; + pIMAPI->fMediaFlags = -1; + } + if (!bInfoOk) + { + pIMAPI->bLastTrack = 0; + pIMAPI->bSessions = 0; + pIMAPI->ulFreeBlocks = 0; + pIMAPI->ulNextWritable = 0; + pIMAPI->ulStartAddress = 0; + } + + + } + break; + } + if (bstrPath) SysFreeString(bstrPath); + } + pdr->Release(); + } + per->Release(); + } + } + } + } + pef->Release(); + } + pdm->Close(); + } + pdm->Release(); + } + } + pIMAPI->header.result = hr; + AsycOp_Complete(&pIMAPI->header); +}
\ No newline at end of file |