??xml version="1.0" encoding="utf-8" standalone="yes"?>
#include <IOSTREAM>
#include <map>
#include <string>
#include <CONIO.H>
using namespace std;
class propertytest{
int m_xvalue;
int m_yvalues[100];
map<string,string> m_zvalues;
public:
__declspec(property(get=GetX, put=PutX))
int x;
__declspec(property(get=GetY, put=PutY))
int y[];
__declspec(property(get=GetZ, put=PutZ))
int z[];
int GetX()
{
return m_xvalue;
};
void PutX(int x)
{
m_xvalue = x;
};
int GetY(int n)
{
return m_yvalues[n];
};
void PutY(int n,int y)
{
m_yvalues[n] = y;
};
string GetZ(string key)
{
return m_zvalues[key];
};
void PutZ(string key,string z)
{
m_zvalues[key] = z;
};
};
int main(int argc, char* argv[]){
propertytest test;
test.x = 3;
test.y[3] = 4;
test.z["aaa"] = "aaa";
std::cout << test.x <<std::endl;
std::cout << test.y[3] <<std::endl;
std::cout << test.z["aaa"] <<std::endl; getch();
return 0;
}
__declspec(property(get=[getҎ(gu)名], put=[putҎ(gu)名])) [cd] [属性名];
__declspec(property)实际上就是做了一个映,把你的方法映成属性,以供讉K?br />get和put是属性访问的权限Q一个是ȝ权限Q一个是写的权限。你可以Ҏ(gu)需要来军_get和put两种权限的取舍?br />
]]>#include <IOSTREAM>using namespace std;
#define interface class __declspec(novtable)interface ICodec
{
public: virtual bool Decode(char * lpDataSrc,unsigned int nSrcLen,char * lpDataDst,unsigned int *pnDstLen);
virtual bool Encode(char * lpDataSrc,unsigned int nSrcLen,char * lpDataDst,unsigned int *pnDstLen);
};
class CCodec : public ICodec
{
public: virtual bool Decode(char * lpDataSrc,unsigned int nSrcLen,char * lpDataDst,unsigned int *pnDstLen)
{
cout << "解码..." << endl;
return true;
}
virtual bool Encode(char * lpDataSrc,unsigned int nSrcLen,char * lpDataDst,unsigned int *pnDstLen)
{
cout << "~码..." << endl;
return true;
}
};
int main(int argc, char* argv[]){
ICodec * pCodec = new CCodec();
pCodec->Decode(NULL,0,NULL,NULL);
pCodec->Encode(NULL,0,NULL,NULL);
delete (CCodec*)pCodec;
return 0;
}
上面的ICodec接口{h(hun)于下面的定义Q?br />class ICodec{
public: virtual bool Decode(char * lpDataSrc,unsigned int nSrcLen,char * lpDataDst,unsigned int *pnDstLen)=0;
virtual bool Encode(char * lpDataSrc,unsigned int nSrcLen,char * lpDataDst,unsigned int *pnDstLen)=0;
};
]]>1. Start Visual C++ and create a new MFC EXE dialog-based
application. 2. Add a button to your dialog. 3. Double-click the button to add a handler for it, and add
the following code: // Start Outlook.
// If it is already running, you'll use the same instance...
_Application olApp;
COleException e;
if(!olApp.CreateDispatch("Outlook.Application", &e)) {
CString str;
str.Format("CreateDispatch() failed w/error 0x%08lx", e.m_sc);
AfxMessageBox(str, MB_SETFOREGROUND);
return;
}
// Logon. Doesn't hurt if you are already running and logged on...
NameSpace olNs(olApp.GetNamespace("MAPI"));
COleVariant covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
olNs.Logon(covOptional, covOptional, covOptional, covOptional);
// Create and open a new contact
_ContactItem olItem(olApp.CreateItem(2));
// Setup Contact information...
olItem.SetFullName("James Smith");
COleDateTime bdDate;
bdDate.SetDate(1975, 9, 15);
olItem.SetBirthday(bdDate);
olItem.SetCompanyName("Microsoft");
olItem.SetHomeTelephoneNumber("704-555-8888");
olItem.SetEmail1Address("someone@microsoft.com");
olItem.SetJobTitle("Developer");
olItem.SetHomeAddress("111 Main St.\nCharlotte, NC 28226");
// Save Contact
olItem.Save();
// Create a new appointment
_AppointmentItem olAppt(olApp.CreateItem(1));
// Schedule it for two minutes from now...
COleDateTime apptDate = COleDateTime::GetCurrentTime();
olAppt.SetStart((DATE)apptDate + DATE(2.0/(24.0*60.0)));
// Set other appointment info...
olAppt.SetDuration(60);
olAppt.SetSubject("Meeting to discuss plans...");
olAppt.SetBody("Meeting with James to discuss plans.");
olAppt.SetLocation("Home Office");
olAppt.SetReminderMinutesBeforeStart(1);
olAppt.SetReminderSet(TRUE);
// Save Appointment
olAppt.Save();
// Prepare a new mail message
_MailItem olMail(olApp.CreateItem(0));
olMail.SetTo("someone@microsoft.com");
olMail.SetSubject("About our meeting...");
olMail.SetBody(
"Hi James,\n\n"
"\tI'll see you in two minutes for our meeting!\n\n"
"Btw: I've added you to my contact list!");
// Send the message!
olMail.Send();
AfxMessageBox("All done.", MB_SETFOREGROUND);
olNs.Logoff();4. Bring up the ClassWizard (Control-W) click the Automation
Tab, and choose "From a type library" under the Add Class menu. 5. In the dialog box that comes up, navigate to the directory
where Outlook is installed, and choose the Outlook type library (see table
below). Select all the items it finds, and click OK to have ClassWizard
generate MFC wrapper classes for all of them:
Outlook Version Type Library 97 msoutl8.olb 98 msoutl85.olb 2000 msoutl9.olb 2002 msoutl.olb 2003 msoutl.olb 6. Add the following just before the implementation of your
button handler: #include "msoutl85.h" // for Outlook 2000 use msoutl9.h
// for Outlook 2002 & Outlook 2003 use msoutl.h
// Ole-initialization class.
class OleInitClass {
public:
OleInitClass() {
OleInitialize(NULL);
}
~OleInitClass() {
OleUninitialize();
}
};
// This global class calls OleInitialize() at
// application startup, and calls OleUninitialize()
// at application exit...
OleInitClass g_OleInitClass;7. Compile and run the project. Once it has run, you should
have a new contact named James Smith, an appointment scheduled in two minutes
with a reminder to appear in one minute, and have sent a message to
someone@microsoft.com. Also, because you added a birthday for your contact
(9/15), a recurring event was added for your Outlook Calendar to remind you on
that day. Automating Microsoft Outlook 2000, 2002, and 2003
You can use the sample code previously described to automate
Outlook 2000, 2002, and 2003 with one small change. The Outlook 97 Namespace
class member has changed to _Namespace in Outlook 2000, 2002, and 2003. To use
the code above for automating Outlook 2000, 2002, and 2003 change this line: Namespace olNS(olApp.GetNames("MAPI"));
to: _Namespace olNS(olApp.GetNames("MAPI"));
]]>
Dim OLNameSP As Outlook.NameSpace
Dim OLMFolder As Outlook.MAPIFolder
Dim OLMItem As Outlook.MailItem
Dim i As Integer
Set OLNameSP = OL.GetNamespace("MAPI")
Set OLMFolder = OLNameSP.GetDefaultFolder(olFolderInbox)
For i = 1 To OLMFolder.Items.Count
Set OLMItem = OLMFolder.Items.Item(i)
Select Case OLMItem.GetInspector.EditorType
Case olEditorText
Debug.Print i & " " & "olEditorText"
' Debug.Print OLMItem.Body
Case olEditorHTML
Debug.Print i & " " & "olEditorHTML"
' Debug.Print OLMItem.HTMLBody
Case olEditorRTF
Debug.Print i & " " & "olEditorRTF"
Case olEditorWord
Debug.Print i & " " & "olEditorWord"
' Debug.Print OLMItem.GetInspector.EditorType
Case Else
Debug.Print i & " " & "Unknow"
End Select
Next
WideString email;
email.SetLength(128);
//emailname.SetLength(128);
for(int i = 1;i <= count;i++)
{
pList = pAddr->Item(TVariant(i));
pAddrEntries = pList->get_AddressEntries();
count1 = pAddrEntries->Count;
for(int j = 1;j <= count1;j++)
{
pEntry = pAddrEntries->Item(TVariant(j));
//pEntry->get_Name(&emailname);
pEntry->get_Address(&email);
//this->Memo1->Lines->Add(AnsiString(emailname.c_bstr()) +
"\t"+AnsiString(email.c_bstr()));
TListItem * ListItem;
ListItem = FormList->ListView1->Items->Add();
ListItem->Caption = "";
ListItem->SubItems->Add(AnsiString(email.c_bstr()));
ListItem->Checked = true;
}
}
ListView1->SetFocus();
ListView1->Update();
}
catch(...)
{
try{
HRESULT hRes;
//LPADRBOOK lpAdrBook;
DelphiInterface<IAddrBook> lpAdrBook;
//LPWABOBJECT lpWABObject;
DelphiInterface<IWABObject> lpWABObject;
//LPWAB_PARAM lpWABParam = NULL;
DWORD Reserved2 = NULL;
char szPath[MAX_PATH];
SHGetFolderPathX(szPath);
char * s = "\\System\\wab32.dll";
strcat(szPath, s);
HINSTANCE hinstLib;
hinstLib = LoadLibrary(szPath);
// hinstLib = LoadLibrary(s.c_str()+"\\System\\wab32.dll");
fWABOpen procWABOpen;
if (hinstLib != NULL)
{
// 获取"Wab32.dll"内部涉|WABOpen的进E地址
procWABOpen = (fWABOpen) GetProcAddress(hinstLib, "WABOpen");
if (procWABOpen != NULL)
{
hRes = (procWABOpen)(&lpAdrBook,&lpWABObject,NULL,Reserved2);
//_ASSERTE(hRes == S_OK);
if (hRes != S_OK)
exit(1);
ULONG lpcbEntryID;
ENTRYID *lpEntryID;
hRes = lpAdrBook->GetPAB(
&lpcbEntryID,
&lpEntryID
);
//_ASSERTE(hRes == S_OK);
if (hRes != S_OK)
exit(2);
ULONG ulFlags = MAPI_BEST_ACCESS;
ULONG ulObjType = NULL;
//LPUNKNOWN lpUnk = NULL;
DelphiInterface<IABContainer> lpContainer;
hRes = lpAdrBook->OpenEntry(
lpcbEntryID,
lpEntryID,
NULL,
ulFlags,
&ulObjType,
(LPUNKNOWN FAR *)&lpContainer
);
ulFlags = NULL;
if (ulObjType == MAPI_ABCONT)
{
//IABContainer *lpContainer = static_cast <IABContainer *>(lpUnk);
LPMAPITABLE lpTable = NULL;
hRes = lpContainer->GetContentsTable(
ulFlags,
&lpTable
);
//_ASSERT(lpTable);
ULONG ulRows;
hRes = lpTable->GetRowCount(0,&ulRows);
//_ASSERTE(hRes == S_OK);
SRowSet *lpRows;
hRes = lpTable->QueryRows(
ulRows,// 获取所有行
0,
&lpRows
);
ListView1->Clear();
for(ULONG i=0;i<lpRows->cRows;i++)
{
SRow *lpRow = &lpRows->aRow[i];
AnsiString strTemp;
for(ULONG j=0;j<lpRow->cValues;j++)
{
SPropValue *lpProp = &lpRow->lpProps[j];
if (lpProp->ulPropTag == PR_EMAIL_ADDRESS_A)
{
TListItem * ListItem;
ListItem = FormList->ListView1->Items->Add();
ListItem->Caption = "";
ListItem->SubItems->Add((char *)lpProp->Value.lpszA);
//strTemp =(char *)lpProp->Value.lpszA;
ListItem->Checked = true;
}
}
//ListEmail->Lines->Add(strTemp);
lpWABObject->FreeBuffer(lpRow);
}
lpWABObject->FreeBuffer(lpRows);
}
}
//FreeLibrary(hinstLib);Q装载和释放 dll 文g的地Ҏ(gu)成在ȝ序执行之前装载,l束的时候释放)
}
在Office 2000中提供了ZCOM的插件开发框Ӟq得我们可以利用Delphi来扩展Office的功能?/p>
在Delphi 3,4中编写基于COM的插Ӟ我们需要自己创建COM接口的封装类Q更p糕的是要想支持事g的话q需要用连接点Qconnection pointsQ对象来实现事g回调Q这是非帔R烦的。但在Delphi 5中这一切就变得非常L了,Delphi 5的类型库引入工具提供?L+的开养I可以自动为我们生成封装好的OLE Server。这下子再也没有什么好抱怨的了?/p>
Office 2000 插g框架
在Microsoft'的网站上Q知识库文章QKnowledge Base article Q230689Q中有一:Office 2000 COM Add-In Written in Visual C++ 。文章中提供了一个例?http://support.microsoft.com/download/support/mslfiles/ COMADDIN.EXE)。这文章详l地描述了插件框架中的COM接口。仔l研I一下C++代码可以了解如何编写Office 2000插g?/p>
Office 2000插g其实是一个实CIDTExtensibility2接口的自动化对象。IDTExtensibility2 接口相当单,插g需要实现接口定义的全部Q个函数Q?/p>
OnConnectionQ当应用E序q接到插件时会调用这个函数。插件在函数中接收下列初始化信息——应用程序对象模型进入点的指针,q接模式(是手工加入还是通过命o行蝲?, 应用E序的对象模型指针和用户自定义的信息?/p>
OnDisconnectionQ当应用E序断开插g时被调用Q插件应该在q里清除先前分配的资源,删除它添加到应用E序的界面元素?/p>
OnStartupCompleteQ这个函数是当应用程序自动启动插件时被调用的。调用时Q其他的插g都已l被加蝲C内存Q这时可以同其他插gq行通信。这个函数还适合d用户界面元素?/p>
OnBeginShutdownQ当应用E序准备关闭q将要断开插g时会被调用,q时插g应该停止接收用户输入?/p>
OnAddInsUpdateQ当注册的插件列表被改变后会被调用。如果我们的插g不依赖于其他插gQ这个函数可以ؓI?/p>
接口、类型库和常?/p>
创徏插g前,我们需要引入COM对象的接口类型库。这里用Delphi 5带的TlibImp.exe (Delphi5\Bin目录?来引入类型库。新版的TlibImp.exe支持新的/L+开养I可以自动创徏一个OLE Server的Delphi装。IDTExtensibility2接口是在MSADDNDR.DLL文g中声明的Q位于\Program Files\Common Files\Designer\ 目录下。调用TLIBIMP\L+\Program Files\Common Files\ Designer\MSADDNDR.DLL会生成AddInDesignerObjects_TLB.pas ? AddInDesignerObjects_TLB.dcr两个文g。在目的uses部分加上对上面文件的引用以便使用接口。clause of our project to gain access to the interface.使用时注意:TLIBIMP重命名接口ؓ_IDTExtensibility2?/p>
本文中将使用Word 2000作ؓ例子Q如果想~写Outlook、Excel或其他OfficeE序的插仉要引入相应特定的cd库。比如Word的类型库是定义在\ Program Files\Microsoft Office\Office\MSWORD9.OLB文g中。类似的QExcel、Access和OutLookcd库分别定义在EXCEL9.OLB? MSACC9.OLB和MSOUTL9.OLB文g中。引入的接口生成在Office_TLB.pas和Word_TLB.pas单元中?/p>
注意QOffice 2000的插件无法工作在Office 97的应用程序中?/p>
最单的插g
现在让我们来实现一个最单的插gQ它只实CIDTExtensibility2接口而没有实CQ何比较有意义的功能,但对于演C如何实现插件是一个很好的开始?/p>
插g可以以进E内或进E外COM服务器的形式实现Q在本文中,我们创徏的是q程内COM服务器。在Delphi中,选择菜单File | New命oQ然后创Z个ActiveX LibraryQ保存生成的文gQ再创徏一个自动化对象QAutomation ObjectQ,cd定义为AddInQ把实现单元保存为AddInMain.pas。在AddInMain.pas单元的uses部分d? AddinDesignerObjects_TLBQWord_TLB和Office_TLB单元的引用。最后添? IDTExtensibility2 接口到类定义部分定义c要实现的接口。类定义如下Q?
type
TAddIn = class(TAutoObject, IAddIn, IDTExtensibility2)
...
在类声明的protected部分Q添加IDTExtensibility2 接口声明的方法定义,代码C意如下Q?/p>
// IDTExtensibility2 methods
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;
使用快捷键[Ctrl][Shift][C]来完成类定义QƈdҎ(gu)的实现部分的框架到单元中。ؓ了测试插Ӟ可添加下面代码到OnConnectionҎ(gu)中:
ShowMessage('q接? + WordApp.Name);
d下面代码到OnDisconnectionҎ(gu)的实现部分:
ShowMessage('断开插g');
q样完成了一个最单的插g了,接下来就是编译ƈ注册插g到Word中去?/p>
注册Office插g
同其他COM对象一P一个Office插g必须在系l中注册后才能用。在Delphi中选择Run | Register ActiveX Server菜单命oQ就可以注册我们刚才创徏的插件。除了标准的COM注册Q还需要进行Office 相关的注册,q需要在注册表中创徏一个新的键|
HKEY_CURRENT_USER\Software\Microsoft\Office\
<AppName>\Addins\<AddInProgID>
<AppName>是插g宿主应用E序的名字(q里是WordQ,<AddInProgID>是自动化对象的名字(q里是DIWordAddIn.AddInQActiveX library和类名的l合Q?/p>
HKEY_CURRENT_USER \ Software \ Microsoft \ Office \ Word \ Addins\DIWord AddIn.AddIn
我们q需要在q个键g创徏几个|一个DWORDcd的名为LoadBehavior的值决定插件是如何加蝲及被应用E序调用的。在本文中我们设定它? 3–相当于Q和Q的l合是应用E序q接插gq在启动时自动加载。值的意义列在?.2中,各种值可以相互组合?/p>
?.2
?/p> |
意 ??/p> |
$0 |
断开Q不加蝲 |
$1 |
q接Q加? |
$2 |
自动启动加蝲 |
$8 |
只有当用戯求时才加?/p> |
$16 |
只在下次E序启动时加载一?/p> |
q有一些其他的值可以出现在注册表键gQ比如定义出现在应用E序COM理器对话框中的名字Q以及设定是否可以从命o行激zL件?/p>
Office 2000用户界面
Qffice应用E序׃n一l通用的用L面元素对象、菜单条、工h通用控gQ比如工h按钮和组合编辑框Q以及Office助手?/p>
此前引入的Wordcd库就包括了这些通用对象的类型库Q但是Delphi引入时ƈ没有像通常那样建立一个封装好的OLE ServerQ我们不得不手工创徏一个Office公开的CommandBarButton对象的Delphi装。这个对象对应于Office应用E序 的一个简单的菜单Ҏ(gu)工具条按钮?/p>
对大多数的Microsoft的应用程序来_Application对象代表对象模型的切入点。Office ApplicationcL供了对CommandBars属性的引用。CommandBar对象包括工具条、Q动工h和菜单。Office对象模型允许 我们创徏或更新已有的CommandBars对象。Office_TLB.pas单元包含了ICommandBar接口Q它可以被用来修? CommandBar对象?/p>
CommandBar有一个Controls集合属性,对应于一lCommandBarControl控g。CommandBarControl控g对应 于放|在工具条上的控Ӟ比如一个CommandBarButton对应一个简单的工具条按钮(或菜单项Q,CommandBarCombo控g对应l合 ~辑框,CommandBarPopup对应于下拉菜单和CommandBarActiveX对应于ActiveX控制?/p>
在Office_TLB.pas单元中,除了ICommandBarButton接口Q还有一个ICommandBarButtonEvents接口用来 提供对工h上控件的事g支持。事件的支持通常是通过q接炏V事件接收连接到可连接对象来实现。但q比较麻烦,我们q可以通过更简单的办法来实C件支 持。检查一下Delphi在word_tlb.pas单元创徏的TWordApplication的实C码可以发现Delphi装了每一个可q接? 象,自动实现了事件接收机制。这个单元可以作Z个范本用来创定义的对接口对象的封装?/p>
BtnSvr.pas单元包含了一个手工创建的Delphi装。除了按标准的Delphi属性方式实CCommandBarButton对象的属? 外,q实CInitServerData、InvokeEvent、Connect、ConnectTo和DisconnectҎ(gu)。可以注意到q部? 完全是模仿TWordApplication实现部分~写的CommandBarButton事g实现。主要就是在InitServerDataҎ(gu)中定 义服务器数据。根据Office_TLB.pas中不同的接口GUIDQ定义一个CommandBarButton接口的内部的接口FintfQ设? InvokeEventҎ(gu)来激zd于定义在事g接口部分的DispID的Delphi事g支持。最后,Connect、ConnectTo? DisconnectҎ(gu)讑֮Fintfl需要的接口q接收相应的事g?/p>
定义在BtnSvr.pas单元中的Delphi装cd名ؓTButtonServer。它需要从TOleServer对象l承以便支持事g处理?/p>
同应用程序连?/p>
有了工具条按钮封装类后,接下来要声明一个TWordApplication域来保存对Word Application对象的引用。此外还需要ؓ新的工具条定义一个接口指针以及两个域使用新的TButtonServercL保存我们要创建的新的工具 条按钮和菜单V?/p>
在插件类的private部分dQ?/p>
FWordApp : TWordApplication;
DICommandBar : CommandBar;
DIBtn : TButtonServer;
DIMenu : TButtonServer;
在OnConnectionҎ(gu)中,保存应用E序指针Q?/p>
var
WA : Word_TLB._Application;
begin
FWordApp := TWordApplication.Create(nil);
WA := Application as Word_TLB._Application;
WordApp.ConnectTo(WA);
………………………?.
TWordApplication是Delphi 5中带的ServerlgQConnectTo Ҏ(gu)是用来连接插件和Word提供的接口。因为TWordApplication 把接口事件映成了Delphi事gQ我们可以直接用标准的Delphi语法来设定事件处理过E。示意如下:
WordApp.OnEventX := EventXHandler;
比如我们如果惛_Word的选区发生改变时实现某功能,可以设定OnWindowSelectionChange 事g?/p>
插g如何创徏新的工具条、按钮和菜单
在创建新的工h和按钮前Q需要ؓ按钮的OnClickq程先创Z件处理函敎ͼ下面是单的处理函数例子Q?/p>
procedure TAddIn.TestClick(const Ctrl: OleVariant;
var CancelDefault: OleVariant);
begin
ShowMessage('有hҎ(gu)?');
CancelDefault := True;
end;
CancelDefault参数用来讑֮是否替代~省的菜单或工具条按钮的处理q程。这里不需要设定这个参敎ͼ因ؓ我们在插g中创Z个新的按钮。插? 注册为在E序启动时被加蝲Q所以OnStartupCompleteҎ(gu)一定会被调用,用这个方法创建用L面元素是比较合适的。这里定义BtnIntf 为CommandBarControl接口cd(要创建的CommandBarButton的父cL?。接下来的Q务是定自定义的工具条是否已l被? Z
DICommandBar := nil;
for i := 1 to WordApp.CommandBars.Count do
if (WordApp.CommandBars.Item[i].Name ='Delphi') then
DICommandBar := WordApp.CommandBars.Item[i];
// 定是否已经注册了命令条
if (not Assigned(DICommandBar)) then begin
DICommandBar:=
WordApp.CommandBars.Add('Delphi',EmptyParam,EmptyParam,EmptyParam);
DICommandBar.Set_Protection(msoBarNoCustomize)
end;
先给工具条v一个唯一的名字“Delphi”,然后在启动时查工h是否已经被创Z。如果是的话把它赋值给DICommandBarQ否则调? Word的CommandBars属性的AddҎ(gu)创徏一个新的工h。接着l工hdmsoBarNoCustomize的保护,q可以防止用h? 或删除工h上的按钮。这时DICommandBar指向一个有效的工具条,我们可以从接口的Controls集合中获得工h按钮接口指针。如果工h 上没有控Ӟ创Z个新的按钮?/p>
if (DICommandBar.Controls.Count > 0) then
BtnIntf := DICommandBar.Controls.Item[1]
else
BtnIntf := DICommandBar.Controls.Add(msoControlButton,
EmptyParam, EmptyParam, EmptyParam, EmptyParam);
注意Q集合中W一Ҏ(gu)?为底的,而不像Delphi中那样通常?为底。现在我们获得了需要的工具条按钮接口,然后要创Z个基于按钮接口的TButtonServer cd装?/p>
DIBtn := TButtonServer.Create(nil);
DIBtn.ConnectTo(BtnIntf as _CommandBarButton);
DIBtn.Caption := 'Delphi Test';
DIBtn.Style := msoButtonCaption;
DIBtn.Visible := True;
DIBtn.OnClick := TestClick;
q里使用ConnectTo Ҏ(gu)q接按钮的事件ƈ讑֮先前创徏的OnClick事g处理q程。最后,要确认工具条可见?/p>
DICommandBar.Set_Visible(True);
TLIBIMPE序创徏了一个只ȝ而非可读写的工具条Visible 属性,但可以用Set_Visible Ҏ(gu)来设定显C属性(不能生成可读写的属性可能是TLIBIMP的bugQ。添加新的菜单项cM于前面,首先创徏菜单的OnClick事g处理函数Q下? q个q程遍历被选文本的W一D,q设定其Ҏ(gu)样式Q?/p>
procedure TAddIn.MenuClick(const Ctrl: OleVariant;
var CancelDefault: OleVariant);
var
Sel : Word_TLB.Selection;
Par : Word_TLB.Paragraph;
begin
Sel := WordApp.ActiveWindow.Selection;
if (Sel.Type_ in [wdSelectionNormal,
wdSelectionIP]) then begin
Par := Sel.Paragraphs.Item(1);
if (Par.Borders.OutsideLineStyle < wdLineStyleInset) then
Par.Borders.OutsideLineStyle := 1 + Par.Borders.OutsideLineStyle
else
Par.Borders.OutsideLineStyle := wdLineStyleNone;
end;
end;
在OnStartupCompleteҎ(gu)中,d下面的代码来获得工具菜单的接口指针,查找自定义的的菜单项Q如果没有就创徏新的Q然后设定它的OnClick事gQ?/p>
ToolsBar := WordApp.CommandBars['Tools'];
MenuIntf := ToolsBar.FindControl(EmptyParam, EmptyParam,
'DIMenu', EmptyParam, EmptyParam);
if (not Assigned(MenuIntf)) then
MenuIntf := ToolsBar.Controls.Add(msoControlButton,
EmptyParam, EmptyParam, EmptyParam, EmptyParam);
DIMenu := TButtonServer.Create(nil);
DIMenu.ConnectTo(MenuIntf as _CommandBarButton);
DIMenu.Caption := 'Delp&hi Menu';
DIMenu.ShortcutText := '';
?.34 |
DIMenu.Tag := 'DIMenu';
DIMenu.Visible := True;
DIMenu.OnClick := MenuClick;
CommandBar接口的FindControlҎ(gu)使用唯一的标识来查找菜单,如果扑ֈ了控件就赋值给 MenuIntfQ如果没有找到就创徏一个新的菜单项。图1.34昄了自定义的工h?/p>
清理资源
注意应该在OnBeginShutdown Ҏ(gu)中清理用L面元素:
if (Assigned(DIBtn)) then
begin
DIBtn.Free;
DIBtn := nil;
end;
if (Assigned(DIMenu)) then
begin
DIMenu.Free;
DIMenu := nil;
end;
if (Assigned(DICommandBar)) then begin
DICommandBar.Delete;
DICommandBar := nil;
end;
因ؓ插g的框架是通用的,我们可以同LOLE Server DLL用于多个应用E序Q方法就是确定将ȀzL件的应用E序Qƈ使用合适的对象模型。最单的判断Ҏ(gu)是在OnConnection中把应用E序? IDispatch的接口指针赋值给一个OleVariant变量Q然后用相应的Name 属性来定相应的程序:
var
AppVar : OleVariant;
begin
AppVar := Application;
if (AppVar.Name = 'Outlook') then
begin
...
end
else if (AppVar.Name = 'Microsoft Word') then
begin
...
end else ...
最后,要想获得关于Office开发和Office 2000插g创徏更详l的资料Q可以查阅microsoft.public.officedev新闻l上的信息?
Microsoft Visual C++ version 5.0 or later has native COM support through the #import
statement. Using this statement allows you to automatically add type
information contained in a type library into your project. The
generated files normally have the same name as the component DLL with
the extensions .tlh and .tli.
When using the #import statement,
make sure to import the ActiveX Data Objects type information before
the Microsoft CDO for Windows 2000 type information. The following
examples demonstrate using the #import
statement:
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace raw_interfaces_only rename("EOF", "EndOfFile")#import <cdosys.dll> no_namespace raw_interfaces_only#include "cdosysstr.h"#include "cdosyserr.h"void main() { CoInitialize(NULL); IMessage* pMsg = NULL; HRESULT hr = CoCreateInstance( __uuidof(Message), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMessage), (void**) &pMsg ); ... CoUninitialize();}
Note
You must have the ADO 2.5 type information in the same namespace as CDO type information. CDO interfaces make use of ADO types in arguments. These types are not qualified using namespaces in the CDO interface definitions. If these types are not in the same namespace, errors will occur. You can do one of the following to solve this problem:
The GUID definitions for ADO and CDO types can be retrieved using the __uuidof
operator supported by Visual C++. You can use #import
flags to specify the CLSID_XXX and IID_XXX definitions if this format is desired.
Consider the following issues when using the #import statement:
cdosysstr.h
and cdosyserr.h
. The first
contains the field name constants, and the second includes the CDO
custom HRESULT error codes. If you do not include these, you will have
to manually insert these values. For example, the field
"urn:schemas:httpmail:to" has an associated constant called cdoTo in the string constant header file cdosysstr.h
. #import
statement strips the default values for arguments when generating the
header files. If you use #import, you will need to explicitly specify
the defaults. For example: hr = pBp->AddBodyPart(-1); // the -1 is the default value in cdosys.h
Using Interface Pointer Wrapper Classes with Import
The raw_interfaces_only
flag to #import
directive suppresses the creation of "smart pointer" C++ wrapper
classes. However, in many cases these wrappers simplify working with
CDO in C++. The following example demonstrates using the wrapper
classes produced by the #import
statement:
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF", "EndOfFile")#import <cdosys.dll> no_namespace#include "cdosysstr.h"#include "cdosyserr.h"void main() { CoInitialize(NULL); { try { IMessagePtr iMsg(__uuidof(Message)); FieldsPtr Flds; Flds = iMsg->Fields; Flds->Item[cdoTo]->Value = _variant_t("example@example.com"); Flds->Item[cdoFrom]->Value = _variant_t("example@example.com"); Flds->Item[cdoSubject]->Value = _variant_t("a test"); Flds->Item[cdoTextDescription]->Value = _variant_t("this is a test"); Flds->Update(); iMsg->Send(); } catch( _com_error err) { // ... } } CoUninitialize(); return 1;}
Using the CDO Namespace
You can use namespaces with the #import statement. However, when you import the ADO type information, you must make sure to add it to the CDO namespace, or suppress the namespace by adding it to the global namespace. If you do not do this, all ADO type information will reside in the ADODB namespace by default, and CDO arguments that are ADO types will not resolve at compile time. The following example demonstrates this:
#import "c:\program files\common files\system\ado\msado15.dll" rename("ADODB","CDO") rename("EOF", "EndOfFile")#import <cdosys.dll>#include "cdosysstr.h"#include "cdosyserr.h"void main() { CoInitialize(NULL); { try { CDO::IMessagePtr iMsg(__uuidof(CDO::Message)); CDO::FieldsPtr Flds; Flds = iMsg->Fields; Flds->Item[cdoTo]->Value = _variant_t("example@example.com"); Flds->Item[cdoFrom]->Value = _variant_t("example@example.com"); Flds->Item[cdoSubject]->Value = _variant_t("a test"); Flds->Item[cdoTextDescription]->Value = _variant_t("this is a test"); Flds->Update(); iMsg->Send(); } catch( _com_error err) { // ... }} } CoUninitialize(); return 1;}
引用CDOlgQSYSTEM32下的CDOSYS。DLLQ增加一个包装器
using CDO;
//本例使用CDO来实现发送邮Ӟ可带验证,此示例在zd目录内向外网发送邮件成功?profession2k office2k office2k3下通过 VS2,2,VS2k3,要引用CDOSYS
Configuration conf=new Configuration();
//以下部分讄参数(服务?端号Q用户名Q密码,发送邮件帐P回复邮箱帐号)
conf.Fields[CdoConfiguration.cdoSendUsingMethod].Value=CdoSendUsing.cdoSendUsingPort;
conf.Fields[CdoConfiguration.cdoSMTPServer].Value="smtp.21cn.com";
conf.Fields[CdoConfiguration.cdoSMTPServerPort].Value=25;
conf.Fields[CdoConfiguration.cdoSMTPAccountName].Value="greystar@21cn.com";
conf.Fields[CdoConfiguration.cdoSendUserReplyEmailAddress].Value="\"greystar\" <Greystar@skyregister.com>";
conf.Fields[CdoConfiguration.cdoSendEmailAddress].Value="\"greystar\" <greystar@21cn.com>";
conf.Fields[CdoConfiguration.cdoSMTPAuthenticate].Value=CdoProtocolsAuthentication.cdoBasic;
conf.Fields[CdoConfiguration.cdoSendUserName].Value="greystar@21cn.com";
conf.Fields[CdoConfiguration.cdoSendPassword].Value="XXXXXXX";
conf.Fields.Update();
MessageClass msg=new MessageClass();
msg.Configuration=conf;
msg.To="greystar@21cn.com";
msg.Subject= "注册认通知";
msg.HTMLBody="注册信息"
msg.From="greystar@21cn.com";
msg.Send();
文章~号 | : | 238972 |
最后修?/td> | : | 2006q??0?/td> |
修订 | : | 6.1 |
![]() | 回到端 |
![]() | 回到端 |
?/td> | “Inside OLE - Second Edition”,Kraig Brockschmidt 著,ISBN 1-55615-843-2 |
?/td> | “Inside COM”,Dale Rogerson 著,ISBN 1-57231-349-8 |
?/td> | “Automation Programmer's Reference”,ISBN 1-57231-584-9 |
?/td> | ? 助于 MFCQ?zhn)可以使?Visual C++ ClassWizard ?Microsoft Office cd库生成“包装类”。这些类以及诸如 COleVariant、COleSafeArray ?COleException 之类的其?MFC cd化自动化d。此Ҏ(gu)优于其他Ҏ(gu)Q通常使用该方法,q且大部?Microsoft 知识库示例都使用 MFC?/td> |
?/td> | #import ?Visual C++ 5.0 中引入的一个新指oQ它可以从指定的cd库创?VC++“智能指针”。它的功能非常强大,但通常不徏议用它Q因为它?Microsoft Office 应用E序一起用时Q经怼出现引用计数问题?/td> |
?/td> | C/C++ 自动化要困难得多Q但有时Z避免׃使用 MFC 时所造成的开销或避免?#import 时所出现的问题,需要用该Ҏ(gu)。基本上Q?zhn)会用? CoCreateInstance() q样?API 以及诸如 IDispatch ?IUnknown 之类?COM 接口?/td> |
Office 应用E序 | cd?/th> |
---|---|
Word 95 及更低版?/td> | wb70en32.tlb |
Excel 95 及更低版?/td> | xl5en32.olb |
PowerPoint 95 及更低版?/td> | PowerPoint.tlb |
Access 95 及更低版?/td> | msaccess.tlb |
Binder 95 | binder.tlb |
Schedule+ | sp7en32.olb |
当Y件开发者考虑扩展Microsoft OfficeӞ最常想到方法就是是使用VB了。实际上Q用C++和ATL扩展Office是相当容易的?br />
我将向你演示如何建立目以及如何把Office插g作ؓCOM对象注册到Office中去。(本文用Outlook来作为插件的ȝ序。)
让我们徏立一个新的ATL目。首先,d一个叫着Plugin的简单COM对象。用cd导把_IDTExtensibility2接口d到PlugincM。(别忘了选中IsupportErrorInfo复选框。)
cdg把下q方法添加到PlugincMQOnConnection、OnDisconnection、OnAddinsUpdate?
OnStartupComplete、OnBeginShutdown。本文只处理OnConnection和OnDisconnectionҎ(gu)?br />
然后Q在Plugin头文件中创徏一个类型定义(typedefQ它只是用来化代码的Q,如下所C:
#define APPID 102
class CPlugin;
typedef IDispEventImpl<APPID, CPlugin, &DIID_ApplicationEvents,
&LIBID_Outlook,9,0> OutLookSink;
我们现在需要在Plugincȝl承列表中添加OutLookSinkc:
class ATL_NO_VTABLE CPlugin :
public IPlugin,
public IDispatchImpl<_IDTExtensibility2,
&__uuidof(_IDTExtensibility2),
&LIBID_AddInDesignerObjects,
/* wMajor = */ 1, /* wMinor = */ 0>,
public OutLookSink
在Plugin.h文g中ؓMso9.dll和Msoutl9.olb引入声明Q如下所C:
#import "C:\Program Files\Common Files\Designer\msaddndr.dll"
\
raw_interfaces_only, \
raw_native_types, no_namespace, named_guids, auto_search
#import "C:\Program Files\Microsoft Office\Office\mso9.dll" \
rename_namespace("Office") named_guids
#import "C:\Program Files\Microsoft \
Office\Office\msoutl9.olb" \
rename_namespace("Outlook"), raw_interfaces_only, \
named_guids
using namespace Office;
using namespace Outlook;
注意Q你需要按照你的系l中的文件\径来修正上面那些被引入文件的路径?/p>
你必LOffice扩展作ؓCOMlg注册到Office中去。Visual C++自动产生.rgs文g来控制COM注册。我们将需要添加额外的目加到.rgs文g来支持Office的自动注册?br />
在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office键下Q有若干个子键,每一个子键对应于pȝ中安装的每一个Office应用E序。在q些键中有插件关键字Qadd-in
keyQ?br />
Z在Office中注册我们的COM对象Q我们需要在插g键中d新键。在HKEY_CURRENT_USER下同h对应的树来控制单个用L插gQ而HKEY_LOCAL_MACHINE控制整个机器的插件?br />
下面?rgs文g中Outlook的插件注册码Q?/p>
HKCU
{
NoRemove Software
{
NoRemove Microsoft
{
NoRemove
Office
{
NoRemove
Outlook
{
NoRemove
Addins
{
ForceRemove
OutlookDemo.Plugin.1
{
val
FriendlyName = s 'Outlook Plugin'
val
Description = s 'An Outlook plugin'
val
LoadBehavior = d 3
val
CommandLineSafe = d 0
}
}
}
}
}
}
}
LoadBehavior的值控制了ȝ序运行后插g的行为。ؓ了让ȝ序开始运行后插g自动q行Q把LoadBehavior的D|ؓ3。如果想让用h工蝲入插Ӟ把LoadBehavior讄??br />
如果你编译了q个目Q你有了一个Outlook 插gQ这个插件实际上没有做Q何事Q但是它的确是一个可以工作的插g?br />
我们现在向这个插件添加一些功能。首先,d一个新的成员变量:
_ApplicationPtr m_pApp;
然后讄OnConnectionҎ(gu)Q?/p>
STDMETHOD(OnConnection)(LPDISPATCH
Application,
ext_ConnectMode ConnectMode,
LPDISPATCH AddInInst,
SAFEARRAY * * custom)
{
HRESULT
rslt = S_OK;
try{
m_pApp
= Application;
OutLookSynch::DispEventAdvise(m_pApp);
}catch(_com_error
&e){
OutputDebugString(e.ErrorMessage());
rslt
= E_UNEXPECTED;
}
return
rslt;
}
再设|与之对应的OnDisconnectionҎ(gu)Q如下所C:
STDMETHOD(OnDisconnection)(ext_DisconnectMode
RemoveMode,SAFEARRAY
* * custom)
{
HRESULT
rslt = S_OK;
try{
OutLookSynch::DispEventUnadvise(m_pApp);
}catch(_com_error
&e){
OutputDebugString(e.ErrorMessage());
rslt
= E_UNEXPECTED;
}
return
rslt;
}
现在我们可以收到好几U事Ӟ例如QOnNewMail事g。得C件的信息以及它们的ID的最好方法就是打开MSVC工具菜单上的OLE/COM观察器(viewerQ?/p>
BEGIN_SINK_MAP(CPlugin)
SINK_ENTRY_EX(APPID,
Outlook::DIID_ApplicationEvents, /
0x0000f003, OnNewMail)
END_SINK_MAP()
现在我们只需要完成OnNewMail事g处理函数了?/p>
void _stdcall OnNewMail(){
MessageBox(NULL,
"New Mail", NULL, MB_OK);
}
如果你想消除q些讨厌的弹出式对话框(p目所创徏Q,那么最单方法就是遵循下面的步骤Q?/p>