C++Builder 6中开发 Office 程序心得
来源:互联网 发布:手机测音调软件 编辑:程序博客网 时间:2024/06/10 08:06
http://tech.ddvip.com/2008-09/122172092767012.html
一、用控件还是用OLEAutomation?
这个问题应该说很常见。我也在任何可能的情况下坚持我的主张:用BCB 6提供的Server控件组。如果你是用Delphi 6/7版本开发,那么用Delphi提供的Server控件组。
这样做有什么好处?我个人认为至少有如下两个:
第一,维护结构化+OO的程序设计风格。例如:
ExcelApplication1->set_DisplayAlerts(0,false);
ExcelApplication1->Quit();
又如:
int SheetCount=ExcelWorkbook1->Worksheets->Count;
这些代码都还是比较直观的。而且很具有OO的美感。
第二,强类型检查胜于弱类型检查。
如果使用OleGetProperty或者OleSetProperty函数,那么对传递给这两个函数的参数,是没有办法控制的。例如,我完全可以随心设置一个单元格的属性FOO为100。但是这样的函数调用在运行期一定会出错。而使用Server控件就不会有这个问题——编译期就不能通过。可以在编译期发现并纠正的错误,不要留到运行期去解决。这是我的主张。
二、必要的辅助手段
使用BCB编写控制Excel的程序,是很繁琐的。因为有时编译通过后会出现难以琢磨的异常!又有一种很无助的感觉:BCB在这上面的帮助简直是BS。CodeInsight的速度又是相当的慢,无法忍受!
不过,我们不要轻易放弃。根据我的经验,在BCB下要想好好掌握Excel编程,必须掌握三个获得帮助的途径。这三个获得帮助的途径就是(以我个人喜好的程度降序排列):
Excel本身的宏命令。我现在的习惯是,如果我要在BCB中实现一个功能,但是却不知道相应的方法和参数,我就会打开Excel,在随便一个Sheet中用宏记录下我要进行的操作,然后研究宏代码。这样做,肯定能获得正确的方法名,但是对参数的数量和类型却不敢保证:这是因为VBA可以选择不PASS一些缺省参数。根据我的个人经验,这样做的有效性应该在80%左右,简易性在100%。
Office自带的VBA帮助。这个帮助在Office安装过程中一般不是缺省安装的。对于Office 2003中文版的用户,应该检查C:Program FilesMicrosoft OfficeOFFICE112052下是否有类似VBAxxnn.chm文件。其中xx应该是如下字符串之一:AC(Access),GR(Microsoft Graphics),OF(Office),OL(Outlook),OWS(Office Web Service),PB(Publisher),PP(PowerPoint),WD(Word),XL(Excel)。而nn是对应的Office应用的版本号。如果没有这些文件,请运行Office安装程序,在自定义安装中将这些文件安装即可。这些文件,对于我们理解Office应用中的DOM模型是非常有用的。其有效性为85%,简易性约为80%。
BCB6中关于Server的头文件。这些头文件的位置应该是在:C:Program FilesBorlandCBuilder6IncludeVcl下。我这里只截一张图。从这张图中,我们也可以看到,BCB6对Office的支持只到Office 2000。Delphi 7可以支持到Office XP。不过它们都能很好的在Office 2003下工作。自Delphi 8之后,我们应该用Interop来操作Office了。这个方法的有效性是100%——因为你一定可以在里面找到BCB支持的方法,但是简易性只有10%——因为这些头文件都相当的大:excel_2k.h的大小是8M+!
这三个手段应该互相帮衬,才能在更短的时间内找到正确的方法。而且通过这三个途径找到的方法、参数应该是互相对应、互相一致的。例如,移动一个Sheet到另一个Sheet的Move方法,其在上述三个帮助途径中的定义分别如下:
在Excel的VBA宏里:Sheets("Sheet2").Move After:=Sheets(3)
Excel的VBA帮助:见下图
BCB的Excel头文件:
// *********************************************************************//
// Interface: IWorksheets
// Flags: (4112) Hidden Dispatchable
// GUID: {000208B1-0001-0000-C000-000000000046}
// *********************************************************************//
interface IWorksheets : public IDispatch
{
public:
... ...
HRESULT STDMETHODCALLTYPE Copy(VARIANT Before/*[in,opt]*/= TNoParam(),
VARIANT After/*[in,opt]*/= TNoParam(),
... ...
HRESULT STDMETHODCALLTYPE Move(VARIANT Before/*[in,opt]*/= TNoParam(),
VARIANT After/*[in,opt]*/= TNoParam(),
long lcid/*[in]*/= TDefLCID()); // [637]
... ...
三、编程指南
(一) ExcelApplication的启动、退出
我们平时所说的启动/退出Excel,在BCB6中应该被确切的说成是ExcelApplication的启动/退出。对应的控件是。用来启动、退出的代码分别如下:
void __fastcall TMainForm::EABtnClick(TObject *Sender)
{
try
{
EA->Connect();
}
catch(...)
{
ShowMessage("Can not launch server");
}
EA->set_Caption((WideString)"Excel Server Invoked by BCB");
EA->set_Visible(0,true);
}
__fastcall TMainForm::~TMainForm()
{
EA->set_DisplayAlerts(0,false);
EA->Quit();
}
注意,Office控件多以接口形式互相关联,所以连接的方法也被贯之以Connect的名称。由于Excel Application一定是顶层对象,所以它的Connect方法不需要指定参数。
这样生成的Excel Application只是一个空架子。我们要增加Workbook(工作簿)。
(二) ExcelWorkbook的创建和相关操作
如果我们把ExcelWorkbook简单的理解为等价于一个xls文件,应该不会差别太大,而且应该对我们有帮助。它的控件图标是。我们来看如何创建Workbook,代码如下:
void __fastcall TMainForm::EWBBtnClick(TObject *Sender)
// 本文转自 C++Builder研究 - http://www.ccrun.com/article.asp?i=1044&d=xr3888
{
EWB->ConnectTo(EA->Workbooks->Add(TNP, 0));
... ...
}
首先,我们要连接到一个Workbook的接口上去,这里我们用的是新增一个Workbook的方式。注意TNP参数,我们会在很多场合使用它。它是我自己程序中定义的一个宏:
#define TNP TNoParam()
而第二个参数0,则又是Locale ID(简称LID)。
ExcelWorkbook还可以用来连接——或者说“打开”更恰当——一个现有的Workbook(一个xls文件),具体代码如下:
EWB->ConnectTo(EA->Workbooks->Open((WideString)"c: emp est.xls",
TNP, TNP, TNP, TNP,
TNP, TNP, TNP, TNP,
TNP, TNP, TNP, TNP, 0));
上述代码中打开了位于c:temp下的test.xls文件。这个方法有很多参数,一般我都会传递TNP给它。具体参数的含义,可以参考相关文档。
(三) ExcelWorksheet的操作
在上文连接Workbook的代码中,我也同时连接了TExcelWorksheet,其控件图标是。所以完整的代码段如下:
void __fastcall TMainForm::EWBBtnClick(TObject *Sender)
{
EWB->ConnectTo(EA->Workbooks->Add(TNP, 0));
// Connect to worksheet as well
EWS1->ConnectTo(EWB->Worksheets->get_Item(V("Sheet1")));
EWS2->ConnectTo(EWB->Worksheets->get_Item(V(2)));
EWS3P=EWB->Worksheets->get_Item(V("Sheet3"));
EWS3->ConnectTo(EWS3P);
EWS3->Activate();
}
我们知道,缺省情况下,一个空白的Excel Workbook有三个空白的Worksheet,所以上文中我用三个ExcelWorksheet控件来连接这三个Worksheet。
我们既可以用表的名字(如“Sheet1”),也可以用表的序号(如“2”)来作为一个表的索引号。请注意V方法,它也是我定义的一个宏:
#define V TVariant
所以,它只是一个用来构造TVariant参数的宏。它和上面的TNP宏都是蛮有用的定义。
下面是一些针对Excel Worksheet的操作,不再一一详细说明。
void __fastcall TMainForm::MoveSheetBtnClick(TObject *Sender)
{
EWS1->Move(TNP, V(EWB->Worksheets->get_Item(V("Sheet3"))), 0);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::RenameBtnClick(TObject *Sender)
{
String NewName;
InputQuery("Rename Excel Worksheet", "Input a new name", NewName);
EWS1->set_Name((WideString)NewName);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::CreateBtnClick(TObject *Sender)
{
EWS4->ConnectTo(EWB->Sheets->
Add(TNP,V(EWB->Worksheets->get_Item(V("Sheet3"))),V(1),V(xlWorksheet)));
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::DelSheetBtnClick(TObject *Sender)
{
EWS2->Delete(0);
}
我们已经操作到了Worksheet级别,但是在我们日常操作中,接触最多的是Range(范围)和Cell(单元格),在后文我们将继续深入讨论,并讨论如何连接数据库、如何画数据图,以及如何用TExcelQueryTable加速数据导入的方法。
四) 单元格的操作
单元格的操作实际上就是对范围(Range)的操作。一般的代码段如下:
RangePtr r;
r=EWS4->get_Range(V("a1"), V("a1"));
r->set_Value(V("Species No"));
所以,对单元格内容的操作实际就是先获得一个要操作的范围(Range),然后再set_Value的过程。
ExcelWorksheet->Cells->set_Item(Variant(row),Variant(col),Variant(value.c_bstr()));
先选中一个RANGE,再插入一行.
AnsiString s_row="A"+IntToStr(row);
RangePtr r=ExcelWorksheet1->get_Range(V(s_row),V(s_row))->Rows;
r->EntireRow->Insert(V(1));
和数据库连接并不很困难。将数据库中的数据写到Excel Worksheet中也不是很难,无非是遍历数据集,然后将对应字段的内容写到相应的单元格里去而已,所以这里我就不详细论述了。公式的输入也是如此。
实际操作的效果如下图:
这里需要说明两个地方。第一是字体和栏目宽度的调整。具体的代码如下:
r=EWS4->get_Range(V(CellRef), V(CellRef2));
r->Font->set_Name(V("Arial"));
r->get_Columns()->AutoFit();
r->set_WrapText(V(true));
其操作流程也是先选择一个范围,然后设置字体名称、栏目宽度、自动换行等。
第二是图形的显示。这需要多一些操作才可以:
void __fastcall TMainForm::FillDataBtnClick(TObject *Sender)
{
unsigned int DataHandle;
HPALETTE APalette;
unsigned short MyFormat;
Graphics::TBitmap *Bitmap = new Graphics::TBitmap();
... ...
while (!Table1->Eof)
{
... ...
CellRef="e"+IntToStr(Count);
r=EWS4->get_Range(V(CellRef), V(CellRef));
Bitmap->Assign(Table1Graphic);
Bitmap->SaveToClipboardFormat(MyFormat,DataHandle,APalette);
Clipboard()->SetAsHandle(MyFormat,DataHandle);
EWS4->Paste(V(LPDISPATCH(r)), TNP, 0);
... ...
}
... ...
delete Bitmap;
return;
}
这里采用了Bitmap->Assign,将图形字段的内容作为Bitmap的内容,然后将Bitmap以剪贴板的格式保存到DataHandle中去,并返回保存的格式到MyFormat。然后用Clipbboard的SetAsHandle方法,将DataHandle中的内容以MyFormat格式“拷贝”到剪贴板上。最后,再用Worksheet的Paste方法将剪贴板上的图片拷贝到r指定的地方。
注意,r只是指明了被拷贝内容左上角的位置。对于图片来说,它不会、也不应该自动适应到一个单元格中。所以,在上述例子中,E列的宽度无法自动适应图片的宽度,需要另外调节。
(五) 根据数据绘制图形
图形在Office Server控件中是独立的,图标为:。操作它的代码一般如下:
void __fastcall TMainForm::DrawBtnClick(TObject *Sender)
// 本文转自 C++Builder研究 - http://www.ccrun.com/article.asp?i=1045&d=6375g6
{
RangePtr r=EWS4->get_Range(V("b1"), V("c"+IntToStr(Count-1)));
EC->ConnectTo(EWB->Sheets->Add(TNP, TNP, V(1), V(xlChart),0));
EC->set_ChartType(xlColumnClustered);
EC->SetSourceData(r, V(xlColumns));
//Set Titles
EC->ChartTitle[0]->set_Caption((WideString)"Animal Data Report");
EC->set_Name((WideString)"Animal Data Chart");
EC->set_HasDataTable(true);
EC->set_HasLegend(0,false);
}
这里的ChartType可用的enum变量可以在帮助文件中获得。在这里,我碰到了难题。因为我不知道如何设置X/Y轴的标题。虽然我可以编写代码使编译通过,但是在运行时一定出错。我已经无计可施,也不想继续下功夫了——因为我想转换到Interop下去编程。
(六) 使用TExcelQueryTable
(四)中介绍的将数据库中的数据导入Excel Worksheet的方法是很慢的。第一,遍历数据集很花费时间;第二,频繁的对单元格进行操作也很花时间。所以,需要用一个改进了的控件来加快大量数据的导入。
这个控件就是TExcelQueryTable,图标是。其典型代码如下:
void __fastcall TMainForm::Button1Click(TObject *Sender)
{
String ConnStr;
RangePtr R;
String SQL;
EWS1->Activate();
ConnStr="ODBC;DSN=testdbf;UID='';PWD=''";
SQL="select * from animals";
R=EWS1->get_Range(V("a1"), V("a1"));
EQT->ConnectTo(EWS1->QueryTables->Add(V(ConnStr), R, V(SQL)));
EQT->Refresh();
}
使用ExcelQueryTable要至少三个参数。
ConnectionString。它用来连接到一个数据库。在上例中我采用的是ODBC方式。用OLE DB也是可以的。
SQL。它用来返回一个记录集。
Range。它指定了记录集从哪个单元格开始填充。从左到右各列将代表记录集的字段;从上到下就是各个记录。
用这个方式填充大量数据,可以大幅度的提升速度。
关于用BCB 6操作Excel的介绍就此打住。希望给大家一些帮助。
//--------------------------一个例子:再WinXP 和bcb6下测试通过
Excel.h//---------------------------------------------------------------------------
#ifndef ExcelH
#define ExcelH
#define TNP TNoParam()
#define V TVariant
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <DBGrids.hpp>
#include <Grids.hpp>
#include <OleServer.hpp>
#include "TTF160_OCX.h"
#include "Excel_2K_SRVR.h"
#include <ADODB.hpp>
#include <DB.hpp>
//---------------------------------------------------------------------------
class TMainExcel : public TDataModule
{
__published: // IDE-managed Components
TExcelApplication *ExcelApplication1;
TExcelWorkbook *ExcelWorkbook1;
TExcelWorksheet *ExcelWorksheet1;
TExcelQueryTable *ExcelQueryTable1;
TADOQuery *dataSet;
private: // User declarations
WideString cs_filename;
public: // User declarations
__fastcall TMainExcel(TComponent* Owner);
void WriteToExcelWorksheet(TExcelWorksheet * ExcelWorksheet,int row,int col,WideString value);
void addRow(TExcelWorksheet * ExcelWorksheet1,int row);
void deleteRow(TExcelWorksheet * ExcelWorksheet1,int row);
void ExcelFilePrint(WideString filename);
void DBGridToExcelPrint(TList * dsList,WideString xlsname);
void FormulaoneSaveToExcel(TF6Book * formulaone);
void FormulaSaveAsExcelAuto(TF6Book * formulaone);
void FormulaoneToExcelPrint(TF6Book * formulaone);
};
//---------------------------------------------------------------------------
extern PACKAGE TMainExcel *MainExcel;
//---------------------------------------------------------------------------
#endif
Excel.cpp
//--------------------------------------------------------------------------
//这三句必须添加。否则debug可以通过,release不能通过
#define NOWIN32_LEAN_AND_MEAN
#include <shlobj.hpp>
#include <vcl.h>
#pragma hdrstop
#include "Excel.h"
#include <utilcls.h>
#include "untdata.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "TTF160_OCX"
#pragma link "Excel_2K_SRVR"
#pragma resource "*.dfm"
TMainExcel *MainExcel;
//---------------------------------------------------------------------------
__fastcall TMainExcel::TMainExcel(TComponent* Owner)
: TDataModule(Owner)
{
cs_filename=WideString(ExtractFilePath(Application->ExeName)+"print.xls");
}
//--------------------------------------------------------------------------
void TMainExcel::WriteToExcelWorksheet(TExcelWorksheet * ExcelWorksheet,int row,int col,WideString value)
{
ExcelWorksheet->Cells->set_Item(Variant(row),Variant(col),Variant(value.c_bstr()));
}
//在row行上添加一行
void TMainExcel::addRow(TExcelWorksheet * ExcelWorksheet1,int row)
{
WideString s_row="A"+IntToStr(row);
RangePtr r=ExcelWorksheet1->get_Range(V(s_row),V(s_row))->Rows;
r->EntireRow->Insert(V(1));
}
void TMainExcel::deleteRow(TExcelWorksheet * ExcelWorksheet1,int row)
{
AnsiString s_row="A"+IntToStr(row);
RangePtr r=ExcelWorksheet1->get_Range(V(s_row),V(s_row))->Rows;
r->EntireRow->Delete(V(1));
}
//---------------------------------------------------------------------------
/*
*io TList dsList (TADOQuery 列表);
xlsname 不带路径不带扩展名
*/
void TMainExcel::DBGridToExcelPrint(TList * dsList,WideString xlsname)
{
TADOQuery * dataSet,*tableExcel;
int iRow,iCol,iLjhh,inc;
String strSQL,strFieldName,strTableName;
WideString xlspath=ExtractFilePath(Application->ExeName)+"TableData//"+xlsname+".xls";
if(!FileExists(xlspath))
{
ShowMessage("缺少xls模板文件!");
return;
}
if(dsList->Count<0)
{
ShowMessage("没有正确的数据集合!");
return;
}
//打开xls文件
OleVariant ItemIndex = 1;
try
{
this->ExcelApplication1->Connect();
}
catch(...)
{
MessageDlg("没有安装Excel!",mtError,TMsgDlgButtons()<<mbYes,0);
Abort();
}
ExcelApplication1->GetDefaultInterface()->set_Visible(0,true);
ExcelApplication1->set_Caption(StringToOleStr("打印申报表"));
ExcelApplication1->Workbooks->Open(StringToOleStr(xlspath),TNP, TNP, TNP, TNP,TNP, TNP, TNP, TNP,TNP, TNP, TNP, TNP,0);
ExcelWorkbook1->ConnectTo(ExcelApplication1->Workbooks->get_Item(ItemIndex));
ExcelWorksheet1->ConnectTo(ExcelWorkbook1->Worksheets->get_Item(ItemIndex));
//写入动态数据
dsList->First();
for(int i=0;i<dsList->Count;i++)
{
dataSet=(TADOQuery *)dsList->operator [](i);
dataSet->First();
inc=0;
tableExcel=new TADOQuery(this);
AnsiString sql="select * from TableExcel where Pname='"+AnsiString(xlsname)+"' and IsHJ=0 and TRead=Yes and Tableorder="+IntToStr(i+1)+" and TLJHH=0 order by prow,pcol";
MainData->PsqlResult(tableExcel,sql);
if(tableExcel->RecordCount<1) //if TLJHH=0 代表是存在记录列表,>0则代表特定号码的记录
{
tableExcel->First();
while(!dataSet->Eof)
{
sql="select * from TableExcel where Pname='"+AnsiString(xlsname)+"' and IsHJ=0 and TRead=Yes and Tableorder="+IntToStr(i+1)+" and TLJHH="+dataSet->FieldByName("LJHH")->AsString+" order by prow,pcol";
MainData->PsqlResult(tableExcel,sql);
//每条记录写入Excel
while(!tableExcel->Eof)
{
//表名和逻辑号码 没有用到
//strTableName=tableExcel->FieldByName("tname")->AsString;
strFieldName=tableExcel->FieldByName("TfieldName")->AsString;
//iLjhh =tableExcel->FieldByName("TLJHH")->AsInteger;
iRow =tableExcel->FieldByName("PRow")->AsInteger;
iCol =tableExcel->FieldByName("PCol")->AsInteger;
WriteToExcelWorksheet(ExcelWorksheet1,Variant(iRow+inc),Variant(iCol+inc),Variant(dataSet->FieldByName(strFieldName)->AsString.c_str()));
//设置Excel单元格数据
//获得累计数---Excel 文件自动计算
tableExcel->Next();
}
//Excel 增加一行
inc++;
addRow(ExcelWorksheet1,iRow+inc);
dataSet->Next();
}
}
else
{
tableExcel->First();
while(!dataSet->Eof)
{
//每条记录写入Excel
while(!tableExcel->Eof)
{
//表名和逻辑号码 没有用到
//strTableName=tableExcel->FieldByName("tname")->AsString;
strFieldName=tableExcel->FieldByName("TfieldName")->AsString;
//iLjhh =tableExcel->FieldByName("TLJHH")->AsInteger;
iRow =tableExcel->FieldByName("PRow")->AsInteger;
iCol =tableExcel->FieldByName("PCol")->AsInteger;
WriteToExcelWorksheet(ExcelWorksheet1,Variant(iRow+inc),Variant(iCol+inc),Variant(dataSet->FieldByName(strFieldName)->AsString.c_str()));
//设置Excel单元格数据
//获得累计数---Excel 文件自动计算
tableExcel->Next();
}
//Excel 增加一行
inc++;
addRow(ExcelWorksheet1,iRow+inc);
dataSet->Next();
}
}
//删除最后多加的一行
deleteRow(ExcelWorksheet1,iRow+inc);
}
ExcelWorksheet1->PrintPreview(TNP,0);
ExcelApplication1->set_DisplayAlerts(0,false);
ExcelApplication1->Quit();
delete tableExcel;
}
void TMainExcel::ExcelFilePrint(WideString filename)
{
OleVariant ItemIndex = 1;
try
{
this->ExcelApplication1->Connect();
}
catch(...)
{
MessageDlg("没有安装Excel!",mtError,TMsgDlgButtons()<<mbYes,0);
Abort();
}
ExcelApplication1->GetDefaultInterface()->set_Visible(0,true);
ExcelApplication1->set_Caption(StringToOleStr("打印申报表"));
ExcelApplication1->Workbooks->Open(StringToOleStr(filename),
TNP, TNP, TNP, TNP,TNP, TNP, TNP, TNP,TNP, TNP, TNP, TNP,0);
ExcelWorkbook1->ConnectTo(ExcelApplication1->Workbooks->get_Item(ItemIndex));
ExcelWorksheet1->ConnectTo(ExcelWorkbook1->Worksheets->get_Item(ItemIndex));
ExcelWorksheet1->PrintPreview(TNP,0);
ExcelApplication1->set_DisplayAlerts(0,false);
ExcelApplication1->Quit();
}
//------------------------------------------------------------------
void TMainExcel::FormulaoneSaveToExcel(TF6Book * formulaone)
{
WideString ls_buf="";
WideString tishi="请选择文件夹及输入文件名";
short li_mode=11; //文件类型Excel97=11
try{
formulaone->SaveFileDlg(tishi.c_bstr(),&ls_buf,&li_mode);
}
catch(...){
}
//如果 点击取消那么 保存文件名为空,保存模式为18
if (!ls_buf.IsEmpty() && li_mode!=18 ){
try{
formulaone->WriteEx(ls_buf,li_mode);
ShowMessage("保存成功。/n表格被保存在"+ls_buf+"中。");
}
catch(Exception &e){
ShowMessage("保存失败。"+e.Message);
}
}
}
//自动保存到当前目录下的print.xls 2009-03-17 Yanfl 返回自动保存的文件名
void TMainExcel::FormulaSaveAsExcelAuto(TF6Book * formulaone)
{
WideString tishi="请选择文件夹及输入文件名";
short li_mode=11; //文件类型Excel97=11
if(FileExists(cs_filename))
{
DeleteFile(cs_filename.c_bstr());
}
//如果 点击取消那么 保存文件名为空,保存模式为18
if (!cs_filename.IsEmpty() && li_mode!=18 ){
try{
formulaone->WriteEx(cs_filename,li_mode);
}
catch(Exception &e){
ShowMessage("临时文件生成失败。"+e.Message);
}
}
}
//--------------------------------------------------------------
void TMainExcel::FormulaoneToExcelPrint(TF6Book * formulaone)
{
//保存为临时xls文件
this->FormulaSaveAsExcelAuto(formulaone);
//打印临时文件
if(FileExists(cs_filename))
{
this->ExcelFilePrint(cs_filename);
}
else
ShowMessage("打印临时文件成成失败");
}
- C++Builder 6中开发 Office 程序心得
- C++Builder中开发Activex
- C++Builder中开发Activex
- 在Borland C++Builder中检查程序
- C++Builder中开发Activex 分享
- 第二次使用C++BUILDER开发SOCKET通讯程序,工作完成
- 在C++Builder中编写发送图象文件程序(一)
- 在C++Builder中编写发送图象文件程序(二)
- 在C++Builder中编写发送图象文件程序(三)
- 在C++Builder中编写发送图象文件程序(四)
- Html5 Builder 开发Android程序过程中遇到问题解决
- 转载:c++builder多线程程序
- Delphi和Office程序开发
- Delphi和Office程序开发
- Delphi和Office程序开发
- 编辑C程序心得
- 程序开发心得
- 最近 程序开发心得
- Five Ways to Enhance BI Functionality Using ABAP--Part III--BEx Variables
- Data Structures
- PowerDesigner教程系列(三)概念数据模型
- PowerDesigner教程系列(四)概念数据模型
- PowerDesigner教程系列(五)概念数据模型
- C++Builder 6中开发 Office 程序心得
- PowerDesigner教程系列(六)概念数据模型
- 深入理解 ASP.NET 动态控件 (Part 2 - 编译过程)
- smart可能会误报
- 限制文本框只能输数字(最多两位小数)
- 一个firefox的插件: CoolPreviews
- Linux 中的计时——gettimeofday函数
- 目前互联网安全领域的问题(2008)
- Visual Studio 2008下载地址汇总及相关注册破解方法.