Разобрался, наконец, когда нашел это .Документация по API на MSDN довольно расплывчата по этому поводу, возможно, потому что сама она восходит к тому времени, но похоже, что все функции буфера обмена используют систему выделения памяти в стиле Windows 3.x (GlobalAlloc
и т. Д.).
Для системного буфера обмена имеет смысл выставлять разделяемую память приложению напрямую, а не ОС, вынужденной копировать данные во внутренние буферы.Но функциональность буфера обмена восходит достаточно далеко, так что более новых схем на основе файла подкачки для общей памяти не существовало, поэтому они должны были использовать GlobalAlloc
память.Когда появилась 32-битная Windows, имело больше смысла просто эмулировать этот механизм, а не нарушать существующий код приложения.
Я сильно подозреваю, что по аналогичным причинам большинство дескрипторов GDI на самом деле также являются дескрипторами GlobalAlloc
, ивот почему вы можете передать возврат из CreateCompatibleBitmap
в буфер обмена.В отличие от этого, CreateDIBSection
действительно не полностью использует распределение в старом стиле, что очевидно из того факта, что вы можете сказать ему хранить биты в отображении файла.(Я подозреваю, что дескриптор, который он возвращает, все еще от GlobalAlloc
, но что блок, выделенный таким образом, содержит прямой указатель на виртуальную память для данных изображения, и SetClipboardData
проверяет это, потому что это очевидная «ошибка».)
Так что я все исправил, просто позволив CreateDIBSection
выделять, где он хочет, потому что так или иначе невозможно передать это на SetClipboardData
в любом случае, а затем делать это, когда я хочу отправитьв буфер обмена:
void CScreenshot::SendToClipboard( void )
{
HGLOBAL hClipboardDib = GlobalAlloc( GMEM_MOVEABLE | GMEM_SHARE, cbDib );
if( !hClipboardDib )
throw 0;
void *pClipboardDib = GlobalLock( hClipboardDib );
memcpy( pClipboardDib, &BitmapInfo, sizeof(BITMAPINFOHEADER) );
memcpy( (BITMAPINFOHEADER*)pClipboardDib+1, pBits, BitmapInfo.bmiHeader.biSizeImage );
GlobalUnlock( hClipboardDib );
if( !OpenClipboard( NULL ) )
{
GlobalFree( hClipboardDib );
throw 0;
}
EmptyClipboard();
SetClipboardData( CF_DIB, hClipboardDib );
CloseClipboard();
}
К сожалению, я должен сделать избыточную копию здесь, но с другой стороны, я сильно подозреваю, что приложение, читающее буфер обмена, увидит ту же копию, а не Windowsлюбое дальнейшее внутреннее копирование.
Если я хочу быть наркоманом общей эффективности, я подозреваю , что дескриптор, возвращенный из CreateCompatibleBitmap
, может быть использован при вызове GlobalLock
, а затем выможет получить биты напрямую, не вызывая копию в CScreenshot::SendToClipboard
, потому что вы можете просто передать ее прямо в SetClipboardData
.Тем не менее, я также сильно подозреваю, что это будет недокументированное поведение (но поправьте меня, если я ошибаюсь!), Так что это довольно плохая идея.Вам также придется отслеживать, передали ли вы это в буфер обмена или нет, и если вы это сделали, не вызывайте DeleteObject
для него.Но я не уверен.Я также подозреваю, что SetClipboardData
в любом случае придется скопировать его, потому что он, вероятно, не выделен с GMEM_SHARE
.
Спасибо комментаторам за то, что я немного приблизился к его выяснению.