Поделиться через


Передача изображения или музыкального файла на устройство

Одной из наиболее распространенных операций, выполняемых приложением, является передача содержимого на подключенное устройство.

Передача содержимого выполняется с помощью интерфейсов, описанных в следующей таблице.

Интерфейс Описание
Интерфейс IPortableDeviceContent Предоставляет доступ к методам, зависящим от содержимого.
Интерфейс IPortableDeviceDataStream Используется при записи содержимого на устройство.
Интерфейс IPortableDeviceValues Используется для получения свойств, описывающих содержимое.
Интерфейс IStream Используется для упрощения чтения содержимого и записи на устройство.

 

Функция TransferContentToDevice в модуле ContentTransfer.cpp примера приложения демонстрирует, как приложение может передавать содержимое с компьютера на подключенное устройство. В этом конкретном примере переданное содержимое может быть файлом, содержащим изображение, музыку или контактные данные.

Первая задача, выполняемая TransferContentToDevice функцией, — предложить пользователю ввести идентификатор объекта, который идентифицирует передаваемый объект.

HRESULT                             hr = S_OK;
WCHAR                               szSelection[81]        = {0};
WCHAR                               szFilePath[MAX_PATH]   = {0};
DWORD                               cbOptimalTransferSize   = 0;
CComPtr<IStream>                    pFileStream;
CComPtr<IPortableDeviceDataStream>  pFinalObjectDataStream;
CComPtr<IPortableDeviceValues>      pFinalObjectProperties;
CComPtr<IPortableDeviceContent>     pContent;
CComPtr<IStream>                    pTempStream;  // Temporary IStream which we use to QI for IPortableDeviceDataStream

// Prompt user to enter an object identifier for the parent object on the device to transfer.
printf("Enter the identifer of the parent object which the file will be transferred under.\n>");
hr = StringCbGetsW(szSelection,sizeof(szSelection));
if (FAILED(hr))
{
    printf("An invalid object identifier was specified, aborting content transfer\n");
}

Второй задачей, TransferContentToDevice выполняемой функцией, является создание объекта IPortableDeviceContent путем вызова метода IPortableDevice::Content .

if (SUCCEEDED(hr))
{
    hr = pDevice->Content(&pContent);
    if (FAILED(hr))
    {
        printf("! Failed to get IPortableDeviceContent from IPortableDevice, hr = 0x%lx\n",hr);
    }
}

Следующая задача, выполняемая TransferContentToDevice функцией, — создание диалогового окна FileOpen , с помощью которого пользователь может указать расположение и имя передаваемого файла.

if (SUCCEEDED(hr))
{
    OPENFILENAME OpenFileNameInfo   = {0};

    OpenFileNameInfo.lStructSize    = sizeof(OPENFILENAME);
    OpenFileNameInfo.hwndOwner      = NULL;
    OpenFileNameInfo.lpstrFile      = szFilePath;
    OpenFileNameInfo.nMaxFile       = ARRAYSIZE(szFilePath);
    OpenFileNameInfo.lpstrFilter    = pszFileTypeFilter;
    OpenFileNameInfo.nFilterIndex   = 1;
    OpenFileNameInfo.Flags          = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
    OpenFileNameInfo.lpstrDefExt    = pszDefaultFileExtension;

    if (GetOpenFileName(&OpenFileNameInfo) == FALSE)
    {
        printf("The transfer operation was canceled.\n");
        hr = E_ABORT;
    }
}

Функция TransferContentToDevice передает строку фильтра (wszFileTypeFilter) методу GetOpenFileName, который определяет тип файлов, которые пользователь может выбрать. См. функцию DoMenu в модуле WpdApiSample.cpp, например три разных фильтра, разрешенных примером.

После того как пользователь идентифицирует определенный файл для передачи на устройство, TransferContentToDevice функция открывает этот файл в качестве объекта IStream и извлекает свойства, необходимые для завершения передачи.

if (SUCCEEDED(hr))
{
    // Open the selected file as an IStream.  This will simplify reading the
    // data and writing to the device.
    hr = SHCreateStreamOnFile(szFilePath, STGM_READ, &pFileStream);
    if (SUCCEEDED(hr))
    {
        // Get the required properties needed to properly describe the data being
        // transferred to the device.
        hr = GetRequiredPropertiesForContentType(guidContentType,           // Content type of the data
                                                 szSelection,              // Parent to transfer the data under
                                                 szFilePath,               // Full file path to the data file
                                                 pFileStream,               // Open IStream that contains the data
                                                 &pFinalObjectProperties);  // Returned properties describing the data
        if (FAILED(hr))
        {
            printf("! Failed to get required properties needed to transfer a file to the device, hr = 0x%lx\n", hr);
        }
    }

    if (FAILED(hr))
    {
        printf("! Failed to open file named (%ws) to transfer to device, hr = 0x%lx\n",szFilePath, hr);
    }
}

Необходимые свойства извлекаются путем вызова вспомогательнойGetRequiredPropertiesForContentType функции, которая работает с объектом IStream. Вспомогаемая GetRequiredPropertiesForContentType функция создает объект IPortableDeviceValues , извлекает свойства из следующего списка и добавляет их в этот объект.

  • Идентификатор родительского объекта
  • Размер потока в байтах
  • Имя файла содержимого
  • Имя содержимого (имя файла без расширения)
  • Тип контента (изображение, звук или контакт)
  • Формат содержимого (JFIF, WMA или vCard2)

В примере приложения используются извлеченные свойства для создания нового содержимого на устройстве. Это выполняется на трех этапах:

  1. Приложение вызывает метод IPortableDeviceContent::CreateObjectWithPropertiesAndData для создания объекта IStream на устройстве.
  2. Приложение использует этот объект для получения объекта IPortableDeviceDataStream из драйвера WPD.
  3. Приложение использует новый объект IPortableDeviceDataStream для записи содержимого на устройство (с помощью вспомогательной функции StreamCopy). Вспомогающая функция записывает данные из исходного файла в поток, возвращенный IPortableDeviceContent::CreateObjectWithPropertiesAndData.
  4. Приложение завершает операцию путем вызова метода Commit в целевом потоке.
// 4) Transfer for the content to the device
if (SUCCEEDED(hr))
{
    hr = pContent->CreateObjectWithPropertiesAndData(pFinalObjectProperties,    // Properties describing the object data
                                                     &pTempStream,              // Returned object data stream (to transfer the data to)
                                                     &cbOptimalTransferSize,    // Returned optimal buffer size to use during transfer
                                                     NULL);

    // Once we have a the IStream returned from CreateObjectWithPropertiesAndData,
    // QI for IPortableDeviceDataStream so we can use the additional methods
    // to get more information about the object (i.e. The newly created object
    // identifier on the device)
    if (SUCCEEDED(hr))
    {
        hr = pTempStream->QueryInterface(IID_PPV_ARGS(&pFinalObjectDataStream));
        if (FAILED(hr))
        {
            printf("! Failed to QueryInterface for IPortableDeviceDataStream, hr = 0x%lx\n",hr);
        }
    }

    // Since we have IStream-compatible interfaces, call our helper function
    // that copies the contents of a source stream into a destination stream.
    if (SUCCEEDED(hr))
    {
        DWORD cbTotalBytesWritten = 0;

        hr = StreamCopy(pFinalObjectDataStream, // Destination (The Object to transfer to)
                        pFileStream,            // Source (The File data to transfer from)
                        cbOptimalTransferSize,  // The driver specified optimal transfer buffer size
                        &cbTotalBytesWritten);  // The total number of bytes transferred from file to the device
        if (FAILED(hr))
        {
            printf("! Failed to transfer object to device, hr = 0x%lx\n",hr);
        }
    }
    else
    {
        printf("! Failed to get IStream (representing destination object data on the device) from IPortableDeviceContent, hr = 0x%lx\n",hr);
    }

    // After transferring content to the device, the client is responsible for letting the
    // driver know that the transfer is complete by calling the Commit() method
    // on the IPortableDeviceDataStream interface.
    if (SUCCEEDED(hr))
    {
        hr = pFinalObjectDataStream->Commit(0);
        if (FAILED(hr))
        {
            printf("! Failed to commit object to device, hr = 0x%lx\n",hr);
        }
    }

    // Some clients may want to know the object identifier of the newly created
    // object.  This is done by calling GetObjectID() method on the
    // IPortableDeviceDataStream interface.
    if (SUCCEEDED(hr))
    {
        PWSTR pszNewlyCreatedObject = NULL;
        hr = pFinalObjectDataStream->GetObjectID(&pszNewlyCreatedObject);
        if (SUCCEEDED(hr))
        {
            printf("The file '%ws' was transferred to the device.\nThe newly created object's ID is '%ws'\n",szFilePath ,pszNewlyCreatedObject);
        }

        if (FAILED(hr))
        {
            printf("! Failed to get the newly transferred object's identifier from the device, hr = 0x%lx\n",hr);
        }

        // Free the object identifier string returned from the GetObjectID() method.
        CoTaskMemFree(pszNewlyCreatedObject);
        pszNewlyCreatedObject = NULL;
    }
}

Интерфейс IPortableDevice

Интерфейс IPortableDeviceContent

Интерфейс IPortableDeviceDataStream

Интерфейс IPortableDeviceValues

Руководство по программированию