Compartilhar via


Alocando memória para dados

O serviço WIA depende das informações fornecidas na estrutura MINIDRV_TRANSFER_CONTEXT para executar uma transferência de dados adequada.

Os membros dessa estrutura relevantes para o minidriver WIA são:

bClassDrvAllocBuf – Boolean de alocação de serviço WIA.

pTransferBuffer – ponteiro para a memória alocada para os dados transferidos.

lBufferSize – tamanho da memória apontada pelo membro pTransferBuffer .

Se o membro bClassDrvAllocBuf da estrutura MINIDRV_TRANSFER_CONTEXT estiver definido como TRUE, o serviço WIA alocará memória para o minidriver. Se o membro bClassDrvAllocBuf estiver definido como FALSE, o serviço WIA não alocará memória para o minidriver.

O minidriver deve alocar memória usando a função CoTaskMemAlloc (descrita na documentação do SDK do Microsoft Windows). Em seguida, o minidriver deve armazenar o ponteiro para o local da memória em pTransferBuffer e o tamanho da memória em lBufferSize (em bytes).

O membro bClassDrvAllocBuff será definido como FALSE somente se a propriedade WIA_IPA_TYMED estiver definida como TYMED_FILE ou TYMED_MULTIPAGE_FILE e a propriedade WIA_IPA_ITEM_SIZE estiver definida como zero.

O minidriver deve ter cuidado para não sobrecarregar o buffer apontado pelo membro pTransferBuffer . Você pode evitar isso gravando dados em quantidades menores ou iguais ao valor armazenado no membro lBufferSize .

Aprimorando o desempenho da transferência de dados usando o tamanho mínimo do buffer

O minidriver WIA pode controlar a quantidade de memória usada durante a transferência de dados definindo as propriedades WIA_IPA_ITEM_SIZE e WIA_IPA_BUFFER_SIZE .

Um aplicativo WIA usa a propriedade WIA_IPA_BUFFER_SIZE para determinar o tamanho mínimo do buffer de transferência a ser solicitado durante uma transferência de memória. Quanto maior for esse valor, maior será o tamanho da banda solicitada. Se um aplicativo WIA solicitar um buffer menor do que o valor na propriedade WIA_IPA_BUFFER_SIZE, o serviço WIA ignorará esse tamanho solicitado e solicitará ao minidriver WIA um buffer WIA_IPA_BUFFER_SIZE bytes de tamanho. O serviço WIA sempre solicita ao minidriver WIA buffers que têm pelo menos WIA_IPA_BUFFER_SIZE bytes de tamanho.

O valor que a propriedade WIA_IPA_BUFFER_SIZE contém é a quantidade mínima de dados que um aplicativo pode solicitar a qualquer momento. Quanto maior o tamanho do buffer, maiores serão as solicitações para o dispositivo. Tamanhos de buffer muito pequenos podem diminuir o desempenho da transferência de dados.

É recomendável que você defina a propriedade WIA_IPA_BUFFER_SIZE para um tamanho razoável para permitir que o dispositivo transfira dados a uma taxa eficiente. Faça isso equilibrando o número de solicitações (tamanho do buffer não muito pequeno) e o número de solicitações demoradas (buffer muito grande) para seu dispositivo, a fim de garantir o desempenho ideal.

Você deve definir a propriedade WIA_IPA_ITEM_SIZE como zero se o minidriver WIA puder transferir dados. Se o tipo de transferência for TYMED_FILE ou TYMED_MULTIPAGE_FILE, é responsabilidade do minidriver alocar memória para que o buffer de dados seja passado para a função de serviço WIA que grava no arquivo. Isso fornece consistência na implementação do método IWiaMiniDrv::d rvAcquireItemData .

O método IWiaMiniDrv::d rvAcquireItemData é chamado pelo serviço WIA quando pretende transferir dados do dispositivo para um aplicativo. O driver WIA deve determinar qual tipo de transferência (por meio do serviço WIA) o aplicativo está tentando, lendo o membro tymed do MINIDRV_TRANSFER_CONTEXT:

O membro tymed , que é definido pelo aplicativo, pode ter um dos quatro valores a seguir:

TYMED_FILE
Transferir dados para um arquivo.

TYMED_MULTIPAGE_FILE
Transferir dados para um formato de arquivo de várias páginas.

TYMED_CALLBACK
Transferir dados para a memória.

TYMED_MULTIPAGE_CALLBACK
Transfira várias páginas de dados para a memória.

As diferentes configurações de TYMED XXX_CALLBACK e XXX_FILE alteram o uso da chamada da interface de retorno de chamada do aplicativo.

TYMED_CALLBACK e TYMED_MULTIPAGE_CALLBACK

Para uma transferência de memória, emita um retorno de chamada IWiaMiniDrvCallBack::MiniDrvCallback :

(pmdtc-pIWiaMiniDrvCallBack-MiniDrvCallback> no código-fonte> de exemplo a seguir)

Faça o retorno de chamada usando os seguintes valores:

IT_MSG_DATA
O driver está transferindo dados.

IT_STATUS_TRANSFER_TO_CLIENT
A mensagem de transferência de dados.

lPercentComplete
O percentual da transferência concluída.

pmdtc-cbOffset>
Atualize-o para o local atual em que o aplicativo deve gravar a próxima parte de dados.

lBytesReceived
O número de bytes na parte de dados que está sendo enviada ao aplicativo.

pmdtc
Ponteiro para uma estrutura MINIDRV_TRANSFER_CONTEXT que contém os valores de transferência de dados.

TYMED_FILE e TYMED_MULTIPAGE_FILE

Para uma transferência de arquivo, emita um retorno de chamada IWiaMiniDrvCallBack::MiniDrvCallback ::

(pmdtc-pIWiaMiniDrvCallBack-MiniDrvCallback> no código-fonte> de exemplo a seguir)

Faça o retorno de chamada usando os valores a seguir.

IT_MSG_STATUS
O driver está enviando apenas status (sem dados).

IT_STATUS_TRANSFER_TO_CLIENT
A mensagem de transferência de dados.

lPercentComplete
O percentual da transferência concluída.

Se o membro ItemSize da estrutura MINIDRV_TRANSFER_CONTEXT estiver definido como zero, isso indicará ao aplicativo que o driver WIA não sabe o tamanho da imagem resultante e alocará seus próprios buffers de dados. O driver WIA lerá a propriedade WIA_IPA_BUFFER_SIZE e alocará memória para uma única faixa de dados. O driver WIA pode alocar qualquer quantidade de memória necessária aqui, mas é recomendável que a alocação seja mantida pequena.

Para ver se o serviço WIA alocou memória para o driver, marcar o sinalizador pmdtc-bClassDrvAllocBuf>. Se estiver definido como TRUE, o serviço WIA terá memória alocada para o driver. Para descobrir quanta memória foi alocada, marcar o valor em pmdtc-lBufferSize>.

Para alocar sua própria memória, use CoTaskMemAlloc (descrito na documentação do SDK do Microsoft Windows) e use o ponteiro localizado em pmdtc-pTransferBuffer>. (Lembre-se de que o driver alocou essa memória, portanto, o driver também deve liberá-la.) Defina pmdtc-lBufferSize> como o tamanho alocado. Conforme mencionado anteriormente, esse driver de exemplo wia aloca um buffer cujo tamanho, em bytes, é igual ao valor contido em WIA_IPA_BUFFER_SIZE. Em seguida, o driver usa essa memória.

O exemplo a seguir mostra uma implementação do método IWiaMiniDrv::d rvAcquireItemData . Este exemplo pode lidar com os dois casos de alocação de memória.

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;
}