为数据分配内存

WIA 服务依赖于 MINIDRV_TRANSFER_CONTEXT 结构中提供的信息来执行适当的数据传输。

此结构中与 WIA 微型驱动程序相关的成员包括:

bClassDrvAllocBuf • WIA 服务分配布尔值。

pTransferBuffer • 指向为传输的数据分配的内存的指针。

lBufferSizepTransferBuffer 成员指向的内存的大小。

如果将 MINIDRV_TRANSFER_CONTEXT 结构的 bClassDrvAllocBuf 成员设置为 TRUE,则 WIA 服务为微型驱动程序分配了内存。 如果 bClassDrvAllocBuf 成员设置为 FALSE,则 WIA 服务未为微型驱动程序分配任何内存。

微型驱动程序应使用 Microsoft Windows SDK 文档) 中所述的 CoTaskMemAlloc 函数 (分配内存。 然后,微型驱动程序应将指向内存位置的指针存储在 pTransferBuffer 中,并将内存大小存储在 lBufferSize (字节) 中。

仅当 WIA_IPA_TYMED 属性设置为 TYMED_FILE 或 TYMED_MULTIPAGE_FILE 且 WIA_IPA_ITEM_SIZE 属性设置为零时,bClassDrvAllocBuff 成员才设置为 FALSE

微型驱动程序必须小心不要过度填充 pTransferBuffer 成员指向的缓冲区。 可以通过写入小于或等于 lBufferSize 成员中存储的值的数据来避免这种情况。

使用最小缓冲区大小增强数据传输性能

WIA 微型驱动程序可以通过设置 WIA_IPA_ITEM_SIZEWIA_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 方法的实现提供一致性。

WIA 服务打算将数据从设备传输到应用程序时,由 IWiaMiniDrv::d rvAcquireItemData 方法调用。 WIA 驱动程序应通过读取MINIDRV_TRANSFER_CONTEXT的 tymed 成员) 应用程序尝试通过 WIA 服务确定哪种类型的传输 (

由应用程序设置 的 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
传输完成的百分比。

如果 MINIDRV_TRANSFER_CONTEXT 结构的 ItemSize 成员设置为零,则向应用程序指示 WIA 驱动程序不知道生成的映像大小,然后将分配自己的数据缓冲区。 WIA 驱动程序将读取 WIA_IPA_BUFFER_SIZE 属性并为单个数据段分配内存。 WIA 驱动程序可以在此处分配所需的任意内存量,但建议保持较小的分配。

若要查看 WIA 服务是否已为驱动程序分配内存,检查 pmdtc-bClassDrvAllocBuf> 标志。 如果设置为 TRUE,则 WIA 服务已为驱动程序分配内存。 若要了解已分配的内存量,检查 pmdtc-lBufferSize> 中的值。

若要分配自己的内存,请使用 Microsoft Windows SDK 文档) 中所述的 CoTaskMemAlloc (,并使用位于 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;
}