2006年12月23日土曜日
在Visual C++2005中调用GCC/G++编译单文件
在VC2005中不能编译单个cpp文件,必须把它放在工程中。但是,我们可以通过调用外部工具实现编译单文件。下面以调用GCC/G++为例说明如何编译cpp文件。
首先下载Dev C++, 目前最新版为4.9.9.2。安装 Dev C++的同时就安装了MinGW,其中包含了 GCC 3.4.2,
假设 Dev C++ 安装在 D:\Dev-Cpp 目录下。
下面我们在VC2005的菜单点击Tools -> External Tools -> Add
分别填入:
Title: GNU C++ Compiler
Command: D:\Dev-Cpp\bin\g++.exe
Arguments: -Wall -o $(ItemFileName) $(ItemFileName)$(ItemExt)
Initial directory: $(ItemDir)
再钩选下面的 Use Output window.
好,下面切换到main函数所在文件,点击 Tools菜单下的GNU C++ Compiler,
如果编译没有出错,Output window是没有任何输出的。当然,你可以修改命令参数,使Output window输出其他编译信息。
如果编译出错,那么错误提示比VC编译器的提示要智能的多,有助于迅速找出问题所在
VC线程同步技术剖析
关键词: VC++6.0; 线程同步;临界区;事件;互斥;信号量;
正文
使线程同步
在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作。更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解。正常情况下对这种处理结果的了解应当在其处理任务完成后进行。
如果不采取适当的措施,其他线程往往会在线程处理任务结束前就去访问处理结果,这就很有可能得到有关处理结果的错误了解。例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。
为了确保读线程读取到的是经过修改的变量,就必须在向变量写入数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。象这种保证线程能了解其他线程任务处理结束后的处理结果而采取的保护措施即为线程同步。
线程同步是一个非常大的话题,包括方方面面的内容。从大的方面讲,线程的同步可分用户模式的线程同步和内核对象的线程同步两大类。用户模式中线程的同步方法主要有原子访问和临界区等方法。其特点是同步速度特别快,适合于对线程运行速度有严格要求的场合。
内核对象的线程同步则主要由事件、等待定时器、信号量以及信号灯等内核对象构成。由于这种同步机制使用了内核对象,使用时必须将线程从用户模式切换到内核模式,而这种转换一般要耗费近千个CPU周期,因此同步速度较慢,但在适用性上却要远优于用户模式的线程同步方式。
临界区
临界区(Critical Section)是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。
临界区在使用时以CRITICAL_SECTION结构对象保护共享资源,并分别用 EnterCriticalSection()和LeaveCriticalSection()函数去标识和释放一个临界区。所用到的 CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有被破坏的可能。
图1 使用临界区保持线程同步
下面通过一段代码展示了临界区在保护多线程访问的共享资源中的作用。通过两个线程来分别对全局变量g_cArray [10]进行写入操作,用临界区结构对象g_cs来保持线程的同步,并在开启线程前对其进行初始化。为了使实验效果更加明显,体现出临界区的作用,在线程函数对共享资源g_cArray[10]的写入时,以Sleep()函数延迟1毫秒,使其他线程同其抢占CPU的可能性增大。如果不使用临界区对其进行保护,则共享资源数据将被破坏(参见图1(a)所示计算结果),而使用临界区对线程保持同步后则可以得到正确的结果(参见图1(b)所示计算结果)。代码实现清单附下:
// 临界区结构对象
CRITICAL_SECTION g_cs;
// 共享资源
char g_cArray[10];
UINT ThreadProc10(LPVOID pParam)
{
// 进入临界区
EnterCriticalSection(&g_cs);
// 对共享资源进行写入操作
for (int i = 0; i < 10; i++)
{
g_cArray[i] = 'a';
Sleep(1);
}
// 离开临界区
LeaveCriticalSection(&g_cs);
return 0;
}
UINT ThreadProc11(LPVOID pParam)
{
// 进入临界区
EnterCriticalSection(&g_cs);
// 对共享资源进行写入操作
for (int i = 0; i < 10; i++)
{
g_cArray[10 - i - 1] = 'b';
Sleep(1);
}
// 离开临界区
LeaveCriticalSection(&g_cs);
return 0;
}
……
void CSample08View::OnCriticalSection()
{
// 初始化临界区
InitializeCriticalSection(&g_cs);
// 启动线程
AfxBeginThread(ThreadProc10, NULL);
AfxBeginThread(ThreadProc11, NULL);
// 等待计算完毕
Sleep(300);
// 报告计算结果
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}
在使用临界区时,一般不允许其运行时间过长,只要进入临界区的线程还没有离开,其他所有试图进入此临界区的线程都会被挂起而进入到等待状态,并会在一定程度上影响。程序的运行性能。尤其需要注意的是不要将等待用户输入或是其他一些外界干预的操作包含到临界区。如果进入了临界区却一直没有释放,同样也会引起其他线程的长时间等待。换句话说,在执行了EnterCriticalSection()语句进入临界区后无论发生什么,必须确保与之匹配的LeaveCriticalSection()都能够被执行到。可以通过添加结构化异常处理代码来确保 LeaveCriticalSection()语句的执行。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。
MFC为临界区提供有一个CCriticalSection类,使用该类进行线程同步处理是非常简单的,只需在线程函数中用 CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片段即可。对于上述代码,可通过 CCriticalSection类将其改写如下:
// MFC临界区类对象
CCriticalSection g_clsCriticalSection;
// 共享资源
char g_cArray[10];
UINT ThreadProc20(LPVOID pParam)
{
// 进入临界区
g_clsCriticalSection.Lock();
// 对共享资源进行写入操作
for (int i = 0; i < 10; i++)
{
g_cArray[i] = 'a';
Sleep(1);
}
// 离开临界区
g_clsCriticalSection.Unlock();
return 0;
}
UINT ThreadProc21(LPVOID pParam)
{
// 进入临界区
g_clsCriticalSection.Lock();
// 对共享资源进行写入操作
for (int i = 0; i < 10; i++)
{
g_cArray[10 - i - 1] = 'b';
Sleep(1);
}
// 离开临界区
g_clsCriticalSection.Unlock();
return 0;
}
……
void CSample08View::OnCriticalSectionMfc()
{
// 启动线程
AfxBeginThread(ThreadProc20, NULL);
AfxBeginThread(ThreadProc21, NULL);
// 等待计算完毕
Sleep(300);
// 报告计算结果
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}
管理事件内核对象
在前面讲述线程通信时曾使用过事件内核对象来进行线程间的通信,除此之外,事件内核对象也可以通过通知操作的方式来保持线程的同步。对于前面那段使用临界区保持线程同步的代码可用事件对象的线程同步方法改写如下:
// 事件句柄
HANDLE hEvent = NULL;
// 共享资源
char g_cArray[10];
……
UINT ThreadProc12(LPVOID pParam)
{
// 等待事件置位
WaitForSingleObject(hEvent, INFINITE);
// 对共享资源进行写入操作
for (int i = 0; i < 10; i++)
{
g_cArray[i] = 'a';
Sleep(1);
}
// 处理完成后即将事件对象置位
SetEvent(hEvent);
return 0;
}
UINT ThreadProc13(LPVOID pParam)
{
// 等待事件置位
WaitForSingleObject(hEvent, INFINITE);
// 对共享资源进行写入操作
for (int i = 0; i < 10; i++)
{
g_cArray[10 - i - 1] = 'b';
Sleep(1);
}
// 处理完成后即将事件对象置位
SetEvent(hEvent);
return 0;
}
……
void CSample08View::OnEvent()
{
// 创建事件
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// 事件置位
SetEvent(hEvent);
// 启动线程
AfxBeginThread(ThreadProc12, NULL);
AfxBeginThread(ThreadProc13, NULL);
// 等待计算完毕
Sleep(300);
// 报告计算结果
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}
在创建线程前,首先创建一个可以自动复位的事件内核对象hEvent,而线程函数则通过 WaitForSingleObject()等待函数无限等待hEvent的置位,只有在事件置位时WaitForSingleObject()才会返回,被保护的代码将得以执行。对于以自动复位方式创建的事件对象,在其置位后一被WaitForSingleObject()等待到就会立即复位,也就是说在执行ThreadProc12()中的受保护代码时,事件对象已经是复位状态的,这时即使有ThreadProc13()对CPU的抢占,也会由于 WaitForSingleObject()没有hEvent的置位而不能继续执行,也就没有可能破坏受保护的共享资源。在ThreadProc12()中的处理完成后可以通过SetEvent()对hEvent的置位而允许ThreadProc13()对共享资源g_cArray的处理。这里 SetEvent()所起的作用可以看作是对某项特定任务完成的通知。
使用临界区只能同步同一进程中的线程,而使用事件内核对象则可以对进程外的线程进行同步,其前提是得到对此事件对象的访问权。可以通过OpenEvent()函数获取得到,其函数原型为:
HANDLE OpenEvent(
DWORD dwDesiredAccess, // 访问标志
BOOL bInheritHandle, // 继承标志
LPCTSTR lpName // 指向事件对象名的指针
);
如果事件对象已创建(在创建事件时需要指定事件名),函数将返回指定事件的句柄。对于那些在创建事件时没有指定事件名的事件内核对象,可以通过使用内核对象的继承性或是调用DuplicateHandle()函数来调用CreateEvent()以获得对指定事件对象的访问权。在获取到访问权后所进行的同步操作与在同一个进程中所进行的线程同步操作是一样的。
如果需要在一个线程中等待多个事件,则用WaitForMultipleObjects()来等待。WaitForMultipleObjects()与 WaitForSingleObject()类似,同时监视位于句柄数组中的所有句柄。这些被监视对象的句柄享有平等的优先权,任何一个句柄都不可能比其他句柄具有更高的优先权。WaitForMultipleObjects()的函数原型为:
DWORD WaitForMultipleObjects(
DWORD nCount, // 等待句柄数
CONST HANDLE *lpHandles, // 句柄数组首地址
BOOL fWaitAll, // 等待标志
DWORD dwMilliseconds // 等待时间间隔
);
参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。 fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。dwMilliseconds在饫锏淖饔糜朐赪aitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1中的某个值,则说明所有指定对象的状态均为已通知状态(当fWaitAll为TRUE时)或是用以减去WAIT_OBJECT_0而得到发生通知的对象的索引(当fWaitAll 为FALSE时)。如果返回值在WAIT_ABANDONED_0与WAIT_ABANDONED_0+nCount-1之间,则表示所有指定对象的状态均为已通知,且其中至少有一个对象是被丢弃的互斥对象(当fWaitAll为TRUE时),或是用以减去WAIT_OBJECT_0表示一个等待正常结束的互斥对象的索引(当fWaitAll为FALSE时)。下面给出的代码主要展示了对WaitForMultipleObjects()函数的使用。通过对两个事件内核对象的等待来控制线程任务的执行与中途退出:
// 存放事件句柄的数组
HANDLE hEvents[2];
UINT ThreadProc14(LPVOID pParam)
{
// 等待开启事件
DWORD dwRet1 = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
// 如果开启事件到达则线程开始执行任务
if (dwRet1 == WAIT_OBJECT_0)
{
AfxMessageBox("线程开始工作!");
while (true)
{
for (int i = 0; i < 10000; i++);
// 在任务处理过程中等待结束事件
DWORD dwRet2 = WaitForMultipleObjects(2, hEvents, FALSE, 0);
// 如果结束事件置位则立即终止任务的执行
if (dwRet2 == WAIT_OBJECT_0 + 1)
break;
}
}
AfxMessageBox("线程退出!");
return 0;
}
……
void CSample08View::OnStartEvent()
{
// 创建线程
for (int i = 0; i < 2; i++)
hEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
// 开启线程
AfxBeginThread(ThreadProc14, NULL);
// 设置事件0(开启事件)
SetEvent(hEvents[0]);
}
void CSample08View::OnEndevent()
{
// 设置事件1(结束事件)
SetEvent(hEvents[1]);
}
MFC为事件相关处理也提供了一个CEvent类,共包含有除构造函数外的4个成员函数 PulseEvent()、ResetEvent()、SetEvent()和UnLock()。在功能上分别相当与Win32 API的PulseEvent()、ResetEvent()、SetEvent()和CloseHandle()等函数。而构造函数则履行了原 CreateEvent()函数创建事件对象的职责,其函数原型为:
CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
按照此缺省设置将创建一个自动复位、初始状态为复位状态的没有名字的事件对象。封装后的CEvent类使用起来更加方便,图2即展示了CEvent类对A、B两线程的同步过程:
图2 CEvent类对线程的同步过程示意
B线程在执行到CEvent类成员函数Lock()时将会发生阻塞,而A线程此时则可以在没有B线程干扰的情况下对共享资源进行处理,并在处理完成后通过成员函数SetEvent()向B发出事件,使其被释放,得以对A先前已处理完毕的共享资源进行操作。可见,使用 CEvent类对线程的同步方法与通过API函数进行线程同步的处理方法是基本一致的。前面的API处理代码可用CEvent类将其改写为:
// MFC事件类对象
CEvent g_clsEvent;
UINT ThreadProc22(LPVOID pParam)
{
// 对共享资源进行写入操作
for (int i = 0; i < 10; i++)
{
g_cArray[i] = 'a';
Sleep(1);
}
// 事件置位
g_clsEvent.SetEvent();
return 0;
}
UINT ThreadProc23(LPVOID pParam)
{
// 等待事件
g_clsEvent.Lock();
// 对共享资源进行写入操作
for (int i = 0; i < 10; i++)
{
g_cArray[10 - i - 1] = 'b';
Sleep(1);
}
return 0;
}
……
void CSample08View::OnEventMfc()
{
// 启动线程
AfxBeginThread(ThreadProc22, NULL);
AfxBeginThread(ThreadProc23, NULL);
// 等待计算完毕
Sleep(300);
// 报告计算结果
CString sResult = CString(g_cArray);
AfxMessageBox(sResult);
}
VC调试器应用-高级断点
一.高级断点语法
高级断点语法由两部分组成:1.上下文部分.2.位置,表达式,变量或Windows消息条件.
用函数,源文件和二进制模块来指定上下文,上下文的表示方法:
{[函数],[源文件],[二进制模块]}
必须指定唯一的,足够的上下文信息才能获取断点位置.如在TEST.CPP的20行设一位置断点,语法为:{,TEST.CPP,}.20,如A.DLL或 B.DLL都使用了该行,又只想在B.DLL的调用中触发,则必须使用:{,TEST.CPP,B.DLL}.20.
VC调试器中可直接输入上下文语法:Breakpoints对话框的Location选项卡BreakAt编辑框中.更容易的方法是使用BreatAt框右的箭头打开菜单,选择Advanced项,然后在Context框中输入断点的相应信息.
如想在一个绝对地址上中断,直接在BreakAt框中输入地址就行.
二.任何函数上快速中断
将函数名输入BreadAt框中.如果是C++代码,同时还需要类限定符.支持重载了的函数,调试器会列出所有满足条件的函数供选择,如输入时提供足够的信息,完全可略过选择过程.如输入:"CString::operator=(const char *)"可唯一确定要中断的函数.
三.在系统或DLL输出的函数中设置断点
在程序中从DLL输入的函数中设置一个断点可能是毫无作用的,调试器需要知道在何处可以找到该函数上下文信息,同时,函数名取决于是否加载了DLL的符号.只有在W2K以上版本中才能在系统DLL中设置断点--原因在于其它系统没有提供边写入边复制保护的功能,若一定要启用这种方法,必须要有COFF(Common Object File Format),并在调试器中输出启动的装载----在Options对话框的Debug页,将Load COFF & Exports选中.
VC调试器用分级的符号信息法,完整的符号的级别高于不太完整的.PDB(Program Database)文件具有所有可能的源码行,函数,变量和类型信息,优先级便高于COFF/DBG文件,后者只有公用函数符号,而COFF/DBG文件高于输出名称,输入的名称是一种伪符号.
调试时,如DEBUG窗口输出:装载DLL的符号,则说明符号已被装入;否则说明没有装载DLL的符号.
没有装入符号时,使用的位置字符串是DLL输出的名称,可能用DUMPBIN程序查看这个名称:DUMPBIN /EXPORTS DLLname.例:在LoadLibraryA中设置中断:"{,,Kernel32.dll}LoadLibraryA".
如装入了符号,则要根据输出函数和调用协议来计算函数名.如上例,LoadLibraryA使用__stdcall调用协议,据该协议,函数名以下划线为前缀,所跟有进栈的字节数为后缀的@号.一般说来,参数个数*4,就是参数占用栈空间的总字节数,LoadLibary的名称便是:_LoadLibraryA@4,故最后的语法是:或"{,,Kernel32.dll}_LoadLibraryA@4"
附:常用的调用协议
1、__stdcall调用约定相当于16位动态库中经常使用的PASCAL调用约定。在32位的VC++5.0中PASCAL调用约定不再被支持(实际上它已被定义为__stdcall。除了__pascal外,__fortran和__syscall也不被支持),取而代之的是__stdcall调用约定。两者实质上是一致的,即函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。
_stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。
2、C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。
_cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。
3、__fastcall调用约定是“人”如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。
_fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。
4、thiscall仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。
5、naked call采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。
关键字 __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前,也可以在编译环境的Setting...\C/C++ \Code Generation项选择。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数分别为 /Gz、/Gd和/Gr。缺省状态为/Gd,即__cdecl。
要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是 WINAPI宏,Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为__stdcall。使用WINAPI宏可以创建自己的APIs。
四.位置断点修饰符
1.跳跃计数.
功能是执行断点但不在断点处停止,直到执行完了一个特定的次数为止.
使用中首先设置一个标准的位置断点,打开BreadPoint对话框,选中该断点,单击Condition,然后在弹出的对话框最下面的编辑控件中输入次数.
只有当程序全速运行时,未执行的循环次数才有用.单步执行跨过断点时不会更新跳跃计数.
例:已知循环可能崩溃,但不清楚在哪次循环时,输入远远大于总循环次数的跳跃计数修饰符,则在崩溃时可打开Breakpoint框,其中将列出还未执行的循环次数,与总次数相减就可得已执行的次数.
2.条件表达式.
只有表达式为真时触发.Breakpoint框Condition按钮,选第一个编辑框,输入表达式即可.规则:
.只可使用C类型比较运算符.
.表达式中不能调用任何函数.
.表达式中不能包含任何宏值.
表达式为@TIB=Thread Infomation Block Linear Address,则程序只在该特定线程中才会中断.例:线程@TIB地址值为0E000,则输入"@TIB==0xE000",则在切换到该线程时中断.对W98,可用@FS=thread specific value.
如在某特定错误后中断,则可用@ERR,如"@ERR=2"表示在最后错误为ERROR_FILE_NOT_FOUND.除@CLK外,所有可在WATCH窗口中使用的伪寄存器均可用于条件表达式.
条件表达式可与跳跃断点组合使用.
3.变量更改
在变量更改时中断程序.只有当位置断点执行时才能检查变量.常用用调用栈高层的函数中发现出错,需要深入调用栈,压缩范围找出根源时.
添加时在Breakpoint框第一个编辑框中输入变量名(可以是指针指向听对象:*p),在第二个编辑框中输入要查看的项目数量.
五.全局表达式和条件断点.
调试器可监控某一地址和该地址上的1,2或4字节的内容.如可用硬件调试寄存器,则不影响速度;否则程序将单步执行ASM指令并在每一步中检查条件,这将严重影响程序运行速度.
总共有4个调试寄存器.硬件调试寄存器不能处理超过1个双字长的引用.确保利用硬件调试寄存器的最好方法是使用表达式和数据更改位置的实际地址值.例如: g_szGlobal是全局数组指针,地址为0x5000,则在Breakpoint对话框中DATA选项卡中将表达式断点设为"*(char*)) 0x5000=='G'",但如果写为"WO(0x5000)=='G',则用不到硬件调试寄存器,会单步执行每条指令.
与全局表达式断点类似,使用变量的16进制地址给定长指针计算地址,并将要查看的单元数设为1,则全局变量断点可发挥最付佳功效.如上例要在变量改动时中断,则输入:"*(long*)0x5000".
六.WINDOWS消息断点.
Breakpoint框的Message页.需要指定一个窗口过程,注意:MFC世界中AfxWndProc是多数窗口的一个窗口过程,所以总会在该断
visual C++ 6.0开发工具与调试
选中所需要规范的代码,按shift+F8
2、 如何在Release状态下进行调试
Project->Setting=>ProjectSetting对话框,选择Release状态。C/C++标签中的Category选General,Optimizations选Disable(Debug),Debut info选Program Database。在Link标签中选中Generate debug info复选框。
注:只是一个介乎Debug合Release的中间状态,所有的ASSERT、VERIFY都不起作用,函数调用方式已经是真正的调用,而不查表,但是这种状态下QuickWatch、调用队列跟踪功能仍然有效,和Debug版一样。
3、 Release和Debug有什么不同。
Release版称为发行版,Debug版称为调试版。
Debug中可以单步执行、跟踪等功能,但生成的可执行文件比较大,代码运行速度较慢。Release版运行速度较快,可执行文件较小,但在其编译条件小无法执行调试功能。
Release的exe文件链接的是标准的MFC DLL(Use MFC in a shared or static dll),比如MFC42.DLL。这些DLL在安装Windows的时候,已经配置,所以这些程序能够在没有安装Visual C++ 6.0的机器上运行。而Debug版本的exe链接了调试版本的MFC DLL文件,如MFC42D.DLL。在没有安装Visual C++6.0的机器上不能运行,因为缺MFC42D.DLL等,除非选择use static dll when link。
4、 ASSERT和VERIFY有什么区别
ASSERT里面的内容在Release版本中不编译,VERIFY里面的内容仍然翻译,但不再判断真假。所以后者更安全一点。
例如ASSERT(file.Open(strFileName))。
一旦到了Release版本中,这一行就忽略了,file根本就不Open()了,而且没有任何出错的信息。如果用VERIFY()就不会有这个问题。
5、 Workspace和Project之间是什么样的关系
每个Workspace可以包括几个project,但只有一个处于Active状态,各个project之间可以有依赖关系,在project的Setting..中可以设定,比如那个Active状态的project可以依赖于其他的提供其函数调用的静态库。
6、 如何在非MFC程序中使用ClassWizard
在工程目录下新建一个空的.RC文件,然后加入到工程中就可以了。
7、 如何设置断点
按F9在当前光标处增加一个断点和取消一个断点。
另外,在编辑状态下,按Ctrl+B组合键,弹出断点设置对话框。然后单击【Condition…】按钮弹出设置断点条件的对话框进行设置。
8、 在编辑状态下发现成员变量或函数不能显示提示是如何打开显示功能
这似乎是目前这个Visual C++ 6.0版本的一个bug,可按如下步骤使其正常,如再出现,可如法炮制:
(1) 关闭Project
(2) 删除“工程名.ncb”文件
(3) 重新打开工程
9、 如何将一个通过ClassWizard生成的类彻底删除
首先在工作区的FileView中选中该类的.h和.cpp文件,按delete删除,然后在文件管理器中将这两个文件删除,再运行ClassWizard,这时出现是否移走该类的提示,选择remove就可以了。
10、 如何将再workspace中消失的类找出来
打开该类对应的头文件,然后将其类名随便改一下,这个时候工作区就会出现新的类,再将这个类改回原来的名字就可以了。
11、 如何清楚所有的断点
菜单【Edit】->【Breakpoints…】,打开“Breakpoints”对话框,单击【Remove All】按钮即可。
快捷键是“Ctrl + Shift + F8”。
12、 如何再ClassWizard中选择未列出的信息
打开“ClassWizard”对话框,然后切换到“Class Info”页面。改变“Message filter”,如选择“Window”,“Message”页面就会出现Window的信息。
13、 如何检测程序中的括号是否匹配
把光标移动到需要检测的括号前面,按快捷键“Ctrl + ]”。如果括号匹配正确,光标就跳到匹配的括号处,否则光标不移动,并且机箱喇叭还会发出一声警告。
14、 如何查看一个宏(或变量、函数)的定义
把光标移动到要查看的一个宏上,就比如说最常见的DECLARE_MAP_MESSAGE上按一下F12(或右键菜单中的相关菜单),如果没有建立浏览文件,就会出现提示对话框,按【确定】按钮,然后就会跳到该宏(或变量、函数)定义的地方。
15、 如何添加Lib文件到当前工程
单击菜单【Project】->【Settings…】弹出“Project Setting”对话框,切换到“Link”标签页,在“Object/library modules”处输入Lib文件名称,不同的Lib之间用空格格开。
16、 如何快速删除项目下的Debug文件夹中临时文件
在工作区的FileView视图中选中对应的项目,单击右键弹出菜单,选择【Clean(selection only)】菜单即可。
17、 如何快速生成一个现有工程除了工程名外完全相同的新工程。
在新建工程的“New”对话框中选择“Custom Appwizard”项,输入新工程的名字,单击【OK】按钮。出现“Custom AppWizard”项,输入新工程的名字,单击【OK】按钮。出现“Custom AppWizard-Step 1 of 2”对话框,选择“An existing Project”项,单击【Next】按钮。出现“Custom AppWizard-Step 2 of 2”对话框,选择现有工程的工程文件名,最后单击【Finish】按钮。编译后就生成一个与现有工程相同但可以重新取名的工程AppWizard。
现在就可以项用MFC AppWizard一样用这个定制的向导。如果不想用了,可以在Visual C++ 6.0安装目录下Common\MSDev98\Template目录中删除该Wizard对应的.awx和.pdb文件。
18、 如何解决Visual C++ 6.0不正确连接的问题
情景:明明之间改动了一个文件,却要把整个项目全部重新编译链接一次。刚刚链接好,一运行,有提示重新编译链接一次。
这是因为出现了未来文件(修改时间和创建时间比系统时间晚)的缘故。可以这样处理:找到工程文件夹下的debug目录,将创建和修改时间都比系统时间的文件全部删除,然后再从新“Rebuild All”一次。
19、 引起LNK2001的常见错误都有哪些
遇到的LNK2001错误主要为:unresolved external symbol “symbol”
如果链接程序不能在所有的库和目标文件内找到所引用的函数、变量或标签,将产生此错误信息。
一般来说,发生错误的原因有两个:一时所引用的函数、变量不存在,拼写不正确或者使用错误;其次可能使用了不同版本的链接库。一下是可能产生LNK2001错误的原因:
<1>由于编码错误导致的LNK2001错误
(1) 不相匹配的程序代码或模块定义(.DEF)文件导致LNK2001。例如,如果在C++源文件了内声明了一变量“var1”,却视图在另一个文件内以变量“var1”访问改变量。
(2) 如果使用的内联函数是在.cpp文件内定义的,而不是在头文件内定义将导致LNK2001错误。
(3) 调用函数是如果所用的参数类型头函数声明是的类型不符将会产生LNK2001错误。
(4) 视图从基类的构造函数或析构函数中调用虚拟函数时将会导致LNK2001错误。
(5) 要注意函数和变量的可公用性,只有全局变量、函数时可公用的。静态函数和静态变量具有相同的使用范围限制。当试图从文件外部方位任何没有在该文件内声明的静态变量时将导致编译错误或LNK2001错误。
<2>由于编译和联机的设置而造成的LNK2001错误
(1) 如果编译时使用的时/NOD(/NODERAULTLIB)选项,程序所需要的运行库和MFC时将得到又编译器写入目标文件模块,但除非在文件中明确包含这些库名,否则这些库不会北链接进工程文件。这种情况下使用/NOD将导致LNK2001错误
(2) 如果没有为wWinMainCRTStartup设定程序入口,在使用Unicode和MFC时讲的到“unresolved external on _WinMain@16”的LNK2001错误信息。
(3) 使用/MD选项编译时,既然所有的运行库都被保留在动态链接库之内,源文件中对“func”的引用,在目标文件里即对“__imp__func”的引用。如果试图使用静态库LIBC.LIB或LIBCMT.LIB进行链接,将在__imp__func上发生LNK2001错误。如果不使用/MD选项编译,在使用MSVCxx.LIB链接时也会发生LNK2001错误。
(4) 使用/ML选项编译时,如用LIBCMT.LIB链接回在_errno上发生LNK2001错误。
(5) 当编译调试版的应用程序时,如果采用发行版模态库进行链接也会产生LNK2001错误;同样,使用调试版模态库链接发行版应用程序时也会产生相同的错误。
(6) 不同版本的库和编译器的混合使用也能产生问题,因为新版的库里可能包含早先的版本没有的符号和说明。
(7) 在不同的模块中使用内联和非内联的编译选项能够导致LNK2001错误。如果创建C++库时打开了函数内联(/Ob1或/Ob2),但是在描述该函数的相应头问卷安里却关闭了函数内联(没有inline关键字),只是将得到错误信息。为避免该问题的发生,应该在相应的头文件中用inline关键字标志为内联函数。
(8) 不正确的/SUBSYSTEM或ENTRY设置也能导致LNK2001错误。
20、 如何调试一个没有源码的exe文件调用的dll
在Visual C++ 6.0中,进入“Project Setting”对话框然后选择Debug标签页。通常Visual Studio默认“executable for debug session”为可执行文件名,但可以将他改成任何你想要的程序。甚至可以指定不同的工作目录以及传递参数到你的程序。这个技术常用来调试Dlls、名字空间扩展、COM对象和其他从某些EXE以及从第三方的EXE中调用的plug-in程序。
21、 Visual C++ 6.0工程中的项目文件都表示什么。
.opt:工程关于开发化境的参数文件。如工具条位置等信息。
.aps(AppStudio File)资源辅助文件,二进制格式,一般不用去管他。
.clw:ClassWizard信息文件,实际上是INI文件格式,又兴趣可以研究一下。有时候ClassWizard出了问题,手工修改CLW文件可以解决。如果此文件不存在的话,每次用ClassWizard的时候回提示是否重建。
.dsp(DevelopStudio Project):项目文件,文本格式,不过不熟悉的或不要手工修改。
.dsw(DevelopStudio Workspace):是工作区文件,其他特点和.dsp差不多。
.plg:是编译信息文件,编译时的error和warning信息文件(实际上时一个html文件),一般用处不大。在单击菜单【Tool】->【Option】弹出的对话框里面有个选项可以控制这个文件的生成。
.hpj(Help Project):是生成帮助文件的工程,用microsoft Help Compiler可以处理。
.mdp(Microsoft DevStudio Project):是旧版本的项目文件,如果要打开此文件的话,回提示你是否转换成新的.dsp格式。
.bsc:是用于浏览项目信息的,如果用Source Brower的话旧必须又这个文件。如果不用这个功能的话,可以在Project Options里面去掉Generate Browse Info File,这样可以加快编译速度。
.map是执行文件的影像信息记录文件,除非对系统底层,这个文件一般用不着。
.pch(Pre-Compiled File):是与编译文件,可以加快编译速度,但是文件非常大。
.pdb(Program Database),记录了程序有关的一些数据和调试信息,在调试的时候可能有用。
.exp:只有在编译DLL的时候才会生成,记录了DLL文件的一些信息,一般也没有用。
.ncb:无编译浏览文件(no compile browser)。当自动完成功能出问题时可以删除此文件。编译工程后回自动生成。
CVS服务器与WinCVS的配置与使用
作者:吴炎溪 Email:Yanxi-Wu@21cn.com
1、CVS简述与CVS服务器的安装:
CVS是目前比较流行与优秀的版本管理与控制工具,它深受大多数开发人员与系统管理的喜爱,同时也是用来管理其它日常文档(如word工作文档之类)的一个强有力的工具。这一部分将对CVS 服务器的安装、配置与使用方法进行简单、扼要的介绍,以帮助大家尽快的使用CVS
2、CVS服务器的安装
CVS服务器版可以从网上的很多地方下载到,也可以到CVS的官方网站进行下载。根据你所使用的系统,下载相应的版本,然后进行安装。因为当前大部分人是在Linux下使用CVS服务器进行版本管理,并且Redhat Linux是目前最流行的一个Linux发行版之一,所以本文将主要基于Redhat Linux进行介绍。首先下载CVS服务器的rpm包,当然也可以下载其它方式的包如源程序包,这也就是Linux的一个优点之一。然后采用以下命令进行安装:
rpm -ivh cvs-1.10.8-3.i386.rpm
3、配置/etc/services文件:
安装完之后的第一件事就是要配置CVS服务器的配置文件,使其能正常的工作。首先在services文件中添加cvspserver,使其成为Linux的一种服务也就是cvs服务器例程的入口,配置如下:
cvspserver 2401/tcp # cvs client/server operations
cvspserver 2401/udp # cvs client/server operations
4、配置xinetd,用xinetd来启动CVS服务器:
进入到/etc/xinetd.d/目录,然后编辑一个文本文件,名字一定要与/etc/services中的入口名字cvspserver一致,所以这里用cvspserver作为文件名,文件的内容如下所示:
service cvspserver
{
disable = no
socket_type = stream
wait = no
user = root
env = HOME=
server = /usr/bin/cvs
server_args = -f --allow -root=/home/cvsroot pserver //指定认证方式为pserver,注意:-f
}
注意:a、service后面的名称一定要和你在/etc/service文件中的cvs服务名称一样;
b、env = HOME=这一行的目的就是为了解决在执行一些cvs操作时产生的读取/root/.cvsignore文件的错误,上面env那行的意思就是在运行 cvs服务的时候将环境变量HOME置空,这样虽然执行cvs的用户是root,但是由于没有了HOME这个环境变量,所以cvs就不会在去读取 /root/.cvsignore文件了。
5、配置CVS用户与cvsroot的属主与属组:
首先建立一个用户组cvs,可以使用 groupadd或者addgroup命名,也可以直接编辑/etc/group文件添加这个组,然后添加一个用户cvsroot,然后修改/etc/passwd文件使cvsroot用户的缺省组是cvs组,而不是cvsroot组(也就是修改)。在/home目录下建立 cvsroot目录(也可以在其它你喜欢的目录下建),然后修改/home/cvsroot的属主及属性:
#chown cvsroot.cvs /home/cvsroot
#chmod 771 /home/cvsroot
6、vs进行初始化:
安装完CVS服务器之后的另一件重要的事情就是要初始化CVS服务器的根目录,以后创建的CVS项目都将建立在这个目录下,采用的命令如下:
#cvs -d /home/cvsroot init
这样目录/home/cvsroot就成为CVS服务器的根目录,以后创建的目录都将默认的存放在这个目录下。
7、登录cvs服务器(在Linux或其它Unix系统下进行登录的情况):
a、如果是远程登录,采用以下语法:
#cvs -d :pserver:cvsroot@host:/home/cvsroot login
CVS password://输入用户cvsroot的口令;
如果没有出现其它的任何提示就表示登录成功了,否则要根据提示进行相应的修改。 下面对登录命令的语法进行说明:
其中,:pserver表示采用pserver方式进行用户登录认证,一般的CVS服务器都采用这
种方式,当然也可以采用其它方式,具体参考相应的资料;
:cvsroot表示要登录的用户名,只要是cvs组的成员都可以,如:cather;
@host表示要登录的服务器,可以是DNS名,也可以是IP地址,如:10.104.1.204;:/home/cvsroot表示CVS在服务器上的目录,也可以是其它目录(由第6中你所
指定的用来做init初始化的目录决定);
提示:也可以把export CVSROOT=:pserver:jchuang@192.168.0.8:/home/cvsroot这一句直
接写在用户的初始化文件中(如:.bash_profile),这样用户每次登录时,只需输入:
#cvs login
然后输入相应的口令就可以登录到cvs服务器上。
b、如果是本地登录,可以直接把export CVSROOT=/home/cvsroot这一句写在用户的初
始化文件如:.bash_profile中;然后直接
8、向CVS添加新的项目模块
一般我们都已经有一个或多个项目了,这样我们可以用下面步骤生成一个新的CVS项目。将一个工程文件置于CVs中进行版本控制,在CVS 术语中称作导入(import)。从名字上就可以看出,在导入前需要为此作些准备工作。
输入操作的基本要求是有个"干净"的目录结构。"干净"的意思是不需要版本控制的文件都被移走了(如编译生成的文件,备份文件等等)。如果工程已经开始一段时间了,这就显得很重要。在目录中也许有些是不打算将其置于版本控制下的文件,但是又想将他们放在这里,这种情况下,你要在输入之前将它们移走,然后再移回来。
注意的是CVS 认为空目录是不存在的。如果想增加一个既不包含文件又不包含子目录的目录,需要在其下创建一个哑文件。建议你创建一个名为 README.txt 的文件,其内容为对目录的简要说明。
进入到已有项目的目录,比如叫 cvstest:
$cd cvstest
运行命令将项目文件导入到cvs仓库中:
$cvs import -m "this is a cvstest project" cvstest v_0_0_1 start
说明:import 是cvs的命令之一,表示向cvs仓库输入项目文件.
-m参数后面的字串是描述文本,对项目进行描述,如果不加 -m 参数,那么cvs会自动运行一个编辑器(一般是vi,但是可以通过修改环境变量EDITOR来改成你喜欢用的编辑器)让你输入信息,cvstest 是项目名称(实际上是仓库名,在CVS服务器上会存储在以这个名字命名的仓库里)
v_0_0_1是这个分支的总标记.没啥用(或曰不常用)
start 是每次 import 标识文件的输入层次的标记,没啥用。
这样我们就在用户指定进行初始化的目录/home/cvsroot下建立了一个名字叫cvstest的CVS模块了,然后,我们可以把这个测试项目的文件删除,试验如何从仓库获取文件这会在后面的客户端文章进行说明。
(注:本小节主要引用自linuxaid.com.cn何伟平的“CVS服务器快速指南”)
9、从cvs本地服务器上checkout相应的模块:
a、如果是在CVS服务器所在计算机上,可以直接采用如下命令:
#cvs checkout stw
这样将把stw项目模块checkout到当前目录下。
b、如果是在远程运行Linux或其它Unix系统上,在第7中登录之后也可以用上边的命
令把相应的项目模块checkout到当前目录下。
另外,如果是在windows端进行checkout,可以采用WinCVS,那么还要对WinCVS进行其它的一些配置,下一部分将具体介绍它的配置方法。
第二部分 WinCVS的配置与使用方法
1、WinCVS简介:
WinCVS是CVS的一个客户端软件,它运行在Windows上,用来在Windows上登录CVS服务器,然后进行一些CVS相关的操作与管理。由于当前很多的企业内部都采用 Linux/Unix做服务器,而用Windows做客户端,所以,WinCVS与CVS服务器配合使用将组成最强有力的版本控制与管理的系统之一。
2、WinCVS的下载与安装;
最新的WinCVS可以从sourceforge.net/project/showfiles.php?group_id=10072地址下载到,也可以在sourceforge.net/project 上下载到最新的或其它版本的WinCVS。
下载到相应的版本后根据向导进行安装,已经要使用CVS的用户,安装这个WinCVS应该没什么问题吧!
3、配置WinCVS:
a、一般选项的设置,选择Admin->Preferences…,出现如下界面:
第一、Authentication:用来配置cvs服务器的认证方式,可以从下拉框中选择其它的认证方式,不过一般只要选择默认的pserver方式就可以,要注意的是必须与cvs服务器配置时所指定的认证方式一致;
第二、Path:用来配置cvs在服务器上的主目录路径,也就是服务器上用进行cvs初始化的目录,如:/home/cvsroot;
第三、Host Address:用来配置cvs服务器所在服务器的地址,可以是IP地址,也可以是DNS名,如:10.104.1.204;
第四、User name:用来配置要使用些WinCVS来登录CVS服务器的用户名,如:cvsyxwu,用户的登录必须由管理员把其添加cvs用户组中;
第五、CVSROOT:此项一般都不需要用户进行修改,用户在输入上边的几个选项时,系统将自动根据用户的输入生成此项的相应内容。
b、全局选项的设置,在上一个界面上选择“Globals”:
此项的配置主要是要注意这几选项:
第一,Checkout read-only不要选上,否则,checkout出来的源代码将不允许用户进行
修改,并且此选项默认是选中的;
第二,Prune (remove) empty directories也不要选上,否则,会自动删除空目录;
第三,对一般配置没有特殊要求的,把Dirty files support、Supply control when adding
与TCP/IP compression选项选中;
4、登录服务器:
选择Admin->login,将出现如下对话框要求用户输入登录口令
输入口令后,选择“OK”按钮,如果CVS服务器与WinCVS的配置都没出错的话,将在CVS的状态栏中提示:
cvs -z9 -d :pserver:cvsyxwu@10.104.1.204:/home/cvsroot login
Logging in to :pserver:cvsyxwu@10.104.1.204:2401/home/cvsroot
***** CVS exited normally with code 0 *****
code 0表示正确的登录;而如果出错的话,将是code 1,那么要根据错误的提示进行相应的修改。
5、从CVS服务器上check out相应的模块:
第一,在workspace中的Modules选中要存放checkout模块的目录;
第二,选择Create->Checkout Modeles,将出现如下对话框:
其中,Module name and path on the server就是要存放checkout 内容的目录,由用户输入;而Local folder to checkout to就是第一中用户所选择的目录。
6、修改之后把文件提交到CVS服务器
a)、只有一个用户对文件进行修改的情况
用自己喜欢的编辑器对checkout出来的文件进行修改,修改之后的文件在没有提交之前会是红色的,如下图example.h文件:
选中红色的文件example.h后右击选择“Commit Selection”选项,如果没有其它用户也对其进行修改并已经提交到CVS服务器上,一切正常的话将把example.h文件提交到CVS服务器并把图标恢复成原来的颜色。
b)、两个或两个以上的用户对同一文件的不同部分进行修改的情况
这种情况就是如用户A与用户B都checkout了文件example.h,内容如下:
int callby (int count)
{
printf("ExcelStor!\n");
}
void main(int argv,char *argc)
{
//added by my cvs
printf("I am Cather\n");
}
int mainexample()
{
printf("OK\n");
}
然后用户A修改成如下,并提交到CVS服务器(一般将正常提交):
int callby (int count)
{//add
printf("ExcelStor!\n");
}
void main(int argv,char *argc)
{
//added by my cvs
printf("I am Cather\n");
}
int mainexample()
{//add
printf("OK\n");
}
接着用户B修改成如下:
int callby (int count)
{//add
printf("ExcelStor!\n");
}
void main(int argv,char *argc)
{
//modified
printf("I am Cather\n");
}
int mainexample()
{//add
printf("OK\n");
}
当用户B选择“Commit Selection”时将提示:
cvs server: Up-to-date check failed for `example.h'
cvs [server aborted]: correct above errors first!
此时表明已经有用户对同一个文件example.h进行修改并提交到CVS服务器,这时
要先选择“Update Selection”对本地example.h与CVS服务器上的example.h文件进行
同步与合并,不用选中出现的任何选项,直接选择“OK”,这时将显示如下:
cvs -z9 update example.h (in directory C:\my cvs\STW\src\)
RCS file: /home/cvsroot/STW/src/example.h,v
retrieving revision 1.5
retrieving revision 1.6
Merging differences between 1.5 and 1.6 into example.h
M example.h
***** CVS exited normally with code 0 *****
表明用户B与用户A的修改已经合并成功,同时文件example.h的图标也将变成红色,合并后的文件是存放在用户B的本地机上,为了更新到CVS服务器还必须选中example.h并右击选择“Commit Selection”才能把用户A与用户B的修改合并后的结果提交到CVS服务器上。注:M表示此文件已经被修改过。
c)、两个或两个以上的用户对同一个文件的相同部分进行修改的情况
这种情况就是如用户A与用户B都下载了文件example.h,内容如下:
void main(int argv,char *argc)
{
printf("I am Cather\n");
}
然后用户A把文件修改成如下,并提交到CVS服务器(一般将正常提交):
void main(int argv,char *argc)
{
printf("I am Cather\n");
printf("I am Pat\n");
}
接着用户B又把文件修改成如下:
void main(int argv,char *argc)
{
printf("I am Cather\n");
printf("I love you Cather\n");
}
如果用户B这时选择“Commit Selection”准备把修改结果提交到CVS服务器,此
时将显示如下的错误提示:
cvs -z9 commit -m "update in 11:20" example.h (in directory C:\my cvs\STW\src\)
cvs server: Up-to-date check failed for `example.h'
cvs [server aborted]: correct above errors first!
***** CVS exited normally with code 1 *****
表明用户B的修改与其它用户的修改冲突,这时要先选择“Update Selection”,将显示如下提示:
cvs -z9 update example.h (in directory C:\my cvs\STW\src\)
RCS file: /home/cvsroot/STW/src/example.h,v
retrieving revision 1.9
retrieving revision 1.10
Merging differences between 1.9 and 1.10 into example.h
rcsmerge: warning: conflicts during merge
cvs server: conflicts found in example.h
C example.h
***** CVS exited normally with code 0 *****
example.h前面的C表示与其它用户的修改有冲突,并且文件的图标会加显示一个“C”,如下所示:
双击example.h将显示example.h的内容,如下:
void main(int argv,char *argc)
{
printf("I am Yanxi\n");
printf("I am Cather\n");
<<<<<<<>>>>>>> 1.10
}
这时你应该与用户A进行协商以决定最终要怎样修改。比如,可以修改成:
void main(int argv,char *argc)
{
printf("I am Yanxi\n");
printf("I am Cather\n");
printf("I love you Yanxi,too!\n"); //这部分为你的修改
printf("I love you Cather!\n"); //这部分为其它用户的修改
}
然后选择“Commit Selection”进行提交,将显示如下的提示信息:
cvs -z9 commit -m "update in 11:20" example.h (in directory C:\my cvs\STW\src\)
Checking in example.h;
/home/cvsroot/STW/src/example.h,v <-- example.h
new revision: 1.11; previous revision: 1.10
done
***** CVS exited normally with code 0 *****
表明用户A与用户的修改已经合并成功。
这样,向CVS服务器提交文件所会遇到的问题也基本上就是这些,用户要根据所遇到的实际问题进行修改。
7、向CVS服务器添加新文件
在本地添加文件后,要提交到服务端。先选中文件,然后点击“添加按钮”,添加文件后,再在右键菜单中选择提交命令“Commit Selection”即可。
如图,选中文件 example.h.bak,因为example.h.bak当前不是CVS的文件,此时“添加按钮”将由不可选状态变成可选状态,所以Status中显示为“NonCvs file”,选择“添加按钮”之后example.h.bak图标将变成红色并增加了一个A字母,如下:
然后选中example.h.bak,右击,选择“Commit Selection”把文件example.h.bak提交到CVS服务器上而成为CVS的一个文件。
8、结束语
来到这里,你基本上已经能为自己或公司配置一个实用的CVS服务器与WinCVS客户端,配合使用CVS进行系统开发或其它文档的版本管理与控制。
VSS(Visual SourceSafe)使用入门
此软件的安装与标准的Windows程序略有不同,安装过程中检查已经安装的旧产品的方式是搜索整个文件系统,因此可能将同一电脑上的其他操作系统上安装的VSS检查出来,此时不必理睬安装程序的警告,将安装位置改到合适的地方继续安装就行了。安装过程可能还会提示数据库版本的问题,新的V6数据库V5软件不能访问,一般来说总是应该使用更新版本的数据库,再说升级VSS客户端软件是很容易的。
安装完成以后得到一个VSS目录,这个目录包含了一切:程序文件、配置文件、默认数据库、用于网络安装的客户端安装程序。如果这个默认数据库需要在网络上共享(一般总是需要的)则将这个VSS目录共享,并给相关人员设置完全访问的权限(即使这个用户只是VSS数据库的只读用户)。注意,目录的安全属性与共享权限不同,“Program Files”目录下子目录和文件的访问权限可能是受限制的,所有VSS用户在VSS目录的安全属性也必须是完全访问的。
这样修改安全属性:在VSS目录上点右键,选择“属性”,切换到“安全”属性页,设置权限,然后点下面的“高级”按钮,选中高级里面最下面的“重置所有子对象的权限并允许传播可继承权限”选择框,点“确定”退出高级,再点确定使修改生效。
安全提示:因为VSS数据库要在网络上完全共享,所以网络要安全,如果有删除所有文件的病毒就会出大麻烦,一般的感染特定文件类型的病毒危害小一些,无论如何,安全最重要。
节 1.03 数据库管理
(a) 介绍
VSS的数据库是一个目录,目录下包含若干个文件和目录,“srcsafe.ini”是关键文件,此文件用来标识一个VSS数据库。VSS客户端打开一个数据库的方式就是找到这个文件,所以共享数据库的方式就是简单地共享包含这个文件的目录。另外一个文件“users.txt”记录用户信息。还有三个目录:“data”、“temp”和“users”,分别存放数据库的内容、临时的信息和用户的相关数据。安装VSS软件的目录除了上面所说的VSS数据库的文件外还包含几个目录存放VSS程序,根据安装类型不同可能还包括网络客户端安装程序。
VSS不存在服务程序,一切都通过文件操作进行。最简单的备份方式、也是最可靠、绝不会出错的方式就是备份整个目录。另一个方式是定期取得数据库里所有文件的最新版本,只对最新版本进行备份。为了避免保存大量不必要的版本,可以对每个项目建立一个VSS数据库,这个数据库保存所有的变更(包括开发过程中的大量尝试性的变更),而公司级的受控库只保存正规的版本。
安装VSS软件的时候可能已经安装了一个数据库,那么这个数据库就是默认的数据库,数据库都用位置命名,目录名字或者网络位置的名字(名字其实是无关紧要的,数据库的实际位置才是关键)。
VSS软件在程序菜单中包含四个程序,其中两个——分析并修复数据库、分析数据库——都是很少用的,另外两个VSS Admin程序(Visual SourceSafe 6.0 Admin)和VSS 浏览器(Microsoft Visual SourceSafe 6.0)是最常用的。另外还有一些其他程序并未出现在程序菜单,需要阅读帮助文件挖掘,VSS支持命令行方式,有编程接口(不然开发工具是如何与VSS整合的)。
每个数据库初始拥有两个用户:“Admin”和“Guest”,都拥有全部权限并且没有口令(因为Admin没有口令是不安全的,所以初次打开Admin程序会得到一个安全警告)。Admin用户是超级用户并且无法被删除也不能修改权限。Admin口令是最重要的,因为默认情况下VSS程序会用上一次的用户名和口令登录VSS,所以一般用户忘记口令是很常见的,这时候就需要Admin用户使用Admin程序重新设定用户的口令了。
VSS以树形目录的方式组织内容,每个目录称之为一个项目(Project,这是一个重要概念),每个子目录也同样是一个项目,如果使用了递归选项则对一个项目的操作会包含下面的所有子项目,对一个项目的设定会影响子项目,子项目的设定会覆盖继承自父项目的设定(但具体每个操作的效果要看具体的操作,并非没有特例)。简单地说,项目就是虚拟目录(并且和外部实际目录对应)。
(b) Visual SourceSafe 6.0 Admin程序
打开Admin程序要求输入口令,但并不总是这样,VSS会保存上次使用的数据库和用户名、口令,这样下一次使用VSS就不需要输入口令,进入VSS的Admin或浏览器程序以后可以再改变数据库。Admin程序的界面如下:
基本上这是个相当简单的界面,功能都通过菜单实现。界面的主要部分用来显示当前的用户列表、权限和登录状态,不支持右键,仅支持双击,双击可以修改用户的名字和权限。权限很简单,默认为“Read-Write”,可以修改为“Read-Only”,显然这不会使我们满足,我们需要的是“Project Rights”,对每个目录给每个用户特定的权限,这些权限包括只读、写入、添加删除重命名、销毁,除了不能控制到文件,这基本上满足了我们的要求。具体操作后面会介绍,相当简单。
(c) 打开指定的数据库
当前打开的数据库也许并不是需要的,菜单“Users”下的第一个菜单“Open SourceSafe Database”用来打开一个不同的数据库,打开新数据库会关闭原来的数据库。
图中的“Open”按钮不可用是因为temp数据库正是现在打开的,选择一个不同的数据库,可以用“Open”按钮打开它。“Browse”按钮用来在系统中寻找一个数据库,寻找的目标就是srcsafe.ini文件,寻找的位置范围就是一般Windows查找文件的范围,从桌面开始,包括了“我的电脑”和“网上邻居”。“Remove”用来从列表中删除一个数据库信息,并非删除数据库。下面的“Username”不可编辑是因为Admin程序只允许Admin用户使用,如果是在VSS浏览器下执行这个功能则可以编辑。
(d) 创建新数据库
很多时候我们并不喜欢默认的数据库,可能是因为它的安装位置太深了。我们也可能为了不同的目的创建几个不同的数据库。创建数据库使用菜单“Tools”下的“Create Database”,仅仅需要选择一个存放数据库的目录即可(不会在这个目录下创建“VSS”目录,所有文件和目录直接放在选择的目录下,因此这个目录应该是新建的专门用来放VSS数据库的)。新创建的数据库的目录里面只有数据库,不包含VSS程序。
(e) 用户管理
用户管理无非是添加、删除、编辑、修改口令,都在“Users”菜单下,一目了然。所有能设定的不过是用户名、口令和是否只有只读权限而已。最重要的功能是“Project Rights”,下面介绍。
(f) 使用项目权限Project Rights
项目权限是针对每个项目的用户权力。使用项目权限首先要启用项目权限,在菜单“Tools”下的“Options”里面的“Project Rights”选项卡上,选中最上面的“Enable Rights and Assignments Commands”选择框即可。
新用户的默认权限建议只给一个“Read”就行了。然后确定。这时“Tools”下面原来不可以使用的菜单全部可以使用了。
现在可以给项目设定权限了。打开菜单“Tools”下面的“Project Rights”,显示如下窗口:
在左边选择项目,右边上面选择一个用户,右边下面设定用户的权限,如果只有“Read”权限可以选择的话说明这个用户是“Read-Only”,在编辑用户那里去掉只读就可以了。因为可以对每一级每个目录单独设定权限,因此很容易疏忽。设定完权限后最好再逐级检查一遍看有没有发生错误,有可能发生这样的情形:试图通过对级别较高的项目的权限的禁止阻止用户访问整个项目,然而对子项目用户有单独设定的访问许可,因此对子项目实际上并未禁止访问。
四种访问权限含义如下:
“Read”,读
“Check Out/Check In”,签入/签出,也就是写
“Add/Rename/Delete”,添加/重命名/删除,删除的文件仍然存储在数据库中,可以被恢复,直到被销毁
“Destory”,销毁,销毁的文件就再也找不回来了,这个是会造成无法挽回的后果的权力,通常不应该赋予任何一个一般用户,仅保留给管理员。
这四种权力逐个包含,选中下面一个上面所有的会被自动选中,取消上面一个下面所有都回被自动取消。
如果一个用户在当前选择的项目上什么权力都没有就不会显示在用户列表中,必须用“Add User”添加进来,“Delete User”则删除用户在当前项目的所有权力并把用户从列表中清除。
菜单“Tools”下的“Rights Assignments for User”则提供了对单一用户的项目权力指派。功能相同,视图不同。
节 1.04 使用VSS浏览器
(a) 借助VSS开发项目
在程序菜单VSS的浏览器叫做“Microsoft Visual SourceSafe 6.0”,用来操作VSS。很多开发工具可以直接操作VSS,因此不需要使用这个工具。另外有些开发工具不支持和VSS浏览器混用(比如MS Visual Studio 里面的InterDev),如果混用,开发工具可能不能正确地处理文件,因此如果项目在使用开发工具整合的VSS功能,建议不要跳过开发工具直接使用VSS浏览器。
如果项目采用VSS作为版本管理,那么被认可的代码(以及其它文档)就应当是VSS数据库里面的最新版本(而不是某个人的版本)。每天早上,每个人都从VSS中取得(叫做Get)最新版本放在自己的工作目录下(这保证每个人用的都是最新的,并同时在每个人的电脑上做了备份),然后获得特定文件的修改权(叫做Check Out,签出),然后开始编辑,编辑完成之后放回并放弃修改权(叫做Check In,签入),签入以后其他人再去取得新版本的时候就会取得这个签入的版本了。
VSS可以记录每个变化,每个文件的每次签入都产生一个新版本,可以单独取得旧版本。VSS可以给整个项目贴上版本标签(Label Version),比如“beta 2”,这个版本是用户定义的版本(与VSS自身对每次签入的自动编号不同),可以针对版本标签取得版本,一个标签标识的是做标签之前签入的最新版本。
VSS可以方便地比较目录或文件,可以是VSS里的不同版本或不同项目互相比较,也可以是VSS和VSS之外的目录或文件比较,也可以是与VSS无关的外部文件或目录的相互比较,比较的结果包括相同的、不同的、增加的、删除的,对于文本文件可以直接可视化地显示差异。
除非Admin用户专门将数据库配置为允许多人签出,否则不会有两个人同时修改一个文件的情况发生(如果多人签出,签入时会要求首先合并,确认合并完成才能签入)。尽管提供了多人签出、分支开发、版本合并的功能,然而实际的合并操作仍然完全依靠脑力进行,因为工具只能指出两个版本有这些差异,但是这些差异如何合并却完全依赖脑力判断,绝非简单地合并为一个文件那么容易。因此很多人总是尽可能地避免多人签出或分支开发。
做好软件项目的配置管理
在软件项目实施过程中正确、有效地进行配置管理,需要进行科学合理的规划工作,并确定相应的执行策略。本文针对软件项目工作的特点,介绍了配置管理工作的一般步骤和注意事项。
当 软件开发团队发展到一定规模时,会越来越强调开发过程规范化和成熟度。软件项目的成败在很大程度上取决于对其开发过程的控制,这包括对质量、源代码、进 度、资金、人员等的控制。软件配置管理可以帮助开发团队对软件开发过程进行有效地变更控制,高效地开发高质量的软件。在质量体系的诸多支持活动中,配置管 理处在支持活动的中心位置,它有机地把其他支持活动结合起来,形成一个整体,相互促进,相互影响,有力地保证了质量体系的实施。
配置项的分类
在开展配置管理工作之前,首先应根据项目特点,对项目实施过程中涉及到的配置项进行分类工作。一般来说,一个完整的软件项目会包括项目管理文档、软件开发文档、程序代码、集成文档、维护文档等五类配置项,配置项分类结构如图所示。
对 于每一类配置项,又可以划分为若干细类,具体的分类方法如下:在项目实施过程中制订的工程总体计划、阶段计划、周计划,定期召开的项目例会或技术专题会议 的纪要,质量评审记录、配置管理报告等与质量相关的工作记录等文档,都属于项目管理方面的范畴,因此均可以划分到项目管理类文档进行管理。
作为一个软件项目,必然会产生贯穿软件工程标准定义的需求调研、需求分析、概要设计、详细设计、单元测试、系统测试、用户测试等各个阶段的软件文档,均可以归入到软件开发类文档之中。
在软件开发过程中产生的各模块程序代码,软件系统运行所需的各类参数以及配置文件等内容,由于其技术和管理特征与文档有很大的不同,而且相互之间的关联性比较强,为了对其版本进行有效地控制,建议单独划为一个类别进行管理。
软件系统的设计、开发和运行离不开硬件环境的支持,因此在软件项目的实施过程中,通常都会涉及到机房设计、主机安装、网络规划等方面的工作内容,因此系统集成类的文档应作为单独的一个类别,纳入到软件项目的配置管理之中。
在软件系统投入运行之后,需要进行相应的日常维护工作,在维护过程中产生阶段性运行总结报告、定期产生的维护日志、系统运行中出现的故障现象及问题解决情况等维护记录,都需要纳入维护类文档进行管理。
在软件项目实施过程中产生的各类文档、程序代码纷繁复杂、数量众多,通过对各类配置项的归类工作,形成逻辑清晰的配置管理结构,便于对文档和程序代码进行日常管理,使项目实施中产生的各类配置记录始终处于可控状态。
建立配置库
在 软件项目的启动阶段,应指定一名专职或兼职的配置管理员,建立一台专用的配置服务器,安装相应的配置工具软件,并根据配置项分类方法,对程序代码和文档的 目录结构进行规划工作,在配置工具软件中,建立起相应的配置目录结构,同时根据使用者角色的不同,设定相应的目录访问和存取权限。配置项及工作角色的对应 关系如表所示。
对于每一个具体的配置项,都需要标识出其作者、时间、版本号、当前状态等基本信息,以便对配置项的版本进行实时监控,方便项目成员对配置项的检索和更新工作。
配置管理员负责整个配置库的安全管理工作,应妥善保管好系统管理员的口令,并进行定期的变更工作,以保证配置库的安全性。
建立执行机制
在配置库建立起来以后,配置管理员应将配置目录结构和权限分配表在项目组内部进行公布,并根据应用行业特点,对CMM/ISO9001的配置管理过程进行合理裁减,制订适用于本项目的配置工作流程,明确项目组中的每位成员在配置管理方面的分工职责,并对项目组成员进行相应的职责和流程培训工作。
配置管理员除了负责对各类配置项进行管理之外,还应对项目配置状况进行分析,定期提供配置报告,发布最新的配置项状态,提出改进建议并跟踪执行情况,避免出现因为文档或程序代码版本更新的不一致,而导致系统故障的情况发生。
在 配置管理工作中,为了保证配置项的可靠性,应制订相应的备份策略,对配置库中的不同类型的配置项进行定期备份。在软件系统的设计开发阶段,程序代码类的配 置项由于变更频繁,建议每天备份一次,在正式发布之后,可以改为每周备份一次。文档类的配置项变更机率相对较小,建议每周备份一次。具体的备份方法,可以 采用手工方式执行备份操作,也可以在工具软件或操作系统中设定备份策略,定期自动执行备份操作,同时配置管理员应做好相应的备份记录工作。
由 于配置工具软件本身一般都提供对每一个配置项历史版本的追溯机制,因此对配置库的备份操作,一般只需对当前配置库的内容进行备份即可。这里需要注意的一点 是,在执行配置库的备份操作之前,应对配置库目录中的数据是否正常进行检查,以避免因库文件损坏而使错误数据覆盖正常备份库,从而导致配置项丢失的情况出 现。
经验总结
如果配置工作流程制订得过于复杂,不具备可操作性,反而起不到应有的管理作用,因此在开展配置管理工作时,应以简单、有效、适合应用行业特点为基本准则,推进软件项目实施过程中的配置管理工作。
配置管理对象不仅仅限于CMM/ISO9001体系规定的内容,凡是与项目实施有关的文档、代码或数据均应纳入配置管理,这样可以实现对项目实施中的每一项工作进行追溯,及时处理项目实施过程中出现的各类问题。
VSS使用手册
1 VSS概述
版本控制是工作组软件开发中的重要方面,它能防止意外的文件丢失、允许反追踪到早期版本、并能对版本进行分支、合并和管理。在软件开发和您需要比较两种版本的文件或找回早期版本的文件时,源代码的控制是非常有用的。
Visual SourceSafe 是一种源代码控制系统,它提供了完善的版本和配置管理功能,以及安全保护和跟踪检查功能。VSS通过将有关项目文档(包括文本文件、图象文件、二进制文件、声音文件、视屏文件)存入数据库进行项目研发管理工作。用户可以根据需要随时快速有效地共享文件。文件一旦被添加进VSS,它的每次改动都会被记录下来,用户可以恢复文件的早期版本,项目组的其他成员也可以看到有关文档的最新版本,并对它们进行修改,VSS也同样会将新的改动记录下来。你还会发现,用 VSS来组织管理项目,使得项目组间的沟通与合作更简易而且直观。
VSS可以同 Visual Basic、Visual C++、Visual J++、Visual InterDev、Visual FoxPro 开发环境以及 Microsoft Office 应用程序集成在一起,提供了方便易用、面向项目的版本控制功能。Visual SourceSafe 可以处理由各种开发语言、创作工具或应用程序所创建的任何文件类型。在提倡文件再使用的今天,用户可以同时在文件和项目级进行工作。Visual SourceSafe 面向项目的特性能更有效地管理工作组应用程序开发工作中的日常任务。
1.1 VSS中的文件
当你要修改某个文档时,需要先从数据库中将它签出(check out),或者告诉VSS你要编辑该文档。VSS会将该文档的副本从数据库中拿到你的工作文件夹(working folder)中,你就可以修改你的文档了。如果其他用户再想对同一文档进行修改,VSS会产生一个信息,告诉他,该文档已被签出(check out),从而避免多人同时修改文档,以保证文档的安全性。
当你完成修改之后,需要将文档 签入(check in)VSS。这个操作从你的工作文件夹(working folder)中复制被你修改的文档,并将它放回VSS数据库,以便其他用户能够及时看到文档的改动。VSS能够保存文档的所有改动,并显示最新版本,同时早期版本也会被跟踪记录下来。VSS对反增量技术的运用,仅需要用很少的磁盘空间就能使得用户获取文档的所有版本。
如果你没有修改文档,你可以执行撤消签出(undo check out)命令,文档将被保存为被签出(check out)之前的状态。
如果你只需读取某一文档而并不需要编辑它,你可以执行取出(get)命令,将文档放入你的工作文件夹,再选择查看文档(view),来查看你的文档的最新版本。
1.2 VSS中的项目
项目(project)是指用户存储在VSS数据库中的所有文件(file)的集合。用户可以在项目之间或项目内部实现文件的添加(add)、删除 (delete)、编辑(edit)、共享(share)。一个“项目(project)”在很大程度上类似于一个普通系统的的文件夹,不同的是它能更好地支持文件合并(merge)、跟踪(archive)和版本控制(version control)功能。
文件保存在VSS数据库中的项目(project)里。你无须管理存储在VSS 中的文件正本,除非你要检查或与其它拷贝进行比较。
VSS为每一位用户提供了一份备份文件放入工作文件夹(working folder),供用户对文件进行查看与编辑。尽管没有工作文件夹也可以查看文件,但要想真正实现对文档的处理,必须建立工作文件夹。
1.3 VSS的版本控制功能
VSS能够保存文件的多个版本,包括文件版本之间每一处微小的变动。版本控制有以下几方面的内容:
l 组内合作——在缺省的情况下,一般一个文件在某一时间只允许一个用户对其进行修改,这样可以防止文件意外地被其他用户改动或者覆盖。但管理员可以改动这种缺省的设置,允许文件多层签出。这种设置也能防止过多的、不必要的改动。
l 版本追踪——VSS能够对源代码和其他文件进行存储和早期版本的追踪,从而实现重建文件早期版本等有关功能。
l 跨平台开发——在多平台开发的情况下,版本追踪用于维护核心代码。
l 代码的再使用—— 追踪程序基准使得代码可重用。
1.4 文件的拆分和共享
在VSS中可以实现一个文件被多个项目共享(share)。在一个项目中对文件的改动可以自动反映到其他共享的项目中去。这正提倡了代码重用。在file菜单中的properties中,点击link,可以查看某一文件的共享情况。
拆分(branch)是将文件从原来共享的项目中分离出来的过程。它使得VSS可以实现从不同的路径追踪文件。
注:在其他版本控制系统中,分支是通过跟踪版本号来实现的。例如:版本“2.3.9.2”是版本2.3的第二个修订版本的第九个分支。而VSS通过明显不同的项目名称实现对文件分支的跟踪。
拆分文件就断开了共享连接,使得本项目中的文件与其他原来共享的项目无关。对此文件的修改将不会再反映到其他项目上。拆分是这样被建立的:两个文件以前有着共同的历史记录,从实现拆分开始,他们的历史记录将被VSS分别追踪。
拆分文件之后,link按纽将不再显示已断开的连接,但你可以用path(file菜单中的properties项)按纽浏览拆分的历史记录。
共享(share)文件就是在多个项目间建立文件的连接。拆分(branch)文件就是在项目之间建立了不同的文件路径。
1.5 工作文件夹(working folder)
VSS是存储和管理文件的工具,但是编辑和编译文件必须在VSS指定文件夹中进行。这个文件夹叫工作文件夹,它可以是现存的文件夹,也可以是VSS新建的文件夹。VSS浏览器在文件列表上方显示了文件的工作文件夹的路径。
在VSS 系统中,工作文件夹才是你真正用于处理文档的地方。当你要编辑或修改某个文档时,必须对文档实施check out 操作(详见3.3.5修改和编辑文件),VSS将该文档从项目中拷贝出来,放入你的工作文件夹。当你修改完毕并check in 文件之后,VSS又将文件重新拷贝到数据库中以记录你的修改。
一旦你将文件签出,VSS就开始在你的本地机上创建并管理你的工作文件夹。
每一个用户、每一个项目或每一台微机都可以有自己的工作文件夹。如果Joe在项目$/SpreadSheet和$/WordProcessor上工作,他就有相应的2个不同的工作文件夹。如果Hanna在同样的项目上工作,对于每一个项目她又有自己的工作文件夹。
当你为某个项目设置了工作文件夹,你可以用它来放置你该项目中包括子项目再内的所
2 VSS的客户端安装
2.1 安装VSS的系统条件
l 计算机/处理器: 处理器为486DX/66MHz或以上PC机推荐Pentium或更高级的处理器。
l 内存:Windows 95或以后的版本要求16 MB RAM (推荐32 MB);Windows NT 4.0要求24 MB (推荐32 MB)。
l 硬盘:客户机:典型安装:59MB; 72 MB;安装过程:66 MB;
l 服务器:典型安装:128 MB;最大安装:141 MB;
l 附加硬盘要求:Internet Explorer:典型为43 MB,最大59 MB;MSDN:典型57MB,最大59 MB
l 驱动器:CD ROM
l 显示:VGA或更高级显示器,推荐Super VGA。
l 操作系统:Microsoft Windows 95或以后版本或者Microsoft Windows NT 4.0,NT要求Service Pack 3或更高版本(包括Service Pack 3〕
l 外围设备/其它: Microsoft Internet Explorer 4.01 Service Pack 1 (包含).
2.2 从网络安装VSS客户端
1) 打开本地计算机的“网上邻居”属性对话框;
2) 点击“配置”按纽;
3) 将“MICROSOFT网络用户”的属性设置为:登录到WINDOWS NT 域,域名为PLANNING;
4) 添加TCP/IP、NETBEUI、IPX/SPX协议;
5) 重新启动计算机,登录“planning”域;
注:管理员为每位NT用户设置的登录密码为“111”,用户在第一次登录时,计算机会提示用户修改密码。
6) 从“网上邻居”的“planning”域中查找服务器“VSSDATA”;
7) 打开共享的“VSS”文件夹并双击“NETSETUP”;
8) 按照安装程序的提示开始安装。
3 VSS的基本使用操作
3.1 登录VSS
点击VSS图标或从程序菜单中运行Visual SourceSafe 6.0,即可打开VSS浏览器。
如果用户登录的VSS密码和登录PLANNING域的密码是一致的,系统将不再提示输入进入VSS数据库的密码;如果用户为VSS设置的密码与登录 PLANNING域的密码不同,系统将提示用户输入VSS的登录密码。关于如何修改VSS用户密码,详见“3.2.14修改用户密码”。
3.2 VSS浏览器
当你一打开VSS,如果你设定了密码的话,它会提示你输入密码。如果你没有设定密码,你可以直接看到浏览器。在浏览器上,你可以浏览你的数据库、查看项目列表、显示文件统计信息、执行命令对文件和项目进行操作等。浏览器的最上方的标题栏是你当前连接的数据库。VSS使用符号来提供有关文件和项目信息。
菜单栏的下面是常用工具栏,这里有许多常用命令的按纽,它可以帮你快速地执行对文件的操作。
在项目栏中,显示有项目列表,包括特殊项目的有关信息。文件栏显示了当前项目的所有文件的列表。结果栏显示当前你所执行的操作的结果。
3.3 VSS基本操作
3.3.1创建新的文件夹
1) 选中要创建新文件夹的项目(上级文件夹);
2) 在file菜单中选中creat project;
3) 写入要添加的文件夹的名称,同时也可以在comment栏中为新建的文件夹添加备注;
4) 点击OK。
3.3.2添加文件夹
1) 选中你要添加文件夹的项目(上级文件夹);
2) 在file菜单中选中add files;
3)在文件夹列表中选中要添加的文件夹;
4)点击add,同时可以在comment栏为你添加的文件夹做一个简单备注;
5)如果你要连同子文件夹一起添加,选择Recursive;
5) 点击OK,成功添加了一个带有备注的文件夹。或者点击close,退出操作,返回add files对话框,点击close。
3.3.3添加文件
3.3.2.1使用add命令添加文件
1)选中你要添加文件的文件夹;
2) 在fil菜单中选中add files;
3) 在文件列表中选中要添加的文件;如果要添加多个文件,可以使用CTRL键或SHIFT键,同时选中多个文件;
4)点击add,同时可以在comment栏为你添加的文件夹做一个简单备注;
5)点击OK。
3.3.2.2用拖动的方法添加文件/文件夹
1)打开VSS浏览器,调整其大小,使得Windows资源管理器能够显示出来;
2)打开Windows资源管理器,调整大小,使得两个浏览器可以同时显示;
3)从Windows资源管理器中选择你要添加的文件或文件夹;
4) 拖动你所选的文件或文件夹,放入VSS浏览器,文件被添加进项目,而添加的文件夹将作为项目的子项目。
3.3.3查看文件
1) 在文件列表中选中要查看的文件;
2) 在EDIT菜单中选中view,打开对话框;
3)选中view SourceSafe’s copy of this file;
4)点击OK。
3.3.4创建工作文件夹
在执行签入(check in)、签出(check out)、撤消签出(undo check out)、取出最新版本(get latest version)和文件合并(merge branches)等命令时都必须使用工作文件夹。工作文件夹可以随时设定或修改,VSS系统中可以通过两种方式设置工作文件夹。
3.3.4.1专门创建工作文件夹
1) 在VSS浏览器的文件或项目列表中选中要设置工作文件夹的文件/文件夹;
2) 在file菜单中选择set working folder,打开对话框;
3) 在资源管理列表中选择或新建文件夹;
4) 点击OK。
3.3.4.2利用check out操作设置工作文件夹
在对文件执行check out操作时,如果该文件还没有设置工作文件夹,系统会提示用户为文件创建或指定工作文件夹,用户可以根据系统的提示对文件进行工作文件夹的设置。
3.3.5修改和编辑文件
1) 在edit菜单中选中edit file,打开对话框;
2) 选择check out this file and edit it in your working folder;
3) 点击OK。
注:如果用户已经为文件设置了工作文件夹,VSS会将该文件的一个COPY放入你的工作文件夹并打开文件,让用户进行修改和编辑;如果用户还没有为文件设置工作文件夹,VSS系统会提醒用户设置工作文件夹,用户可根据系统提示,先设置工作文件夹,才可以对文件进行编辑。
3.3.6移动文件/文件夹
3.3.6.1移动文件
你只有一种方法移动文件:将文件共享(share)到项目中,再将其从原来的项目中delete或是destroy。移动文件后,历史信息仍然有效。但是你不能用move命令来移动单个的文件。
3.3.6.2移动文件夹(project)
注:要使用移动(move)命令,必须先请管理员为你设置对移动目的项目的添加(add)权限和对源项目中文件的破坏(destroy)权限。
使用移动命令你可以重新定位子文件夹,将其从一个文件夹移动到另一个文件夹中。这个命令重新定义了被移动文件夹的路径。
这个命令不可以重命名文件;你只能通过执行重命名命令来实现它。这个移动命令不会改变文件夹的内容或其中子文件夹的历史信息,它只会影响到新的和旧的上级文件夹的历史信息。
警告:当你移动一个文件夹之后,就不能再如实地重建其上级文件夹的早期版本。
移动文件夹的具体操作步骤如下:
1) 选中要移动的文件夹;
2) 在file菜单中选中move,打开对话框;
3) 在列表中选择目标文件夹;
4) 点击OK。
3.3.7共享文件/文件夹(share)
1) 在VSS浏览器中选择你要共享的目标项目。
2) 在SourceSafe菜单中选择share,打开共享对话框。
3) 在file to share列表中选择你要共享的文件,如果文件没有显示,可以旁边的项目列表中查找。
4) 点击share。
5) 点击close。
3.3.8拆分文件(branch)
3.3.8.1拆分被共享的文件
1) 在浏览器中选中你想要拆分的文件;
2) 在SourceSafe菜单中选择branch,打开拆分对话框;
3) 在comment中填写备注;
4) 点击OK。
3.3.8.2用一步操作完成文件的拆分与共享
1) 在VSS浏览器中选择你要branch/share的项目;
2) 在SourceSafe菜单中打开share对话框;
3) 在file to share列表中选择要共享的文件,如果你要的文件没有显示,在项目列表中
3.3.9删除/恢复文件或文件夹
如果想从VSS中移走某个文件,你必须首先确定是仅仅从项目中移走,还是从VSS数据库中移走。你还必须确定是要删除文件,但使其能够恢复,还是永久性地破坏它。
VSS中有以下三种途径可以实现从数据库中移走文件。
3.3.3.9.1删除(delete)
将文件从项目中移走。该文件仍然存在于你的VSS数据库和其它共享该文件的项目中,你可以恢复它。此命令同样适用于项目。
1) 选择文件或项目;
2) 选择file菜单中的delete命令;
3) 点击OK。
3.3.3.9.2破坏(destroy)
删除(delete)对话框中有永久性破坏(the Destroy Permanently)选项,你一旦选中它,文件或项目将从VSS数据库中被移走,你不能再恢复它。此外,当Destroy 和Destroy Permanently命令用于共享文件时,它只作用于当前文件夹,其它共享的文件夹仍然保留该文件,该文件依然保存在VSS数据库中。
1) 选择文件或项目;
2) 选择file菜单中的delete命令;
3) 选中 Destroy Permanently 选项;
4) 点击OK。
3.3.3.9.3清除(Purge)
这个命令将永久性地移走你已经删除的文件或项目,但没有破坏它。你可以使用这一命令清空你的文件或项目中的所有内容,但不能恢复它们。
1) 在VSS浏览器中选中项目;
2) 打开file菜单的properties对话框,按delete按纽;
3) 在列表中选择要清除的文件名;
4) 点击purge;
5) 如果要继续,在VSS给你的提示栏中点击yes。
3.3.10查看文件/文件夹的历史信息或早期版本
在历史信息中保存有每一个文件的详细信息。在history对话框中,你不仅可以浏览到文件的版本信息、备注、以及文件的相关历史记录,也能够获取文件的某个旧版本。
注:只有文件(file)可以从历史信息中check out,文件夹(project)不能从中check out。
你还可以从历史信息对话框中执行get、check out、diff、pin、unpin、roll back和reprot等操作。
要查看历史信息:
1) 在tool菜单选中show history,打开history options对话框;
2) 点击OK。
3.3.11获取文件的最新版本
1) 选择你要操作的文件,也可以是多个文件或某个项目;
2) 在SourceSafe菜单中选择get latest version;
3) 如果你事先没有设定工作文件夹,VSS会提示你是否设定一个工作文件夹,点击OK,设定一个工作文件夹;
4) 如果你已经确定了选项,VSS就会显示get latest version对话框,你就可以从当前的项目中获取文件的最新版本的备份,它放在你的工作文件夹中。
3.3.12获取文件的早期版本
1) 选中你要查看的文件;
2) 在tool菜单中选中show history,打开history option对话框;
3) 点击OK,打开history对话框;
4) 选中你要看的版本;
5) 点击get,打开get对话框;
6) 如果你事先没有设定工作文件夹,VSS会提示你是否设定一个工作文件夹,点击OK,设定一个工作文件夹;
7) 在取出对话框中点击OK,文件版本的备份就会从当前项目调入你的工作文件夹。
3.3.13修改用户密码
使用更改密码命令来设置或更改你的密码。要更改密码,必须首先知道当前的密码,如果你忘记了自己的密码,请与管理员联系。
登录的时候,VSS会提示你输入密码以确认你的身份。如果管理员为你设置的用户名与你的网络名是相同的,VSS将不会再提示你输入密码。
注:你的VSS的密码可以与你使用的操作系统的密码相同,也可以不同,它并不会替换你操作系统的密码。
如何更改密码:
1) 从tool菜单打开change password对话框;
2) 在旧密码框里键入你当前的密码;
3) 在新密码框里键入你的新密码;
注:密码可以设1到15个字符,它以*的形式显示;
4) 在确认框里再次键入新密码;
5) 点OK。
3.3.15打开/关闭数据库
如果你使用了VSS,你的文件和项目就会被存储在一个数据库中。它安全地保存你的信息并为你提供重要的历史信息和版本跟踪。要创建新的数据库,要与VSS管理员联系。
3.3.15.1打开现有的数据库
要运行你的VSS,你必须与存储你的文件的数据库连接。这一步通常由VSS自动完成,除非你要选择其他的数据库。如果数据库还没有安装,请与管理员联系。
1) 从file菜单,选择open SourceSafe database,打开对话框;
2) 从数据库列表中选择一个数据库;
3) 点击open,打开数据库。
3.3.15.2关闭数据库
你只能在一个数据库中进行工作。因此,如果要关闭一个数据库,只需打开另一个数据库即可。
2006年12月22日金曜日
软件配置管理与SourceSafe使用指南
软件配置管理是指通过执行版本控制、变更控制的规程,以及使用合适的配置管理软件,来保证所有配置项的完整性和可跟踪性。配置管理是对工作成果的一种有效保护。
2、为什么需要配置管理
如果没有软件配置管理,最大的麻烦是工作成果无法回溯。随着工作的进展新的程序覆盖了老的程序,当突然发现新程序有问题而老程序正确时怎么办?那只能重写老的程序来覆盖新的程序。过一段时间又发现原来的老程序有问题,而解决方法在原来的新程序中……您是不是快要发疯了。
为了避免成果被覆盖,包括我自己在内的很多人早期采用手工管理版本的方式,例如当一个新版本产生时用当时的日期来命名文件夹,然后再复制一下以后的修改在复制的文件夹内进行,这样上一个版本就被保存下来了,周而复始不同的版本不会被覆盖。虽然这种方式可以从某种程度上解决版本的回溯问题,但他存在的缺点是显而易见的:第一点如果保留结果过于频繁,将会导致产生大量的有着重复内容的文件夹,庞大的物理空间,管理起来很麻烦;如果保留旧版本的时间间隔太长,可能产生某些有用的老程序无法回溯。拿我最近开发的一个程序来说程序只有几十兆,经过一年的开发各版本累计到1G。第二容易产生版本的混乱,如果是团队开发软件,这种简单的方法更难解决问题的本质了。
3、人的问题
配置管理的方法是成熟的,而且相应的软件工具也是成熟的,基本上不存在看不懂、不会用的问题。配置管理的执行效果如何,完全是事在人为。妨碍配置管理的主要问题是人们嫌麻烦和侥幸心理作怪。
在没出乱子的情况下,执行版本控制看起来有些麻烦。每次修改工作的时候总是要Get Latest Version,接着Check Out,修改完后又要Check In,多做了三步。其实这三步加起来也就十几秒钟,而且不费脑子,根本没有添加多少麻烦,仅仅是个人感觉不爽而以。然而不执行版本控制的话,万一发生工作成果被覆盖或丢失等问题,麻烦就大了。
4、软件配置管理规范
软件研发和管理过程中会产生许许多多的工作成果,例如文档、程序和数据等,他们都应当妥善地保管起来,以便查阅和修改。如果把所有文件一股脑的塞进计算机里,那么使用起来很麻烦。
凡是纳入配置管理范畴的工作成果统称为配置项配置项主要有两大类:一类是属于产品的组成部分,例如需求文档、设计文档、源代码、测试用例等等;另一类是在管理过程中产生的文档,例如各种计划、报告等。
每个配置项的主要属性有名称、标识符、文件状态、版本、作者、日期等。配置项及历史纪录反映了软件的演化过程。
基线由一组配置项组成,这些配置项构成了一个相对稳定的逻辑实体。基线中的配置项被冻结后,不能在被任何人随意更改。基线通常对应于开发过程中的里程碑。通常将交付该客户的基线称为一个Release,为内部开发用的基线称为一个Build。
版本控制的目的是按照一定的规则保存配置项的所有版本,避免发生版本丢失或混乱等现象。配置项的状态有三种:“草稿”、“正式发布”和“正在修改”
配置项的版本号与配置项的状态紧密相关:
(1) 处于“草稿”状态的配置项的版本号格式为:0.YZ
(2) 处于“正式发布”状态的配置项的版本号格式为:X.Y。
一般只是Y值递增,当Y值到达一定的范围时X值才发生变化。
(3) 处于“正在修改”状态的配置项的版本号格式为:X.YZ。
一般只增大Z值,当配置项修改完毕,状态重新变成“正式发布”时,将Z值变为0,增加X.Y值。
5、常用的配置管理软件
A)、自从20世纪80年代后期研制并完善了“增量存储算法”后配置管理工具的春天便开始了,目前国内常用的配置管理工具大概有SourceSafe、CVS和ClearCase。
SourceSafe是Micrsoft公司推出的一款支持团队协同开发的配置管理工具,是Visual Studio的套件之一。因为其短小精悍,又继承了微软集成销售的一贯作风用户可以相对于免费的价格得到,用户量绝对是第一位。
SourceSafe简单易用人们在使用配置管理工具时候,80%的时间只是用Add,Check in,Check out等区区几个功能。
SourceSafe 的主要局限性:只支持WINDOWS不支持异构环境下的配置管理;对INTERNET支持不够完善。
B)、在详细介绍SourceSafe首先简单概述一下它的基本机制。SourceSafe是使用服务器、本地机的概念来进行操作的,它认为所有需要操作的文件都存在服务器版本文件和本地机版本文件,无论您的SourceSafe的架构是服务器客户机形式还是个人单机版形式,它的机制都是这样。用户所用的修改都是在本地机上完成的,修改完成后再上传服务器。单机版也是这样操作。我们一定要明确两个版本后再来分析。
服务器版本文件是一个绝对受配置管理软件限制的文件,用户只能通过SourceSafe的规定的权限和操作方法修改它,因为它并不是您一个人的,它是大家的。本地文件是一个基本不受限制的文件,您可以象操作本地文件一样操作它。
SourceSafe由Visual SourceSafe 6.0 Admin、Microsoft Visual SourceSafe 6.0、Analyze VSS DB、Analyze & Fix VSS DB四部分组成。
C)、Analyze VSS DB、Analyze & Fix VSS DB两个工具不是很常用,前者用于检查SourceSafe数据库文件的完整性,后者主要是修正SourceSafe数据库文件存在的错误。
D)、Visual SourceSafe 6.0 Admin的功能类似于win2k的用户管理器,软件配置管理人员用它来分配用户和设定相应的权限。
管理员的管理操作一般都集中在Visual SourceSafe 6.0 Admin中,系统中只有一个系统管理员Admin可以登陆到此程序中进行管理工作,一般刚刚安装的系统中此用户的密码缺省为空。而且系统为Admin这个用户保留的一切权力,不可更改。
数据库的创建这个操作必须在服务器上执行,因为通过客户端创建数据库的操作,只是在客户端的机器上创建的数据库,这个数据库往往只能单机使用。同时要必须注意,由于VSS是通过WINDOWS的网络共享来完成服务器端受控版本文件的共享,因此VSS服务端的数据库必须建立在服务器的一个完全共享的目录之中。否则,客户端将无法获得数据库中的文件。
数据库的备份与恢复,备份数据库或者其中的一个项目,点击tools-archive projects…菜单弹出对话框,根据提示一步步进行备份,最后会形成一个扩展名为*.ssa的备份档案文件。
如果要从档案文件中恢复VSS数据库中的文件数据,点击tools-restore projects菜单根据提示一步步完成数据恢复工作。其中,在恢复过程中,可以选择恢复为原有工程,也可改变恢复成其他工程目录。
E)、Microsoft Visual SourceSafe 6.0是SourceSafe的主要使用平台,样子象Windows的文件管理器,它所显示的路经是以‘$’符号为根节点的相对路径,在这里介绍一下此平台的主要使用方法。
(1)添加项目
您可以在根节点下添加项目,方法是:File—Add File出现Add File对话框后选中相关文件,按Add即可。
你可以继续通过File-Create Project在根目录下创建一个项目后在此项目节点下添加文件。
添加完文件后,您所添加源文件的属性自动变为只读,并在所添加文件的文件夹下生成一个vssver文件 ,以后对文件的操作基本与原文件没有关系了。
(2)浏览Source Safe Server中的文件
在Visual SourceSafe Explore中双击要打开的文件,会弹出一个对话框,您直接点击OK即可。这时SourceSafe Explore会将文件拷贝一份到本地机的临时文件夹中(临时文件夹路径在tools-options-general下设置),因原文件前面提到已经变了只读,所以临时文件也是只读属性,而且文件名会通过系统自动更改。
(3)设置工作文件夹
SourceSafe 的文件夹需要在本地计算机上指定一个“working folder”。当“check out”时,相应文件会下载到这个本地工作文件夹中。我们在本地的文件中修改文件,然后把修改后的文件“check in”回服务器的source safe中。