//+------------------------------------------------------------------------- // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright 1998 - 1999 Microsoft Corporation. All Rights Reserved. // // PROGRAM: htmlprop.cxx // // PURPOSE: Sits on the Indexing Service HTML filter to translate string // meta properties into specified data types. // // PLATFORM: Windows 2000 // //-------------------------------------------------------------------------- #define UNICODE #include #include #include #include #include #include #include #include "htmlprop.hxx" #include "filtreg.hxx" // The typid of the html filter CLSID TYPID_HtmlPropIFilter = { 0xc8e2ab80, 0xa1db, 0x11d1, { 0xa8, 0xfb, 0x00, 0xe0, 0x98, 0x00, 0x6e, 0xd3 } }; // The CLSID for the html property filter CLSID CLSID_HtmlPropIFilter = { 0xf4309e80, 0xa1db, 0x11d1, { 0xa8, 0xfb, 0x00, 0xe0, 0x98, 0x00, 0x6e, 0xd3 } }; // Class of the html property filter CLSID CLSID_HtmlPropClass = { 0x4cbd1020, 0xa1db, 0x11d1, { 0xa8, 0xfb, 0x00, 0xe0, 0x98, 0x00, 0x6e, 0xd3 } }; // The built-in html filter CLSID CLSID_HtmlIFilter = { 0xe0ca5340, 0x4534, 0x11cf, { 0xb9, 0x52, 0x00, 0xaa, 0x00, 0x51, 0xfe, 0x20 } }; // The guid for html meta properties CLSID CLSID_MetaProperty = { 0xd1b5d3f0, 0xc0b3, 0x11cf, { 0x9a, 0x92, 0x00, 0xa0, 0xc9, 0x08, 0xdb, 0xf1 } }; // The guid for html meta properties in string form const WCHAR * pwcMetaProperty = L"d1b5d3f0-c0b3-11cf-9a92-00a0c908dbf1"; struct SPropertyEntry { WCHAR awcName[cwcMaxName]; // name of the meta property DBTYPE dbType; // data type of property }; const SPropertyEntry aTypeEntries[] = { { L"DBTYPE_I1", DBTYPE_I1 }, // 0: signed char { L"DBTYPE_UI1", DBTYPE_UI1 }, // 1: unsigned char { L"DBTYPE_I2", DBTYPE_I2 }, // 2: 2 byte signed int { L"DBTYPE_UI2", DBTYPE_UI2 }, // 3: 2 byte unsigned int { L"DBTYPE_I4", DBTYPE_I4 }, // 4: 4 byte signed int { L"DBTYPE_UI4", DBTYPE_UI4 }, // 5: 4 byte unsigned int { L"DBTYPE_I8", DBTYPE_I8 }, // 6: 8 byte signed int { L"DBTYPE_UI8", DBTYPE_UI8 }, // 7: 8 byte unsigned int { L"DBTYPE_R4", DBTYPE_R4 }, // 8: 4 byte float { L"DBTYPE_R8", DBTYPE_R8 }, // 9: 8 byte float { L"DBTYPE_BOOL", DBTYPE_BOOL }, // 12: BOOL (true=-1, false=0) { L"DBTYPE_WSTR", DBTYPE_WSTR }, // 14: wide null terminated string { L"VT_FILETIME", VT_FILETIME } // 19: I8 filetime }; const ULONG cTypeEntries = sizeof aTypeEntries / sizeof aTypeEntries[0]; const WCHAR * pwcHTMLPropertyFile = L"htmlprop.ini"; const ULONG maxEntries = 500; SPropertyEntry g_aEntries[ maxEntries ]; ULONG g_cEntries = 0; // count of entries in g_aEntries long g_cInstances = 0; HMODULE g_hHtmlDll = 0; // module handle for nlhtml.dll // htmlfilt.dll for IS 1.x and nlhtml.dll for IS 2.x const WCHAR * pwcHTMLFilter = L"nlhtml.dll"; //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::HtmlPropIFilter // // Synopsis: Constructor // //-------------------------------------------------------------------------- HtmlPropIFilter::HtmlPropIFilter() : _pHtmlFilter( 0 ), _pPersistFile( 0 ), _lRefs( 1 ) { InterlockedIncrement( &g_cInstances ); } //HtmlPropIFilter //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::~HtmlPropIFilter // // Synopsis: Destructor // //-------------------------------------------------------------------------- HtmlPropIFilter::~HtmlPropIFilter() { InterlockedDecrement( &g_cInstances ); if ( _pHtmlFilter ) _pHtmlFilter->Release(); if ( _pPersistFile ) _pPersistFile->Release(); } //~HtmlPropIFilter //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::QueryInterface // // Synopsis: Rebind to other interface // // Arguments: [riid] -- IID of new interface // [ppvObject] -- New interface * returned here // // Returns: S_OK if bind succeeded, E_NOINTERFACE if bind failed // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::QueryInterface( REFIID riid, void ** ppvObject) { if ( IID_IFilter == riid ) *ppvObject = (IFilter *)this; else if ( IID_IPersist == riid ) *ppvObject = (IPersist *)(IPersistFile *)this; else if ( IID_IPersistFile == riid ) *ppvObject = (IPersistFile *)this; else if ( IID_IUnknown == riid ) *ppvObject = (IUnknown *)(IPersist *)(IPersistFile *)this; else { *ppvObject = 0; return E_NOINTERFACE; } AddRef(); return S_OK; } //QueryInterface //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::AddRef // // Synopsis: Increments refcount // //-------------------------------------------------------------------------- ULONG STDMETHODCALLTYPE HtmlPropIFilter::AddRef() { return InterlockedIncrement( &_lRefs ); } //AddRef //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::Release // // Synopsis: Decrement refcount. Delete if necessary. // //-------------------------------------------------------------------------- ULONG STDMETHODCALLTYPE HtmlPropIFilter::Release() { long lTmp = InterlockedDecrement( &_lRefs ); if ( 0 == lTmp ) delete this; return lTmp; } //Release //+------------------------------------------------------------------------- // // Member: HtmlPropIFilter::Init, public // // Synopsis: Initializes instance of property filter // // Arguments: [grfFlags] -- flags for filter behavior // [cAttributes] -- number of attributes in array aAttributes // [aAttributes] -- array of attributes // [pFlags] -- flags // //---------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::Init( ULONG grfFlags, ULONG cAttributes, FULLPROPSPEC const * aAttributes, ULONG * pFlags ) { return _pHtmlFilter->Init( grfFlags, cAttributes, aAttributes, pFlags ); } //Init //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::GetChunk // // Synopsis: Gets the next chunk // // Arguments: [pStat] -- Chunk info // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::GetChunk( STAT_CHUNK * pStat ) { SCODE sc = _pHtmlFilter->GetChunk( pStat ); // If we got a chunk, it's a value chunk, and it's a meta property with // a string identifier, store away the name so it's available when the // value is retrieved. if ( ( SUCCEEDED( sc ) ) && ( 0 != ( pStat->flags & CHUNK_VALUE ) ) && ( PRSPEC_LPWSTR == pStat->attribute.psProperty.ulKind ) && ( CLSID_MetaProperty == pStat->attribute.guidPropSet ) && ( 0 != pStat->attribute.psProperty.lpwstr ) && ( wcslen( pStat->attribute.psProperty.lpwstr ) < cwcMaxName ) ) { _fMetaProperty = TRUE; wcscpy( _awcName, pStat->attribute.psProperty.lpwstr ); } else { _fMetaProperty = FALSE; } return sc; } //GetChunk //+------------------------------------------------------------------------- // // Function: ParseDateTime // // Synopsis: Parse the string and return a date/time // n.b. If the date is ill-formed and this function returns // an error code, the file will be unfiltered, and can be // found by using the unfiltered admin query. // The date parsing is very strict. // Times are in any time zone you like, but .htx will interpret // all dates as GMT. You can always use .asp script to munge // dates as you like. // // Arguments: [pwcDate] -- String form of the date/time // [ft] -- Returns the date/time // //-------------------------------------------------------------------------- SCODE ParseDateTime( WCHAR * pwcDate, FILETIME & ft ) { SYSTEMTIME stValue = { 0, 0, 0, 0, 0, 0, 0, 0 }; // Some day add more flexible date formats! int cItems = swscanf( pwcDate, L"%4hd/%2hd/%2hd %2hd:%2hd:%2hd:%3hd", &stValue.wYear, &stValue.wMonth, &stValue.wDay, &stValue.wHour, &stValue.wMinute, &stValue.wSecond, &stValue.wMilliseconds ); if ( 1 == cItems ) cItems = swscanf( pwcDate, L"%4hd-%2hd-%2hd %2hd:%2hd:%2hd:%3hd", &stValue.wYear, &stValue.wMonth, &stValue.wDay, &stValue.wHour, &stValue.wMinute, &stValue.wSecond, &stValue.wMilliseconds ); if( cItems != 3 && cItems != 6 && cItems != 7) return E_INVALIDARG; // Make a sensible split for Year 2000 if (stValue.wYear < 30) stValue.wYear += 2000; else if (stValue.wYear < 100) stValue.wYear += 1900; if( !SystemTimeToFileTime( &stValue, &ft ) ) return HRESULT_FROM_WIN32( GetLastError() ); return S_OK; } //ParseDateTime //+------------------------------------------------------------------------- // // Function: Translate // // Synopsis: Translates the string property value to the given type // // Arguments: [v] -- The variant to translate // [dbType] -- The resulting data type // //-------------------------------------------------------------------------- SCODE Translate( PROPVARIANT & v, DBTYPE dbType ) { PROPVARIANT varCopy = v; BOOL fChange = TRUE; SCODE sc = S_OK; WCHAR *p = v.pwszVal; // The source must be a string variant if ( 0 == p || VT_LPWSTR != v.vt ) return S_OK; v.vt = dbType; // Eat leading white space while ( ' ' == *p ) p++; int base = 10; // Is this a hex number? if ( '0' == *p && ( 'x' == *(p+1) || 'X' == *(p+1) ) ) { p += 2; base = 16; } WCHAR * pwc; // DBTYPE_I1 isn't an official OLE property type, so it isn't supported. // // Note: range checking could be added and a failure code could be // returned if the check failed. That would put the file in the // unfiltered list. switch ( dbType ) { case DBTYPE_UI1 : v.bVal = (BYTE) wcstoul( v.pwszVal, &pwc, base ); break; case DBTYPE_I2 : v.iVal = (SHORT) wcstol( v.pwszVal, &pwc, base ); break; case DBTYPE_UI2 : v.uiVal = (USHORT) wcstoul( v.pwszVal, &pwc, base ); break; case DBTYPE_I4 : v.lVal = (LONG) wcstol( v.pwszVal, &pwc, base ); break; case DBTYPE_UI4 : v.ulVal = (ULONG) wcstoul( v.pwszVal, &pwc, base ); break; case DBTYPE_I8 : swscanf( v.pwszVal, L"%I64d", &v.fltVal ); break; case DBTYPE_UI8 : swscanf( v.pwszVal, L"%I64u", &v.fltVal ); break; case DBTYPE_R4 : swscanf( v.pwszVal, L"%f", &v.fltVal ); break; case DBTYPE_R8 : swscanf( v.pwszVal, L"%lf", &v.dblVal ); break; case DBTYPE_BOOL : // If the first character is t or 1, assume VARIANT_TRUE if ( 't' == *v.pwszVal || 'T' == *v.pwszVal || '1' == *v.pwszVal ) v.boolVal = VARIANT_TRUE; else v.boolVal = VARIANT_FALSE; break; case VT_FILETIME : sc = ParseDateTime( p, v.filetime ); break; default : // leave the property as it was fChange = FALSE; v.vt = varCopy.vt; break; } // Free the memory for the (now converted) string value if ( fChange ) PropVariantClear( &varCopy ); return sc; } //Translate //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::GetValue // // Synopsis: Retrieves the current value // // Arguments: [ppPropValue] -- Where the value is stored // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::GetValue( PROPVARIANT ** ppPropValue ) { SCODE sc = _pHtmlFilter->GetValue( ppPropValue ); // Is this a value that must be translated? if ( SUCCEEDED( sc ) && _fMetaProperty && 0 != *ppPropValue ) { // lookup the datatype of the translation based on property name for ( ULONG i = 0; i < g_cEntries; i++ ) { if ( !_wcsicmp( g_aEntries[i].awcName, _awcName ) ) { // found it, so translate it sc = Translate( **ppPropValue, g_aEntries[i].dbType ); break; } } } return sc; } //GetValue //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::GetText // // Synopsis: Gets text from the filter // // Arguments: [pcwcBuffer] -- Returns the # of WCHARs returned // [awcBuffer] -- Where text is written // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::GetText( ULONG * pcwcBuffer, WCHAR * awcBuffer ) { return _pHtmlFilter->GetText( pcwcBuffer, awcBuffer ); } //GetText //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::BindRegion // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::BindRegion( FILTERREGION origPos, REFIID riid, void ** ppunk ) { return _pHtmlFilter->BindRegion( origPos, riid, ppunk ); } //BindRegion //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::GetClassID // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::GetClassID( CLSID * pClassID ) { *pClassID = CLSID_HtmlPropIFilter; return S_OK; } //GetClassID //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::IsDirty // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::IsDirty() { return S_FALSE; } //IsDirty //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::Load // // Synopsis: Loads the file for the filter // // Arguments: [pszFileName] -- Name of the file // [dwMode] -- Mode of the load // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::Load( LPCWSTR pszFileName, DWORD dwMode) { // // Get html filter class object and class factory // IClassFactory * pcf; SCODE sc = E_FAIL; if ( 0 != g_hHtmlDll ) { LPFNGETCLASSOBJECT pfn = (LPFNGETCLASSOBJECT)GetProcAddress( g_hHtmlDll, "DllGetClassObject" ); if ( 0 != pfn ) sc = (pfn)( CLSID_HtmlIFilter, IID_IClassFactory, (void **)&pcf ); } if ( SUCCEEDED(sc) ) { // Get an html filter sc = pcf->CreateInstance( 0, IID_IFilter, (void **)&_pHtmlFilter ); pcf->Release(); if ( SUCCEEDED(sc) ) { // Load the file sc = _pHtmlFilter->QueryInterface( IID_IPersistFile, (void **) &_pPersistFile ); if ( SUCCEEDED(sc) ) { sc = _pPersistFile->Load( pszFileName, dwMode ); } else { _pHtmlFilter->Release(); _pHtmlFilter = 0; } } } return sc; } //Load //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::Save // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::Save( LPCWSTR pszFileName, BOOL fRemember ) { return E_FAIL; } //Save //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::SaveCompleted // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::SaveCompleted( LPCWSTR pszFileName ) { return E_FAIL; } //SaveCompleted //+------------------------------------------------------------------------- // // Method: HtmlPropIFilter::GetcurFile // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilter::GetCurFile( LPWSTR * ppszFileName ) { return _pPersistFile->GetCurFile( ppszFileName ); } //GetCurFile //+------------------------------------------------------------------------- // // Function: ParsePropertyFile // // Synopsis: Parses a property file and stores datatypes for html meta // properties. Property files are in .idq format, with a // [Names] section. // //-------------------------------------------------------------------------- void ParsePropertyFile() { // already parsed the file? if ( g_cEntries > 0 ) return; // open the property file in the system32 directory WCHAR awcFile[ MAX_PATH ]; GetSystemDirectory( awcFile, sizeof awcFile / sizeof WCHAR ); wcscat( awcFile, L"\\" ); wcscat( awcFile, pwcHTMLPropertyFile ); FILE *file = _wfopen( awcFile, L"r" ); if ( 0 != file ) { WCHAR awc[ 500 ]; BOOL fNames = FALSE; // for each line of the file while ( fgetws( awc, sizeof awc, file ) ) { int cwc = wcslen( awc ); if ( 0 == cwc ) continue; // trim off trailing newline if ( awc[ cwc - 1 ] == '\n' ) { cwc--; awc[cwc] = 0; } // is this a section line? if ( '[' == awc[0] ) { fNames = !_wcsicmp( L"[names]", awc ); } else if ( ( '#' != awc[0] ) && fNames ) { // parse the data type WCHAR *pwcType = wcschr( awc, L'(' ); if ( 0 != pwcType ) { pwcType++; while ( ' ' == *pwcType ) pwcType++; WCHAR *pwcX = wcstok( pwcType, L", )" ); if ( 0 != pwcType ) { // lookup the type in the list of types for ( ULONG i = 0; i < cTypeEntries; i++ ) { if ( !_wcsicmp( aTypeEntries[i].awcName, pwcType ) ) break; } // got the type, so find the guid if ( i < cTypeEntries ) { // find the property guid pwcType += 1 + wcslen( pwcType ); WCHAR *pwcName = wcschr( pwcType, '=' ); if ( pwcName ) { pwcName++; while ( ' ' == *pwcName ) pwcName++; // is this the html guid? if ( !_wcsnicmp( pwcName, pwcMetaProperty, wcslen( pwcMetaProperty ) ) ) { // find the property name pwcName = wcschr( pwcName, ' ' ); if ( pwcName ) { // skip white space while ( ' ' == *pwcName ) pwcName++; // got it -- save it away if it fits if ( ( wcslen( pwcName ) < cwcMaxName ) && ( g_cEntries < maxEntries ) ) { wcscpy( g_aEntries[g_cEntries].awcName, pwcName ); g_aEntries[g_cEntries++].dbType = aTypeEntries[i].dbType; } } } } } } } } } fclose( file ); } } //ParsePropertyFile //+------------------------------------------------------------------------- // // Method: HtmlPropIFilterCF::HtmlPropIFilterCF // // Synopsis: HTML Property IFilter class factory constructor // //-------------------------------------------------------------------------- HtmlPropIFilterCF::HtmlPropIFilterCF() { _lRefs = 1; long c = InterlockedIncrement( &g_cInstances ); if ( 0 == g_hHtmlDll && 1 == c ) { // load the html filter dll g_hHtmlDll = LoadLibrary( pwcHTMLFilter ); // load the property information ParsePropertyFile(); } } //HtmlPropIFilterCF //+------------------------------------------------------------------------- // // Method: HtmlPropIFilterCF::~HtmlPropIFilterCF // // Synopsis: HTML IFilter class factory constructor // //-------------------------------------------------------------------------- HtmlPropIFilterCF::~HtmlPropIFilterCF() { long c = InterlockedDecrement( &g_cInstances ); if ( 0 != g_hHtmlDll && 0 == c ) { FreeLibrary( g_hHtmlDll ); g_hHtmlDll = 0; } } //~HtmlPropIFilterCF //+------------------------------------------------------------------------- // // Method: HtmlPropIFilterCF::QueryInterface // // Synopsis: Rebind to other interface // // Arguments: [riid] -- IID of new interface // [ppvObject] -- New interface * returned here // // Returns: S_OK if bind succeeded, E_NOINTERFACE if bind failed // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilterCF::QueryInterface( REFIID riid, void ** ppvObject ) { if ( IID_IClassFactory == riid ) *ppvObject = (IUnknown *) (IClassFactory *) this; else if ( IID_IUnknown == riid ) *ppvObject = (IUnknown *) (IPersist *) this; else { *ppvObject = 0; return E_NOINTERFACE; } AddRef(); return S_OK; } //QueryInterface //+------------------------------------------------------------------------- // // Method: HtmlPropIFilterCF::AddRef // // Synopsis: Increments refcount // //-------------------------------------------------------------------------- ULONG STDMETHODCALLTYPE HtmlPropIFilterCF::AddRef() { return InterlockedIncrement( &_lRefs ); } //AddRef //+------------------------------------------------------------------------- // // Method: HtmlPropIFilterCF::Release // // Synopsis: Decrement refcount. Delete if necessary. // //-------------------------------------------------------------------------- ULONG STDMETHODCALLTYPE HtmlPropIFilterCF::Release() { long lTmp = InterlockedDecrement( &_lRefs ); if ( 0 == lTmp ) delete this; return lTmp; } //Release //+------------------------------------------------------------------------- // // Method: HtmlPropIFilterCF::CreateInstance // // Synopsis: Creates new HtmlPropIFilter object // // Arguments: [pUnkOuter] -- 'Outer' IUnknown // [riid] -- Interface to bind // [ppvObject] -- Interface returned here // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilterCF::CreateInstance( IUnknown * pUnkOuter, REFIID riid, void * * ppvObject ) { *ppvObject = 0; HtmlPropIFilter * pFilter = new HtmlPropIFilter; if ( 0 == pFilter ) return E_OUTOFMEMORY; SCODE sc = pFilter->QueryInterface( riid , ppvObject ); pFilter->Release(); return sc; } //CreateInstance //+------------------------------------------------------------------------- // // Method: HtmlPropIFilterCF::LockServer // // Synopsis: Force class factory to remain loaded // // Arguments: [fLock] -- TRUE if locking, FALSE if unlocking // // Returns: S_OK // //-------------------------------------------------------------------------- SCODE STDMETHODCALLTYPE HtmlPropIFilterCF::LockServer(BOOL fLock) { if(fLock) InterlockedIncrement( &g_cInstances ); else InterlockedDecrement( &g_cInstances ); return S_OK; } //LockServer //+------------------------------------------------------------------------- // // Function: DllGetClassObject // // Synopsis: Ole DLL load class routine // // Arguments: [cid] -- Class to load // [iid] -- Interface to bind to on class object // [ppvObj] -- Interface pointer returned here // // Returns: HTML Property filter class factory // //-------------------------------------------------------------------------- extern "C" SCODE STDMETHODCALLTYPE DllGetClassObject( REFCLSID cid, REFIID iid, void ** ppvObj ) { IUnknown * pUnk = 0; if ( CLSID_HtmlPropIFilter == cid || CLSID_HtmlPropClass == cid ) { pUnk = new HtmlPropIFilterCF; if ( 0 == pUnk ) return E_OUTOFMEMORY; } else { *ppvObj = 0; return E_NOINTERFACE; } SCODE sc = pUnk->QueryInterface( iid, ppvObj ); pUnk->Release(); return sc; } //DllGetClassObject //+------------------------------------------------------------------------- // // Method: DllCanUnloadNow // // Synopsis: Notifies DLL to unload (cleanup global resources) // // Returns: S_OK if it is acceptable for caller to unload DLL. // //-------------------------------------------------------------------------- extern "C" SCODE STDMETHODCALLTYPE DllCanUnloadNow( void ) { if ( 0 == g_cInstances ) return S_OK; else return S_FALSE; } //DllCanUnloadNow SClassEntry const aHtmlPropClasses[] = { { L".hhc", L"hhcfile", L"HHC file", L"{7f73b8f6-c19c-11d0-aa66-00c04fc2eddc}", L"HHC file" }, { L".htm", L"htmlfile", L"HTML file", L"{25336920-03f9-11cf-8fd0-00aa00686f13}", L"HTML file" }, { L".html", L"htmlfile", L"HTML file", L"{25336920-03f9-11cf-8fd0-00aa00686f13}", L"HTML file" }, { L".htx", L"htmlfile", L"HTML file", L"{25336920-03f9-11cf-8fd0-00aa00686f13}", L"HTML file" }, { L".stm", L"htmlfile", L"HTML file", L"{25336920-03f9-11cf-8fd0-00aa00686f13}", L"HTML file" }, { L".htw", L"htmlfile", L"HTML file", L"{25336920-03f9-11cf-8fd0-00aa00686f13}", L"HTML file" }, { L".asp", L"asp_auto_file", L"ASP auto file", L"{bd70c020-2d24-11d0-9110-00004c752752}", L"ASP auto file" }, }; SHandlerEntry const HtmlPropHandler = { L"{c694d910-a439-11d1-a903-00e098006ed3}", L"html property persistent handler", L"{f4309e80-a1db-11d1-a8fb-00e098006ed3}", }; SFilterEntry const HtmlPropFilter = { L"{f4309e80-a1db-11d1-a8fb-00e098006ed3}", L"html property filter", L"HtmlProp.dll", L"Both" }; DEFINE_DLLREGISTERFILTER( HtmlPropHandler, HtmlPropFilter, aHtmlPropClasses )