mirror of
				https://github.com/cookiengineer/audacity
				synced 2025-10-31 14:13:50 +01: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;
 | |
| }
 |