Udostępnij przez


Przydzielanie pamięci dla danych

Usługa WIA opiera się na informacjach podanych w strukturze MINIDRV_TRANSFER_CONTEXT w celu przeprowadzenia odpowiedniego transferu danych.

Elementy członkowskie tej struktury, które są istotne dla minidrivera WIA, to:

bClassDrvAllocBuf − Wartość logiczna alokacji usługi WIA.

pTransferBuffer − Wskaźnik do pamięci przydzielonej do przesyłanych danych.

lBufferSize − Rozmiar pamięci wskazywanej przez członek pTransferBuffer.

Jeśli element członkowski bClassDrvAllocBuf struktury MINIDRV_TRANSFER_CONTEXT jest ustawiony na PRAWDA, to usługa WIA przydzieliła pamięć minidriverowi. Jeśli element bClassDrvAllocBuf członka jest ustawiony na FALSE, to usługa WIA nie przydzieliła żadnej pamięci dla minidrivera.

Minidriver powinien przydzielić pamięć, używając funkcji CoTaskMemAlloc (opisanej w dokumentacji Microsoft Windows SDK). Minidriver powinien następnie przechowywać wskaźnik do lokalizacji pamięci w pTransferBuffer i rozmiar pamięci w lBufferSize (w bajtach).

Członek bClassDrvAllocBuff jest ustawiony na wartość FALSE tylko wtedy, gdy właściwość WIA_IPA_TYMED jest ustawiona na TYMED_FILE lub TYMED_MULTIPAGE_FILE, a właściwość WIA_IPA_ITEM_SIZE ma wartość zero.

Minidriver powinien być ostrożny, aby nie przepełnić buforu wskazywanego przez pTransferBuffer członka. Można tego uniknąć, zapisując dane w ilościach mniejszych lub równych wartości przechowywanej w elementu członkowskiego lBufferSize.

Zwiększanie wydajności transferu danych przy użyciu minimalnego rozmiaru buforu

Minidriver WIA może kontrolować ilość pamięci używanej podczas transferu danych, ustawiając właściwości WIA_IPA_ITEM_SIZE i WIA_IPA_BUFFER_SIZE.

Aplikacja WIA używa właściwości WIA_IPA_BUFFER_SIZE, aby określić minimalny rozmiar buforu transferu do żądania podczas transferu pamięci. Większa jest ta wartość, tym większy będzie żądany rozmiar przedziału. Jeśli aplikacja WIA żąda buforu o rozmiarze mniejszym niż wartość właściwości WIA_IPA_BUFFER_SIZE, usługa WIA ignoruje ten żądany rozmiar i zwraca się do minidrivera WIA o bufor mający rozmiar WIA_IPA_BUFFER_SIZE bajtów. Usługa WIA zawsze prosi minidriver WIA o bufory, które mają rozmiarze co najmniej WIA_IPA_BUFFER_SIZE bajtów.

Wartość, którą zawiera właściwość WIA_IPA_BUFFER_SIZE, to minimalna ilość danych, które aplikacja może zażądać w dowolnym momencie. Większy rozmiar buforu, tym większe żądania będą kierowane do urządzenia. Bufory, które są zbyt małe, mogą spowalniać transfer danych.

Zaleca się ustawienie właściwości WIA_IPA_BUFFER_SIZE na rozsądny rozmiar, aby umożliwić urządzeniu przesyłanie danych w wydajnym tempie. Zrób to, równoważąc liczbę żądań (rozmiar buforu nie jest zbyt mały) i liczbę żądań czasochłonnych (bufor zbyt duży) dla urządzenia w celu zapewnienia optymalnej wydajności.

Należy ustawić właściwość WIA_IPA_ITEM_SIZE na zero, jeśli minidriver WIA może przesyłać dane. Jeśli typ transferu to TYMED_FILE lub TYMED_MULTIPAGE_FILE, minidriver jest odpowiedzialny za przydzielenie pamięci na bufor danych, który zostanie przekazany do funkcji WIA zapisującej dane do pliku. Zapewnia to spójność w implementacji metody IWiaMiniDrv::drvAcquireItemData.

Metoda IWiaMiniDrv::drvAcquireItemData jest wywoływana przez usługę WIA, gdy zamierza przesyłać dane z urządzenia do aplikacji. Sterownik WIA powinien określić, jakiego rodzaju transferu za pośrednictwem usługi WIA próbuje dokonać aplikacja, odczytując członek tymed w MINIDRV_TRANSFER_CONTEXT:

Członek ustawiany przez aplikację może mieć jedną z następujących czterech wartości:

TYMED_FILE
Transfer danych do pliku.

TYMED_MULTIPAGE_FILE
Transfer danych do formatu pliku wielostronicowego.

TYMED_CALLBACK
Transfer danych do pamięci.

TYMED_MULTIPAGE_CALLBACK
Przesyłanie wielu stron danych do pamięci.

Różne ustawienia TYMED XXX_CALLBACK i XXX_FILE zmieniają sposób użycia interfejsu wywołania zwrotnego aplikacji.

TYMED_CALLBACK i TYMED_MULTIPAGE_CALLBACK

W przypadku transferu pamięci wydaj IWiaMiniDrvCallBack::MiniDrvCallback wywołania zwrotnego:

(pmdtc->pIWiaMiniDrvCallBack->MiniDrvCallback w poniższym przykładowym kodzie źródłowym)

Wykonaj wywołanie zwrotne przy użyciu następujących wartości:

IT_MSG_DATA
Sterownik przesyła dane.

STATUS_PRZEKAZANIA_DO_KLIENTA
Komunikat transferu danych.

lPercentComplete
Procent ukończenia transferu.

pmdtc->cbOffset
Zaktualizuj tę wartość do bieżącej lokalizacji, w której aplikacja powinna zapisać następny fragment danych.

lBytesReceived
Liczba bajtów we fragmentach danych wysyłanych do aplikacji.

pmdtc
Wskaźnik do struktury MINIDRV_TRANSFER_CONTEXT zawierającej wartości transferu danych.

TYMED_FILE i TYMED_MULTIPAGE_FILE

W przypadku transferu plików wykonaj wywołanie IWiaMiniDrvCallBack::MiniDrvCallback:

(pmdtc->pIWiaMiniDrvCallBack->MiniDrvCallback w poniższym przykładowym kodzie źródłowym)

Wykonaj wywołanie zwrotne przy użyciu następujących wartości.

IT_MSG_STATUS
Sterownik wysyła tylko stan (brak danych).

IT_STATUS_TRANSFER_TO_CLIENT
Komunikat transferu danych.

lPercentComplete
Procent ukończenia transferu.

Jeśli ItemSize element członkowski struktury MINIDRV_TRANSFER_CONTEXT jest ustawiony na zero, oznacza to dla aplikacji, że sterownik WIA nie zna wynikowego rozmiaru obrazu i następnie przydzieli własne bufory danych. Sterownik WIA odczytuje właściwość WIA_IPA_BUFFER_SIZE i przydziela pamięć dla pojedynczego przedziału danych. Sterownik WIA może przydzielić w tym miejscu dowolną ilość pamięci, ale zaleca się, aby alokacja była mała.

Aby sprawdzić, czy usługa WIA przydzieliła pamięć dla sterownika, sprawdź flagę pmdtc->bClassDrvAllocBuf. Jeśli ustawiono wartość true, usługa WIA przydzieliła pamięć dla sterownika. Aby dowiedzieć się, ile pamięci zostało przydzielone, sprawdź wartość w pmdtc->lBufferSize.

Aby przydzielić własną pamięć, użyj CoTaskMemAlloc (opisanej w dokumentacji zestawu MICROSOFT Windows SDK) i użyj wskaźnika znajdującego się w pmdtc->pTransferBuffer. (Należy pamiętać, że sterownik przydzielił tę pamięć, więc sterownik musi ją również zwolnić). Ustaw pmdtc->lBufferSize na rozmiar, który przydzieliłeś. Jak wspomniano wcześniej, ten przykładowy sterownik WIA przydziela bufor, którego rozmiar w bajtach jest równy wartości zawartej w WIA_IPA_BUFFER_SIZE. Następnie sterownik używa tej pamięci.

W poniższym przykładzie przedstawiono implementację metody IWiaMiniDrv::drvAcquireItemData. W tym przykładzie można obsłużyć oba przypadki alokacji pamięci.

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