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


Выделение памяти для данных

Для правильной передачи данных служба WIA использует информацию, предоставленную в структуре MINIDRV_TRANSFER_CONTEXT .

Ниже перечислены элементы этой структуры, относящиеся к мини-накопителю WIA:

bClassDrvAllocBuf — логическое значение выделения службы WIA.

pTransferBuffer — указатель на память, выделенную для передаваемых данных.

lBufferSize — размер памяти, на которую указывает элемент pTransferBuffer .

Если элемент bClassDrvAllocBuf структуры MINIDRV_TRANSFER_CONTEXT имеет значение TRUE, то служба WIA выделила память для мини-драйвера. Если для элемента bClassDrvAllocBuf задано значение FALSE, служба WIA не выделила память для мини-драйвера.

Мини-диск должен выделять память с помощью функции CoTaskMemAlloc (описанной в документации по Microsoft Windows SDK). Затем в мини-накопителе должен храниться указатель на расположение памяти в pTransferBuffer и размер памяти в lBufferSize (в байтах).

Член bClassDrvAllocBuff имеет значение FALSE , только если свойство WIA_IPA_TYMED имеет значение TYMED_FILE или TYMED_MULTIPAGE_FILE, а свойство WIA_IPA_ITEM_SIZE равно нулю.

Мини-диск должен быть осторожным, чтобы не переполнять буфер, на который указывает элемент pTransferBuffer . Этого можно избежать, записав данные в объемах, меньше или равных значению, хранящейся в элементе lBufferSize .

Повышение производительности передачи данных с помощью минимального размера буфера

Мини-накопитель WIA может управлять объемом памяти, используемым во время передачи данных, задавая свойства WIA_IPA_ITEM_SIZE и WIA_IPA_BUFFER_SIZE .

Приложение WIA использует свойство WIA_IPA_BUFFER_SIZE для определения минимального размера буфера передачи, запрашиваемого во время передачи памяти. Чем больше это значение, тем больше будет запрошенный размер полосы. Если приложение WIA запрашивает буфер меньше значения в свойстве WIA_IPA_BUFFER_SIZE, служба WIA игнорирует запрошенный размер и запрашивает у мини-драйвера WIA буфер, размер WIA_IPA_BUFFER_SIZE байтов. Служба WIA всегда запрашивает у мини-драйвера WIA буферы размером не менее WIA_IPA_BUFFER_SIZE байтов.

Значение, содержащееся в свойстве WIA_IPA_BUFFER_SIZE, — это минимальный объем данных, которые приложение может запрашивать в любой момент времени. Чем больше размер буфера, тем больше будут запросы к устройству. Слишком маленькие размеры буферов могут снизить производительность передачи данных.

Рекомендуется задать для свойства WIA_IPA_BUFFER_SIZE разумный размер, чтобы устройство пустит передачу данных с эффективной скоростью. Для этого сбалансируйте количество запросов (размер буфера не слишком мал) и количество длительных запросов (слишком большой буфер) для устройства, чтобы обеспечить оптимальную производительность.

Если мини-накопитель WIA может передавать данные, для свойства WIA_IPA_ITEM_SIZE следует задать нулевое значение. Если тип передачи TYMED_FILE или TYMED_MULTIPAGE_FILE, мини-диск отвечает за выделение памяти для буфера данных, передаваемого в функцию службы WIA, которая записывает данные в файл. Это обеспечивает согласованность в реализации метода IWiaMiniDrv::d rvAcquireItemData .

Метод IWiaMiniDrv::d AcrvAcquireItemData вызывается службой WIA, когда она намерена передать данные с устройства в приложение. Драйвер WIA должен определить, какой тип передачи (через службу WIA) пытается приложение, считывая элемент tymedMINIDRV_TRANSFER_CONTEXT:

Элемент tymed , заданный приложением, может иметь одно из следующих четырех значений:

TYMED_FILE
Передача данных в файл.

TYMED_MULTIPAGE_FILE
Передача данных в многостраничных файлах.

TYMED_CALLBACK
Передача данных в память.

TYMED_MULTIPAGE_CALLBACK
Передача нескольких страниц данных в память.

Различные параметры TYMED XXX_CALLBACK и XXX_FILE изменить использование интерфейса обратного вызова приложения.

TYMED_CALLBACK и TYMED_MULTIPAGE_CALLBACK

Для передачи памяти выполните обратный вызов IWiaMiniDrvCallBack::MiniDrvCallback :

(pmdtc-pIWiaMiniDrvCallBack-MiniDrvCallback>> в следующем примере исходного кода)

Выполните обратный вызов, используя следующие значения:

IT_MSG_DATA
Драйвер передает данные.

IT_STATUS_TRANSFER_TO_CLIENT
Сообщение о передаче данных.

lPercentComplete
Процент завершения передачи.

pmdtc-cbOffset>
Измените его до текущего расположения, в которое приложение должно записать следующий фрагмент данных.

lBytesReceived
Количество байтов в блоке данных, отправляемом в приложение.

pmdtc
Указатель на структуру MINIDRV_TRANSFER_CONTEXT , содержащую значения для передачи данных.

TYMED_FILE и TYMED_MULTIPAGE_FILE

Для передачи файлов выполните обратный вызов IWiaMiniDrvCallBack::MiniDrvCallback ::

(pmdtc-pIWiaMiniDrvCallBack-MiniDrvCallback>> в следующем примере исходного кода)

Выполните обратный вызов, используя следующие значения.

IT_MSG_STATUS
Драйвер отправляет только состояние (без данных).

IT_STATUS_TRANSFER_TO_CLIENT
Сообщение о передаче данных.

lPercentComplete
Процент завершения передачи.

Если элемент ItemSize структуры MINIDRV_TRANSFER_CONTEXT имеет нулевое значение, это указывает приложению, что драйвер WIA не знает результирующий размер изображения и затем выделит собственные буферы данных. Драйвер WIA считывает свойство WIA_IPA_BUFFER_SIZE и выделяет память для одного диапазона данных. Драйвер WIA может выделить любой объем памяти, который ему нужен, но рекомендуется, чтобы выделение было небольшим.

Чтобы узнать, выделена ли в службе WIA память для драйвера, проверка флаг pmdtc-bClassDrvAllocBuf>. Если задано значение TRUE, то служба WIA выделила память для драйвера. Чтобы узнать, сколько памяти было выделено, проверка значение в pmdtc-lBufferSize>.

Чтобы выделить собственную память, используйте CoTaskMemAlloc (описано в документации по Microsoft Windows SDK) и указатель, расположенный в pmdtc-pTransferBuffer>. (Помните, что драйвер выделил эту память, поэтому драйвер также должен освободить ее.) Задайте для параметра pmdtc-lBufferSize> выделенный размер. Как уже говорилось ранее, в этом примере драйвера WIA выделяется буфер, размер которого в байтах равен значению в WIA_IPA_BUFFER_SIZE. Затем драйвер использует такую память.

В следующем примере показана реализация метода IWiaMiniDrv::d rvAcquireItemData . В этом примере можно обрабатывать оба варианта выделения памяти.

HRESULT _stdcall CWIADevice::drvAcquireItemData(
  BYTE                      *pWiasContext,
  LONG                      lFlags,
  PMINIDRV_TRANSFER_CONTEXT pmdtc,
  LONG                      *plDevErrVal)
{
  //
  // If the caller did not pass in the correct parameters,
  // then fail the call with E_INVALIDARG.
  //

  if (!pWiasContext) {
    return E_INVALIDARG;
  }

  if (!pmdtc) {
    return E_INVALIDARG;
  }

  if (!plDevErrVal) {
    return E_INVALIDARG;
  }

  *plDevErrVal = 0;

  HRESULT hr = E_FAIL;
  LONG lBytesTransferredToApplication = 0;
  LONG lClassDrvAllocSize = 0;
  //
  // (1) Memory allocation
  //

  if (pmdtc->bClassDrvAllocBuf) {

    //
    // WIA allocated the buffer for data transfers
    //

    lClassDrvAllocSize = pmdtc->lBufferSize;
    hr = S_OK;
  } else {

    //
    // Driver allocated the buffer for data transfers
    //

    hr = wiasReadPropLong(pWiasContext, WIA_IPA_BUFFER_SIZE, &lClassDrvAllocSize,NULL,TRUE);
    if (FAILED(hr)) {

      //
      // no memory was allocated, here so we can return early
      //

      return hr;
    }

    //
    // allocate memory of WIA_IPA_BUFFER_SIZE (own min buffer size)
    //

    pmdtc->pTransferBuffer = (PBYTE) CoTaskMemAlloc(lClassDrvAllocSize);
    if (!pmdtc->pTransferBuffer) {

      //
      // no memory was allocated, here so we can return early
      //

      return E_OUTOFMEMORY;
    }

    //
    // set the lBufferSize member
    //

    pmdtc->lBufferSize = lClassDrvAllocSize;
  }

  //
  // (2) Gather all information about data transfer settings and
  //     calculate the total data amount to transfer
  //

  if (hr == S_OK) {
    //
    // WIA service will populate the MINIDRV_TRANSFER_CONTEXT by reading the WIA properties.
    //
    // The following values will be written as a result of the 
    // wiasGetImageInformation() call
    //
    // pmdtc->lWidthInPixels
    // pmdtc->lLines
    // pmdtc->lDepth
    // pmdtc->lXRes
    // pmdtc->lYRes
    // pmdtc->lCompression
    // pmdtc->lItemSize
    // pmdtc->guidFormatID
    // pmdtc->tymed
    //
    // if the FORMAT is set to BMP or MEMORYBMP, the
    // following values will also be set automatically
    //
    // pmdtc->cbWidthInBytes
    // pmdtc->lImageSize
    // pmdtc->lHeaderSize
    // pmdtc->lItemSize (will be updated using the known image format information)
    //

    hr = wiasGetImageInformation(pWiasContext,0,pmdtc);
    if (hr == S_OK) {

      //
      // (3) Send the image data to the application
      //

      LONG lDepth = 0;
      hr = wiasReadPropLong(pWiasContext, WIA_IPA_DEPTH, &lDepth,NULL,TRUE);
      if (hr == S_OK) {

        LONG lPixelsPerLine = 0;
        hr = wiasReadPropLong(pWiasContext, WIA_IPA_PIXELS_PER_LINE, &lPixelsPerLine,NULL,TRUE);
        if (hr == S_OK) {

            LONG lBytesPerLineRaw     = ((lPixelsPerLine * lDepth) + 7) / 8;
            LONG lBytesPerLineAligned = (lPixelsPerLine * lDepth) + 31;
            lBytesPerLineAligned      = (lBytesPerLineAligned / 8) & 0xfffffffc;
            LONG lTotalImageBytes     = pmdtc->lImageSize + pmdtc->lHeaderSize;
            LONG lBytesReceived       = pmdtc->lHeaderSize;
            lBytesTransferredToApplication = 0;
            pmdtc->cbOffset = 0;

            while ((lBytesReceived)) {

              LONG lPercentComplete = (LONG)(((float)lBytesTransferredToApplication/(float)lTotalImageBytes) * 100.0f);
              switch (pmdtc->tymed) {
              case TYMED_MULTIPAGE_CALLBACK:
              case TYMED_CALLBACK:
                {
                  hr = pmdtc->pIWiaMiniDrvCallBack->MiniDrvCallback(IT_MSG_DATA,IT_STATUS_TRANSFER_TO_CLIENT,
                                                                  lPercentComplete,pmdtc->cbOffset,lBytesReceived,pmdtc,0);
                pmdtc->cbOffset += lBytesReceived;
                lBytesTransferredToApplication += lBytesReceived;
           }
            break;
          case TYMED_MULTIPAGE_FILE:
          case TYMED_FILE:
            {
                //
                // lItemSize is the amount that wiasWriteBufToFile will write to FILE
                //

                pmdtc->lItemSize = lBytesReceived;
                hr = wiasWriteBufToFile(0,pmdtc);
                if (FAILED(hr)) {
                    break;
                }

                hr = pmdtc->pIWiaMiniDrvCallBack->MiniDrvCallback(IT_MSG_STATUS,IT_STATUS_TRANSFER_TO_CLIENT,
                                                                  lPercentComplete,0,0,NULL,0);
                lBytesTransferredToApplication += lBytesReceived;
              }
              break;
          default:
              {
          hr = E_FAIL;
              }
              break;
          }

          //
          // scan from device, requesting ytesToReadFromDevice
          //

          LONG lBytesRemainingToTransfer = (lTotalImageBytes - lBytesTransferredToApplication);
          if (lBytesRemainingToTransfer <= 0) {
              break;
            }

            //
            // calculate number of bytes to request from device
            //

            LONG lBytesToReadFromDevice = (lBytesRemainingToTransfer > pmdtc->lBufferSize) ? pmdtc->lBufferSize : lBytesRemainingToTransfer;

            // RAW data request
            lBytesToReadFromDevice = (lBytesToReadFromDevice / lBytesPerLineAligned) * lBytesPerLineRaw;

            // Aligned data request
            // lBytesToReadFromDevice = (lBytesToReadFromDevice / lBytesPerLineAligned) * lBytesPerLineAligned;

            if ((hr == S_FALSE)||FAILED(hr)) {

              //
              // user canceled or the callback failed for some reason
              //

              break;
            }

            //
            // request byte amount from device
            //

            hr = GetDataFromMyDevice(pmdtc->pTransferBuffer, lBytesToReadFromDevice, (DWORD*)&lBytesReceived);
            if (FAILED(hr)) {
                break;
            }

            //
            // this device returns raw data.  If your device does this too, then you should call the AlignInPlace
            // helper function to align the data.
            //

            lBytesReceived = AlignMyRawData(pmdtc->pTransferBuffer,lBytesReceived,lBytesPerLineAligned,lBytesPerLineRaw);

          } // while ((lBytesReceived))
        }
      }
    }
  }

  //
  // free any allocated memory for buffers
  //

  if (!pmdtc->bClassDrvAllocBuf) {
    CoTaskMemFree(pmdtc->pTransferBuffer);
    pmdtc->pTransferBuffer = NULL;
    pmdtc->lBufferSize = 0;
  }

  return hr;
}