windows-nt/Source/XPSP1/NT/printscan/ui/printui/dragdrop.cxx
2020-09-26 16:20:57 +08:00

847 lines
23 KiB
C++

/*++
Copyright (C) Microsoft Corporation, 1995 - 1999
All rights reserved.
Module Name:
dragdrop.hxx
Abstract:
Print queue drag & drop related stuff
Author:
Lazar Ivanov (LazarI) 10-Mar-2000
Revision History:
--*/
#include "precomp.hxx"
#pragma hdrstop
#include "dragdrop.hxx"
#include <initguid.h>
#include "guids.h"
/////////////////////////////////////////////////////
// CPrintQueueDT - print queue drop target impl.
//
QITABLE_DECLARE(CPrintQueueDT)
class CPrintQueueDT: public CUnknownMT<QITABLE_GET(CPrintQueueDT)>, // MT impl. of IUnknown
public IDropTarget,
public IPrintQueueDT,
public CSimpleWndSubclass<CPrintQueueDT>
{
private:
// private members
void DrawVisualFeedBack() const;
void VisualFeedBack(const POINTL &ptl, BOOL bRemove = FALSE);
int GetTargetItem(const POINTL &ptl) const;
HRESULT DropMoveJob(const DragDrop::JOBINFO &job, POINTL ptl);
HRESULT DropPrintFiles(IDataObject *pDataObj);
DWORD RightClickChooseEffect(const POINTL &ptl, DWORD dwEffectIn);
HRESULT GetPrinterName(LPTSTR pszName, UINT nMaxLength);
// static services
static DWORD WINAPI ThreadProc_PrintFiles(LPVOID lpParameter);
public:
// construction/destruction
CPrintQueueDT();
~CPrintQueueDT();
//////////////////
// IUnknown
//
IMPLEMENT_IUNKNOWN()
///////////////////
// IPrintQueueDT
//
STDMETHODIMP RegisterDragDrop(HWND hwndLV, TPrinter *pPrinter);
STDMETHODIMP RevokeDragDrop();
///////////////////
// IDropTarget
//
virtual HRESULT STDMETHODCALLTYPE DragEnter(
/* [unique][in] */ IDataObject *pDataObj,
/* [in] */ DWORD grfKeyState,
/* [in] */ POINTL pt,
/* [out][in] */ DWORD *pdwEffect);
virtual HRESULT STDMETHODCALLTYPE DragOver(
/* [in] */ DWORD grfKeyState,
/* [in] */ POINTL pt,
/* [out][in] */ DWORD *pdwEffect);
virtual HRESULT STDMETHODCALLTYPE DragLeave( void);
virtual HRESULT STDMETHODCALLTYPE Drop(
/* [unique][in] */ IDataObject *pDataObj,
/* [in] */ DWORD grfKeyState,
/* [in] */ POINTL pt,
/* [out][in] */ DWORD *pdwEffect);
// implement CSimpleWndSubclass<CPrintQueueDT>
LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
private:
// private data
LONG m_cRef; // ref count
TPrinter *m_pPrinter; // target print queue
COleComInitializer m_ole2; // make sure OLE2 is initialized.
AUTO_SCROLL_DATA m_asd; // drag & drop autoscroll stuff (declared in shlobjp.h)
DWORD m_grfKeyStateLast; // we need this to implement IDropTarget::Drop correctly
DWORD m_dwEffect; // save the last effect to implement IDropTarget::DragOver
int m_iLastItem; // last item selected (visual feedback)
};
// QueryInterface table
QITABLE_BEGIN(CPrintQueueDT)
QITABENT(CPrintQueueDT, IDropTarget), // IID_IDropTarget
QITABENT(CPrintQueueDT, IPrintQueueDT), // IID_IPrintQueueDT
QITABLE_END()
///////////////////
// CPrintQueueDT
//
void CPrintQueueDT::DrawVisualFeedBack() const
{
ASSERT(IsAttached());
int iCount = ListView_GetItemCount(m_hwnd);
ASSERT(iCount && m_iLastItem <= iCount && m_iLastItem >= 0);
RECT rc;
if( m_iLastItem < ListView_GetItemCount(m_hwnd) )
{
ListView_GetItemRect(m_hwnd, m_iLastItem, &rc, LVIR_BOUNDS);
}
else
{
ListView_GetItemRect(m_hwnd, m_iLastItem-1, &rc, LVIR_BOUNDS);
InflateRect(&rc, 0, -abs(rc.bottom - rc.top));
}
// draw a standard XOR line
HDC hDC = GetDC(m_hwnd);
if( hDC )
{
CAutoHandlePen shPen = CreatePen(PS_SOLID, 2, GetSysColor(COLOR_WINDOW));
if( shPen )
{
HGDIOBJ hObjOld = SelectObject(hDC, shPen);
SetROP2(hDC, R2_XORPEN);
MoveToEx(hDC, rc.left+1, rc.top+1, NULL);
LineTo(hDC, rc.right-1, rc.top+1);
SelectObject(hDC, hObjOld);
}
ReleaseDC(m_hwnd, hDC);
}
}
void CPrintQueueDT::VisualFeedBack(const POINTL &ptl, BOOL bRemove)
{
ASSERT(IsAttached());
if( bRemove && -1 != m_iLastItem )
{
// hide the latest visual feedback
DrawVisualFeedBack();
m_iLastItem = -1;
}
else
{
int iItem = GetTargetItem(ptl);
if( iItem != -1 && iItem != m_iLastItem )
{
if( -1 != m_iLastItem )
{
// hide the latest visual feedback
DrawVisualFeedBack();
m_iLastItem = -1;
}
// save the lastest item and draw the new
// visual feedback
m_iLastItem = iItem;
DrawVisualFeedBack();
}
else
{
if( -1 == iItem && -1 != m_iLastItem )
{
// hide the latest visual feedback
DrawVisualFeedBack();
m_iLastItem = -1;
}
}
}
}
int CPrintQueueDT::GetTargetItem(const POINTL &ptl) const
{
ASSERT(IsAttached());
LV_HITTESTINFO info = {0};
int iItem = -1;
// find the item on this point
info.pt.x = ptl.x;
info.pt.y = ptl.y;
ScreenToClient(m_hwnd, &info.pt);
iItem = ListView_HitTest(m_hwnd, &info);
if( (info.flags & LVHT_ONITEM) && iItem != -1 )
{
// get the header RECT here
RECT rcHeader;
HWND hwndHeader = ListView_GetHeader(m_hwnd);
ASSERT(hwndHeader);
GetWindowRect(hwndHeader, &rcHeader);
MapWindowPoints(NULL, m_hwnd, reinterpret_cast<LPPOINT>(&rcHeader), 2);
// check if the point is inside the header.
if( PtInRect(&rcHeader, info.pt) )
{
// this item is partially covered from the header -
// not a valid item.
iItem = -1;
}
else
{
RECT rcItem;
ListView_GetItemRect(m_hwnd, iItem, &rcItem, LVIR_BOUNDS);
if( abs(rcItem.top - info.pt.y) > (abs(rcItem.bottom - rcItem.top)/2) )
{
// this point is in the lower half of the item rect -
// go to the next item (not a problem if iItem == count
iItem ++;
}
}
}
return iItem;
}
HRESULT CPrintQueueDT::DropMoveJob(const DragDrop::JOBINFO &job, POINTL ptl)
{
ASSERT(IsAttached());
IDENT jobID = -1;
int iItem = GetTargetItem(ptl);
HRESULT hr = S_OK;
// get the position of the job we are mving in front of
if( iItem < ListView_GetItemCount(m_hwnd) )
{
LVITEM lvi = {0};
lvi.iItem = iItem;
lvi.iSubItem = 0;
lvi.mask = LVIF_PARAM;
if( ListView_GetItem(m_hwnd, &lvi) )
{
jobID = m_pPrinter->pData()->GetId((HITEM)lvi.lParam);
}
}
// calculate the new & old position of the job moved
NATURAL_INDEX uNewPos = (-1 == jobID) ? iItem : m_pPrinter->pData()->GetNaturalIndex(jobID, NULL);
NATURAL_INDEX uOldPos = (-1 == job.dwJobID) ? job.iItem : m_pPrinter->pData()->GetNaturalIndex(job.dwJobID, NULL);
if( uNewPos > uOldPos )
{
uNewPos--;
}
if( uNewPos != uOldPos )
{
CAutoHandlePrinter shPrinter = NULL;
DWORD dwAccess = 0;
if( ERROR_SUCCESS == TPrinter::sOpenPrinter(job.szPrinterName, &dwAccess, &shPrinter) )
{
DWORD cbJob2 = 0;
CAutoPtrSpl<JOB_INFO_2> spJob2 = NULL;
if( VDataRefresh::bGetJob(shPrinter, job.dwJobID, 2, spJob2.GetPPV(), &cbJob2) )
{
// set the new position and call SetJob to reorder.
spJob2->Position = uNewPos+1;
if( !SetJob(shPrinter, job.dwJobID, 2, spJob2.GetPtrAs<LPBYTE>(), 0) )
{
hr = HRESULT_FROM_WIN32(GetLastError());
MessageBeep((UINT)-1);
}
}
}
}
return hr;
}
HRESULT CPrintQueueDT::DropPrintFiles(IDataObject *pDataObj)
{
HRESULT hr = E_UNEXPECTED;
CRefPtrCOM<IStream> spStream;
// we create a background therad which will attempt to print
// documents in pDataObj
hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObj, &spStream);
if( SUCCEEDED(hr) )
{
CAutoPtr<CCookiesHolder> spThreadInfo = new CCookiesHolder;
if( spThreadInfo && spThreadInfo->SetCount(2) )
{
// as we pass pointer to ourselves to the thread proc
// we need to AddRef ourselves here
AddRef();
// setup the cookie holder
spThreadInfo->SetCookie<IStream*>(0, spStream);
spThreadInfo->SetCookie<CPrintQueueDT*>(1, this);
// spin the background thread here
DWORD dwThreadId;
CAutoHandleNT shThread = TSafeThread::Create(NULL, 0,
(LPTHREAD_START_ROUTINE)CPrintQueueDT::ThreadProc_PrintFiles,
spThreadInfo, 0, &dwThreadId);
if( shThread )
{
// the thread will take care to free this memory
spThreadInfo.Detach();
spStream.Detach();
hr = S_OK;
}
else
{
// thread creation failed. setup appropriate HRESULT from
// last error
hr = HRESULT_FROM_WIN32(GetLastError());
// compensate the AddRef above
Release();
}
}
else
{
// new CCookiesHolder has failed
hr = E_OUTOFMEMORY;
}
}
return hr;
}
HRESULT CPrintQueueDT::GetPrinterName(LPTSTR pszName, UINT nMaxLength)
{
HRESULT hr = E_INVALIDARG;
TCHAR szCurrentPrinterName[kPrinterBufMax];
if( pszName )
{
if( m_pPrinter )
{
m_pPrinter->pszPrinterName(szCurrentPrinterName);
lstrcpyn(pszName, szCurrentPrinterName, nMaxLength);
hr = S_OK;
}
else
{
hr = E_UNEXPECTED;
}
}
return hr;
}
DWORD WINAPI CPrintQueueDT::ThreadProc_PrintFiles(LPVOID lpParameter)
{
TCHAR szPrinterName[kPrinterBufMax];
HRESULT hr = E_UNEXPECTED;
CAutoPtr<CCookiesHolder> spThreadInfo = (CCookiesHolder *)lpParameter;
if( spThreadInfo )
{
// make sure OLE2 is initialized.
COleComInitializer ole2(TRUE);
// get the cookies
CAutoPtrCOM<IStream> spStream = spThreadInfo->GetCookie<IStream*>(0);
CAutoPtrCOM<CPrintQueueDT> spPrintQueueDT = spThreadInfo->GetCookie<CPrintQueueDT*>(1);
if( ole2 && spStream && spPrintQueueDT )
{
UINT uCount = 0;
CAutoPtrPIDL pidlPrinter;
CRefPtrCOM<IShellFolder> spLocalPrnFolder;
CRefPtrCOM<IDropTarget> spPrinterDT;
for( ;; )
{
// attempt to get IDropTarget for the printer name
if( SUCCEEDED(hr = spPrintQueueDT->GetPrinterName(szPrinterName, ARRAYSIZE(szPrinterName))) &&
SUCCEEDED(hr = ShellServices::CreatePrinterPIDL(NULL, szPrinterName,
&spLocalPrnFolder, &pidlPrinter)) &&
SUCCEEDED(hr = spLocalPrnFolder->GetUIObjectOf(NULL, 1, pidlPrinter.GetPPCT(),
IID_IDropTarget, &uCount, spPrinterDT.GetPPV())) )
{
// unmarshal the data object from the stream
CRefPtrCOM<IDataObject> spDataObj;
hr = CoGetInterfaceAndReleaseStream(spStream, IID_IDataObject, spDataObj.GetPPV());
if( SUCCEEDED(hr) )
{
// CoGetInterfaceAndReleaseStream relases the stream -
// make sure we don't release twice
spStream.Detach();
// simulate drag & drop over the printer object here
DWORD dwEffect = DROPEFFECT_COPY;
hr = SHSimulateDrop(spPrinterDT, spDataObj, MK_LBUTTON, NULL, &dwEffect);
}
}
if( FAILED(hr) && ERROR_INVALID_PRINTER_NAME == SCODE_CODE(GetScode(hr)) )
{
// this printer is probably a remote printer which the user is not connected to.
// he must be connected to the printer in order to be able to print. show up
// appropriate message to inform the user about this case and ask if he wants to
// connect to this printer and then print.
if( IDYES == iMessage(NULL, IDS_PRINTERS_TITLE, IDS_PRINT_NOTCONNECTED,
MB_YESNO|MB_ICONQUESTION, kMsgNone, NULL) )
{
// make a printer connection here.
UINT uLen = COUNTOF(szPrinterName);
// attempt to connect to this printer. if this fails it shows up the
// appropriate error message, so don't bother to show up UI here.
if( bPrinterSetup(NULL, MSP_NETPRINTER, uLen, szPrinterName, &uLen, NULL) )
{
// release/reset the smart pointers and try to print again.
spLocalPrnFolder = NULL;
spPrinterDT = NULL;
pidlPrinter = NULL;
continue;
}
}
}
break;
}
}
}
// setup the return value here
if( SUCCEEDED(hr) )
{
return ERROR_SUCCESS;
}
else
{
DWORD dwErr = HRESULT_CODE(hr);
SetLastError(dwErr);
return dwErr;
}
}
// implement CSimpleWndSubclass<CPrintQueueDT>
LRESULT CPrintQueueDT::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch( uMsg )
{
case WM_VSCROLL:
case WM_HSCROLL:
{
// make sure the feedback is hidden when scrolling
POINTL ptl = {0};
VisualFeedBack(ptl, TRUE);
}
break;
default:
break;
}
// allways call the default processing
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
DWORD CPrintQueueDT::RightClickChooseEffect(const POINTL &ptl, DWORD dwEffectIn)
{
HMENU hMenu = NULL;
// pop up a menu to choose from.
if( dwEffectIn == DROPEFFECT_MOVE )
{
// we're moving a job
hMenu = ShellServices::LoadPopupMenu(ghInst, POPUP_DRAGDROP_MOVE);
SetMenuDefaultItem(hMenu, POPUP_DRAGDROP_MOVE, MF_BYCOMMAND);
}
else if( dwEffectIn == DROPEFFECT_COPY )
{
// we are printing files
hMenu = ShellServices::LoadPopupMenu(ghInst, POPUP_DRAGDROP_PRINT);
SetMenuDefaultItem(hMenu, POPUP_DRAGDROP_PRINT, MF_BYCOMMAND);
}
if( hMenu )
{
// show up the context menu
BOOL bReturn = TrackPopupMenu(hMenu,
TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTALIGN,
ptl.x, ptl.y, 0, m_hwnd, NULL);
// modify the dwEffectIn according the user choise
switch( bReturn )
{
case IDM_DRAGDROP_PRINT:
dwEffectIn = DROPEFFECT_COPY;
break;
case IDM_DRAGDROP_MOVE:
dwEffectIn = DROPEFFECT_MOVE;
break;
case IDM_DRAGDROP_CANCEL:
dwEffectIn = DROPEFFECT_NONE;
break;
default:
dwEffectIn = DROPEFFECT_NONE;
}
DestroyMenu(hMenu);
}
else
{
// if hMenu is NULL (i.e. LoadMenu has falied) then
// cancel the whole operation
dwEffectIn = DROPEFFECT_NONE;
}
// return modified dwEffectIn.
return dwEffectIn;
}
CPrintQueueDT::CPrintQueueDT()
: m_cRef(1),
m_pPrinter(NULL),
m_ole2(TRUE),
m_grfKeyStateLast(0),
m_dwEffect(0),
m_iLastItem(-1)
{
// nothing
}
CPrintQueueDT::~CPrintQueueDT()
{
if( IsAttached() )
{
RevokeDragDrop();
}
}
/////////////////////////////////////////////////////
// IPrintQueueDT members
//
STDMETHODIMP CPrintQueueDT::RegisterDragDrop(HWND hwndLV, TPrinter *pPrinter)
{
HRESULT hr = E_FAIL;
if( !IsAttached() && m_ole2 && hwndLV && pPrinter )
{
// register this window for OLE2 drag & drop
hr = ::RegisterDragDrop(GetParent(hwndLV), static_cast<IDropTarget*>(this));
if( SUCCEEDED(hr) && Attach(hwndLV) )
{
// make sure shell icon cache is initialized, so
// DAD_* functions behave correctly.
FileIconInit(FALSE);
m_pPrinter = pPrinter;
}
}
return hr;
}
STDMETHODIMP CPrintQueueDT::RevokeDragDrop()
{
HRESULT hr = E_FAIL;
if( IsAttached() && m_ole2 && m_pPrinter )
{
// unregister this window for OLE2 drag & drop
hr = ::RevokeDragDrop(GetParent(m_hwnd));
Detach();
m_pPrinter = NULL;
}
return hr;
}
// this API is supposed to be declared in shlobjp.h, and exported from shell32.dll,
// but for some reason it isn't. duplicate the code here.
STDAPI_(BOOL) DAD_DragEnterEx3(HWND hwndTarget, const POINTL ptStart, IDataObject *pdtobj)
{
RECT rc;
GetWindowRect(hwndTarget, &rc);
// If hwndTarget is RTL mirrored, then measure the
// the client point from the visual right edge
// (near edge in RTL mirrored windows). [samera]
POINT pt;
if( GetWindowLong(hwndTarget, GWL_EXSTYLE) & WS_EX_LAYOUTRTL )
pt.x = rc.right - ptStart.x;
else
pt.x = ptStart.x - rc.left;
pt.y = ptStart.y - rc.top;
return DAD_DragEnterEx2(hwndTarget, pt, pdtobj);
}
///////////////////
// IDropTarget
//
HRESULT STDMETHODCALLTYPE CPrintQueueDT::DragEnter(
/* [unique][in] */ IDataObject *pDataObj,
/* [in] */ DWORD grfKeyState,
/* [in] */ POINTL pt,
/* [out][in] */ DWORD *pdwEffect)
{
ASSERT(IsAttached());
// save the last key state
m_grfKeyStateLast = grfKeyState;
// by default - none
*pdwEffect = DROPEFFECT_NONE;
STGMEDIUM medium;
FORMATETC fmte = {DragDrop::g_cfPrintJob, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
// try to get DragDrop::g_cfPrintJob data first.
if( SUCCEEDED(pDataObj->GetData(&fmte, &medium)) )
{
// aquire the JOBINFO
DragDrop::JOBINFO *pJobInfo = (DragDrop::JOBINFO *)GlobalLock(medium.hGlobal);
if( pJobInfo && pJobInfo->hwndLV == m_hwnd )
{
// this is a print job from our window, accept this
*pdwEffect = DROPEFFECT_MOVE;
}
// release medium
GlobalUnlock(medium.hGlobal);
ReleaseStgMedium(&medium);
}
else
{
// try to get CF_HDROP data.
FORMATETC fmte2 = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
if( SUCCEEDED(pDataObj->QueryGetData(&fmte2)) )
{
// we also accept files for printing
*pdwEffect = DROPEFFECT_COPY;
}
}
// store dwEffect for DragOver
m_dwEffect = *pdwEffect;
// invoke shell to do the standard stuff
DAD_DragEnterEx3(m_hwnd, pt, pDataObj);
DAD_InitScrollData(&m_asd);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CPrintQueueDT::DragOver(
/* [in] */ DWORD grfKeyState,
/* [in] */ POINTL pt,
/* [out][in] */ DWORD *pdwEffect)
{
ASSERT(IsAttached());
// effect was stored in DragEnter
*pdwEffect = m_dwEffect;
// convert to local coords
POINT pts = {pt.x, pt.y};
ScreenToClient(m_hwnd, &pts);
// assume coords of our window match listview
if( DAD_AutoScroll(m_hwnd, &m_asd, &pts) )
{
*pdwEffect |= DROPEFFECT_SCROLL;
}
if( DROPEFFECT_MOVE & (*pdwEffect) )
{
// draw visual feedback if necessary
VisualFeedBack(pt, FALSE);
}
// invoke shell to do the standard stuff
DAD_DragMove(pts);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CPrintQueueDT::DragLeave( void)
{
ASSERT(IsAttached());
// remove the visual feedback if any
POINTL ptl = {0};
VisualFeedBack(ptl, TRUE);
// invoke shell to do the standard stuff
DAD_DragLeave();
return S_OK;
}
HRESULT STDMETHODCALLTYPE CPrintQueueDT::Drop(
/* [unique][in] */ IDataObject *pDataObj,
/* [in] */ DWORD grfKeyState,
/* [in] */ POINTL pt,
/* [out][in] */ DWORD *pdwEffect)
{
ASSERT(IsAttached());
HRESULT hr = E_UNEXPECTED;
if( m_grfKeyStateLast & MK_LBUTTON )
{
*pdwEffect = m_dwEffect;
}
else
{
*pdwEffect = RightClickChooseEffect(pt, m_dwEffect);
}
// do the drop here
if( DROPEFFECT_COPY == *pdwEffect )
{
// we're printing the file(s) pDataObj to the printer
DropPrintFiles(pDataObj);
}
else if( DROPEFFECT_MOVE == *pdwEffect )
{
// we are reordering jobs withing this print queue
STGMEDIUM medium = {0};
FORMATETC fmte = {DragDrop::g_cfPrintJob, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
hr = pDataObj->GetData(&fmte, &medium);
if( SUCCEEDED(hr) )
{
DragDrop::JOBINFO *pJobInfo = (DragDrop::JOBINFO *)GlobalLock(medium.hGlobal);
if( pJobInfo )
{
hr = DropMoveJob(*pJobInfo, pt);
GlobalUnlock(medium.hGlobal);
}
ReleaseStgMedium(&medium);
}
}
// make sure to unlock the window for updating
DragLeave();
return hr;
}
/////////////////////////////////////////////////////
// common drag & drop APIs & data structures
//
namespace DragDrop
{
// print job clipboard format (JOBINFO)
CLIPFORMAT g_cfPrintJob = 0;
// registers the clipboard format for a print job (JOBINFO)
void RegisterPrintJobClipboardFormat()
{
if( !g_cfPrintJob )
{
g_cfPrintJob = static_cast<CLIPFORMAT>(
RegisterClipboardFormat(TEXT("PrintJob32")));
}
}
// creates IDataObject & IDropSource for a printer job objec
HRESULT CreatePrintJobObject(const JOBINFO &jobInfo, REFIID riid, void **ppv)
{
HRESULT hr = E_INVALIDARG;
if( ppv )
{
*ppv = NULL; // reset this
// make sure print job data type is registered first
RegisterPrintJobClipboardFormat();
// instantiate CSimpleDataObjImpl template for JOBINFO
CAutoPtrCOM< CDataObj<> > spDataObj = new CDataObj<>;
CAutoPtrCOM< CSimpleDataObjImpl<JOBINFO> > spJobObj = new CSimpleDataObjImpl<JOBINFO>(jobInfo, g_cfPrintJob, spDataObj);
if( spDataObj && spJobObj )
{
hr = spJobObj->QueryInterface(riid, ppv);
}
else
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
// instantiate a IPrintQueueDT implementation
HRESULT CreatePrintQueueDT(REFIID riid, void **ppv)
{
HRESULT hr = E_INVALIDARG;
if( ppv )
{
*ppv = NULL; // reset this
// instantiate CPrintQueueDT
CAutoPtrCOM<CPrintQueueDT> spObj = new CPrintQueueDT;
if( spObj )
{
hr = spObj->QueryInterface(riid, ppv);
}
else
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
} // namespace DragDrop