Загрузите IFilter без реестра

поэтому мне сказали, что у меня может не быть доступа к реестру или программам, с помощью которых обычно загружаются их фильтры IFilter в систему, поэтому мне приходится включать библиотеки DLL IFilter в приложение и загружать их непосредственно оттуда. В настоящее время я использую классы IFilter С# CodeProject, но у меня все еще есть несколько вещей, которые не укладываются в мою голову, когда дело доходит до filterPersistClass, persistHandlerClass и COM, и поэтому я немного потерялся в том, как я мог заставить это работать.

Я сделал все обыденные вещи, такие как получение dll, настройка файла ресурсов с «Расширением, путем DLL» и так далее, но просто не могу понять, как теперь загрузить DLL IFilter. Возможно, мне следует начать с нуля, но я подумал, что сначала попрошу помощи.

ИЗМЕНИТЬ (частичное решение)

Ну, я понял, как загрузить query.dll, используя приведенный ниже код в конструкторе FilterReader в FilterReader.cs, хотя сейчас у меня проблемы с загрузкой файла PDFFilter.dll, и я получаю следующую ошибку:

Не удалось найти точку входа с именем «LoadIFilter» в DLL «C:\Program Files\Adobe\Adobe PDF iFilter 9 для 64-разрядных платформ\bin\PDFFilter.dll»

Проблема, на которой я сейчас застрял, заключается в том, что PDFFilter.dll использует STA, а приложения C # - MTA.

[DllImport("query.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int LoadIFilter(string pwcsPath, [MarshalAs(UnmanagedType.IUnknown)] ref object pUnkOuter, ref IFilter ppIUnk);

// --------------------------- constructor ----------------------------------

var isFilter = false;
object iUnknown = null;

LoadIFilter(fileName, ref iUnknown, ref _filter);

var persistFile = (_filter as IPersistFile);
if (persistFile != null)
{
    persistFile.Load(fileName, 0);
    IFILTER_FLAGS flags;
    IFILTER_INIT iflags =
        IFILTER_INIT.CANON_HYPHENS |
        IFILTER_INIT.CANON_PARAGRAPHS |
        IFILTER_INIT.CANON_SPACES |
        IFILTER_INIT.APPLY_INDEX_ATTRIBUTES |
        IFILTER_INIT.HARD_LINE_BREAKS |
        IFILTER_INIT.FILTER_OWNED_VALUE_OK;

    if (_filter.Init(iflags, 0, IntPtr.Zero, out flags) == IFilterReturnCode.S_OK)
        isFilter = true;
}

if (_filter != null && isFilter) return;
if (_filter != null) Marshal.ReleaseComObject(_filter);

person Luke    schedule 23.10.2012    source источник
comment
Что ж, прекратите то, что вы делаете, потому что без регистрации фильтров это не сработает. Индексатор использует реестр для поиска фильтров.   -  person Hans Passant    schedule 24.10.2012


Ответы (1)


В IFilter объектах нет ничего волшебного. Они размещаются в стандартных библиотеках COM. В конце концов, все, что вам нужно, это clsid класса, который умеет обрабатывать pdf файлов.

Функция LoadIFilter в query.dll — это просто удобная вспомогательная функция. Все, что он делает, вы можете сделать сами.

В реестре есть стандартный способ, при котором расширение файла (например, .pdf) преобразуется в clsid (например, {E8978DA6-047F-4E3D-9C78-CDBE46041603})

Примечание. Вы также можете просто перейти к концу и узнать, что clsid реализации Adobe IFilter — {E8978DA6-047F-4E3D-9C78-CDBE46041603}. Но это не гарантируется, поэтому вам нужно просканировать реестр.

Алгоритм преобразования .ext в clsid объекта, реализующего IFilter:

GetIFilterClassIDForFileExtension(String extension)   
    arguments:   
        extension (String) e.g. ".pdf"   
    returns:    
        clsid (Guid) e.g. 

    //Get the Persistent Handler for this extension
    //e.g. 
    //   HKLM\Software\Classes\.pdf\PersistentHandler\(Default)
    //returns
    //   "{F6594A6D-D57F-4EFD-B2C3-DCD9779E382E}"
    persistentHandlerGuid = HKLM\Software\Classes\.pdf\PersistentHandler\(Default)

    //Get the clsid associated with this persistent handler
    //e.g. 
    //   HKLM\Software\Classes\CLSID\{F6594A6D-D57F-4EFD-B2C3-DCD9779E382E}\PersistentAddinsRegistered\{89BCB740-6119-101A-BCB7-00DD010655AF}
    //where the final guid is the interface identifier (IID) of IFilter
    clsid = HKLM\persistentHandlerGuid\PersistentAddinsRegistered\{89BCB740-6119-101A-BCB7-00DD010655AF}

    //e.g. returns "{E8978DA6-047F-4E3D-9C78-CDBE46041603}", the clsid of Adobe's PDF IFilter
    return clsid

Когда у вас есть clsid соответствующего объекта, вы создаете его с помощью:

Guid clsid = GetIFilterClassForFileExtension(".pdf")
IFilter filter = CreateComObject(clsid);

Теперь у вас есть вся суть функции LoadIFilter из query.dll:

IFilter LoadIFilter(String filename)
{
   String extension = ExtractFileExt(filename); //e.g. "foo.pdf" --> ".pdf"
   Guid clsid = GetIFilterClassForFileExtension(extension);
   return CreateComObject(clsid) as IFilter;
}

Теперь все это по-прежнему требует реестра, потому что вы все еще должны иметь возможность преобразовать расширение в clsid. Если вы уже знаете classid, вам не нужен реестр:

IFilter adobeIFilterForPdfs = CreateComObject("{E8978DA6-047F-4E3D-9C78-CDBE46041603}")

И вы можете идти.

Важным моментом является то, что функция, которую вы пытаетесь вызвать, LoadIFilter не находится внутри dll Adobe (или любой другой dll IFilter, предоставленной любой другой компанией, для сканирования любых других типов файлов). Функция LoadIFilter экспортируется query.dll и является просто вспомогательной функцией для описанных выше шагов.

Все библиотеки IFilter являются библиотеками COM. Задокументированный способ загрузки COM-dll — через функцию CoCreateInstance:

IUnknown CreateComObject(Guid ClassID)
{
   IUnknown unk;

   HRESULT hr = CoCreateInstance(ClassID, null, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IUnknown, ref unk);
   if (Failed(hr))
      throw new Exception("Could not create instance: "+hr);
   return unk;
}

Я оставлю вам возможность найти правильный способ создания COM-объекта из управляемого кода C#. Я забыт.

Примечание. Любой код, опубликованный в открытом доступе. Атрибуция не требуется.

person Ian Boyd    schedule 15.03.2014