Posted: 29 Aug 2008, 23:43
Okay, here we go...
1) Start the drag'n'drop operation:
2) Add this to your implementation of IDataObject::SetData(). It draws the item count label on top of the drag image.
3) Add this small hack to your implementation of IDropTarget::DragEnter():
4) Add this to your implementation of IDropSource::GiveFeedback(). It makes drop descriptions work.
5) Do this in your DI_GETDRAGIMAGE message handler:
6) CreateVistaOLEDragImage():
7) CreateThumbnail():
8) Instanciate the IWICImagingFactory. I do this in the constructor of my control class.
That's it. Easy, isn't it? :buck:
1) Start the drag'n'drop operation:
Code: Select all
IDataObject* pDataObject = NULL;
// TODO: Create an IDataObject instance here and don't forget to tell it the item count number to display.
// TODO: Set a flag 'useItemCountLabelHack' if the item count number you want to display is >1. See step 3 for the hack.
IDragSourceHelper* pDragSourceHelper = NULL;
CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_ALL, IID_IDragSourceHelper, reinterpret_cast<LPVOID*>(&pDragSourceHelper));
if(pDragSourceHelper) {
if(flags.usingThemes && IsWindowsVistaOrNewer()) {
IDragSourceHelper2* pDragSourceHelper2 = NULL;
pDragSourceHelper->QueryInterface(IID_IDragSourceHelper2, reinterpret_cast<LPVOID*>(&pDragSourceHelper2));
if(pDragSourceHelper2) {
pDragSourceHelper2->SetFlags(DSH_ALLOWDROPDESCRIPTIONTEXT);
// this was the only place we actually use IDragSourceHelper2
pDragSourceHelper->Release();
pDragSourceHelper = static_cast<IDragSourceHelper*>(pDragSourceHelper2);
}
}
HRESULT hr = pDragSourceHelper->InitializeFromWindow(hWnd, &mousePosition, pDataObject);
if(FAILED(hr)) {
/* This happens if full window dragging is deactivated. Actually, InitializeFromWindow() contains a
fallback mechanism for this case. This mechanism retrieves the passed window's class name and
builds the drag image using TVM_CREATEDRAGIMAGE if it's SysTreeView32, LVM_CREATEDRAGIMAGE if
it's SysListView32 and so on. Our class name is ExplorerListView[U|A], so we're doomed.
So how can we have drag images anyway? Well, we use a very ugly hack: We temporarily activate
full window dragging. */
BOOL fullWindowDragging;
SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &fullWindowDragging, 0);
if(!fullWindowDragging) {
SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, NULL, 0);
pDragSourceHelper->InitializeFromWindow(hWnd, &mousePosition, pDataObject);
SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, FALSE, NULL, 0);
}
}
if(pDragSourceHelper) {
pDragSourceHelper->Release();
}
}
HRESULT hr = DoDragDrop(pDataObject, pDragSource, supportedEffects, reinterpret_cast<LPDWORD>(pPerformedEffects));
Code: Select all
// of course this must be done before storing the data internally
if(properties.numberOfItemsToDisplay > 1) {
if(pFormat->cfFormat == static_cast<CLIPFORMAT>(RegisterClipboardFormat(TEXT("DragImageBits")))) {
if(pData->hGlobal) {
LPTSTR pBuffer = ConvertIntegerToString(properties.numberOfItemsToDisplay);
if(pBuffer) {
CT2W converter(pBuffer);
LPWSTR pLabelText = converter;
if(pLabelText) {
LPVOID pDragStuff = GlobalLock(pData->hGlobal);
SHDRAGIMAGE dragImage = {0};
CopyMemory(&dragImage, pDragStuff, sizeof(SHDRAGIMAGE));
LPRGBQUAD pDragImageBits = reinterpret_cast<LPRGBQUAD>(reinterpret_cast<LPBYTE>(pDragStuff) + sizeof(SHDRAGIMAGE));
CTheme themingEngine;
themingEngine.OpenThemeData(NULL, VSCLASS_DRAGDROP);
if(!themingEngine.IsThemeNull()) {
CDC memoryDC;
memoryDC.CreateCompatibleDC();
// calculate size and position
DWORD textDrawStyle = DT_SINGLELINE;
WTL::CRect textRectangle;
themingEngine.GetThemeTextExtent(memoryDC, DD_TEXTBG, 1, pLabelText, -1, textDrawStyle | DT_CALCRECT, NULL, &textRectangle);
MARGINS margins = {0};
themingEngine.GetThemeMargins(memoryDC, DD_TEXTBG, 1, TMT_CONTENTMARGINS, &textRectangle, &margins);
WTL::CRect labelRectangle = textRectangle;
labelRectangle.left -= margins.cxLeftWidth;
labelRectangle.right += margins.cxRightWidth;
labelRectangle.top -= margins.cyTopHeight;
labelRectangle.bottom += margins.cyBottomHeight;
labelRectangle.OffsetRect(-labelRectangle.left, -labelRectangle.top);
int xLabelStart = (dragImage.sizeDragImage.cx - labelRectangle.Width()) / 2;
int yLabelStart = (dragImage.sizeDragImage.cy - labelRectangle.Height()) / 2;
BITMAPINFO bitmapInfo = {0};
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = labelRectangle.Width();
bitmapInfo.bmiHeader.biHeight = labelRectangle.Height();
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
LPRGBQUAD pLabelBits = NULL;
CBitmap dragImageBMP;
dragImageBMP.CreateDIBSection(memoryDC, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<LPVOID*>(&pLabelBits), NULL, NULL);
ATLASSERT(pLabelBits);
if(pLabelBits) {
// for correct alpha-blending the label bitmap must have the correct background
LPRGBQUAD pLabelPixel = pLabelBits;
LPRGBQUAD pDragImagePixel = pDragImageBits;
pDragImagePixel += yLabelStart * dragImage.sizeDragImage.cx;
for(int y = 0; y < labelRectangle.Height(); ++y, pDragImagePixel += dragImage.sizeDragImage.cx, pLabelPixel += labelRectangle.Width()) {
CopyMemory(pLabelPixel, pDragImagePixel + xLabelStart, labelRectangle.Width() * sizeof(RGBQUAD));
}
HBITMAP hPreviousBitmap = memoryDC.SelectBitmap(dragImageBMP);
// draw the background
themingEngine.DrawThemeBackground(memoryDC, DD_TEXTBG, 1, &labelRectangle, NULL);
// draw the text
textRectangle.left = labelRectangle.left + margins.cxLeftWidth;
textRectangle.right = labelRectangle.right - margins.cxRightWidth;
textRectangle.top = labelRectangle.top + margins.cyTopHeight;
textRectangle.bottom = labelRectangle.bottom - margins.cyBottomHeight;
themingEngine.DrawThemeText(memoryDC, DD_TEXTBG, 1, pLabelText, -1, textDrawStyle, 0, &textRectangle);
// insert the label with the drag image
pLabelPixel = pLabelBits;
pDragImagePixel = pDragImageBits;
pDragImagePixel += yLabelStart * dragImage.sizeDragImage.cx;
POINT pt = {0};
for(pt.y = 0; pt.y < labelRectangle.Height(); ++pt.y, pDragImagePixel += dragImage.sizeDragImage.cx, pLabelPixel += labelRectangle.Width()) {
LPRGBQUAD pDst = pDragImagePixel + xLabelStart;
LPRGBQUAD pSrc = pLabelPixel;
for(pt.x = 0; pt.x < labelRectangle.Width(); ++pt.x, ++pDst, ++pSrc) {
if(pSrc->rgbReserved == 0x00) {
// TODO: Why do we need to correct the alpha channel here?
if(textRectangle.PtInRect(pt)) {
pSrc->rgbReserved = 0xFF;
}
}
*pDst = *pSrc;
}
}
memoryDC.SelectBitmap(hPreviousBitmap);
}
}
GlobalUnlock(pData->hGlobal);
}
SECUREFREE(pBuffer);
}
}
}
}
Code: Select all
pDropTargetHelper->DragEnter(*this, pDataObject, &mousePosition, *pEffect);
if(useItemCountLabelHack) {
pDropTargetHelper->DragLeave();
pDropTargetHelper->DragEnter(*this, pDataObject, &mousePosition, *pEffect);
useItemCountLabelHack = FALSE;
}
Code: Select all
BOOL useDefaultCursors = TRUE;
if(flags.usingThemes && IsWindowsVistaOrNewer()) {
// pDataObject is the IDataObject passed to DoDragDrop
ATLASSUME(pDataObject);
BOOL useDropDescriptionHack = FALSE;
FORMATETC format = {0};
format.cfFormat = static_cast<CLIPFORMAT>(RegisterClipboardFormat(TEXT("UsingDefaultDragImage")));
format.dwAspect = DVASPECT_CONTENT;
format.lindex = -1;
format.tymed = TYMED_HGLOBAL;
STGMEDIUM medium = {0};
if(SUCCEEDED(pDataObject->GetData(&format, &medium))) {
if(medium.hGlobal) {
useDropDescriptionHack = *reinterpret_cast<LPBOOL>(GlobalLock(medium.hGlobal));
GlobalUnlock(medium.hGlobal);
}
ReleaseStgMedium(&medium);
}
if(useDropDescriptionHack) {
// this will make drop descriptions work
format.cfFormat = static_cast<CLIPFORMAT>(RegisterClipboardFormat(TEXT("DragWindow")));
format.dwAspect = DVASPECT_CONTENT;
format.lindex = -1;
format.tymed = TYMED_HGLOBAL;
if(SUCCEEDED(pDataObject->GetData(&format, &medium))) {
if(medium.hGlobal) {
// WM_USER + 1 (with wParam = 0 and lParam = 0) hides the drag image
#define WM_SETDROPEFFECT WM_USER + 2 // (wParam = DCID_*, lParam = 0)
#define WM_INVALIDATEDRAGIMAGE WM_USER + 3 // (wParam = 0, lParam = 0)
typedef enum DROPEFFECTS
{
DCID_NULL = 0,
DCID_NO = 1,
DCID_MOVE = 2,
DCID_COPY = 3,
DCID_LINK = 4,
DCID_MAX = 5
} DROPEFFECTS;
HWND hWndDragWindow = *reinterpret_cast<HWND*>(GlobalLock(medium.hGlobal));
GlobalUnlock(medium.hGlobal);
DROPEFFECTS dropEffect = DCID_NULL;
switch(effect) {
case DROPEFFECT_NONE:
dropEffect = DCID_NO;
break;
case DROPEFFECT_COPY:
dropEffect = DCID_COPY;
break;
case DROPEFFECT_MOVE:
dropEffect = DCID_MOVE;
break;
case DROPEFFECT_LINK:
dropEffect = DCID_LINK;
break;
}
SendMessage(hWndDragWindow, WM_SETDROPEFFECT, dropEffect, 0);
}
ReleaseStgMedium(&medium);
}
SetCursor(static_cast<HCURSOR>(LoadImage(NULL, MAKEINTRESOURCE(OCR_NORMAL), IMAGE_CURSOR, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE | LR_SHARED)));
useDefaultCursors = FALSE;
}
}
return (useDefaultCursors ? DRAGDROP_S_USEDEFAULTCURSORS : S_OK);
Code: Select all
BOOL succeeded = FALSE;
BOOL useVistaDragImage = FALSE;
if(flags.usingThemes && IsWindowsVistaOrNewer()) {
succeeded = CreateVistaOLEDragImage(pDraggedItems, reinterpret_cast<LPSHDRAGIMAGE>(lParam));
useVistaDragImage = succeeded;
}
if(!succeeded) {
// use a legacy drag image as fallback
succeeded = CreateLegacyOLEDragImage(pDraggedItems, reinterpret_cast<LPSHDRAGIMAGE>(lParam));
}
if(succeeded && IsWindowsVistaOrNewer()) {
FORMATETC format = {0};
format.cfFormat = static_cast<CLIPFORMAT>(RegisterClipboardFormat(TEXT("UsingDefaultDragImage")));
format.dwAspect = DVASPECT_CONTENT;
format.lindex = -1;
format.tymed = TYMED_HGLOBAL;
STGMEDIUM medium = {0};
medium.tymed = TYMED_HGLOBAL;
medium.hGlobal = GlobalAlloc(GHND, sizeof(BOOL));
if(medium.hGlobal) {
LPBOOL pUseVistaDragImage = reinterpret_cast<LPBOOL>(GlobalLock(medium.hGlobal));
*pUseVistaDragImage = useVistaDragImage;
GlobalUnlock(medium.hGlobal);
// pDataObject is the IDataObject passed to DoDragDrop
pDataObject->SetData(&format, &medium, TRUE);
}
}
// TODO: Why do we have to return FALSE to have the set offset not ignored if a Vista drag image is used?
return succeeded && !useVistaDragImage;
Code: Select all
BOOL succeeded = FALSE;
CTheme themingEngine;
themingEngine.OpenThemeData(NULL, VSCLASS_DRAGDROP);
if(themingEngine.IsThemeNull()) {
// FIXME: What should we do here?
ATLASSERT(FALSE && "Current theme does not define the \"DragDrop\" class.");
} else {
// retrieve the drag image's size
CDC memoryDC;
memoryDC.CreateCompatibleDC();
themingEngine.GetThemePartSize(memoryDC, DD_IMAGEBG, 1, NULL, TS_TRUE, &pDragImage->sizeDragImage);
MARGINS margins = {0};
themingEngine.GetThemeMargins(memoryDC, DD_IMAGEBG, 1, TMT_CONTENTMARGINS, NULL, &margins);
pDragImage->sizeDragImage.cx -= margins.cxLeftWidth + margins.cxRightWidth;
pDragImage->sizeDragImage.cy -= margins.cyTopHeight + margins.cyBottomHeight;
}
ATLASSERT(pDragImage->sizeDragImage.cx > 0);
ATLASSERT(pDragImage->sizeDragImage.cy > 0);
// create target bitmap
BITMAPINFO bitmapInfo = {0};
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = pDragImage->sizeDragImage.cx;
bitmapInfo.bmiHeader.biHeight = -pDragImage->sizeDragImage.cy;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
LPRGBQUAD pDragImageBits = NULL;
pDragImage->hbmpDragImage = CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<LPVOID*>(&pDragImageBits), NULL, 0);
HIMAGELIST hSourceImageList = (cachedSettings.hHighResImageList ? cachedSettings.hHighResImageList : cachedSettings.hImageList);
if(!hSourceImageList) {
// report success, although we've an empty drag image
return TRUE;
}
IImageList2* pImgLst = NULL;
HMODULE hMod = LoadLibrary(TEXT("comctl32.dll"));
if(hMod) {
typedef HRESULT WINAPI HIMAGELIST_QueryInterfaceFn(HIMAGELIST, REFIID, LPVOID*);
HIMAGELIST_QueryInterfaceFn* pfnHIMAGELIST_QueryInterface = reinterpret_cast<HIMAGELIST_QueryInterfaceFn*>(GetProcAddress(hMod, "HIMAGELIST_QueryInterface"));
if(pfnHIMAGELIST_QueryInterface) {
pfnHIMAGELIST_QueryInterface(hSourceImageList, IID_IImageList2, reinterpret_cast<LPVOID*>(&pImgLst));
}
FreeLibrary(hMod);
}
if(!pImgLst) {
IImageList* p = reinterpret_cast<IImageList*>(hSourceImageList);
p->QueryInterface(IID_IImageList2, reinterpret_cast<LPVOID*>(&pImgLst));
}
ATLASSUME(pImgLst);
if(pImgLst) {
// set this to the number of dragged items
int numberOfItems = ...;
ATLASSERT(numberOfItems > 0);
// don't display more than 5 (10) thumbnails
numberOfItems = min(numberOfItems, (hSourceImageList == cachedSettings.hHighResImageList ? 5 : 10));
int cx = 0;
int cy = 0;
pImgLst->GetIconSize(&cx, &cy);
SIZE thumbnailSize;
thumbnailSize.cy = pDragImage->sizeDragImage.cy - 3 * (numberOfItems - 1);
if(thumbnailSize.cy < 8) {
// don't get smaller than 8x8 thumbnails
numberOfItems = (pDragImage->sizeDragImage.cy - 8) / 3 + 1;
thumbnailSize.cy = pDragImage->sizeDragImage.cy - 3 * (numberOfItems - 1);
}
thumbnailSize.cx = thumbnailSize.cy;
int thumbnailBufferSize = thumbnailSize.cx * thumbnailSize.cy * sizeof(RGBQUAD);
LPRGBQUAD pThumbnailBits = reinterpret_cast<LPRGBQUAD>(HeapAlloc(GetProcessHeap(), 0, thumbnailBufferSize));
ATLASSERT(pThumbnailBits);
if(pThumbnailBits) {
for each dragged item until 'numberOfItems' is reached:
{
// get the item's icon
int icon = ...;
pImgLst->ForceImagePresent(icon, ILFIP_ALWAYS);
HICON hIcon = NULL;
pImgLst->GetIcon(icon, ILD_TRANSPARENT, &hIcon);
ATLASSERT(hIcon);
if(hIcon) {
// finally create the thumbnail
ZeroMemory(pThumbnailBits, thumbnailBufferSize);
HRESULT hr = CreateThumbnail(hIcon, thumbnailSize, pThumbnailBits, TRUE);
DestroyIcon(hIcon);
if(FAILED(hr)) {
break;
}
// add the thumbail to the drag image keeping the alpha channel intact
if(i == 0) {
LPRGBQUAD pDragImagePixel = pDragImageBits;
LPRGBQUAD pThumbnailPixel = pThumbnailBits;
for(int scanline = 0; scanline < thumbnailSize.cy; ++scanline, pDragImagePixel += pDragImage->sizeDragImage.cx, pThumbnailPixel += thumbnailSize.cx) {
CopyMemory(pDragImagePixel, pThumbnailPixel, thumbnailSize.cx * sizeof(RGBQUAD));
}
} else {
LPRGBQUAD pDragImagePixel = pDragImageBits;
LPRGBQUAD pThumbnailPixel = pThumbnailBits;
pDragImagePixel += 3 * i * pDragImage->sizeDragImage.cx;
for(int scanline = 0; scanline < thumbnailSize.cy; ++scanline, pDragImagePixel += pDragImage->sizeDragImage.cx) {
LPRGBQUAD p = pDragImagePixel + 2 * i;
for(int x = 0; x < thumbnailSize.cx; ++x, ++p, ++pThumbnailPixel) {
// merge the pixels
p->rgbRed = pThumbnailPixel->rgbRed * pThumbnailPixel->rgbReserved / 0xFF + (0xFF - pThumbnailPixel->rgbReserved) * p->rgbRed / 0xFF;
p->rgbGreen = pThumbnailPixel->rgbGreen * pThumbnailPixel->rgbReserved / 0xFF + (0xFF - pThumbnailPixel->rgbReserved) * p->rgbGreen / 0xFF;
p->rgbBlue = pThumbnailPixel->rgbBlue * pThumbnailPixel->rgbReserved / 0xFF + (0xFF - pThumbnailPixel->rgbReserved) * p->rgbBlue / 0xFF;
p->rgbReserved = pThumbnailPixel->rgbReserved + (0xFF - pThumbnailPixel->rgbReserved) * p->rgbReserved / 0xFF;
}
}
}
}
}
HeapFree(GetProcessHeap(), 0, pThumbnailBits);
succeeded = TRUE;
}
pImgLst->Release();
}
return succeeded;
Code: Select all
HRESULT CreateThumbnail(HICON hIcon, SIZE& size, LPRGBQUAD pBits, BOOL doAlphaChannelPostProcessing)
{
if(!hIcon || !pBits || !pWICImagingFactory) {
return E_FAIL;
}
ICONINFO iconInfo;
GetIconInfo(hIcon, &iconInfo);
ATLASSERT(iconInfo.hbmColor);
BITMAP bitmapInfo = {0};
if(iconInfo.hbmColor) {
GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bitmapInfo);
} else if(iconInfo.hbmMask) {
GetObject(iconInfo.hbmMask, sizeof(BITMAP), &bitmapInfo);
}
bitmapInfo.bmHeight = abs(bitmapInfo.bmHeight);
BOOL needsFrame = ((bitmapInfo.bmWidth < size.cx) || (bitmapInfo.bmHeight < size.cy));
if(iconInfo.hbmColor) {
DeleteObject(iconInfo.hbmColor);
}
if(iconInfo.hbmMask) {
DeleteObject(iconInfo.hbmMask);
}
HRESULT hr = E_FAIL;
CComPtr<IWICBitmapScaler> pWICBitmapScaler = NULL;
if(!needsFrame) {
hr = pWICImagingFactory->CreateBitmapScaler(&pWICBitmapScaler);
ATLASSERT(SUCCEEDED(hr));
ATLASSUME(pWICBitmapScaler);
}
if(needsFrame || SUCCEEDED(hr)) {
CComPtr<IWICBitmap> pWICBitmapSource = NULL;
hr = pWICImagingFactory->CreateBitmapFromHICON(hIcon, &pWICBitmapSource);
ATLASSERT(SUCCEEDED(hr));
ATLASSUME(pWICBitmapSource);
if(SUCCEEDED(hr)) {
if(!needsFrame) {
hr = pWICBitmapScaler->Initialize(pWICBitmapSource, size.cx, size.cy, WICBitmapInterpolationModeFant);
}
if(SUCCEEDED(hr)) {
WICRect rc = {0};
if(needsFrame) {
rc.Height = bitmapInfo.bmHeight;
rc.Width = bitmapInfo.bmWidth;
UINT stride = rc.Width * sizeof(RGBQUAD);
LPRGBQUAD pIconBits = reinterpret_cast<LPRGBQUAD>(HeapAlloc(GetProcessHeap(), 0, rc.Width * rc.Height * sizeof(RGBQUAD)));
hr = pWICBitmapSource->CopyPixels(&rc, stride, rc.Height * stride, reinterpret_cast<LPBYTE>(pIconBits));
ATLASSERT(SUCCEEDED(hr));
if(SUCCEEDED(hr)) {
// center the icon
int xIconStart = (size.cx - bitmapInfo.bmWidth) / 2;
int yIconStart = (size.cy - bitmapInfo.bmHeight) / 2;
LPRGBQUAD pIconPixel = pIconBits;
LPRGBQUAD pPixel = pBits;
pPixel += yIconStart * size.cx;
for(int y = yIconStart; y < yIconStart + bitmapInfo.bmHeight; ++y, pPixel += size.cx, pIconPixel += bitmapInfo.bmWidth) {
CopyMemory(pPixel + xIconStart, pIconPixel, bitmapInfo.bmWidth * sizeof(RGBQUAD));
}
HeapFree(GetProcessHeap(), 0, pIconBits);
rc.Height = size.cy;
rc.Width = size.cx;
// TODO: now draw a frame around it
}
} else {
rc.Height = size.cy;
rc.Width = size.cx;
UINT stride = rc.Width * sizeof(RGBQUAD);
hr = pWICBitmapScaler->CopyPixels(&rc, stride, rc.Height * stride, reinterpret_cast<LPBYTE>(pBits));
ATLASSERT(SUCCEEDED(hr));
if(SUCCEEDED(hr) && doAlphaChannelPostProcessing) {
for(int i = 0; i < rc.Width * rc.Height; ++i, ++pBits) {
if(pBits->rgbReserved == 0x00) {
ZeroMemory(pBits, sizeof(RGBQUAD));
}
}
}
}
} else {
ATLASSERT(FALSE && "Bitmap scaler failed");
}
}
}
return hr;
}
Code: Select all
CComPtr<IWICImagingFactory> pWICImagingFactory;
if(IsWindowsVistaOrNewer()) {
CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, reinterpret_cast<LPVOID*>(&pWICImagingFactory));
ATLASSUME(pWICImagingFactory);
}