![]() ![]() ![]() ![]() ![]() |
Интеграция CA-Visual Objects с Microsoft Word и Excel | 2/4 |
Использование DDE
Есть три решения как использовать Dynamic Data Exchange для интеграции CA-Visul Objects с MS-Excel - Excel является клиентом, сервером, клиентом и сервером. В большинстве ситуаций Вы захотите иметь в качестве сервера Excel, в качестве клиента свое приложение. Ваше приложение делает запросы - Excel возвращает данные по этим запросам.
Когда два приложения соединены, то говорят, что между ними "conversation" (беседа). Для инициализации "conversation" (давайте я это обзову конвисэйшн) (также как Вы набираете номер телефона для разговора) необходимо вызвать Excel сервер с именем "Excel", когда Вы конструируете IPC клиента.
oExcelDDEClient := IPCClient{"EXCEL"}
К сожалению, если Excel еще не загружен в память, то появиться сообщение об ошибке, так что сперва Вам следует удостовериться о его наличии в памяти. Функция Windows API GetModuleHandle() может определить активен ли Excel в данный момент. Следующая приведенная функция демонстрирует трюк проверки наличия Excel в памяти, и если нет, то загрузку его в память.
FUNCTION LoadExcel() LOCAL nPtr AS PTR LOCAL nResult AS INT LOCAL lLoadOK AS LOGIC nPtr := GetModuleHandle(String2PSZ("EXCEL.EXE")) IF nPtr == 0 nResult := ; WinExec(String2PSZ("C:\MSOFFICE\EXCEL\EXCEL.EXE"),; SW_SHOWMINNOACTIVE) IF nResult > 32 lLoadOK := TRUE ELSE // Ошибка! lLoadOK := FALSE ENDIF ENDIF RETURN lLoadOKПеред установкой связи Вам следует убедиться в наличие Excel в памяти. Допустим Вы установили связь без особых проблем. Что делать дальше? Дальше надо запускать на выполнение процедуры Excel, требовать с него данные. Как это сделать? Для этого в классе IPCClient предусмотрены методы Execute() и RequestData(). Они имеют следующий синтаксис:
<oIpcClient>:Execute(<cTopic>, <cItem>, <cCommand>) --->
NIL
<cTopic> Имя IPC topic для которого сервер выполняет команду.
<cItem> Имя IPC item для которого сервер выполняет команду.
<cCommand> Команда, которую должен выполнить сервер.
<oIpcClient>:RequestData(<oIpcTopic>, [<lContinuous>]) --->
NIL
<oIpcTopic> IPC topic данные которого требуются клиенту.
<lContinuous> Если установлен в TRUE, сервер посылает данные
клиенту каждый раз как эти данные на сервере изменяются; Если установлен
в FALSE, сервер посылает данные клиенту только по этому требованию. По
умолчанию FALSE.
Как все усложнилось, подумаете Вы, но для Вас, напротив, все очень просто.
Execute() метод требует для своей работы
некий Topic (но это так же естественно как Вы по телефону говорите
о политике или о погоде). Topic обычно определяет конкретную страницу
или специальный системный Topic. Item не используетс
в Microsoft Windows и его можно проигнорировать здесь (передайте пустую
строку). Command это конкретная команда, требующая выполнения, така
как, например, печать страницы или занесение данных в ячейку.
При запросе данных Вы должны определить реально существующий в DDE
сервере Topic. Реально существующие Topic-и в DDE серверах
определяются из документации к этим серверам. Также при запросе данных
Вы должны определить какой вид связи вы желаете иметь - горячий или холодный.
Макро язык Excel и Visual Basic для приложений. (Excel Macro
Language (XLM) и Visual Basic for Applications (VBA))
Одно из неудобств использования DDE это тот печальный факт, что разработчику в распоряжение дается ограниченный набор методов ведение конвисэйшн, в то время как он, разработчик, требует по возможности все внутренние методы. Но нет никаких препятствий, для того чтобы написать весь коммуникационный код на языке Visual Objects.
Не надо быть экспертом в Excel, чтобы понять, что все, что может Excel, может его внутренний Basic, подобный макроязыку (Excel Macro Language (XLM)) или Visual Basic for Applications (VBA). Excel версии 4.0 снабжен XLM, версии 5.0 VBA - аналогом популярного продукта Microsoft.
Как применить указанное свойство Excel в нашем случае? Есть метод, который
называется RUN и который может быть вызван через DDE. Этот метод
выполняет макрос или вызывает функцию. Это может быть использовано дл
запуска на выполнение макроса, написанного на VBA, макроязыка Microsoft
Excel 4.0, или даже функции в .DLL. Для примера давайте разработаем небольшую
программку для импорта данных ASCII файла в страницу Excel. На ум сразу
приходит решение - просканировать ASCII файл низкоуровневыми функциями
доступа CA-Visual Objects и отправить эти данные в таблицу Excel использу
механизм DDE. Однако VBA Excel имеет собственные функции низкого уровн
доступа к файлам, поэтому решение может выглядеть следующем образом.
Пишем макрос на языке VBA:
Sub LoadCurrentTable() nCount = 1 Open "TESTFILE" For Input As #1 ' Открытие файла Do While Not EOF(1) ' Цикл до конца файла Input #1, cData ' Читаем данные в переменную Worksheets("Лист1").Cells(nCount, nCount).Value = VAL(cData) If Worksheets("Лист1").Cells(1, nCount).Value > 100 Then With Worksheets("Лист1").Cells(nCount, nCount).Font ' Изменим фонт .Size = 14 .Bold = True .Italic = True End With End If nCount = nCount + 1 Loop Close #1 ' Закрыть файл End SubНа языке CA-Visual Objects подобный код мог бы выглядеть гораздо сложнее, так как VO ничего не знает о клетке страницы таблицы Excel. Однако сконструировав его мы можем вызвать процедуру LoadCurrentTable() из VO использу DDE технологию, просто набрав:
oIPCClient:Execute("Книга1", "", `[Application.Run("LoadCurrentTable()")]')
Отметьте, пожалуйста, как расставлены символы "", `' и [] - это достаточно важно при реализации DDE в CA-Visual Objects. Если Вы хотите выполнить макрофункцию на языке XLM Вы должны указать страницу, в которой этот макрос находиться:
oIPCClient:Execute("Книга1",; `',`[Application.Run("Модуль1.XLM!LoadCurrentTable()")]')
Использование внутренних языков Excel XLM и/или VBA наиболее легкий
путь интеграции Excel с приложениями CA-Visual Objects.
Разница между версиями Excel 2-5, 5.x и более поздними.
К сожалению от версии Excel сильно зависит то как Вы будете интегрировать свои приложения. В ранних версиях Excel не поддерживалась последная спецификаци OLE 2.0 и полная поддержка расширенных системных Topic , которые были добавлены, начиная с версии 2.0. Вот еще некоторые различия:
DDE имеет несколько ограничений, наименьшее из которых убогий интерфейс
доступа к данным. Слишком много операций следует предпринять, чтобы манипулировать
с данными. Хороший пример с тем, что Вы хотите извлечь данные из клетки.
С DDE Вы не можете просто вытащить содержимое клетки, метод Execute не
возвращает никакого результата. Для этой цели Вы вынуждены использовать
метод RequestData
Через механизм OLE, однако доступно гораздо больше опций. Если опци
не доступна в Excel или в его аналоге через OCX, по крайней мере от Microsoft,
Вы вправе использовать компоненты других фирм. Наиболее известной компонентой
является компонента "Formula One" от "Visual Component". Это очень сильна
вещь со многих позиций. Во первых она удобней, по ней написан огромный
HELP, чего нельзя сказать про Excel. Во вторых она меньше ест ресурсов
и быстрей работает
Иерархия OLE объектов в Exel.
Excel имеет несколько типов объектов, доступных через механизм OLE (самих объектов более чем 128). В то время как другие сервера автоматизации имеют один объект APPLICATION, Excel имеет три:
APPLICATION
AddIn
AutoCorrect
Debug
Dialog
Name
MenuBar
Toolbar
Window
Workbook
Worksheet
DialogSheet
Chart
Module
DocumentProperty
Style
Window
Name
RoutineSlip
Как Вы видите объект класса APPLICATION является контейнером для объектов
класса WORKBOOK, которые, в свою очередь, являются контейнерами для объектов
класса WORKSHEET, CHAR и Style. С подобной схемой становиться более понятной
логическая схема работы Excel.
В CA-Visual Object Вы можете сделать объект класса APPLICATION, заполнить
его объектами WORKBOOK, которые могут содержать несколько объектов WORKSHEET.
Начало работы через OLE c Excel.
Есть два наиболее приемлемых пути для определения объекта Excel.
Первое - Вы можете использовать OleAutoObject класс, который создает
OLE объект времени выполнения связанный с сервером автоматизации OLE объектов
(Excel). Это очень удобно, но имеет свои отрицательные стороны:
Создаем новое приложение. Обратите внимание, что при создании приложени CA Visual Object спрашивает Вас об использовании OLE классов. Ответьте утвердительно:
.
Наберите программу:
METHOD Start() CLASS App LOCAL oW AS ShellWindow LOCAL oAO AS OLEAutoObject oW := ShellWindow{SELF} oW:Show(SHOWCENTERED) oAO := OLEAutoObject{"EXCEL.APPLICATION.5"} IF !oAO:fInit TextBox{oW, "Все паршиво!"}:Show() ELSE TextBox{oW, "Все удачно!"}:Show() END IF
SELF:Exec()И убедитесь ... что она не работает. Точнее не создается объект oAO. Поскольку не работает основное, далее можно не продолжать. При попытке создать класс на основе интерфейса автоматизации, выдается сообщение об ошибки и ничего не генерится:
Связь с Excel через OLE OCX компоненту
Для использования OCX компоненты, ее надо установить на машину. Это делает стандартный инсталлятор, и если следовать инструкциям Вы наверняка получите то, что хотите. Помимо того, что инсталлирование переписывает файлы на Ваш диск, оно еще занимается регистрацией установленной компоненты в файл регистратор системы - Register. Это необходимое требование для нормальной работы компонента.
CLASS VCIFormulaOneWorkbook INHERIT OleControl
Обратите внимание от чего он наследуется. Если Вы посмотрите на методы сгенеренного класса, то наверняка обратите внимание на то, что все они похожи друг на друга как близнецы:
METHOD Read(; pPathName,; // AS STRING pFileType,; // AS PTR ) CLASS VCIFormulaOneWorkbook // Считывает рабочую книгу или рабочий лист с диска LOCAL a[OFD_LEN] AS ARRAY LOCAL pParamDesc AS VOOLEARGDESC LOCAL pRetDesc IS VOOLEARGDESC LOCAL uRetValue AS USUAL a[OFD_NAME] := String2Symbol("Read") a[OFD_MEMBERID] := 202 a[OFD_INVOKE] := INVOKE_METHOD a[OFD_PARAMS] := 2 a[OFD_OPTPARAMS] := 0 a[OFD_NAMEDARGS] := .T. pParamDesc := oAuto:__AllocArgDescs(2) a[OFD_PARAMDESC] := pParamDesc pParamDesc.dwIDLFlag := 0 pParamDesc.dwVarType := VT_BSTR pParamDesc.atomArgName := #pPathName pParamDesc += 1 pParamDesc.dwIDLFlag := 0 pParamDesc.dwVarType := VT_PTR pParamDesc.dwArrPtrType := VT_I2 pParamDesc.atomArgName := #pFileTypeЭ pParamDesc += 1 pRetDesc.dwIDLFlag := 0 pRetDesc.dwVarType := VT_VOID a[OFD_RETDESC] := @pRetDesc uRetValue := oAuto:__InvokeMethod(a,DWORD(_bp+16),PCount()) MemFree(pParamDesc) RETURN(uRetValue)
ACCESS SheetName(; nSheet,; // AS LONG ) CLASS VCIFormulaOneWorkbook ASSIGN SheetName(; nSheet,; // AS LONG uParam002,; // AS STRING ) CLASS VCIFormulaOneWorkbook METHOD SheetNameACCESS(; nSheet,; // AS LONG ) CLASS VCIFormulaOneWorkbook METHOD SheetNameASSIGN(; nSheet,; // AS LONG uParam002,; // AS STRING ) CLASS VCIFormulaOneWorkbook
CLASS FrmOneControl INHERIT DATAWINDOW EXPORT oDCF1 AS VCIFORMULAONEWORKBOOK
METHOD Init(oWindow,iCtlID,oServer,uExtra) CLASS FrmOneControl SELF:PreInit(oWindow,iCtlID,oServer,uExtra) SUPER:Init(oWindow,ResourceID{"FrmOneControl",_GetInst()},iCtlID) oDCF1 := VCIFORMULAONEWORKBOOK{SELF,ResourceID{FRMONECONTROL_F1,_GetInst()}} oDCF1:HyperLabel := HyperLabel{#F1,NULL_STRING,NULL_STRING,NULL_STRING} oDCF1:CreateFromAppDocStorage(OLEAppDocStorage{"OLE200", "FrmOneControl", "F1"}) SELF:Caption := "DataWindow Caption" SELF:HyperLabel := HyperLabel{#FrmOneControl,"DataWindow Caption",; NULL_STRING,NULL_STRING} SELF:PostInit(oWindow,iCtlID,oServer,uExtra) RETURN SELF
METHOD Test() CLASS ShellWindow LOCAL oF1c AS FrmOneControl LOCAL oP := 4 AS INT LOCAL kL, nL, L1, L2 AS LONG LOCAL lP AS LOGIC //Флаг удачного поиска нужной страницы LOCAL oServ AS DBserver LOCAL sDBF, sXLS AS STRING LOCAL m1, m2 AS USUAL // sXLS := "d:\St\Tehnicon97\Использование Excel и Word в CA-Visual Objects 2.0\Ole1.xls" sDBF := "d:\St\Tehnicon97\Использование Excel и WORD в CA-Visual Objects 2.0\t1.dbf" IF File(sDBF) DeleteFile(AsPsz(sDBF)) END IF DBCREATE(sDBF, {{"F1", "N", 26, 8}, {"F2", "C", 20, 0}}) oServ := dbserver{sDBF, DBEXCLUSIVE, DBREADWRITE} // oF1c := FrmOneControl{SELF} oF1c:oDCF1:Read(sXLS, @oP) //Выбираем нужную странице в файле Platts kL := oF1c:oDCF1:NumSheets lP := FALSE FOR nL := 1 UPTO kL IF "ПРИМЕР" == Upper(AllTrim(StrTran(oF1c:oDCF1:SheetNameAccess(nL)," ",;""))) oF1c:oDCF1:Sheet := nL lP := TRUE EXIT END IF NEXT IF lP //Если мы ее нашли L1 := oF1c:oDCF1:LastRow FOR L2 := 1 UPTO L1 oServ:append() m1 := Val(oF1c:oDCF1:EntryRC(L2, 1)) m2 := oF1c:oDCF1:EntryRC(L2, 2) oServ:FIELDPUT(#F1, m1) oServ:FIELDPUT(#F2, m2) NEXT TextBox{SELF, "Все класно!"}:Show() ELSE TextBox{SELF, "Все не очень класно!"}:Show() END IF //
![]() |
![]() |