|
|
//+------------------------------------------------------------------------- // // 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 (c) 1998 Microsoft Corporation. All Rights Reserved. // // File: smpfilt.cxx // // Contents: Sample filter Implementation for Indexing Service // // Summary: The sample filter reads unformated text files (with the // extension .smp) using the current thread's ANSI code page // and outputs UNICODE text for the current locale. // // It accepts as input only single-byte-character text files, // and not multibyte-character or UNICODE text files. // // Platform: Windows 2000 // //--------------------------------------------------------------------------
//+------------------------------------------------------------------------- // // Include file Purpose // // windows.h Win32 declarations // filter.h IFilter interface declarations // filterr.h FACILITY_ITF error definitions for IFilter // ntquery.h Indexing Service declarations // filtreg.hxx DLL registration and unregistration macros // smpfilt.hxx Sample filter declarations // //--------------------------------------------------------------------------
#include <windows.h> #include <filter.h> #include <filterr.h> #include <ntquery.h> #include "filtreg.hxx" #include "smpfilt.hxx"
//C------------------------------------------------------------------------- // // Class: CSmpFilter // // Summary: Implements sample filter class // //--------------------------------------------------------------------------
//M------------------------------------------------------------------------- // // Method: CSmpFilter::CSmpFilter // // Summary: Class constructor // // Arguments: void // // Purpose: Manages global instance count // //--------------------------------------------------------------------------
CSmpFilter::CSmpFilter() : m_hFile(INVALID_HANDLE_VALUE), m_lRefs(1), m_pwszFileName(0), m_ulBufferLen(0), m_ulCharsRead(0), m_ulChunkID(1), m_fContents(FALSE), m_fEof(FALSE) { InterlockedIncrement( &g_lInstances ); }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::~CSmpFilter // // Summary: Class destructor // // Arguments: void // // Purpose: Manages global instance count and file handle // //--------------------------------------------------------------------------
CSmpFilter::~CSmpFilter() { delete [] m_pwszFileName; if ( INVALID_HANDLE_VALUE != m_hFile ) CloseHandle ( m_hFile );
InterlockedDecrement( &g_lInstances ); }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::QueryInterface (IUnknown::QueryInterface) // // Summary: Queries for requested interface // // Arguments: riid // [in] Reference IID of requested interface // ppvObject // [out] Address that receives requested interface pointer // // Returns: S_OK // Interface is supported // E_NOINTERFACE // Interface is not supported // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::QueryInterface( REFIID riid, void ** ppvObject ) { IUnknown *pUnkTemp = 0;
if ( IID_IFilter == riid ) pUnkTemp = (IUnknown *)(IFilter *)this; else if ( IID_IPersistFile == riid ) pUnkTemp = (IUnknown *)(IPersistFile *)this; else if ( IID_IPersist == riid ) pUnkTemp = (IUnknown *)(IPersist *)(IPersistFile *)this; else if ( IID_IUnknown == riid ) pUnkTemp = (IUnknown *)(IPersist *)(IPersistFile *)this; else { *ppvObject = NULL; return E_NOINTERFACE; }
*ppvObject = (void *)pUnkTemp; pUnkTemp->AddRef();
return S_OK; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::AddRef (IUnknown::AddRef) // // Summary: Increments interface refcount // // Arguments: void // // Returns: Value of incremented interface refcount // //--------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE CSmpFilter::AddRef() { return InterlockedIncrement( &m_lRefs ); }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::Release (IUnknown::Release) // // Summary: Decrements interface refcount, deleting if unreferenced // // Arguments: void // // Returns: Value of decremented interface refcount // //--------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE CSmpFilter::Release() { ULONG ulTmp = InterlockedDecrement( &m_lRefs );
if ( 0 == ulTmp ) delete this;
return ulTmp; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::Init (IFilter::Init) // // Summary: Initializes sample filter instance // // Arguments: grfFlags // [in] Flags for filter behavior // cAttributes // [in] Number attributes in array aAttributes // aAttributes // [in] Array of requested attribute strings // pFlags // [out] Pointer to return flags for additional properties // // Returns: S_OK // Initialization succeeded // E_FAIL // File not previously loaded // E_INVALIDARG // Count and contents of attributes do not agree // FILTER_E_ACCESS // Unable to access file to be filtered // FILTER_E_PASSWORD // (not implemented) // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::Init( ULONG grfFlags, ULONG cAttributes, FULLPROPSPEC const * aAttributes, ULONG * pFlags ) { // Ignore flags for text canonicalization (text is unformatted) // Check for proper attributes request and recognize only "contents"
if( 0 < cAttributes ) { ULONG ulNumAttr;
if ( 0 == aAttributes ) return E_INVALIDARG;
for ( ulNumAttr = 0 ; ulNumAttr < cAttributes; ulNumAttr++ ) { if ( guidStorage == aAttributes[ulNumAttr].guidPropSet && PID_STG_CONTENTS == aAttributes[ulNumAttr].psProperty.propid ) break; }
if ( ulNumAttr < cAttributes ) m_fContents = TRUE; else m_fContents = FALSE; } else if ( 0 == grfFlags || (grfFlags & IFILTER_INIT_APPLY_INDEX_ATTRIBUTES) ) m_fContents = TRUE; else m_fContents = FALSE;
m_fEof = FALSE;
// Open the file previously specified in call to IPersistFile::Load
if ( 0 != m_pwszFileName ) { if ( INVALID_HANDLE_VALUE != m_hFile ) { CloseHandle ( m_hFile ); m_hFile = INVALID_HANDLE_VALUE; }
m_hFile = CreateFile ( m_pwszFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
if ( INVALID_HANDLE_VALUE == m_hFile ) return FILTER_E_ACCESS; } else return E_FAIL;
// Enumerate OLE properties, since any NTFS file can have them
*pFlags = IFILTER_FLAGS_OLE_PROPERTIES;
// Re-initialize
m_ulChunkID = 1; m_ulCharsRead = 0;
return S_OK; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::GetChunk (IFilter::GetChunk) // // Summary: Gets the next chunk (text only) // // Note: GetChunk accepts as input only single-byte-character text // files, and not multibyte-character or UNICODE text files. // // Arguments: ppStat // [out] Pointer to description of current chunk // Returns: S_OK // Chunk was successfully retrieved // E_FAIL // Character conversion failed // FILTER_E_ACCESS // General access failure occurred // FILTER_E_END_OF_CHUNKS // Previous chunk was the last chunk // FILTER_E_EMBEDDING_UNAVAILABLE // (not implemented) // FILTER_E_LINK_UNAVAILABLE // (not implemented) // FILTER_E_PASSWORD // (not implemented) // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::GetChunk( STAT_CHUNK * pStat ) { if ( INVALID_HANDLE_VALUE == m_hFile ) return FILTER_E_ACCESS;
// Read characters from single-byte file char cszBuffer[TEXT_FILTER_CHUNK_SIZE];
if ( !ReadFile( m_hFile, cszBuffer, TEXT_FILTER_CHUNK_SIZE, &m_ulBufferLen, NULL ) ) return FILTER_E_ACCESS; else if( 0 == m_ulBufferLen ) m_fEof = TRUE;
if ( !m_fContents || m_fEof ) return FILTER_E_END_OF_CHUNKS;
// Convert single-byte characters to UNICODE // // This conversion assumes a one-to-one conversion: // one input single-byte character to // one output multibyte (UNICODE) character // // This is typically not the general case with multibyte // characters, and a general case needs to handle the possible // difference of pre- and post-conversion buffer lengths
if ( !MultiByteToWideChar( CP_THREAD_ACP, MB_COMPOSITE, cszBuffer, m_ulBufferLen, m_wcsBuffer, TEXT_FILTER_CHUNK_SIZE ) ) return E_FAIL;
// Set chunk description pStat->idChunk = m_ulChunkID; pStat->breakType = CHUNK_NO_BREAK; pStat->flags = CHUNK_TEXT; pStat->locale = GetSystemDefaultLCID(); pStat->attribute.guidPropSet = guidStorage; pStat->attribute.psProperty.ulKind = PRSPEC_PROPID; pStat->attribute.psProperty.propid = PID_STG_CONTENTS; pStat->idChunkSource = m_ulChunkID; pStat->cwcStartSource = 0; pStat->cwcLenSource = 0;
m_ulCharsRead = 0; m_ulChunkID++;
return S_OK; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::GetText (IFilter::GetText) // // Summary: Retrieves UNICODE text for index // // Arguments: pcwcBuffer // [in] Pointer to size of UNICODE buffer // [out] Pointer to count of UNICODE characters returned // awcBuffer // [out] Pointer to buffer to receive UNICODE text // // Returns: S_OK // Text successfully retrieved, but text remains in chunk // FILTER_E_NO_MORE_TEXT // All of the text in the current chunk has been returned // FILTER_S_LAST_TEXT // Next call to GetText will return FILTER_E_NO_MORE_TEXT // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::GetText( ULONG * pcwcBuffer, WCHAR * awcBuffer ) { if ( !m_fContents || 0 == m_ulBufferLen ) { *pcwcBuffer = 0; return FILTER_E_NO_MORE_TEXT; }
// Copy characters in chunk buffer to output UNICODE buffer // // This copy assumes a one-to-one conversion in GetChunk // of input single-byte characters (each 1 byte long) // to output UNICODE characters (each 2 bytes long)
ULONG ulToCopy = min( *pcwcBuffer, m_ulBufferLen - m_ulCharsRead );
memcpy( awcBuffer, m_wcsBuffer + m_ulCharsRead, 2*ulToCopy ); m_ulCharsRead += ulToCopy; *pcwcBuffer = ulToCopy;
if ( m_ulBufferLen == m_ulCharsRead ) { m_ulCharsRead = 0; m_ulBufferLen = 0; return FILTER_S_LAST_TEXT; }
return S_OK; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::GetValue (IFilter::GetValue) // // Summary: Retrieves properites for index // // Arguments: ppPropValue // [out] Address that receives pointer to property value // // Returns: FILTER_E_NO_VALUES // Always // FILTER_E_NO_MORE_VALUES // (not implemented) // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::GetValue( PROPVARIANT ** ppPropValue ) { // Sample filter does not retieve any properties
return FILTER_E_NO_VALUES; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::BindRegion (IFilter::BindRegion) // // Summary: Creates moniker or other interface for indicated text // // Arguments: origPos // [in] Description of text location and extent // riid // [in] Reference IID of specified interface // ppunk // [out] Address that receives requested interface pointer // // Returns: E_NOTIMPL // Always // FILTER_W_REGION_CLIPPED // (not implemented) // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::BindRegion( FILTERREGION origPos, REFIID riid, void ** ppunk ) { // BindRegion is currently reserved for future use
return E_NOTIMPL; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::GetClassID (IPersist::GetClassID) // // Summary: Retrieves the class id of the filter class // // Arguments: pClassID // [out] Pointer to the class ID of the filter // // Returns: S_OK // Always // E_FAIL // (not implemented) // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::GetClassID( CLSID * pClassID ) { *pClassID = CLSID_CSmpFilter; return S_OK; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::IsDirty (IPersistFile::IsDirty) // // Summary: Checks whether file has changed since last save // // Arguments: void // // Returns: S_FALSE // Always // S_OK // (not implemented) // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::IsDirty() { // File is opened read-only and never changes
return S_FALSE; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::Load (IPersistFile::Load) // // Summary: Opens and initializes the specified file // // Arguments: pszFileName // [in] Pointer to zero-terminated string // of absolute path of file to open // dwMode // [in] Access mode to open the file // // Returns: S_OK // File was successfully loaded // E_OUTOFMEMORY // File could not be loaded due to insufficient memory // E_FAIL // (not implemented) // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::Load( LPCWSTR pszFileName, DWORD dwMode ) { // Load just sets the filename for GetChunk to read and ignores the mode
ULONG ulChars = wcslen( pszFileName ) + 1; delete [] m_pwszFileName;
m_pwszFileName = new WCHAR [ulChars];
if ( 0 != m_pwszFileName ) wcscpy( m_pwszFileName, pszFileName ); else return E_OUTOFMEMORY;
return S_OK; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::Save (IPersistFile::Save) // // Summary: Saves a copy of the current file being filtered // // Arguments: pszFileName // [in] Pointer to zero-terminated string of // absolute path of where to save file // fRemember // [in] Whether the saved copy is made the current file // // Returns: E_FAIL // Always // S_OK // (not implemented) // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::Save( LPCWSTR pszFileName, BOOL fRemember ) { // File is opened read-only; saving it is an error
return E_FAIL; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::SaveCompleted (IPersistFile::SaveCompleted) // // Summary: Determines whether a file save is completed // // Arguments: pszFileName // [in] Pointer to zero-terminated string of // absolute path where file was previously saved // // Returns: S_OK // Always // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::SaveCompleted( LPCWSTR pszFileName ) { // File is opened read-only, so "save" is always finished
return S_OK; }
//M------------------------------------------------------------------------- // // Method: CSmpFilter::GetCurFile (IPersistFile::GetCurFile) // // Summary: Returns a copy of the current file name // // Arguments: ppszFileName // [out] Address to receive pointer to zero-terminated // string for absolute path to current file // // Returns: S_OK // A valid absolute path was successfully returned // S_FALSE // (not implemented) // E_OUTOFMEMORY // Operation failed due to insufficient memory // E_FAIL // Operation failed due to some reason // other than insufficient memory // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilter::GetCurFile( LPWSTR * ppszFileName ) { if ( 0 == m_pwszFileName ) return E_FAIL;
ULONG ulChars = wcslen( m_pwszFileName ) + 1; *ppszFileName = (WCHAR *)CoTaskMemAlloc(ulChars*sizeof(WCHAR));
if ( 0 != *ppszFileName ) wcscpy( *ppszFileName, m_pwszFileName ); else return E_OUTOFMEMORY;
return S_OK; }
//C------------------------------------------------------------------------- // // Class: CSmpFilterCF // // Summary: Implements class factory for sample filter // //--------------------------------------------------------------------------
//M------------------------------------------------------------------------- // // Method: CSmpFilterCF::CSmpFilterCF // // Summary: Class factory constructor // // Arguments: void // // Purpose: Manages global instance count // //--------------------------------------------------------------------------
CSmpFilterCF::CSmpFilterCF() : m_lRefs(1) { InterlockedIncrement( &g_lInstances ); }
//M------------------------------------------------------------------------- // // Method: CSmpFilterCF::~CSmpFilterCF // // Summary: Class factory destructor // // Arguments: void // // Purpose: Manages global instance count // //--------------------------------------------------------------------------
CSmpFilterCF::~CSmpFilterCF() { InterlockedDecrement( &g_lInstances ); }
//M------------------------------------------------------------------------- // // Method: CSmpFilterCF::QueryInterface (IUnknown::QueryInterface) // // Summary: Queries for requested interface // // Arguments: riid // [in] Reference IID of requested interface // ppvObject // [out] Address that receives requested interface pointer // // Returns: S_OK // Interface is supported // E_NOINTERFACE // Interface is not supported // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilterCF::QueryInterface( REFIID riid, void ** ppvObject ) { IUnknown *pUnkTemp;
if ( IID_IClassFactory == riid ) pUnkTemp = (IUnknown *)(IClassFactory *)this; else if ( IID_IUnknown == riid ) pUnkTemp = (IUnknown *)this; else { *ppvObject = NULL; return E_NOINTERFACE; }
*ppvObject = (void *)pUnkTemp; pUnkTemp->AddRef();
return S_OK; }
//M------------------------------------------------------------------------- // // Method: CSmpFilterCF::AddRef (IUknown::AddRef) // // Summary: Increments interface refcount // // Arguments: void // // Returns: Value of incremented interface refcount // //--------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE CSmpFilterCF::AddRef() { return InterlockedIncrement( &m_lRefs ); }
//M------------------------------------------------------------------------- // // Method: CSmpFilterCF::Release (IUnknown::Release) // // Summary: Decrements interface refcount, deleting if unreferenced // // Arguments: void // // Returns: Value of decremented refcount // //--------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE CSmpFilterCF::Release() { ULONG ulTmp = InterlockedDecrement( &m_lRefs );
if ( 0 == ulTmp ) delete this;
return ulTmp; }
//M------------------------------------------------------------------------- // // Method: CSmpFilterCF::CreateInstance (IClassFactory::CreateInstance) // // Summary: Creates new sample filter object // // Arguments: pUnkOuter // [in] Pointer to IUnknown interface of aggregating object // riid // [in] Reference IID of requested interface // ppvObject // [out] Address that receives requested interface pointer // // Returns: S_OK // Sample filter object was successfully created // CLASS_E_NOAGGREGATION // pUnkOuter parameter was non-NULL // E_NOINTERFACE // (not implemented) // E_OUTOFMEMORY // Sample filter object could not be created // due to insufficient memory // E_UNEXPECTED // Unsuccessful due to an unexpected condition // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilterCF::CreateInstance( IUnknown * pUnkOuter, REFIID riid, void * * ppvObject ) { CSmpFilter *pIUnk = 0;
if ( 0 != pUnkOuter ) return CLASS_E_NOAGGREGATION;
pIUnk = new CSmpFilter();
if ( 0 != pIUnk ) { if ( SUCCEEDED( pIUnk->QueryInterface( riid , ppvObject ) ) ) // Release extra refcount from QueryInterface pIUnk->Release(); else { delete pIUnk; return E_UNEXPECTED; } } else return E_OUTOFMEMORY;
return S_OK; }
//M------------------------------------------------------------------------- // // Method: CSmpFilterCF::LockServer (IClassFactory::LockServer) // // Summary: Forces/allows filter class to remain loaded/be unloaded // // Arguments: fLock // [in] TRUE to lock, FALSE to unlock // // Returns: S_OK // Always // E_FAIL // (not implemented) // E_OUTOFMEMORY // (not implemented) // E_UNEXPECTED // (not implemented) // //--------------------------------------------------------------------------
SCODE STDMETHODCALLTYPE CSmpFilterCF::LockServer( BOOL fLock ) { if( fLock ) InterlockedIncrement( &g_lInstances ); else InterlockedDecrement( &g_lInstances );
return S_OK; }
//+------------------------------------------------------------------------- // // DLL: SmpFilt.dll // // Summary: Implements Dynamic Link Library functions for sample filter // //--------------------------------------------------------------------------
//F------------------------------------------------------------------------- // // Function: DllMain // // Summary: Called from C-Runtime on process/thread attach/detach // // Arguments: hInstance // [in] Handle to the DLL // fdwReason // [in] Reason for calling DLL entry point // lpReserved // [in] Details of DLL initialization and cleanup // // Returns: TRUE // Always // //--------------------------------------------------------------------------
extern "C" BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD fdwReason, LPVOID lpvReserved ) { if ( DLL_PROCESS_ATTACH == fdwReason ) DisableThreadLibraryCalls( (HINSTANCE)hInstance );
return TRUE; }
//F------------------------------------------------------------------------- // // Function: DllGetClassObject // // Summary: Create sample filter class factory object // // Arguments: cid // [in] Class ID of class that class factory creates // iid // [in] Reference IID of requested class factory interface // ppvObj // [out] Address that receives requested interface pointer // // Returns: S_OK // Class factory object was created successfully // CLASS_E_CLASSNOTAVAILABLE // DLL does not support the requested class // E_INVALIDARG // (not implemented) // E_OUTOFMEMORY // Insufficient memory to create the class factory object // E_UNEXPECTED // Unsuccessful due to an unexpected condition // //--------------------------------------------------------------------------
extern "C" SCODE STDMETHODCALLTYPE DllGetClassObject( REFCLSID cid, REFIID iid, void ** ppvObj ) { IUnknown *pResult = 0;
if ( CLSID_CSmpFilter == cid ) pResult = (IUnknown *) new CSmpFilterCF; else return CLASS_E_CLASSNOTAVAILABLE;
if ( 0 != pResult ) { if( SUCCEEDED( pResult->QueryInterface( iid, ppvObj ) ) ) // Release extra refcount from QueryInterface pResult->Release(); else { delete pResult; return E_UNEXPECTED; } } else return E_OUTOFMEMORY;
return S_OK; }
//F------------------------------------------------------------------------- // // Function: DllCanUnloadNow // // Summary: Indicates whether it is possible to unload DLL // // Arguments: void // // Returns: S_OK // DLL can be unloaded now // S_FALSE // DLL must remain loaded // //--------------------------------------------------------------------------
extern "C" SCODE STDMETHODCALLTYPE DllCanUnloadNow( void ) { if ( 0 >= g_lInstances ) return S_OK; else return S_FALSE; }
//F------------------------------------------------------------------------- // // Function: DllRegisterServer // DllUnregisterServer // // Summary: Registers and unregisters DLL server // // The registration procedure uses a set of macros // developed for use within the Indexing Service code. // The macros are in the included filtreg.hxx. // // Returns: DllRegisterServer // S_OK // Registration was successful // SELFREG_E_CLASS // Registration was unsuccessful // SELFREG_E_TYPELIB // (not implemented) // E_OUTOFMEMORY // (not implemented) // E_UNEXPECTED // (not implemented) // DllUnregisterServer // S_OK // Unregistration was successful // S_FALSE // Unregistration was successful, but other // entries still exist for the DLL's classes // SELFREG_E_CLASS // (not implemented) // SELFREG_E_TYPELIB // (not implemented) // E_OUTOFMEMORY // (not implemented) // E_UNEXPECTED // (not implemented) // //--------------------------------------------------------------------------
//+------------------------------------------------------------------------- // // These structures define the registry keys for the registration process. // //--------------------------------------------------------------------------
SClassEntry const asmpClasses[] = { { L".smp", L"SmpFilt.Document", L"Sample Filter Document", L"{8B0E5E72-3C30-11d1-8C0D-00AA00C26CD4}", L"Sample Filter Document" } };
SHandlerEntry const smpHandler = { L"{8B0E5E73-3C30-11d1-8C0D-00AA00C26CD4}", L"SmpFilt Persistent Handler", L"{8B0E5E70-3C30-11d1-8C0D-00AA00C26CD4}" };
SFilterEntry const smpFilter = { L"{8B0E5E70-3C30-11d1-8C0D-00AA00C26CD4}", L"Sample Filter", L"smpfilt.dll", L"Both" };
//+------------------------------------------------------------------------- // // This macro defines the registration/unregistration routines for the DLL. // //--------------------------------------------------------------------------
DEFINE_DLLREGISTERFILTER( smpHandler, smpFilter, asmpClasses )
|