C++ Builder + Excel = ?
Zdrastvuyte. Mojno li s pomoshyu C++ Builder sozdat' progu kotoraya budet vvedonnuyu v neyo informaciyu soxranat' v Excel fayle. A takje dobavit' v programmu poisk kotoriy budet izvlekat' informaciyu iz togo je Excel fayla.
Можно.
Zdrastvuyte. Mojno li s pomoshyu C++ Builder sozdat' progu kotoraya budet vvedonnuyu v neyo informaciyu soxranat' v Excel fayle. A takje dobavit' v programmu poisk kotoriy budet izvlekat' informaciyu iz togo je Excel fayla.
1. В комплект C++Builder 6 входят компотенты для работы с M$ Office.
2. См. функции CreateObject, OlePropertySet, OlePropertyGet, OleFunction.
3. См. удобную библиотеку для экспорта в формат Excel vtxExport
4. См. Excel Component Suite
5. См. Flexcel
Можно также почитать хелп по DDE и OLE.
Можно воспользоваться поиском по форуму и найти следующие треды:
http://forum.codenet.ru/showthread.php?s=&threadid=27799
http://forum.codenet.ru/showthread.php?s=&threadid=26024
http://forum.codenet.ru/showthread.php?s=&threadid=25900
http://forum.codenet.ru/showthread.php?s=&threadid=25159
http://forum.codenet.ru/showthread.php?s=&threadid=24791
http://forum.codenet.ru/showthread.php?s=&threadid=24501
Также можно почитать выдержку из C++Builder FAQ (Отвечают ASm: Andrew Smirnov, 2:5030/538, DG: Dmitry Gushin, 2:5000/130.2, SE: Sergey Ezhov, [email]sestudio@iptelecom.net.ua[/email]):
>Q22: Почему не работает код:
> Variant v = Variant::CreateObject("Excel.Application");
> v.OlePropertySet("Visible",true);
A(SE): Из-за особенностей реализации OLE-сервера Excel русской локализации.
В Borland`s examples сказано, что примеры с OLE работают, только если
у вас стоит английская версия Word или Excel.
Hеобходимо использовать библиотеку типов Excel.
-+----------
>Q23: Как работать с OLE-сервером Excel с помощью библиотеки типов?
A(SE): Достаточно выполнить два шага:
Шаг 1.
*******
Подключаем библиотеку типов Excel к своему проекту.
Выбираем Project|Import Type Library. Hажимаем кнопку Add и ищем в каталоге
с офисом файл xl5en32.olb или excel8.olb для офиса-97. Открываем библиотеку
типов и жмем Ok. ВСВ создает файлы Excel_TLB.cpp и Excel_TLB.h и
подсоединяет их к проекту.
Шаг 2.
*******
Пишем код для запуска Excel:
...
Application_Disp app; // дисп-интерфейс для работы с объектом Application
try {
// пытаемся присоединится к запущенному Excel (а вдруг?)
HRESULT result = app.BindToActive(DIID_Application_);
if(!SUCCEEDED(result)) // в системе нет запущенного Excel
result = app.Bind(DIID_Application_); // запускаем...
if(SUCCEEDED(result)) // если все ок
app.Visible = true; // показываем Excel
}
catch (Exception& e) {
// здесь должна быть обработка ошибки
}
... // работаем с Excel, очень долго и плодотворно
app.Quit(); // ну а здесь принудительно завершаем работу с Excel
A(DG):
Категорически не согласен !!!
Попpобовал я эту TLB - все клево, только тоpмоза жуткие пpи компиляции.
(header TLB огpомный, пpекомпиляция не спасает) Вполне можно pаботать на
базе <comobj.hpp>
Вот пpимеp, котоpый у меня pаботает, и никаких "особенностей pеализации"
#include <comobj.hpp>
Variant app ;
Variant books ;
Variant book ;
Variant sheet ;
//...
app = CreateOleObject("Excel.Application");
books = app.OlePropertyGet("Workbooks");
books.Exec(Procedure("Open")<<"d:\\work\\finder\\files\\22222.xls");
book = books.OlePropertyGet("item",1);
sheet= book.OlePropertyGet("WorkSheets",1);
app.OlePropertySet("Visible", 1);
//...
для чтения/записи ячеек я использую две функции:
Variant __fastcall getValue(int row,int col)
{
return sheet.OlePropertyGet("Range", toText(row,col) );
}
char* __fastcall toText(int row,int col)
{
static char cellText[256] ;
cellText[0] = 'A' + col ;
sprintf(&cellText[1],"%d",row+1);
return cellText;
}
void __fastcall setValue(int row,int col,AnsiString as)
{
Variant r = sheet.OlePropertyGet("Range", toText(row,col) );
r.OlePropertySet("Value", String(as));
}
Все пpовеpено в бою на BCB3 с пачиком: BCB3P1CS.EXE. До пачика были
замечены слеты пpи возникновении Exception-ов.
A(SE):
По поводу использования TLB. Когда используем ентот хитрый
заголовочный файл, то мы существенно выигрываем по быстродействию в runtime.
Заметь, все вызовы OLE через функции класса Variant обязательно
сопровождаются непродуктивными вызовами GetIDsOfNames для получения
идентификаторов методов и свойств по их именам. Эта избитая тема обсуждается
во всех книгах по OLE. Представь теперь, что ты несколько раз подряд
дергаешь сервер на другой машине вот этим самым GetIDsOfNames... Жуть Ж:-(.
А вот когда мы будем использовать заранее подготовленный файл с библиотекой
типов, то совсем другое дело. Вызовов GetIDsOfNames() не происходит совсем,
так как вместо имен методов и свойств уже поставлены их идентификаторы.
Я согласен, что компиляция может несколько и удлиняется, но лучше подождать
на сборке, чем заставлять ждать пользователя, когда он работает с готовой
программой.
Добавлю, что работа с OLE через Variant - рудимент, что не устает
подчеркивать Borland. Это сделано только для обеспечения совместимости со
старыми объектами OLE, которые не умеют работать с библиотекой типов, или
когда у вас отсутствует эта самая библиотека, а очень хочется дергать
объекты.
Что касается примера с <comobj.hpp>, прошу уточнить, Какой Excel? Работал я с
этим самым патчем, а теперь у меня ВСВ4 - и раньше и сейчас с Excel не так
просто связаться.
A(ASm): Пример работы с Excel. Пробовалось все на связке builder 3 и
Excel разных версий.
Для успешной работы с русским excel надо подправить файлы comobj.pas и
oleauto.pas (они лежат в \source\vcl), после чего подключить их к проекту.
У меня по какой-то причине затребовался ffmt, посему ffmt.asm также был
подключен к проекту. Внесенные в исходники VCL изменения действуют, когда в
опциях проекта убрана галка build with runtime packages (или что-то в этом
роде).
comobj.pas:
в районе строки (1326) GetThreadLocale заменяем на выражение в скобках, в
результате сей фрагмент выглядит так:
Temp := Dispatch.GetIDsOfNames(GUID_NULL, NameRefs, NameCount,
{ GetThreadLocale,}
((LANG_ENGLISH+SUBLANG_DEFAULT*1024)+SORT_DEFAULT* 65536 ),
DispIDs);
oleauto.pas:
в районе строки (809):
вместо
if Dispatch.GetIDsOfNames(GUID_NULL, @NameRefs, NameCount,
LOCALE_SYSTEM_DEFAULT, DispIDs) <> 0 then
ставим
if Dispatch.GetIDsOfNames(GUID_NULL, @NameRefs, NameCount,
((LANG_ENGLISH+SUBLANG_DEFAULT*1024)+SORT_DEFAULT* 65536 ),