Ureader.com  
Microsoft software help and Community
   home   |   control panel login   |   archive   |  
 
platform
active.directory
adsi
adsi.iis-admin
base
com_ole
complus_mts
component_svcs
database
directx
gdi
graphics_mm
internet.client
internet.server
internet.server.isapi-dev
localization
mapi
messaging
msi
mslayerforunicode
multimedia
networking
networking.ipv6
sdk_install
security
shell
telephony.tapi_2
telephony.tapi_3
telephony.tsp
telephony.wte
tools
ui
ui_shell
win_base_svcs
win16
  
 
date: Sat, 12 Apr 2008 18:42:13 -0500,    group: microsoft.public.platformsdk.shell        back       


Thumbnail Extractor Shell Extension - "E_PENDING"   
Hi folks,

(Thanks to Matt Ellis for an earlier response to some of the questions 
here.)

Please pardon a COM / ATL newbie's venture into the (very) deep end of the 
pool, but I'm trying to implement a pre-Vista thumbnail extractor shell 
extension with VS 2008 VC++ ATL that implements the "IEIFLAG_ASYNC" protocol 
for multithreaded access the way the MSDN docs seem to say that it's 
supposed to be implemented -- or at least the way I read them.

According to the MSDN docs on IExtractImage::GetLocation, a) if you return 
E_PENDING, you are indicating that IExtractImage::Extract can be called by a 
(Shell?) worker thread, and b) if you return E_PENDING, your server must 
also implement IRunnableTask.  There's one other wrinkle here, which is that 
the current docs indicate that Vista will actually ignore the return code 
and call Extract on a worker thread regardless.

I took this to mean that a) if you return E_PENDING, then your server should 
be capable of being marked with the "Both" or "Free" threading model for 
best performance, b) that at least some IRunnableTask methods must be 
implemented, and c) if running under Vista, both a) and b) better be true 
regardless.  (As it turns out, The IRunnableTask docs indicate that, 
specifically for the IExtractImage case, only the "IsRunning" method needs 
to be implemented, as the Extract call itself constitutes the "Run" call. 
The other IRunnableTask methods are optionally implementable, but the 
current docs indicate that if they are implemented, they *will* be used.)

There seem to be only a couple of comprehensive examples of how to do this 
in VC++ on the web (and none in MSDN, other than a highly simplfied example 
from Dino Esposito in a reproduced magazine article from many years ago). 
One is on CodeProject 
(http://www.codeproject.com/KB/shell/thumbextract.aspx), and the other on a 
site referenced out of the comments on the CodeProject article.

In each case, the authors have marked their servers as "Apartment" threaded, 
and used ATL's "CComSingleThreadModel" to create their objects.  However, in 
each case, in the IExtractImage::GetLocation call, the authors test the 
"IEIFLAG_ASYNC" bit of the pdwFlags paramter, and return "E_PENDING" if the 
bit is set.  (Which it usually seems to be.)  In neither case does the 
author implement IRunnableTask.  This appears to me to be a double-bogey by 
the docs, but neither author seems to have a serious problem with it. 
(Though some users have commented on occaisional problems.)

Now, my first bit of confusion here may be whether or not there's a 
difference between the Shell's use of a worker thread to call Extract, and 
the server's corresponding threading model, and whether it's an error for 
these guys to be returning "E_PENDING" given their implementations.  The 
MSDN docs are very silent on what threading model the Shell - XP or Vista - 
actually uses to call the server.  (As an aside, it's not clear that the 
pre-Vista thumbnail extraction protocol is actually even a "supported" 
protocol.  There doesn't seem to be any formal MSDN documentation of the 
thumbnail extraction protocol that actually links the IPersistFile::Load 
call to send the file path to the extraction server with the IExtractImage 
calls to return the thumbnail.  As near as I can tell, the protocol is 
"heresay" based on a few paragraphs from a couple of Esposito's old magazine 
articles reproduced in MSDN.)

Based on the examples above, along with some suggestions from the various 
comments posted to the articles, I've implemented inproc server dll versions 
of the extractor for "Apartment" / "CComSingleThreadModel", "Both" / 
CComMultiThreadModel, and "Free" / CComMultiThreadModel.  With the "Both" 
and "Free" versions, I added some elementary critical section 
synchronization (or at least, I think I have -- again, taken from an article 
on CodeProject - http://www.codeproject.com/KB/COM/critsectionwrap.aspx).  I 
also added a very simplistic (maybe too simplistic) 
"IRunnableTask::IsRunning" implementation.

The CodeProject critical section code that tests whether or not the critical 
section needs to be created and used applies a test to determine what kind 
of threading model the object was created in (it tests the return code from 
a "probe" CoInitialize() call).  Interestingly, even when the server is 
marked "Both", the code trace indicates that the server is still created in 
an STA.  However, when the server is marked "Free", the code trace indicates 
that the server actually is created in the MTA.

Possbily because the "Both" version appears to be just created in an STA 
anyway, the extractor appears to run OK.  However, when I attempt to run the 
"Free" version, I get this Explorer exception in the output window of the 
debugger, and the extraction protocol appears to abort silently:

"First-chance exception at 0x7c812a5b in explorer.exe: 0x8001010E: The 
application called an interface that was marshalled for a different thread."

It appears that the exception occurs only *after* the IPersistFile::Load and 
IExtractImage::GetLocation calls have run succesfully, and E_PENDING has 
been returned from GetLocation.  The Extract call never seems to get called, 
nor does IRunnableTask::IsRunning seem to get pinged before this happens.

Again, I'm a total novice here, but I'm guessing this is the result of the 
Shell seeing the E_PENDING return, and actually handing off an interface 
pointer from the Shell MTA to an STA.

What I'm also suspecting is that if the Shell for some reason also decided 
to actually load my "Both" version into the MTA instead of an STA, that it 
would actually choke in the same way.

FWIW, I had already scanned some web stuff about the exception, and did go 
back and comment out the stdafx.h "#define _ATL_APARTMENT_THREADED" line and 
replace it with "#define _ATL_FREE_THREADED".  (Aside -- In the ATL Simple 
Object Wizard, I selected "Free" threading for this version -- is this an 
old bug in the ATL wizard?)  I've also seen a lot of stuff about the "GIT" 
and Free-Threaded-Marshaller that I don't quite grok yet, but it's also not 
clear to me if this something I have a lot of control over on the server 
side anyway, particularly with the lack of insight into what the Shell 
really wants to do here.

To keep this message a little shorter, I'll post the major code pieces in a 
reply.

To end this, I guess my questions are,

1) Should I just stop worrying about Vista using a worker thread for Extract 
no matter what I tell it, set "Apartment" / "CComSingleThreadModel",ignore 
the IEIFLAG_ASYNC bit, *not* return E_PENDING and accept any performance 
issues that arise from the marshalling (does it look from the "evidence" 
like it's going to end up marshalling under the covers regardless)?
2) Is there anything that anyone could recommend to fix the "Free" threaded 
(and presumably "Both" threaded) version?  (Is it even remotely possible 
that there is a bug in the Shell's interface pointer handling in this case?)
3) Is the critical section implementation I applied to support 
multithreading in the server sensible?
4) Is the simplistic IRunnableTask::IsRunning implementation I used in the 
ballpark?
5) There seem to be very few recent ATL/COM books, but the older books all 
worry about marshalling performance.  Is that still a big deal on modern 
boxes where I'm not actually implementing an enterprise app of some kind?

Again, thanks for your patience.
date: Sat, 12 Apr 2008 18:42:13 -0500   author:   linearred am

Re: Thumbnail Extractor Shell Extension - "E_PENDING"   
Code for the "Free" threaded version.  .h and .cpp based on Philipos 
Sakellaropoulos' article from:
http://www.codeproject.com/KB/shell/thumbextract.aspx

NOTES:
* Inproc server component is marked "Free".
* stdafx.h "#define _ATL_APARTMENT_THREADED" line replaced with "#define 
_ATL_FREE_THREADED"

-------------------
// myThumbGetter.h : Declaration of the CmyThumbGetter

#pragma once
#include "resource.h"       // main symbols

#include "myThumbProto7_i.h"

//mine
#include <shlobj.h>
#include "ComSmartAutoCriticalSection.h"
//endmine


// CmyThumbGetter

class ATL_NO_VTABLE CmyThumbGetter :
 public CComObjectRootEx<CComMultiThreadModel>,
 public CComCoClass<CmyThumbGetter, &CLSID_myThumbGetter>,
 public IPersistFile,
 public IExtractImage2,
 public IRunnableTask
{
public:
 CmyThumbGetter()
 {
  m_lExtractionState = IRTIR_TASK_NOT_RUNNING;
 }

DECLARE_REGISTRY_RESOURCEID(IDR_MYTHUMBGETTER)

DECLARE_NOT_AGGREGATABLE(CmyThumbGetter)

BEGIN_COM_MAP(CmyThumbGetter)
 COM_INTERFACE_ENTRY(IPersistFile)
 COM_INTERFACE_ENTRY(IExtractImage)
 COM_INTERFACE_ENTRY(IExtractImage2)
 COM_INTERFACE_ENTRY(IRunnableTask)
END_COM_MAP()



 DECLARE_PROTECT_FINAL_CONSTRUCT()

 HRESULT FinalConstruct()
 {
  return S_OK;
 }

 void FinalRelease()
 {
 }

public:
//IPersistFile
 STDMETHODIMP GetClassID(LPCLSID)      { return E_NOTIMPL; }
 STDMETHODIMP IsDirty()                { return E_NOTIMPL; }
 STDMETHODIMP Load(LPCOLESTR, DWORD);
 STDMETHODIMP Save(LPCOLESTR, BOOL)    { return E_NOTIMPL; }
 STDMETHODIMP SaveCompleted(LPCOLESTR) { return E_NOTIMPL; }
 STDMETHODIMP GetCurFile(LPOLESTR*)    { return E_NOTIMPL; }

//IExtractImage
 STDMETHODIMP GetLocation(LPWSTR pszPathBuffer,
         DWORD cchMax,
         DWORD *pdwPriority,
         const SIZE *prgSize,
         DWORD dwRecClrDepth,
         DWORD *pdwFlags);
 STDMETHODIMP Extract(HBITMAP*);

// IExtractImage2
 STDMETHODIMP GetDateStamp(FILETIME *pDateStamp);

//IRunnableTask
    STDMETHODIMP Run( void) { return E_NOTIMPL; }
    STDMETHODIMP Kill( BOOL bWait) { return E_NOTIMPL; }
    STDMETHODIMP Suspend( void) { return E_NOTIMPL; }
    STDMETHODIMP Resume( void) { return E_NOTIMPL; }
 ULONG STDMETHODCALLTYPE IsRunning(void);



protected:
 SIZE m_bmSize;
 int m_nColorDepth;
 TCHAR m_szFile[500];

private:
 CComSmartAutoCriticalSection m_csSafeAccess;
 LONG m_lExtractionState;

};

OBJECT_ENTRY_AUTO(__uuidof(myThumbGetter), CmyThumbGetter)


-------------
// myThumbGetter.cpp : Implementation of CmyThumbGetter

#include "stdafx.h"
#include "myThumbGetter.h"

//mine
#include <shlobj.h>
#include <gdiplus.h>

using namespace Gdiplus;

// CmyThumbGetter

STDMETHODIMP CmyThumbGetter::Load(LPCOLESTR wszFile, DWORD dwMode)
{
 m_csSafeAccess.Lock();

  USES_CONVERSION;
  _tcscpy_s(m_szFile, OLE2T((WCHAR*)wszFile));

 m_csSafeAccess.Unlock();

 return S_OK;
};


STDMETHODIMP CmyThumbGetter::GetLocation(LPWSTR pszPathBuffer,
  DWORD cchMax, DWORD *pdwPriority,
  const SIZE *prgSize, DWORD dwRecClrDepth,
  DWORD *pdwFlags) {

 m_csSafeAccess.Lock();

  m_bmSize = *prgSize;
  m_nColorDepth = dwRecClrDepth;

  *pdwFlags |= IEIFLAG_CACHE;

  if (*pdwFlags & IEIFLAG_ASYNC) {

   m_csSafeAccess.Unlock();
   return E_PENDING;

  } else {

   m_csSafeAccess.Unlock();
   return NOERROR;

  }

 //m_csSafeAccess.Unlock(); Performed.
}


STDMETHODIMP CmyThumbGetter::GetDateStamp(FILETIME *pDateStamp)
{
 m_csSafeAccess.Lock();

  FILETIME ftCreationTime,ftLastAccessTime,ftLastWriteTime;
  // open the file and get last write time
  HANDLE hFile = CreateFile(m_szFile,GENERIC_READ,FILE_SHARE_READ,NULL,
   OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  if(!hFile) return E_FAIL;
  GetFileTime(hFile,&ftCreationTime,&ftLastAccessTime,&ftLastWriteTime);
  CloseHandle(hFile);
  *pDateStamp = ftLastWriteTime;

 m_csSafeAccess.Unlock();

 return NOERROR;
}

STDMETHODIMP CmyThumbGetter::Extract(HBITMAP* phBmpThumbnail)
{

 m_csSafeAccess.Lock();

  GdiplusStartupInput gdiplusStartupInput;
  ULONG_PTR gdiplusToken;
  GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

  {
   Bitmap bmOriginal(m_szFile, FALSE);

   int nOriginalWidth = bmOriginal.GetWidth();
   int nOriginalHeight = bmOriginal.GetHeight();

   int nPixelFormat(0);
   switch (m_nColorDepth) {
    case 24:
     nPixelFormat = PixelFormat24bppRGB;
     break;
    case 32:
     nPixelFormat = PixelFormat32bppARGB;
     break;
    default:
     nPixelFormat = PixelFormat24bppRGB;
   }

   Bitmap bmScaledBitmap(m_bmSize.cx, m_bmSize.cy, nPixelFormat);

   Graphics gGraphics(&bmScaledBitmap);
   gGraphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);

   Color bgColor(255, 255, 255);
   gGraphics.Clear(bgColor);

   int nScaledHt = (nOriginalHeight * 96) / nOriginalWidth;
   gGraphics.DrawImage(&bmOriginal, 0, 0, 96, nScaledHt);

   bmScaledBitmap.GetHBITMAP(bgColor, phBmpThumbnail);

  }

  Gdiplus::GdiplusShutdown(gdiplusToken);

  m_lExtractionState = IRTIR_TASK_FINISHED;

 m_csSafeAccess.Unlock();

 return NOERROR;
}

ULONG STDMETHODCALLTYPE CmyThumbGetter::IsRunning(void) {

 LONG lExtractionState;

 m_csSafeAccess.Lock();

  lExtractionState = m_lExtractionState;

 m_csSafeAccess.Unlock();

 return lExtractionState;

}

------------
(Below taken from Jeremiah Talkar's article on CodeProject:
 http://www.codeproject.com/KB/COM/critsectionwrap.aspx)
-------------
//ComSmartAutoCriticalSection.h

#pragma once

class CComSmartAutoCriticalSection
{
public:
 CComSmartAutoCriticalSection()
 {
  if (SUCCEEDED(::CoInitialize(NULL)))
  {
   ::CoUninitialize();

   m_bInMTA = FALSE;
  }
  else
  {
   m_bInMTA = TRUE;
  }

  if (m_bInMTA)
   ::InitializeCriticalSection(&m_csSafeAccess);
 }

 ~CComSmartAutoCriticalSection()
 {
  if (m_bInMTA)
   ::DeleteCriticalSection(&m_csSafeAccess);
 }

 void Lock()
 {
  if (m_bInMTA)
   ::EnterCriticalSection(&m_csSafeAccess);
 }

 void Unlock()
 {
  if (m_bInMTA)
   ::LeaveCriticalSection(&m_csSafeAccess);
 }

private:
 BOOL              m_bInMTA;
 CRITICAL_SECTION  m_csSafeAccess;
};
date: Sat, 12 Apr 2008 18:46:11 -0500   author:   linearred am

Google
 
Web ureader.com


    COPYRIGHT 2007, YARDI TECHNOLOGY LIMITED, ALL RIGHT RESERVE  |   contact us