|
|
用户名:suyong5968 笔名:狂 地区: 行业:其他 |
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
欢迎访问为笑而狂的博客
BGCPro有bug,删除了
经过一段时间的使用,发现有bug,可能是使用D版的原因,在调试程序时有莫名其妙的错误,停止使用了,现在想找一个开源的界面类。VC写界面真的很痛苦
以下是转自CSDN的文章,希望能找到一些有用的东西。
界面技术概述
在做“HOOK文件打开/保存对话框”的过程中,我首先研究了界面库的相关知识。界面库一般都是由C/C++这种中低级语言编码,这是因为在Windows下的界面库实现技术大都以直接操作控制Windows的消息和调用Windows的API为主,这就是这种中低级语言的优势了。无论何种界面库,最为根本的原理就是获得或者截获窗口的某些消息,按照自己的需要处理这些消息,画出自己需要的界面。
按照Windows下的界面库的使用方法来分类,可以分为两种:
1、 通过派生、继承界面库中的类来使用库。这类界面库现在是占绝大多数。这类界面库通常可以对同种类型的控件、窗口自己控制显示风格。这种类型的界面库典型的代表就是GuiToolkit、ProfUIS。
2、 通过Link头文件,使用DLL来使用的界面库。这类界面库一般都是商业化的界面库。这类界面库一般对于同种类型的控件、窗口都是显示统一的风格。这种类型的界面库的典型代表是Skin++、AppFace。
上面的分类,其实同时也代表着两种界面库实现技术,也就是获取用于自绘窗口的消息的两种来源:
1、 通过子类化、超类化改变窗口风格。其实就是调用Windows的API SetWindowLong或者通过类的派生和继承来改变Windows窗口的默认的消息处理函数。
2、 使用HOOK技术改变Windows的默认消息处理。
一、SetWindowLong
SetWindowLong(HWND hWnd,//需要改变UI的窗口的窗口句柄
Int nIndex,//替换窗口的默认消息处理函数时为GWL_WNDPROC
Long dwNewLong)//新的默认的窗口消息处理函数
调用这个API函数可以替换一个窗口的默认的消息处理函数,这样就可以在新的窗口消息处理函数中截获到目标窗口的相关消息,然后根据需要处理这些消息。这个API用于获取当前窗口的消息,它不能获取窗口中子窗口的消息。
这种类型的界面库,一般是开源的或者是提供了头文件和Lib文件的界面库。
这个Windows API大家很可能很少直接调用,但是它的封装――――SubclassWindow这个成员函数,我想大家都使用过。先让我们看看各种类型的子类化过程中的SetWindowLong都藏在什么地方,它们是如何工作的。
1、 MFC下的实现
在MFC程序中,可以先在工程中添加一个用于子类化的窗口类,然后就可以通过ClassWizard这个工具来完成剩下的子类化的工作了。我们以一个自定义的Button类CMyButton类来举例。在打开ClassWizard为我们的基于Dialog的工程中的一个Button资源添加一个对应的变量的时候我们就可以看到可以直接定义了CMyButton类,如下图:
我们为ID为IDC_BUTTON1的一个按钮资源定义一个类型为CMyButton的成员变量m_MyBtn。这时候ClassWizard就会在重载的虚函数DoDataExchange中为我们添加上一条语句,如下:
void CMFCSampleDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMFCSampleDlg)
DDX_Control(pDX, IDC_BUTTON1, m_MyBtn);//这就是ClassWizard为我们添加的
//}}AFX_DATA_MAP
}
让我们来看看DDX_Control这个函数为我们做了什么,在MFC的源代码DLGDATA.CPP文件中我们能找到这个函数的源代码:
void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl)
{
if (rControl.m_hWnd == NULL) // not subclassed yet
{
……
…
//注意看看,噢,原来SubclassWindow在这里
if (!rControl.SubclassWindow(hWndCtrl))
{
ASSERT(FALSE); // possibly trying to subclass twice?
AfxThrowNotSupportedException();
}
……
…
}
}
让我们再看看MFC下的SubclassWindow这个成员函数的实现,在MFC的源代码wincore.cpp中可以看到所有MFC下窗口类的基类CWnd中的SubclassWindow的实现:
BOOL CWnd::SubclassWindow(HWND hWnd)
{
if (!Attach(hWnd))
return FALSE;
// allow any other subclassing to occur
PreSubclassWindow();
// now hook into the AFX WndProc
WNDPROC* lplpfn = GetSuperWndProcAddr();
//注意看看,原来它也是使用SetWindowLong啊
WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,
(DWORD)AfxGetAfxWndProc());
……
…
return TRUE;
}
上面的源代码跟踪说明,ClassWizard为我们自动完成的子类化工作中,其实也是调用的SetWindowLong这个Windows API。当然在MFC下你也可以自己调用SubclassWindow这个成员函数去手动的完成子类化。
2、 ATL/WTL下的实现
在ATL的源代码中,文件ATLWIN.H文件中,我们可以找到所有ATL窗口类的基类CwindowImplBaseT中的SubClassWindow的实现:
template <class TBase, class TWinTraits>
BOOL CWindowImplBaseT< TBase, TWinTraits >::SubclassWindow(HWND hWnd)
{
ATLASSERT(m_hWnd == NULL);
ATLASSERT(::IsWindow(hWnd));
m_thunk.Init(GetWindowProc(), this);
WNDPROC pProc = (WNDPROC)&(m_thunk.thunk);
//注意看看,它也是调用SetWindowLong的!
WNDPROC pfnWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);
if(pfnWndProc == NULL)
return FALSE;
m_pfnSuperWindowProc = pfnWndProc;
m_hWnd = hWnd;
return TRUE;
}
在ATL/WTL没有MFC下的那种自动子类化的机制,如果需要子类化一般都是直接调用SubclassWindow这个成员函数。
二、SetWindowsHookEx
SetWindowsHookEx( int idHook,//HOOK的类型
HOOKPROC lpfn,//HOOK的回调函数
HINSTANCE hMod,//应用程序的实例句柄
DWORD dwThreadId)//线程的ID
SetWindowsHookEx这个API可以设置很多种类型的HOOK,它们分别在不同的时间截获线程ID为dwThreadId的线程的不同消息。在编写界面库的时候一般设置类型为WH_CALLWNDPROC的HOOK,用以在窗口处理消息之前获得消息并进行处理。在HOOK的CALLBACK函数中可以获得窗口句柄,进而获得窗口的类型和窗口的风格,这样就可以知道需要处理的消息类型。一般都是从截获WM_CREATE消息开始,然后处理WM_PAINT、WM_NCPAINT等等和UI有关的消息以达到自绘窗口UI的目的。
使用HOOK技术的界面库中当然也可以使用SetWindowLong技术,在C++写的界面库中一般是在获得了窗口句柄以后,即使用SetWindowLong技术将窗口句柄关联到一种特定的窗口类上。并且可以根据窗口的风格让同一个类做出不同风格的显示效果,可惜的是现在市面上的界面库都很少进行这种比较繁琐的区分工作。
这种类型的界面库一般都是以DLL文件格式出现,现在最为流行的是将界面库封装为一个COM DLL,在应用程序中创建这个COM组件,获得相关的接口,调用相关的接口函数来为应用程序安装HOOK。在做“Picasso风格的文件打开/保存对话框”过程中,我们一直以Yahoo Messenger的Save对话框作为参考,其实Yahoo Messenger就是使用的这种类型的界面库的。Yahoo messenger的界面库截获了进程中所有线程的消息,并做了相关的处理,所以它可以截获一些系统的窗口的创建消息和UI相关的消息,以达到改变Windows系统窗口的显示风格的目的。
最后介绍一下个比较好的开源的界面库或者例子。
1、 ClassXP(http://www.yonsm.net/read.php?26)
一个个人的开源界面库,C语言写的,不完善,使用HOOK技术。Yahoo messenger的界面库就类似于这个界面库,只不过Yahoo messenger做的更完善而已。
2、 ProfUIS(http://www.codeproject.com/docking/prod_profuis.asp)
一个部分开源的界面库,有完善功能的商业版。比较完善,MFC写的,使用的是SetWindowLong技术。
3、 GuiToolkit(http://www.codeproject.com/library/guitoolkit.asp)
一个开源的界面库,比较完善,MFC写的,使用的是SetWindowLong技术。
4、 MSDN中的ControlSpy例子。
MSDN中的一个例子,用于了解各种控件的消息。
- 作者: suyong5968 2007年01月6日, 星期六 17:20 回复(2) | 引用(1) 加入博采
IceSword冰刃使用方法[转] 大 | 中 | 小





- 作者: suyong5968 2006年10月27日, 星期五 14:43 回复(1) | 引用(1) 加入博采
创建多级目录
(自己的代码)
// 创建多级目录
BOOL CDownloadThread::MakeDirectory(CString strFilePath)
{
HANDLE fFile; //文件句柄
WIN32_FIND_DATA fileinfo; //文件信息结构体
CStringArray m_arr; //字符串数组用于存储目录结构
BOOL bOk; //用于测试创建目录成功
int nX = 0; //目录计数
CString temStr = ""; //临时字符串
fFile = FindFirstFile(strFilePath,&fileinfo);
if (fileinfo.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) //如果该目录存在
{
FindClose(fFile);
return TRUE;
}
//不存在,开始作创建的准备工作
m_arr.RemoveAll(); //清空字符串数组
for (nX = 0;nX < strFilePath.GetLength();nX++) //开始解析字符串
{
if (strFilePath.GetAt(nX) != '\\') //如果字符不是"\"
{
temStr += strFilePath.GetAt(nX); // 拷贝字符到临时字符串直到遇到路径分格符"\"
}
else //遇到路径分格符"\"
{
m_arr.Add(temStr); //增加解析的路径到字符串数组
temStr += _T("\\"); //增加路径分格符
}
if (nX == strFilePath.GetLength()-1) //最后一个
{
m_arr.Add(temStr);
}
}
FindClose(fFile); //关闭文件
//开始分级创建目录
for (nX = 1; nX < m_arr.GetSize(); nX++)
{
temStr = m_arr.GetAt(nX); //取解析好的路径
bOk = ::CreateDirectory(temStr,NULL); //从一级目录开始创建
//如果目录存在,bOk将返回FALSE
if(bOk)
{
SetFileAttributes(temStr,FILE_ATTRIBUTE_NORMAL); //如果创建目录成功设置属性为Normal
}
}
//再检查一次目录是否创建成功
fFile = FindFirstFile(strFilePath,&fileinfo);
m_arr.RemoveAll();
if (fileinfo.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
{
FindClose(fFile); //目录存在
return TRUE;
}
else
{
FindClose(fFile); //不存在
return FALSE;
}
}
- 作者: suyong5968 2006年10月3日, 星期二 10:18 回复(0) | 引用(1) 加入博采
CppUnit使用心得
1. 在stdafx.h中添加
//装载CppUnit库
#ifndef _DEBUG
#pragma comment(lib, "cppunit_dll.lib")
#pragma comment(lib, "TestRunner.lib")
#else
#pragma comment(lib, "cppunitd_dll.lib")
#pragma comment(lib, "TestRunnerd.lib")
#endif
2. 在对话框程序的InitInstance()中注释掉
/*
CTestDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: 在此放置处理何时用“确定”来关闭
//对话框的代码
}
else if (nResponse == IDCANCEL)
{
// TODO: 在此放置处理何时用“取消”来关闭
//对话框的代码
}
// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
// 而不是启动应用程序的消息泵。
*/
添加
CppUnit::MfcUi::TestRunner runner;
CppUnit::Test *pSuite = CppUnit::TestFactoryRegistry::getRegistry("TestSetCFG").makeTest();
runner.addTest(pSuite);
runner.run();
包含 #include <cppunit/ui/mfc/TestRunner.h>
和测试头文件 #include "TestSetCFG.h"
3. 测试类的头文件添加代码
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestFixture.h>
#include <cppunit/Asserter.h>
class CTestSetCFG : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(CTestSetCFG);
CPPUNIT_TEST(Test1);
CPPUNIT_TEST_SUITE_END();
public:
CTestSetCFG(void);
~CTestSetCFG(void);
public:
void setUp()
{
}
void tearDown()
{
}
void Test1(void);
};
4. 测试类的实现部分添加代码
#include "StdAfx.h"
#include ".\testsetcfg.h"
#include <cppunit/TestResult.h>
//注册
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CTestSetCFG,"TestSetCFG");
void CTestSetCFG::Test1(void)
{
..................测试代码
}
- 作者: suyong5968 2006年10月2日, 星期一 13:56 回复(0) | 引用(1) 加入博采
Team版的VS2005里面包含了完整的Test功能,具体有:Unit Test,WebTest和LoadTest.这一整套的测试基本涵盖了软件开发会使用到的测试功能.
我这里先从单元测试开始介绍(Unit Test).说起单元测试,很多使用.net进行开发的人员也许马上就想起了NUnit,实际上它是个很好的工具,在VS2005出来之前,我也一直使用.不过现在VS2005已经提供了与NUnit一样,甚至还要强大的功能,我们又有什么理由不使用呢?
OK,进入正题.首先我们要解决一个问题,为什么我们需要做单元测试?这个问题,对有经验的老程序员来说,并不算是问题.一个软件在开发的过程中,倘若不是非常微型的软件,那么我们通常会划分出若干个功能模块来,然后一个模块一个模块的进行开发.每个子模块完成后,我们并不知道它是否能正常的运行,尤其是当这个模块只是个中间件类似的代码块,那么我们为了减少之后可能出现的问题和debug的难度(可以想象,如果在整合时期进行测试或者是甚至还有其他的模块需要依赖该模块才能进行开发的时候,尽早的测试将会是非常的重要),我们常常会对这单个模块进行测试,比如写段小程序,人为的写入几个参数来调用组件等.不用怀疑了,这就是单元测试.我相信,大部分的程序员都做过这样的工作,而且也许还有许多程序员就如我上面所说的,单独写段小程序来进行单元测试(我自己以前也是如此),现在我们需要认真考虑下下一个问题了:如何进行高效的,高可靠的,甚至自动化的单元测试?
VSTS里的Unit Test可以帮助我们实现我们希望的绝大多数功能.我们从实际的项目开发入手来介绍.假设我们新建了一个.net项目,嗯,这是一个有关缓存的子项目,名字叫MyCache.我们很认真的设计了项目的架钩,进行了可行性分析,接口和抽象的建立,具体对象的建立,关系建立,最后编码完成了.项目经理叫我们不要高兴的太早,他要求我们必须对这个项目进行可靠的单元测试,因为这个子项目非常重要,将会被许多项目引用.尽管我们很有信心,但是没有办法,我们依然需要进行单元测试.我们使用了Visual Studio Team System开发了这个项目,于是我们理所当然的使用自带的Unit Test工具进行单元测试.
Step1.我们需要建立项目文件与测试文件的映射关系.
难道要我们去手动创建吗?这可是整个项目啊,里面也许包含了几十个类,数百个方法…当然没那么复杂!实际上,我们需要做的工作很少,只是动动鼠标,等几秒就可以了:)
在VS2005的IDE环境下,选择menu里的Test,继续选New Test项,这时将跳出个窗体,里面可以选择测试项目类型,这里我们选择Unit Test Wizard,确定,输入测试项目名,然后将又出现一个窗体,里面包含你当前的solution里的所有project,我们选上我们的MyCache,确定.OK,看见一个进度条,这是在执行测试代码的映射工作,等结束后,你就会发现,已经建立了一个测试项目了,里面的文件完全对应你的目标项目,每个类包含的方法也是与目标类的方法一一对应,非常简单,cool,mission complete!
Step2.运行我们的测试项目.
接下来,我们怎么进行测试呢?里面有许多的类和方法,很多方法上还带有像TestMethod这样的标签属性,但是我们关心的是,如何进行测试?绝对不是通常的F5来运行:(,在VSTS里,单元测试实际上有专门的管理工具.再次选到menu上的Test选项,移到windows上展开自菜单,里面有好几个选项,我们选择TestManager打开.在IDE窗口内出现了一个视图结构的东西,在分割线的右边是一个listView,里面全是当前测试项目包含的方法,我们随便选几个方法给勾上,右键,Run Checked Test,下边马上有出现了Test Result窗体,里面就是刚才你选择的方法.如果不出意外的话,你的这个窗体内的方法result应该都是failed之类的数据,嗯,先不管这个,最起码,我们已经运行了一次测试项目了,虽然有些奇怪,不过我们已经知道了如何运行一个测试项目了,那么再进入下一个step吧:)
Step3.看看我们的测试代码里都有些什么.
虽然知道了怎么运行测试项目,但莫名其妙的全部出错,是怎么回事呢?我们进入测试项目具体的代码来看看.
我们会发现,每个测试类的名称就是对应的目标类的名称+"test",里面的方法也是如此,如果是构造函数,则是诸如
ConstructorTest或ConstructorTestN这样的形式,N为重载次数.每个方法里面的代码看上去也不奇怪,只是构造参数来调用而已,最后通过断言来判断(用过NUnit的朋友不会陌生吧?).我们试着直接把一个方法里的断言去掉看看,编译,TestManager,run,嘿,果然,去掉断言的方法就pass了!看来蒙老大不难呢,只要把所有的方法的断言都给去掉,然后给老大看测试后的结果,呵呵…
Step4.深入的了解一下方法上带有的属性的含义.
每个方法上几乎都带有TestMethod这个属性,我们直觉告诉我们,这肯定是表示被测试函数的意思.事实也正是如此,在Unit Test里,有许多测试属性,常用的如下:
| 属性 | 描述 |
TestClass() | 该属性表示一个测试装置。 |
TestMethod() | 该属性表示一个测试用例。 |
AssemblyInitialize() | 在执行为执行选择的第一个 TestClass() 中的第一个 TestMethod() 之前,执行带有该属性的方法。 |
ClassInitialize() | 带有该属性的方法在执行第一个测试之前调用。 |
TestInitialize() | 带有该属性的方法在执行每个 TestMethod() 之前调用。 |
TestCleanup() | 带有该属性的方法在执行每个 TestMethod() 之后调用。 |
ClassCleanup() | 带有该属性的方法在执行 ALL 测试之后调用。 |
AssemblyCleanup() | 在执行为执行选择的第一个 TestClass() 中的第一个 TestMethod() 之后,执行带有该属性的方法。 |
Description() | 提供关于给定 TestMethod() 的描述。 |
Ignore() | 由于某种原因忽略 TestMethod() 或 TestClass()。 |
ExpectedException() | 当测试特定异常时,如果使用该属性指定的异常不是从实现代码引发,则测试不会失败。 |
需要注意的是,上面的属性不是可以适用于所有方法的,比如AssemblyInitialize()和ClassInitialize()是必须是静态方法的属性.
我们可以把初始化的操作放在他们里进行.
Step5.修改测试方法及其断言.
到现在,我们的思路开始清晰起来了,我们要开始做真正的测试了,不是仅仅去掉断言就pass那么简单了:)
我们的测试思路应该是这样:我们调用该方法,需要传入什么值,会影响什么值,当它执行之后,会产生怎样的期待值?我们把期待值与实际的值想比较,同时写下断言失败的message.
还是以我们的MyCahce为例,假如我们有个ListCache类,里面有个AddItemToTop(item)方法,表示把一个item插入到当前链表的头部.我们实际的测试函数该这么写
Guid id = System.Guid.NewGuid();
Item item = new Item(id);
list.AddItemToTop(item);
Assert.AreEqual(id, roomList.FirstLinkedItem.Key, "插入后查询获得的key值与插入的对象的key值不相等!");
通过比对插入后的链表的头部的key与之前保存的key值来判断,这是不是一次成功的入.
这只是个很简单的例子,我们当然应该根据具体的方法需要实现的功能来定义测试代码.
Step6.OVER
完成了上面5部,相信你已经对VSTS的Unit Test非常的熟悉了,接下来需要做的就是把你需要的测试的method都提供正确的测试代码,注意,这里我们甚至不要考虑我们本身的项目究竟有没有实现该功能,但我们应该该知道,我们需要什么功能.我们只针对应该产生的结果写测试代码.当测试不通过时,我们只需要修改我们的目标项目,而不再需要修改我们的测试项目.这其实正是TDD(测试驱动开发)的思想,我们如果要验证我们的方法有没有错,只需要run一下test即可,真正实现了全自动化单元测试,这里边的实际开发效率的提高,只有你在真正体会过后才能明白:)
- 作者: suyong5968 2006年10月1日, 星期日 16:18 回复(1) | 引用(1) 加入博采
使用CppUnit进行单元测试
一. CppUnit的安装
从http://sourceforge.net/projects/cppunit CppUnit的源码包. CppUnit是开源产品 , 当前最高版本为1.12.0. (在上面的链接所指向的页面上选择 Development Snapshot ).
下载后,将源码包解压缩到本地硬盘. 以C:为例, 解压到C:\CppUnit-1.11.0 . 接下来进行编译工作. 在src/目录下, 将CppUnitLibraries.dsw工程文件用vc 打开. 因为这个工程是vc6建的工程, 所以会提示是否将此工程转为vc.net的工程格式. 选择'全是'就行. 然后在'生成'菜单中选择'生成解决方案',编译整个工程(要包括debug和Release版本,不然会有找不到×××.lib文件错误)编译通过以后, 在lib/目录下,会生成若干lib,和dll文件, 都以cppunit开头. cppunitd表示debug版, cppunit表示release版.
然后配置vc环境: 工具菜单->选项->项目->vc++目录, 将C:\cppunit-1.11.0\include加入到vc 的 头文件目录中. 将C:\cppunit-1.11.0\lib 加入到vc的 连接库目录中. 然后将 C:\cppunit-1.11.0\lib路径加入到系统路径中(因为我们的测试程序需要cppunit的dll文件,所以要dll所在路径加入到系统路径(path)变量中, 或者将这些文件拷贝到系统目录中也可以).
二. CppUnit 的使用
以上工作完成以后,就可以正式使用CppUnit了.
我们新建一个控制台工程, 假定工程名为TDD1. 假设我们将在这个工程下开发, 那么根据测试驱动的原理,我们需要先建立一个单元测试框架. 在CppUnit下, 可以选择控制台方式和UI方式两种表现方案.我们选择UI方式. 那么现在在原来工程的基础上,再新增加一个对话框工程. 假设名为:Unit_Test .
在CppUnit中, 是以TestCase为最小的测试单位, 若干TestCase组成一个TestSuite. 所以我们要先建立一个TestCase.
新建一个类, 命名为CTestCase , 让其从CppUnit::TestCase派生. 为其新增一个方法,假设为 void doTest(); 我们将在这个函数中写入我们的一些测试代码. 切记要包含头文件
#include
class CTestCase : public CppUnit::TestCase
{
public:
CTestCase(void);
~CTestCase(void);
void doTest();
};
接下来, 我们要对我们的TestCase进行声明. 声明用到了三个宏.
CPPUNIT_TEST_SUITE();
CPPUNIT_TEST();
CPPUNIT_TEST_SUITE_END();
第一个宏声明一个测试包,第二个宏声明一个测试用例. 现在我们的CTestCase类看上去象这样.
class CTestCase : public CppUnit::TestCase
{
CPPUNIT_TEST_SUITE(CTestCase);
CPPUNIT_TEST(doTest);
CPPUNIT_TEST_SUITE_END();
public:
CTestCase(void);
~CTestCase(void);
void doTest();
};
接下来,我们要注册我们的测试suite. 使用CPPUNIT_TEST_SUITE_NAMED_REGISTRATION()来注册一个测试suite. 这个宏的第二个参数是我们注册的suite的名字. 在这里我们可以用字符串来代替, 但我们用一个静态函数来返回这个suite的名字.
//TestCase.h
static std::string getSuiteName();
//TestCase.cpp
std::string CTestCase::getSuiteName()
{
return 'TDDExample3';
}
记得要在TestCase.h中包含 #include
然后在 TestCase.cpp注册我们的suite.
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CTestCase , CTestCase::getSuiteName() );
接下来我们写一个注册函数, 使其在运行期生成一个Test.
static CppUnit::Test* getSuite();
CppUnit::Test* CTestCase::getSuite()
{
CppUnit::TestFactoryRegistry& reg =
CppUnit::TestFactoryRegistry::getRegistry ( CTestCase::getSuiteName() );
return reg.makeTest();
}
记住包含头文件:
#include
最后, 我们的单元测试建立一个UI测试界面.
我们在CUnit_TestApp::InitInstance()函数中,将原先显示主对话框的代码以下面的代码取代:
CppUnit::MfcUi::TestRunner runner;
runner.addTest(CTestCase::getSuite());
runner.run();
必须先包含头文件:
#include
#include 'TestCase.h'
到此为止, 我们已经建立好一个简单的单元测试框架. 我们可以在doTest()中加入我们的测试代码,来进行测试了.CppUnit会将测试结果通过一个界面显示出来.
但此时我们虽然能编译通过,但在连接时会发生错误. 所以将下面两个库加入到工程中. 也可以在TestCase.h 中显式的加入:
#pragma comment(lib,'cppunit.lib')
#pragma comment(lib,'testrunnerd.lib')
下面是完整的程序清单:
//TestCase.h
#pragma once
#include
#include
#include
#include
#pragma comment(lib,'cppunit.lib')
#pragma comment(lib,'testrunnerd.lib')
class CTestCase : public CppUnit::TestCase
{
CPPUNIT_TEST_SUITE(CTestCase);
CPPUNIT_TEST(doTest);
CPPUNIT_TEST_SUITE_END();
public:
static std::string getSuiteName();
static CppUnit::Test* getSuite();
public:
CTestCase(void);
~CTestCase(void);
void doTest();
};
//TestCase.Cpp
#include 'StdAfx.h'
#include '.\testcase.h'
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CTestCase , CTestCase::getSuiteName() );
CTestCase::CTestCase(void)
{
}
CTestCase::~CTestCase(void)
{
}
void CTestCase::doTest()
{
}
std::string CTestCase::getSuiteName()
{
return 'TDDExample3';
}
CppUnit::Test* CTestCase::getSuite()
{
CppUnit::TestFactoryRegistry& reg =
CppUnit::TestFactoryRegistry::getRegistry( CTestCase::getSuiteName() );
return reg.makeTest();
}
//Unit_Test.cpp
#include
#include 'TestCase.h'
BOOL CUnit_TestApp::InitInstance()
{
InitCommonControls();
CWinApp::InitInstance();
AfxEnableControlContainer();
CppUnit::MfcUi::TestRunner runner;
runner.addTest(CTestCase::getSuite());
runner.run();
return FALSE;
}
现在编译,运行, 如图一:
进度条呈现绿色, 表示我们此次测试通过. 事实上我们的doTest是一个空函数,什么代码也没有写,当然能通过了. :)
下面我们在doTest()中加点代码进去.
char* p = NULL; p[0]='c';
这段代码会引起一个访问异常. 看UnitCpp怎么来报告这段代码的错误.
编译,运行如图2:
这时CppUnit会告诉我们dotest出现一个错误. 并提供了错误的一些信息.
这样我们用CppUnit搭建一个简单单元测试框架.
参考文章:
- 作者: suyong5968 2006年09月7日, 星期四 10:34 回复(0) | 引用(1) 加入博采
单元测试
经过系统的几个月测试,对测试有一定的了解,发现单元测试是测试一个非常重要的一部分,于是对单元测试发生了兴趣,通过阅读资料、实践。对单元测试进行了一些总结,希望能够对大家有些帮助,得到抛砖引玉的作用。
单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。例如,你可能把一个很大的值放入一个有序list 中去,然后确认该值出现在list 的尾部。或者,你可能会从字符串中删除匹配某种模式的字符,然后确认字符串确实不再包含这些字符了。
编写代码时,一定会反复调试保证它能够编译通过。如果是编译没有通过的代码,没有任何人会愿意交付给自己的老板。但代码通过编译,只是说明了它的语法正确;却无法保证它的语义也一定正确,没有任何人可以轻易承诺这段代码的行为一定是正确的。
幸运,单元测试会为我们的承诺做保证。编写单元测试就是用来验证这段代码的行为是否与我们期望的一致。有了单元测试,我们可以自信的交付自己的代码,而没有任何的后顾之忧。但我们对单元测试也有一些认识的误区,误区有哪些呢?
1、 编写单元测试太花时间了
在开发时越早发现BUG,就能节省更多的时间,降低更多的风险。如果不及时修改错误,一后期发现的错误,调试修改很困难,更浪费时间;二是维护越多,代码的结构越乱,甚至改变当初的实际思路。
2、 运行测试的时间太长了
合适的测试是不会让这种情况发生的。实际上,大多数测试的执行都是非常快的,因此你在几秒之内就可以运行成千上万个测试。但是有时某些测试会花费很长的时间。这时,需要把这些耗时的测试和其他测试分开。通常可以每天运行这种测试一次,或者几天一次。
3、 测试代码并不是我的工作
工作就是保证代码能够正确的完成,恰恰相反,测试代码是不可缺少的工作。
4、 不清楚代码的行为,所以也就无从测试
如果实在不清楚代码的行为,那么估计现在并不是编码的时候。如果并不知道代码的行为,那么你又如何知道你编写的代码是正确的呢?
5、 但是这些代码都能够编译通过
代码通过编译只是验证它的语法通过,但并不能保证它的行为就一定正确。
6、 项目进度吃紧时少做些测试,时间富裕时多做测试
这是不重视软件测试的表现,也是软件项目过程管理混乱的表现,必然会降低软件测试的质量。一个软件项目的顺利实现需要有合理的项目进度计划,其中包括合理的测试计划,对项目实施过程中的任何问题,都要有风险分析和相应的对策,不要因为开发进度的延期而简单的缩短测试时间、人力和资源。因为缩短测试时间带来的测试不完整,对项目质量的下降引起的潜在风险,往往造成更大的浪费。克服这种现象的最好办法是加强软件过程的计划和控制,包括软件测试计划、测试设计、测试执行、测试度量和测试控制。
7、 软件测试是没有前途的工作,只有程序员才是软件高手
项目的成功往往靠个别全能程序员决定,他们负责总体设计和程序详细设计,认为软件开发就是编写代码,给人的印象往往是程序员是真正的牛人,具有很高的地位和待遇。因此,在这种环境下,软件测试很不受重视,软件测试人员的地位和待遇自然就很低了,甚至软件测试变得可有可无。随着市场对软件质量的不断提高,软件测试将变得越来越重要,相应的软件测试人员的地位和待遇将会逐渐提高。在微软等软件过程比较规范的大公司,软件测试人员的数量和待遇与程序员没有多大差别,优秀测试人员的待遇甚至比程序员还要高。软件测试将会成为一个具有很大发展前景的行业,软件测试大有前途,市场需要更多具有丰富测试技术和管理经验的测试人员,他们同样是软件专家。
1、它是一种验证行为。
程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支缓。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。
2、它是一种设计行为。
编写单元测试将使我们从调用者观察、思考。特别是先写测试(test-first),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。
3、它是一种编写文档的行为。
单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。
4、它具有回归性。
自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随的快速运行测试。
单元测试越早越好,早到什么程度?XP开发理论讲究TDD,即测试驱动开发,先编写测试代码,再进行开发。在实际的工作中,可以不必过分强调先什么后什么,重要的是高效和感觉舒适。从经验来看,先编写产品函数的框架,然后编写测试函数,针对产品函数的功能编写测试用例,然后编写产品函数的代码,每写一个功能点都运行测试,随时补充测试用例。所谓先编写产品函数的框架,是指先编写函数空的实现,有返回值的随便返回一个值,编译通过后再编写测试代码,这时,函数名、参数表、返回类型都应该确定下来了,所编写的测试代码以后需修改的可能性比较小。
单元测试与其他测试不同,单元测试可看作是编码工作的一部分,应该由程序员完成,也就是说,经过了单元测试的代码才是已完成的代码,提交产品代码时也要同时提交测试代码。测试部门可以作一定程度的审核。
单元测试任务包括:1 模块接口测试;2 模块局部数据结构测试;3 模块边界条件测试;4 模块中所有独立执行通路测试;5 模块的各条错误处理通路测试。
模块接口测试是单元测试的基础。只有在数据能正确流入、流出模块的前提下,其他测试才有意义。测试接口正确与否应该考虑下列因素:
1 、输入的实际参数与形式参数的个数是否相同;
2、 输入的实际参数与形式参数的属性是否匹配;
3 、输入的实际参数与形式参数的量纲是否一致;
4、 调用其他模块时所给实际参数的个数是否与被调模块的形参个数相同;
5、 调用其他模块时所给实际参数的属性是否与被调模块的形参属性匹配;
6、调用其他模块时所给实际参数的量纲是否与被调模块的形参量纲一致; 7、 调用预定义函数时所用参数的个数、属性和次序是否正确; 8 、是否存在与当前入口点无关的参数引用; 9、 是否修改了只读型参数; 10 、对全程变量的定义各模块是否一致; 11、是否把某些约束作为参数传递。 如果模块内包括外部输入输出,还应该考虑下列因素: 2、 OPEN/CLOSE语句是否正确; 3、 格式说明与输入输出语句是否匹配; 4、缓冲区大小与记录长度是否匹配; 5、文件使用前是否已经打开; 6、是否处理了文件尾; 7、是否处理了输入/输出错误; 8、输出信息中是否有文字性错误; 检查局部数据结构是为了保证临时存储在模块内的数据在程序执行过程中完整、正确。局部数据结构往往是错误的根源,应仔细设计测试用例,力求发现下面几类错误: 1 、不合适或不相容的类型说明; 2、变量无初值; 3、变量初始化或省缺值有错; 4、不正确的变量名(拼错或不正确地截断); 5出现上溢、下溢和地址异常。 除了局部数据结构外,如果可能,单元测试时还应该查清全局数据(例如FORTRAN的公用区)对模块的影响。 在模块中应对每一条独立执行路径进行测试,单元测试的基本任务是保证模块中每条语句至少执行一次。此时设计测试用例是为了发现因错误计算、不正确的比较和不适当的控制流造成的错误。此时基本路径测试和循环测试是最常用且最有效的测试技术。计算中常见的错误包括: 1、 误解或用错了算符优先级; 2、混合类型运算; 3、变量初值错; 4、精度不够; 5、表达式符号错。 比较判断与控制流常常紧密相关,测试用例还应致力于发现下列错误: 1、不同数据类型的对象之间进行比较; 2、错误地使用逻辑运算符或优先级; 了解了单元测试的任务,下面介绍测试用例,测试用例也是单元测试的核心,决定你的单元测试是否成功。测试用例的核心是输入数据。预期输出是依据输入数据和程序功能来确定的,也就是说,对于某一程序,输入数据确定了,预期输出也就可以确定了,至于生成/销毁被测试对象和运行测试的语句,是所有测试用例都大同小异的。 单元测试测试用例一般采用逻辑覆盖法和基本路径法进行设计。 逻辑覆盖是以程序内部的逻辑结构为基础的测试用例设计技术,这一方法要求测试人员对程序的逻辑结构有清楚的了解。逻辑覆盖可分为:语句覆盖、判定覆盖、条件覆盖、判定-条件覆盖、条件组合覆盖与路径覆盖。 1. 语句覆盖就是设计若干个测试用例,运行所测程序,使得每一可执行语句至少执行一次。 2. 判定覆盖就是设计若干个测试用例,运行所测程序,使得程序中每个判断的取真分支和取假分支至少经历一次。 3. 条件覆盖就是设计若干个测试用例,运行所测程序,使得程序中每个判断的每个条件的可能取值至少执行一次。 4. 判定--条件覆盖就是设计足够的测试用例,使得判断中每个条件的所有可能取值至少执行一次,同时每个判断的所有可能判断结果也至少执行一次。 5. 条件组合覆盖就是设计足够的测试用例,运行所测程序,使得每个判断的所有可能的条件取值组合至少执行一次。 6. 路径测试就是设计足够的测试用例,覆盖程序中所有可能的路径。 基本路径测试法是在程序控制流图的基础上,通过分析控制构造的环路复杂性,导出基本可执行路径集合,从而设计测试用例的方法。设计出的测试用例要保证在测试中程序的每个可执行语句至少执行一次。基本路径测试法包括以下5个方面: 1. 程序的控制流图:描述程序控制流的一种图示方法。 2. 程序环境复杂性:McCabe复杂性度量;从程序的环路复杂性可导出程序基本路径集合中的独立路径条数,这是确定程序中每个可执行语句至少执行依次所必须的测试用例数目的上界。 3. 导出测试用例。 4. 准备测试用例,确保基本路径集中的每一条路径的执行。 5. 图形矩阵:是在基本路径测试中起辅助作用的软件工具,利用它可以实现自动地确定一个基本路径集。 好久前看过一篇非常不错的单元测试用例设计案例,但没有保留下来,感觉好可惜。一个好的单元测试案例对单元测试的入门是非常重要,希望这个案例能够帮助你对单元测试一些了解和对单元测试重要性的一个认识。 例如币种换算单元函数,要对这个函数进行单元测试。应该怎么去做个函数的单元测试呢?首先考虑的是该币种换算单元函数的输入输出参数,其次是输出结果与期望值是否相等;再次建立单元测试处理类,并相应建立测试该函数的测试方法,按照JUnit规范一般是test****()。 开始准备建立单元测试类及其测试方法,币种换算函数的输入参数为:目标币种,转换币种,金额,输出为转换后目标币种的金额。建立第一个简单的单元测试处理类及单元测试测试方法: Public BigDecimal testCurryChange(){ 引用币种换算函数并输入测试参数,假如测试参数为:目标币种为人民币,转换币种为美元,金额为100.00。 BigDecimal bgResult = 799.95; If(bdResult.equals(函数(”10”,”32”,100.00)== false){ Throw new Exception(“转换错误!”); } } 第一个单元测试函数已经写完毕,可以进行单元测试,但不能完全测试币种换算函数是否完全正确,第一金额的不同转换结构可能不同;第二不同币种之间转换,结果不同。那下一步应该怎么做呢? 可以采用这样的方法,建立同上的多个函数,但输入参数不一样,如金额不一样;目标币种与转换币种不一样。这样也可以在不同的方向来检测币种换算函数是否正确。一般情况下对于这种情况下,金额输入参数处理会才用边界发,取一个最小值和最大值作为输入参数进行检验,另外是看实际情况,要换算多少中币种,进行穷举法,把所有的币种进行处理,根据金额,币种的组合分别进行测试。这儿就不一一写出代码。 但是上述方法虽然是一个可行的方法,但处理起来比较复杂,如果币种换算比较多,合起来要写很多的单元测试方法,用起来比较烦琐,那应该怎么处理呢?在这种情况下可以采用数组的方法+循环的方法来实现,这样就很容易避开上述方法的烦琐。具体实现就不列举出来,大家可以自行研究一下。 写单元测试也是一个非常有趣的事情,上述方法在不同的情况都有可能实现,不是每一个单元测试案例都要追求最完美,能够达到要求和实现目标即可。还有很多写测试案例的方法就不一一列举,大家可以根据实际情况而使用最好的方法。 不同的开发语言,单元测试的工具也不一样。JAVA语言开发系统:目前比较流行的是JUnit;.Net语言开发系统:目前比较流行的是:NUnit;C/C++语言开发系统:目前比较流行的是CPPUnit。目前系统开发是用的JAVA,于是对JUnit研究的比较多,有机会可以和大家共同探讨一下,其他的如果有兴趣,大家也可以自行研究。还有很多的单元测试工具,可以根据具体情况而选择合适自己的单元测试工具。 JUnit的简介: http://blog.csdn.net/askmyself/archive/2005/10/10/498599.aspx NUnit的简介: http://blog.csdn.net/litp/archive/2005/11/11/527684.aspx http://blog.csdn.net/litp/archive/2005/11/11/527684.aspx CPPUnit的简介: http://blog.csdn.net/lzw2005/archive/2005/09/23/488013.aspx http://blog.csdn.net/casualgame/archive/2005/03/27/332162.aspx 通过单元测试的阅读和了解,希望能够把单元测试工具JUnit引入到目前的系统开发中来,而在实践过程中发现并不是十分可行,在进行测试的参数中并不能很好地获得一个数据库连接。于是通过别的方法来代替JUnit而达到单元测试,自己编写一个或多个单元测试业务处理类+Log4j(日志记录)也同样能够实现单元测试,相对来说这样比较麻烦,要自己写对象的创建释放、测试业务处理等等。在实际情况中,或者能够同样遇到这样的问题,希望大家能够在实践过程中找到自己合适的方法,而不是一定要采用单元测试工具。 总而言之,单元测试会让我们的开发工作变得更加轻松,让我们对自己的代码更加自信。无论是大型项目还是小型项目,无论是时间紧迫的项目还是时间宽裕的项目,只要代码不是一次写完永不改动,编写单元测试就一定超值,它已成为我们编码不可缺少的一部分。 Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=562609
1 、文件属性是否正确;
3、因计算机表示的局限性,期望理论上相等而实际上不相等的两个量相等;
4、比较运算或变量出错;
5、循环终止条件或不可能出现;
6、迭代发散时不能退出;
7、错误地修改了循环变量。
一个好的设计应能预见各种出错条件,并预设各种出错处理通路,出错处理通路同样需要认真测试,测试应着重检查下列问题:
1、输出的出错信息难以理解;
2、记录的错误与实际遇到的错误不相符;
3、在程序自定义的出错处理段运行之前,系统已介入;
4、异常处理不当;
5、错误陈述中未能提供足够的定位出错信息。
边界条件测试是单元测试中最后,也是最重要的一项任务。众的周知,软件经常在边界上失效,采用边界值分析技术,针对边界值及其左、右设计测试用例,很有可能发现新的错误。 7. 单元测试用例及用例设计
7.1. 逻辑覆盖法
7.2. 基本路径法
7.3. 单元测试用例设计案例
8. 单元测试工具介绍
9. 总结
VC编程实现IE风格的界面
- 作者: suyong5968 2006年08月1日, 星期二 17:01 回复(1) | 引用(1) 加入博采
检测和删除系统中的木马(Trojan Horse)教程
- 作者: suyong5968 2006年07月28日, 星期五 15:03 回复(0) | 引用(1)/a> 加入博采
C#的office操作[word&Excel]
C#实现对Word文件读写
手头上的一个项目报表相对比较简单,所以报表打印采用VBA引擎,通过定制Word模版,然后根据模版需要填充数据,然后OK,打印即可。
实现方法:首先需要引用VBA组建,我用的是Office2003 Professional,Dll版本号为Microsoft Word11.0

另外当然还需要引用Interop.Word.Dll.
代码如下:
///#region 打开Word文档,并且返回对象wDoc,wDoc
///
/// 打开Word文档,并且返回对象wDoc,wDoc
///
/// 完整Word文件路径+名称
/// 返回的Word.Document wDoc对象
/// 返回的Word.Application对象
public static void CreateWordDocument(string FileName,ref Word.Document wDoc,ref Word.Application WApp)
{
if(FileName == "") return;
Word.Document thisDocument = null;
Word.FormFields formFields = null;
Word.Application thisApplication = new Word.ApplicationClass();
thisApplication.Visible = true;
thisApplication.Caption = "";
thisApplication.Options.CheckSpellingAsYouType = false;
thisApplication.Options.CheckGrammarAsYouType = false;
Object filename = FileName;
Object ConfirmConversions = false;
Object ReadOnly = true;
Object AddToRecentFiles = false;
Object PasswordDocument = System.Type.Missing;
Object PasswordTemplate = System.Type.Missing;
Object Revert = System.Type.Missing;
Object WritePasswordDocument = System.Type.Missing;
Object WritePasswordTemplate = System.Type.Missing;
Object Format = System.Type.Missing;
Object Encoding = System.Type.Missing;
Object Visible = System.Type.Missing;
Object OpenAndRepair = System.Type.Missing;
Object DocumentDirection = System.Type.Missing;
Object NoEncodingDialog = System.Type.Missing;
Object XMLTransform = System.Type.Missing;
try
{
Word.Document wordDoc =
thisApplication.Documents.Open(ref filename, ref ConfirmConversions,
ref ReadOnly, ref AddToRecentFiles, ref PasswordDocument, ref PasswordTemplate,
ref Revert,ref WritePasswordDocument, ref WritePasswordTemplate, ref Format,
ref Encoding, ref Visible, ref OpenAndRepair, ref DocumentDirection,
ref NoEncodingDialog, ref XMLTransform );
thisDocument = wordDoc;
wDoc = wordDoc;
WApp = thisApplication;
formFields = wordDoc.FormFields;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
#endregion
http://ike.126.com
调用上面静态方法,打开目标文件并且把DataGrid中数据填充到对应Word标签中去
///#region Word填充数据(For Example)
///
/// Word填充数据
///
private void WordLoadData()
{
Word.Document wDoc=null;
Word.Application wApp=null;
sysFun.CreateWordDocument("E:\\监测报告(new).dot",ref wDoc,ref wApp);
//对标签"C"进行填充
object bkmC="C";
if(wApp.ActiveDocument.Bookmarks.Exists("C") == true)
{
wApp.ActiveDocument.Bookmarks.get_Item
(ref bkmC).Select();
}
wApp.Selection.TypeText(this.txt1.Text);
object bkmG = "TWaterTable3";
object unit;
object count; //移动数
object extend;
extend = Word.WdMovementType.wdExtend;
unit = Word.WdUnits.wdCell;
//把DataGrid中数据填充到标签TWaterTable3上
if(wApp.ActiveDocument.Bookmarks.Exists("TWaterTable3") == true)
{
wApp.ActiveDocument.Bookmarks.get_Item
(ref bkmG).Select();
for(int i=0;i {
if(i==0)
{
count=1;
}
else
{
count=0;
}
//需填充5列数据
wApp.Selection.Move(ref unit,ref count);
wApp.Selection.TypeText(gridEX1.GetRow(i).Cells[0].Text);
count=1;
wApp.Selection.Move(ref unit,ref count);
wApp.Selection.TypeText(gridEX1.GetRow(i).Cells[1].Text);
wApp.Selection.Move(ref unit,ref count);
wApp.Selection.TypeText(gridEX1.GetRow(i).Cells[2].Text);
wApp.Selection.Move(ref unit,ref count);
wApp.Selection.TypeText(gridEX1.GetRow(i).Cells[3].Text);
wApp.Selection.Move(ref unit,ref count);
wApp.Selection.TypeText(gridEX1.GetRow(i).Cells[4].Text);
//换行
wApp.Selection.MoveRight(ref unit,ref count,ref extend);
}
}
}
#endregion
然后就OK了,在对标签表控制要注意列循环和换行.
C#操作Excel(导入导出)
有很多朋友说需要C#导出到Excel的代码,现共享给大家
///
/// 读取Excel文档
///
/// 文件名称
/// 返回一个数据集
public DataSet ExcelToDS(string Path)
{
string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" +"Data Source="+ Path +";"+"Extended Properties=Excel 8.0;";
OleDbConnection conn = new OleDbConnection(strConn);
conn.Open();
string strExcel = "";
OleDbDataAdapter myCommand = null;
DataSet ds = null;
strExcel="select * from [sheet1$]";
myCommand = new OleDbDataAdapter(strExcel, strConn);
ds = new DataSet();
myCommand.Fill(ds,"table1");
return ds;
}
///
/// 写入Excel文档
///
/// 文件名称
public bool SaveFP2toExcel(string Path)
{
try
{
string strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" +"Data Source="+ Path +";"+"Extended Properties=Excel 8.0;";
OleDbConnection conn = new OleDbConnection(strConn);
conn.Open();
System.Data.OleDb.OleDbCommand cmd=new OleDbCommand ();
cmd.Connection =conn;
//cmd.CommandText ="UPDATE [sheet1$] SET 姓名='2005-01-01' WHERE 工号='日期'";
//cmd.ExecuteNonQuery ();
for(int i=0;i {
if(fp2.Sheets [0].Cells[i,0].Text!="")
{
cmd.CommandText ="INSERT INTO [sheet1$] (工号,姓名,部门,职务,日期,时间) VALUES('"+fp2.Sheets [0].Cells[i,0].Text+ "','"+
fp2.Sheets [0].Cells[i,1].Text+"','"+fp2.Sheets [0].Cells[i,2].Text+"','"+fp2.Sheets [0].Cells[i,3].Text+
"','"+fp2.Sheets [0].Cells[i,4].Text+"','"+fp2.Sheets [0].Cells[i,5].Text+"')";
cmd.ExecuteNonQuery ();
}
}
conn.Close ();
return true;
}
catch(System.Data.OleDb.OleDbException ex)
{
System.Diagnostics.Debug.WriteLine ("写入Excel发生错误:"+ex.Message );
}
return false;
}
这种方法是相当有效的。
http://ike.126.com