Compartir a través de


Asignación de memoria para datos

El servicio WIA se basa en la información proporcionada en la estructura MINIDRV_TRANSFER_CONTEXT para realizar una transferencia de datos adecuada.

Los miembros de esta estructura que son relevantes para el minidriver WIA son:

bClassDrvAllocBuf: booleano de asignación de servicios WIA.

pTransferBuffer : puntero a la memoria asignada para los datos transferidos.

lBufferSize : tamaño de la memoria a la que apunta el miembro pTransferBuffer .

Si el miembro bClassDrvAllocBuf de la estructura MINIDRV_TRANSFER_CONTEXT se establece en TRUE, el servicio WIA asignó memoria para el minidriver. Si el miembro bClassDrvAllocBuf está establecido en FALSE, el servicio WIA no asignó ninguna memoria para el minidriver.

El minidriver debe asignar memoria mediante la función CoTaskMemAlloc (que se describe en la documentación del SDK de Microsoft Windows). A continuación, el minidriver debe almacenar el puntero a la ubicación de memoria en pTransferBuffer y el tamaño de la memoria en lBufferSize (en bytes).

El miembro bClassDrvAllocBuff se establece en FALSE solo si la propiedad WIA_IPA_TYMED está establecida en TYMED_FILE o TYMED_MULTIPAGE_FILE, y la propiedad WIA_IPA_ITEM_SIZE está establecida en cero.

El minicontrolador debe tener cuidado de no sobrellenar el búfer al que apunta el miembro pTransferBuffer. Puede evitarlo escribiendo datos en cantidades inferiores o iguales al valor almacenado en el miembro lBufferSize .

Mejora del rendimiento de la transferencia de datos mediante el uso del tamaño mínimo del búfer

El minidriver WIA puede controlar la cantidad de memoria utilizada durante la transferencia de datos estableciendo las propiedades WIA_IPA_ITEM_SIZE y WIA_IPA_BUFFER_SIZE .

Una aplicación WIA usa la propiedad WIA_IPA_BUFFER_SIZE para determinar el tamaño mínimo del búfer de transferencia que se va a solicitar durante una transferencia de memoria. Cuanto mayor sea este valor, mayor será el tamaño de banda solicitado. Si una aplicación WIA solicita un búfer que tiene un tamaño menor que el valor de la propiedad WIA_IPA_BUFFER_SIZE, el servicio WIA ignora este tamaño solicitado y solicita al minicontrolador WIA un búfer que tenga un tamaño de WIA_IPA_BUFFER_SIZE bytes. El servicio WIA siempre solicita al minicontrolador WIA los búferes que tienen al menos un tamaño de WIA_IPA_BUFFER_SIZE bytes.

El valor que contiene la propiedad WIA_IPA_BUFFER_SIZE es la cantidad mínima de datos que una aplicación puede solicitar en cualquier momento dado. Cuanto mayor sea el tamaño del búfer, mayor será el tamaño de las solicitudes para el dispositivo. Los tamaños de búfer que son demasiado pequeños pueden ralentizar el rendimiento de la transferencia de datos.

Se recomienda establecer la propiedad WIA_IPA_BUFFER_SIZE en un tamaño razonable para permitir que el dispositivo transfiera datos a una velocidad eficaz. Para ello, equilibre el número de solicitudes (el tamaño del búfer no es demasiado pequeño) y el número de solicitudes que consumen mucho tiempo (búfer demasiado grande) para el dispositivo con el fin de garantizar un rendimiento óptimo.

Debe establecer la propiedad WIA_IPA_ITEM_SIZE en cero si el minidriver WIA puede transferir datos. Si el tipo de transferencia es TYMED_FILE o TYMED_MULTIPAGE_FILE, es responsabilidad del minicontrolador asignar memoria para que el búfer de datos se pase a la función de servicio WIA que escribe en el archivo. Esto proporciona coherencia en la implementación del método IWiaMiniDrv::d rvAcquireItemData .

El servicio WIA llama al método IWiaMiniDrv::d rvAcquireItemData cuando pretende transferir datos del dispositivo a una aplicación. El controlador WIA debe determinar qué tipo de transferencia (a través del servicio WIA) está intentando la aplicación, leyendo el miembro tymed del MINIDRV_TRANSFER_CONTEXT:

El miembro tymed , que establece la aplicación, puede tener uno de los cuatro valores siguientes:

TYMED_FILE
Transferir datos a un archivo.

TYMED_MULTIPAGE_FILE
Transferir datos a un formato de archivo de varias páginas.

TYMED_CALLBACK
Transferir datos a la memoria.

TYMED_MULTIPAGE_CALLBACK
Transferir varias páginas de datos a la memoria.

Las diferentes configuraciones de TYMED XXX_CALLBACK y XXX_FILE cambian la forma en la que se utiliza la interfaz de devolución de llamada de la aplicación.

TYMED_CALLBACK y TYMED_MULTIPAGE_CALLBACK

Para transferir memoria, realice una devolución de llamada IWiaMiniDrvCallBack::MiniDrvCallback:

(pmdtc->pIWiaMiniDrvCallBack->MiniDrvCallback en el código fuente de ejemplo siguiente)

Realiza el callback utilizando los siguientes valores:

IT_MSG_DATA
El controlador transfiere datos.

IT_STATUS_TRANSFER_TO_CLIENT
Mensaje de transferencia de datos.

lPercentComplete
Porcentaje de la transferencia completada.

pmdtc->cbOffset
Actualícelo a la ubicación actual donde la aplicación debe escribir el siguiente fragmento de datos.

lBytesReceived
Número de bytes del fragmento de datos que se envía a la aplicación.

pmdtc
Puntero a una estructura MINIDRV_TRANSFER_CONTEXT que contiene los valores de transferencia de datos.

TYMED_FILE y TYMED_MULTIPAGE_FILE

Para transferir archivos, realice una devolución de llamada IWiaMiniDrvCallBack::MiniDrvCallback:

(pmdtc->pIWiaMiniDrvCallBack->MiniDrvCallback en el siguiente código fuente de ejemplo)

Realice el callback con los siguientes valores.

IT_MSG_STATUS
El controlador solo envía el estado (sin datos).

IT_STATUS_TRANSFER_TO_CLIENT
Mensaje de transferencia de datos.

lPercentComplete
Porcentaje de la transferencia completada.

Si el miembro ItemSize de la estructura MINIDRV_TRANSFER_CONTEXT está establecido en cero, esto indica a la aplicación que el controlador WIA no conoce el tamaño de imagen resultante y, a continuación, asignará sus propios búferes de datos. El controlador WIA leerá la propiedad WIA_IPA_BUFFER_SIZE y asignará memoria para una sola banda de datos. El controlador WIA puede asignar cualquier cantidad de memoria que necesite aquí, pero se recomienda mantener la asignación pequeña.

Para ver si el servicio WIA ha asignado memoria para el controlador, compruebe la marca pmdtc-bClassDrvAllocBuf>. Si se establece en TRUE, el servicio WIA ha asignado memoria para el controlador. Para averiguar la cantidad de memoria asignada, compruebe el valor en pmdtc-lBufferSize>.

Para asignar su propia memoria, use CoTaskMemAlloc (que se describe en la documentación del SDK de Microsoft Windows) y use el puntero ubicado en pmdtc-pTransferBuffer>. (Recuerde que el controlador asignó esta memoria, por lo que el controlador también debe liberarla). Establezca pmdtc-lBufferSize> en el tamaño asignado. Como se indicó anteriormente, este controlador de ejemplo de WIA asigna un búfer cuyo tamaño, en bytes, es igual al valor contenido en WIA_IPA_BUFFER_SIZE. A continuación, el controlador usa esa memoria.

En el ejemplo siguiente se muestra una implementación del método IWiaMiniDrv::d rvAcquireItemData . Este ejemplo puede controlar ambos casos de asignación de memoria.

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