最近发现很多人都在研究OFFICE方面的编程,当然,偶也是一个啦:)
可是这方面的资料却很难找,而且大部分(几乎全部)都是英文的。
于是,便有了写这篇文章的念头(好了,言归正传)。
本来OFFICE已经为大家提供了很好用的COM组件,但我发现我怎么用怎么不顺手(估计是本人太菜了)。
于是便绞尽脑汁想用纯 C++ 代码来实现,终于,哈哈,嘿嘿,嚯嚯……
好了,下面是我的步骤(偶用的VC++ 6.0):
1. 先新建一个 “Win32 控制台应用/Win32 Console Application ”工程,工程名不妨叫做 “createXLS ”。
2. 工程向导里选择 “A "Hello,World!" application ”,新建完毕(废话)。
3. 打开 “createXLS.cpp ”文件,添加代码(本不想贴代码的,想做个工程让大家下载,因为太简单,不好意思兴师动众了):
可是这方面的资料却很难找,而且大部分(几乎全部)都是英文的。
于是,便有了写这篇文章的念头(好了,言归正传)。
本来OFFICE已经为大家提供了很好用的COM组件,但我发现我怎么用怎么不顺手(估计是本人太菜了)。
于是便绞尽脑汁想用纯 C++ 代码来实现,终于,哈哈,嘿嘿,嚯嚯……
好了,下面是我的步骤(偶用的VC++ 6.0):
1. 先新建一个 “Win32 控制台应用/Win32 Console Application ”工程,工程名不妨叫做 “createXLS ”。
2. 工程向导里选择 “A "Hello,World!" application ”,新建完毕(废话)。
3. 打开 “createXLS.cpp ”文件,添加代码(本不想贴代码的,想做个工程让大家下载,因为太简单,不好意思兴师动众了):
#include
<
ole2.h
>
//
这个头文件一定要包含,否则就不能自动化了
#include < stdio.h >
// 接着修改我们添加一个函数,这个函数是整个程序的基础
// 若以后写别的程序而想用纯 C++ 来实现自动化,这个函数是可以复用的
HRESULT AutoWrap( int autoType, VARIANT * pvResult, IDispatch * pDisp, LPOLESTR ptName, int cArgs...);
// 修改主函数
int main( int argc, char * argv[])
{
// printf("Hello World! "); // 注释掉这一句
// 初始化COM库
CoInitialize(NULL);
// 获得EXCEL的CLSID
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L " Excel.Application " , & clsid);
if (FAILED(hr)) {
::MessageBox(NULL, " CLSIDFromProgID() 函数调用失败! " , " 错误 " , 0x10010 );
return - 1 ;
}
// 创建实例
IDispatch * pXlApp;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, ( void ** ) & pXlApp);
if (FAILED(hr)) {
::MessageBox(NULL, " 请检查是否已经安装EXCEL! " , " 错误 " , 0x10010 );
return - 2 ;
}
// 显示,将Application.Visible属性置1
VARIANT x;
x.vt = VT_I4;
x.lVal = 1 ;
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlApp, L " Visible " , 1 , x);
// 获取Workbooks集合
IDispatch * pXlBooks;
{
VARIANT result;
VariantInit( & result);
AutoWrap(DISPATCH_PROPERTYGET, & result, pXlApp, L " Workbooks " , 0 );
pXlBooks = result.pdispVal;
}
// 调用Workbooks.Add()方法,创建一个新的Workbook
IDispatch * pXlBook;
{
VARIANT result;
VariantInit( & result);
AutoWrap(DISPATCH_PROPERTYGET, & result, pXlBooks, L " Add " , 0 );
pXlBook = result.pdispVal;
}
// 创建一个15x15的数组,用于填充表格
VARIANT arr;
WCHAR szTmp[ 32 ];
arr.vt = VT_ARRAY | VT_VARIANT;
SAFEARRAYBOUND sab[ 2 ];
sab[ 0 ].lLbound = 1 ; sab[ 0 ].cElements = 15 ;
sab[ 1 ].lLbound = 1 ; sab[ 1 ].cElements = 15 ;
arr.parray = SafeArrayCreate(VT_VARIANT, 2 , sab);
// 初始化数组内容
for ( int i = 1 ; i <= 15 ; i ++ ) {
for ( int j = 1 ; j <= 15 ; j ++ ) {
VARIANT tmp;
tmp.vt = VT_BSTR;
wsprintfW(szTmp,L " %i,%i " ,i,j);
tmp.bstrVal = SysAllocString(szTmp);
// 添加数据到数组中
long indices[] = {i,j};
SafeArrayPutElement(arr.parray, indices, ( void * ) & tmp);
}
}
// 从Application.ActiveSheet属性获得Worksheet对象
IDispatch * pXlSheet;
{
VARIANT result;
VariantInit( & result);
AutoWrap(DISPATCH_PROPERTYGET, & result, pXlApp, L " ActiveSheet " , 0 );
pXlSheet = result.pdispVal;
}
// 选择一个15x15大小的Range
IDispatch * pXlRange;
{
VARIANT parm;
parm.vt = VT_BSTR;
parm.bstrVal = ::SysAllocString(L " A1:O15 " );
VARIANT result;
VariantInit( & result);
AutoWrap(DISPATCH_PROPERTYGET, & result, pXlSheet, L " Range " , 1 , parm);
VariantClear( & parm);
pXlRange = result.pdispVal;
}
::MessageBox(NULL, " 我要填充数据了哈! " , " 通知 " , 0x10000 );
// 用我们的数组填充这个Range
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlRange, L " Value " , 1 , arr);
pXlRange -> Release();
// 另外再选择一个Range
{
VARIANT parm;
parm.vt = VT_BSTR;
parm.bstrVal = ::SysAllocString(L " A11:O25 " );
VARIANT result;
VariantInit( & result);
AutoWrap(DISPATCH_PROPERTYGET, & result, pXlSheet, L " Range " , 1 , parm);
VariantClear( & parm);
pXlRange = result.pdispVal;
}
::MessageBox(NULL, " 我还要填充一次哈! " , " 通知 " , 0x10000 );
// 用我们的数组再次填充这个Range
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlRange, L " Value " , 1 , arr);
::MessageBox(NULL, " 好了,我们该保存文件了! " , " 通知 " , 0x10000 );
// 接下来我们该保存文件了,利用Worksheet.SaveAs()方法(我这里忽略了其他所有参数,除了文件名)
{
VARIANT filename;
filename.vt = VT_BSTR;
filename.bstrVal = SysAllocString(L " c:/test.xls " );
AutoWrap(DISPATCH_METHOD, NULL, pXlSheet, L " SaveAs " , 1 , filename);
}
::MessageBox(NULL, " 哈哈,收工了! " , " 通知 " , 0x10000 );
// 退出,调用Application.Quit()方法
AutoWrap(DISPATCH_METHOD, NULL, pXlApp, L " Quit " , 0 );
// 释放所有的接口以及变量
pXlRange -> Release();
pXlSheet -> Release();
pXlBook -> Release();
pXlBooks -> Release();
pXlApp -> Release();
VariantClear( & arr);
// 注销COM库
CoUninitialize();
return 0 ;
}
// AutoWrap 函数的正体(真身,哈哈)
// 先声明:这个函数不是偶写的哈(别问是谁写的,偶也不知道)
// AutoWrap() - Automation helper function...
HRESULT AutoWrap( int autoType, VARIANT * pvResult, IDispatch * pDisp, LPOLESTR ptName, int cArgs...) {
// Begin variable-argument list...
va_list marker;
va_start(marker, cArgs);
if ( ! pDisp) {
MessageBox(NULL, " NULL IDispatch passed to AutoWrap() " , " Error " , 0x10010 );
_exit( 0 );
}
// Variables used...
DISPPARAMS dp = { NULL, NULL, 0 , 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr;
char buf[ 200 ];
char szName[ 200 ];
// Convert down to ANSI
WideCharToMultiByte(CP_ACP, 0 , ptName, - 1 , szName, 256 , NULL, NULL);
// Get DISPID for name passed...
hr = pDisp -> GetIDsOfNames(IID_NULL, & ptName, 1 , LOCALE_USER_DEFAULT, & dispID);
if (FAILED(hr)) {
sprintf(buf, " IDispatch::GetIDsOfNames("%s") failed w/err 0x%08lx " , szName, hr);
MessageBox(NULL, buf, " AutoWrap() " , 0x10010 );
_exit( 0 );
return hr;
}
// Allocate memory for arguments...
VARIANT * pArgs = new VARIANT[cArgs + 1 ];
// Extract arguments...
for ( int i = 0 ; i < cArgs; i ++ ) {
pArgs[i] = va_arg(marker, VARIANT);
}
// Build DISPPARAMS
dp.cArgs = cArgs;
dp.rgvarg = pArgs;
// Handle special-case for property-puts!
if (autoType & DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = 1 ;
dp.rgdispidNamedArgs = & dispidNamed;
}
// Make the call!
hr = pDisp -> Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, & dp, pvResult, NULL, NULL);
if (FAILED(hr)) {
sprintf(buf, " IDispatch::Invoke("%s"=%08lx) failed w/err 0x%08lx " , szName, dispID, hr);
MessageBox(NULL, buf, " AutoWrap() " , 0x10010 );
_exit( 0 );
return hr;
}
// End variable-argument section...
va_end(marker);
delete [] pArgs;
return hr;
}
#include < stdio.h >
// 接着修改我们添加一个函数,这个函数是整个程序的基础
// 若以后写别的程序而想用纯 C++ 来实现自动化,这个函数是可以复用的
HRESULT AutoWrap( int autoType, VARIANT * pvResult, IDispatch * pDisp, LPOLESTR ptName, int cArgs...);
// 修改主函数
int main( int argc, char * argv[])
{
// printf("Hello World! "); // 注释掉这一句
// 初始化COM库
CoInitialize(NULL);
// 获得EXCEL的CLSID
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L " Excel.Application " , & clsid);
if (FAILED(hr)) {
::MessageBox(NULL, " CLSIDFromProgID() 函数调用失败! " , " 错误 " , 0x10010 );
return - 1 ;
}
// 创建实例
IDispatch * pXlApp;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, ( void ** ) & pXlApp);
if (FAILED(hr)) {
::MessageBox(NULL, " 请检查是否已经安装EXCEL! " , " 错误 " , 0x10010 );
return - 2 ;
}
// 显示,将Application.Visible属性置1
VARIANT x;
x.vt = VT_I4;
x.lVal = 1 ;
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlApp, L " Visible " , 1 , x);
// 获取Workbooks集合
IDispatch * pXlBooks;
{
VARIANT result;
VariantInit( & result);
AutoWrap(DISPATCH_PROPERTYGET, & result, pXlApp, L " Workbooks " , 0 );
pXlBooks = result.pdispVal;
}
// 调用Workbooks.Add()方法,创建一个新的Workbook
IDispatch * pXlBook;
{
VARIANT result;
VariantInit( & result);
AutoWrap(DISPATCH_PROPERTYGET, & result, pXlBooks, L " Add " , 0 );
pXlBook = result.pdispVal;
}
// 创建一个15x15的数组,用于填充表格
VARIANT arr;
WCHAR szTmp[ 32 ];
arr.vt = VT_ARRAY | VT_VARIANT;
SAFEARRAYBOUND sab[ 2 ];
sab[ 0 ].lLbound = 1 ; sab[ 0 ].cElements = 15 ;
sab[ 1 ].lLbound = 1 ; sab[ 1 ].cElements = 15 ;
arr.parray = SafeArrayCreate(VT_VARIANT, 2 , sab);
// 初始化数组内容
for ( int i = 1 ; i <= 15 ; i ++ ) {
for ( int j = 1 ; j <= 15 ; j ++ ) {
VARIANT tmp;
tmp.vt = VT_BSTR;
wsprintfW(szTmp,L " %i,%i " ,i,j);
tmp.bstrVal = SysAllocString(szTmp);
// 添加数据到数组中
long indices[] = {i,j};
SafeArrayPutElement(arr.parray, indices, ( void * ) & tmp);
}
}
// 从Application.ActiveSheet属性获得Worksheet对象
IDispatch * pXlSheet;
{
VARIANT result;
VariantInit( & result);
AutoWrap(DISPATCH_PROPERTYGET, & result, pXlApp, L " ActiveSheet " , 0 );
pXlSheet = result.pdispVal;
}
// 选择一个15x15大小的Range
IDispatch * pXlRange;
{
VARIANT parm;
parm.vt = VT_BSTR;
parm.bstrVal = ::SysAllocString(L " A1:O15 " );
VARIANT result;
VariantInit( & result);
AutoWrap(DISPATCH_PROPERTYGET, & result, pXlSheet, L " Range " , 1 , parm);
VariantClear( & parm);
pXlRange = result.pdispVal;
}
::MessageBox(NULL, " 我要填充数据了哈! " , " 通知 " , 0x10000 );
// 用我们的数组填充这个Range
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlRange, L " Value " , 1 , arr);
pXlRange -> Release();
// 另外再选择一个Range
{
VARIANT parm;
parm.vt = VT_BSTR;
parm.bstrVal = ::SysAllocString(L " A11:O25 " );
VARIANT result;
VariantInit( & result);
AutoWrap(DISPATCH_PROPERTYGET, & result, pXlSheet, L " Range " , 1 , parm);
VariantClear( & parm);
pXlRange = result.pdispVal;
}
::MessageBox(NULL, " 我还要填充一次哈! " , " 通知 " , 0x10000 );
// 用我们的数组再次填充这个Range
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlRange, L " Value " , 1 , arr);
::MessageBox(NULL, " 好了,我们该保存文件了! " , " 通知 " , 0x10000 );
// 接下来我们该保存文件了,利用Worksheet.SaveAs()方法(我这里忽略了其他所有参数,除了文件名)
{
VARIANT filename;
filename.vt = VT_BSTR;
filename.bstrVal = SysAllocString(L " c:/test.xls " );
AutoWrap(DISPATCH_METHOD, NULL, pXlSheet, L " SaveAs " , 1 , filename);
}
::MessageBox(NULL, " 哈哈,收工了! " , " 通知 " , 0x10000 );
// 退出,调用Application.Quit()方法
AutoWrap(DISPATCH_METHOD, NULL, pXlApp, L " Quit " , 0 );
// 释放所有的接口以及变量
pXlRange -> Release();
pXlSheet -> Release();
pXlBook -> Release();
pXlBooks -> Release();
pXlApp -> Release();
VariantClear( & arr);
// 注销COM库
CoUninitialize();
return 0 ;
}
// AutoWrap 函数的正体(真身,哈哈)
// 先声明:这个函数不是偶写的哈(别问是谁写的,偶也不知道)
// AutoWrap() - Automation helper function...
HRESULT AutoWrap( int autoType, VARIANT * pvResult, IDispatch * pDisp, LPOLESTR ptName, int cArgs...) {
// Begin variable-argument list...
va_list marker;
va_start(marker, cArgs);
if ( ! pDisp) {
MessageBox(NULL, " NULL IDispatch passed to AutoWrap() " , " Error " , 0x10010 );
_exit( 0 );
}
// Variables used...
DISPPARAMS dp = { NULL, NULL, 0 , 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr;
char buf[ 200 ];
char szName[ 200 ];
// Convert down to ANSI
WideCharToMultiByte(CP_ACP, 0 , ptName, - 1 , szName, 256 , NULL, NULL);
// Get DISPID for name passed...
hr = pDisp -> GetIDsOfNames(IID_NULL, & ptName, 1 , LOCALE_USER_DEFAULT, & dispID);
if (FAILED(hr)) {
sprintf(buf, " IDispatch::GetIDsOfNames("%s") failed w/err 0x%08lx " , szName, hr);
MessageBox(NULL, buf, " AutoWrap() " , 0x10010 );
_exit( 0 );
return hr;
}
// Allocate memory for arguments...
VARIANT * pArgs = new VARIANT[cArgs + 1 ];
// Extract arguments...
for ( int i = 0 ; i < cArgs; i ++ ) {
pArgs[i] = va_arg(marker, VARIANT);
}
// Build DISPPARAMS
dp.cArgs = cArgs;
dp.rgvarg = pArgs;
// Handle special-case for property-puts!
if (autoType & DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = 1 ;
dp.rgdispidNamedArgs = & dispidNamed;
}
// Make the call!
hr = pDisp -> Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, & dp, pvResult, NULL, NULL);
if (FAILED(hr)) {
sprintf(buf, " IDispatch::Invoke("%s"=%08lx) failed w/err 0x%08lx " , szName, dispID, hr);
MessageBox(NULL, buf, " AutoWrap() " , 0x10010 );
_exit( 0 );
return hr;
}
// End variable-argument section...
va_end(marker);
delete [] pArgs;
return hr;
}