Извлечь текст из документов MS Office на VC++. || Проблема с IpFilter

Тема в разделе "LANGS.C", создана пользователем Stampler, 10 мар 2010.

  1. Stampler

    Stampler New Member

    Публикаций:
    0
    Регистрация:
    10 мар 2010
    Сообщения:
    5
    Здравствуйте.
    Возникла необходимость получить текст из MsOffice документов (.doc,.docx,.xls). без помощи VB макросов итп.
    Пишу на Visual C++ 2008 Pro.
    Вопрос: Есть ли какие-то готовые решения(dll,класса итп) по этому поводу ? как изьять текст из оффлайн-документа ?

    Долго и упорно вникал в гугл, где было найдено решение гордо именуемое "IFilter".
    а также пример его использования.
    Проект компилировуется, но работать должным образом отказывается.
    Дебаг показал, что при вызове:
    HRESULT hr = LoadIFilter( lpwSrc, NULL, (void**)&pUnk );
    filterDoc.h требует инициализации CoInitialize(NULL);

    Инициализировал (добавив CoInitialize(NULL); в начало и CoUninitialize(); в конец filterDoc.h),
    но потом появляется другая, непонятная мне ошибка в пошаговом дебаге
    "hr | 0x80030002 Не удается найти %1. | HRESULT"
    Имя документа для изьятия текста менял. оно иммет вид не "1*.doc".
    Как исправить ? поделитесь опытом !
    Спасибо за ваши ответы.

    Код прилагаю:

    Код (C):
    1. ////////////////////////////////////////////////////////////////////////
    2. //[] Filename   : filterDoc.h
    3. ////////////////////////////////////////////////////////////////////////
    4.  
    5. # ifndef _filterDoc_h_
    6. #   define _filterDoc_h_
    7.  
    8. # include <windows.h>
    9. # include <ntquery.h>
    10. # include <filter.h>
    11. # include <filterr.h>
    12. # include <atlbase.h>
    13. # include <atlconv.h>
    14.  
    15. bool ReallocBuffer( LPVOID& lpDst, ULONG ulDst, LPVOID lpSrc, ULONG ulSrc )
    16. {
    17.   if ( 0 == ulDst )
    18.     lpDst = NULL;
    19.   else
    20.   {
    21.     lpDst = CoTaskMemAlloc( ulDst );
    22.     if ( NULL == lpDst )
    23.       return false;
    24.   }
    25.  
    26.   if ( NULL != lpSrc )
    27.   {
    28.     if ( 0 != ulSrc )
    29.       CopyMemory( lpDst, lpSrc, (ulDst < ulSrc) ? ulDst : ulSrc );
    30.     CoTaskMemFree( lpSrc );
    31.   }
    32.  
    33.   return true;
    34. }
    35.  
    36. HRESULT FilterDocument( const WCHAR* lpwSrc, LPVOID& lpvData, ULONG& ccvData )
    37. {
    38.   lpvData = NULL;
    39.   ccvData = 0;
    40.  
    41.   if ( NULL == lpwSrc || 0 == *lpwSrc )
    42.     return E_INVALIDARG;
    43.  
    44.   CComPtr<IUnknown> pUnk    = NULL;
    45.   CComPtr<IFilter>  pFilter = NULL;
    46.   HRESULT           hr      = LoadIFilter( lpwSrc, NULL, (void**)&pUnk );
    47.   LPVOID            lpv     = NULL;
    48.   ULONG             ccv     = 0x10000;
    49.  
    50.   if ( S_OK != hr || NULL == (IUnknown*)pUnk )
    51.     return E_FAIL;
    52.  
    53.   hr = pUnk->QueryInterface( IID_IFilter, (void**)&pFilter );
    54.   if ( S_OK != hr || NULL == (IFilter*)pFilter )
    55.     return hr;
    56.  
    57.   ULONG lFlags = 0;
    58.   hr = pFilter->Init(
    59.     IFILTER_INIT_CANON_PARAGRAPHS       |
    60.     IFILTER_INIT_CANON_HYPHENS          |
    61.     IFILTER_INIT_CANON_SPACES           |
    62.     IFILTER_INIT_APPLY_INDEX_ATTRIBUTES |
    63.     IFILTER_INIT_INDEXING_ONLY,
    64.     0,
    65.     NULL,
    66.     &lFlags );
    67.   if ( S_OK != hr )
    68.     return hr;
    69.  
    70.   if ( ! ReallocBuffer( lpv, ccv, NULL, 0 ) )
    71.     return E_OUTOFMEMORY;
    72.  
    73.   STAT_CHUNK chunk = { 0 };
    74.   chunk.breakType = CHUNK_EOP;
    75.  
    76.   WCHAR  wbBuf[0x1000]  = { 0 };
    77.   ULONG  lSize          = sizeof(wbBuf)/sizeof(wbBuf[0]);
    78.   ULONG  lRes           = 0;
    79.   DWORD  dwWritten      = 0;
    80.  
    81.   USES_CONVERSION;
    82.  
    83.   for ( ; ; )
    84.   {
    85.     hr = pFilter->GetChunk( &chunk );
    86.  
    87.     if ( FILTER_E_END_OF_CHUNKS == hr )
    88.       break;
    89.     switch ( hr )
    90.     {
    91.     case FILTER_E_EMBEDDING_UNAVAILABLE :
    92.     case FILTER_E_LINK_UNAVAILABLE      : continue;
    93.     case FILTER_E_PASSWORD              :
    94.     case FILTER_E_ACCESS                : return E_FAIL;
    95.     default                             : break;
    96.     }
    97.  
    98.     for ( ; ; )
    99.     {
    100.       lRes = lSize;
    101.       hr = pFilter->GetText( &lRes, wbBuf );
    102.  
    103.       if ( FILTER_E_NO_TEXT == hr         // the chunk contains no text
    104.         || FILTER_E_NO_MORE_TEXT  == hr ) // the chunk contains no more text
    105.         break;
    106.  
    107.       wbBuf[lRes] = 0;
    108.       if ( ccv < ( lRes + dwWritten ) * 2 )
    109.       {
    110.         LPVOID  lpvT  = NULL;
    111.         ULONG   ccvT  = ( lRes + dwWritten ) * 2 + 0x10000;
    112.         if ( ! ReallocBuffer( lpvT, ccvT, lpv, dwWritten * 2 ) )
    113.         {
    114.           ReallocBuffer( lpv, 0, lpv, ccv );
    115.           return E_FAIL;
    116.         }
    117.         lpv = lpvT;
    118.         ccv = ccvT;
    119.       }
    120.  
    121.       CopyMemory( (WCHAR*)lpv + dwWritten, wbBuf, lRes * 2 );
    122.       dwWritten += lRes;
    123.       ((WCHAR*)lpv)[dwWritten] = 0;
    124.  
    125.       if ( FILTER_S_LAST_TEXT == hr ) /// This was last text fragment in chunk
    126.         break;
    127.     }
    128.  
    129.   }
    130.  
    131.   lpvData = lpv;
    132.   ccvData = dwWritten * 2;
    133.  
    134.   return S_OK;
    135. }
    136.  
    137. # endif // _filterDoc_h_
    138.  
    139.  
    140.  
    141. ////////////////////////////////////////////////////////////////////////
    142. //[] Filename   : flttest.cpp
    143. //[] Description: Contains an entry point of IFilter tester.
    144. ////////////////////////////////////////////////////////////////////////
    145.  
    146. # ifdef _MSC_VER
    147. #   pragma comment ( lib, "ntquery.lib" )
    148. # endif
    149.  
    150. # include <atlbase.h>
    151. # include <atlconv.h>
    152. # include <io.h>
    153. # include <fcntl.h>
    154. # include <stdio.h>
    155. # include "filterDoc.h"
    156.  
    157. static char gszAbout[] =
    158.   "RSDN Group (R) QnA on IFilter Code Sample\n";
    159. static char gszUsage[] =
    160.   "Usage: flttest [srcfile [dstfile]]\n";
    161.  
    162. int main( int argc, char* argv[] )
    163. {
    164.   FILE* os  = stdout;
    165.   int   err = 0;
    166.  
    167.   fprintf( stderr, gszAbout );
    168.  
    169.   if ( argc == 1 || ( argc > 1 && 0 == *argv[1] ) )
    170.   {
    171.     fprintf( stderr, gszUsage );
    172.     return 0;
    173.   }
    174.   if ( argc >= 3 && 0 != *argv[2] )
    175.   {
    176.     os = fopen( argv[2], "wb" );
    177.     if ( NULL == os )
    178.       os = stdout;
    179.   }
    180.  
    181.   if ( os == stdout )
    182.     setmode( fileno( stdout ), O_BINARY );
    183.  
    184.   USES_CONVERSION;
    185.  
    186.   WCHAR*  lpwName = A2W( argv[1] );
    187.   LPVOID  lpvData = NULL;
    188.   ULONG   ccvData = NULL;
    189.   HRESULT hr      = S_OK;
    190.  
    191.   hr = FilterDocument( lpwName, lpvData, ccvData );
    192.   if ( S_OK == hr )
    193.   {
    194.     fwrite( lpvData, sizeof(char), ccvData, os );
    195.    
    196.     ReallocBuffer( lpvData, 0, lpvData, 0 );
    197.   } else {
    198.     fprintf( stderr, "Filtering failed!\n" );
    199.     err = 1;
    200.   }
    201.  
    202.   if ( os != stdout )
    203.     fclose( os );
    204.  
    205.   return err;
    206. }
     
  2. valterg

    valterg Active Member

    Публикаций:
    0
    Регистрация:
    19 авг 2004
    Сообщения:
    2.105
    Если я правильно понял по поиску, IFilter - это переходник, использующий механизм COM-сервер и без установленного Офиса искать не умеет.
    - это означает что не запускается Ворд.
    IFilter это не есть доступ ко всему, а это механизм поиска по всему к чему есть реализованная читалка. Возможно я ошибаюсь.
     
  3. Z3N

    Z3N New Member

    Публикаций:
    0
    Регистрация:
    10 фев 2009
    Сообщения:
    812
    Если только текст, то можно извлечь вручную.
    doc файлы - это компаунд файлы (OLE хранилища).
    А docx - это вообще зип файл :).
    В первом случае будут явные проблемы со структурой файла.
     
  4. Stampler

    Stampler New Member

    Публикаций:
    0
    Регистрация:
    10 мар 2010
    Сообщения:
    5
    Окей, буду заниматься. Спасибо =)
    про zip файлы дико удивило )
    Достаточно распаковать, и в "word/document.xml" будет находиться текст.
    эко чудо )
     
  5. BaGiE

    BaGiE New Member

    Публикаций:
    0
    Регистрация:
    27 мар 2005
    Сообщения:
    84
    Адрес:
    Mordor
    http://www.codeproject.com/KB/cs/getwordtext.aspx тут хороший пример, правда на C#, но и работа с COM.

    А если по поводу Excel, то их можно открыть через OLEDB. Например через Microsoft.Jet.OLEDB.4.0 или поставить Microsoft Office Access 2007 Runtime (SP2) и юзать поставщик Microsoft.ACE.OLEDB.12.0 с параметром Extended Properties=Excel 12.0
    Второй умеет также открывать xlsx.
     
  6. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Интересно, а спустя столько лет появилась какая-нибудь реализация на чистом Си/С++ или нет? Сишарп не знаю, врядли перепишу.
     
  7. f13nd

    f13nd Well-Known Member

    Публикаций:
    0
    Регистрация:
    22 июн 2009
    Сообщения:
    1.993
    Код (C++):
    1. //Обработчик события закрытия документа. Устанавливает флаг Saved и забирает данные из документа.
    2. void CWordEventSink::OnDocClose(){
    3.     m_pWord->ActiveDocument->Saved = VARIANT_TRUE;
    4.             IDataObject *pDataObjectDispatch;
    5.                 if (SUCCEEDED(m_pWord->ActiveDocument->QueryInterface(IID_IDataObject,(void **)&pDataObjectDispatch))){
    6.                     IEnumFORMATETC *EnumFormatEtc;
    7.                     pDataObjectDispatch->EnumFormatEtc(DATADIR_GET,&EnumFormatEtc);
    8.                     FORMATETC FormatEtc;
    9.                     TCHAR FormatName[MAX_PATH];
    10.                     //Перечисляем доступные форматы, находим среди них RTF
    11.                     while (EnumFormatEtc->Next(1,&FormatEtc,0)==S_OK){
    12.                         GetClipboardFormatName(FormatEtc.cfFormat,(LPWSTR)&FormatName,MAX_PATH);
    13.                         if (lstrcmp((LPCWSTR)&FormatName,L"Rich Text Format")==0){
    14.                             STGMEDIUM StgMedium;
    15.                             //Берем данные в формате RTF
    16.                             if (SUCCEEDED(pDataObjectDispatch->GetData(&FormatEtc,&StgMedium))){
    17.                                 LPVOID RtfBuffer = GlobalLock(StgMedium.hGlobal);
    Она всегда существовала, не понимаю в чем проблема.
     
    M0rg0t нравится это.
  8. M0rg0t

    M0rg0t Well-Known Member

    Публикаций:
    0
    Регистрация:
    18 окт 2010
    Сообщения:
    1.576
    Проблема в моем незнании СОМ и C#. Спасибо, буду разбираться.