Compartir a través de


Asignación de memoria para datos

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

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

bClassDrvAllocBuf - Boolean de asignación de servicio 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 (descrita en la documentación de Microsoft Windows SDK). 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 se establece en cero.

El minidriver debe tener cuidado de no rellenar el búfer señalado por 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 de menor tamaño que el valor de la propiedad WIA_IPA_BUFFER_SIZE, el servicio WIA omite este tamaño solicitado y solicita al minidriver WIA un búfer que WIA_IPA_BUFFER_SIZE bytes de tamaño. El servicio WIA siempre solicita al minidriver WIA los búferes que tienen al menos WIA_IPA_BUFFER_SIZE bytes de tamaño.

El valor que contiene la propiedad WIA_IPA_BUFFER_SIZE es la cantidad mínima de datos que una aplicación puede solicitar en un 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 minidriver 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.

La configuración de TYMED diferente XXX_CALLBACK y XXX_FILE cambiar el uso de la llamada a la interfaz de devolución de llamada de la aplicación.

TYMED_CALLBACK y TYMED_MULTIPAGE_CALLBACK

Para una transferencia de memoria, emita una devolución de llamada IWiaMiniDrvCallBack::MiniDrvCallback :

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

Realice la devolución de llamada con los valores siguientes:

IT_MSG_DATA
El controlador está transfiriendo 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 una transferencia de archivos, emita una devolución de llamada IWiaMiniDrvCallBack::MiniDrvCallback ::

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

Realice la devolución de llamada con los valores siguientes.

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 de MINIDRV_TRANSFER_CONTEXT se establece 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 que la asignación se mantenga 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 cuánta memoria se asignó, compruebe el valor en pmdtc-lBufferSize>.

Para asignar su propia memoria, use CoTaskMemAlloc (que se describe en la documentación de Microsoft Windows SDK) 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;
}