VC操作Word书签模板

VC操作Word书签模板

一 制作模板
 

1 新建一个文档,设置文档内容。对于循环的部分,建议放到表格内,这样容易定位、选择、复制、粘贴。
2 将鼠标定位到要插入书签的位置,从菜单上,“插入”->“书签,弹出对话框,输入书签名,点击“添加”按钮。


插入以下书签:order_num,报告日期_,报表模板__,name,age,结论__
 其中,报表模板__,用于定位模板表格。可有可无,没有时,默认表格1。


完成后,所有书签的名称和位置如下图所示:


3 保存模板,比如“word书签模板.doc”

 


二 添加word 类型库

通过类向导MFC ClassWizard,从菜单上 View->ClassWizade,打开ClassWizade窗口,选择Add Class->From a type library...,,浏览找到office 2003安装路径下的msword.olb文件,例如:"C:\Program Files\Microsoft Office\OFFICE11\MSWORD.OLB",然后选择以下类,这些类形成文件 msword.h和msword.cpp,并自动加入到工程中。




其中必须的:
_Application,Documents,_Document ,Selection,
我这里用到的:
 Tables,Table,Rows,Row,Columns,Column,Cells,Cell,Bookmarks,Bookmark,Range,
也可能用到的:
 Borders,Border,Window,_ParagraphFormat,Shapes,
 PageSetup,Shape,InlineShapes,InlineShape,Fields,View,_Font

 


三 为了操作简单,基于msword.h 和  msword.cpp,再次封装成一个类对应wordWork.h 和 wordWork.cpp,并加入到工程中。其内容附加到后面附录部分。
另外,由于多个类都有table,row等等,为了区别起见,将类放到命名空间内加以区别,方法如下:

1 头文件msword.h中将类放到命名空间内
namespace ms_word//将整体放入命名空间
{
 class _Application : public COleDispatchDriver
 {
 public:
 _Application() {}  // Calls COleDispatchDriver default constructor
 .....
 };
 class Tables : public COleDispatchDriver
 {
 public:
 Tables() {}  // Calls COleDispatchDriver default constructor
 .....
 };

 ......
}//end namespace ms_word


2 实现文件msword.cpp内引入命名空间
#include "stdafx.h"
#include "msword.h"

using namespace ms_word;//引入命名空间

LPDISPATCH _Application::GetApplication()
{
 LPDISPATCH result;
 InvokeHelper(0x3e8, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, NULL);
 return result;
}
long Tables::GetCount()
{
 long result;
 InvokeHelper(0x2, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL);
 return result;
}
........

3 在使用的文件内加入头文件,
#include "msword.h"
然后类名加入限定空间,即可加以区别
 ms_word::Tables m_tables;


四 编译执行
  在你的函数内调用这些方法,编译运行结果如下:

 

 

五 主要的代码如下

1 添加word 类型库 MSWORD.OLB
  
2 在使用的程序中,加入以下语句
#include "wordWork.h"


 char ch[90];
#define say(x) AfxMessageBox(x)

===== 1 主操作代码 ===

void wordTxtBookmark(WordMain wdMain,CString BookMark="",CString strText="",bool addLine=false,bool mulitLine=false)
{
 if ( BookMark !="" )
 {
     if (  !wdMain.GotoBookmark(BookMark) )  return;//没有书签就返回,不要写
 }
 if ( strText !="" )
 {
  if (mulitLine)
  {//前提条件是,本书签上面有一空行,否则影响上面行的位置
        //正序,插入多行
   wdMain.MoveUp(1);//移到书签的上面一行
   wdMain.EndKey();//移动到上面行的行尾
   wdMain.Enter(1);//打回车,加一行,相当于把书签下移一行,以便后面查到书签时已经下移
  }
  wdMain.GetRange(0, 0, 0, 0);//不要选择任何字符,否则会吃掉第一个字符
  wdMain.SetTextHAlignment(WD_LEFT);
  //wdMain.SetTableBorderLine(WD_EDGE_BOTTOM, WD_LINESTYLE_SINGLE);
  wdMain.SetItemText( strText);

 }
 if (addLine)
 {//增加一个空白行,以便与上行分开
  wdMain.Enter(1);
 }
}

 

void wordPicText(WordMain wdMain,CString strPic,CString strText,bool addLine=true)
{

//picture
 BOOL bInsertPic;
 wdMain.Enter(1);//加一行
 wdMain.MoveUp(1);//再次回到上面,相当于插入一行,否则会向后错,吃掉后面的行

 wdMain.SetTextHAlignment(WD_CENTER);//居中
 {
  bInsertPic = wdMain.InsertPicture(strPic, 404.5, 248.3); 
 }

//text
 if (strText!="")
 {
  wdMain.Enter(1);
  wdMain.GetRange(0, 0, 0, 0);//不要选择任何字符,否则会吃掉第一个字符
  wdMain.SetTextHAlignment(WD_CENTER);//居中
  wdMain.SetItemText( strText);
 
//add line 
 if (addLine)
 {//增加一个空白行,以便与上行分开
  wdMain.Enter(1);
  wdMain.Enter(1);///与后面的隔开

 }


}

void SetNameCellValue(CString list[])
{
 COleVariant covTrue((short)TRUE), covFalse((short)FALSE), covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
 
 WordMain wdMain;CString strGet;
 MyFont wdFont;
 MyPageSet wdPageSet;
 CString doc_template_name,t_mc,docname;

 docname.Format("c:\\i22i.doc");
 doc_template_name.Format("c:\\ii.doc" );

 wdMain.Open(doc_template_name);
 wdMain.PageSet(wdPageSet);

 
 // xlMain.SetVisible(TRUE);
 
 //(一) 这里设置表头的项目,比如报表日期
 //特别注意:为了容易起见,命名单元格的规则如下
 //1.1 开头处的命名单元格,以1个下划线结束,比如,报告日期_
 //1.2 中间循环命名单元格,就是正常的,与数据集的字段名一致为好
 //1.3 结尾处的命名单元格,以2个下划线结束,比如,合计__
 TRY
 {
  wordTxtBookmark( wdMain,"报告日期_","当天");

 }
 CATCH_ALL(e)
 {
  
 }
 END_CATCH_ALL
  
  
 VARIANT vtIndex;
 Bookmarks bookmarks;
 Bookmark bookmark;
// Range name_range;
 Rows wdRows;
 Row wdRow;
 Columns wdCols;
 Cells wdCells;
 Cell wdCell;
 Tables tables;
 Table operTable;
 Range tmpRange;
 Range srcRange,desRange;
 Columns columns;
 //(二) 根据数据源的个数,设置重复变化的数据行组,
 //1 声明与命名单元格相关的变量和数组
 int min_Row = 65536, min_Col = 65536, max_Row = 0;
 CString min_Name = "";
 bookmarks=wdMain.m_document.GetBookmarks();
 int nameCellCount = bookmarks.GetCount();//wdMain.m_document.GetBookmarks();//获得命名单元格的总数
 int* nameCellRow = new int[nameCellCount];//名称单元格所在的行
 int* nameCellColumn = new int[nameCellCount];//名称单元格所在的列
 CString * nameCellTag = new CString[nameCellCount];//某个命名单元格的常规地址 ,比如 $A$1 --> A
 CString * nameCellName = new CString[nameCellCount];//某个命名单元格的自定义名称,比如 工资
 
 CString strName ,str;
 int nameCellIdx = 0;
 
 //读取不为空的区域的信息,包括已经使用的行数、列数、起始行从1开始、起始列从1开始
 long lStartRow,  lStartCol,  lRowNum,  lColNum;
 
 int hang_shu = lRowNum;
 int lie_shu =lColNum;
 int used_first_col = lStartCol;
 int used_last_col = lStartCol+lColNum-1;

 int row1,col1,row2;

 //2 寻找所有命名的单元格,并找到行号最小者,以便在它之前插入循环行
 for (int k = 0; k < nameCellCount; k++)
 {
  vtIndex.vt = VT_I4;
  vtIndex.lVal = k+1;// 注意,从1开始,不是0
  bookmark=bookmarks.Item(&vtIndex);

  strName=bookmark.GetName();//.GetNameLocal();
  str = strName.Mid(strName.GetLength()-1,1);
  if (str == "_")
  {
   continue;//如果第一个字符为下划线,则认为是固定命名单元格,不是命名的循环单元格
  }
  TRY
  {
   
   tmpRange=bookmark.GetRange();
   //获得书签在表格中的行列号
   wdCells=tmpRange.GetCells();
   wdCell=wdCells.Item(1);
   
   row1=wdCell.GetRowIndex();
   col1=wdCell.GetColumnIndex();
   
   
   nameCellColumn[nameCellIdx] = col1;
   nameCellRow[nameCellIdx] = row1;
   nameCellName[nameCellIdx] = strName;
   
   nameCellTag[nameCellIdx] = " ";//strName.Mid(1,strName.Find("$",1)-1);//$A$1--> A
    
   if (min_Row > nameCellRow[nameCellIdx])
   {
    min_Row = nameCellRow[nameCellIdx];//最小的行号
    min_Col = nameCellColumn[nameCellIdx];
    min_Name = nameCellName[nameCellIdx];
    
   }
   if (max_Row < nameCellRow[nameCellIdx]) max_Row = nameCellRow[nameCellIdx]; ;//最大行号
   nameCellIdx++;//真实的循环的命名单元格序号
  }
  CATCH_ALL(e)
  {
   
  }
  END_CATCH_ALL
  
 }
 nameCellCount = nameCellIdx;//实际要处理的循环的命名单元格数目
 int loopRow = max_Row - min_Row + 1;//一次循环的函数

 //3 也可以使用 //foreach ( Word.Bookmark bk in wh.wordDoc.Bookmarks)MessageBox.Show(bk.Name);

 // 方法1 通常使用第一个表来作为模板表
 tables = wdMain.m_document.GetTables();
 operTable =tables.Item(1);
 // 方法2 使用一个书签来标志模板表
 TRY
 {//使用一个特殊的标签 table_bookmark_template_
   wdMain.GotoBookmark("报表模板__");//
   tables=wdMain.m_selection.GetTables();
  operTable = tables.Item(1);//tmpRange.Tables[1];//得到该书签所在的表,以它为报表的循环模板
 }
 CATCH_ALL(e)
 {
  
 }
 END_CATCH_ALL
 
 
 int template_start = 0;
 int template_end = 0;
 columns=operTable.GetColumns();
 int table_columns=columns.GetCount();//Columns.Count;//本表格的烈数
 
 wdCell= operTable.Cell(min_Row, 1);
 tmpRange = wdCell.GetRange();
 template_start =tmpRange.GetStart();//循环行组的第一行,第一个单元格,到文档开头的距离

 
 wdCell= operTable.Cell(max_Row+1, 1);//Cell(max_Row, table_columns) 将少一列
 tmpRange = wdCell.GetRange();
 template_end =tmpRange.GetStart();//循环行组的最后行,最后一个单元格,到文档开头的距离

 wdCell= operTable.Cell(1, 1);
 tmpRange = wdCell.GetRange();
 srcRange = tmpRange;//operTable.Cell(1, 1).Range;
 desRange = tmpRange;//operTable.Cell(1, 1).Range;


 
 
 //3 也可以使用 foreach ( Excel.Name  nn in xlMain.m_application.ActiveWorkbook.Names)MessageBox.Show(nn.Name);
 int iidex=0;

 //4 根据数据集的实际数据行数,查找命名单元格,循环插入数据
 int cur_row = min_Row;
 for (int dt_row_idx = 0; dt_row_idx < 2; dt_row_idx++)//dt.Rows.Count
 {//循环实际的数据行数2,修改为实际的变量
  
  //4.1 找到行号最小的命名单元格,以它来定位
  wdMain.GotoBookmark(min_Name);//
  tmpRange=wdMain.m_selection.GetRange();

  //4.2 //插入行重复摸板行组的行,使得所有命名单元格都向后移,以便下次循环查找定位时使用
  for (int j = 0; j < loopRow; j++)
  {//插入需要重复循环的摸板行数loopRow
   // range2=name_range.GetEntireRow();
   // range2.Insert(COleVariant((long) 1),COleVariant((long) true));
  }
  //4.3 定位到摸板行组
  wdMain.GotoBookmark(min_Name);//
  tmpRange=wdMain.m_selection.GetRange();

  wdCells=tmpRange.GetCells();
  wdCell=wdCells.Item(1);
  row1=wdCell.GetRowIndex();
  //col1=cell.GetColumnIndex();
  row2 = row1 + loopRow - 1; //摸板行组的最后一行

  wdCell= operTable.Cell(row1, 1);
  tmpRange = wdCell.GetRange();
  template_start =tmpRange.GetStart();//循环行组的第一行,第一个单元格,到文档开头的距离

  //template_end = operTable.Cell(row2, table_columns).Range.Start;//这样少一列
  wdCell= operTable.Cell(row2+1, 1);//到下一行,第一个单元格
  tmpRange = wdCell.GetRange();
  template_end =tmpRange.GetStart();//循环行组的第一行,第一个单元格,到文档开头的距离


  //4.4 复制模板整体多行组,固定的摸板的格式和相关的文字说明,也可一个一个单元格复制
  srcRange.SetRange(template_start, template_end);//整体多行组复制摸板行组
  srcRange.Copy();

  
  //4.5 定位到新加入行的第一个单元格内
  //row1 = row1 - loopRow;//向上回退到新加入的行组
  //row2 = row2 - loopRow;
  
  //4.6 粘贴整体多行组,固定的摸板的格式和相关的文字说明
  //desRange = operTable.Rows[row1].Range;//整体多行组粘贴摸板行组
  wdRows = operTable.GetRows();//.Rows[row1].Range;//整体多行组粘贴摸板行组
  wdRow=wdRows.Item(row1);
  desRange = wdRow.GetRange();
  desRange.Paste();


  //方法2  一行一行复制粘贴,代码很少
 // for (int j = 0; j < loopRow; j++)
 // {//插入需要重复循环的行数loopRow的空行,一行一行的复制粘贴
 //  //复制模板行
 //  wdRows = operTable.GetRows();
 //  wdRow=wdRows.Item(cur_row + j + j);// 所以,粘贴几行,就要多加几行,j+j
 //  srcRange = wdRow.GetRange();
 //  srcRange.Copy();

 //  //粘贴到目标行
 //  wdRows = operTable.GetRows();
 //  wdRow=wdRows.Item(cur_row + j );//因为,新粘贴的行 在原来模板行的上面
 //  desRange = wdRow.GetRange();
 //  desRange.Paste();
 // }
 // cur_row += loopRow;

 

  //4.7 动态的数值加入
  for (int name_cell_idx = 0; name_cell_idx < nameCellCount; name_cell_idx++)
  {//循环命名单元格数量
   str.Format("%s%d", nameCellTag[name_cell_idx], nameCellRow[name_cell_idx]);//为了简单这里用其列名和行号作为内容
   if (nameCellName[name_cell_idx] == "order_num")
   {
    str.Format("%d", dt_row_idx + 1);
   }
   else
   {
    str=list[ iidex++];
    //str= dt.Rows[dt_row_idx][nameCellName[name_cell_idx]].ToString();
    //这里的数据,来自实际的数据集??????????????????????????????
    
   }
   wdCell=operTable.Cell(nameCellRow[name_cell_idx], nameCellColumn[name_cell_idx]);//.Range.Text = str;
   tmpRange=wdCell.GetRange();
   tmpRange.SetText(str);
  
   nameCellRow[name_cell_idx] += loopRow;//行号增加重复行的个数loopRow,准备下个循环,定位行使用
  }
  
 }

 // 5 删除重复的摸板行,不再需要

 wdMain.GotoBookmark(min_Name);//
 tmpRange=wdMain.m_selection.GetRange();
 
 wdCells=tmpRange.GetCells();
 wdCell=wdCells.Item(1);
 row1=wdCell.GetRowIndex();
 row2 = row1 + loopRow - 1; //摸板行组的最后一行
 //方法1  一行一行地删除
// for (int dd = 0; dd < loopRow; dd++)
// {
//  wdMain.DeleteRows();
// }
 
 //方法2  多行组一次删除
 wdCell= operTable.Cell(row1, 1);
 tmpRange = wdCell.GetRange();
 template_start =tmpRange.GetStart();//循环行组的第一行,第一个单元格,到文档开头的距离
 //template_end = operTable.Cell(row2, table_columns).Range.Start;//这样少一列
 wdCell= operTable.Cell(row2+1, 1);//到下一行,第一个单元格
 tmpRange = wdCell.GetRange();
 template_end =tmpRange.GetStart();//循环行组的第一行,第一个单元格,到文档开头的距离
 
 srcRange.SetRange(template_start, template_end);//整体多行组复制摸板行组
 srcRange.Cut();
 //(三) 清除剪贴板,避免Excel关闭工作簿的时候出现提示
 //1 删除剪切板的内容,防止退出提示
 //xlMain.m_application.CutCopyMode = flase;//Excel.XlCutCopyMode.xlCut;//删除剪切板的内容,防止退出提示
 //2 直接用range("A1").copy就行,不必把剪贴板都清空,这会影响其他进程的工作的
 wdCell= operTable.Cell(1, 1);
 tmpRange = wdCell.GetRange();
 tmpRange.Copy();

 
 //(四) 结尾方面的工作
 TRY
 {
  wordTxtBookmark( wdMain,"结论__","成功完成");
 }
 CATCH_ALL(e)
 {
  
 }
 END_CATCH_ALL
  
  
  
 //(五) 另存文件
  
// CString  docname;
// docname.Format("%s\\%s.xls","c:","12");//g_save_path,end_serial);  //路径
 wdMain.SaveAs(docname);

 //(六) 释放
 bookmarks.ReleaseDispatch();
 bookmark.ReleaseDispatch();
 wdRows.ReleaseDispatch();
 wdRow.ReleaseDispatch();
 wdCols.ReleaseDispatch();
 wdCells.ReleaseDispatch();
 wdCell.ReleaseDispatch();
 tables.ReleaseDispatch();
 operTable.ReleaseDispatch();
 tmpRange.ReleaseDispatch();
 srcRange.ReleaseDispatch();
 desRange.ReleaseDispatch();
 columns.ReleaseDispatch();
 
 delete []nameCellRow;
 delete []nameCellColumn;
 delete []nameCellTag;
 delete []nameCellName;

 wdMain.Close();
// say("ok");
 return;
 }

 

====2==封装的word操作类=====

1

//文件名称:wordWork.h

#include
#include "msword.h"

//字体颜色
#define WD_BLACK       0
#define WD_RED         255
#define WD_BLUE        16711680

//对齐方式
#define WD_LEFT        0
#define WD_CENTER      1
#define WD_RIGHT       2
#define WD_JUSTIFY     3
#define WD_DISTRIBUTE  4

//边框方位
#define WD_DIAGONAL_DOWN         -7
#define WD_DIAGONAL_UP           -8
#define WD_EDGE_BOTTOM              -3
#define WD_EDGE_LEFT             -2
#define WD_EDGE_RIGHT            -4
#define WD_EDGE_TOP              -1
#define WD_INSIDE_HORIZONTAL     -5
#define WD_INSIDE_VERTICAL       -6

//边框类型
#define WD_LINESTYLE_NONE        0
#define WD_LINESTYLE_SINGLE      1
#define WD_LINESTYLE_DOT         2

typedef struct MyFont
{
 //字体名
 CString strName;
 //字体大小
 float fSize;
 //字体颜色
 long lColor;
 //粗体
 BOOL bBold;
 //斜体
 BOOL bItalic;
 //下划线
 BOOL bUnderLine;
    MyFont(){fSize = 12; lColor = WD_BLACK; bBold = FALSE; bItalic = FALSE; bUnderLine = FALSE;}
}MYFONT;


typedef struct MyPageSet
{
 //页边距,单位为厘米
 //上
 float fTopMargin;
 //下
 float fBottomMargin;
 //左
 float fLeftMargin;
 //右
 float fRightMargin;
 MyPageSet() {fTopMargin = fBottomMargin = (float)67.0; fLeftMargin = fRightMargin = (float)57.0;}
}MYPAGESET;


class WordMain
{
private:
public:
 _Application m_application;
 Documents m_documents;
 _Document m_document;
 Window m_window;
 View m_view;
 Selection m_selection;
 Tables m_tables;

public:
 //构造函数
 WordMain();
    //析构函数
 ~WordMain();
 //关闭Word应用程序
 void Close();
 //保存
 void Save();
 //另存为strFileName包含路径名和文件名
 BOOL SaveAs(const CString &strFileName);
 //打开
 BOOL Open();
 //打开已存在fileName.doc或.dot文件
 BOOL Open(const CString &strFileName);
 //根据模板Template.dot新建
 BOOL New(const CString &strTemplateName);
 //插入表格
 void InsertTable(long lRow, long lCol);
 //选择区域(光标上下左右移动数量)
 void GetRange(long lLeft = 0, long lUp = 0, long lRight = 0, long lDown = 0);
 //全选
 void GetWholeStory();
 //文件路径strFn是否存在,bDir判定是否包含文件名
 BOOL IsFileExist(const CString &strFn, BOOL bDir);
 //向行首移动光标
 void HomeKey();
 //向行尾移动光标
 void EndKey();
 //向上移动光标
 void MoveUp(long lCount);
 //向下移动光标
 void MoveLeft(long lCount);
 //向左移动光标
 void MoveRight(long lCount);
 //向右移动光标
 void MoveDown(long lCount);
 //BackSpace键删除选中区域,lCount为退格数
 void BackSpace(long lCount);
 //Delete键删除选中区域
 void Delete();
 //输入文本
 void SetItemText(const CString &strText);
 //Enter键:lCount=回车次数
 void Enter(long lCount);
 //字体设置
 void SetFont(const MYFONT &myFont);
 //文本对齐方式(即水平对齐方式)
 void SetTextHAlignment(long lHAlignment);
 //表格内文本垂直对齐方式
 void SetCellVAlignment(long lVAlignment);
 //插入书签
 void InsertBookmark(const CString &strName);
 //书签定位
 BOOL GotoBookmark(const CString &strBookmarkName);
 //得到所有的书签名集合
 void nameBookmarks(WordMain wdMain,CString &strSet);
 //插入图片
 BOOL InsertPicture(const CString &strFileName, float lWidth, float lHeight);
 //对文档中的第lIndex个表格写数据,表格的行列为lRow、lCol
 void SetTableText(long lIndex, long lRow, long lCol, const CString &strText);
 //获得文档中表格数量
 long GetTablesCount();
 //选中文档中指定的第lIndex个表格
 void SelectTable(long lIndex);
 //获得光标所在当前页码
 long GetPage();
 //指定的第lIndex个表格设置标题行换页后重复显示
 void SetHeadingFormat(long lIndex, BOOL bShow = FALSE);
 //获得表格列数,第lIndex个表格
 long GetTableColumCount(long lIndex);
 //获得表格行数,第lIndex个表格
 long GetTableRowCount(long lIndex);
    void SetTableColumnWidth(float fNewValue);
 void SetTableBorderLine(long lPosition, long lBorderLineStyle = WD_LINESTYLE_SINGLE);
 void PageSet(const MYPAGESET &pageSet);  
 void SetTableHAlignment(long lVAlignment);
 void UpdateDomain();
 void Copy();
 void Cut();
 void Paste();
 void DeleteRows();
 void InsertRowsAbove(long lRowCount);
 BOOL ShowBookMark(BOOL bShow = FALSE);
};

2

//文件名称:wordWork.cpp

#include "stdafx.h"
#include "wordWork.h"
#include


WordMain::WordMain()
{

}

WordMain::~WordMain()
{

}

void WordMain::Close()
{
    m_selection.ReleaseDispatch();
 m_window.ReleaseDispatch();
 m_document.ReleaseDispatch();
 m_documents.ReleaseDispatch();
 m_document.Close(&vtMissing, &vtMissing, &vtMissing);
 m_application.Quit(&vtMissing, &vtMissing, &vtMissing);
 m_application.ReleaseDispatch();
// CoUninitialize();
}

BOOL WordMain::Open()
{
 if (FAILED(CoInitialize(NULL)))
 {
  AfxMessageBox("初始化COM支持库失败!");
  return FALSE;
 }
 if(m_application.m_lpDispatch == NULL)
 {
  if (!m_application.CreateDispatch("Word.Application", NULL))
  {
   AfxMessageBox("Word初始化时出错,没有安装Word或安装不正确!", MB_OK | MB_ICONERROR);
   return FALSE;
  }
 }
    LPDISPATCH lpDisp = m_application.GetDocuments();
 m_documents.AttachDispatch(lpDisp);
 COleVariant covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
 lpDisp = m_documents.Add(&covOptional, &vtMissing, &vtMissing, &vtMissing);
    m_document.AttachDispatch(lpDisp);
 lpDisp = m_application.GetActiveWindow();
    m_window.AttachDispatch(lpDisp);
 lpDisp = m_window.GetSelection();
 m_selection.AttachDispatch(lpDisp);
 return TRUE;
}

BOOL WordMain::Open(const CString &strFileName)
{
 if (FAILED(CoInitialize(NULL)))
 {
  AfxMessageBox("初始化COM支持库失败!");
  return FALSE;
 }
 if(m_application.m_lpDispatch == NULL)
 {
  if (!m_application.CreateDispatch("Word.Application", NULL))
  {
   AfxMessageBox("Word初始化时出错,没有安装Word或安装不正确!", MB_OK | MB_ICONERROR);
   return FALSE;
  }
 }
 LPDISPATCH lpDisp = m_application.GetDocuments();
 m_documents.AttachDispatch(lpDisp);
 lpDisp = m_documents.Open(&(_variant_t)strFileName, &vtMissing, &vtMissing, &vtMissing,
  &vtMissing, &vtMissing, &vtMissing, &vtMissing, &vtMissing, &vtMissing,
  &vtMissing, &vtMissing, &vtMissing, &vtMissing, &vtMissing, &vtMissing);
    m_document.AttachDispatch(lpDisp);
 lpDisp = m_application.GetActiveWindow();
    m_window.AttachDispatch(lpDisp);
 lpDisp = m_window.GetSelection();
 m_selection.AttachDispatch(lpDisp);
 return TRUE;
}

BOOL WordMain::New(const CString &strTemplateName)
{
 if (FAILED(CoInitialize(NULL)))
 {
  AfxMessageBox("初始化COM支持库失败!");
  return FALSE;
 }
 if(m_application.m_lpDispatch == NULL)
 {
  if (!m_application.CreateDispatch("Word.Application", NULL))
  {
   AfxMessageBox("Word初始化时出错,没有安装Word或安装不正确!", MB_OK | MB_ICONERROR);
   return FALSE;
  }
 }
 LPDISPATCH lpDisp = m_application.GetDocuments();
 m_documents.AttachDispatch(lpDisp);
    lpDisp = m_documents.Add(&(_variant_t)strTemplateName, &vtMissing, &vtMissing, &vtMissing);
    m_document.AttachDispatch(lpDisp); 
 lpDisp = m_application.GetActiveWindow();
    m_window.AttachDispatch(lpDisp);
 lpDisp = m_window.GetSelection();
 m_selection.AttachDispatch(lpDisp);
 return TRUE;
}

BOOL WordMain::SaveAs(const CString &strFileName)
{
    if (IsFileExist(strFileName, FALSE))
 {
  CString str, str1;
  long i = strFileName.ReverseFind('\\');
  assert(i != -1);
  long j = strFileName.ReverseFind('.');
  assert(j != -1);
  str = strFileName.Mid(i + 1, j - i - 1);
  str1 = strFileName.Mid(i + 1);
  HWND hWnd = ::FindWindow(0, str + " - Microsoft Word");
  HWND hWnd1 = ::FindWindow(0, str1 + " - Microsoft Word");
  if (hWnd != NULL || hWnd1 != NULL)
    
   return FALSE;
 
  }

  while(!DeleteFile(strFileName))
  {
      Sleep(1);
  }
 }
 m_document.SaveAs(&_variant_t(strFileName), &vtMissing, &vtMissing, &vtMissing, &vtMissing, &vtMissing, &vtMissing,
  &vtMissing, &vtMissing, &vtMissing, &vtMissing, &vtMissing, &vtMissing, &vtMissing, &vtMissing, &vtMissing);
 return TRUE;
}

void WordMain::Save()
{
    m_document.Save();
}

BOOL WordMain::IsFileExist(const CString &strFn, BOOL bDir)
{
    HANDLE handle;
 LPWIN32_FIND_DATA lpFindFileData = new WIN32_FIND_DATA;
 BOOL bFound = FALSE;
 if (lpFindFileData)
 {
  handle = FindFirstFile(strFn, lpFindFileData);
  bFound = (handle != INVALID_HANDLE_VALUE);
  if(bFound)
  {
   if(bDir)
   {
    bFound = lpFindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
   }
   FindClose(handle);
  }
  delete lpFindFileData;
 }
 return bFound;
}

void WordMain::InsertTable(long lRow, long lCol)
{

 m_tables = m_document.GetTables();
 m_tables.Add(m_selection.GetRange(), lRow, lCol, &vtMissing, &vtMissing);//m_selection.GetRange()为光标位置
 m_selection.SetStyle(&_variant_t("网格型"));
}

void WordMain::GetRange(long lLeft, long lUp, long lRight, long lDown)
{
 long Extend = 1;//选中标志
 m_selection.MoveLeft(&vtMissing, &_variant_t(lLeft), &_variant_t(Extend));
 m_selection.MoveUp(&vtMissing, &_variant_t(lUp), &_variant_t(Extend));
 m_selection.MoveRight(&vtMissing, &_variant_t(lRight), &_variant_t(Extend));
 m_selection.MoveDown(&vtMissing, &_variant_t(lDown), &_variant_t(Extend));
}

void WordMain::GetWholeStory()
{
    m_selection.WholeStory();
}
void WordMain::HomeKey()
{
 m_selection.HomeKey(&vtMissing,  &vtMissing);
}
void WordMain::EndKey()
{
 m_selection.EndKey(&vtMissing,  &vtMissing);
}

void WordMain::MoveUp(long lCount)
{
 m_selection.MoveUp(&vtMissing, &_variant_t(lCount), &vtMissing);
}

void WordMain::MoveLeft(long lCount)
{
 m_selection.MoveLeft(&vtMissing, &_variant_t(lCount), &vtMissing);
}

void WordMain::MoveRight(long lCount)
{
 m_selection.MoveRight(&vtMissing, &_variant_t(lCount), &vtMissing);
}

void WordMain::MoveDown(long lCount)
{
 m_selection.MoveDown(&vtMissing, &_variant_t(lCount), &vtMissing);
}

void WordMain::BackSpace(long lCount)
{
 for (short i = 0; i
 {
  m_selection.TypeBackspace();
 }
}

void WordMain::Delete()
{
    m_selection.Delete(&vtMissing, &vtMissing);
}

void WordMain::SetItemText(const CString &strText)
{
    m_selection.TypeText(strText);
}

void WordMain::Enter(long lCount)
{
 for (short i = 0; i
 {
        m_selection.TypeParagraph();
 }
}

void WordMain::SetFont(const MYFONT &myFont)
{
 _Font font = m_selection.GetFont();
 font.SetName(myFont.strName);
 font.SetColor(myFont.lColor);
 font.SetSize(myFont.fSize);
 font.SetBold(myFont.bBold);
 font.SetItalic(myFont.bItalic);
 font.SetUnderline(myFont.bUnderLine);
 font.ReleaseDispatch();
}

void WordMain::SetTextHAlignment(long lHAlignment)
{
 _ParagraphFormat paraFormat = m_selection.GetParagraphFormat();
 paraFormat.SetAlignment(lHAlignment);
    paraFormat.ReleaseDispatch();
}

void WordMain::SetCellVAlignment(long lVAlignment)
{
    Cells cells = m_selection.GetCells();
 cells.SetVerticalAlignment(lVAlignment);
    cells.ReleaseDispatch();
}

void WordMain::InsertBookmark(const CString &strName)
{
    Bookmarks bookmarks = m_document.GetBookmarks();
 bookmarks.Add(strName, &_variant_t(m_selection.GetRange()));
 bookmarks.ReleaseDispatch();
}

BOOL WordMain::GotoBookmark(const CString &strBookmarkName)
{
    Bookmarks bookmarks = m_document.GetBookmarks();
 if (!bookmarks.Exists(strBookmarkName))
 {
  return FALSE;
 }
 short GoToBookmark = -1;
    m_selection.GoTo(&_variant_t(GoToBookmark), &vtMissing, &vtMissing, &_variant_t(strBookmarkName));
 bookmarks.ReleaseDispatch();
 return TRUE;
}
void WordMain::nameBookmarks(WordMain wdMain,CString &strSet)
{
 Bookmarks bookmarks=wdMain.m_document.GetBookmarks();
 Bookmark bookmark;
 int bksCount=bookmarks.GetCount();
 CString str;
 str="";
 VARIANT vtIndex;
for (int k=0;k < bksCount;k++)
 {
  vtIndex.vt = VT_I4;
  vtIndex.lVal = k+1;// 注意,从1开始,不是0
  bookmark=bookmarks.Item(&vtIndex);
  str=bookmark.GetName()+"|";
  strSet +=str ;
 
 }//for (for k=0;k

 bookmarks.ReleaseDispatch();
 bookmark.ReleaseDispatch();
}
BOOL WordMain::InsertPicture(const CString &strFileName, float lWidth, float lHeight)
{
    if (!IsFileExist(strFileName, FALSE))
 {

  AfxMessageBox(strFileName+ " 要插入的图片不存在!");
  return FALSE;
 }
    //Shapes shapes = m_document.GetShapes();
    InlineShapes inlineShapes = m_selection.GetInlineShapes();
 inlineShapes.AddPicture(strFileName, &vtMissing, &vtMissing, &vtMissing);
 GetRange(1, 0, 0, 0);
 inlineShapes = m_selection.GetInlineShapes();
 InlineShape inlineShape = inlineShapes.Item(1);
 inlineShape.SetWidth(lWidth);
 inlineShape.SetHeight(lHeight);
 MoveRight(1);
 inlineShape.ReleaseDispatch();
 inlineShapes.ReleaseDispatch();
 return TRUE;
}

void WordMain::SetTableText(long lIndex, long lRow, long lCol, const CString &strText)
{
    Tables tables = m_document.GetTables();
 if (lIndex > tables.GetCount())
 {
  AfxMessageBox("试图将数据写入不存在的表格!");
  return;
 }
 Table table = tables.Item(lIndex);
 Cell cell =  table.Cell(lRow, lCol);
 cell.Select();
 m_selection.SetText(strText);
 cell.ReleaseDispatch();  
 table.ReleaseDispatch();
 tables.ReleaseDispatch();
}

long WordMain::GetTablesCount()
{
    Tables tables = m_document.GetTables();
 long lTalbesCount = tables.GetCount();
 tables.ReleaseDispatch();
 return lTalbesCount;
}

void WordMain::SelectTable(long lIndex)
{
    Tables tables = m_document.GetTables();
 Table table = tables.Item(lIndex);
    table.Select();
 table.ReleaseDispatch();
 tables.ReleaseDispatch();
}

long WordMain::GetPage()
{
    VARIANT vt = m_selection.GetInformation(1);

 return vt.lVal;
}

void WordMain::SetHeadingFormat(long lIndex, BOOL bShow)
{
 if (bShow)
 {
  Tables tables = m_document.GetTables();
  Table table = tables.Item(lIndex);
  Cell cell = table.Cell(1, GetTableColumCount(lIndex));
  Row row = cell.GetRow();       
  row.SetHeadingFormat(9999998);
  row.ReleaseDispatch();
  cell.ReleaseDispatch();
        table.ReleaseDispatch();
  tables.ReleaseDispatch();
 }
}

long WordMain::GetTableColumCount(long lIndex)
{
 Tables tables = m_document.GetTables();
 Table table = tables.Item(lIndex);
 Columns columns = table.GetColumns();
 long lColumsCount = columns.GetCount();
 columns.ReleaseDispatch();
 table.ReleaseDispatch();
 tables.ReleaseDispatch();
 return lColumsCount;
}

long WordMain::GetTableRowCount(long lIndex)
{
 Tables tables = m_document.GetTables();
 Table table = tables.Item(lIndex);
 Rows rows = table.GetRows();
 long lRowCount = rows.GetCount();
 rows.ReleaseDispatch();
 table.ReleaseDispatch();
 tables.ReleaseDispatch();
 return lRowCount;
}

void WordMain::SetTableColumnWidth(float fNewValue)

 Columns columns = m_selection.GetColumns();
 columns.SetPreferredWidthType(3);
 columns.SetPreferredWidth(fNewValue);
 columns.ReleaseDispatch();
}
 
void WordMain::SetTableBorderLine(long lPosition, long lBorderLineStyle)
{
 Borders borders = m_selection.GetBorders();
 Border border = borders.Item(lPosition);
 border.SetLineStyle(lBorderLineStyle);
 border.ReleaseDispatch();
 borders.ReleaseDispatch();
}


void WordMain::PageSet(const MYPAGESET &pageSet)
{
    PageSetup pgSetup = m_document.GetPageSetup();
 pgSetup.SetTopMargin(pageSet.fTopMargin);
 pgSetup.SetBottomMargin(pageSet.fBottomMargin);
 pgSetup.SetLeftMargin(pageSet.fLeftMargin);
 pgSetup.SetRightMargin(pageSet.fRightMargin);
    pgSetup.ReleaseDispatch();
}

void WordMain::SetTableHAlignment(long lVAlignment)
{
 Tables tables = m_selection.GetTables();
 Table table = tables.Item(1);
 Rows rows = table.GetRows();
 rows.SetAlignment(lVAlignment);
 rows.ReleaseDispatch();
 table.ReleaseDispatch();
 tables.ReleaseDispatch();
}

void WordMain::UpdateDomain()
{
    m_selection.WholeStory();
 Fields fields = m_selection.GetFields();
 fields.Update();
}

void WordMain::Cut()
{
    m_selection.Cut();
}

void WordMain::Copy()
{
    m_selection.Copy();
}

void WordMain::Paste()
{
    m_selection.Paste();
}

void WordMain::DeleteRows()
{
 Rows rows = m_selection.GetRows();
 rows.Delete();
    rows.ReleaseDispatch();
}

void WordMain::InsertRowsAbove(long lRowCount)
{
 m_selection.InsertRowsAbove(&_variant_t(long(lRowCount)));
}

BOOL WordMain::ShowBookMark(BOOL bShow)
{
 m_view = m_window.GetView();
    m_view.SetShowBookmarks (bShow);
    m_view.ReleaseDispatch();
 return TRUE;
}


原文:http://blog.sina.com.cn/s/blog_45eaa01a0102vsqx.html


第1章 MFC概述 1 1.1 MFC是一个编程框架 1 1.1.1 封装 1 1.1.2 继承 2 1.1.3 虚拟函数和动态约束 2 1.1.4 MFC的宏观框架体系 2 1.2 MDI应用程序的构成 3 1.2.1 构成应用程序的对象 3 1.2.2 构成应用程序的对象之间的关系 5 1.2.3 构成应用程序的文件 5 第2章 MFC和Win32 9 2.1 MFC Object和Windows Object的关系 9 2.2 Windows Object 12 2.2.1 Windows的注册 12 2.2.2 MFC窗口类CWnd 15 2.2.3 在MFC下创建一个窗口对象 17 2.2.4 MFC窗口的使用 18 2.2.5 在MFC下窗口的销毁 19 2.3 设备描述表 20 2.3.1 设备描述表概述 20 2.3.2 设备描述表在MFC中的实现 22 2.3.3 MFC设备描述表类的使用 24 2.4 GDI对象 25 第3章 CObject类 28 3.1 CObject的结构 28 3.2 CObject类的特性 30 3.3 实现CObject特性的机制 32 3.3.1 DECLARE_DYNAMIC等宏的定义 32 3.3.2 CruntimeClass类的结构与功能 35 3.3.3 动态类信息、动态创建的原理 38 3.3.4 序列化的机制 39 第4章 消息映射的实现 42 4.1 Windows消息概述 42 4.1.1 消息的分类 42 4.1.2 消息结构和消息处理 42 4.2 消息映射的定义和实现 44 4.2.1 MFC处理的三类消息 44 4.2.2 MFC消息映射的实现方法 45 4.2.3 在声明与实现的内部 46 4.2.3.1 消息映射声明的解释 47 4.2.3.2 消息映射实现的解释 49 4.2.4 消息映射宏的种类 51 4.3 CcmdTarget类 54 4.4 MFC窗口过程 55 4.4.1 MFC窗口过程的指定 56 4.4.2 对Windows消息的接收和处理 58 4.4.2.1 从窗口过程到消息映射 59 4.4.2.2 Windows消息的查找和匹配 60 4.4.2.3 Windows消息处理函数的调用 62 4.4.2.4 消息映射机制完成虚拟函数功能的原理 63 4.4.3 对命令消息的接收和处理 64 4.4.3.1 MFC标准命令消息的发送 64 4.4.3.2 命令消息的派发和消息的多次处理 67 4.4.3.3 一些消息处理类的OnCmdMsg的实现 69 4.4.3.4 一些消息处理类的OnCommand的实现 71 4.4.4 对控制通知消息的接收和处理 72 4.4.4.1 WM_COMMAND控制通知消息的处理 72 4.4.4.2 WM_NOTIFY消息及其处理: 73 4.4.4.3 消息反射 74 4.4.5 对更新命令的接收和处理 77 4.4.5.1 实现方法 77 4.4.5.2 状态更新命令消息 78 4.4.5.3 类CCmdUI 79 4.4.5.4 自动更新用户接口对象状态的机制 80 4.5 消息的预处理 82 4.6 MFC消息映射的回顾 83 第5章 MFC对象的创建 85 5.1 MFC对象的关系 85 5.1.1 创建关系 85 5.1.2 交互作用关系 86 5.2 MFC提供的接口 87 5.2.1 虚拟函数接口 87 5.2.2 消息映射方法和标准命令消息 91 5.3 MFC对象的创建过程 94 5.3.1 应用程序中典型对象的结构 94 5.3.1.1 应用程序类的成员变量 95 5.3.1.2 CDocument的成员变量 97 5.3.1.3 文档模板的属性 97 5.3.2 WinMain入口函数 99 5.3.2.1 WinMain流程 99 5.3.2.2 MFC空闲处理 101 5.3.3 SDI应用程序的对象创建 102 5.3.3.1 文档模板的创建 102 5.3.3.2 文件的创建或者打开 103 5.3.3.3 SDI边框窗口的创建 112 5.3.3.4 视的创建 115 5.3.3.5 窗口初始化 116 5.3.3.6 视的初始化 120 5.3.3.7 激活边框窗口(处理WM_ACTIVE) 121 5.3.3.8 SDI流程的回顾 122 5.3.4 MDI程序的对象创建 123 5.3.4.1 有别于SDI的主窗口加载过程 124 5.3.4.2 MDI子窗口、视、文档的创建 125 5.3.4.3 MDI子窗口的初始化和窗口的激活 127 第6章 应用程序的退出 131 6.1 边框窗口对WM_CLOSE的处理 131 6.2 窗口的销毁过程 135 6.2.1 DestroyWindow 135 6.2.2 处理WM_DESTROY消息 136 6.2.3 处理WM_NCDESTROY消息 136 6.3 SDI窗口、MDI主、子窗口的关闭 137 第7章 MFC的DLL 139 7.1 DLL的背景知识 139 7.2 调用约定 141 7.2.1 MFC的DLL应用程序的类型 142 7.3 DLL的几点说明 143 7.4 输出函数的方法 145 第8章 MFC的进程和线程 148 8.1 Win32的进程和线程概念 148 8.2 Win32的进程处理简介 148 8.2.1 进程的创建 148 8.2.2 进程的终止 149 8.3 Win32的线程 150 8.3.1 线程的创建 150 8.3.2 线程的终止 150 8.3.3 线程局部存储 151 8.4 线程同步 152 8.4.1 同步对象 152 8.4.2 等待函数 153 8.5 MFC的线程处理 154 8.5.1 创建用户界面线程 155 8.5.2 创建工作者线程 155 8.5.3 AfxBeginThread 155 8.5.4 CreateThread和_AfxThreadEntry 157 8.5.5 线程的结束 160 8.5.6 实现线程的消息循环 161 第9章 MFC的状态 163 9.1 模块状态 163 9.2 模块、进程和线程状态的数据结构 164 9.2.1 层次关系 164 9.2.2 CNoTrackObject类 166 9.2.3 AFX_MODULE_STATE类 166 9.2.4 _AFX_BASE_MODULE_STATE 169 9.2.5 _AFX_THREAD_STATE 169 9.2.6 AFX_MODULE_THREAD_STATE 171 9.3 线程局部存储机制和状态的实现 172 9.3.1 CThreadSlotData和_afxThreadData 173 9.3.1.1 CThreadSlotData的定义 173 9.3.1.2 CThreadSlotData的一些数据成员 174 9.3.1.3 _afxThreadData 175 9.3.2 线程状态_afxThreadState 176 9.3.3 进程模块状态afxBaseModuleState 178 9.3.4 状态对象的创建 180 9.3.4.1 状态对象的创建过程 180 9.3.4.2 创建过程所涉及的几个重要函数的算法 183 9.4 管理状态 184 9.4.1 模块状态切换 184 9.4.2 扩展DLL的模块状态 186 9.4.2.1 _AFX_EXTENSION_MODULE 187 9.4.2.2 扩展DLL的初始化函数 188 9.4.3 核心MFC DLL 190 9.4.4 动态链接的规则DLL的模块状态的实现 190 9.5 状态信息的作用 192 9.5.1.1 模块信息的保存和管理 192 9.5.2 MFC资源、运行类信息的查找 193 9.5.3 模块信息的显示 194 9.5.4 模块-线程状态的作用 196 9.5.4.1 只能访问本线程MFC对象的原因 196 9.5.4.2 实现MFC对象和Windows对象之间的映射 196 9.5.4.3 临时对象的处理 199 9.6 状态对象的删除和销毁 199 第10章 内存分配方式和调试机制 202 10.1 M内存分配 202 10.1.1 内存分配函数 202 10.1.2 C++的new 和 delete操作符 204 10.2 调试手段 204 10.2.1 C运行库提供和支持的调试功能 204 10.2.2 MFC提供的调试手段 205 10.2.3 内存诊断 208 第11章 MFC下的文件类 210 11.1 文件操作的方法 210 11.2 MFC的文件类 210 11.2.1 CFile的结构 211 11.2.1.1 CFile定义的枚举类型 211 11.2.1.2 CFile的其他一些成员变量 212 11.2.1.3 CFile的成员函数 212 11.2.2 CFile的部分实现 213 11.2.3 CFile的派生类 215 第12章 对话框和对话框类CDialog 217 12.1 模式和无模式对话框 217 12.1.1 模式对话框 217 12.1.2 无模式对话框 218 12.2 对话框的MFC实现 219 12.2.1 CDialog的设计和实现 219 12.2.1.1 CDialog的成员变量 219 12.2.1.2 CDialog的成员函数: 219 12.2.2 MFC模式对话框的实现 220 12.2.2.1 MFC对话框过程 220 12.2.2.2 模式对话框窗口过程 221 12.2.2.3 使用原对话框窗口过程作消息的缺省处理 225 12.2.2.4 Dialog命令消息和控制通知消息的处理 226 12.2.2.5 消息预处理和Dialog消息 228 12.2.2.6 模式对话框的消息循环 229 12.2.3 对话框的数据交换 233 12.2.3.1 数据交换的方法 233 12.2.3.2 CDataExchange 234 12.2.3.3 数据交换和验证函数 236 12.2.3.4 UpdateData函数 239 12.3 无模式对话框 240 12.3.1 CScrollView 240 12.3.2 CFormView 242 12.3.2.1 CFormView的创建 243 12.3.2.2 CFormView的消息预处理 245 12.3.2.3 CFormView的输入焦点 247 第13章 MFC工具条和状态栏 248 13.1 Windows控制窗口 248 13.2 MFC的工具条和状态栏类 249 13.2.1 控制窗口的创建 251 13.2.1.1 PreCreateWindow 251 13.2.1.2 控制条的窗口创建 253 13.2.2 控制条的销毁 259 13.2.3 处理控制条的位置 259 13.2.3.1 计算控制条位置的过程和算法 259 13.2.3.2 CFrameWnd的虚拟函数RecalcLayout 260 13.2.3.3 CWnd的成员函数RepositionBars 262 13.2.4 工具条、状态栏和边框窗口的接口 265 13.2.4.1 应用程序在状态栏中显示信息 265 13.2.4.2 状态栏显示菜单项的提示信息 268 13.2.4.3 控制条的消息分发处理 270 13.2.4.4 Tooltip 275 13.2.4.5 禁止和允许 279 13.2.4.6 显示或者隐藏工具栏和状态栏 285 13.2.5 泊位和漂浮 286 第14章 SOCKET类的设计和实现 288 14.1 WinSock基本知识 288 14.1.1 WinSock API 288 14.1.2 Socket的使用 290 14.2 MFC对WinSockt API的封装 291 14.2.1 CAsyncSocket 291 14.2.2 socket对象的创建和捆绑 292 14.2.3 异步网络事件的处理 296 14.3 CSocket 297 14.4 CSocketFile 299
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值