Skip to content

Instantly share code, notes, and snippets.

@meitinger
Last active November 25, 2018 23:35
Show Gist options
  • Save meitinger/00a6d079d919320bc11bed383b070b28 to your computer and use it in GitHub Desktop.
Save meitinger/00a6d079d919320bc11bed383b070b28 to your computer and use it in GitHub Desktop.
Utility that creates an ISO file from an CD or DVD.
/* Copyright (C) 2017-2018, Manuel Meitinger
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define STRICT
#include <Windows.h>
#include <ShlObj.h>
#pragma comment(linker, "/ENTRY:Run")
#pragma comment(linker, "/SUBSYSTEM:WINDOWS")
#pragma comment(lib, "Advapi32.lib")
#pragma comment(lib, "Comdlg32.lib")
#pragma comment(lib, "Kernel32.lib")
#pragma comment(lib, "Ole32.lib")
#pragma comment(lib, "Shell32.lib")
#pragma comment(lib, "User32.lib")
#define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
#define IOCTL_CDROM_GET_DRIVE_GEOMETRY CTL_CODE(IOCTL_CDROM_BASE, 0x0013, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_CDROM_MEDIA_REMOVAL CTL_CODE(IOCTL_CDROM_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS)
#define BUFFER_SIZE ((DWORD)100000)
#define DO(what, op, error) \
if (!(op)) \
{ \
exitCode = error; \
MessageBoxW(NULL, L"Failed to " what L".", caption, MB_OK | MB_ICONERROR | MB_TASKMODAL); \
goto Cleanup; \
}
#define DOWIN32(what, op) DO(what, op, GetLastError())
#define DOCOM(what, op) DO(what, SUCCEEDED(hr = (op)), hr)
void Run(void)
{
WCHAR captionBuffer[MAX_PATH];
DWORD captionBufferSize;
LPWSTR caption;
INT argc;
LPWSTR *argv = NULL;
UINT exitCode = ERROR_SUCCESS;
WCHAR driveName[7] = L"\\\\.\\?:";
HANDLE drive = INVALID_HANDLE_VALUE;
PREVENT_MEDIA_REMOVAL preventRemoval;
DWORD bytesRead;
BOOL lockedDrive = FALSE;
LPWSTR fileName;
OPENFILENAMEW ofn;
WCHAR fileNameBuffer[MAX_PATH];
HRESULT comInit = E_FAIL;
HRESULT hr;
IProgressDialog *progressDialog = NULL;
DISK_GEOMETRY diskGeometry;
LPVOID buffer = NULL;
DWORD alignmentCorrection;
LPVOID alignedBuffer;
DWORD bufferSize;
HANDLE file = INVALID_HANDLE_VALUE;
ULONGLONG total;
ULONGLONG completed;
captionBufferSize = sizeof(captionBuffer);
if (RegGetValueW(HKEY_CLASSES_ROOT, L"Drive\\shell\\MakeIso", L"", RRF_RT_REG_SZ, NULL, captionBuffer, &captionBufferSize) == ERROR_SUCCESS)
{
caption = captionBuffer;
}
else
{
caption = L"Make ISO file...";
}
DOWIN32(L"get command line", argv = CommandLineToArgvW(GetCommandLineW(), &argc));
if (argc < 2 || argc > 3 || !((L'a' <= argv[1][0] && argv[1][0] <= L'z' || L'A' <= argv[1][0] && argv[1][0] <= L'Z') && argv[1][1] == L':' && (argv[1][2] == L'\0' || argv[1][2] == L'\\' && argv[1][3] == L'\0')))
{
MessageBoxW(NULL, L"USAGE: MakeIso.exe <driveletter>: [<isofile>]", caption, MB_OK | MB_ICONINFORMATION);
goto Cleanup;
}
driveName[4] = argv[1][0];
DOWIN32(L"open drive", (drive = CreateFileW(driveName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE);
preventRemoval.PreventMediaRemoval = TRUE;
lockedDrive = DeviceIoControl(drive, IOCTL_CDROM_MEDIA_REMOVAL, &preventRemoval, sizeof(preventRemoval), NULL, 0, &bytesRead, NULL);
if (argc == 3)
{
fileName = argv[2];
}
else
{
SecureZeroMemory(&ofn, sizeof(ofn));
SecureZeroMemory(&fileNameBuffer, sizeof(fileNameBuffer));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFilter = L"ISO Files\0*.iso\0\0";
ofn.nFilterIndex = 1;
ofn.lpstrFile = fileNameBuffer;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrDefExt = L"iso";
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
if (!GetSaveFileNameW(&ofn))
{
DO(L"show file save dialog", CommDlgExtendedError() == 0, CommDlgExtendedError())
goto Cleanup;
}
fileName = fileNameBuffer;
}
DOCOM(L"initialize COM", comInit = CoInitialize(NULL));
DOCOM(L"create progress dialog", CoCreateInstance(&CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, &IID_IProgressDialog, &progressDialog));
progressDialog->lpVtbl->SetTitle(progressDialog, caption);
progressDialog->lpVtbl->SetLine(progressDialog, 1, argv[1], TRUE, NULL);
progressDialog->lpVtbl->SetLine(progressDialog, 2, fileName, TRUE, NULL);
DOCOM(L"show progress dialog", progressDialog->lpVtbl->StartProgressDialog(progressDialog, NULL, NULL, PROGDLG_AUTOTIME, NULL));
DOWIN32(L"get disc information", DeviceIoControl(drive, IOCTL_CDROM_GET_DRIVE_GEOMETRY, NULL, 0, &diskGeometry, sizeof(diskGeometry), &bytesRead, NULL));
DOWIN32(L"allocate buffer", buffer = HeapAlloc(GetProcessHeap(), 0, BUFFER_SIZE + diskGeometry.BytesPerSector));
alignmentCorrection = (SIZE_T)buffer % diskGeometry.BytesPerSector;
if (alignmentCorrection > 0)
{
alignmentCorrection = diskGeometry.BytesPerSector - alignmentCorrection;
alignedBuffer = (LPVOID)((SIZE_T)buffer + alignmentCorrection);
}
else
{
alignedBuffer = buffer;
}
bufferSize = BUFFER_SIZE - (BUFFER_SIZE % diskGeometry.BytesPerSector);
DOWIN32(L"create ISO file", (file = CreateFileW(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE);
total = diskGeometry.Cylinders.QuadPart * diskGeometry.TracksPerCylinder * diskGeometry.SectorsPerTrack * diskGeometry.BytesPerSector;
completed = 0;
progressDialog->lpVtbl->Timer(progressDialog, PDTIMER_RESET, NULL);
while (completed < total && !progressDialog->lpVtbl->HasUserCancelled(progressDialog))
{
DOWIN32(L"read disc", ReadFile(drive, alignedBuffer, bufferSize, &bytesRead, NULL));
if (bytesRead == 0)
{
break;
}
completed += bytesRead;
DOWIN32(L"write ISO file", WriteFile(file, alignedBuffer, bytesRead, &bytesRead, NULL));
progressDialog->lpVtbl->SetProgress64(progressDialog, completed, total);
}
progressDialog->lpVtbl->StopProgressDialog(progressDialog);
Cleanup:
if (file != INVALID_HANDLE_VALUE)
{
CloseHandle(file);
}
if (buffer)
{
HeapFree(GetProcessHeap(), 0, buffer);
}
if (progressDialog)
{
progressDialog->lpVtbl->Release(progressDialog);
}
if (SUCCEEDED(comInit))
{
CoUninitialize();
}
if (lockedDrive)
{
preventRemoval.PreventMediaRemoval = FALSE;
DeviceIoControl(drive, IOCTL_CDROM_MEDIA_REMOVAL, &preventRemoval, sizeof(preventRemoval), NULL, 0, &bytesRead, NULL);
}
if (drive != INVALID_HANDLE_VALUE)
{
CloseHandle(drive);
}
if (argv)
{
LocalFree(argv);
}
ExitProcess(exitCode);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment