COM Add-Ins have become more important when Microsoft introduced the Office Fluent User Interface. But you can use COM Add-Ins to extend functionality starting with Microsoft Office 2000 and with access to the object model of its host application. They are also used to provide add-in functionality to Visual Studio.
A COM Add-In is an in-process COM-Server that must implement the IDTExtensibility2
interface. The IRibbonExtensibility
interface must be implemented to gain access to the Ribbon UI. That’s why we will use Delphi to create COM Add-Ins. A first simple project will describe the things to do in addition to the implementation of interfaces.
A COM Add-In needs some additional registry entries. We override the UpdateRegistry
method of the TAutoObjectFactory
to create the required entries.
type TXLComAddinFactory = class(TAutoObjectFactory) procedure UpdateRegistry(Register: Boolean); override; end;
There are different locations in the registry depending on the target application.
HKEY_CURRENT_USERSoftwareMicrosoftOfficeExcelAddins<ProgId> or in general HKEY_CURRENT_USERSoftwareMicrosoftOffice<Office App>Addins<ProgId> except HKEY_CURRENT_USERSoftwareMicrosoftVisioAddins<ProgId> and HKEY_CURRENT_USERSoftwareMicrosoftVisualStudio<Ver>Addins<ProgId> HKEY_CURRENT_USERSoftwareMicrosoftVBAVBE6.0Addins<ProgId>
A Subkey with the name of the Add-In’s ProgID must be created. The Values under the Subkey describe the COM add-in. We use TRegistry
because the values have type REG_SZ
and REG_DWORD
.
LoadBehavior
is a Value that is not optional. In most cases the default value 3 will be fine. You can find a description of possible values here.
The CommandLineSafe
Value is optional. It is marked as not applicable for Office Add-Ins. If set to value 1 it indicates that the add-in avoids any UI manipulation when it is called with ConnectMode = ext_cm_CommandLine
.
The Add-In Manager shows a list of available Add-Ins. An optional FriendlyName
Value will appear in the list instead of the Add-In’s ProgID and we can add an optional Description
Value.
We delete the Subkey and its Values when the Add-In is unregistered.
{...} procedure TXLComAddinFactory.UpdateRegistry(Register: Boolean); var RootKey: HKEY; AddInKey: String; r: TRegistry; begin Rootkey:=HKEY_CURRENT_USER; AddInKey:='SoftwareMicrosoftOfficeExcelAddins' + ProgID; r:=TRegistry.Create; r.RootKey:=RootKey; try if Register then if r.OpenKey(AddInKey, True) then begin r.WriteInteger('LoadBehavior', 3); r.WriteInteger('CommandLineSafe', 0); r.WriteString('FriendlyName', 'Delphi Sample Add-In'); r.WriteString('Description', 'Sample Add-In written in Delphi'); r.CloseKey; end else raise EOleError.Create('Can''t register Add-In ' + ProgID) else if r.KeyExists(AddInKey) then r.DeleteKey(AddInKey); finally r.Free; end; inherited; end;
The UpdateRegistry
method of the ancestor may raise an EOleRegistrationError
exception when DLLRegisterServer
is called to register the COM server under HKCR
without administrative rights. But DLLRegisterServer
is called not only by regsvr32
. The Add-In Manager contains two buttons to Add and Remove Add-Ins. The Subkey we had created is deleted by clicking Remove. You can add COM Add-Ins by clicking Add. The Add-In Manager calls DLLRegisterServer
if you select a file that is an already registered COM Server. This is done to force the Add-In to re-create the Subkey and the Values mentioned above. Thus, the Subkey should be created regardless that the following registration may fail. This is the reason why inherited
is called at the end.
Now, we can start writing a first simple Add-In by implementing the IDTExtensibility2
interface. You can find the definition in the Microsoft Add-In Designer (MSADDNDR.DLL
). The interface can be extracted from the TLB as follows.
type // *********************************************************************// // Interface: IDTExtensibility2 // Flags: (4432) Hidden Dual OleAutomation Dispatchable // GUID: {B65AD801-ABAF-11D0-BB8B-00A0C90F2744} // *********************************************************************// IDTExtensibility2 = interface(IDispatch) ['{B65AD801-ABAF-11D0-BB8B-00A0C90F2744}'] procedure OnConnection(const Application: IDispatch; ConnectMode: ext_ConnectMode; const AddInInst: IDispatch; var custom: PSafeArray); safecall; procedure OnDisconnection(RemoveMode: ext_DisconnectMode; var custom: PSafeArray); safecall; procedure OnAddInsUpdate(var custom: PSafeArray); safecall; procedure OnStartupComplete(var custom: PSafeArray); safecall; procedure OnBeginShutdown(var custom: PSafeArray); safecall; end; // *********************************************************************// // DispIntf: IDTExtensibility2Disp // Flags: (4432) Hidden Dual OleAutomation Dispatchable // GUID: {B65AD801-ABAF-11D0-BB8B-00A0C90F2744} // *********************************************************************// IDTExtensibility2Disp = dispinterface ['{B65AD801-ABAF-11D0-BB8B-00A0C90F2744}'] procedure OnConnection(const Application: IDispatch; ConnectMode: ext_ConnectMode; const AddInInst: IDispatch; var custom: {PSafeArray} OleVariant); dispid 1; procedure OnDisconnection(RemoveMode: ext_DisconnectMode; var custom: {PSafeArray} OleVariant); dispid 2; procedure OnAddInsUpdate(var custom: {PSafeArray} OleVariant); dispid 3; procedure OnStartupComplete(var custom: {PSafeArray} OleVariant); dispid 4; procedure OnBeginShutdown(var custom: {PSafeArray} OleVariant); dispid 5; end;
We add a new Automation Object to our project and add IDTExtensibility2
to the list of implemented interfaces.
type TDelphiAddin1 = class(TAutoObject, IDelphiAddin1, IDTExtensibility2) private procedure OnConnection(const Application: IDispatch; ConnectMode: ext_ConnectMode; const AddInInst: IDispatch; var custom: PSafeArray); safecall; procedure OnDisconnection(RemoveMode: ext_DisconnectMode; var custom: PSafeArray); safecall; procedure OnAddInsUpdate(var custom: PSafeArray); safecall; procedure OnStartupComplete(var custom: PSafeArray); safecall; procedure OnBeginShutdown(var custom: PSafeArray); safecall; protected end;
You can find a description of the five methods and a VBA example here. We want to keep the first project simple. So, we only write code to the OnConnection
method and leave the others empty.
{ TDelphiAddin1 } procedure TDelphiAddin1.OnConnection(const Application: IDispatch; ConnectMode: ext_ConnectMode; const AddInInst: IDispatch; var custom: PSafeArray); var s: WideString; x: OleVariant; begin if ConnectMode <> ext_cm_CommandLine then try x:= Application; s:= 'Hello World!'; s:= s + ' You''re running ' + x.Name + ' version ' + x.Version; MessageBoxW(0, PWideChar(s), '', MB_OK); except end; end; {...}
We check the ConnectMode
parameter and display a message box which shows the name and version of the Add-In’s host Application
. The last step is to exchange the factory.
initialization TXLComAddinFactory.Create(ComServer, TDelphiAddin1, Class_DelphiAddin1, ciMultiInstance, tmApartment); end.
Now write codes using above variables on a Button click or other appropriate event.
1. Create and connect with an Excel Application…
myxlApp := TExcelApplication.Create(Nil);
myxlApp.Connect;
myxlApp.Visible[LCID] := True; // will show newly connected Excel application // most of case not required //
2. Close and Free the Excel application….
myxlApp.Disconnect;
myxlApp.Quit;
FreeAndNil(myxlApp);
3. Add a Workbook
myxlApp.Workbooks.Add(EmptyParam, LCID); //it will also add a default sheet to workbok//
myxlBook := TExcelWorkbook.Create(myxlApp);
myxlBook.ConnectTo(myxlApp.ActiveWorkbook);
4. Disconnect Workbook before close
myxlBook.Close(True,’C:jitendraExcelTest1.xlsx’); //Saves the changes to mentioned file//
myxlBook.Disconnect;
FreeAndNil(myxlBook);
5. Add new Worksheet
myxlSheet11 := TExcelWorksheet.Create(myxlBook);
myxlSheet11.ConnectTo(myxlBook.ActiveSheet as _worksheet); //connecting with the default worksheet//
myxlSheet11.Name := ‘Class 11’;
6. Disconnect worksheet before close
myxlSheet11.Disconnect;
FreeAndNil(myxlSheet11);
7. Adding a new Worksheet to the Workbook
myxlBook.Worksheets.Add(EmptyParam, EmptyParam, EmptyParam, EmptyParam, LCID);
myxlSheet12 := TExcelWorksheet.Create(myxlBook);
myxlSheet12.ConnectTo(myxlBook.ActiveSheet as _worksheet);
myxlSheet12.Name := ‘Class 12’;
8. Access Sheets by Index or Name
(myxlApp.Worksheets[0] as _Worksheet).Activate(LCID);
Or
(myxlApp.Worksheets[‘Sheet1’] as _Worksheet).Activate(LCID);
9. Assign values to Cell by using Cell or Range property
myxlApp.Cells.Item[1,1] := ‘Value 1’; //with row, col number//
myxlApp.Range[‘A3′,’A3’].Value := ‘value 2’; //with cell from, to names//
myxlSheet11.Cells.Item[1,5] := ‘JITENDRA’; //with row, col number//
myxlSheet11.Range[‘E3′,’E3’].Value := ‘7834911261’; //with cell from, to names//
10. Change font format of an Excel Range
with myxlSheet11.Range[‘A1’, ‘B3’] do
begin
Font.Name := ‘Verdana’;
Font.Size := 15;
Font.Bold := True;
Font.Strikethrough := True;
Font.Color := clRed;
end;
11. Change Background Color of cells
with myxlSheet11.Range[‘A1’, ‘A1’].Interior.Color := clYellow;
myxlSheet11.Range[‘A5’, ‘D7’].Merge(False);// merge cells and fill color in merged cells//
myxlSheet11.Range[‘A5’, ‘D7’].Interior.Color := clRed;
12. Merge Cells in a range
myxlSheet11.Range[‘A5’, ‘D7’].Merge(False); //False by default if True it would merge cells row by row//
myxlSheet11.Range[‘A5’, ‘D7’].Value := ‘Merged data’;
13. Change Column width and Row height
myxlSheet11.Range[‘B5’, ‘B5’].ColumnWidth := 5; //single column B//
myxlSheet11.Range[‘J5’, ‘L8’].ColumnWidth := 15; //multiple column J,K,L//
myxlSheet11.Range[‘B5’, ‘B5’].RowHeight := 50; //single row 5//
myxlSheet11.Range[‘J10’, ‘J15’].RowHeight := 50; //multiple row 10-15//
14. Open the workbook that already exists:
myxlApp.Workbooks.Open ( ‘C:jitendraExcelTest1.xlsx’
EmptyParam , EmptyParam , EmptyParam , EmptyParam ,
EmptyParam , EmptyParam , EmptyParam , EmptyParam ,
EmptyParam , EmptyParam , EmptyParam , EmptyParam , 0 );
15. Copy and Paste Data from one cell to another in same sheet…
a.
myxlSheet11.UsedRange[LCID].Copy(myxlSheet11.Range[‘J10’, ‘J10’]);
myxlSheet11.Range[‘A5’, ‘D7’].Copy(myxlSheet11.Range[‘J10’, ‘J10’]);
b.
myxlSheet11.UsedRange[LCID].Copy(EmptyParam);
myxlSheet11.Range[‘J10’, ‘J10’].PasteSpecial(xlPasteAll, xlPasteSpecialOperationNone, EmptyParam, EmptyParam);
myxlSheet11.Range[‘A5’, ‘D7’].Copy(EmptyParam);
myxlSheet11.Range[‘J10’, ‘J10’].PasteSpecial(xlPasteAll, xlPasteSpecialOperationNone, EmptyParam, EmptyParam);
16. Copy and Paste Data from one Sheet to another sheet.
a.
myxlSheet11.UsedRange[LCID].Copy(myxlSheet12.Range[‘J10’, ‘J10’]);
myxlSheet11.Range[‘A5’, ‘D7’].Copy(myxlSheet12.Range[‘J10’, ‘J10’]);
b.
myxlSheet11.UsedRange[LCID].Copy(EmptyParam);
myxlSheet12.Range[‘J10’, ‘J10’].PasteSpecial(xlPasteAll, xlPasteSpecialOperationNone,EmptyParam, EmptyParam);
myxlSheet11.Range[‘A5’, ‘D7’].Copy(EmptyParam);
myxlSheet12.Range[‘J10’, ‘J10’].PasteSpecial(xlPasteAll, xlPasteSpecialOperationNone,EmptyParam, EmptyParam);
17. Insert new columns before b1
myxlSheet11.Range[‘b1’, ‘b1’].Columns.Insert(xlShiftToRight); // we can use xlShiftToLeft//
18. Insert new rows above b2
myxlSheet11.Range[‘b2’, ‘b2’].Rows.Insert(xlShiftDown); // we can use xlShiftUp//
19. Clear a Selected Range Content or Format
myxlSheet11.Range[‘b3’, ‘b10’].ClearContents;
myxlSheet11.Range[‘b3’, ‘b10’].ClearFormats;
20.Save, Save as a Sheet / Workbook
myxlSheet11.SaveAs(‘Filename’);
myxlBook.Save;
21. Print Preview / Print of a Sheet / Workbook
myxlSheet11.PrintPreview;
myxlSheet11.PrintOut;
myxlBook.PrintPreview;
myxlBook.PrintOut;
22. Set Sheet PageSetup
myxlSheet11.PageSetup.
A. header:
myxlSheet11.PageSetup.CenterHeader := » The report shows » ;
B. footer:
myxlSheet11.PageSetup.CenterFooter := » The & P » ;
The C. header into the top margin 2cm:
myxlSheet11.PageSetup.HeaderMargin := 2 / 0.035 ;
D. footer bottom margin 3cm:
myxlSheet11.PageSetup.HeaderMargin := 3 / 0.035 ;
E. top margin 2cm:
myxlSheet11.PageSetup.TopMargin := 2 / 0.035 ;
The bottom edge of the f. from the 2cm:
myxlSheet11.PageSetup.BottomMargin := 2 / 0.035 ;
G. left 2cm:
myxlSheet11.PageSetup.LeftMargin := 2 / 0.035 ;
On the right side of the H. from the 2cm:
myxlSheet11.PageSetup.RightMargin := 2 / 0.035 ;
I. pages horizontally:
myxlSheet11.PageSetup.CenterHorizontally := 2 / 0.035 ;
The J. page vertically:
myxlSheet11.PageSetup.CenterVertically := 2 / 0.035 ;
K. print cell line:
myxlSheet11.PageSetup.PrintGridLines := True ;
23. Auto Fill of Range. Fills value 1-10 in p1:p10
myxlSheet11.Range[‘p1’, ‘p1’].Value := 1;
myxlSheet11.Range[‘p2’, ‘p2’].Value := 2;
myxlSheet11.Range[‘p1’, ‘p1’].AutoFill(myxlSheet11.Range[‘p1’, ‘p10’], xlFillSeries);
Other fill options
xlFillCopy
xlFillDays
xlFillDefault
xlFillFormats
xlFillMonths
xlFillSeries
xlFillValues
xlFillWeekdays
xlFillYears
xlGrowthTrend
xlLinearTrend
24. Change Border style of cells in a range
myxlSheet11.Range[‘p3’, ‘p4’].Borders.Color := clRed;
myxlSheet11.Range[‘p3’, ‘p4’].Borders.LineStyle := xlDouble;
myxlSheet11.Range[‘p3’, ‘p4’].Borders.Item[xlEdgeLeft].Color := clBlue;
myxlSheet11.Range[‘p3’, ‘p4’].Borders.Item[xlEdgeRight].Color := clBlue;
Other line styles.
xlContinuous
xlDash
xlDashDot
xlDashDotDot
xlDot
xlDouble
xlSlantDashDot
xlLineStyleNone
25. Fill pattern style of cells in a range
myxlSheet11.Range[‘p3’, ‘p4’].Interior.Pattern := xlPatternCrissCross;
myxlSheet11.Range[‘p3’, ‘p4’].Interior.PatternColor := clBlue;
Other pattern styles
xlPatternAutomatic
xlPatternChecker
xlPatternCrissCross
xlPatternDown
xlPatternGray16
xlPatternGray25
xlPatternGray50
xlPatternGray75
xlPatternGray8
xlPatternGrid
xlPatternHorizontal
xlPatternLightDown
xlPatternLightHorizontal
xlPatternLightUp
xlPatternLightVertical
xlPatternNone
xlPatternSemiGray75
xlPatternSolid
xlPatternUp
xlPatternVertical
26. Add calculation function SUM/AVG/MAX/COUNT etc..
myxlSheet11.Range[‘k1’, ‘k1’].Formula := ‘=Sum(p3:p8)’;
myxlSheet11.Range[‘k3’, ‘k3’].Formula := ‘=Avg(p3:p8)’;
myxlSheet11.Range[‘k5’, ‘k5’].Formula := ‘=Max(p3:p8)’;
myxlSheet11.Range[‘k7’, ‘k7’].Formula := ‘=Count(p3:p8)’;
Read, Write excel 2002/2003 XML (SpreadsheetML / XML Spreadsheet) library.
// Creating new workbook var workBook: TZWorkBook; ... workBook := TZWorkBook.Create(); try workBook.Sheets.Add('My sheet'); workBook.Sheets[0].ColCount := 10; workBook.Sheets[0].RowCount := 10; workBook.Sheets[0].CellRef['A', 0].AsString := 'Hello'; workBook.Sheets[0].RangeRef['A', 0, 'B', 2].Merge(); workBook.SaveToFile('file.xlsx'); finally workBook.Free(); end
// Editing exists workbook var workBook: TZWorkBook; ... workBook := TZWorkBook.Create(); try workBook.LoadFromFile('file.xlsx'); workBook.Sheets[0].CellRef['A', 0].AsString := 'Hello'; workBook.Sheets[0].CellRef['A', 0].FontStyle := [fsBold]; workBook.SaveToFile('file.xlsx'); finally workBook.Free(); end
Тут иногда бывают вопросы о том, как можно из Delphi работать с документами Excel. Решил написать такую инструкцию, где рассказано об основных действиях.
Для работы с OLE нужно к строке Uses добавить модуль ComObj. Так же нужно объявить переменную типа Variant.
Delphi | ||
|
Что бы начать работу с Экселем, нужно создать OLE объект:
Ap := CreateOleObject(‘Excel.Application’);
После этого нужно либо создать новую книгу:
Ap.Workbooks.Add;
либо открыть файл:
Ap.Workbooks.Open(<имя файла>);
Что бы открыть файл только для чтения, нужно указать:
Ap.Workbooks.Open(<имя файла>,0,True);
где True и указывает, что файл открывается только для чтения.
По умолчанию окно Excel не будет отображаться… что бы оно появилось, нужно выполнить
Ap.Visible := True;
Но это желательно делать в последний момент, т.к. когда окно видимое, то все изменения в нём происходят медленнее. Поэтому, лучше оставить его невидимым, сделать там все необходимые изменения, и только после этого сделать его видимым или закрыть. Если вы его оставите невидимым, то процесс EXCEL.EXE останется висеть в памяти, даже когда будет закрыто ваше приложение.
Что бы закрыть Excel, выполните Ap.Quit или Ap.Application.Quit. Честно говоря, я не знаю, чем они отличаются.
Что бы при закрытии не выдавался запрос на сохранение файла, можно отключить сообщения:
Ap.DisplayAlerts := False;
Что бы записать или прочитать содержимое ячейки можно использовать Ap.Range[<имя ячейки>] или Ap.Cells[<позиция по Y>,<позиция по X>]
Ap.Range[‘D1’] := ‘Ляляля’;
Ap.Cells[1,4] := ‘Ляляля’;
Эти две строки выполняют одно и тоже действие: записывают строку «Ляляля» в ячейку D1
Читать значение из ячейки таким же образом:
S := Ap.Range[‘D1’];
или
S := Ap.Cells[1,4];
Так же можно записывать значение сразу в несколько ячеек… можно перечислить через точку с запятой или указать диапазон через двоеточие:
Delphi | ||
|
Изменение свойств текста
Всё это можно применять как к отдельным ячейкам, так и к группам ячеек, строк, столбцов и т.п. Я буду показывать примеры на Ap.Cells… но Вам никто не мешает использовать Ap.Range[‘D5’], Ap.Range[‘A2:E8’], Ap.Columns[‘B:F’] и т.п.
Delphi | ||
|
Изменение цвета фона ячеек:
Delphi | ||
|
Наверное, вы обращали внимание, что в новом документе появляются 3 листа (их список отображается внизу программы Excel). По умолчанию, мы работаем с активным листом (сразу после создания это первый лист). Но при желании, мы можем использовать и другие листы. Доступ к ним осуществляется с помощью Worksheets[<номер листа>]. Обратите внимание, что нумеруются они начиная с 1 (а не с 0, как привыкли все программисты).
Delphi | ||
|
Свойства страницы
Delphi | ||
|
Ниже приведён более полный список размеров бумаги из модуля ExcelXP:
Delphi | ||
|
Распечатать
Delphi | ||
|
Выделение
Excel.Range[Excel.Cells[1, 1], Excel.Cells[5, 3]].Select;
а также любые другие комбинации выбора ячейки с окончанием .select — выбор 1 или группы ячеек
С выбранными ячейками возможны следующие преобразования:
1) объединение ячеек
Excel.Selection.MergeCells:=True;
2) перенос по словам
Excel.Selection.WrapText:=True;
3) горизонтальное выравнивание
Excel.Selection.HorizontalAlignment:=3;
при присваивании значения 1 используется выравнивание по умолчанию, при 2 — выравнивание слева, 3 — по центру, 4 — справа.
4) вериткальное выравнивание
Excel.Selection.VerticalAlignment:=1;
присваиваемые значения аналогичны горизонтальному выравниванию.
5) граница для ячеек
Excel.Selection.Borders.LineStyle:=1;
При значении 1 границы ячеек рисуются тонкими сплошными линиями.
Кроме этого можно указать значения для свойства Borders, например, равное 3. Тогда установится только верхняя граница для блока выделения:
Excel.Selection.Borders[3].LineStyle:=1;
Значение свойства Borders задает различную комбинацию граней ячеек.
В обоих случаях можно использовать значения в диапазоне от 1 до 10. ]
//9 и 11 — лучшие
\\\\\\\\\\\\\\\\
Установка пароля для активной книги может быть произведена следующим образом:
Delphi | ||
|
где pass — устанавливаемый пароль на книгу.
Снятие пароля с книги аналогично, использовуем команду
Delphi | ||
|
где pass — пароль, установленный для защиты книги.
Установка и снятие пароля для активного листа книги Excel производится командами
Delphi | ||
|
где pass — пароль, установленный для защиты книги.
Вспомогательные операции в EXCEL
Удаление строк со сдвигом вверх:
Delphi | ||
|
при выполнении данных действий будут удалены строки с 5 по 15.
Установка закрепления области на активном листе Excel
Delphi | ||
|
Спасибо VampireKB за дополнения