Freigeben über


Desktopduplizierungs-API

Windows 8 deaktiviert standardmäßige Windows 2000 Display Driver Model (XDDM)-Spiegeltreiber und bietet stattdessen die Desktopduplizierungs-API an. Die Desktopduplizierungs-API bietet Remotezugriff auf ein Desktopimage für Zusammenarbeitsszenarien. Apps können die Desktopduplizierungs-API verwenden, um auf Frame-nach-Frame-Updates auf den Desktop zuzugreifen. Da Apps Updates für das Desktopimage auf einer DXGI-Oberfläche erhalten, können die Apps die volle Leistungsfähigkeit der GPU verwenden, um die Bildupdates zu verarbeiten.

Aktualisieren der Desktopbilddaten

DXGI stellt eine Oberfläche bereit, die über die neue IDXGIOutputDuplication::AcquireNextFrame-Methode ein aktuelles Desktopimage enthält. Das Format des Desktopbilds ist immer DXGI_FORMAT_B8G8R8A8_UNORM unabhängig vom aktuellen Anzeigemodus. Zusammen mit dieser Oberfläche geben diese IDXGIOutputDuplication- Methoden die angegebenen Informationstypen zurück, mit denen Sie ermitteln können, welche Pixel innerhalb der Oberfläche Verarbeitet werden müssen:

  • IDXGIOutputDuplication::GetFrameDirtyRects gibt schmutzige Bereiche zurück, die nicht überlappende Rechtecke sind, die die Bereiche des Desktopimages angeben, die das Betriebssystem seit der Verarbeitung des vorherigen Desktopimages aktualisiert hat.
  • IDXGIOutputDuplication::GetFrameMoveRects verschieben Bereiche zurück, die Rechtecke von Pixeln im Desktopimage sind, die das Betriebssystem an eine andere Position innerhalb desselben Bilds verschoben hat. Jeder Bewegungsbereich besteht aus einem Zielrechteck und einem Quellpunkt. Der Quellpunkt gibt den Speicherort an, von dem das Betriebssystem die Region kopiert hat, und das Zielrechteck gibt an, wo das Betriebssystem diese Region verschoben hat. Verschiebungsbereiche sind immer nicht gestreckte Regionen, sodass die Quelle immer die gleiche Größe wie das Ziel aufweist.

Angenommen, das Desktopimage wurde über eine langsame Verbindung mit Ihrer Remoteclient-App übertragen. Die Datenmenge, die über die Verbindung gesendet wird, wird reduziert, indem nur Daten darüber empfangen werden, wie Ihre Client-App Bereiche von Pixeln und nicht tatsächlichen Pixeldaten verschieben muss. Um die Verschiebungen zu verarbeiten, muss ihre Client-App das vollständige letzte Bild gespeichert haben.

Während das Betriebssystem nicht verarbeitete Desktopimageupdates ansammelt, ist es möglicherweise nicht mehr verfügbar, um die Updatebereiche genau zu speichern. In dieser Situation beginnt das Betriebssystem, die Updates anzusammeln, indem sie mit vorhandenen Updateregionen zusammengetragen werden, um alle neuen Updates abzudecken. Daher deckt das Betriebssystem Pixel ab, die es noch nicht in diesem Frame aktualisiert hat. Diese Situation erzeugt jedoch keine visuellen Probleme in Ihrer Client-App, da Sie das gesamte Desktopimage und nicht nur die aktualisierten Pixel erhalten.

Um das richtige Desktopimage zu rekonstruieren, muss Ihre Client-App zuerst alle Verschiebungsbereiche verarbeiten und dann alle schmutzigen Bereiche verarbeiten. Eine dieser Listen mit geänderten und verschobenen Bereichen kann vollständig leer sein. Der Beispielcode aus dem Desktopduplizierungsbeispiel zeigt, wie sowohl die geänderten als auch die Verschieben von Bereichen in einem einzelnen Frame verarbeitet werden:

//
// Get next frame and write it into Data
//
HRESULT DUPLICATIONMANAGER::GetFrame(_Out_ FRAME_DATA* Data)
{
    HRESULT hr = S_OK;

    IDXGIResource* DesktopResource = NULL;
    DXGI_OUTDUPL_FRAME_INFO FrameInfo;

    //Get new frame
    hr = DeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource);
    if (FAILED(hr))
    {
        if ((hr != DXGI_ERROR_ACCESS_LOST) && (hr != DXGI_ERROR_WAIT_TIMEOUT))
        {
            DisplayErr(L"Failed to acquire next frame in DUPLICATIONMANAGER", L"Error", hr);
        }
        return hr;
    }

    // If still holding old frame, destroy it
    if (AcquiredDesktopImage)
    {
        AcquiredDesktopImage->Release();
        AcquiredDesktopImage = NULL;
    }

    // QI for IDXGIResource
    hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&AcquiredDesktopImage));
    DesktopResource->Release();
    DesktopResource = NULL;
    if (FAILED(hr))
    {
        DisplayErr(L"Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER", L"Error", hr);
        return hr;
    }

    // Get metadata
    if (FrameInfo.TotalMetadataBufferSize)
    {
        // Old buffer too small
        if (FrameInfo.TotalMetadataBufferSize > MetaDataSize)
        {
            if (MetaDataBuffer)
            {
                delete [] MetaDataBuffer;
                MetaDataBuffer = NULL;
            }
            MetaDataBuffer = new (std::nothrow) BYTE[FrameInfo.TotalMetadataBufferSize];
            if (!MetaDataBuffer)
            {
                DisplayErr(L"Failed to allocate memory for metadata in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY);
                MetaDataSize = 0;
                Data->MoveCount = 0;
                Data->DirtyCount = 0;
                return E_OUTOFMEMORY;
            }
            MetaDataSize = FrameInfo.TotalMetadataBufferSize;
        }

        UINT BufSize = FrameInfo.TotalMetadataBufferSize;

        // Get move rectangles
        hr = DeskDupl->GetFrameMoveRects(BufSize, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(MetaDataBuffer), &BufSize);
        if (FAILED(hr))
        {
            if (hr != DXGI_ERROR_ACCESS_LOST)
            {
                DisplayErr(L"Failed to get frame move rects in DUPLICATIONMANAGER", L"Error", hr);
            }
            Data->MoveCount = 0;
            Data->DirtyCount = 0;
            return hr;
        }
        Data->MoveCount = BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);

        BYTE* DirtyRects = MetaDataBuffer + BufSize;
        BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;

        // Get dirty rectangles
        hr = DeskDupl->GetFrameDirtyRects(BufSize, reinterpret_cast<RECT*>(DirtyRects), &BufSize);
        if (FAILED(hr))
        {
            if (hr != DXGI_ERROR_ACCESS_LOST)
            {
                DisplayErr(L"Failed to get frame dirty rects in DUPLICATIONMANAGER", L"Error", hr);
            }
            Data->MoveCount = 0;
            Data->DirtyCount = 0;
            return hr;
        }
        Data->DirtyCount = BufSize / sizeof(RECT);

        Data->MetaData = MetaDataBuffer;
    }

    Data->Frame = AcquiredDesktopImage;
    Data->FrameInfo = FrameInfo;

    return hr;
}

//
// Release frame
//
HRESULT DUPLICATIONMANAGER::DoneWithFrame()
{
    HRESULT hr = S_OK;

    hr = DeskDupl->ReleaseFrame();
    if (FAILED(hr))
    {
        DisplayErr(L"Failed to release frame in DUPLICATIONMANAGER", L"Error", hr);
        return hr;
    }

    if (AcquiredDesktopImage)
    {
        AcquiredDesktopImage->Release();
        AcquiredDesktopImage = NULL;
    }

    return hr;
}

Drehen des Desktopimages

Sie müssen Ihrer Desktopduplizierungsclient-App expliziten Code hinzufügen, um gedrehte Modi zu unterstützen. In einem gedrehten Modus befindet sich die Oberfläche, die Sie von IDXGIOutputDuplication::AcquireNextFrame erhalten, immer in der nicht gedrehten Ausrichtung, und das Desktopbild wird innerhalb der Oberfläche gedreht. Wenn der Desktop beispielsweise bei einer Drehung von 90 Grad auf 768 x 1024 festgelegt ist, gibt AcquireNextFrame eine 1024x768-Oberfläche zurück, wobei das Desktopbild darin gedreht ist. Hier sind einige Drehungsbeispiele.

Anzeigemodus, der über die Anzeigesteuerung festgelegt ist Von GDI oder DXGI zurückgegebener Anzeigemodus Von AcquireNextFrame zurückgegebene Surface
1024 x 768 Querformat Drehung von 1024 x 768 0 Grad 1024x768[newline] nicht gedrehten Remotedesktop-
1024 x 768 Hochformat Drehung von 768 x 1024 90 Grad 1024x768[neue Zeile] um 90 Grad Remotedesktop
1024 x 768 Querformat (gekippt) Drehung von 1024 x 768 180 Grad 1024x768[neue Linie] um 180 Grad Remotedesktop
1024 x 768 Hochformat (gedreht) Drehung von 768 x 1024 270 Grad 1024x768[Newline] um 270 Grad Remotedesktop

 

Der Code in Ihrer Desktopduplizierungsclient-App muss das Desktopimage entsprechend drehen, bevor Sie das Desktopimage anzeigen.

Anmerkung

In Szenarien mit mehreren Monitoren können Sie das Desktopimage für jeden Monitor unabhängig drehen.

 

Aktualisieren des Desktopzeigers

Sie müssen die Desktopduplizierungs-API verwenden, um zu ermitteln, ob Ihre Client-App das Mauszeiger-Shape auf das Desktopimage zeichnen muss. Entweder wird der Mauszeiger bereits auf das Desktopbild gezeichnet, das IDXGIOutputDuplication::AcquireNextFrame bereitstellt oder der Mauszeiger vom Desktopimage getrennt ist. Wenn der Mauszeiger auf das Desktopbild gezeichnet wird, zeigen die Zeigerpositionsdaten, die von AcquireNextFrame- gemeldet werden (im PointerPosition-Element Member von DXGI_OUTDUPL_FRAME_INFO, dass der pFrameInfo Parameter verweist) angibt, dass ein separater Zeiger nicht sichtbar ist. Wenn der Grafikadapter den Mauszeiger über dem Desktopbild überlagert, meldet AcquireNextFrame, dass ein separater Zeiger sichtbar ist. Ihre Client-App muss also das Mauszeiger-Shape auf das Desktopbild zeichnen, um genau darzustellen, was der aktuelle Benutzer auf dem Monitor sehen wird.

Verwenden Sie zum Zeichnen des Mauszeigers des Desktops das PointerPosition-element member of DXGI_OUTDUPL_FRAME_INFO aus dem pFrameInfo Parameter von AcquireNextFrame, um zu bestimmen, wo die obere linke Ecke des Mauszeigers auf dem Desktopbild gefunden werden soll. Wenn Sie den ersten Frame zeichnen, müssen Sie die IDXGIOutputDuplication::GetFramePointerShape-Methode verwenden, um Informationen zur Form des Mauszeigers abzurufen. Jeder Aufruf von AcquireNextFrame zum Abrufen des nächsten Frames stellt auch die aktuelle Zeigerposition für diesen Frame bereit. Andererseits müssen Sie GetFramePointerShape nur dann erneut verwenden, wenn sich die Form ändert. Behalten Sie also eine Kopie des letzten Zeigerbilds bei, und verwenden Sie es, um auf dem Desktop zu zeichnen, es sei denn, die Form des Mauszeigers ändert sich.

Anmerkung

Zusammen mit dem Zeiger-Shape-Bild stellt GetFramePointerShape die Größe der Hotspotposition bereit. Der Hotspot wird nur zu Informationszwecken gegeben. Die Position, an der das Zeigerbild gezeichnet werden soll, ist unabhängig vom Hotspot.

 

Dieser Beispielcode aus dem Desktopduplizierungsbeispiel zeigt, wie das Mauszeiger-Shape abgerufen wird:

//
// Retrieves mouse info and write it into PtrInfo
//
HRESULT DUPLICATIONMANAGER::GetMouse(_Out_ PTR_INFO* PtrInfo, _In_ DXGI_OUTDUPL_FRAME_INFO* FrameInfo, INT OffsetX, INT OffsetY)
{
    HRESULT hr = S_OK;

    // A non-zero mouse update timestamp indicates that there is a mouse position update and optionally a shape change
    if (FrameInfo->LastMouseUpdateTime.QuadPart == 0)
    {
        return hr;
    }

    bool UpdatePosition = true;

    // Make sure we don't update pointer position wrongly
    // If pointer is invisible, make sure we did not get an update from another output that the last time that said pointer
    // was visible, if so, don't set it to invisible or update.
    if (!FrameInfo->PointerPosition.Visible && (PtrInfo->WhoUpdatedPositionLast != OutputNumber))
    {
        UpdatePosition = false;
    }

    // If two outputs both say they have a visible, only update if new update has newer timestamp
    if (FrameInfo->PointerPosition.Visible && PtrInfo->Visible && (PtrInfo->WhoUpdatedPositionLast != OutputNumber) && (PtrInfo->LastTimeStamp.QuadPart > FrameInfo->LastMouseUpdateTime.QuadPart))
    {
        UpdatePosition = false;
    }

    // Update position
    if (UpdatePosition)
    {
        PtrInfo->Position.x = FrameInfo->PointerPosition.Position.x + OutputDesc.DesktopCoordinates.left - OffsetX;
        PtrInfo->Position.y = FrameInfo->PointerPosition.Position.y + OutputDesc.DesktopCoordinates.top - OffsetY;
        PtrInfo->WhoUpdatedPositionLast = OutputNumber;
        PtrInfo->LastTimeStamp = FrameInfo->LastMouseUpdateTime;
        PtrInfo->Visible = FrameInfo->PointerPosition.Visible != 0;
    }

    // No new shape
    if (FrameInfo->PointerShapeBufferSize == 0)
    {
        return hr;
    }

    // Old buffer too small
    if (FrameInfo->PointerShapeBufferSize > PtrInfo->BufferSize)
    {
        if (PtrInfo->PtrShapeBuffer)
        {
            delete [] PtrInfo->PtrShapeBuffer;
            PtrInfo->PtrShapeBuffer = NULL;
        }
        PtrInfo->PtrShapeBuffer = new (std::nothrow) BYTE[FrameInfo->PointerShapeBufferSize];
        if (!PtrInfo->PtrShapeBuffer)
        {
            DisplayErr(L"Failed to allocate memory for pointer shape in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY);
            PtrInfo->BufferSize = 0;
            return E_OUTOFMEMORY;
        }

        // Update buffer size
        PtrInfo->BufferSize = FrameInfo->PointerShapeBufferSize;
    }

    UINT BufferSizeRequired;
    // Get shape
    hr = DeskDupl->GetFramePointerShape(FrameInfo->PointerShapeBufferSize, reinterpret_cast<VOID*>(PtrInfo->PtrShapeBuffer), &BufferSizeRequired, &(PtrInfo->ShapeInfo));
    if (FAILED(hr))
    {
        if (hr != DXGI_ERROR_ACCESS_LOST)
        {
            DisplayErr(L"Failed to get frame pointer shape in DUPLICATIONMANAGER", L"Error", hr);
        }
        delete [] PtrInfo->PtrShapeBuffer;
        PtrInfo->PtrShapeBuffer = NULL;
        PtrInfo->BufferSize = 0;
        return hr;
    }

    return hr;
}

DXGI 1.2 Verbesserungen