mirror of
https://github.com/cookiengineer/audacity
synced 2025-04-30 07:39:42 +02:00
2237 lines
51 KiB
C++
2237 lines
51 KiB
C++
/*
|
|
* PortBurn
|
|
* Windows XP IMAPI v2 implementation
|
|
*
|
|
* Authors:
|
|
* Leland Lucius
|
|
*
|
|
* License: LGPL
|
|
*/
|
|
|
|
#include "portburn.h"
|
|
|
|
#define _WIN32_WINNT 0x0400
|
|
|
|
#include <windows.h>
|
|
#include <tchar.h>
|
|
#include <malloc.h>
|
|
#include <imapi2.h>
|
|
#include <imapi2error.h>
|
|
#include <Objidl.h>
|
|
#include <ObjBase.h>
|
|
#include <stdio.h>
|
|
|
|
#define DEBUG(a) printf a
|
|
|
|
typedef struct {
|
|
|
|
// Used for duration of portburn open session
|
|
|
|
BSTR client; // Things that must be cleaned up at close
|
|
|
|
HRESULT hres;
|
|
bool verify;
|
|
bool test;
|
|
bool underrun;
|
|
bool eject;
|
|
bool gapless;
|
|
int speed;
|
|
|
|
// Used for duration of device open session
|
|
|
|
IStorage *pStorage; //
|
|
IStream *pMarshall; //
|
|
IStream *pStream; // Things that must be cleaned up at close
|
|
HANDLE hThread; //
|
|
BSTR diskid; //
|
|
|
|
float fraction;
|
|
int curtrack;
|
|
int threadres;
|
|
bool burning;
|
|
bool erasing;
|
|
bool cancel;
|
|
VARIANT_BOOL fullerase;
|
|
} PBHandlev2;
|
|
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
|
|
class WriteEventsTAO:public DDiscFormat2TrackAtOnceEvents
|
|
{
|
|
public:
|
|
WriteEventsTAO(PBHandlev2 *h)
|
|
{
|
|
LPTYPELIB pTypeLib;
|
|
HRESULT hres;
|
|
|
|
mH = h;
|
|
mRefs = 0;
|
|
mCookie = -1;
|
|
mConnectionPoint = NULL;
|
|
mConnectionPointContainer = NULL;
|
|
mTypeInfo = NULL;
|
|
mAdjust = 0;
|
|
|
|
hres = LoadRegTypeLib(LIBID_IMAPILib2,
|
|
IMAPILib2_MajorVersion,
|
|
IMAPILib2_MinorVersion,
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
&pTypeLib);
|
|
if (FAILED(hres)) {
|
|
return;
|
|
}
|
|
|
|
hres = pTypeLib->GetTypeInfoOfGuid(__uuidof(DDiscFormat2TrackAtOnceEvents), &mTypeInfo);
|
|
|
|
pTypeLib->Release();
|
|
|
|
if (FAILED(hres)) {
|
|
return;
|
|
}
|
|
|
|
AddRef();
|
|
}
|
|
|
|
~WriteEventsTAO()
|
|
{
|
|
Disconnect();
|
|
|
|
if (mTypeInfo != NULL) {
|
|
mTypeInfo->Release();
|
|
}
|
|
}
|
|
|
|
void Connect(IUnknown *pConnectTo)
|
|
{
|
|
HRESULT hres;
|
|
|
|
hres = pConnectTo->QueryInterface(__uuidof(IConnectionPointContainer),
|
|
(void **)&mConnectionPointContainer);
|
|
if (FAILED(hres)) {
|
|
return;
|
|
}
|
|
|
|
hres = mConnectionPointContainer->FindConnectionPoint(__uuidof(DDiscFormat2TrackAtOnceEvents),
|
|
&mConnectionPoint);
|
|
if (FAILED(hres)) {
|
|
Disconnect();
|
|
return;
|
|
}
|
|
|
|
hres = mConnectionPoint->Advise((IUnknown*)this, &mCookie);
|
|
if (FAILED(hres)) {
|
|
Disconnect();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Disconnect()
|
|
{
|
|
if (mCookie != -1) {
|
|
mConnectionPoint->Unadvise(mCookie);
|
|
mCookie = -1;
|
|
}
|
|
|
|
if (mConnectionPoint != NULL) {
|
|
mConnectionPoint->Release();
|
|
mConnectionPoint = NULL;
|
|
}
|
|
|
|
if (mConnectionPointContainer != NULL) {
|
|
mConnectionPointContainer->Release();
|
|
mConnectionPointContainer = NULL;
|
|
}
|
|
}
|
|
|
|
// IUnknown
|
|
|
|
STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv)
|
|
{
|
|
if (ppv == NULL) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
*ppv = NULL;
|
|
|
|
if (riid == IID_IUnknown || riid == IID_IDispatch || riid == IID_DDiscFormat2TrackAtOnceEvents) {
|
|
*ppv = this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) AddRef(VOID)
|
|
{
|
|
return InterlockedIncrement((LONG*) &mRefs);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) Release(VOID)
|
|
{
|
|
ULONG ref = InterlockedDecrement((LONG*) &mRefs);
|
|
|
|
if (ref == 0) {
|
|
delete this;
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
// IDispatch
|
|
|
|
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo)
|
|
{
|
|
*pctinfo = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
|
|
{
|
|
if (ppTInfo == NULL) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (iTInfo != 0) {
|
|
return DISP_E_BADINDEX;
|
|
}
|
|
|
|
mTypeInfo->AddRef();
|
|
|
|
*ppTInfo = mTypeInfo;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
|
|
{
|
|
return DispGetIDsOfNames(mTypeInfo, rgszNames, cNames, rgDispId);
|
|
}
|
|
|
|
STDMETHODIMP Invoke(DISPID dispIdMember,
|
|
REFIID riid,
|
|
LCID lcid,
|
|
WORD wFlags,
|
|
DISPPARAMS *pDispParams,
|
|
VARIANT *pVarResult,
|
|
EXCEPINFO *pExcepInfo,
|
|
UINT *puArgErr)
|
|
{
|
|
return DispInvoke(this,
|
|
mTypeInfo,
|
|
dispIdMember,
|
|
wFlags,
|
|
pDispParams,
|
|
pVarResult,
|
|
pExcepInfo,
|
|
puArgErr);
|
|
}
|
|
|
|
// DDiscFormat2WriteEvents
|
|
|
|
STDMETHODIMP Update(IDispatch *object, IDispatch *progress)
|
|
{
|
|
IDiscFormat2TrackAtOnceEventArgs *pEventArgs;
|
|
IMAPI_FORMAT2_TAO_WRITE_ACTION action;
|
|
LONG track = -1;
|
|
LONG elapsed = -1;
|
|
LONG remaining = -1;
|
|
LONG sectors = -1;
|
|
LONG start = -1;
|
|
LONG last = -1;
|
|
HRESULT hres;
|
|
|
|
hres = progress->QueryInterface(IID_PPV_ARGS(&pEventArgs));
|
|
if (SUCCEEDED(hres)) {
|
|
float fraction;
|
|
|
|
hres = pEventArgs->get_CurrentTrackNumber(&track);
|
|
hres = pEventArgs->get_CurrentAction(&action);
|
|
hres = pEventArgs->get_ElapsedTime(&elapsed);
|
|
hres = pEventArgs->get_RemainingTime(&remaining);
|
|
// need to figure out best progress indicators
|
|
hres = pEventArgs->get_SectorCount(§ors);
|
|
hres = pEventArgs->get_StartLba(&start);
|
|
hres = pEventArgs->get_LastWrittenLba(&last);
|
|
|
|
fraction = (float) (last - start) / (float) sectors;
|
|
|
|
printf("track %d action = %d elapsed %d remaining %d\n", track, action, elapsed, remaining);
|
|
|
|
// fraction = (elapsed + mAdjust) / (float) (remaining + elapsed + mAdjust);
|
|
|
|
// Never set fraction to 1.0. Screws up synchro between thread and user.
|
|
if (fraction >= 1.0) {
|
|
fraction = (float) 0.99;
|
|
}
|
|
|
|
mH->fraction = fraction;
|
|
|
|
pEventArgs->Release();
|
|
}
|
|
|
|
if (mH->cancel) {
|
|
IDiscFormat2TrackAtOnce *pDiscFormat;
|
|
|
|
hres = object->QueryInterface(IID_PPV_ARGS(&pDiscFormat));
|
|
if (SUCCEEDED(hres)) {
|
|
|
|
hres = pDiscFormat->CancelAddTrack();
|
|
if (SUCCEEDED(hres)) {
|
|
mH->cancel = false;
|
|
}
|
|
|
|
pDiscFormat->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
private:
|
|
PBHandlev2 *mH;
|
|
LPTYPEINFO mTypeInfo;
|
|
ULONG mRefs;
|
|
DWORD mCookie;
|
|
IConnectionPoint *mConnectionPoint;
|
|
IConnectionPointContainer *mConnectionPointContainer;
|
|
int mAdjust;
|
|
};
|
|
|
|
/* Track At Once burning thread */
|
|
DWORD WINAPI PortBurn_v2_RecordDiscTAO(LPVOID lpParam)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) lpParam;
|
|
IDiscRecorder2 *pDiscRecorder = NULL;
|
|
IDiscFormat2TrackAtOnce *pDiscFormat = NULL;
|
|
WriteEventsTAO *pWriteEvents = NULL;
|
|
IStorage *pStorage = NULL;
|
|
IMalloc *pMalloc = NULL;
|
|
IEnumSTATSTG *pEnum = NULL;
|
|
bool prepared = false;
|
|
|
|
h->cancel = false;
|
|
h->fraction = 0.0;
|
|
|
|
h->hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
|
|
if (FAILED(h->hres)) {
|
|
h->threadres = pbErrBurnFailed;
|
|
h->fraction = 1.0;
|
|
return S_OK;
|
|
}
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscRecorder));
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscRecorder->InitializeDiscRecorder(h->diskid);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscFormat2TrackAtOnce),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscFormat));
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscFormat->put_ClientName(h->client);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscFormat->put_Recorder(pDiscRecorder);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
pWriteEvents = new WriteEventsTAO(h);
|
|
if (pWriteEvents == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
pWriteEvents->Connect(pDiscFormat);
|
|
|
|
h->hres = pDiscFormat->PrepareMedia();
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
prepared = true;
|
|
|
|
VARIANT_BOOL underrun = (h->underrun ? VARIANT_FALSE : VARIANT_TRUE);
|
|
h->hres = pDiscFormat->put_BufferUnderrunFreeDisabled(underrun);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = CoGetMalloc(1, &pMalloc);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = CoGetInterfaceAndReleaseStream(h->pMarshall,
|
|
__uuidof(IStorage),
|
|
(void **) &pStorage);
|
|
h->pMarshall = NULL; // stream is always released regardless of failure
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pStorage->EnumElements(0, NULL, NULL, &pEnum);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
STATSTG stat;
|
|
ULONG fetched;
|
|
h->hres = pEnum->Next(1, &stat, &fetched);
|
|
|
|
while (h->hres == S_OK) {
|
|
IStream *pStream;
|
|
|
|
if (stat.pwcsName == NULL) {
|
|
h->hres = E_POINTER;
|
|
goto done;
|
|
}
|
|
|
|
_tprintf(L"stat name = %s\n", stat.pwcsName);
|
|
h->hres = pStorage->OpenStream(stat.pwcsName,
|
|
NULL,
|
|
STGM_SHARE_EXCLUSIVE |
|
|
STGM_READ,
|
|
NULL,
|
|
&pStream);
|
|
|
|
pMalloc->Free(stat.pwcsName);
|
|
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscFormat->AddAudioTrack(pStream);
|
|
|
|
pStream->Release();
|
|
pStream = NULL;
|
|
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pEnum->Next(1, &stat, &fetched);
|
|
}
|
|
|
|
h->hres = pDiscFormat->ReleaseMedia();
|
|
prepared = false;
|
|
|
|
done:
|
|
|
|
if (FAILED(h->hres)) {
|
|
h->threadres = pbErrBurnFailed;
|
|
}
|
|
else {
|
|
h->threadres = pbSuccess;
|
|
}
|
|
|
|
if (prepared) {
|
|
pDiscFormat->ReleaseMedia();
|
|
}
|
|
|
|
if (pMalloc != NULL) {
|
|
pMalloc->Release();
|
|
}
|
|
|
|
if (pEnum != NULL) {
|
|
pEnum->Release();
|
|
}
|
|
|
|
if (pStorage != NULL) {
|
|
pStorage->Release();
|
|
}
|
|
|
|
if (pWriteEvents != NULL) {
|
|
pWriteEvents->Disconnect();
|
|
pWriteEvents->Release();
|
|
}
|
|
|
|
if (pDiscFormat != NULL) {
|
|
pDiscFormat->put_Recorder(NULL);
|
|
pDiscFormat->put_ClientName(NULL);
|
|
pDiscFormat->Release();
|
|
}
|
|
|
|
if (pDiscRecorder != NULL) {
|
|
if (h->eject) {
|
|
pDiscRecorder->EjectMedia();
|
|
}
|
|
|
|
pDiscRecorder->Release();
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
h->fraction = 1.0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
|
|
class WriteEventsDAO:public DDiscFormat2RawCDEvents
|
|
{
|
|
public:
|
|
WriteEventsDAO(PBHandlev2 *h)
|
|
{
|
|
LPTYPELIB pTypeLib;
|
|
HRESULT hres;
|
|
|
|
mH = h;
|
|
mRefs = 0;
|
|
mCookie = -1;
|
|
mConnectionPoint = NULL;
|
|
mConnectionPointContainer = NULL;
|
|
mTypeInfo = NULL;
|
|
mAdjust = 0;
|
|
|
|
hres = LoadRegTypeLib(LIBID_IMAPILib2,
|
|
IMAPILib2_MajorVersion,
|
|
IMAPILib2_MinorVersion,
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
&pTypeLib);
|
|
if (FAILED(hres)) {
|
|
return;
|
|
}
|
|
|
|
hres = pTypeLib->GetTypeInfoOfGuid(__uuidof(DDiscFormat2RawCDEvents), &mTypeInfo);
|
|
|
|
pTypeLib->Release();
|
|
|
|
if (FAILED(hres)) {
|
|
return;
|
|
}
|
|
|
|
AddRef();
|
|
}
|
|
|
|
~WriteEventsDAO()
|
|
{
|
|
Disconnect();
|
|
|
|
if (mTypeInfo != NULL) {
|
|
mTypeInfo->Release();
|
|
}
|
|
}
|
|
|
|
void Connect(IUnknown *pConnectTo)
|
|
{
|
|
HRESULT hres;
|
|
|
|
hres = pConnectTo->QueryInterface(__uuidof(IConnectionPointContainer),
|
|
(void **)&mConnectionPointContainer);
|
|
if (FAILED(hres)) {
|
|
return;
|
|
}
|
|
|
|
hres = mConnectionPointContainer->FindConnectionPoint(__uuidof(DDiscFormat2RawCDEvents),
|
|
&mConnectionPoint);
|
|
if (FAILED(hres)) {
|
|
Disconnect();
|
|
return;
|
|
}
|
|
|
|
hres = mConnectionPoint->Advise((IUnknown*)this, &mCookie);
|
|
if (FAILED(hres)) {
|
|
Disconnect();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Disconnect()
|
|
{
|
|
if (mCookie != -1) {
|
|
mConnectionPoint->Unadvise(mCookie);
|
|
mCookie = -1;
|
|
}
|
|
|
|
if (mConnectionPoint != NULL) {
|
|
mConnectionPoint->Release();
|
|
mConnectionPoint = NULL;
|
|
}
|
|
|
|
if (mConnectionPointContainer != NULL) {
|
|
mConnectionPointContainer->Release();
|
|
mConnectionPointContainer = NULL;
|
|
}
|
|
}
|
|
|
|
// IUnknown
|
|
|
|
STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv)
|
|
{
|
|
if (ppv == NULL) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
*ppv = NULL;
|
|
|
|
if (riid == IID_IUnknown || riid == IID_IDispatch || riid == IID_DDiscFormat2RawCDEvents) {
|
|
*ppv = this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) AddRef(VOID)
|
|
{
|
|
return InterlockedIncrement((LONG*) &mRefs);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) Release(VOID)
|
|
{
|
|
ULONG ref = InterlockedDecrement((LONG*) &mRefs);
|
|
|
|
if (ref == 0) {
|
|
delete this;
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
// IDispatch
|
|
|
|
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo)
|
|
{
|
|
*pctinfo = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
|
|
{
|
|
if (ppTInfo == NULL) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (iTInfo != 0) {
|
|
return DISP_E_BADINDEX;
|
|
}
|
|
|
|
mTypeInfo->AddRef();
|
|
|
|
*ppTInfo = mTypeInfo;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
|
|
{
|
|
return DispGetIDsOfNames(mTypeInfo, rgszNames, cNames, rgDispId);
|
|
}
|
|
|
|
STDMETHODIMP Invoke(DISPID dispIdMember,
|
|
REFIID riid,
|
|
LCID lcid,
|
|
WORD wFlags,
|
|
DISPPARAMS *pDispParams,
|
|
VARIANT *pVarResult,
|
|
EXCEPINFO *pExcepInfo,
|
|
UINT *puArgErr)
|
|
{
|
|
return DispInvoke(this,
|
|
mTypeInfo,
|
|
dispIdMember,
|
|
wFlags,
|
|
pDispParams,
|
|
pVarResult,
|
|
pExcepInfo,
|
|
puArgErr);
|
|
}
|
|
|
|
// DDiscFormat2RawCDEvents
|
|
|
|
STDMETHODIMP Update(IDispatch *object, IDispatch *progress)
|
|
{
|
|
IDiscFormat2RawCDEventArgs *pEventArgs;
|
|
LONG sectors = -1;
|
|
LONG start = -1;
|
|
LONG last = -1;
|
|
HRESULT hres;
|
|
|
|
hres = progress->QueryInterface(IID_PPV_ARGS(&pEventArgs));
|
|
if (SUCCEEDED(hres)) {
|
|
float fraction;
|
|
|
|
hres = pEventArgs->get_SectorCount(§ors);
|
|
hres = pEventArgs->get_StartLba(&start);
|
|
hres = pEventArgs->get_LastWrittenLba(&last);
|
|
|
|
fraction = (float) (last - start) / (float) sectors;
|
|
|
|
// printf("sectors %d startlba %d lastlba %d frac %f\n", sectors, start, last, fraction);
|
|
|
|
// Never set fraction to 1.0. Screws up synchro between thread and user.
|
|
if (fraction >= 1.0) {
|
|
fraction = (float) 0.99;
|
|
}
|
|
|
|
mH->fraction = fraction;
|
|
|
|
pEventArgs->Release();
|
|
}
|
|
|
|
if (mH->cancel) {
|
|
IDiscFormat2RawCD *pDiscFormat;
|
|
|
|
hres = object->QueryInterface(IID_PPV_ARGS(&pDiscFormat));
|
|
if (SUCCEEDED(hres)) {
|
|
|
|
hres = pDiscFormat->CancelWrite();
|
|
if (SUCCEEDED(hres)) {
|
|
mH->cancel = false;
|
|
}
|
|
|
|
pDiscFormat->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
private:
|
|
PBHandlev2 *mH;
|
|
LPTYPEINFO mTypeInfo;
|
|
ULONG mRefs;
|
|
DWORD mCookie;
|
|
IConnectionPoint *mConnectionPoint;
|
|
IConnectionPointContainer *mConnectionPointContainer;
|
|
int mAdjust;
|
|
};
|
|
|
|
/* Track At Once burning thread */
|
|
DWORD WINAPI PortBurn_v2_RecordDiscDAO(LPVOID lpParam)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) lpParam;
|
|
IDiscRecorder2 *pDiscRecorder = NULL;
|
|
IDiscFormat2RawCD *pDiscFormat = NULL;
|
|
IRawCDImageCreator *pDiscImage = NULL;
|
|
WriteEventsDAO *pWriteEvents = NULL;
|
|
IStorage *pStorage = NULL;
|
|
IMalloc *pMalloc = NULL;
|
|
IEnumSTATSTG *pEnum = NULL;
|
|
bool prepared = false;
|
|
|
|
h->cancel = false;
|
|
h->fraction = 0.0;
|
|
|
|
h->hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
|
|
if (FAILED(h->hres)) {
|
|
h->threadres = pbErrBurnFailed;
|
|
h->fraction = 1.0;
|
|
return S_OK;
|
|
}
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscRecorder));
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscRecorder->InitializeDiscRecorder(h->diskid);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscFormat2RawCD),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscFormat));
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscFormat->put_ClientName(h->client);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscFormat->put_Recorder(pDiscRecorder);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
pWriteEvents = new WriteEventsDAO(h);
|
|
if (pWriteEvents == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
pWriteEvents->Connect(pDiscFormat);
|
|
|
|
h->hres = pDiscFormat->PrepareMedia();
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
prepared = true;
|
|
|
|
VARIANT_BOOL underrun = (h->underrun ? VARIANT_FALSE : VARIANT_TRUE);
|
|
h->hres = pDiscFormat->put_BufferUnderrunFreeDisabled(underrun);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftRawCDImageCreator),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscImage));
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
VARIANT_BOOL gapless = (h->gapless ? VARIANT_FALSE : VARIANT_TRUE);
|
|
h->hres = pDiscImage->put_DisableGaplessAudio(gapless);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = CoGetMalloc(1, &pMalloc);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = CoGetInterfaceAndReleaseStream(h->pMarshall,
|
|
__uuidof(IStorage),
|
|
(void **) &pStorage);
|
|
h->pMarshall = NULL; // stream is always released regardless of failure
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pStorage->EnumElements(0, NULL, NULL, &pEnum);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
STATSTG stat;
|
|
ULONG fetched;
|
|
h->hres = pEnum->Next(1, &stat, &fetched);
|
|
|
|
while (h->hres == S_OK) {
|
|
IStream *pStream;
|
|
|
|
if (stat.pwcsName == NULL) {
|
|
h->hres = E_POINTER;
|
|
goto done;
|
|
}
|
|
|
|
_tprintf(L"stat name = %s\n", stat.pwcsName);
|
|
h->hres = pStorage->OpenStream(stat.pwcsName,
|
|
NULL,
|
|
STGM_SHARE_EXCLUSIVE |
|
|
STGM_READ,
|
|
NULL,
|
|
&pStream);
|
|
|
|
pMalloc->Free(stat.pwcsName);
|
|
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
LONG tindex = 0;
|
|
h->hres = pDiscImage->AddTrack(IMAPI_CD_SECTOR_AUDIO, pStream, &tindex);
|
|
|
|
pStream->Release();
|
|
pStream = NULL;
|
|
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pEnum->Next(1, &stat, &fetched);
|
|
}
|
|
|
|
IStream *pStream;
|
|
h->hres = pDiscImage->CreateResultImage(&pStream);
|
|
if (SUCCEEDED(h->hres)) {
|
|
h->hres = pDiscFormat->WriteMedia(pStream);
|
|
|
|
pStream->Release();
|
|
}
|
|
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscFormat->ReleaseMedia();
|
|
prepared = false;
|
|
|
|
done:
|
|
|
|
if (FAILED(h->hres)) {
|
|
h->threadres = pbErrBurnFailed;
|
|
}
|
|
else {
|
|
h->threadres = pbSuccess;
|
|
}
|
|
|
|
if (prepared) {
|
|
pDiscFormat->ReleaseMedia();
|
|
}
|
|
|
|
if (pMalloc != NULL) {
|
|
pMalloc->Release();
|
|
}
|
|
|
|
if (pEnum != NULL) {
|
|
pEnum->Release();
|
|
}
|
|
|
|
if (pStorage != NULL) {
|
|
pStorage->Release();
|
|
}
|
|
|
|
if (pDiscImage != NULL) {
|
|
pDiscImage->Release();
|
|
}
|
|
|
|
if (pWriteEvents != NULL) {
|
|
pWriteEvents->Disconnect();
|
|
pWriteEvents->Release();
|
|
}
|
|
|
|
if (pDiscFormat != NULL) {
|
|
pDiscFormat->put_Recorder(NULL);
|
|
pDiscFormat->put_ClientName(NULL);
|
|
pDiscFormat->Release();
|
|
}
|
|
|
|
if (pDiscRecorder != NULL) {
|
|
if (h->eject) {
|
|
pDiscRecorder->EjectMedia();
|
|
}
|
|
|
|
pDiscRecorder->Release();
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
h->fraction = 1.0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
|
|
class EraseEvents:public DDiscFormat2EraseEvents
|
|
{
|
|
public:
|
|
EraseEvents(PBHandlev2 *h)
|
|
{
|
|
LPTYPELIB pTypeLib;
|
|
HRESULT hres;
|
|
|
|
mH = h;
|
|
mRefs = 0;
|
|
mCookie = -1;
|
|
mConnectionPoint = NULL;
|
|
mConnectionPointContainer = NULL;
|
|
|
|
hres = LoadRegTypeLib(LIBID_IMAPILib2,
|
|
IMAPILib2_MajorVersion,
|
|
IMAPILib2_MinorVersion,
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
&pTypeLib);
|
|
if (FAILED(hres)) {
|
|
return;
|
|
}
|
|
|
|
hres = pTypeLib->GetTypeInfoOfGuid(IID_DDiscFormat2EraseEvents, &mTypeInfo);
|
|
|
|
pTypeLib->Release();
|
|
|
|
if (FAILED(hres)) {
|
|
return;
|
|
}
|
|
|
|
AddRef();
|
|
}
|
|
|
|
~EraseEvents()
|
|
{
|
|
Disconnect();
|
|
|
|
if (mTypeInfo != NULL) {
|
|
mTypeInfo->Release();
|
|
}
|
|
}
|
|
|
|
void Connect(IUnknown *pConnectTo)
|
|
{
|
|
HRESULT hres;
|
|
|
|
hres = pConnectTo->QueryInterface(IID_IConnectionPointContainer,
|
|
(void **)&mConnectionPointContainer);
|
|
if (FAILED(hres)) {
|
|
return;
|
|
}
|
|
|
|
hres = mConnectionPointContainer->FindConnectionPoint(IID_DDiscFormat2EraseEvents,
|
|
&mConnectionPoint);
|
|
if (FAILED(hres)) {
|
|
Disconnect();
|
|
return;
|
|
}
|
|
|
|
hres = mConnectionPoint->Advise((IUnknown*)this, &mCookie);
|
|
if (FAILED(hres)) {
|
|
Disconnect();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Disconnect()
|
|
{
|
|
if (mCookie != -1) {
|
|
mConnectionPoint->Unadvise(mCookie);
|
|
mCookie = -1;
|
|
}
|
|
|
|
if (mConnectionPoint != NULL) {
|
|
mConnectionPoint->Release();
|
|
mConnectionPoint = NULL;
|
|
}
|
|
|
|
if (mConnectionPointContainer != NULL) {
|
|
mConnectionPointContainer->Release();
|
|
mConnectionPointContainer = NULL;
|
|
}
|
|
}
|
|
|
|
// IUnknown
|
|
|
|
STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv)
|
|
{
|
|
if (ppv == NULL) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
*ppv = NULL;
|
|
|
|
if (riid == IID_IUnknown || riid == IID_IDispatch || riid == IID_DDiscFormat2EraseEvents) {
|
|
*ppv = this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) AddRef(VOID)
|
|
{
|
|
return InterlockedIncrement((LONG*) &mRefs);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) Release(VOID)
|
|
{
|
|
ULONG ref = InterlockedDecrement((LONG*) &mRefs);
|
|
|
|
if (ref == 0) {
|
|
delete this;
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
// IDispatch
|
|
|
|
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo)
|
|
{
|
|
*pctinfo = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
|
|
{
|
|
if (ppTInfo == NULL) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (iTInfo != 0) {
|
|
return DISP_E_BADINDEX;
|
|
}
|
|
|
|
mTypeInfo->AddRef();
|
|
|
|
*ppTInfo = mTypeInfo;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
|
|
{
|
|
return DispGetIDsOfNames(mTypeInfo, rgszNames, cNames, rgDispId);
|
|
}
|
|
|
|
STDMETHODIMP Invoke(DISPID dispIdMember,
|
|
REFIID riid,
|
|
LCID lcid,
|
|
WORD wFlags,
|
|
DISPPARAMS *pDispParams,
|
|
VARIANT *pVarResult,
|
|
EXCEPINFO *pExcepInfo,
|
|
UINT *puArgErr)
|
|
{
|
|
return DispInvoke(this,
|
|
mTypeInfo,
|
|
dispIdMember,
|
|
wFlags,
|
|
pDispParams,
|
|
pVarResult,
|
|
pExcepInfo,
|
|
puArgErr);
|
|
}
|
|
|
|
// DDiscFormat2EraseEvents
|
|
|
|
STDMETHODIMP Update(IDispatch *object,
|
|
LONG elapsedSeconds,
|
|
LONG estimatedTotalSeconds)
|
|
{
|
|
float fraction = elapsedSeconds / (float) estimatedTotalSeconds;
|
|
|
|
// Never set fraction to 1.0. Screws up synchro between thread and user.
|
|
if (fraction >= 1.0) {
|
|
fraction = (float) 0.99;
|
|
}
|
|
|
|
mH->fraction = fraction;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
private:
|
|
PBHandlev2 *mH;
|
|
LPTYPEINFO mTypeInfo;
|
|
ULONG mRefs;
|
|
DWORD mCookie;
|
|
IConnectionPoint *mConnectionPoint;
|
|
IConnectionPointContainer *mConnectionPointContainer;
|
|
};
|
|
|
|
/* Erasing Thread */
|
|
DWORD WINAPI PortBurn_v2_EraseDisc(LPVOID lpParam)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) lpParam;
|
|
IDiscRecorder2 *pDiscRecorder = NULL;
|
|
IDiscFormat2Erase *pDiscErase = NULL;
|
|
EraseEvents *pEraseEvents = NULL;
|
|
bool mcndisabled = false;
|
|
|
|
h->hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
|
|
if (FAILED(h->hres)) {
|
|
h->threadres = pbErrEraseFailed;
|
|
h->fraction = 1.0;
|
|
return S_OK;
|
|
}
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscRecorder));
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscRecorder->InitializeDiscRecorder(h->diskid);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscRecorder->DisableMcn();
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
mcndisabled = true;
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscFormat2Erase),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscErase));
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscErase->put_ClientName(h->client);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscErase->put_Recorder(pDiscRecorder);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscErase->put_FullErase(h->fullerase);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
pEraseEvents = new EraseEvents(h);
|
|
if (pEraseEvents == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
pEraseEvents->Connect(pDiscErase);
|
|
|
|
h->cancel = false;
|
|
h->fraction = 0.0;
|
|
|
|
h->hres = pDiscErase->EraseMedia();
|
|
|
|
if (SUCCEEDED(h->hres)) {
|
|
IDiscRecorder2Ex *pDiscRecorderEx;
|
|
int cnt = 50;
|
|
bool blank = false;
|
|
|
|
h->hres = pDiscRecorder->QueryInterface(__uuidof(IDiscRecorder2Ex),
|
|
(void **) &pDiscRecorderEx);
|
|
if (SUCCEEDED(h->hres)) {
|
|
while (!blank && cnt--) {
|
|
BYTE *info;
|
|
ULONG_IMAPI2_DISC_INFORMATION size;
|
|
|
|
SleepEx(100, true);
|
|
|
|
h->hres = pDiscRecorderEx->GetDiscInformation(&info, &size);
|
|
if (FAILED(h->hres)) {
|
|
break;
|
|
}
|
|
|
|
blank = ((info[2] & 0x03) == 0);
|
|
|
|
CoTaskMemFree(info);
|
|
}
|
|
|
|
pDiscRecorderEx->Release();
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
if (FAILED(h->hres)) {
|
|
h->threadres = pbErrEraseFailed;
|
|
}
|
|
else {
|
|
h->threadres = pbSuccess;
|
|
}
|
|
|
|
if (pEraseEvents != NULL) {
|
|
pEraseEvents->Disconnect();
|
|
pEraseEvents->Release();
|
|
}
|
|
|
|
if (pDiscErase != NULL) {
|
|
pDiscErase->put_ClientName(NULL);
|
|
pDiscErase->put_Recorder(NULL);
|
|
pDiscErase->Release();
|
|
}
|
|
|
|
if (mcndisabled) {
|
|
pDiscRecorder->EnableMcn();
|
|
}
|
|
|
|
if (pDiscRecorder != NULL) {
|
|
pDiscRecorder->Release();
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
h->fraction = 1.0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
/// ==================================================================
|
|
|
|
static BSTR get_diskid(void *handle, int index)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
IDiscMaster2 *pDiscMaster = NULL;
|
|
BSTR bID = NULL;
|
|
LONG count;
|
|
|
|
if (h == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscMaster2),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscMaster));
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscMaster->get_Count(&count);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
if (index < 0 || index >= count) {
|
|
h->hres = E_INVALIDARG;
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscMaster->get_Item(index, &bID);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
|
|
if (pDiscMaster != NULL) {
|
|
pDiscMaster->Release();
|
|
}
|
|
|
|
return bID;
|
|
}
|
|
|
|
static IDiscRecorder2 *get_recorder(void *handle, int index)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
IDiscRecorder2 *pDiscRecorder = NULL;
|
|
BSTR bID = NULL;
|
|
|
|
if (h == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
bID = get_diskid(handle, index);
|
|
if (bID == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscRecorder));
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscRecorder->InitializeDiscRecorder(bID);
|
|
if (FAILED(h->hres)) {
|
|
pDiscRecorder->Release();
|
|
pDiscRecorder = NULL;
|
|
}
|
|
|
|
done:
|
|
|
|
if (bID != NULL) {
|
|
SysFreeString(bID);
|
|
}
|
|
|
|
return pDiscRecorder;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void *PortBurn_v2_Open()
|
|
{
|
|
PBHandlev2 *h;
|
|
WCHAR name[255];
|
|
|
|
#if defined(_DEBUG)
|
|
AllocConsole();
|
|
freopen("CONOUT$", "w", stdout);
|
|
#endif
|
|
|
|
h = (PBHandlev2 *) HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof(PBHandlev2));
|
|
if (h == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
h->test = pbTestDefault;
|
|
h->verify = pbVerifyDefault;
|
|
h->underrun = pbUnderrunDefault;
|
|
h->eject = pbEjectDefault;
|
|
h->gapless = pbGaplessDefault;
|
|
h->speed = pbSpeedDefault;
|
|
|
|
_stprintf_s(name, 255, L"portburn_client_%d", GetTickCount());
|
|
|
|
h->client = SysAllocString(name);
|
|
if (h->client == NULL) {
|
|
HeapFree(GetProcessHeap(), 0, h);
|
|
return NULL;
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
/* Cleanup */
|
|
int PortBurn_v2_CloseDevice(void *handle);
|
|
void PortBurn_v2_Close(void *handle)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
|
|
if (h == NULL) {
|
|
return;
|
|
}
|
|
|
|
PortBurn_v2_CloseDevice(h);
|
|
|
|
if (h->client != NULL) {
|
|
SysFreeString(h->client);
|
|
h->client = NULL;
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, h);
|
|
}
|
|
|
|
/* Return a human-readable error string for the last operating system
|
|
specific error (NOT human readable strings for the PortBurn error
|
|
codes). Caller should dispose of the returned string using free(). */
|
|
char *PortBurn_v2_LastError(void *handle)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
HRESULT hr;
|
|
LPTSTR windowsErrorString = NULL;
|
|
char *errorString = NULL;
|
|
int len;
|
|
|
|
if (h == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Have Windows allocate a buffer for us and format the error
|
|
message in windowsErrorString */
|
|
hr = h->hres;
|
|
if (HRESULT_FACILITY(hr) == FACILITY_WINDOWS) {
|
|
hr = HRESULT_CODE(hr);
|
|
}
|
|
|
|
len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPTSTR) &windowsErrorString, 0, NULL);
|
|
if (windowsErrorString == NULL) {
|
|
windowsErrorString = (LPTSTR) LocalAlloc(LPTR, 128 * sizeof(TCHAR));
|
|
if (windowsErrorString == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
len = _stprintf_s(windowsErrorString, 128, L"HRESULT = %08x", hr);
|
|
}
|
|
|
|
/* Convert the string */
|
|
errorString = (char *) malloc(len + 1);
|
|
if (errorString != NULL) {
|
|
errorString[0] = '\0';
|
|
WideCharToMultiByte(CP_ACP, 0, windowsErrorString, len, errorString, len, NULL, NULL);
|
|
errorString[len] = '\0';
|
|
}
|
|
|
|
LocalFree(windowsErrorString);
|
|
|
|
return errorString;
|
|
}
|
|
|
|
/* Get the number of devices capable of burning audio CDs.
|
|
If the result is N, then calls to GetDeviceName and OpenDevice
|
|
are guaranteed to be valid for indices from 0 up to N-1, until
|
|
the next time you call GetNumDevices. At that point, the list of
|
|
devices will be rescanned, and may be different. */
|
|
int PortBurn_v2_GetNumDevices(void *handle)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
IDiscMaster2 *pDiscMaster;
|
|
LONG count = 0;
|
|
|
|
if (h == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscMaster2),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscMaster));
|
|
if (SUCCEEDED(h->hres)) {
|
|
h->hres = pDiscMaster->get_Count(&count);
|
|
|
|
pDiscMaster->Release();
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/* Get the name of the device with a given index. Only valid
|
|
after a call to GetNumDevices. */
|
|
char *PortBurn_v2_GetDeviceName(void *handle, int index)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
IDiscRecorder2 *pDiscRecorder = NULL;
|
|
BSTR bVendor = NULL;
|
|
BSTR bProduct = NULL;
|
|
BSTR bRevision = NULL;
|
|
char *name = NULL;
|
|
TCHAR *wname;
|
|
int len;
|
|
|
|
if (h == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
pDiscRecorder = get_recorder(h, index);
|
|
if (pDiscRecorder == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscRecorder->get_VendorId(&bVendor);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscRecorder->get_ProductId(&bProduct);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
h->hres = pDiscRecorder->get_ProductRevision(&bRevision);
|
|
if (FAILED(h->hres)) {
|
|
goto done;
|
|
}
|
|
|
|
len = SysStringLen(bVendor) + 1 +
|
|
SysStringLen(bProduct) + 1 +
|
|
SysStringLen(bRevision);
|
|
|
|
name = (char *) malloc(len + 1);
|
|
if (name == NULL) {
|
|
h->hres = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
wname = (LPWSTR) alloca((len + 1) * sizeof(wchar_t));
|
|
_stprintf_s(wname,
|
|
(len + 1),
|
|
_T("%s %s %s"),
|
|
(LPCWSTR) bVendor,
|
|
(LPCWSTR) bProduct,
|
|
(LPCWSTR) bRevision);
|
|
|
|
name[0] = '\0';
|
|
WideCharToMultiByte(CP_ACP, 0, wname, len, name, len, NULL, NULL);
|
|
name[len] = '\0';
|
|
|
|
done:
|
|
|
|
if (bRevision != NULL) {
|
|
SysFreeString(bRevision);
|
|
}
|
|
|
|
if (bProduct != NULL) {
|
|
SysFreeString(bProduct);
|
|
}
|
|
|
|
if (bVendor != NULL) {
|
|
SysFreeString(bVendor);
|
|
}
|
|
|
|
if (pDiscRecorder != NULL) {
|
|
pDiscRecorder->Release();
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
/* Open a particular device by index number. Returns 0 on success;
|
|
any nonzero value indicates an error, for example if the device is
|
|
already open by some other program. */
|
|
int PortBurn_v2_OpenDevice(void *handle, int index)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
if (h->diskid != NULL) {
|
|
return pbErrDeviceAlreadyOpen;
|
|
}
|
|
|
|
h->diskid = get_diskid(handle, index);
|
|
if (h->diskid == NULL) {
|
|
return pbErrCannotReserveDevice;
|
|
}
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
/* Close a device */
|
|
int PortBurn_v2_CloseDevice(void *handle)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
if (h->hThread != NULL) {
|
|
h->cancel = true;
|
|
WaitForSingleObject(h->hThread, INFINITE);
|
|
CloseHandle(h->hThread);
|
|
h->hThread = NULL;
|
|
}
|
|
|
|
if (h->pStream != NULL) {
|
|
h->pStream->Release();
|
|
h->pStream = NULL;
|
|
}
|
|
|
|
if (h->pMarshall != NULL) {
|
|
IStorage *pStorage;
|
|
h->hres = CoGetInterfaceAndReleaseStream(h->pMarshall,
|
|
__uuidof(IStorage),
|
|
(void **) &pStorage);
|
|
h->pMarshall = NULL;
|
|
}
|
|
|
|
if (h->pStorage != NULL) {
|
|
h->pStorage->Release();
|
|
h->pStorage = NULL;
|
|
}
|
|
|
|
if (h->diskid != NULL) {
|
|
SysFreeString(h->diskid);
|
|
h->diskid = NULL;
|
|
}
|
|
|
|
h->burning = false;
|
|
h->erasing = false;
|
|
h->cancel = false;
|
|
h->fraction = 0.0;
|
|
|
|
h->test = pbTestDefault;
|
|
h->verify = pbVerifyDefault;
|
|
h->underrun = pbUnderrunDefault;
|
|
h->eject = pbEjectDefault;
|
|
h->gapless = pbGaplessDefault;
|
|
h->speed = pbSpeedDefault;
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
/* Eject the media in the currently opened device */
|
|
int PortBurn_v2_EjectDevice(void *handle)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
IDiscRecorder2 *pDiscRecorder = NULL;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
if (h->diskid == NULL) {
|
|
return pbErrDeviceNotOpen;
|
|
}
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscRecorder));
|
|
if (FAILED(h->hres)) {
|
|
return pbErrCannotEject;
|
|
}
|
|
|
|
h->hres = pDiscRecorder->InitializeDiscRecorder(h->diskid);
|
|
if (FAILED(h->hres)) {
|
|
pDiscRecorder->Release();
|
|
return pbErrCannotEject;
|
|
}
|
|
|
|
h->hres = pDiscRecorder->EjectMedia();
|
|
|
|
pDiscRecorder->Release();
|
|
|
|
if (FAILED(h->hres)) {
|
|
return pbErrCannotEject;
|
|
}
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
/* This indicates you're ready to start staging audio data for the
|
|
currently opened device. At this point you are committing to
|
|
exclusive access to the CD burner, and this is the function that
|
|
will fail if another program is using the device, or if there is
|
|
no writable CD media in the device at this point. */
|
|
int PortBurn_v2_StartStaging(void *handle, const char *tmpdir)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
if (h->diskid == NULL) {
|
|
return pbErrDeviceNotOpen;
|
|
}
|
|
|
|
if (h->pStorage != NULL) {
|
|
return pbErrAlreadyStagingOrBurning;
|
|
}
|
|
|
|
h->curtrack = -1;
|
|
|
|
h->hres = StgCreateStorageEx(h->client, // NULL,
|
|
STGM_CREATE |
|
|
STGM_SHARE_EXCLUSIVE |
|
|
STGM_READWRITE |
|
|
STGM_DELETEONRELEASE,
|
|
STGFMT_STORAGE,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
IID_IStorage,
|
|
(void **) &h->pStorage);
|
|
if (FAILED(h->hres)) {
|
|
return pbErrCannotCreateStagingDirectory;
|
|
}
|
|
|
|
h->hres = CoMarshalInterThreadInterfaceInStream(__uuidof(IStorage),
|
|
h->pStorage,
|
|
&h->pMarshall);
|
|
if (FAILED(h->hres)) {
|
|
h->pStorage->Release();
|
|
h->pStorage = NULL;
|
|
return pbErrCannotCreateStagingDirectory;
|
|
}
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
/* Start a new audio track. Pass the name of the track, and the
|
|
length in CD Audio frames (each frame is 1/75.0 of a second, exactly). */
|
|
int PortBurn_v2_StartTrack(void *handle, const char *name)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
WCHAR wname[255];
|
|
int curtrack;
|
|
printf("start track\n");
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
if (h->pStorage == NULL) {
|
|
return pbErrMustCallStartStaging;
|
|
}
|
|
|
|
curtrack = h->curtrack + 1;
|
|
if (curtrack == 99) {
|
|
return pbErrCannotStageTrack;
|
|
}
|
|
|
|
_stprintf_s(wname, 255, L"track #%d", curtrack);
|
|
|
|
h->hres = h->pStorage->CreateStream(wname,
|
|
STGM_CREATE |
|
|
STGM_SHARE_EXCLUSIVE |
|
|
STGM_READWRITE,
|
|
0,
|
|
0,
|
|
&h->pStream);
|
|
if (FAILED(h->hres)) {
|
|
return pbErrCannotCreateStagingFile;
|
|
}
|
|
|
|
h->curtrack = curtrack;
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
/* Add one frame of audio to the current track. The buffer must be exactly
|
|
1176 elements long, containing interleaved left and right audio samples.
|
|
The values should be signed 16-bit numbers in the native endianness of
|
|
this computer. */
|
|
int PortBurn_v2_AddFrame(void *handle, short *buffer)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
int oneBlockByteCount = 1176 * 2;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
if (h->pStream == NULL) {
|
|
return pbErrMustCallStartTrack;
|
|
}
|
|
|
|
h->hres = h->pStream->Write(buffer, oneBlockByteCount, NULL);
|
|
if (FAILED(h->hres)) {
|
|
return pbErrCannotWriteToStagingFile;
|
|
}
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
/* Finish the current audio track. */
|
|
int PortBurn_v2_EndTrack(void *handle)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
LARGE_INTEGER zero = {0};
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
if (h->pStream == NULL) {
|
|
return pbErrMustCallStartTrack;
|
|
}
|
|
|
|
h->pStream->Release();
|
|
h->pStream = NULL;
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
/* Begin burning the disc. */
|
|
int PortBurn_v2_StartBurning(void *handle)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
DWORD dwID;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
printf("starting burn\n");
|
|
h->hres = S_OK;
|
|
|
|
if (h->curtrack < 0) {
|
|
return pbErrMustCallStartTrack;
|
|
}
|
|
|
|
if (h->hThread != NULL) {
|
|
return pbErrAlreadyStagingOrBurning;
|
|
}
|
|
|
|
h->burning = true;
|
|
|
|
LPTHREAD_START_ROUTINE start;
|
|
#if 1
|
|
IDiscFormat2RawCD *pDiscFormat;
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscFormat2RawCD),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscFormat));
|
|
if (SUCCEEDED(h->hres)) {
|
|
pDiscFormat->Release();
|
|
start = PortBurn_v2_RecordDiscDAO;
|
|
}
|
|
else {
|
|
start = PortBurn_v2_RecordDiscTAO;
|
|
}
|
|
#else
|
|
start = PortBurn_v2_RecordDiscTAO;
|
|
#endif
|
|
|
|
h->hThread = CreateThread(NULL,
|
|
0,
|
|
start,
|
|
h,
|
|
0,
|
|
&dwID);
|
|
if (h->hThread == NULL) {
|
|
return pbErrCannotStartBurning;
|
|
}
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
/* Cancel if burning was in progress. It might take a while for
|
|
this to take effect; wait until GetStatus says 1.0 to close
|
|
the device. */
|
|
int PortBurn_v2_CancelBurning(void *handle)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
float frac = 0.0;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
if (h->burning == false) {
|
|
return pbErrNotCurrentlyBurning;
|
|
}
|
|
|
|
h->cancel = true;
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
/* During burning, returns the fraction complete in the given
|
|
float pointer, from 0.0 to 1.0. If this function returns
|
|
nonzero, the disc burning has failed and should be aborted.
|
|
If *out_fraction_complete is equal to 1.0, the burning is done;
|
|
you can call PortBurn_CloseDevice.
|
|
*/
|
|
int PortBurn_v2_GetStatus(void *handle, float *out_fraction_complete)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
if (h->hThread == NULL) {
|
|
return pbErrDeviceNotOpen;
|
|
}
|
|
|
|
if (h->burning == false) {
|
|
return pbErrNotCurrentlyBurning;
|
|
}
|
|
|
|
MSG msg;
|
|
if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
WaitForSingleObject(h->hThread, 100);
|
|
|
|
*out_fraction_complete = h->fraction;
|
|
|
|
if (h->fraction == 1.0) {
|
|
h->burning = false;
|
|
WaitForSingleObject(h->hThread, INFINITE);
|
|
CloseHandle(h->hThread);
|
|
h->hThread = NULL;
|
|
}
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
/* Get option value. */
|
|
int PortBurn_v2_GetOption(void *handle, int option, int *value)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
int ret = pbSuccess;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
switch (option)
|
|
{
|
|
case pbOptTest:
|
|
{
|
|
*value = h->test;
|
|
}
|
|
break;
|
|
|
|
case pbOptVerify:
|
|
{
|
|
*value = h->verify;
|
|
}
|
|
break;
|
|
|
|
case pbOptUnderrun:
|
|
{
|
|
*value = h->underrun;
|
|
}
|
|
break;
|
|
|
|
case pbOptEject:
|
|
{
|
|
*value = h->eject;
|
|
}
|
|
break;
|
|
|
|
case pbOptGapless:
|
|
{
|
|
*value = h->gapless;
|
|
}
|
|
break;
|
|
|
|
case pbOptSpeed:
|
|
{
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
ret = pbErrInvalidOption;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int PortBurn_v2_SetOption(void *handle, int option, int value)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
int ret = pbSuccess;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
switch (option)
|
|
{
|
|
case pbOptTest:
|
|
{
|
|
h->test = value != 0;
|
|
}
|
|
break;
|
|
|
|
case pbOptVerify:
|
|
{
|
|
h->verify = value != 0;
|
|
}
|
|
break;
|
|
|
|
case pbOptUnderrun:
|
|
{
|
|
h->underrun = value != 0;
|
|
}
|
|
break;
|
|
|
|
case pbOptEject:
|
|
{
|
|
h->eject = value != 0;
|
|
}
|
|
break;
|
|
|
|
case pbOptGapless:
|
|
{
|
|
h->gapless = value != 0;
|
|
}
|
|
break;
|
|
|
|
case pbOptSpeed:
|
|
{
|
|
h->speed = value;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
ret = pbErrInvalidOption;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Erase the media in the currently opened device */
|
|
int PortBurn_v2_StartErasing(void *handle, int type)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
DWORD dwID;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
if (h->hThread != NULL) {
|
|
return pbErrCannotStartErasing;
|
|
}
|
|
|
|
h->fullerase = (type == pbEraseQuick ? VARIANT_FALSE : VARIANT_TRUE);
|
|
|
|
h->erasing = true;
|
|
|
|
h->hThread = CreateThread(NULL,
|
|
0,
|
|
PortBurn_v2_EraseDisc,
|
|
h,
|
|
0,
|
|
&dwID);
|
|
if (h->hThread == NULL) {
|
|
return pbErrCannotStartErasing;
|
|
}
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
int PortBurn_v2_GetEraseStatus(void *handle, float *out_fraction_complete)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
|
|
if (h->diskid == NULL) {
|
|
return pbErrDeviceNotOpen;
|
|
}
|
|
|
|
if (h->erasing == false) {
|
|
return pbErrNotCurrentlyErasing;
|
|
}
|
|
|
|
WaitForSingleObject(h->hThread, 100);
|
|
|
|
*out_fraction_complete = h->fraction;
|
|
|
|
if (h->fraction == 1.0) {
|
|
h->erasing = false;
|
|
WaitForSingleObject(h->hThread, INFINITE);
|
|
CloseHandle(h->hThread);
|
|
h->hThread = NULL;
|
|
}
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
/* */
|
|
int PortBurn_v2_GetMediaState(void *handle, int *state)
|
|
{
|
|
PBHandlev2 *h = (PBHandlev2 *) handle;
|
|
IDiscRecorder2 *pDiscRecorder;
|
|
IDiscFormat2Data *pDiscFormat;
|
|
IMAPI_FORMAT2_DATA_MEDIA_STATE status;
|
|
int mstate;
|
|
|
|
if (h == NULL) {
|
|
return pbErrNoHandle;
|
|
}
|
|
|
|
h->hres = S_OK;
|
|
mstate = 0;
|
|
|
|
if (h->diskid == NULL) {
|
|
return pbErrDeviceNotOpen;
|
|
}
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscRecorder));
|
|
if (FAILED(h->hres)) {
|
|
return pbErrCannotAccessDevice;
|
|
}
|
|
|
|
h->hres = pDiscRecorder->InitializeDiscRecorder(h->diskid);
|
|
if (FAILED(h->hres)) {
|
|
pDiscRecorder->Release();
|
|
return pbErrCannotAccessDevice;
|
|
}
|
|
|
|
h->hres = CoCreateInstance(__uuidof(MsftDiscFormat2Data),
|
|
NULL,
|
|
CLSCTX_ALL,
|
|
IID_PPV_ARGS(&pDiscFormat));
|
|
if (FAILED(h->hres)) {
|
|
pDiscRecorder->Release();
|
|
return pbErrCannotAccessDevice;
|
|
}
|
|
|
|
h->hres = pDiscFormat->put_Recorder(pDiscRecorder);
|
|
if (FAILED(h->hres)) {
|
|
pDiscFormat->Release();
|
|
pDiscRecorder->Release();
|
|
return pbErrCannotAccessDevice;
|
|
}
|
|
|
|
h->hres = pDiscFormat->get_CurrentMediaStatus(&status);
|
|
|
|
pDiscFormat->put_Recorder(NULL);
|
|
pDiscFormat->Release();
|
|
pDiscRecorder->Release();
|
|
|
|
if (h->hres == E_IMAPI_RECORDER_MEDIA_NO_MEDIA) {
|
|
*state = pbMediaNone;
|
|
return pbSuccess;
|
|
}
|
|
|
|
if (FAILED(h->hres)) {
|
|
return pbErrCannotAccessDevice;
|
|
}
|
|
|
|
if (status & IMAPI_FORMAT2_DATA_MEDIA_STATE_BLANK) {
|
|
mstate |= pbMediaBlank;
|
|
}
|
|
|
|
if (!(status & IMAPI_FORMAT2_DATA_MEDIA_STATE_WRITE_PROTECTED)) {
|
|
mstate |= pbMediaErasable;
|
|
}
|
|
|
|
if (status & IMAPI_FORMAT2_DATA_MEDIA_STATE_APPENDABLE) {
|
|
mstate |= pbMediaAppendable;
|
|
}
|
|
|
|
if (status & IMAPI_FORMAT2_DATA_MEDIA_STATE_OVERWRITE_ONLY) {
|
|
mstate |= pbMediaOverwritable;
|
|
}
|
|
|
|
*state = mstate;
|
|
|
|
return pbSuccess;
|
|
}
|
|
|
|
int PortBurn_v2_GetSupportedSpeeds(void *handle, int *cnt, int *speeds[])
|
|
{
|
|
*cnt = 0;
|
|
*speeds = NULL;
|
|
return pbErrNoHandle;
|
|
}
|