Здравствуйте. Возникла необходимость получить текст из 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): //////////////////////////////////////////////////////////////////////// //[] Filename : filterDoc.h //////////////////////////////////////////////////////////////////////// # ifndef _filterDoc_h_ # define _filterDoc_h_ # include <windows.h> # include <ntquery.h> # include <filter.h> # include <filterr.h> # include <atlbase.h> # include <atlconv.h> bool ReallocBuffer( LPVOID& lpDst, ULONG ulDst, LPVOID lpSrc, ULONG ulSrc ) { if ( 0 == ulDst ) lpDst = NULL; else { lpDst = CoTaskMemAlloc( ulDst ); if ( NULL == lpDst ) return false; } if ( NULL != lpSrc ) { if ( 0 != ulSrc ) CopyMemory( lpDst, lpSrc, (ulDst < ulSrc) ? ulDst : ulSrc ); CoTaskMemFree( lpSrc ); } return true; } HRESULT FilterDocument( const WCHAR* lpwSrc, LPVOID& lpvData, ULONG& ccvData ) { lpvData = NULL; ccvData = 0; if ( NULL == lpwSrc || 0 == *lpwSrc ) return E_INVALIDARG; CComPtr<IUnknown> pUnk = NULL; CComPtr<IFilter> pFilter = NULL; HRESULT hr = LoadIFilter( lpwSrc, NULL, (void**)&pUnk ); LPVOID lpv = NULL; ULONG ccv = 0x10000; if ( S_OK != hr || NULL == (IUnknown*)pUnk ) return E_FAIL; hr = pUnk->QueryInterface( IID_IFilter, (void**)&pFilter ); if ( S_OK != hr || NULL == (IFilter*)pFilter ) return hr; ULONG lFlags = 0; hr = pFilter->Init( IFILTER_INIT_CANON_PARAGRAPHS | IFILTER_INIT_CANON_HYPHENS | IFILTER_INIT_CANON_SPACES | IFILTER_INIT_APPLY_INDEX_ATTRIBUTES | IFILTER_INIT_INDEXING_ONLY, 0, NULL, &lFlags ); if ( S_OK != hr ) return hr; if ( ! ReallocBuffer( lpv, ccv, NULL, 0 ) ) return E_OUTOFMEMORY; STAT_CHUNK chunk = { 0 }; chunk.breakType = CHUNK_EOP; WCHAR wbBuf[0x1000] = { 0 }; ULONG lSize = sizeof(wbBuf)/sizeof(wbBuf[0]); ULONG lRes = 0; DWORD dwWritten = 0; USES_CONVERSION; for ( ; ; ) { hr = pFilter->GetChunk( &chunk ); if ( FILTER_E_END_OF_CHUNKS == hr ) break; switch ( hr ) { case FILTER_E_EMBEDDING_UNAVAILABLE : case FILTER_E_LINK_UNAVAILABLE : continue; case FILTER_E_PASSWORD : case FILTER_E_ACCESS : return E_FAIL; default : break; } for ( ; ; ) { lRes = lSize; hr = pFilter->GetText( &lRes, wbBuf ); if ( FILTER_E_NO_TEXT == hr // the chunk contains no text || FILTER_E_NO_MORE_TEXT == hr ) // the chunk contains no more text break; wbBuf[lRes] = 0; if ( ccv < ( lRes + dwWritten ) * 2 ) { LPVOID lpvT = NULL; ULONG ccvT = ( lRes + dwWritten ) * 2 + 0x10000; if ( ! ReallocBuffer( lpvT, ccvT, lpv, dwWritten * 2 ) ) { ReallocBuffer( lpv, 0, lpv, ccv ); return E_FAIL; } lpv = lpvT; ccv = ccvT; } CopyMemory( (WCHAR*)lpv + dwWritten, wbBuf, lRes * 2 ); dwWritten += lRes; ((WCHAR*)lpv)[dwWritten] = 0; if ( FILTER_S_LAST_TEXT == hr ) /// This was last text fragment in chunk break; } } lpvData = lpv; ccvData = dwWritten * 2; return S_OK; } # endif // _filterDoc_h_ //////////////////////////////////////////////////////////////////////// //[] Filename : flttest.cpp //[] Description: Contains an entry point of IFilter tester. //////////////////////////////////////////////////////////////////////// # ifdef _MSC_VER # pragma comment ( lib, "ntquery.lib" ) # endif # include <atlbase.h> # include <atlconv.h> # include <io.h> # include <fcntl.h> # include <stdio.h> # include "filterDoc.h" static char gszAbout[] = "RSDN Group (R) QnA on IFilter Code Sample\n"; static char gszUsage[] = "Usage: flttest [srcfile [dstfile]]\n"; int main( int argc, char* argv[] ) { FILE* os = stdout; int err = 0; fprintf( stderr, gszAbout ); if ( argc == 1 || ( argc > 1 && 0 == *argv[1] ) ) { fprintf( stderr, gszUsage ); return 0; } if ( argc >= 3 && 0 != *argv[2] ) { os = fopen( argv[2], "wb" ); if ( NULL == os ) os = stdout; } if ( os == stdout ) setmode( fileno( stdout ), O_BINARY ); USES_CONVERSION; WCHAR* lpwName = A2W( argv[1] ); LPVOID lpvData = NULL; ULONG ccvData = NULL; HRESULT hr = S_OK; hr = FilterDocument( lpwName, lpvData, ccvData ); if ( S_OK == hr ) { fwrite( lpvData, sizeof(char), ccvData, os ); ReallocBuffer( lpvData, 0, lpvData, 0 ); } else { fprintf( stderr, "Filtering failed!\n" ); err = 1; } if ( os != stdout ) fclose( os ); return err; }
Если я правильно понял по поиску, IFilter - это переходник, использующий механизм COM-сервер и без установленного Офиса искать не умеет. - это означает что не запускается Ворд. IFilter это не есть доступ ко всему, а это механизм поиска по всему к чему есть реализованная читалка. Возможно я ошибаюсь.
Если только текст, то можно извлечь вручную. doc файлы - это компаунд файлы (OLE хранилища). А docx - это вообще зип файл . В первом случае будут явные проблемы со структурой файла.
Окей, буду заниматься. Спасибо =) про zip файлы дико удивило ) Достаточно распаковать, и в "word/document.xml" будет находиться текст. эко чудо )
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.
Интересно, а спустя столько лет появилась какая-нибудь реализация на чистом Си/С++ или нет? Сишарп не знаю, врядли перепишу.
Код (C++): //Обработчик события закрытия документа. Устанавливает флаг Saved и забирает данные из документа. void CWordEventSink::OnDocClose(){ m_pWord->ActiveDocument->Saved = VARIANT_TRUE; IDataObject *pDataObjectDispatch; if (SUCCEEDED(m_pWord->ActiveDocument->QueryInterface(IID_IDataObject,(void **)&pDataObjectDispatch))){ IEnumFORMATETC *EnumFormatEtc; pDataObjectDispatch->EnumFormatEtc(DATADIR_GET,&EnumFormatEtc); FORMATETC FormatEtc; TCHAR FormatName[MAX_PATH]; //Перечисляем доступные форматы, находим среди них RTF while (EnumFormatEtc->Next(1,&FormatEtc,0)==S_OK){ GetClipboardFormatName(FormatEtc.cfFormat,(LPWSTR)&FormatName,MAX_PATH); if (lstrcmp((LPCWSTR)&FormatName,L"Rich Text Format")==0){ STGMEDIUM StgMedium; //Берем данные в формате RTF if (SUCCEEDED(pDataObjectDispatch->GetData(&FormatEtc,&StgMedium))){ LPVOID RtfBuffer = GlobalLock(StgMedium.hGlobal); Она всегда существовала, не понимаю в чем проблема.