Форум программистов, компьютерный форум, киберфорум
C++/CLI
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.59/34: Рейтинг темы: голосов - 34, средняя оценка - 4.59
10 / 10 / 3
Регистрация: 20.05.2016
Сообщений: 321
1

Desktop Duplication API - Получение серии скриншотов с экрана

11.09.2017, 17:17. Показов 6632. Ответов 19
Метки нет (Все метки)

Представляю свой исходник:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// via https://pastebin.com/JCYPtd5j
 
#include "stdafx.h"
#include "iostream"
 
#include "D3D9.h"
#include <Wincodec.h>
#include <chrono>
 
#include <shellapi.h>
#include <d3d11.h>
#include <dxgi1_2.h>
#include <Atlbase.h>
#include <comdef.h>
 
#include <windows.h>
#include <shlobj.h>
#include <shellapi.h>
#include <dxgi1_2.h>
#include <d3d11.h>
#include <memory>
#include <algorithm>
#include <string>
 
#pragma comment(lib, "D3D11.lib")
#pragma comment(lib, "D3d9.lib")
#pragma comment(lib, "dxgi.lib")
 
#pragma comment(lib, "gdi32.lib")
 
using namespace System;
 
#define EXIT(hr) { if (FAILED(hr)) \
                { Console::WriteLine("Error!"); \
                Console::ReadKey(); return -1; } }
 
HBITMAP ExtractBitmap(ID3D11Texture2D* d3dtex, ID3D11Device* pDevice) 
{
    HRESULT hr;
 
    HBITMAP hBitmapTexture = NULL;
    HGDIOBJ hBitmap;
 
    D3D11_TEXTURE2D_DESC desc;
    ID3D11Texture2D* pNewTexture = NULL;
 
    D3D11_TEXTURE2D_DESC description;
 
    d3dtex->GetDesc(&desc);
    d3dtex->GetDesc(&description);
 
    description.BindFlags = 0;
    description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
    description.Usage = D3D11_USAGE_STAGING;
    description.MiscFlags = 0;
 
    if (FAILED(pDevice->CreateTexture2D(&description, NULL, &pNewTexture)))
    {
        Console::WriteLine("CreateTexture2D failed!");
        return NULL;
    }
 
    ID3D11DeviceContext* ctx = NULL;
    pDevice->GetImmediateContext(&ctx);
 
    ctx->CopyResource(pNewTexture, d3dtex);
 
    D3D11_MAPPED_SUBRESOURCE resource;
    UINT subresource = D3D11CalcSubresource(0, 0, 0);
    ctx->Map(pNewTexture, subresource, D3D11_MAP_READ_WRITE, 0, &resource);
 
    // Copy from texture to bitmap buffer.
    uint8_t* sptr = reinterpret_cast<uint8_t*>(resource.pData);
    uint8_t* dptr = new uint8_t[desc.Width*desc.Height * 4];
 
    for (size_t h = 0; h < desc.Height; ++h)
    {
        size_t msize = std::min<size_t>(desc.Width * 4, resource.RowPitch);
        memcpy_s(dptr, desc.Width * 4, sptr, msize);
        sptr += resource.RowPitch;
        dptr += desc.Width * 4;
    }
 
    dptr -= desc.Width*desc.Height * 4;
 
    // Swap BGR to RGB bitmap.
    uint32_t *dPtr = reinterpret_cast<uint32_t*>(dptr);
    for (size_t count = 0; count < desc.Width*desc.Height * 4; count += 4)
    {
        uint32_t t = *dPtr;
        uint32_t t1 = (t & 0x00ff0000) >> 16;
        uint32_t t2 = (t & 0x000000ff) << 16;
        uint32_t t3 = (t & 0x0000ff00);
        uint32_t ta = (t & 0xFF000000);
        *(dPtr++) = t1 | t2 | t3 | ta;
    }
 
    hBitmapTexture = CreateCompatibleBitmap(GetDC(NULL), desc.Width, desc.Height);
    SetBitmapBits(hBitmapTexture, desc.Width*desc.Height * 4, dptr);
 
    return (HBITMAP)CopyImage(hBitmapTexture, IMAGE_BITMAP, desc.Width, desc.Height, LR_CREATEDIBSECTION);
}
 
 
int main(array<System::String^> ^args)
{
    HRESULT hr;
 
    // Supported feature levels.
    D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_1
    };
 
    D3D_FEATURE_LEVEL d3dFeatLvl;
    ID3D11Device* pDevice = nullptr;
    ID3D11DeviceContext* pImmediateContext = nullptr;
 
    // Get device object.
    hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE,
        NULL, 0, featureLevels,
        ARRAYSIZE(featureLevels),
        D3D11_SDK_VERSION,
        &pDevice,
        &d3dFeatLvl,
        &pImmediateContext);
    EXIT(hr);
 
    // Get DXGI device.
    IDXGIDevice* DxgiDevice = nullptr;
    hr = pDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&DxgiDevice));
    EXIT(hr);
 
    // Get DXGI adapter.
    IDXGIAdapter* DxgiAdapter = nullptr;
    hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&DxgiAdapter));
    DxgiDevice->Release();
    DxgiDevice = nullptr;
    EXIT(hr);
 
    // Get DXGI output.
    IDXGIOutput* DxgiOutput = nullptr;
    hr = DxgiAdapter->EnumOutputs(0, &DxgiOutput);
    DxgiAdapter->Release();
    DxgiAdapter = nullptr;
    EXIT(hr);
 
    DXGI_OUTPUT_DESC OutputDesc;
    DxgiOutput->GetDesc(&OutputDesc);
 
    // Query interface for Output1.
    IDXGIOutput1* DxgiOutput1 = nullptr;
    hr = DxgiOutput->QueryInterface(__uuidof(IDXGIOutput1), reinterpret_cast<void**>(&DxgiOutput1));
    DxgiOutput->Release();
    DxgiOutput = nullptr;
    EXIT(hr);
 
    // Create desktop duplication.
    IDXGIOutputDuplication* DeskDupl = nullptr;
    hr = DxgiOutput1->DuplicateOutput(pDevice, &DeskDupl);
    DxgiOutput1->Release();
    DxgiOutput1 = nullptr;
    EXIT(hr);
 
    DXGI_OUTDUPL_DESC OutputDuplDesc;
    DeskDupl->GetDesc(&OutputDuplDesc);
 
    ///////////////////////////////////////////////////////////////////////////
 
    ID3D11Texture2D* AcquiredDesktopImage = nullptr;
 
    IDXGIResource* DesktopResource = nullptr;
    DXGI_OUTDUPL_FRAME_INFO FrameInfo;
 
    // Get new frame.
    hr = DeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource);
    EXIT(hr);
 
    // If still holding old frame, destroy it.
    if (AcquiredDesktopImage)
    {
        AcquiredDesktopImage->Release();
        AcquiredDesktopImage = nullptr;
    }
 
    // Query interface for IDXGIResource.
    hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&AcquiredDesktopImage));
    DesktopResource->Release();
    DesktopResource = nullptr;
    EXIT(hr);
 
    HBITMAP hBmp = (HBITMAP)ExtractBitmap(AcquiredDesktopImage, pDevice);
 
    ///////////////////////////////////////////////////////////////////////////
 
    System::Drawing::Bitmap::FromHbitmap(IntPtr(hBmp))->Save("Screenshot.bmp");
 
    Console::WriteLine("END!");
    Console::ReadKey();
    return 0;
}



К сожалению, не могу его никак отладить. Получается пустая картинка.
Как зациклить тоже не знаю. По идее, логично было бы начать цикл с вызова AcquireNextFrame, но на второй итерации это приведёт к выбросу исключения.

Прошу помочь разобраться. Язык - C++ CLI (издержки конечного проекта).
__________________
Помощь в написании контрольных, курсовых и дипломных работ здесь
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
11.09.2017, 17:17
Ответы с готовыми решениями:

Создание серии скриншотов
Есть две формы через Button1 запускаю цикл, как мне через эту же кнопку его прервать. oid...

Исходник программы по снятию скриншотов экрана
Наверное вы часто не моли найти негде исходник или исходный код программы по снятию скриншотов ...

Как организовать в программе снятие скриншотов экрана
Необходимо сделать так, чтобы программа автоматически снимала скрины экрана по заданному интервалу...

Программа для снятия и отправки по email скриншотов экрана
Доброго времени суток. Необходима программа, которая будет снимать скриншоты и отправлять их на...

19
Ушел с форума
Эксперт С++
16429 / 7393 / 1186
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
11.09.2017, 20:52 2
Лучший ответ Сообщение было отмечено edward_freedom как решение

Решение

У тебя инициализация Desktop Duplication (здесь и далее - просто DDA) сделана неверно.

Инициализация DDA должна начинаться с вызова CreateDXGIFactory1 и перечисления адаптеров.
Адаптер - это абстракция, за которой скрывается либо реальное устройство (видео-карта,
hardware adapter), либо ее некое программное подобие (software adapter). Еще в системе
всегда присутствует третий тип адаптера - синтетическое устройство 'Microsoft Basic
Render Driver' (VID=1414, PID=008c). В консольных сеансах снимать скрины и видео можно
только с настоящих, т.е. hardware, адаптеров. В терминальных сессиях (RDP) используются
software-адаптеры и синтетика от MS.

Каждый адаптер контролирует от нуля до нескольких output-устройств. Output - это, по
сути, дисплей. Adapter->EnumOutputs позволяет перечислять output-ы. Если output
подключен к дисплею, у него свойство DXGI_OUTPUT_DESC.AttachedToDesktop будет в TRUE.
Вот такие output-ы и надо использовать, неактивные пропускать.

То есть, в системе с двумя видеокартами и тремя подключенными мониторами у тебя будет
два hardware-адаптера и минимум три приаттаченных к соответствующим дисплеям output-а.
Если нужны скрины или видео со всех мониторов, нужно использовать все адаптеры и output-ы.

И вот только потом, имея список адаптеров и output-ов, можно вызывать для каждого
адаптера D3D11CreateDevice и снимать скрины.

-----

Код не привожу, он достаточно длинный и местами неочевидный. Напишу лишь основные
моменты, на которые стоит обратить внимание.

1. С каждым output-ом нужно работать в отдельном потоке.
Если этого не делать и пытаться работать с данными со всех дисплеев сразу, могут
возникать различные "странные" эффекты, вплоть до access violation (0xC0000005).
Почему - мне выяснить в свое время не удалось. Возможно, это какая-то баго-фича DDA.
Было это на первых версиях Win8, сейчас уже, наверное, пофиксили.

2. Цикл работы с DDA (AcquireNextFrame и т.п.) должен, как это ни странно, включать
либо Sleep, либо WaitForXxx, хотя бы с минимальным интервалом ожидания. Если этого нету,
то на многомониторных системах иногда информация об измененных регионах (dirty rects +
move rects) приходит неполная. Также подозреваю, что баг ранних версий Win8.

3. С передачей текстур из одного потока в другой лучше не баловаться. Делай все внутри
одного потока. Например, у меня CopyResource или CopySubresourceRegion (D3D11DeviceContext)
быстро приводила к access violation, никакая синхронизация не помогла, ни критическая
секция, ни IDXGIKeyedMutex.

4. Иногда функции DDA возвращают ошибку с кодами типа DXGI_ERROR_ACCESS_LOST.
Обычно такое происходит при изменении разрешения или частоты экрана, при переключении
на другой рабочий стол и т.д. Пугаться этого не нужно, а надо просто освободить все
ресурсы и начать "сеанс" работы с DDA по-новой, начиная с перечисления адаптеров.

5. Доступ к пикселям на AcquireNextFrame. Здесь все очень просто: запрашиваем у
IDXGIResource интерфейс ID3D11Texture2D - это и есть текстура с тещущим скрином экрана.
Далее из нее содержимое копируется в промежуточную структуру (CopyResource или
CopySubresourceRegion), потом пиксели мапятся в память и после этого уже можно
работать с ними. Например, сохранять в BMP-файл При работе с пикселями следует
учитывать pitch, т.е. шаг в байтах между горизонтальными строками.

6. Исходная структура всегда содержит неперевернутое изображение. Обычно ее нужно
повернуть на нужное к-во градусов, в зависимости от ориентации монитора.

7. Не забудь обозначить свое приложение, как DPI-совместимое (см. блок 'dpiAware' в
манифесте), иначе оконные координаты будут искусственно изменены системой и
вместо, например, 1920*1080 можно получить что-то вроде 1600*800...

Вся документация по DDA есть в MSDN, также есть хороший, хотя и не самый простой,
исходник:

DXGI desktop duplication sample
http://code.msdn.microsoft.com... e-da4c696a

Также у VNC используется DDA и их исходники открыты для изучения.

Вообще, в DDA-приложениях довольно легко что-нибудь напутать и получить, например,
искаженное изображение или левый монитор на месте правого. Или вообще черный квадрат.
Так что тестировать их надо очень тщательно: с разными разрешениями экрана и шрифтов, с
разным количеством и раскладкой мониторов (например, правый - основной, левый -
дополнительный), с поворотом мониторов, в консольных и терминальных сессиях, на виртуальных
машинах, на разных видео-картах, с включенным и отключенным 3D-ускорением и т.д.

Надеюсь, эта информация хоть чем-то поможет.
Работал с DDA давно уже, года три назад, многое забыл.
2
10 / 10 / 3
Регистрация: 20.05.2016
Сообщений: 321
12.09.2017, 22:45  [ТС] 3
Спасибо. Но есть пара вопросов.

Кажется, я переделал свой код, как вы советовали:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
static bool TakeScreenshots(int adapterInx, int outputInx)
{
    HRESULT hr;
 
    //
    // Open IDXGIFactory1.
    //
 
    IDXGIFactory1* pFactory;
    IDXGIAdapter1* pAdapter;
    IDXGIOutput* pOutput;
    IDXGIOutput1* pOutput1;
 
    hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory));
    if (FAILED(hr)) return false;
 
    // Get DXGI output.
 
    hr = pFactory->EnumAdapters1(adapterInx, &pAdapter);
    if (FAILED(hr)) return false;
 
    hr = pAdapter->EnumOutputs(outputInx, &pOutput);
    if (FAILED(hr)) return false;
 
    // Query interface for IDXGIOutput1.
    hr = pOutput->QueryInterface(__uuidof(IDXGIOutput1), reinterpret_cast<void**>(&pOutput1));
    if (FAILED(hr)) return false;
 
    //
    // Get device object.
    //
 
    D3D_FEATURE_LEVEL d3dFeatLvl;
    ID3D11Device* pDevice = nullptr;
    ID3D11DeviceContext* pImmediateContext = nullptr;
 
    hr = D3D11CreateDevice(pAdapter, D3D_DRIVER_TYPE_UNKNOWN,
        0, 0, 0, 0,
        D3D11_SDK_VERSION,
        &pDevice,
        &d3dFeatLvl,
        &pImmediateContext);
    if (FAILED(hr)) return false;
 
    // Create desktop duplication.
    IDXGIOutputDuplication* pDeskDupl;
    hr = pOutput1->DuplicateOutput(pDevice, &pDeskDupl);
    if (FAILED(hr)) return false;
 
    ///////////////////////////////////////////////////////////////////////////
 
    ID3D11Texture2D* AcquiredDesktopImage;
    IDXGIResource* DesktopResource = nullptr;
    DXGI_OUTDUPL_FRAME_INFO FrameInfo;
 
    for (int i = 0; i < 10; i++)
    {
        Console::WriteLine(i);//!
        Sleep(1);
 
        // Get new frame.
        hr = pDeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource);
        if (FAILED(hr)) return false;
 
        // Query interface for IDXGIResource.
        hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&AcquiredDesktopImage));
        DesktopResource->Release();
        if (FAILED(hr)) return false;
 
        hr = pDeskDupl->ReleaseFrame();
        if (FAILED(hr)) return false;
 
        hr = AcquiredDesktopImage->Release();
        if (FAILED(hr)) return false;
 
        HBITMAP hBmp = (HBITMAP)ExtractBitmap(AcquiredDesktopImage, pDevice);
        System::Drawing::Bitmap::FromHbitmap(IntPtr(hBmp))->Save("Screenshot" + i + ".bmp");
    }
 
    return true;
}
Работать я собираюсь только с одним выводом, так что советы 1 и 3 я могу пропустить.
Цитата Сообщение от Убежденный Посмотреть сообщение
интерфейс ID3D11Texture2D - это и есть текстура с тещущим скрином экрана.
Далее из нее содержимое копируется в промежуточную структуру (CopyResource или
CopySubresourceRegion), потом пиксели мапятся в память и после этого уже можно
работать с ними.
А что за промежуточная структура?
Мне её в ExtractBitmap передать?
0
Ушел с форума
Эксперт С++
16429 / 7393 / 1186
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
12.09.2017, 23:06 4
Цитата Сообщение от dm stark Посмотреть сообщение
Кажется, я переделал свой код
А почему в коде нигде нету вызова Release()?
Без этого ведь объекты factory, adapter, output и т.д. не удаляются. Утечка...

Цитата Сообщение от dm stark Посмотреть сообщение
А что за промежуточная структура?
ID3D11Texture2D. Но наверное, можно и без промежуточных копирований.
В моем случае это нужно было для того, чтобы перерисовывать только измененные
регионы экрана. Т.е. я из AcquireNextFrame получал IDXGIResource и запрашивал
у него ID3D11Texture2D - это исходная текстура с полным изображением экрана.
Вторая текстура у меня была создана через ID3D11Device->CreateTexture2D - в
нее я копировал из исходной структуры только измененные регионы экрана,
попутно выполняя их поворот и другие преобразования.
0
10 / 10 / 3
Регистрация: 20.05.2016
Сообщений: 321
12.09.2017, 23:57  [ТС] 5
Но я к чему это, "исправленный" код выше не работает)
Он даёт 10 пустых картинок... Функцию ExtractBitmap я не менял.

А вот по поводу копирования только изменённых регионов интересно.
Это как в dfmirage (драйвер, который использует VNC; он, кстати, у меня выдавал на Windows 7 Ultimate "кривой" скрин в плане цвета).
А как вы узнали координаты и размеры изменённых "прямоугольников" экрана?
И как можно понять, что они перевёрнуты?
Занимает ли этот процесс много времени?
0
Ушел с форума
Эксперт С++
16429 / 7393 / 1186
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
13.09.2017, 09:35 6
Цитата Сообщение от dm stark Посмотреть сообщение
Но я к чему это, "исправленный" код выше не работает)
Он даёт 10 пустых картинок...
Скорее всего, у тебя используется "не тот" Adapter или Output.
Их нужно все перебрать и отфильтровать ненужные. И брать скрины только с
подходящих Adapter/Output. В коде этого не видно.

Цитата Сообщение от dm stark Посмотреть сообщение
А как вы узнали координаты и размеры изменённых "прямоугольников" экрана?
IDXGIOutputDuplication, методы GetFrameDirtyRects и GetFrameMoveRects.

Цитата Сообщение от dm stark Посмотреть сообщение
Это как в dfmirage
Да.
Mirroring-драйвер под XP-Win7, кстати, тоже приходилось писать.
DDA по сравнению с этим - детская милая сказка

Цитата Сообщение от dm stark Посмотреть сообщение
И как можно понять, что они перевёрнуты?
IDXGIOutput, метод GetDesc. И далее см. поле Rotation структуры DXGI_OUTPUT_DESC.
0
10 / 10 / 3
Регистрация: 20.05.2016
Сообщений: 321
13.09.2017, 14:34  [ТС] 7
Вот что я имею:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
static ref class MyTestClass abstract
{
public:
 
    ref struct OutputDevice
    {
    public:
        int Index;
        String^ DeviceName;
 
        OutputDevice(int inx, String^ name)
        {
            Index = inx;
            DeviceName = name;
        }
    };
 
    ref struct VideoCard
    {
    public:
        int Index;
        String^ Description;
        array<OutputDevice^>^ Outputs;
    };
 
    static bool EnumVideoAdapters(array<VideoCard^>^% adapters)
    {
        //
        // Open IDXGIFactory.
        //
 
        IDXGIFactory1 *pFactory;
        if (FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory))))
            return false;
 
        UINT i = 0, j = 0;
        IDXGIAdapter1 *pAdapter;
        IDXGIOutput *pOutput;
 
        DXGI_ADAPTER_DESC1 adapterDesc1 = { 0 };
        DXGI_OUTPUT_DESC outputDesc = { 0 };
 
        List<VideoCard^>^ adaptersList = gcnew List<VideoCard^>();
 
        //
        // Enumerate adapters.
        //
        while (SUCCEEDED(pFactory->EnumAdapters1(i, &pAdapter)))
        {
            pAdapter->GetDesc1(&adapterDesc1);
            if (!(adapterDesc1.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) && adapterDesc1.VendorId != 0x1414 && adapterDesc1.DeviceId != 0x008c)
            {
                VideoCard^ adapter = gcnew VideoCard();
                adapter->Index = i;
                adapter->Description = gcnew String(adapterDesc1.Description);
 
                List<OutputDevice^>^ outputs = gcnew List<OutputDevice^>();
 
                //
                // Enumerate adapter outputs.
                //
                while (SUCCEEDED(pAdapter->EnumOutputs(j, &pOutput)))
                {
                    pOutput->GetDesc(&outputDesc);
                    if (outputDesc.AttachedToDesktop) 
                        outputs->Add(gcnew OutputDevice(j, gcnew String(outputDesc.DeviceName)));
                    j++;
                }
 
                adapter->Outputs = outputs->ToArray();
                adaptersList->Add(adapter);
            }
 
            i++;
        }
 
        pFactory->Release();
        adapters = adaptersList->ToArray();
    }
 
    static bool TakeScreenshots(int adapterInx, int outputInx)
    {
        HRESULT hr;
 
        //
        // Open IDXGIFactory1.
        //
 
        IDXGIFactory1* pFactory;
        IDXGIAdapter1* pAdapter;
        IDXGIOutput* pOutput;
        IDXGIOutput1* pOutput1;
 
        hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory));
        if (FAILED(hr)) return false;
 
        // Get DXGI output.
 
        hr = pFactory->EnumAdapters1(adapterInx, &pAdapter);
        if (FAILED(hr)) return false;
 
        hr = pAdapter->EnumOutputs(outputInx, &pOutput);
        if (FAILED(hr)) return false;
 
        // Query interface for IDXGIOutput1.
        hr = pOutput->QueryInterface(__uuidof(IDXGIOutput1), reinterpret_cast<void**>(&pOutput1));
        if (FAILED(hr)) return false;
 
        //
        // Get device object.
        //
 
        D3D_FEATURE_LEVEL d3dFeatLvl;
        ID3D11Device* pDevice = nullptr;
        ID3D11DeviceContext* pImmediateContext = nullptr;
 
        hr = D3D11CreateDevice(pAdapter, D3D_DRIVER_TYPE_UNKNOWN,
            0, 0, 0, 0,
            D3D11_SDK_VERSION,
            &pDevice,
            &d3dFeatLvl,
            &pImmediateContext);
        if (FAILED(hr)) return false;
 
        // Create desktop duplication.
        IDXGIOutputDuplication* pDeskDupl;
        hr = pOutput1->DuplicateOutput(pDevice, &pDeskDupl);
        if (FAILED(hr)) return false;
 
        ///////////////////////////////////////////////////////////////////////////
 
        ID3D11Texture2D* AcquiredDesktopImage;
        IDXGIResource* DesktopResource = nullptr;
        DXGI_OUTDUPL_FRAME_INFO FrameInfo;
 
        for (int i = 0; i < 10; i++)
        {
            Console::WriteLine(i);//!
            Sleep(5);
 
            // Get new frame.
            hr = pDeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource);
            if (FAILED(hr)) return false;
 
            // Query interface for IDXGIResource.
            hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&AcquiredDesktopImage));
            DesktopResource->Release();
            if (FAILED(hr)) return false;
 
            hr = pDeskDupl->ReleaseFrame();
            if (FAILED(hr)) return false;
 
            hr = AcquiredDesktopImage->Release();
            if (FAILED(hr)) return false;
 
            //
            HBITMAP hBmp = (HBITMAP)ExtractBitmap(AcquiredDesktopImage, pDevice);
            System::Drawing::Bitmap::FromHbitmap(IntPtr(hBmp))->Save("Screenshot" + i + ".bmp");
            //
        }
 
        pFactory->Release();
        pDevice->Release();
        pOutput1->Release();
        pOutput->Release();
        pDeskDupl->Release();
        pDevice->Release();
 
        return true;
    }
};
 
 
int main(array<System::String^> ^args)
{
    array<MyTestClass::VideoCard^>^ adapters;
    MyTestClass::EnumVideoAdapters(adapters);
 
    for each (MyTestClass::VideoCard^ adapter in adapters)
    {
        Console::WriteLine("Adapter " + adapter->Index + "\nDescrition: " + adapter->Description + "\nOutputs:\n");
        for each (MyTestClass::OutputDevice^ output in adapter->Outputs)
            Console::WriteLine("\tOutput " + output->Index + "\n\tName: " + output->DeviceName);
        Console::WriteLine();
    }
 
    int adapterInx, outputInx;
    Console::Write("Enter adapter and output device index: ");
    String^ input = Console::ReadLine();
 
    Console::WriteLine(MyTestClass::TakeScreenshots(Convert::ToInt32(input->Remove(0, 1)), Convert::ToInt32(input->Remove(1, 1))) ? "Successfully!" : "Failed!");
    Console::ReadKey();
    return 0;
}
Результат - 10 пустых скринов...

Цитата Сообщение от Убежденный Посмотреть сообщение
Mirroring-драйвер под XP-Win7, кстати, тоже приходилось писать.
DDA по сравнению с этим - детская милая сказка
Оо. Это уж да.
0
Ушел с форума
Эксперт С++
16429 / 7393 / 1186
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
13.09.2017, 15:00 8
Вижу, что в EnumVideoAdapters появилась какая-то логика определения нужных адаптеров и output-ов.
На вид все логично. Но в TakeScreenshots эти найденные объекты же нигде не используются...
0
10 / 10 / 3
Регистрация: 20.05.2016
Сообщений: 321
13.09.2017, 15:04  [ТС] 9
Да. Мне нужно это разделение.
Адаптер и устройство вывода должен выбрать пользователь. Мы просто запоминаем индексы этих устройств.
Ну в принципе же нет разницы? Первый метод просто проходится по всем индексам и находит все видеоадаптеры и их output'ы. Всё закрывает. А второй метод уже имея определённые индексы (у меня 0,0) абсолютно таким же способом (через IDXGIFactory1) находит нужный адаптер и output.

Может, проблема всё таки в ExtractBitmap?
0
Ушел с форума
Эксперт С++
16429 / 7393 / 1186
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
13.09.2017, 16:19 10
Цитата Сообщение от dm stark Посмотреть сообщение
Ну в принципе же нет разницы?
А ты в TakeScreenshots запроси свойства (GetDesc) адаптера и Output и выведи их в консоль.
Там должны быть названия твоей видео-карты. Если нет - adapter и/или output выбраны не те.

Вот это место выглядит подозрительно:
C++
1
2
3
4
5
6
hr = D3D11CreateDevice(pAdapter, D3D_DRIVER_TYPE_UNKNOWN,
            0, 0, 0, 0,
            D3D11_SDK_VERSION,
            &pDevice,
            &d3dFeatLvl,
            &pImmediateContext);
А где массив значений D3D_FEATURE_LEVEL (5-ый и 6-ой параметры)?
Вот что пишет MSDN по поводу D3D11CreateDevice:
If the Direct3D 11.1 runtime is present on the computer and pFeatureLevels is set to NULL, this function won't create a D3D_FEATURE_LEVEL_11_1 device. To create a D3D_FEATURE_LEVEL_11_1 device, you must explicitly provide a D3D_FEATURE_LEVEL array that includes D3D_FEATURE_LEVEL_11_1. If you provide a D3D_FEATURE_LEVEL array that contains D3D_FEATURE_LEVEL_11_1 on a computer that doesn't have the Direct3D 11.1 runtime installed, this function immediately fails with E_INVALIDARG.
И еще вот это ну очень-очень подозрительно:
C++
1
2
3
4
5
hr = AcquiredDesktopImage->Release();
if (FAILED(hr)) return false;
 
//
HBITMAP hBmp = (HBITMAP)ExtractBitmap(AcquiredDesktopImage, pDevice);
Т.е. сначала ты делаешь Release объекту AcquiredDesktopImage.
Вероятно, он в этот момент уничтожается. А дальше идет
попытка его использования в функции ExtractBitmap...

Вообще, вместо ручного вызова Release я бы посоветовал RAII-обертки, которые делают это автоматически.
Например, в ATL есть класс CComPtr. И если у тебя срабатывает какой-либо из FAILED(hr), не делай сразу
return false, а выведи сначала в лог код ошибки из hr - это поможет найти причину.
1
10 / 10 / 3
Регистрация: 20.05.2016
Сообщений: 321
13.09.2017, 20:39  [ТС] 11
Так изменил цикл:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
        System::Diagnostics::Stopwatch^ s = gcnew System::Diagnostics::Stopwatch();
        for (int i = 1; i <= 5; i++)
        {
            s->Restart();
            Sleep(5);
 
            if (AcquiredDesktopImage)
            {
                hr = AcquiredDesktopImage->Release();
                if (FAILED(hr)) return false;
            }
 
            // Get new frame.
            hr = pDeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource);
            if (FAILED(hr)) return false;
 
            // Query interface for IDXGIResource.
            hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&AcquiredDesktopImage));
            DesktopResource->Release();
            if (FAILED(hr)) return false;
 
            //
            HBITMAP hBmp = (HBITMAP)ExtractBitmap(AcquiredDesktopImage, pDevice);
            s->Stop();
            Console::WriteLine(i + " - " + s->ElapsedMilliseconds + " ms");
            System::Drawing::Bitmap::FromHbitmap(IntPtr(hBmp))->Save("Screenshot" + i + ".bmp");
            //
 
            hr = pDeskDupl->ReleaseFrame();
            if (FAILED(hr)) return false;
        }
Всё работает, но иногда первый скрин получается пустым.
Спасибо вам большое!

Единственно, ExtractBitmap выполняется слишком долго. 160 мс!
Я ради скорости и использую DDA. Это даже медленный BitBlt быстрее в разы (запасной вариант получения скриншотов).

Может быть не делать вовсе преобразования к managment Bitmap?
Как мне эти изменённые треугольники из текстуры вычленить, и какой у них будет тип?
Могу ли я их по сети отправить с их координатами соответственно и далее наклеить прямо на картинку на окне?
Что для этого использовать?
0
Ушел с форума
Эксперт С++
16429 / 7393 / 1186
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
14.09.2017, 09:44 12
Цитата Сообщение от dm stark Посмотреть сообщение
ExtractBitmap выполняется слишком долго. 160 мс!
Я ради скорости и использую DDA. Это даже медленный BitBlt быстрее в разы (запасной вариант получения скриншотов).
Скорее всего, основная задержка в ExtractBitmap как раз связана с GDI, а не с DDA
Там же в самом низу сидят старые-добрые (+медленные) GetDC, BitBlt и т.п.
У меня DDA давал на FullHD где-то 50FPS при загрузке проца всего 7-8% (на Core i5-2500).
Это на физической машине с настоящей видеокартой и т.п. На виртуалках, где нет
"настоящего" аппаратного ускорения, все заметно хуже (но все равно быстрее, чем GDI).

Цитата Сообщение от dm stark Посмотреть сообщение
Как мне эти изменённые треугольники из текстуры вычленить, и какой у них будет тип?
Если честно, без понятия. Дальше получения текстуры и пикселей я не заходил.
Тут надо уже, наверное, копать в сторону интерфейсов DirectX 11.

Цитата Сообщение от dm stark Посмотреть сообщение
Могу ли я их по сети отправить с их координатами соответственно и далее наклеить прямо на картинку на окне?
Что для этого использовать?
Да любую сетевую библиотеку. Например, под C++ очень хороша Boost.Asio.
0
10 / 10 / 3
Регистрация: 20.05.2016
Сообщений: 321
14.09.2017, 10:11  [ТС] 13
Да я не о проблеме передачи по сети. Я имею ввиду, не в курсе ли вы как ID3D11Texture2D наклеить на окно?
0
Ушел с форума
Эксперт С++
16429 / 7393 / 1186
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
14.09.2017, 13:01 14
dm stark, тут мои познания в DXGI/DirectX заканчиваются.
Попробуй спросить на форуме C++ или здесь: https://www.cyberforum.ru/directx/
0
10 / 10 / 3
Регистрация: 20.05.2016
Сообщений: 321
14.09.2017, 14:48  [ТС] 15
Хорошо. Спасибо.

Позже опубликую полное решение.
0
10 / 10 / 3
Регистрация: 20.05.2016
Сообщений: 321
22.09.2017, 16:09  [ТС] 16
По поводу эффективного выполнения..

Попробовал использование GetFrameDirtyRects:
C++
1
2
3
4
5
6
7
8
9
10
11
12
            // Get dirty rects.
 
            RECT* buf = new RECT[1024];
            UINT realsize;
            pDeskDupl->GetFrameDirtyRects(1024 * sizeof RECT, buf, &realsize);
 
            for (int i = 0; i < realsize; i)
            {
                RECT rect = *(buf + i);
                Console::WriteLine("RECT " + rect.left + " " + rect.top + " " + rect.right + " " + rect.bottom);
                i += sizeof RECT;
            }
Результат на скрине. Правильно я всё сделал?

Да и вообще в чём разница между GetFrameDirtyRects и GetFrameMoveRects? Первый определяет изменённые области на экране (включается ли сюда движения курсора мышки?), а второй - перемещённые прямоугольники. Вот что ещё за перемещённые прямоугольники?

Достаточно ли использования одного из методов или нужно использовать оба для полноценной реализации?

Вот на счёт ускорения ExtractBitmap (до 15-17 мс по утверждению автора; сам пока не тестировал):
Миниатюры
Desktop Duplication API - Получение серии скриншотов с экрана  
0
Ушел с форума
Эксперт С++
16429 / 7393 / 1186
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
22.09.2017, 16:39 17
Цитата Сообщение от dm stark Посмотреть сообщение
Правильно я всё сделал?
Похоже, что нет
Скорее всего, нужно вместо 'i += sizeof RECT;' делать просто '++i'.

Цитата Сообщение от dm stark Посмотреть сообщение
Да и вообще в чём разница между GetFrameDirtyRects и GetFrameMoveRects? Первый определяет изменённые области на экране (включается ли сюда движения курсора мышки?), а второй - перемещённые прямоугольники. Вот что ещё за перемещённые прямоугольники?
Берешь окно за край и тянешь с одного места в другое. В таких случаях система
обычно и генерирует "перемещение". То есть, одно событие move вместо серии dirty.
Это оптимизация.

Цитата Сообщение от dm stark Посмотреть сообщение
Достаточно ли использования одного из методов или нужно использовать оба для полноценной реализации?
Нужно использовать оба. Иначе потеряешь какие-то области экрана, которые попали в move.
И насколько я помню, в документации рекомендуется сначала обрабатывать move, а потом dirty.
Или наоборот...
0
10 / 10 / 3
Регистрация: 20.05.2016
Сообщений: 321
22.09.2017, 17:41  [ТС] 18
Цитата Сообщение от Убежденный Посмотреть сообщение
И насколько я помню, в документации рекомендуется сначала обрабатывать move, а потом dirty.
Верно.

Цитата Сообщение от Убежденный Посмотреть сообщение
Похоже, что нет
Скорее всего, нужно вместо 'i += sizeof RECT;' делать просто '++i'.
И в самом деле. Результатов стало намного больше, что выглядит куда правдоподобнее)

Вот добавил GetFrameMoveRects:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
            // Get moved rects.
 
            DXGI_OUTDUPL_MOVE_RECT* bufMoved = new DXGI_OUTDUPL_MOVE_RECT[1024];
            UINT realsize = 0;
            pDeskDupl->GetFrameMoveRects(1024 * sizeof DXGI_OUTDUPL_MOVE_RECT, bufMoved, &realsize);
 
            for (int i = 0; i < realsize; i++)
            {
                DXGI_OUTDUPL_MOVE_RECT rect = *(bufMoved + i);
                Console::WriteLine("\tMOVED RECT " + rect.SourcePoint.x + " & " + rect.SourcePoint.y + " TO " + 
                    rect.DestinationRect.left + " " + rect.DestinationRect.top + " " + rect.DestinationRect.right + 
                    " " + rect.DestinationRect.bottom);
            }
 
            // Get dirty rects.
 
            RECT* buf = new RECT[1024];
            realsize = 0;
            pDeskDupl->GetFrameDirtyRects(1024 * sizeof RECT, buf, &realsize);
 
            for (int i = 0; i < realsize; i++)
            {
                RECT rect = *(buf + i);
                Console::WriteLine("\tDIRTY RECT " + rect.left + " " + rect.top + " " + rect.right + " " + rect.bottom);
            }
Почему то ни одного значения "moved rect" не отобразилось в серии из 10 замеров.
Я и консоль пробовал двигать, и другие окна.
Много выходит "dirty rects" в значении "0 0 0 0". Это игнорировать?

Добавлено через 43 минуты
Я вот подумал ещё каким образом мне потом полученные прямоугольники вытащить.
Я всё равно потом буду скрин преобразовывать в management Bitmap для его отображения (использую Emgu CV, а он работает только с management Bitmap при масштабировании с использованием бикубической интерполяции).
Может мне эти области вытащить уже из Bitmap? Методом Clone. Надеюсь это не будет долго.

Но вообще, конечно, может быть быстрей просто отправить цельный скрин, чем вытаскивать, а потом налепливать эти прямоугольники?
0
10 / 10 / 3
Регистрация: 20.05.2016
Сообщений: 321
24.09.2017, 23:38  [ТС] 19
Вообще, как мне добиться получения результатов в списке "moved rects". Какой то объект должен двигаться на экране?
Могут ли "moved rects" и "dirty rects" пересекаться?
0
0 / 0 / 0
Регистрация: 03.11.2017
Сообщений: 1
07.11.2017, 23:10 20
Товарищи, в итоге получился рабочий результат?
Поделитесь исходниками плиз.
У меня черный экран грабится в BMP =(
Я в Delphi проект делаю, выложу сюда потом исходники на Delphi, если кому нужно.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
07.11.2017, 23:10

Написать программу, которая будет проверять цвет пикселя без скриншотов экрана
Возможно ли на Python написать программу, которая будет проверять цвет пикселя без скриншотов...

API Вконтакте и загрузка большого количества фотографий (Desktop App)
Задача: необходимо загружать в альбомы группы большое количество фотографий с использованием API...

Web desktop VK API или расставить все точки над И
Доброго времени суток, товарищи. Открываю для себя новый и чудесный мир веб программирования и...

Duplication entry
Добрый день! Имею следующее(в процедуре) CREATE PROCEDURE insertIfNotExist(key in varchar,...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.