(转载)VC2005中MFC程序的部署问题
原文:http://rain.newegg.cn/user4/sunliguang/archives/2007/216734.html
在VC6中,Release版本的程序的部署,除了程序自身关联的各个Dll之外,只需添加MFC42.dll即可。
在VC2005中,发生的变化包括:
1.MFC的版本发生变化,最新的版本为8.0,所有应该包括MFC80.dll
2.在VC2005的架构下,采用manifest进行dll的版本确认,因此需要添加MFC程序所需的manifest文件。
在VC2005的安装目录下:D:"Program Files"Microsoft Visual Studio 8"VC"下有一个文件夹为redist专用于
程序的部署和发布。在其中的x86文件夹用于Release版本的程序发布,其中的Microsoft.VC80.MFC文件
夹用于发布MFC程序,包括混合有Unicode以及CLR的程序,可以根据程序需要选择copy。对于我个人的
单纯MFC的程序,只需要复制Microsoft.VC80.MFC.manifest和mfc80.dll即可。其余几个,文件名称中带有
“u”的表示兼容unicode编码,带有“m”表示使用托管代码生成规则。
还有另一个解决manifest文件的办法:
在VC2005的开发环境下,选择项目属性-->清单工具-->输入和输出,在潜入清单位置,选择“否”,这
样VC2005将会直接在Release目录下,exe文件的旁边为您生成一个同名的manifest文件,文件内容和
Microsoft.VC80.MFC.manifest有关联。直接复制这个文件也可以起到效果。
对不太熟练的开发人员,可以直接将这些dll统统复制到自己的exe文件所在目录下,应该可以确保万无
一失,只不过有的文件没有被利用而已。
最简单的程序发布方法莫过于直接复制文件,对依赖的dll文件,可以直接和exe文件放在同一个文件夹下,
而不将其复制到系统文件夹下。
Sockets/Windows Sockets错误码
Windows Sockets在头文件winsock.h中定义了所有的错误码,它们包括以“WSA”打头的
Windows Sockets实现返回的错误码和Berkeley Sockets定义的错误码全集。定义Berkeley
Sockets错误码是为了确保原有软件的可移植性。
WSAEACCES (10013) Permission denied.
试图使用被禁止的访问权限去访问套接字。例如,在没有使用函数setsockopt()的
SO_BROADCAST命令设置广播权限的套接字上使用函数sendto()给一个广播地址发送数据。
WSAEADDRINUSE (10048) Address already in use.
正常情况下每一个套接字地址(协议/IP地址/端口号)只允许使用一次。当应用程序试图
使用bind()函数将一个被已存在的或没有完全关闭的或正在关闭的套接字使用了的IP地址/
端口号绑扎到一个新套接字上时,该错误发生。对于服务器应用程序来说,如果需要使用
bind()函数将多个套接字绑扎到同一个端口上,可以考虑使用setsockopt()函数的
SO_REUSEADDR命令。客户应用程序一般不必使用bind()函数——connect()函数总是自动
选择没有使用的端口号。当bind()函数操作的是通配地址(包括ADDR_ANY)时,错误
WSAEADDRINUSE可能延迟到一个明确的地址被提交时才发生。这可能在后续的函数如
connect()、listen()、WSAConnect()或WSAJoinLeaf()调用时发生。
WSAEADDRNOTAVAIL (10049) Cannot assign requested address.
被请求的地址在它的环境中是不合法的。通常地在bind()函数试图将一个本地机器不合法的
地址绑扎到套接字时产生。它也可能在connect()、sendto()、WSAConnect()、WSAJoinLeaf()
或WSASendTo()函数调用时因远程机器的远程地址或端口号非法(如0地址或0端口号)而
产生。
WSAEAFNOSUPPORT (10047) Address family not supported by protocol family.
使用的地址与被请求的协议不兼容。所有的套接字在创建时都与一个地址族(如IP协议对
应的AF_INET)和一个通用的协议类型(如SOCK_STREAM)联系起来。如果在socket()调
用中明确地要求一个不正确的协议,或在调用sendto()等函数时使用了对套接字来说是错误
的地址族的地址,该错误返回。
WSAEALREADY (10037) Operation already in progress.
当在非阻塞套接字上已经有一个操作正在进行时,又有一个操作试图在其上执行则产生此错
误。如:在一个正在进行连接的非阻塞套接字上第二次调用connect()函数;或取消一个已经
被取消或已完成的异步请求(WSAAsyncGetXbyY())。
WSAECONNABORTED (10053) Software caused connection abort.
一个已建立的连接被你的主机上的软件终止,可能是因为一次数据传输超时或是协议错误。
WSAECONNREFUSED (10061) Connection refused.
因为目标主机主动拒绝,连接不能建立。这通常是因为试图连接到一个远程主机上不活动的
服务,如没有服务器应用程序处于执行状态。
WSAECONNRESET (10054) Connection reset by peer.
存在的连接被远程主机强制关闭。通常原因为:远程主机上对等方应用程序突然停止运行,
或远程主机重新启动,或远程主机在远程方套接字上使用了“强制”关闭(参见setsockopt
(SO_LINGER))。另外,在一个或多个操作正在进行时,如果连接因“keep-alive”活动检测
到一个失败而中断,也可能导致此错误。此时,正在进行的操作以错误码WSAENETRESET
失败返回,后续操作将失败返回错误码WSAECONNRESET。
WSAEDESTADDRREQ (10039) Destination address required.
在套接字上一个操作所必须的地址被遗漏。例如,如果sendto()函数被调用且远程地址为
ADDR_ANY时,此错误被返回。
WSAEFAULT (10014) Bad address.
系统检测到调用试图使用的一个指针参数指向的是一个非法指针地址。如果应用程序传递一
个非法的指针值,或缓冲区长度太小,此错误发生。例如,参数为结构sockaddr,但参数的
长度小于sizeof(struct sockaddr)。
WSAEHOSTDOWN (10064) Host is down.
套接字操作因为目的主机关闭而失败返回。套接字操作遇到不活动主机。本地主机上的网络
活动没有初始化。这些条件由错误码WSAETIMEDOUT指示似乎更合适。
WSAEHOSTUNREACH (10065) No route to host.
试图和一个不可达主机进行套接字操作。参见WSAENETUNREACH。
WSAEINPROGRESS (10036) Operation now in progress.
一个阻塞操作正在执行。Windows Sockets只允许一个任务(或线程)在同一时间可以有一
个未完成的阻塞操作,如果此时调用了任何函数(不管此函数是否引用了该套接字或任何其
它套接字),此函数将以错误码WSAEINPROGRESS返回。
WSAEINTR (10004) Interrupted function call.
阻塞操作被函数WSACancelBlockingCall ()调用所中断。
WSAEINVAL (10022) Invalid argument.
提供了非法参数(例如,在使用setsockopt()函数时指定了非法的level)。在一些实例中,
它也可能与套接字的当前状态相关,例如,在套接字没有使用listen()使其处于监听时调用
accept()函数。
WSAEISCONN (10056) Socket is already connected.
连接请求发生在已经连接的套接字上。一些实现对于在已连接SOCK_DGRAM套接字上使
用sendto()函数的情况也返回此错误(对于SOCK_STREAM套接字,sendto()函数的to参数
被忽略),尽管其它一些实现将此操作视为合法事件。
WSAEMFILE (10024) Too many open files.
打开了太多的套接字。不管是对整个系统还是每一进程或线程,Windows Sockets实现都可
能有一个最大可用的套接字句柄数。
WSAEMSGSIZE (10040) Message too long.
在数据报套接字上发送的一个消息大于内部消息缓冲区或一些其它网络限制,或者是用来接
受数据报的缓冲区小于数据报本身。
WSAENETDOWN (10050) Network is down.
套接字操作遇到一个不活动的网络。此错误可能指示网络系统(例如WinSock DLL运行的
协议栈)、网络接口或本地网络本身发生了一个严重的失败。
WSAENETRESET (10052) Network dropped connection on reset.
在操作正在进行时连接因“keep-alive”检测到失败而中断。也可能由setsockopt()函数返回,
如果试图使用它在一个已经失败的连接上设置SO_KEEPALIVE。
WSAENETUNREACH (10051) Network is unreachable.
试图和一个无法到达的网络进行套接字操作。它常常意味着本地软件不知道到达远程主机的
路由。
WSAENOBUFS (10055) No buffer space available.
由于系统缺乏足够的缓冲区空间,或因为队列已满,在套接字上的操作无法执行。
WSAENOPROTOOPT (10042) Bad protocol option.
在getsockopt()或setsockopt()调用中,指定了一个未知的、非法的或不支持的选项或层
(level)。
WSAENOTCONN (10057) Socket is not connected.
因为套接字没有连接,发送或接收数据的请求不被允许,或者是使用sendto()函数在数据报
套接字上发送时没有提供地址。任何其它类型的操作也可以返回此错误,例如,使用
setsockopt()函数在一个已重置的连接上设置SO_KEEPALIVE。
WSAENOTSOCK (10038) Socket operation on non-socket.
操作试图不是在套接字上进行。它可能是套接字句柄参数没有引用到一个合法套接字,或者
是调用select()函数时,一个fd_set中的成员不合法。
WSAEOPNOTSUPP (10045) Operation not supported.
对于引用的对象的类型来说,试图进行的操作不支持。通常它发生在套接字不支持此操作的
套接字描述符上,例如,试图在数据报套接字上接收连接。
WSAEPFNOSUPPORT (10046) Protocol family not supported.
协议簇没有在系统中配置或没有支持它的实现存在。它与WSAEAFNOSUPPORT有些微的
不同,但在绝大多数情况下是可互换的,返回这两个错误的所有Windows Sockets函数的说
明见WSAEAFNOSUPPORT的描述。
命令。
WSAEPROCLIM (10067) Too many processes.
Windows Sockets实现可能限制同时使用它的应用程序的数量,如果达到此限制,
WSAStartup()函数可能因此错误失败。
WSAEPROTONOSUPPORT (10043) Protocol not supported.
请求的协议没有在系统中配置或没有支持它的实现存在。例如,socket()调用请求一个
SOCK_DGRAM套接字,但指定的是流协议。
WSAEPROTOTYPE (10041) Protocol wrong type for socket.
在socket()函数调用中指定的协议不支持请求的套接字类型的语义。例如,ARPA Internet
UDP协议不能和SOCK_STREAM套接字类型一同指定。
WSAESHUTDOWN (10058) Cannot send after socket shutdown.
因为套接字在相应方向上已经被先前的shutdown()调用关闭,因此该方向上的发送或接收请
求不被允许。通过调用shutdown()函数来请求对套接字的部分关闭,它发送一个信号来停止
发送或接收或双向操作。
WSAESOCKTNOSUPPORT (10044) Socket type not supported.
不支持在此地址族中指定的套接字类型。例如,socket()调用中选择了可选的套接字类型
SOCK_RAW,但是实现却根本不支持SOCK_RAW类型的套接字。
WSAETIMEDOUT (10060) Connection timed out.
连接请求因被连接方在一个时间周期内不能正确响应而失败,或已经建立的连接因被连接的
主机不能响应而失败。
WSATYPE_NOT_FOUND (10109) Class type not found
指定的类没有找到。
WSAEWOULDBLOCK (10035) Resource temporarily unavailable.
此错误由在非阻塞套接字上不能立即完成的操作返回,例如,当套接字上没有排队数据可读
时调用了recv()函数。此错误不是严重错误,相应操作应该稍后重试。对于在非阻塞
SOCK_STREAM套接字上调用connect()函数来说,报告WSAEWOULDBLOCK是正常的,
因为建立一个连接必须花费一些时间。
WSAHOST_NOT_FOUND (11001) Host not found.
主机未知。此名字不是一个正式主机名,也不是一个别名,它不能在查询的数据库中找到。
此错误也可能在协议和服务查询中返回,它意味着指定的名字不能在相关数据库中找到。
WSA_INVALID_HANDLE (OS dependent) Specified event object handle is invalid.
应用程序试图使用一个事件对象,但指定的句柄非法。
WSA_INVALID_PARAMETER (OS dependent) One or more parameters are invalid.
应用程序使用了一个直接映射到Win32函数的WinSock函数,而Win32函数指示一个或多
个参数有问题。
WSAINVALIDPROCTABLE (OS dependent) Invalid procedure table from service
provider.
服务提供者返回了一个假的WS2_32.DLL程序(procedure)表。这通常是由一个或多个函数
指针为空引起。
WSAINVALIDPROVIDER (OS dependent) Invalid service provider version number.
服务提供者返回一个不同于2.2的版本号。
WSA_IO_INCOMPLETE (OS dependent) Overlapped I/O event object not in signaled
state.
应用程序试图检测一个没有完成的重叠操作的状态。应用程序使用函数
WSAGetOverlappedResult()(参数fWait设置为false)以轮询模式检测一个重叠操作是否完
成时将得到此错误码,除非该操作已经完成。
WSA_IO_PENDING (OS dependent) Overlapped operations will complete later.
应用程序已经初始化了一个不能立即完成的重叠操作。当稍后此操作完成时将有完成指示。
WSA_NOT_ENOUGH_MEMORY (OS dependent) Insufficient memory available.
应用程序使用了一个直接映射到Win32函数的WinSock函数,而Win32函数指示缺乏必要
的内存资源。
WSANOTINITIALISED (10093) Successful WSAStartup() not yet performed.
应用程序没有调用WSAStartup()函数,或函数WSAStartup()调用失败了。应用程序可能访问
了不属于当前活动任务的套接字(例如试图在任务间共享套接字),或调用了过多的
WSACleanup()函数。
WSANO_DATA (11004) Valid name, no data record of requested type.
请求的名字合法并且在数据库中找到了,但它没有正确的关联数据用于解析。此错误的通常
例子是主机名到地址(使用gethostbyname()或WSAAsyncGetHostByName()函数)的DNS转
换请求,返回了MX(Mail eXchanger)记录但是没有A(Address)记录,它指示主机本身
是存在的,但是不能直接到达。
WSANO_RECOVERY (11003) This is a non-recoverable error.
此错误码指示在数据库查找时发生了某种不可恢复错误。它可能是因为数据库文件(如BSD
兼容的HOSTS、SERVICES或PROTOCOLS文件)找不到,或DNS请求应服务器有严重错
误而返回。
WSAPROVIDERFAILEDINIT (OS dependent) Unable to initialize a service provider.
服务提供者的DLL不能加载(LoadLibrary()失败)或提供者的WSPStartup/NSPStartup函数
失败。
WSASYSCALLFAILURE (OS dependent) System call failure..
当一个不应该失败的系统调用失败时返回。例如,如果WaitForMultipleObjects()调用失败,
或注册的API不能够利用协议/名字空间目录。
WSASYSNOTREADY (10091) Network subsystem is unavailable.
此错误由WSAStartup()函数返回,它表示此时Windows Sockets实现因底层用来提供网络服
务的系统不可用。用户应该检查:
是否有合适的Windows Sockets DLL文件在当前路径中。
是否同时使用了多个WinSock实现。如果有多于一个的WINSOCK DLL在系统
中,必须确保搜索路径中第一个WINSOCK DLL文件是当前加载的网络子系统所
需要的。
查看WinSock实现的文档以确保所有必须的部件都正确地安装并配置好了。
WSATRY_AGAIN (11002) Non-authoritative host not found.
此错误通常是在主机名解析时的临时错误,它意味着本地服务器没有从授权服务器接收到一
个响应。稍后的重试可能会获得成功。
WSAVERNOTSUPPORTED (10092) WINSOCK.DLL version out of range.
当前的WinSock实现不支持应用程序指定的Windows Sockets规范版本。检查是否有旧的
Windows Sockets DLL文件正在被访问。
WSAEDISCON (10101) Graceful shutdown in progress.
由WSARecv()和WSARecvFrom()函数返回,指示远程方已经初始化了一个“雅致”的关闭
序列。
WSA_OPERATION_ABORTED (OS dependent) Overlapped operation aborted.
因为套接字的关闭,一个重叠操作被取消,或是执行了WSAIoctl()函数的SIO_FLUSH
使用标准C++的类型转换符:static_cast、dynamic_cast、reinterpret_cast、和const_cast。
3.1 static_cast
用法:static_cast < type-id > ( expression )
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
①用于类层次结构中基类和子类之间指针或引用的转换。
进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
3.2 dynamic_cast
用法:dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;
如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
class B{
public:
int m_iNum;
virtual void foo();
};
class D:public B{
public:
char *m_szName[100];
};
void func(B *pb){
D *pd1 = static_cast<D *>(pb);
D *pd2 = dynamic_cast<D *>(pb);
}
在上面的代码段中,如果pb指向一个D类型的对象,pd1和pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的;
但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),
而pd2将是一个空指针。
另外要注意:B要有虚函数,否则会编译出错;static_cast则没有这个限制。
这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(
关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,
没有定义虚函数的类是没有虚函数表的。
另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示。
class A{
public:
int m_iNum;
virtual void f(){}
};
class B:public A{
};
class D:public A{
};
void foo(){
B *pb = new B;
pb->m_iNum = 100;
D *pd1 = static_cast<D *>(pb); //compile error
D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL
delete pb;
}
在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错;而使用 dynamic_cast的转换则是允许的,结果是空指针。
3.3 reinpreter_cast
用法:reinpreter_cast<type-id> (expression)
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。
它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,
在把该整数转换成原类型的指针,还可以得到原先的指针值)。
该运算符的用法比较多。
3.4 const_cast
用法:const_cast<type_id> (expression)
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
Voiatile和const类试。举如下一例:
class B{
public:
int m_iNum;
}
void foo(){
const B b1;
b1.m_iNum = 100; //comile error
B b2 = const_cast<B>(b1);
b2. m_iNum = 200; //fine
}
上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;
使用const_cast把它转换成一个常量对象,就可以对它的数据成员任意改变。注意:b1和b2是两个不同的对象。
1、引言
在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯。WIN32 API提供了许多函数使我们能够方便高效的进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换,就如同在WIN16中对本地进程进行读写操作一样。
典型的WIN16两进程可以通过共享内存来进行数据交换:(1)进程A将GlobalAlloc(GMEM_SHARE...)API分配一定长度的内存;(2)进程A将GlobalAlloc函数返回的句柄传递给进程B(通过一个登录消息);(3)进程B对这个句柄调用GlobalLock函数,并利用GlobalLock函数返回的指针访问数据。这种方法在WIN32中可能失败,这是因为GlobalLock函数返回指向的是进程A的内存,由于进程使用的是虚拟地址而非实际物理地址,因此这一指针仅与A进程有关,而于B进程无关。
本文探讨了几种WIN32下进程之间通讯的几种实现方法,读者可以使用不同的方法以达到程序运行高效可靠的目的。
2、Windows95中进程的内存空间管理
WIN32进程间通讯与Windows95的内存管理有密切关系,理解Windows95的内存管理对我们如下的程序设计将会有很大的帮助,下面我们讨论以下Windows95中进程的内存空间管理。
在WIN16下,所有Windows应用程序共享单一地址,任何进程都能够对这一空间中属于共享单一的地址空间,任何进程都能够对这一空间中属于其他进程的内存进行读写操作,甚至可以存取操作系统本身的数据,这样就可能破坏其他程序的数据段代码。
在WIN32下,每个进程都有自己的地址空间,一个WIN32进程不能存取另一个地址的私有数据,两个进程可以用具有相同值的指针寻址,但所读写的只是它们各自的数据,这样就减少了进程之间的相互干扰。另一方面,每个WIN32进程拥有4GB的地址空间,但并不代表它真正拥有4GB的实际物理内存,而只是操作系统利用CPU的内存分配功能提供的虚拟地址空间。在一般情况下,绝大多数虚拟地址并没有物理内存于它对应,在真正可以使用这些地址空间之前,还要由操作系统提供实际的物理内存(这个过程叫“提交”commit)。在不同的情况下,系统提交的物理内存是不同的,可能是RAM,也可能是硬盘模拟的虚拟内存。
3、WIN32中进程间的通讯
在Windows 95中,为实现进程间平等的数据交换,用户可以有如下几种选择:
* 使用内存映射文件
* 通过共享内存DLL共享内存
* 向另一进程发送WM_COPYDATA消息
* 调用ReadProcessMemory以及WriteProcessMemory函数,用户可以发送由GlobalLock(GMEM_SHARE,...)函数调用提取的句柄、GlobalLock函数返回的指针以及VirtualAlloc函数返回的指针。
3.1、利用内存映射文件实现WIN32进程间的通讯
Windows95中的内存映射文件的机制为我们高效地操作文件提供了一种途径,它允许我们在WIN32进程中保留一段内存区域,把目标文件映射到这段虚拟内存中。在程序实现中必须考虑各进程之间的同步。具体实现步骤如下:
首先我们在发送数据的进程中需要通过调用内存映射API函数CreateFileMapping创建一个有名的共享内存:
HANDLE CreateFileMapping(
HANDLE hFile, // 映射文件的句柄,
//设为0xFFFFFFFF以创建一个进程间共享的对象
LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // 安全属性
DWORD flProtect, // 保护方式
DWORD dwMaximumSizeHigh, //对象的大小
DWORD dwMaximumSizeLow,
LPCTSTR lpName // 必须为映射文件命名
);
与虚拟内存类似,保护方式可以是PAGE_READONLY或是PAGE_READWRITE。如果多进程都对同一共享内存进行写访问,则必须保持相互间同步。映射文件还可以指定PAGE_WRITECOPY标志,可以保证其原始数据不会遭到破坏,同时允许其他进程在必要时自由的操作数据的拷贝。
在创建文件映射对象后使用可以调用MapViewOfFile函数映射到本进程的地址空间内。
下面说明创建一个名为MySharedMem的长度为4096字节的有名映射文件:
HANDLE hMySharedMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF),
NULL,PAGE_READWRITE,0,0x1000,"MySharedMem");
并映射缓存区视图:
LPSTR pszMySharedMapView=(LPSTR)MapViewOfFile(hMySharedMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
其他进程访问共享对象,需要获得对象名并调用OpenFileMapping函数。
HANDLE hMySharedMapFile=OpenFileMapping(FILE_MAP_WRITE,
FALSE,"MySharedMem");
一旦其他进程获得映射对象的句柄,可以象创建进程那样调用MapViewOfFile函数来映射对象视图。用户可以使用该对象视图来进行数据读写操作,以达到数据通讯的目的。
当用户进程结束使用共享内存后,调用UnmapViewOfFile函数以取消其地址空间内的视图:
if (!UnmapViewOfFile(pszMySharedMapView))
{ AfxMessageBox("could not unmap view of file"); }
3.2、利用共享内存DLL
共享数据DLL允许进程以类似于Windows 3.1 DLL共享数据的方式访问读写数据,多个进程都可以对该共享数据DLL进行数据操作,达到共享数据的目的。在WIN32中为建立共享内存,必须执行以下步骤:
首先创建一个有名的数据区。这在Visual C++中是使用data_seg pragma宏。使用data_seg pragma宏必须注意数据的初始化:
#pragma data_seg("MYSEC")
char MySharedData[4096]={0};
#pragma data_seg()
然后在用户的DEF文件中为有名的数据区设定共享属性。
LIBRARY TEST
DATA READ WRITE
SECTIONS
.MYSEC READ WRITE SHARED
这样每个附属于DLL的进程都将接受到属于自己的数据拷贝,一个进程的数据变化并不会反映到其他进程的数据中。
在DEF文件中适当地输出数据。以下的DEF文件项说明了如何以常数变量的形式输出MySharedData。
EXPORTS
MySharedData CONSTANT
最后在应用程序(进程)按外部变量引用共享数据。
extern _export"C"{char * MySharedData[];}
进程中使用该变量应注意间接引用。
m_pStatic=(CEdit*)GetDlgItem(IDC_SHARED);
m_pStatic->GetLine(0,*MySharedData,80);
3.3、用于传输只读数据的WM_COPYDATA
传输只读数据可以使用Win32中的WM_COPYDATA消息。该消息的主要目的是允许在进程间传递只读数据。Windows95在通过WM_COPYDATA消息传递期间,不提供继承同步方式。SDK文档推荐用户使用SendMessage函数,接受方在数据拷贝完成前不返回,这样发送方就不可能删除和修改数据:
SendMessage(hwnd,WM_COPYDATA,wParam,lParam);
其中wParam设置为包含数据的窗口的句柄。lParam指向一个COPYDATASTRUCT的结构:
typedef struct tagCOPYDATASTRUCT{
DWORD dwData;//用户定义数据
DWORD cbData;//数据大小
PVOID lpData;//指向数据的指针
}COPYDATASTRUCT;
该结构用来定义用户数据。
3.4、直接调用ReadProcessMemory和WriteProcessMemory函数实现进程间通讯
通过调用ReadProcessMemory以及WriteProcessMemory函数用户可以按类似与Windows3.1的方法实现进程间通讯,在发送进程中分配一块内存存放数据,可以调用GlobalAlloc或者VirtualAlloc函数实现:
pApp->m_hGlobalHandle=GlobalAlloc(GMEM_SHARE,1024);
可以得到指针地址:
pApp->mpszGlobalHandlePtr=(LPSTR)GlobalLock
(pApp->m_hGlobalHandle);
在接收进程中要用到用户希望影响的进程的打开句柄。为了读写另一进程,应按如下方式调用OpenProcess函数:
HANDLE hTargetProcess=OpenProcess(
STANDARD_RIGHTS_REQUIRED|
PROCESS_VM_REDA|
PROCESS_VM_WRITE|
PROCESS_VM_OPERATION,//访问权限
FALSE,//继承关系
dwProcessID);//进程ID
为保证OpenProcess函数调用成功,用户所影响的进程必须由上述标志创建。
一旦用户获得一个进程的有效句柄,就可以调用ReadProcessMemory函数读取该进程的内存:
BOOL ReadProcessMemory(
HANDLE hProcess, // 进程指针
LPCVOID lpBaseAddress, // 数据块的首地址
LPVOID lpBuffer, // 读取数据所需缓冲区
DWORD cbRead, // 要读取的字节数
LPDWORD lpNumberOfBytesRead
);
使用同样的句柄也可以写入该进程的内存:
BOOL WriteProcessMemory(
HANDLE hProcess, // 进程指针
LPVOID lpBaseAddress, // 要写入的首地址
LPVOID lpBuffer, // 缓冲区地址
DWORD cbWrite, // 要写的字节数
LPDWORD lpNumberOfBytesWritten
);
如下所示是读写另一进程的共享内存中的数据:
ReadProcessMemory((HANDLE)hTargetProcess,
(LPSTR)lpsz,m_strGlobal.GetBuffer(_MAX_FIELD),
_MAX_FIELD,&cb);
WriteProcessMemory((HANDLE)hTargetProcess,
(LPSTR)lpsz,(LPSTR)STARS,
m_strGlobal.GetLength(),&cb);
4、进程之间的消息发送与接收
在实际应用中进程之间需要发送和接收Windows消息来通知进程间相互通讯,发送方发送通讯的消息以通知接收方,接收方在收到发送方的消息后就可以对内存进行读写操作。
我们在程序设计中采用Windows注册消息进行消息传递,首先在发送进程初始化过程中进行消息注册:
m_nMsgMapped=::RegisterWindowsMessage("Mapped");
m_nMsgHandle=::RegisterWindowsMessage("Handle");
m_nMsgShared=::RegisterWindowsMessage("Shared");
在程序运行中向接收进程发送消息:
CWnd* pWndRecv=FindWindow(lpClassName,"Receive");
pWndRecv->SendMessage(m_MsgMapped,0,0);
pWndRecv->SendMessage(m_nMsgHandle,
(UINT)GetCurrentProcessID(),(LONG)pApp->m_hGlobalHandle);
pWndRecv->SendMessage(m_nMsgShared,0,0);
可以按如下方式发送WM_COPYDATA消息:
static COPYDATASTRUCT cds;//用户存放数据
pWnd->SendMessage(WM_COPYDATA,NULL,(LONG)&cds);
接收方进程初始化也必须进行消息注册:
UNIT CRecvApp:: m_nMsgMapped=::RegisterWindowsMessage("Mapped");
UNIT CRecvApp::m_nMsgHandle=::RegisterWindowsMessage("Handle");
UNIT CRecvApp::m_nMsgShared=::RegisterWindowsMessage("Shared");
同时映射消息函数如下:
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgMapped,OnRegMsgMapped)
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgHandle,OnRegMsgHandle)
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgShared,OnRegMsgShared)
在这些消息函数我们就可以采用上述技术实现接收进程中数据的读写操作了。
5、结束语
从以上分析中我们可以看出Windows95的内存管理与Windows 3.x相比有很多的不同,对进程之间的通讯有较为严格的限制。这就确保了任何故障程序无法意外地写入用户的地址空间,而用户则可根据实际情况灵活地进行进程间的数据通讯,从这一点上来讲Windows95增强应用程序的强壮性。
作者:Amit Dey 译:刘涛
近来,我写了一个outlook2000的Addin Com作为我建立CRM 工具的工程的一部分。当我为这个工程写代码的时候,我想这可能是一个很好的题目,因为我在internet上找到的与Office相关的资料大部分是VB/VBA 相关的,几乎没有与ATL相关的。
在这篇文章里的代码并没有进行优化,为了使读者便于跟随,我尽量将它写的浅显易懂。我写这篇文章花了一些时间,并且也尽了我的最大努力,万一还存在什么错误,请爽快的给我发封邮件。如果你喜欢这篇文章或者觉得它读起来很有趣,并给我一个高的评价或是发邮件告诉我你的看法,我将非常高兴。谢谢!
概况:
通过这篇文章,我们将会了解怎样使用纯ATL Com 对象编写Outlook2000/2K+ COM addin程序。我们将从写一个最基本的Com AddIn程序开始。接下来我将向你们展示怎样将标准的界面元素比如工具栏或是菜单项加入到outlook中去,并响应他们的事件。紧接着,我们要为Outlook's Tools->Options加入我们自己编写的属性表。接着我们将看一些相关的注册键和ATL向导的一些非常有用的特征并且学习有效地使用他们。
虽然我们写的是一个Outlook2000 COM addin的程序。但是Office2000的应用程序,比如Word,Access等等,他们的Com AddIn的写法是非常相似的。除了注册键和接口,其余的部分基本上是一样的。
我假设你是一个VC++ Com的开发人员,并且也有一些基于ATL的组件开发和OLE/自动化方面的经验,尽管这也不是必须的。创建和测试这个AddIn程序,你必须安装Office2000,至少有outlook2000。程序代码使用VC++ 6.0 sp3+/ATL3.0创建,使用的操作系统是:安装了Office2000的Windows2000。
开始:
Office AddIn 是一个可以动态扩充和增强的Com 自动化组件,可以控制任何的Office应用程序。微软的Office2000和以后的版本都支持创建Add_Ins的一个新的、统一的应用设计架构。AddIn通常都被置于一个ActiveX动态库中(进程内服务器),并且能被用户动态的从主程序中引导和卸载。
Office AddIn 必须实现 _IDTExtensibility2 接口。IDTExtensibility2接口定义于MSADDin Designer typelibrary (MSADDNDR.dll/MSADDNDR.tlb)文件中。一般在/Program Files/Common Files/Designer目录下。
接口象这样定义:
enum {
ext_cm_AfterStartup = 0,
ext_cm_Startup = 1,
ext_cm_External = 2,
ext_cm_CommandLine = 3
} ext_ConnectMode;
enum {
ext_dm_HostShutdown = 0,
ext_dm_UserClosed = 1
} ext_DisconnectMode;
...
...
...
interface _IDTExtensibility2 : IDispatch {
[id(0x00000001)]
HRESULT OnConnection(
[in] IDispatch* Application,
[in] ext_ConnectMode ConnectMode,
[in] IDispatch* AddInInst,
[in] SAFEARRAY(VARIANT)* custom);
[id(0x00000002)]
HRESULT OnDisconnection(
[in] ext_DisconnectMode RemoveMode,
[in] SAFEARRAY(VARIANT)* custom);
[id(0x00000003)]
HRESULT OnAddInsUpdate([in] SAFEARRAY(VARIANT)* custom);
[id(0x00000004)]
HRESULT OnStartupComplete([in] SAFEARRAY(VARIANT)* custom);
[id(0x00000005)]
HRESULT OnBeginShutdown([in] SAFEARRAY(VARIANT)* custom);
};
所有的Com AddIn继承于IDTExtensibility2,而且必须实现他的五个方法。
当AddIn被引导和卸载的时候,OnConnection 和 OnDisconnection, 就像他们的名字显示的一样。AddIn程序可以被引导,也可以在应用程序使用过程中被用户启动或者通过自动化和enumerator ext_Connect 指示连接到那些模块。当一组Com AddIn组件被改变,那么OnAddinsUpdate被调用。OnStartupComplete 只有在应用程序使用过程中启动Com AddIn组件时才被调用,如果AddIn在主应用程序被关掉的时候断开与主应用程序的连接,那么OnBeginShutdown 被调用。
注册AddIn组件:
使用主应用程序注册AddIn组件,我们需要在注册表目录:
HKEY_CURRENT_USER"Software"Microsoft"Office"<TheOfficeApp>"Addins"<ProgID> 下创建两个子键,这里ProgID指的是Addin Com对象的唯一标识符。别的入口通过AddIn提供的关于他自己的信息和制定的引导选项给主应用程的是:
FriendlyName – 字符串 – 主应用程序显示的这个AddIn程序的名字。
Description – 字符串 – 对AddIn的描述.
LoadBehavior - DWORD 值. –一个决定AddIn怎样被主应用程序引导的值的组合。 设置成 0x03 表示主应用程序启动时引导,设置成0x08表示由用户来激活。
CommandLineSafe - DWORD 值. 0x01(TRUE) 或者 0x00(FALSE).
对于所有值和可选项的完整描述,请参考MSDN。
创建一个小的Com AddIn:
现在我们了解了足够的知识,应该朝前一步编写一个小的Outlook2K COM addin。创建一个新的ATL COM Appwizard 工程,命名为OutlookAddin。记住如果你把他命名成别的,他可能会不能运行(开个玩笑)。
在向导的第一个对话框中接收默认的服务器类型Dynamic Link Library(DLL),检查Allow merging of proxy-stub code,选择这个可选项,点击完成。接着点击OK,产生工程文件。
下一步,点击Insert->New ATL Object菜单项,通过从Category中选择Objects从Objects列表中选择Simple Object插入一个ATL simple object到工程中。点击Next,输入”AddIn”作为ShortName,在属性表里选上Support ISupportErrorInfo。接受剩下的默认选项,然后点击OK。
到现在为止,向导已经给我们了一个置于动态链接库中的自动化兼容的、DispInterface-savvy的进程内的Com对象。默认的情况下,一个加到Com对象上的指定注册值的注册脚本文件被提交给我们。Build这个工程,看看一切是否运行良好。
如果你想我一样雄心勃勃,起码在继续往下进行前还应该编译你工程中的.idl文件。现在就去做吧。
接下来我们为AddIn写一些特定的代码去实现IDTExtensibility2 接口。在类视图里,我们在CAddIn类上右键点击,选择Implement Interface,这将带出ATL Implement Interface 向导。点击Add Typelib,在Browse Typelibraries对话框里向下滚动,选上Microsoft Add-in Designer(1.0),点击OK。在AddinDesignerObjects列表中选择_IDTExtensibility2接口点击OK。
向导为IDTExtensibility2接口的五个方法中每一个生成默认的实现,将他们加到CAddIn类中,并且更新COM_INTERFACE_MAP()宏。当然在加有些有用的代码之前每个方法都只会返回E_NOTIMPL。现在,为ComAddIn进行必要的注册,我们的Com AddIn已经就绪了。
使用主应用程序注册我们的Addin组件。如果是outlook2000,打开工程的AddIn.rgs注册脚本文件。把下面的代码加到文件的结尾。
HKCU
{
Software
{
Microsoft
{
Office
{
Outlook
{
Addins
{
'OutlookAddin.Addin'
{
val FriendlyName = s 'ADOutlook2K Addin'
val Description = s 'ATLCOM Outlook Addin'
val LoadBehavior = d '00000008'
val CommandLineSafe = d '00000000'
}
}
}
}
}
}
}
既然我们希望在程序启动的时候AddIn被引导,那么LoadBehavior设置为3。现在Build这个工程。如果一切顺利,那么将会创建成功并且注册了这个AddIn。为了测试这个AddIn,我们要运行这个工程并输入完整的outlook.exe的完整的路径("Program Files"Microsoft Office"Office"Outlook.exe),或者在注册了这个DLL之后从VC++IDE环境外运行outlook。如果你的AddIn被成功的注册了,那么在outlook里,点击Tools->Options,在Other页点击Advanced Options->COM Addins,我们的AddIn应该已经出现在可获得的AddIns的列表中。字符串是我们在脚本中为'FriendlyName'指定的值。
AddIn可以被编写来执行各种不同的任务。典型的,包括为outlook添加一些界面元素,比如工具条和菜单项,而且用户可以控制AddIn。通过点击这个工具条按钮和菜单项,用户可以实现AddIn的功能。接下来我们将制作这样一个工具条和附加的菜单项。
命令与征服:
在Office应用程序中,菜单和工具条被组合在一个名叫“CommandBars “的完全可编程的集合中。CommandBars通常是可共享可编程的对象,并且作为所有的office应用程序的一部分被暴露。CommandBars代表一个同一的机制,通过他可以将单个的工具条和菜单项加到相应的应用程序里。每一个CommandBars由几个独立的CommandBar对象组成。每一个CommandBar又由CommandBarControl对象集合组成,这个集合被叫做CommandBarControls。
CommandBarControls代表了一个复杂的对象和组成它的子对象层次。一个CommandBarControl能被包含在一个CommandBar中,并且通过控件的CommandBar属性访问。最后每一个在控件的CommandBarControls集合中的CommandBarControl即可能是CommandBarComboBox、CommandBarButton(工具条按钮)也可能是CommandBarPopup(弹出式菜单)。我很希望我能画出一个代表这个对象层次的图例,但是我很不擅长这个(我很诚实!)。我保证在MSDN中一定有关于MS Office CommandBars描述的图例。
在我们的AddIn里,我想加入以下的界面元素:
? 在一个新的工具条里加入两个位图按钮。
? 在“Tool“菜单里添加一个新的带位图的弹出式菜单项。
首先,我们应该将office和outlook的类型库导入到我们的工程中。我们打开stdAfx.h,然后添加以下语句:
#import "C:"Program Files"Microsoft Office"Office"mso9.dll" "
rename_namespace("Office") named_guids
using namespace Office;
#import "C:"Program Files"Microsoft Office"Office"MSOUTL9.olb"
rename_namespace("Outlook"), raw_interfaces_only, named_guids
using namespace Outlook;
注意:你应该改变这些路径,是他们匹配你安装的office的路径。
好了,现在让我们来看看代码。首先式ToolBand和ToolBar Button。
在outlook模块里,Application 对象位于代表整个应用程序的对象层次的最顶层。通过他的ActiveExplorer 方法我们可以得到代表当前窗口的Explorer对象。下来我们使用GetCommandBars方法得到CommandBars对象(他是outlook工具条和菜单项的集合)。我们使用CommandBars集合的Add方法加上相应的参数就可以添加一个新的工具条。如果想向工具条中加入按钮只需要得到工具条的CommandBarControls集合,接着调用他的Add方法。最后我们为那些对应于按钮的CommandBarButton对象(我们可以用它来设置按钮的风格和别的属性,比如标题、提示和文本等等)。
代码片断如下:
STDMETHODIMP CAddin::OnConnection(IDispatch * Application,
ext_ConnectMode ConnectMode,
IDispatch * AddInInst, SAFEARRAY * * custom)
{
CComPtr < Office::_CommandBars> spCmdBars;
CComPtr < Office::CommandBar> spCmdBar;
// QI() for _Application
CComQIPtr <Outlook::_Application> spApp(Application);
ATLASSERT(spApp);
// get the CommandBars interface that represents Outlook's
//toolbars & menu items
CComPtr<Outlook::_Explorer> spExplorer;
spApp->ActiveExplorer(&spExplorer);
HRESULT hr = spExplorer->get_CommandBars(&spCmdBars);
if(FAILED(hr))
return hr;
ATLASSERT(spCmdBars);
// now we add a new toolband to Outlook
// to which we'll add 2 buttons
CComVariant vName("OutlookAddin");
CComPtr <Office::CommandBar> spNewCmdBar;
// position it below all toolbands
//MsoBarPosition::msoBarTop = 1
CComVariant vPos(1);
CComVariant vTemp(VARIANT_TRUE); // menu is temporary
CComVariant vEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR);
//Add a new toolband through Add method
// vMenuTemp holds an unspecified parameter
//spNewCmdBar points to the newly created toolband
spNewCmdBar = spCmdBars->Add(vName, vPos, vEmpty, vTemp);
//now get the toolband's CommandBarControls
CComPtr < Office::CommandBarControls> spBarControls;
spBarControls = spNewCmdBar->GetControls();
ATLASSERT(spBarControls);
//MsoControlType::msoControlButton = 1
CComVariant vToolBarType(1);
//show the toolbar?
CComVariant vShow(VARIANT_TRUE);
CComPtr < Office::CommandBarControl> spNewBar;
CComPtr < Office::CommandBarControl> spNewBar2;
// add first button
spNewBar = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow);
ATLASSERT(spNewBar);
// add 2nd button
spNewBar2 = spBarControls->Add(vToolBarType, vEmpty, vEmpty, vEmpty, vShow);
ATLASSERT(spNewBar2);
_bstr_t bstrNewCaption(OLESTR("Item1"));
_bstr_t bstrTipText(OLESTR("Tooltip for Item1"));
// get CommandBarButton interface for each toolbar button
// so we can specify button styles and stuff
// each button displays a bitmap and caption next to it
CComQIPtr < Office::_CommandBarButton> spCmdButton(spNewBar);
CComQIPtr < Office::_CommandBarButton> spCmdButton2(spNewBar2);
ATLASSERT(spCmdButton);
ATLASSERT(spCmdButton2);
// to set a bitmap to a button, load a 32x32 bitmap
// and copy it to clipboard. Call CommandBarButton's PasteFace()
// to copy the bitmap to the button face. to use
// Outlook's set of predefined bitmap, set button's FaceId to //the
// button whose bitmap you want to use
HBITMAP hBmp =(HBITMAP)::LoadImage(_Module.GetResourceInstance(),
MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
// put bitmap into Clipboard
::OpenClipboard(NULL);
::EmptyClipboard();
::SetClipboardData(CF_BITMAP, (HANDLE)hBmp);
::CloseClipboard();
::DeleteObject(hBmp);
// set style before setting bitmap
spCmdButton->PutStyle(Office::msoButtonIconAndCaption);
HRESULT hr = spCmdButton->PasteFace();
if (FAILED(hr))
return hr;
spCmdButton->PutVisible(VARIANT_TRUE);
spCmdButton->PutCaption(OLESTR("Item1"));
spCmdButton->PutEnabled(VARIANT_TRUE);
spCmdButton->PutTooltipText(OLESTR("Tooltip for Item1"));
spCmdButton->PutTag(OLESTR("Tag for Item1"));
//show the toolband
spNewCmdBar->PutVisible(VARIANT_TRUE);
spCmdButton2->PutStyle(Office::msoButtonIconAndCaption);
//specify predefined bitmap
spCmdButton2->PutFaceId(1758);
spCmdButton2->PutVisible(VARIANT_TRUE);
spCmdButton2->PutCaption(OLESTR("Item2"));
spCmdButton2->PutEnabled(VARIANT_TRUE);
spCmdButton2->PutTooltipText(OLESTR("Tooltip for Item2"));
spCmdButton2->PutTag(OLESTR("Tag for Item2"));
spCmdButton2->PutVisible(VARIANT_TRUE);
//..........
//..........
//code to add new menubar to be added here
//read on
//..........
我们用相似的方法来给outlook的Tools菜单添加菜单项,我们照以下方法做。CommandBars的ActiveMenuBar属性返回一个表示在Application容器中活动的菜单。我们通过GetControls方法找到活动的菜单控件集合。我们想要加入一个弹出式的菜单项到outlook的Tools菜单(第6个菜单项),我们从Activemenubars控件集合中可以找到第6个菜单项,直接调用Add方法创建一个新的菜单项并且将他连接到Tools菜单。这里没有什么新东西。
相应的代码片断如下所示:
//......
//code to add toolbar here
//......
_bstr_t bstrNewMenuText(OLESTR("New Menu Item"));
CComPtr < Office::CommandBarControls> spCmdCtrls;
CComPtr < Office::CommandBarControls> spCmdBarCtrls;
CComPtr < Office::CommandBarPopup> spCmdPopup;
CComPtr < Office::CommandBarControl> spCmdCtrl;
// get CommandBar that is Outlook's main menu
hr = spCmdBars->get_ActiveMenuBar(&spCmdBar);
if (FAILED(hr))
return hr;
// get menu as CommandBarControls
spCmdCtrls = spCmdBar->GetControls();
ATLASSERT(spCmdCtrls);
// we want to add a menu entry to Outlook's 6th(Tools) menu //item
CComVariant vItem(5);
spCmdCtrl= spCmdCtrls->GetItem(vItem);
ATLASSERT(spCmdCtrl);
IDispatchPtr spDisp;
spDisp = spCmdCtrl->GetControl();
// a CommandBarPopup interface is the actual menu item
CComQIPtr < Office::CommandBarPopup> ppCmdPopup(spDisp);
ATLASSERT(ppCmdPopup);
spCmdBarCtrls = ppCmdPopup->GetControls();
ATLASSERT(spCmdBarCtrls);
CComVariant vMenuType(1); // type of control - menu
CComVariant vMenuPos(6);
CComVariant vMenuEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR);
CComVariant vMenuShow(VARIANT_TRUE); // menu should be visible
CComVariant vMenuTemp(VARIANT_TRUE); // menu is temporary
CComPtr < Office::CommandBarControl> spNewMenu;
// now create the actual menu item and add it
spNewMenu = spCmdBarCtrls->Add(vMenuType, vMenuEmpty, vMenuEmpty,
vMenuEmpty, vMenuTemp);
ATLASSERT(spNewMenu);
spNewMenu->PutCaption(bstrNewMenuText);
spNewMenu->PutEnabled(VARIANT_TRUE);
spNewMenu->PutVisible(VARIANT_TRUE);
//we'd like our new menu item to look cool and display
// an icon. Get menu item as a CommandBarButton
CComQIPtr < Office::_CommandBarButton> spCmdMenuButton(spNewMenu);
ATLASSERT(spCmdMenuButton);
spCmdMenuButton->PutStyle(Office::msoButtonIconAndCaption);
// we want to use the same toolbar bitmap for menuitem too.
// we grab the CommandBarButton interface so we can add
// a bitmap to it through PasteFace().
spCmdMenuButton->PasteFace();
// show the menu
spNewMenu->PutVisible(VARIANT_TRUE);
return S_OK;
}
点击F5,如果一切都没问题,那么工程将成功建立,并且你将第一次看见你的AddIn程序的运行。现在我们运行outlook来测试我们的AddIn。在'Executable for Debug'对话框,设置outlook可执行程序的当前路径,现在我们准备测试。在outlook中点击Tools->Option,点击Other页面,点击Advanced Options。在Advanced Option对话框中,点击Com AddIns 按钮。接着从可获得的AddIns列表中选择我们的AddIn并点击OK。当我们的AddIn被引导,一个停靠工具条将被创建,你也可以看到你加入到Tools菜单的菜单项。
他们就在那里!一个有你写的AddIn的outlook,一个带有很酷的工具条和新的菜单项的扩展的outlook!感谢ATL!你的小于50Kb的AddIn同样提供了轻量级的有意义的Com服务。享受这一刻吧!
单单放置两个工具条按钮和一个菜单项并没有什么用处,除非我们写命令处理代码和响应他们的事件。现在我们回到正题。当然在这里,点击不同的按钮和菜单项,我们紧紧弹出简单的对话框。这就是你添加AddIn功能的地方。从CRM 工具、自动联系管理、邮件通知、邮件过滤到高级的文档管理到各种各样的应用,Com AddIns可以执行各种各样的任务的验证。
CommandBarButton控件暴露了一个点击事件(当用户点击一个Command Bar 按钮时触发)。当用户点击工具条按钮或者是点击菜单项的时候我们将使用这个事件去运行代码。对于这些,我们的Com AddIn对象不得不处理_CommandBarButtonEvents事件。点击事件被声明如下:
//...
//....Office objects typelibrary
//....
[id(0x00000001), helpcontext(0x00038271)]
void Click(
[in] CommandBarButton* Ctrl,
[in, out] VARIANT_BOOL* CancelDefault);
//....
//...
我们不得不做所有我们能做的事情去实现那些将被事件源通过规范的连接点协议调用的接收器接口(无论什么时候一个工具条按钮或菜单项被点击)。通过回调函数我们可以得到一个源CommandBarButton 对象的指针和一个用来接受和取消默认操作的布尔值。就像实现一个dispatch接收器接口一样,那也不是什么新东西,作为一个ATL程序员你可能要花一段时间去做这些。
但是对于那些非初始化的,ATL为ATLCom对象提供两个模板类IDispEventImpl<> 和 IDispEventSimpleImpl<> ,这为IDispatch接口提供了实现。我更喜欢用轻量级的IDispEventSimpleImpl,因为它不需要另外的类型库信息。你的类紧紧源于IDispEventSimpleImpl<>。建立你的接收器映射,通过_ATL_SINK_INFO结构体设置你的回调参数,最后调用DispEventAdvise 和 DispEventUnadvise从源接口连接和断开。对于我们的工具条按钮和菜单项,如果我们要写一个单一的回调函数来处理所有的事件,那么,一旦我们有一个指向触发事件的CommandBarButton的指针,我们可以使用GetCaption去得到这个按钮的文本,在这个基础上,我们可以执行一些选择性的动作。但是对于这个例子,我们为每一个事件编写一个回调函数。
下面是编写的步骤:
使你的类继承于IDispSimpleEventImpl-第一个参数是封装在ActiveX控件中的子窗口的ID。但是对于我们来说,它可以是任何预先定义的唯一标识事件源的整数(在这里指的是第一个工具条按钮)。
class ATL_NO_VTABLE CAddin :
public CComObjectRootEx < CComSingleThreadModel>,
.....
.....
public IDispEventSimpleImpl<1,CAddin,&__uuidof(Office::_CommandBarButtonEvents>
建立回调函数-第一个我们定义的,如下所示:
void __stdcall OnClickButton(IDispatch * /*Office::_CommandBarButton**/ Ctrl,VARIANT_BOOL * CancelDefault);
接下来我们使用_ATL_SINK_INFO结构去描述回调参数。打开AddIn.h文件,在文件顶部添加如下声明:
? extern _ATL_FUNC_INFO OnClickButtonInfo;
接着打开AddIn.cpp,添加如下定义:
? _ATL_FUNC_INFO OnClickButtonInfo ={CC_STDCALL,VT_EMPTY,2,{VT_DISPATCH,VT_BYREF | VT_BOOL}};
OnClickButton是非常基础的,就像下面的:
? void __stdcall CAddin::OnClickButton(IDispatch* /*Office::_CommandBarButton* */ Ctrl,
? VARIANT_BOOL * CancelDefault)
? {
? USES_CONVERSION;
? CComQIPtr<Office::_CommandBarButton> pCommandBarButton(Ctrl);
? //the button that raised the event. Do something with this...
? MessageBox(NULL, "Clicked Button1", "OnClickButton", MB_OK);
?
? }
我们使用ATL宏BEGIN_SINK_MAP() 和 END_SINK_MAP()建立接收器消息映射。接收器消息映射由SINK_ENTRY_XXX组成。接收器消息映射提供定义事件的Dispatch ID和处理他的成员函数。
? BEGIN_SINK_MAP(CAddin)
? SINK_ENTRY_INFO(1, __uuidof(Office::_CommandBarButtonEvents),/*dispid*/ 0x01,
? OnClickButton, &OnClickButtonInfo)
? END_SINK_MAP()
现在每一件事情都到位了,我们不得不使用DispEventAdvise() and DispEventUnadvise()连接和断开事件源.我们的CAddIn类的OnConnection() 和OnDisconnection()仅仅是替代了这些。对于DispEventAdvise() and DispEventUnadvise()的参数分别是事件源上的任何的接口和任何被期望的事件源上的接口。
//connect to event source in OnConnection
// m_spButton member variable is a smart pointer to _CommandBarButton
// that is used to cache the pointer to the first toolbar button.
DispEventAdvise((IDispatch*)m_spButton,&DIID__CommandBarButtonEvents);
//when I'm done disconnect from the event source
//some where in OnDisconnection()
DispEventUnadvise((IDispatch*)m_spButton);
为我们的命令按钮和菜单项实现Dispatch 接收器是很相似的,写处理代码并且连接和断开他们就像上面的描述。如果每一步都进行的畅通无阻,在你Rebuild你的程序并且运行它。无论什么时候,按钮和菜单项被点击,你的回调函数将被执行。
添加属性页:
在这篇文章里我们最后要学会去做的是添加我们自己的“Option“属性页到outlook的Tools->Option的属性表中。
下来我们要加一个页到outlook的option菜单里作为我们我们的AddIn的一部分。我们将象ActiveX控件一样实现实现属性页。当用户点击Tools->Option菜单项,应用程序对象发出一个OptionsPagesAdd事件(通过outlook对象模块中的_ApplicationEvents接口)。
dispinterface ApplicationEvents
{
....
[id(0x0000f005), helpcontext(0x0050df87)]
void OptionsPagesAdd([in] PropertyPages* Pages);
....
}
[
odl,
uuid(00063080-0000-0000-C000-000000000046),
helpcontext(0x0053ec78),
dual,
oleautomation
]
....
....
interface PropertyPages : IDispatch {
[id(0x0000f000), propget, helpcontext(0x004deb87)]
HRESULT Application([out, retval] _Application** Application);
....
....
[id(0x0000005f), helpcontext(0x00526624)]
HRESULT Add([in] VARIANT Page,
[in, optional] BSTR Title);
[id(0x00000054), helpcontext(0x00526625)]
HRESULT Remove([in] VARIANT Index);
};
OptionsPagesAdd事件传递给我们我们一个PropertyPages Dispatch接口,他的Add方法用来添加页。Add方法的参数是我们的控件的ProgID和新的页的标题文本。相似的,我们调用Remove()方法和要删除页的索引来删除页。
现在我们来加一个ActiveX复合控件。我们点击Insert->new ATL Object.从Category中选择Controls,从Object列表中选择Lite Composite Control,点击OK。在ShortName中输入PropPage,在属性页面选上Support ISupportErrorInfo选项。点击Ok,接受所有的默认选项。
现在我们要来实现PropertyPage接口。在类视图里右键点击CPropPage,选择Implement Interface,点击Add TypeLib按钮。选中Microsoft Outlook 9.0 Object Library 点击OK。从接口列表中选择PropertyPage点击OK。
向导自动为PropertyPage接口添加三个方法:Apply()、Get_Dirty()、GetPageInfo()。现在做下面的修改,在Com Map中把这一行:
COM_INTERFACE_ENTRY(IDispatch)
改成:
COM_INTERFACE_ENTRY2(IDispatch,IPropPage)
以排除不明确的地方。
接下来实现IDispatch,我们使用IDispatchImpl<>模板类。我们在CPropPage类的声明部分用以下代码:
public IDispatchImpl < Outlook::PropertyPage,&__uuidof(Outlook::PropertyPage),
&LIBID_OUTLOOKADDINLib>
替换掉:
class ATL_NO_VTABLE CPropPage :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<IPropPage, &IID_IPropPage, &LIBID_TRAILADDINLib>,
....
....
public PropertyPage
从PropPage.h文件的顶部删掉多余的#import语句。类型库已经在stdAfx.h中导入了,因此这里没有必要再导入。
下来我们要连接和断开ApplicationEvents接口,并为他写回调函数。你已经知道该做什么了。我们再次使用IDispEventSimpleImpl<>为ApplicationEvents建立Dispatch接收器,更新接收器映射,为OptionsAddPage事件写回调函数。因为我们多次使用了IDispEventSimpleImpl<>, 我们为每一个接口事件使用TypeDef。代码片段如下:
extern _ATL_FUNC_INFO OnOptionsAddPagesInfo;
class ATL_NO_VTABLE CAddin :
....
....
public IDispEventSimpleImpl<4,CAddin,&__uuidof(Outlook::ApplicationEvents)>
{
public:
//typedef for applicationEvents sink implementation
typedef IDispEventSimpleImpl</*nID =*/ 4,CAddin,
&__uuidof(Outlook::ApplicationEvents)> AppEvents;
....
....
....
BEGIN_SINK_MAP(CAddin)
....
SINK_ENTRY_INFO(4,__uuidof(Outlook::ApplicationEvents),
/*dispid*/0xf005,OnOptionsAddPages,&OnOptionsAddPagesInfo)
END_SINK_MAP()
public:
//callback method for OptionsAddPages event
void __stdcall OnOptionsAddPages(IDispatch *Ctrl);
};
//in PropPage.cpp file
_ATL_FUNC_INFO OnOptionsAddPagesInfo = (CC_STDCALL,VT_EMPTY,1,{VT_DISPATCH}};
void __stdcall CAddin::OnOptionsAddPages(IDispatch* Ctrl)
{
CComQIPtr<Outlook::PropertyPages> spPages(Ctrl);
ATLASSERT(spPages);
//ProgId of the propertypage control
CComVariant varProgId(OLESTR("OutlookAddin.PropPage"));
//tab text
CComBSTR bstrTitle(OLESTR("OutlookAddin"));
HRESULT hr = spPages->Add((_variant_t)varProgId,(_bstr_t)bstrTitle);
if(FAILED(hr))
ATLTRACE(""nFailed adding propertypage");
}
最后,在OnConnection和OnDisConnection里,调用DispEventAdvise 和 DispEventUnadvise连接和断开ApplicationEvents。现在一切就绪,我们ReBuild工程。下来点击F5,点击Outlook的Tools->Options菜单。你应该看见了我们新加的页。但是当我们点击这个新的页,一个对话框将出现告诉我们属性页不能被显示。发生了什么?难道我们的辛苦劳动白费了?
发生这个情况的原因是:尽管我们的属性页创建了,但是outlook并没有得到关于这个页的键盘行为的任何信息。IOleControl的GetControlInfo方法的ATL的默认实现返回E_NOTIMPL,因此包容器无法为这个属性页和包容器处理击键事件。因此我们的页不能被显示。修改这个问题,只需重载GetControlInfo()方法,让他返回S_OK。
在.PropPage.h里添加如下声明:
STDMETHOD(GetControlInfo)(LPCONTROLINFO lpCI);
我们在PropPage.cpp文件里重载GetControlInfo()方法,仅仅将返回值改为S_OK,代码如下:
STDMETHODIMP CPropPage::GetControlInfo(LPCONTROLINFO lpCI)
{
return S_OK;
}
就是这些了。现在再次Build工程,点击outlook的tools->Option,激活我们的页,现在我们的属性页应该正确无误的显示了。
我们的学习要结束了。我们能在office里我们能做的事情无穷无尽。因为在一个AddIn里你可以获得父应用程序的内部对象模块,你能做所有主应用程序能做的事,或者更多。另外你也能使用别的接口比如MS Assistant(并不直接关联到应用程序)。没有做不到的只有想不到的。
函数调用方式分为两类:标准调用约定、C/C++调用约定。
标准调用约定(__stdcall):这些函数将在返回到调用者之间将参数从栈中删除。
C/C++调用约定(__cdecl):栈的清理工作由调用者来完成。
除C/C++之外的其他语言缺省情况下使用标准调用。
MICROSOFT平台上的COM接口提供的所有函数使用的均是标准调用约定,参数可变的函数使用的则是C调用约定。
WINDEF.H中
#define pascal __stdcall
OBJBASE.H中
#define STDMETHODCALLTYPE __stdcall
导入PPT所需类型库
#import "C:"Program Files"Microsoft Office"Office"mso9.dll" rename_namespace("Office") "
rename("RGB","OfficeRGB") "
rename("DocumentProperties","OfficeDocumentProperties")
using namespace Office;
#import "C:"Program Files"Common Files"Microsoft Shared"VBA"VBA6"VBE6EXT.olb" rename_namespace("VBE6")
using namespace VBE6;
#import "C:"Program Files"Microsoft Office"Office"MSPPT9.OLB" named_guids,rename_namespace("MSPPT") "
rename("RGB","PPTRGB")
using namespace MSPPT;
摘 要:介绍了动态链接库这种模块复用方法及在VC中对它的调用,并给出了一个通过复用来实现数据加密的具体实例。
关键词:VC DLL 模块复用 数据加密
引言
模块化思想贯穿于软件工程各个发展阶段,模块复用是构建大系统的一种重要思想。模块复用方法有:函数、函数库、动态链接库、COM。其都是基于模块化的基本思想。函数是最简单的模块化思想,也是后面方法的基础,甚至是一个应用程序的基础。函数库是函数的组合,一般将一些功能相似的函数放在一起作为函数库,这种函数库通常叫做静态库,其链接方式是静态的。COM即组件对象模型,是一种集成技术,可以使程序在运行时把各种不相关的软件程序混合在一起,而不必考虑这些不相关的程序是用什么语言编写的,它也是一种标准或者称为协议,负责将一个软件模块和另一个软件连接起来。动态链接库DLL(Dynamic Link Library)是一个可以被其它应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源,其链接方式是动态的。动态链接库文件的扩展名一般是dll,也有可能是fon、sys和dry,它和可执行文件(.exe)非常相似,区别在于DLL中虽然包含了可执行代码却不能单独执行,而应由Windows应用程序直接或间接调用。Windows操作系统包含大量动态链接库,其中最主要的是KERNEL32.DLL、USER32.DLL、GDI32.DLL 。
DLL的调用
调用DLL,首先需要将DLL文件映像到用户进程的地址空间中,然后才能进行函数调用,这个函数和进程内部一般函数的调用方法相同。Windows提供了两种将DLL映像到进程地址空间的方法:
1、隐式的加载时链接
这种方法需要DLL工程经编译产生的LIB文件,此文件中包含了DLL允许应用程序调用的所有函数的列表,当链接器发现应用程序调用了LIB文件列出的某个函数,就会在应用程序的可执行文件的文件映像中加入一些信息,这些信息指出了包含这个函数的DLL文件的名字。当这个应用程序运行时,也就是它的可执行文件被操作系统产生映像文件时,系统会查看这个映像文件中关于DLL的信息,然后将这个DLL文件映像到进程的地址空间。
系统通过DLL文件的名称,试图加载这个文件到进程地址空间时,它寻找DLL 文件的路径按照先后顺序如下:
烦绦蛟诵惺钡哪柯迹纯芍葱形募诘哪柯迹?br />
返鼻俺绦蚬ぷ髂柯?br />
废低衬柯迹憾杂赪indows95/98来说,可以调用GetSystemDirectory函数来得到,对于WindowsNT/2000来说,指的是32位Windows的系统目录,也可以调用GetSystemDirectory函数来得到,得到的值为SYSTEM32。
稺indows目录
妨性赑ATH环境变量中的所有目录
VC中加载DLL的LIB文件的方法有以下三种:
①LIB文件直接加入到工程文件列表中
在VC中打开File View一页,选中工程名,单击鼠标右键,然后选中"Add Files to Project"菜单,在弹出的文件对话框中选中要加入DLL的LIB文件即可。
②设置工程的 Project Settings来加载DLL的LIB文件
打开工程的 Project Settings菜单,选中Link,然后在Object/library modules下的文本框中输入DLL的LIB文件。
③通过程序代码的方式
加入预编译指令#pragma comment (lib,"*.lib"),这种方法优点是可以利用条件预编译指令链接不同版本的LIB文件。因为,在Debug方式下,产生的LIB文件是Debug版本,如Regd.lib;在Release方式下,产生的LIB文件是Release版本,如Regr.lib。
当应用程序对DLL的LIB文件加载后,还需要把DLL对应的头文件(*.h)包含到其中,在这个头文件中给出了DLL中定义的函数原型,然后声明。
2、显式的运行时链接
隐式链接虽然实现较简单,但除了必须的*.dll文件外还需要DLL的*.h文件和*.lib文件,在那些只提供*.dll文件的场合就无法使用,而只能采用显式链接的方式。这种方式通过调用API函数来完成对DLL的加载与卸载,其能更加有效地使用内存,在编写大型应用程序时往往采用此方式。这种方法编程具体实现步骤如下:
①使用Windows API函数Load Library或者MFC提供的AfxLoadLibrary将DLL模块映像到进程的内存空间,对DLL模块进行动态加载。
②使用GetProcAddress函数得到要调用DLL中的函数的指针。
③不用DLL时,用Free Library函数或者AfxFreeLibrary函数从进程的地址空间显式卸载DLL。
VC中调用实例
数据加密是计算机安全领域的重要内容,其基本思想是通过变换信息的表现形式来保护敏感信息,使非授权者不能了解被保护信息的内容。常见的数据加密算法有:DES,IDEA,RSA,ECC,AES,MD5,SHA等。
《共享软件加密算法库》是一款针对个人、企业开发共享软件的加密工具,支持Windows平台下各类开发工具:VC、VB、Delphi、PB、VFP等,算法库集成的算法有:BlowFish、MD5、Secret16、AES、SHA、CRC32、RSA、DES、字符串加/解密、文件加/解密等多种功能强大的算法。其提供了DLL文件-Reg.dll,可以通过复用它来实现数据加密与解密。
1、隐式链接
其提供了 Reg.h与Reg.lib两个隐式链接所必须的文件,所以可以采用此种方式。
①在VC中打开File View一页,选中工程名,单击鼠标右键,然后选中"Add Files to Project"菜单,在弹出的文件对话框中选中要加入Reg.lib。
②在VC中打开File View一页,选中Header files,单击鼠标右键,然后选中"Add Files to Folder"菜单,在弹出的文件对话框中选中要加入Reg.h,然后在工程相应的头文件中加入#include "Reg.h"。在Reh.h头文件中给出了DLL中定义的函数原型及声明。
如:加密函数原型及声明为extern "C" BOOL WINAPI File Encrypt(LPCTSTR lpInputFileName, LPCTSTR lpOutputFileName, LPCTSTR lpKey, LPCTSTR lpRegisterCode);解密函数原型及声明为extern "C" BOOL WINAPI File Decrypt(LPCTSTR lpInputFileName, LPCTSTR lpOutputFileName, LPCTSTR lpKey, LPCTSTR lpRegisterCode)。其中对于WINAPI宏,把它加到函数原型定义前,系统会把它翻译为适当的调用方式,在Win32中,是把它翻译为_stdcall调用方式。
③直接调用所需要的加密与解密函数,如调用File Encrypt()函数实现文本文件和二进制文件的加密,调用File Decrypt()函数实现文本文件和二进制文件的解密,调用时的参数要与函数定义参数相符合。
2、显式链接
如果只提供Reg.dll一个文件,那么须用此种方式。
①加密模块:调用File Encrypt()函数实现文本文件和二进制文件的加密。
//装载加密/解密DLL HINSTANCE hdll=::Load Library ("Reg.dll"); //通过类型定义语句typedef来定义函数指针类型 Typedef BOOL (_stdcall *lpFileEncrypt)(LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR); //函数声明 LpFileEncrypt FileEncrypt1; //获取加密函数File Encrypt的函数指针 FileEncrypt1=(lpFileEncrypt)::GetProcAddress(hdll,"FileEncrypt"); //调用DLL中加密函数File Encrypt对文件加密,user-12345678为软件注册号 FileEncrypt1(加密源文件名,加密生成目标文件名,密码,"user-12345678"); //释放DLL模块 ::AfxFreeLibrary(hdll); |
②解密模块:调用File Decrypt()函数实现文本文件和二进制文件的解密。
//装载加密/解密DLL HINSTANCE hdll=::Load Library ("Reg.dll"); //通过类型定义语句typedef来定义函数指针类型 Typedef BOOL (_stdcall *lpFileDecrypt)(LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR); //函数声明 LpFileDecrypt FileDecrypt2; //获取解密函数File Decrypt的函数指针 FileDecrypt2=(lpFileDecrypt)::GetProcAddress(hdll,"FileDecrypt"); //调用DLL中解密函数FileDecrypt对文件加密,user-12345678为软件注册号 FileDecrypt2(解密源文件名,解密生成目标文件名,密码,"user-12345678"); //释放DLL模块 ::AfxFreeLibrary(hdll); |
结束语
利用DLL这种模块复用方法可以减少软件工程开发的工作量,增强代码的可移植性,降低模块测试的复杂性,从总体上提高软件工程的开发效率。
12.1 多任务、进程和线程
12.1.1 Windows 3.x的协同多任务 在16位的Windows 3.x中,应用程序具有对CPU的控制权。只有在调用了GetMessage、PeekMessage、WaitMessage或Yield后,程序才有可能把CPU控制权交给系统,系统再把控制权转交给别的应用程序。如果应用程序在长时间内无法调用上述四个函数之一,那么程序就一直独占CPU,系统会被挂起而无法接受用户的输入。 因此,在设计16位的应用程序时,程序员必须合理地设计消息处理函数,以使程序能够尽快返回到消息循环中。如果程序需要进行费时的操作,那么必须保证程序在进行操作时能周期性的调用上述四个函数中的一个。 在Windows 3.x环境下,要想设计一个既能执行实时的后台工作(如对通信端口的实时监测和读写),又能保证所有界面响应用户输入的单独的应用程序几乎是不可能的。 有人可能会想到用CWinApp::OnIdle函数来执行后台工作,因为该函数是程序主消息循环在空闲时调用的。但OnIdle的执行并不可靠,例如,如果用户在程序中打开了一个菜单或模态对话框,那么OnIdle将停止调用,因为此时程序不能返回到主消息循环中!在实时任务代码中调用PeekMessage也会遇到同样的问题,除非程序能保证用户不会选择菜单或弹出模态对话框,否则程序将不能返回到PeekMessage的调用处,这将导致后台实时处理的中断。 折衷的办法是在执行长期工作时弹出一个非模态对话框并禁止主窗口,在消息循环内分批执行后台操作。对话框中可以显示工作的进度,也可以包含一个取消按钮以让用户有机会中断一个长期的工作。典型的代码如清单12.1所示。这样做既可以保证工作实时进行,又可以使程序能有限地响应用户输入,但此时程序实际上已不能再为用户干别的事情了。 清单12.1 在协同多任务环境下防止程序被挂起的一种方法 bAbort=FALSE; lpMyDlgProc=MakeProcInstance(MyDlgProc, hInst); hMyDlg=CreateDialog(hInst, “Abort”, hwnd, lpMyDlgProc); //创建一个非模态对话框 ShowWindow(hMyDlg, SW_NORMAL); UpdateWindow(hMyDlg); EnableWindow(hwnd, FALSE); //禁止主窗口 . . . while(!bAbort) { . . . //执行一次后台操作 . . . while(PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) { if(!IsDialogMessage(hMyDlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } EnableWindow(hwnd, TRUE); //允许主窗口 DestroyWindow(hMyDlg); FreeProcInstance(lpMyDlgProc);
12.1.2 Windows 95/NT的抢先式多任务 在32位的Windows系统中,采用的是抢先式多任务,这意味着程序对CPU的占用时间是由系统决定的。系统为每个程序分配一定的CPU时间,当程序的运行超过规定时间后,系统就会中断该程序并把CPU控制权转交给别的程序。与协同式多任务不同,这种中断是汇编语言级的。程序不必调用象PeekMessage这样的函数来放弃对CPU的控制权,就可以进行费时的工作,而且不会导致系统的挂起。 例如,在Windows3.x 中,如果某一个应用程序陷入了死循环,那么整个系统都会瘫痪,这时唯一的解决办法就是重新启动机器。而在Windows 95/NT中,一个程序的崩溃一般不会造成死机,其它程序仍然可以运行,用户可以按Ctrl+Alt+Del键来打开任务列表并关闭没有响应的程序。 12.1.3 进程与线程 在32位的Windows系统中,术语多任务是指系统可以同时运行多个进程,而每个进程也可以同时执行多个线程。 进程就是应用程序的运行实例。每个进程都有自己私有的虚拟地址空间。每个进程都有一个主线程,但可以建立另外的线程。进程中的线程是并行执行的,每个线程占用CPU的时间由系统来划分。 可以把线程看成是操作系统分配CPU时间的基本实体。系统不停地在各个线程之间切换,它对线程的中断是汇编语言级的。系统为每一个线程分配一个CPU时间片,某个线程只有在分配的时间片内才有对CPU的控制权。实际上,在PC机中,同一时间只有一个线程在运行。由于系统为每个线程划分的时间片很小(20毫秒左右),所以看上去好象是多个线程在同时运行。 进程中的所有线程共享进程的虚拟地址空间,这意味着所有线程都可以访问进程的全局变量和资源。这一方面为编程带来了方便,但另一方面也容易造成冲突。 虽然在进程中进行费时的工作不会导致系统的挂起,但这会导致进程本身的挂起。所以,如果进程既要进行长期的工作,又要响应用户的输入,那么它可以启动一个线程来专门负责费时的工作,而主线程仍然可以与用户进行交互。 12.1.4 线程的创建和终止 线程分用户界面线程和工作者线程两种。用户界面线程拥有自己的消息泵来处理界面消息,可以与用户进行交互。工作者线程没有消息泵,一般用来完成后台工作。 MFC应用程序的线程由对象CWinThread表示。在多数情况下,程序不需要自己创建CWinThread对象。调用AfxBeginThread函数时会自动创建一个CWinThread对象。 例如,清单12.2中的代码演示了工作者线程的创建。AfxBeginThread函数负责创建新线程,它的第一个参数是代表线程的函数的地址,在本例中是MyThreadProc。第二个参数是传递给线程函数的参数,这里假定线程要用到CMyObject对象,所以把pNewObject指针传给了新线程。线程函数MyThreadProc用来执行线程,请注意该函数的声明。线程函数有一个32位的pParam参数可用来接收必要的参数。 清单12.2 创建一个工作者线程 //主线程 pNewObject = new CMyObject; AfxBeginThread(MyThreadProc, pNewObject);
//新线程 UINT MyThreadProc( LPVOID pParam ) { CMyObject* pObject = (CMyObject*)pParam;
if (pObject == NULL || !pObject->IsKindOf(RUNTIME_CLASS(CMyObject))) return -1; // 非法参数
// 用pObject对象来完成某项工作
return 0; // 线程正常结束 }
AfxBeginThread的声明为:
参数pfnThreadProc是工作线程函数的地址。pParam是传递给线程函数的参数。nPriority是线程的优先级,一般是THREAD_PRIORITY_NORMAL,若为0,则使用创建线程的优先级。nStackSize说明了线程的堆栈尺寸,若为0则堆栈尺寸与创建线程相同。dwCreateFlags指定了线程的初始状态,如果为0,那么线程在创建后立即执行,如果为CREATE_SUSPENDED,则线程在创建后就被挂起。参数lpSecurityAttrs用来说明保密属性,一般为0。函数返回新建的CWinThread对象的指针。 程序应该把AfxBeginThread返回的CWinThread指针保存起来,以便对创建的线程进行控制。例如,可以调用CWinThread::SetThreadPriority来设置线程的优先级,用CWinThread::SuspendThread来挂起线程。如果线程被挂起,那么直到调用CWinThread::ResumeThread后线程才开始运行。 如果要创建用户界面线程,那么必须从CWinThread派生一个新类。事实上,代表进程主线程的CWinApp类就是CWinThread的派生类。派生类必须用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏来声明和实现。需要重写派生类的InitInstance、ExitInstance、Run等函数。 可以使用AfxBeginThread函数的另一个版本来创建用户界面线程。函数的声明为:
参数pThreadClass指向一个CRuntimeClass对象,该对象是用RUNTIME_CLASS宏从CWinThread的派生类创建的。其它参数以及函数的返回值与第一个版本的AfxBeginThread是一样的。 当发生下列事件之一时,线程被终止:
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Smartphone & PocketPC dotNet Compact Framework
- 要查询的远程设备的地址,BT_ADDR类型,定义在Ws2bth.h中:
- 提供Winsock的版本和实现细节的数据来初始化caller application。可以通过调用WSAStartup函数来获得这个数据。
- 通过设置WSAQUERYSET结构体来指定搜索参数。
- 调用WSALookupServiceBegin函数初始化搜索,将第一步中创建的WSAQUERYSET变量传递给pQuerySet参数来指定搜索标准
- 要返回在远程设备上所找到的服务的相关数据,使用从WSALookupServiceBegin返回的句柄调用 WSALookupServiceNext函数。
- 提供Winsock的版本和实现细节的数据来初始化caller application。可以通过调用WSAStartup函数来获得这个数据。
- 创建并初始化一个WSAQUERYSET变量用于指定搜索参数,设置dwNameSpace成员为NS_BTH限制为查询蓝牙设备。
- 调用WSALookupServiceBegin函数来执行一个查询。
- 要枚举在上一步中调用WSALookupServiceBegin所找到的设备,就要使用WSALookupServiceNex函数。函数返回一个指向存有查询结果的WSAQUERYSET及构体。
- 设置一个WSAQUERYSET结构体来储存WSALookupServiceNext函数返回的设备数据。
- 调用WSALookupServiceNext并将WSALookupServiceBegin返回的hLookUp参数传递给它。为了提高性能,对WSALookupServiceBegin 的调用只返回设备的地址并存储在内存中。要返回设备的名称和地址,就要将LUP_RETURN_NAME | LUP_RETURN_ADDR赋给dwFlags参数。
- 调用WSALookupServiceEnd函数来结束设备搜索。这个函数将释放由WSALookupServiceBegin创建的lookup句柄。
- 要结束对Winsock服务的使用,调用WSACleanup函数。在程序中对每个成功调用的WSAStartup都必须对应地调用WSACleanup。
要结束对Winsock服务的使用,调用WSACleanup函数。在程序中对每个成功调用的WSAStartup都必须对应地调用WSACleanup。
一、基本知识
Win32下串口通信与16位串口通信有很大的区别。在Win32下,可以使用两种编程方式实现串口通信,其一是调用的Windows的API函数,其二是使用ActiveX控件。使用API 调用,可以清楚地掌握串口通信的机制,熟悉各种配置和自由灵活采用不同的流控进行串口通信。下面介绍串口操作的基本知识。
打开串口:使用CreateFile()函数,可以打开串口。有两种方法可以打开串口,一种是同步方式(NonOverlapped),另外一种异步方式(Overlapped)。使用Overlapped打开时,适当的方法是:
HANDLE hComm;
hComm = CreateFile( gszPort,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
if (hComm == INVALID_HANDLE_VALUE)
// error opening port; abort
配置串口:
1.DCB配置
DCB(Device Control Block)结构定义了串口通信设备的控制设置。许多重要设置都是在DCB结构中设置的,有三种方式可以初始化DCB。
(1)通过GetCommState()函数得DCB的初始值,其使用方式为:
DCB dcb = {0};
if (!GetCommState(hComm, &dcb))
// Error getting current DCB settings
else
// DCB is ready for use.
(2)用BuildCommDCB()函数初始化DCB结构,该函数填充 DCB的波特率、奇偶校验类型、数据位、停止位。对于流控成员函数设置了缺省值。其用法是:
DCB dcb;
FillMemory(&dcb, sizeof(dcb), 0);
dcb.DCBlength = sizeof(dcb);
if (!BuildCommDCB(“9600,n,8,1"", &dcb)) {
// Couldn"'t build the DCB. Usually a problem
// with the communications specification string.
return FALSE;
}
else
// DCB is ready for use.
(3)用SetCommState()函数手动设置DCB初值。用法如下:
DCB dcb;
FillMemory(&dcb, sizeof(dcb), 0);
if (!GetCommState(hComm, &dcb)) // get current DCB
// Error in GetCommState
return FALSE;
// Update DCB rate.
dcb.BaudRate = CBR_9600 ;
// Set new state.
if (!SetCommState(hComm, &dcb))
// Error in SetCommState.
Possibly a problem with the communications
// port handle or a problem with the DCB structure itself.
手动设置DCB值时,DCB的结构的各成员的含义,可以参看MSDN帮助。
2.流控设置
硬件流控:串口通信中的硬件流控有两种,DTE/DSR方式和RTS/CTS方式,这与DCB结构的初始化有关系,DCB结构中的OutxCtsFlow、 fOutxDsrFlow、fDsrSensitivity、fRtsControl、fDtrControl几个成员的初始值很关键,不同的值代表不同流控,也可以自己设置流控,但建议采用标准流行的流控方式。采用硬件流控时,DTE、DSR、RTS、CTS的逻辑位直接影响到数据的读写及收发数据的缓冲区控制。
软件流控:串口通信中采用特殊字符XON和XOFF作为控制串口数据的收发。与此相关的DCB成员是:fOut、fInX、XoffChar、XonChar、 XoffLim和XonLim。具体含义参见MSDN帮助。
串口读写操作:串口读写有两种方式:同步方式(NonOverlapped)和异步方式(Overlapped)。同步方式是指必须完成了读写操作,函数才返回,这可能造成程序死掉,因为如果在读写时发生了错误,永远不返回就会出错,可能线程将永远等待在那儿。而异步方式则灵活得多,一旦读写不成功,就将读写挂起,函数直接返回,可以通过GetLastError函数得知读写未成功的原因,所以常常采用异步方式操作。
读操作:ReadFile()函数用于完成读操作。异步方式的读操作为:
DWORD dwRead;
BOOL fWaitingOnRead = FALSE;
OVERLAPPED osReader = {0};
// Create the overlapped event. Must be closed before exiting
// to avoid a handle leak.
osReader.hEvent = CreateEvent
(NULL, TRUE, FALSE, NULL);
if (osReader.hEvent == NULL)
// Error creating overlapped event; abort.
if (!fWaitingOnRead) {
// Issue read operation.
if (!ReadFile(hComm, lpBuf, READ_BUF_SIZE,
&dwRead, &osReader)) {
if (GetLastError() != ERROR_IO_PENDING)
// read not delayed?
// Error in communications; report it.
else
fWaitingOnRead = TRUE;
}
else {
// read completed immediately
HandleASuccessfulRead(lpBuf, dwRead);
}
}
如果读操作被挂起,可以调用WaitForSingleObject()函数或WaitForMuntilpleObjects()函数等待读操作完成或者超时发生,再调用 GetOverlappedResult()得到想要的信息。
写操作:与读操作相似,故不详述,调用的API函数是: WriteFile函数。
串口状态:
(1)通信事件:用SetCommMask()函数设置想要得到的通信事件的掩码,再调用WaitCommEvent()函数检测通信事件的发生。可设置的通信事件标志(即SetCommMask()函数所设置的掩码)可以有EV_BREAK、EV_CTS、EV_DSR、 EV_ERR、EV_RING、EV_RLSD、EV_RXCHAR、EV_RXFLAG、EV_TXEMPTY。
注意:1对于EV_RING标志的设置,WIN95是不会返回EV_RING事件的,因为WIN95不检测该事件。2设置EV_RXCHAR,可以检测到字符到达,但是在绑定此事件和ReadFile()函数一起读取串口接收数据时,可能会出现错误,造成少读字节数,具体原因查看MSDN帮助。可以采用循环读的办法,另外一个比较好的解决办法是调用ClearCommError()函数,确定在一次读操作中在缓冲区中等待被读的字节数。
(2)错误处理和通信状态:在串口通信中,可能会产生很多的错误,使用ClearCommError()函数可以检测错误并且清除错误条件。
(3)Modem状态:用SetcommMask()可以包含很多事件标志,但是这些事件标志只指示在串口线路上的电压变化情况。而调用 GetCommModemStatus()函数可以获得线路上真正的电压状态。
扩展函数:如果应用程序想用自己的流控,可以使用 EscapeCommFunction()函数设置DTR和RTS线路的电平。
通信超时:在通信中,超时是个很重要的考虑因素,因为如果在数据接收过程中由于某种原因突然中断或停止,如果不采取超时控制机制,将会使得I/O线程被挂起或无限阻塞。串口通信中的超时设置分为两步,首先设置 COMMTIMEOUTS结构的五个变量,然后调用SetcommTimeouts()设置超时值。对于使用异步方式读写的操作,如果操作挂起后,异步成功完成了读写,WaitForSingleObject()或 WaitForMultipleObjects()函数将返回WAIT_OBJECT_0,GetOverlappedResult()返回TRUE。其实还可以用GetCommTimeouts()得到系统初始值。
关闭串口:程序结束或需要释放串口资源时,应该正确关闭串口,关闭串口比较简单,使用API调用CloseHandle()关闭串口的句柄就可以了。
调用方法为:CloseHandle(hComm);
但是值得注意的是在关闭串口之前必须保证读写串口线程已经退出,否则会引起误操作,一般采用的办法是使用事件驱动机制,启动一事件,通知串口读写线程强制退出,在线程退出之前,通知主线程可以关闭串口。
二、实现
1.程序设计思路
对于不同的应用程序,虽然界面不同,但是如果采用串口与主机之间的通信,对串口的处理方式大致相似,无非就是通过串口收发数据,对于通过串口接收到的数据,交给上层软件处理显示,对于上层要发给串口的数据,进行转发。但在实际编程中,由于采用的通信方式和流控不同,串口设置也不同,这就涉及到 DCB的初始化问题和读写串口等细节问题。串口通信应用程序设计的总体思路(即操作过程)是:首先,确定要打开的串口名、波特率、奇偶校验方式、数据位、停止位,传递给CreateFile()函数打开特定串口;其次,为了保护系统对串口的初始设置,调用 GetCommTimeouts()得到串口的原始超时设置;然后,初始化DCB对象,调用SetCommState() 设置DCB,调用SetCommTimeouts()设置串口超时控制;再次,调用SetupComm()设置串口接收发送数据的缓冲区大小,串口的设置就基本完成,之后就可以启动读写线程了。
一般来说,串口的读写由串口读写线程完成,这样可以避免读写阻塞时主程序死锁。对于全双工的串口读写,应该分别开启读线程和写线程;对于半双工和单工的,建议只需开启一个线程即可。在线程中,按照预定好的通信握手方式,正确检测串口状态,读取发送串口数据。
2.实现细节
在半双工的情况下,首先完成必要的串口配置,成功打开串口、DCB设置、超时设置;然后开启线程,如: CwinThread hSerialThread = (CWinThread*) AfxBeginThread(SerialOperation,hWnd,THREAD_PRIORITY_NORMAL); 其中开启之线程为SerialOperation,优先级为普通。
全双工情况下的串口编程,与单工差不多,区别仅仅在于启动双线程,分别为读线程和写线程,读线程根据不同的事件或消息,通过不断查询串口所收到的有效数据,完成读操作;写线程通过接收主线程的发送数据事件和要发送的数据,向串口发送。
串口是常用的计算机与外部串行设备之间的数据传输通道,由于串行通信方便易行,所以应用广泛。我们可以利用Windows API 提供的通信函数编写出高可移植性的串行通信程序。本实例介绍在Visual C++6.0下如何利用Win32 API 实现串行通信程序。程序编译运行后的界面效果如图一所示:
图一、串口通信示例程序
一、实现方法
在Win16中,可以利用OpenComm()、CloseComm()和WriteComm()等函数打开、关闭和读写串口。但在Win32中,串口和其他通信设备均被作为文件处理,串口的打开、关闭和读写等操作所用的API函数与操作文件的函数相同。可通过CreateFile()函数打开串口;通过CloseFile()函数关闭串口;通过DCB结构、CommProp()、GetCommProperties()、SetCommProperties()、GetCommState()及SetCommState()等函数设置串口状态,通过函数ReadFile()和WritFile()等函数读写串口。下面来详细介绍其实现原理。
对于串行通信设备,Win32 API支持同步和异步两种I/O操作。同步操作方式的程序设计相对比较简单,但I/O操作函数在I/O操作结束前不能返回,这将挂起调用线程,直到I/O操作结束。异步操作方式相对要复杂一些,但它可让耗时的I/O操作在后台进行,不会挂起调用线程,这在大数据量通信的情况下对改善调用线程的响应速度是相当有效的。异步操作方式特别适合同时对多个串行设备进行I/O操作和同时对一个串行设备进行读/写操作。
串行设备的初始化
串行设备的初始化是利用CreateFile()函数实现的。该函数获得串行设备句柄并对其进行通信参数设置,包括设置输出/接收缓冲区大小、超时控制和事件监视等。 例如下面的代码实现了串口的初始化:
//串行设备句柄;
HANDLE hComDev=0;
//串口打开标志;
BOOL bOpen=FALSE;
//线程同步事件句柄;
HANDLE hEvent=0;
DCB dcb;
COMMTIMEOUTS timeouts;
//设备已打开
if(bOpen) return FALSE;
//打开COM1
if((hComDev=CreateFile(“COM1”,GENERIC?READ|GENERIC?WRITE,0,NULL,OPEN?EXISTING,FILE?ATTRIBUTE?NORMAL,NULL))==INVALID?HANDLE?VALUE)
return FALSE;
//设置超时控制
SetCommTimeouts(hComDev,&timeouts);
//设置接收缓冲区和输出缓冲区的大小
SetupComm(hComDev,1024,512);
//获取缺省的DCB结构的值
GetCommState(hComDev,&dcb);
//设定波特率为9600 bps
dcb.BaudRate=CBR?9600;
//设定无奇偶校验
dcb.fParity=NOPARITY;
//设定数据位为8
dcb.ByteSize=8;
//设定一个停止位
dcb.StopBits=ONESTOPBIT;
//监视串口的错误和接收到字符两种事件
SetCommMask(hComDev,EV?ERR|EV?RXCHAR);
//设置串行设备控制参数
SetCommState(hComDev,&dcb);
//设备已打开
bOpen=TRUE;
//创建人工重设、未发信号的事件
hEvent=CreateEvent(NULL,FALSE,FALSE,
“WatchEvent”);
//创建一个事件监视线程来监视串口事件
AfxBeginThread(CommWatchProc,pParam);
}
在设置串口DCB结构的参数时,不必设置每一个值。首先读出DCB缺省的参数设置,然后只修改必要的参数,其他参数都取缺省值。由于对串口进行的是同步I/O操作,所以除非指定进行监测的事件发生,否则WaitCommEvent()函数不会返回。在串行设备初始化的最后要建立一个单独的监视线程来监视串口事件,以免挂起当前调用线程,其中pParam可以是一个对事件进行处理的窗口类指针。
如果要进行异步I/O操作,打开设备句柄时,CreateFile的第6个参数应增加FILE?FLAG?OVERLAPPED 标志。
数据发送
数据发送利用WriteFile()函数实现。对于同步I/O操作,它的最后一个参数可为NULL;而对异步I/O操作,它的最后一个参数必需是一个指向OVERLAPPED结构的指针,通过OVERLAPPED结构来获得当前的操作状态。
BOOL WriteComm(LPCVOID lpSndBuffer,DWORD dwBytesToWrite)
{
//lpSndBuffer为发送数据缓冲区指针,
dwBytesToWrite为将要发送的字节长度
//设备已打开
BOOL bWriteState;
//实际发送的字节数
DWORD dwBytesWritten;
//设备未打开
if(!bOpen) return FALSE;
bWriteState=WriteFile(hComDev,lpSndBuffer,dwBytesToWrite,&dwBytesWritten,NULL);
if(!bWriteState || dwBytesToWrite!=dwBytesWritten)
//发送失败
return FALSE;
else
//发送成功
return TRUE;
}
数据接收
接收数据的任务由ReadFile函数完成。该函数从串口接收缓冲区中读取数据,读取数据前,先用ClearCommError函数获得接收缓冲区中的字节数。接收数据时,同步和异步读取的差别同发送数据是一样的。
DWORD ReadComm(LPVOID lpInBuffer,DWORD dwBytesToRead)
{
//lpInBuffer为接收数据的缓冲区指针, dwBytesToRead为准备读取的数据长度(字节数)
//串行设备状态结构
COMSTAT ComStat;
DWORD dwBytesRead,dwErrorFlags;
//设备未打开
if(!bOpen) return 0;
//读取串行设备的当前状态
ClearCommError(hComDev,&dwErrorFlags,&ComStat);
//应该读取的数据长度
dwBytesRead=min(dwBytesToRead,ComStat.cbInQue);
if(dwBytesRead>0)
//读取数据
if(!ReadFile(hComDev,lpInBuffer,dwBytesRead,&dwBytesRead,NULL))
dwBytesRead=0;
return dwBytesRead;
}
事件监视线程
事件监视线程对串口事件进行监视,当监视的事件发生时,监视线程可将这个事件发送(SendMessage)或登记(PostMessage)到对事件进行处理的窗口类(由pParam指定)中。
UINT CommWatchProc(LPVOID pParam)
{
DWORD dwEventMask=0; //发生的事件;
while(bOpen)
{
//等待监视的事件发生
WaitCommEvent(hComDev, &dwEventMask,NULL);
if ((dwEventMask & EV?RXCHAR)==EV?RXCHAR)
……//接收到字符事件后,可以将此消息登记到由pParam有指定的窗口类中进行处理
if(dwEventMask & EV?ERR)==EV?ERROR)
……//发生错误时的处理
}
SetEvent(hEvent);
//发信号,指示监视线程结束
return 0;
}
关闭串行设备
在整个应用程序结束或不再使用串行设备时,应将串行设备关闭,包括取消事件监视,将设备打开标志bOpen置为FALSE以使事件监视线程结束,清除发送/接收缓冲区和关闭设备句柄。
void CloseSynComm()
{
if(!bOpen) return;
//结束事件监视线程
bOpen=FALSE;
SetCommMask(hComDev,0);
//取消事件监视,此时监视线程中的WaitCommEvent将返回
WaitForSingleObject(hEvent,INFINITE);
//等待监视线程结束
CloseHandle(hEvent); //关闭事件句柄
//停止发送和接收数据,并清除发送和接收缓冲区
PurgeComm(hComDev,PURGE?TXABORT| PURGE?RXABORT|PURGE?TXCLEAR|PURGE?RXCLEAR);
//关闭设备句柄
CloseHandle(hComDev);
}
二、编程步骤
1、 启动Visual C++6.0,生成一个基于对话框的的应用程序,将该程序命名为“SerealCom”;
2、 按照图一的界面设计对话框,具体设置参见代码部分;
3、 使用Class Wizard为对话框的按钮添加鼠标单击消息响应函数;
4、 添加代码,编译运行程序。
三、程序代码
//////////////////////////////////////////////////////////////
#if !defined(_COMM_ACCESS_FUNCTIONS_AND_DATA)
#define _COMM_ACCESS_FUNCTIONS_AND_DATA
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define EVENTCHAR 0x0d
#define MAXBLOCKLENGTH 59
extern BYTE XwCom;
extern BYTE sCom1[5],sCom2[MAXBLOCKLENGTH+12];
extern sCom3[MAXBLOCKLENGTH+12];
extern BYTE opation;
extern short ComNum;
#define FC_DTRDSR 0x01
#define FC_RTSCTS 0x02
#define FC_XONXOFF 0x04
#define ASCII_BEL 0x07
#define ASCII_BS 0x08
#define ASCII_LF 0x0A
#define ASCII_CR 0x0D
#define ASCII_XON 0x11
#define ASCII_XOFF 0x13
class CComStatus
{
public:
HANDLE m_hCom;
BYTE m_bComId;
BYTE m_bByteSize;
BYTE m_bStopBits;
BYTE m_bParity;
DWORD m_dwBaudRate;
//WORD m_fChEvt;
char m_bEvtChar;
DWORD m_fBinary;
BOOL m_bConnected;
BOOL m_fXonXoff;
BOOL m_bFlowCtrl;
OVERLAPPED m_rdos;
OVERLAPPED m_wtos;
//functions
CComStatus();
CComStatus(BYTE bComId,BYTE bByteSize,BYTE bStopBits,BYTE bParity,
DWORD dwBaudRate,/*WORD fChEvt,*/char bEvtChar,DWORD fBinary);
BOOL OpenConnection();
BOOL CloseConnection();
BOOL SetupConnection();
BOOL IsConnected();
};
UINT CommWatchProc( LPVOID lpData );
BOOL WriteCommBlock( CComStatus& comDev, LPSTR lpByte , DWORD dwBytesToWrite);
int ReadCommBlock(CComStatus& comDev,LPSTR lpszBlock, int nMaxLength );
int ReadCommBlockEx(CComStatus& comDev,LPSTR lpszBlock, int nMaxLength,DWORD dwTimeOut);
#endif
///////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "com232.h"
BYTE XwCom=0x40;
BYTE sCom1[5],sCom2[MAXBLOCKLENGTH+12],sCom3[MAXBLOCKLENGTH+12];
BYTE opation;
short ComNum;
CComStatus::CComStatus()
{
m_hCom = NULL;
m_bComId = (char)ComNum;//COM1
m_bByteSize=8;
m_bStopBits=ONESTOPBIT;
m_bParity=NOPARITY;
m_dwBaudRate=9600;
m_bEvtChar=EVENTCHAR;
m_fBinary=1;
m_bConnected = FALSE;
m_bFlowCtrl = FC_XONXOFF ;
m_fXonXoff = FALSE;
}
CComStatus::CComStatus(BYTE bComId,BYTE bByteSize,BYTE bStopBits,BYTE bParity,DWORD dwBaudRate,/*WORD fChEvt,*/char bEvtChar,DWORD fBinary)
{
m_hCom = NULL;
m_bComId = bComId;
m_bByteSize=bByteSize;
m_bStopBits=bStopBits;
m_bParity=bParity;
m_dwBaudRate=dwBaudRate;
m_bEvtChar=bEvtChar;
m_fBinary=fBinary;
m_bConnected = FALSE;
m_bFlowCtrl = FC_XONXOFF ;
m_fXonXoff = FALSE;
}
BOOL CComStatus::OpenConnection()
{
char csCom[10];
COMMTIMEOUTS CommTimeOuts ;
if((m_bComId < 0) || (m_bComId > 4))
return FALSE;//从COM1到COM4
if(m_hCom)//if already open
return FALSE;
//OVERLAPPED包含异步I/O信息
m_rdos.Offset = 0;
m_rdos.OffsetHigh = 0;
m_rdos.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if(m_rdos.hEvent == NULL)
return FALSE;
m_wtos.Offset = 0;
m_wtos.OffsetHigh = 0;
m_wtos.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if(m_wtos.hEvent == NULL)
{
CloseHandle(m_rdos.hEvent);
return FALSE;
}
wsprintf(csCom,"COM%d",m_bComId);
m_hCom = CreateFile(csCom,GENERIC_READ | GENERIC_WRITE, 0,NULL, OPEN_EXISTING,ILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,NULL);
if(m_hCom == INVALID_HANDLE_VALUE) {
//dwError = GetLastError();
// handle error
return FALSE;
}
else
{
SetCommMask( m_hCom, EV_RXCHAR ) ; // get any early notifications
SetupComm( m_hCom, 4096, 4096 ) ; // setup device buffers
// purge any information in the buffer
PurgeComm( m_hCom, PURGE_TXABORT | PURGE_RXABORT |PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
// set up for overlapped I/O
DWORD dwTemp = 1000 / (this->m_dwBaudRate / 8);
CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF ;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;//((dwTemp > 0) ? dwTemp : 1);
CommTimeOuts.ReadTotalTimeoutConstant = 1000 ;
// CBR_9600 is approximately 1byte/ms. For our purposes, allow
// double the expected time per character for a fudge factor.
CommTimeOuts.WriteTotalTimeoutMultiplier =2*CBR_9600/this->m_dwBaudRate;//( npTTYInfo ) ;
CommTimeOuts.WriteTotalTimeoutConstant = 0;//1000 ;
SetCommTimeouts( m_hCom, &CommTimeOuts ) ;
}
if(!SetupConnection())
{
CloseConnection();
return FALSE;
}
EscapeCommFunction( m_hCom, SETDTR );
m_bConnected = TRUE;
return TRUE;
}
BOOL CComStatus::CloseConnection()
{
if (NULL == m_hCom)
return ( TRUE ) ;
// set connected flag to FALSE
m_bConnected = FALSE;
// disable event notification and wait for thread
// to halt
SetCommMask( m_hCom, 0 ) ;
EscapeCommFunction( m_hCom, CLRDTR ) ;
// purge any outstanding reads/writes and close device handle
PurgeComm( m_hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
CloseHandle( m_hCom ) ;
m_hCom = NULL;
// change the selectable items in the menu
CloseHandle(m_rdos.hEvent);
CloseHandle(m_wtos.hEvent);
return ( TRUE ) ;
}
BOOL CComStatus::SetupConnection()
{
BOOL fRetVal ;
BYTE bSet ;
DCB dcb ;
if(m_hCom == NULL)
return FALSE;
dcb.DCBlength = sizeof( DCB ) ;
GetCommState( m_hCom, &dcb ) ;
dcb.BaudRate = this->m_dwBaudRate;
dcb.ByteSize = this->m_bByteSize;
dcb.Parity = this->m_bParity;
dcb.StopBits = this->m_bStopBits ;
dcb.EvtChar = this->m_bEvtChar ;
// setup hardware flow control
bSet = (BYTE) ((m_bFlowCtrl & FC_DTRDSR) != 0) ;
dcb.fOutxDsrFlow = bSet ;
if (bSet)
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE ;
else
dcb.fDtrControl = DTR_CONTROL_ENABLE ;
bSet = (BYTE) ((m_bFlowCtrl & FC_RTSCTS) != 0) ;
dcb.fOutxCtsFlow = bSet ;
if (bSet)
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE ;
else
dcb.fRtsControl = RTS_CONTROL_ENABLE ;
// setup software flow control
bSet = (BYTE) ((m_bFlowCtrl & FC_XONXOFF) != 0) ;
dcb.fInX = dcb.fOutX = bSet ;
dcb.XonChar = ASCII_XON ;
char xon = ASCII_XON ;
dcb.XoffChar = ASCII_XOFF ;
char xoff = ASCII_XOFF ;
dcb.XonLim = 100 ;
dcb.XoffLim = 100 ;
// other various settings
dcb.fBinary = TRUE ;
dcb.fParity = TRUE ;
fRetVal = SetCommState( m_hCom, &dcb ) ;
return ( fRetVal ) ;
} // end of SetupConnection()
BOOL CComStatus::IsConnected()
{
return m_bConnected;
}
UINT CommWatchProc( LPVOID lpData )
{
DWORD dwEvtMask ;
//NPTTYINFO npTTYInfo = (NPTTYINFO) lpData ;
OVERLAPPED os ;
int nLength ;
//BYTE abIn[ MAXBLOCK + 1] ;
CComStatus * pCom = (CComStatus *)lpData;
memset( &os, 0, sizeof( OVERLAPPED ) ) ;
// create I/O event used for overlapped read
os.hEvent = CreateEvent( NULL, // no security
TRUE, // explicit reset req
FALSE, // initial event reset
NULL ) ; // no name
if (os.hEvent == NULL)
{
MessageBox( NULL, "Failed to create event for thread!", "TTY Error!",MB_ICONEXCLAMATION | MB_OK ) ;
return ( FALSE ) ;
}
if (!SetCommMask( pCom->m_hCom, EV_RXCHAR ))
return ( FALSE ) ;
char buf[256];
while ( pCom->m_bConnected )
{
dwEvtMask = 0 ;
WaitCommEvent( pCom->m_hCom, &dwEvtMask, NULL );
if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR)
{
if ((nLength = ReadCommBlock( *pCom, (LPSTR) buf, 255 )))
{
//WriteTTYBlock( hTTYWnd, (LPSTR) abIn, nLength ) ;
buf[nLength]='"0';
AfxMessageBox(buf);
}
}
}
CloseHandle( os.hEvent ) ;
return( TRUE ) ;
} // end of CommWatchProc()
int ReadCommBlock(CComStatus& comDev,LPSTR lpszBlock, int nMaxLength )
{
BOOL fReadStat ;
COMSTAT ComStat ;
DWORD dwErrorFlags;
DWORD dwLength;
DWORD dwError;
char szError[ 10 ] ;
// only try to read number of bytes in queue
ClearCommError( comDev.m_hCom, &dwErrorFlags, &ComStat ) ;
dwLength = min( (DWORD) nMaxLength, ComStat.cbInQue ) ;
if (dwLength > 0)
{
fReadStat = ReadFile( comDev.m_hCom, lpszBlock,dwLength, &dwLength, &(comDev.m_rdos) ) ;
if (!fReadStat)
{
if (GetLastError() == ERROR_IO_PENDING)
{
OutputDebugString(""n"rIO Pending");
while(!GetOverlappedResult( comDev.m_hCom ,&(comDev.m_rdos), &dwLength, TRUE ))
{
dwError = GetLastError();
if(dwError == ERROR_IO_INCOMPLETE)
// normal result if not finished
continue;
else
{
// an error occurred, try to recover
wsprintf( szError, "<CE-%u>", dwError ) ;
ClearCommError( comDev.m_hCom , &dwErrorFlags, &ComStat ) ;
break;
}
}
}
else
{
// some other error occurred
dwLength = 0 ;
ClearCommError( comDev.m_hCom , &dwErrorFlags, &ComStat ) ;
}
}
}
return ( dwLength ) ;
} // end of ReadCommBlock()
int ReadCommBlockEx(CComStatus& comDev,LPSTR lpszBlock, int nMaxLength,DWORD dwTimeOut)
{
LPSTR lpOffset=lpszBlock;
int nReadCount = 0;
char chBuf;
//time_t beginTime,endTime;
if(!comDev.m_hCom)
return 0;
if(dwTimeOut <= 0)
return 0;
MSG msg;
//time(&beginTime);
DWORD dwLastTick,dwNowTick,dwGoneTime;
dwGoneTime = 0;
dwLastTick = GetTickCount();
dwNowTick = dwLastTick;
// double diftime;
do
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
if(ReadCommBlock(comDev,&chBuf,1) > 0)
{
//TRACE("----get a char----"n");
*lpOffset = chBuf;
lpOffset ++;
nReadCount ++;
}
dwNowTick = GetTickCount();
if(dwNowTick < dwLastTick)
{
dwLastTick = dwNowTick;
}
dwGoneTime = dwNowTick - dwLastTick;
//TRACE("gon time = %lu"n",dwGoneTime);
}while((nReadCount < nMaxLength) && (dwGoneTime < dwTimeOut));
return (nReadCount);
}//end ReadCommBlockEx
BOOL WriteCommBlock( CComStatus& comDev, LPSTR lpByte , DWORD dwBytesToWrite)
{
BOOL fWriteStat ;
DWORD dwBytesWritten ;
DWORD dwErrorFlags;
DWORD dwError;
DWORD dwBytesSent=0;
COMSTAT ComStat;
char szError[ 128 ] ;
fWriteStat = WriteFile( comDev.m_hCom , lpByte, dwBytesToWrite,&dwBytesWritten, &( comDev.m_wtos) ) ;
if (!fWriteStat)
{
if(GetLastError() == ERROR_IO_PENDING)
{
while(!GetOverlappedResult( comDev.m_hCom,&(comDev.m_wtos), &dwBytesWritten, TRUE ))
{
dwError = GetLastError();
if(dwError == ERROR_IO_INCOMPLETE)
{
// normal result if not finished
dwBytesSent += dwBytesWritten;
continue;
}
else
{
// an error occurred, try to recover
wsprintf( szError, "<CE-%u>", dwError ) ;
ClearCommError( comDev.m_hCom, &dwErrorFlags, &ComStat ) ;
break;
}
}
dwBytesSent += dwBytesWritten;
if( dwBytesSent != dwBytesToWrite )
wsprintf(szError,""nProbable Write Timeout: Total of %ld bytes sent", dwBytesSent);
else
wsprintf(szError,""n%ld bytes written", dwBytesSent);
OutputDebugString(szError);
}
else
{
// some other error occurred
ClearCommError( comDev.m_hCom, &dwErrorFlags, &ComStat ) ;
return ( FALSE );
}
}
return ( TRUE ) ;
} // end of WriteCommBlock()
四、小结
以上给出了用Win32 API设计串行通信的基本思路,在实际应用中,我们可以利用Win32 API设计出满足各种需要的串行通信程序。
ASCII 打印字符
数字 32–126 分配给了能在键盘上找到的字符,当您查看或打印文档时就会出现。数字 127 代表 DELETE 命令。
十进制 字符 十进制 字符
32 space 80 P
33 ! 81 Q
34 " 82 R
35 # 83 S
36 $ 84 T
37 % 85 U
38 & 86 V
39 ' 87 w
40 ( 88 X
41 ) 89 Y
42 * 90 Z
43 + 91 [
44 , 92 "
45 - 93 ]
46 . 94 ^
47 / 95 _
48 0 96 `
49 1 97 a
50 2 98 b
51 3 99 c
52 4 100 d
53 5 101 e
54 6 102 f
55 7 103 g
56 8 104 h
57 9 105 i
58 : 106 j
59 ; 107 k
60 < 108 l
61 = 109 m
62 > 110 n
63 ? 111 o
64 @ 112 p
65 A 113 q
66 B 114 r
67 C 115 s
68 D 116 t
69 E 117 u
70 F 118 v
71 G 119 w
72 H 120 x
73 I 121 y
74 J 122 z
75 K 123 {
76 L 124 |
77 M 125 }
78 N 126 ~
79 O 127 DEL
扩展 ASCII 打印字符
扩展的 ASCII 字符满足了对更多字符的需求。扩展的 ASCII 包含 ASCII 中已有的 128 个字符(数字 0–32 显示在下图中),又增加了 128 个字符,总共是 256 个。即使有了这些更多的字符,许多语言还是包含无法压缩到 256 个字符中的符号。因此,出现了一些 ASCII 的变体来囊括地区性字符和符号。
例如,许多软件程序把 ASCII 表(又称作 ISO 8859-1)用于北美、西欧、澳大利亚和非洲的语言。
扩展的 ASCII 打印字符表十进制 字符 十进制 字符
128 ?nbsp; 192 └
129 ?nbsp; 193 ┴
130 ?nbsp; 194 ┬
131 ?nbsp; 195 ├
132 ?nbsp; 196 ─
133 ?nbsp; 197 ┼
134 ?nbsp; 198 ╞
135 ?nbsp; 199 ╟
136 ?nbsp; 200 ╚
137 ?nbsp; 201 ╔
138 ?nbsp; 202 ╩
139 ?nbsp; 203 ╦
140 ?nbsp; 204 ╠
141 ?nbsp; 205 ═
142 ?nbsp; 206 ╬
143 ?nbsp; 207 ╧
144 ?nbsp; 208 ╨
145 ?nbsp; 209 ╤
146 ?nbsp; 210 ╥
147 ?nbsp; 211 ╙
148 ?nbsp; 212 ?br /> 149 ?nbsp; 213 ╒
150 ?nbsp; 214 ╓
151 ?nbsp; 215 ╫
152 216 ╪
153 ?nbsp; 217 ┘
154 ?nbsp; 218 ┌
155 ?nbsp; 219 ?euro;
156 ?nbsp; 220 ▄
157 ?nbsp; 221 ?bdquo;
158 ? 222 ?
159 ƒ 223 ?
160 ?nbsp; 224 α
161 ?nbsp; 225 ?br /> 162 ?nbsp; 226 Γ
163 ?nbsp; 227 π
164 ?nbsp; 228 Σ
165 ?nbsp; 229 σ
166 ?nbsp; 230 ?br /> 167 ?nbsp; 231 τ
168 ?nbsp; 232 Φ
169 ? 233 Θ
170 ?nbsp; 234 Ω
171 ?nbsp; 235 δ
172 ?nbsp; 236 ∞
173 ?nbsp; 237 φ
174 ?nbsp; 238 ε
175 ?nbsp; 239 ∩
176 ? 240 ≡
177 ? 241 ?br /> 178 ?circ; 242 ≥
179 │ 243 ≤
180 ┤ 244 ?
181 ╡ 245 ?
182 ╢ 246 ?br /> 183 ╖ 247 ≈
184 ╕ 248 ≈
185 ╣ 249 ?
186 ║ 250 ?br /> 187 ╗ 251 √
188 ╝ 252 ?
189 ╜ 253 ?br /> 190 ╛ 254 ■
191 ┐ 255
ASCII 非打印控制字符
ASCII 表上的数字 0–31 分配给了控制字符,用于控制像打印机等一些外围设备。例如,12 代表换页/新页功能。此命令指示打印机跳到下一页的开头。
ASCII 非打印控制字符表十进制 字符 十进制 字符
0 空 16 数据链路转意
1 头标开始 17 设备控制 1
2 正文开始 18 设备控制 2
3 正文结束 19 设备控制 3
4 传输结束 20 设备控制 4
5 查询 21 反确认
6 确认 22 同步空闲
7 震铃 23 传输块结束
8 backspace 24 取消
9 水平制表符 25 媒体结束
10 换行/新行 26 替换
11 竖直制表符 27 转意
12 换页/新页 28 文件分隔符
13 回车 29 组分隔符
14 移出 30 记录分隔符
15 移入 31 单元分隔符
ASCII 之外
另一个更新的字符表称为 Unicode (Unicode:Unicode Consortium 开发的一种字符编码标准。该标准采用多(于一)个字节代表每一字符,实现了使用单个字符集代表世界上几乎所有书面语言。)。 因为 Unicode 表大得多,它可以表示 65,536 个字符,而 ASCII 表只能表示 128 个字符,扩展的 ASCII 表也只能表示 256 个字符。这一更大的容量使不同语言的大多数字符都能包含在同一个字符集中。
电脑各种中英文信息对照及错误信息总汇
一、BIOS中的提示信息
提示信息说明
DriveAerror驱动器A错误
Systemhalt系统挂起
Keyboardcontrollererror键盘控制器错误
Keyboarderrorornokeyboardpresent键盘错误或者键盘不存在
BIOSROMchecksumerrorBIOS ROM校验错误
Singlehardiskcablefail当硬盘使用Cable选项时硬盘安装位置不正确
FDDControllerFailureBIOS软盘控制器错误
HDDControllerFailureBIOS硬盘控制器错误
DriverError驱动器错误
CacheMemoryBad,DonotEnableCache高速缓存Cache损坏,不能使用
Error:UnabletocontrolA20line错误提示:不能使用A20地址控制线
Memorywrite/Readfailure内存读写失败
Memoryallocationerror内存定位错误
CMOSBatterystateLowCMOS没电了
Keyboardinterfaceerror键盘接口错误
Harddiskdrivefailure加载硬盘失败
Harddisknotpresent硬盘不存在
Floppydisk(s)fail(40)软盘驱动器加载失败,一般是数据线插反,电源线没有插接,CMOS内部软驱设置错误
CMOSchecksumerror-efaultsloaded.CMOS校验错误,装入缺省(默认)设置
二、BIOS刷新失败后,Bootblock启动时出现的提示信息
提示信息说明
DetectingfloppydriveAmedia...检测软驱A的格式
Drivemediais:1.44Mb1.2Mb720Kb360K驱动器格式是1.44Mb、12Mb、720kb、360kb的一种
DISKBOOTFAILURE,INSERTSYSTEMDISKANDPRESSENTER磁盘引导失败,插入系统盘后按任意键继续
三、MBR主引导区提示信息
提示信息说明
Invalidpartitiontable无效的分区表
Errorloadingoperatingsystem不能装入引导系统
Missingoperatingsystem系统引导文件丢失
说明:如果在计算机启动过程中,在硬件配置清单下方(也就时在平时正常启动时出现StartingWindows98…的地方)出现不可识别字符,此时可判断硬盘分区表损坏。如果你的硬盘上有重要资料,这时你不要轻易进行分区,可找专业的数据恢复公司。
四、DOS活动分区中的提示信息
提示信息说明
Invalidsystemdisk无效的系统盘
DiskI/Oerror,Replacethediskandpressanykey.磁盘I/O错误,替换磁盘后按任意键(当C盘系统文件丢失或被破坏时出现该提示信息。这时可能SYSC:为修复系统文件)
InvalidMediaTypereachingDriveC:无效的C盘媒体格式说明,也就是C盘没有格式化或者是其他操作系统的磁盘格式如NTFS
InvalidBootDisketteBootFailure无效的启动盘,启动失败
五、IO.SYS中的提示信息
提示信息说明
Insertdiskettefordriveandpressanykeywhenready插入磁盘到驱动器中后按任意键
Yourprogramcausedadivideoverflowerror
Iftheproblempersists,contactyourprogramvendor你的程序导致溢出错误。如果该问题还存在,请联系你的程序供应商
WindowshasdisableddirectdiskaccesstoprotectyourlongfilenamesWindows不能直接访问受保护的长文件名
Thesystemhasbeenhalted.PressCtrl Alt Deltorestartyourcomputer系统挂起,按Ctrl Alt Del重新启动你的计算机
YoustartedyourcomputerwithaversionofMS-DOSincompatiblewiththisversionofWindows.InsertaStartupdiskettematchingthisversionofWindowsandthenrestart你启动的MS-DOS版本与Windows版本不兼容,请插入与Windows版本匹配的系统盘后再重启
ThisversionofWindowsrequiresa386orbetterprocessor该Windows版本需要386以后的CPU支持
A20hardwareerror.Contacttechnicalsupporttoidentifytheproblem地址线A20错误。联系技术支持来识别该问题
StartingWindows98...正在启动Windows98……
Windows98isnowstartingyourMS-DOS-basedprogramWindows98正在启动基于MS-DOS的程序
Windows98isnowrestarting...Windows98正在重新启动
PressEscnowtocancelMS-DOSmodeandrestartWindows98...按Esc退出MS-DOS模式,重新启动Windows98…
ThereisanunrecognizedcommandinyourCONFIG.SYSfile在你的CONFIG.SYS文件中有不可识别的命令
ThefollowingcommandinyourCONFIG.SYSfileisincorrect:在你的CONFIG.SYS有下列错误命令
Thesectorsizespecifiedinthisfileistoolarge:该文件中指定的扇区太大
Thefollowingfileismissingorcorrupted:WIN.COMCOMMAND.COM下列文件丢失或被破坏:WIN.COM和COMMAND.COM
ThereisaninvalidcountrycodeorcodepageinyourCONFIG.SYSfile在你的CONFIG.SYS里有一个无效的国家代码
ThereisanerrorintheCOUNTRYcommandinyourCONFIG.SYSfile在你的CONFIG.SYS里有一个错误的设置国家命令
ThereisnotenoughmemoryfortheCOUNTRY.SYSfile没有足够的内存来加载COUNTRY.SYS文件
RemovesomedriversfromyourCONFIG.SYSfile,andthentryagain请从CONFIG.SYS文件中删除部分驱动程序,然后再试
Removesomedrivers,andthentryagain去除一些驱动程序,然后再试。这里的驱动程序是指在CONFIG.SYS中使用device或devicehigh命令加载的程序文件
RemovesomediskdriversfromyourCONFIG.SYSfile,andthentryagain从你的(CONFIG.SYS)移去部分程序,然后再试
TheconfigurationspecifiedinyourCONFIG.SYSfileistoolargeformemory调整你的CONFIG.SYS文件以获得足够大的内存
YouhavetoomanyblockdevicesspecifiedinyourCONFIG.SYSfile调整你的CONFIG.SYS文件以获得blockdevices
TheSTACKSsetting(s)inyourCONFIG.SYSfileareincorrect在你的CONFIG.SYS中堆栈设置不正确
Defaultstacksettingswillbeusedinstead缺省的堆栈设置将被替代使用
ThereisanerrorinyourCONFIG.SYSfileonlineXX在你的CONFIG.SYS里XX行错误
Warning:LogicaldrivespastZexistandwillbeignored警告:逻辑驱动器Z已经存在,将被忽略
TypethenameoftheCommandInterpreter(e.g.,C:"WINDOWS"COMMAND.COM)Pressanykeytocontinue…请输入命令解释器的文件名(如:C:"WINDOWS"COMMAND.COM)后,按任意键继续
Windowsisbypassingyourstartupfiles
MinimalnetworksupportwillbeloadedifavailableWindows正在跳过你的启动文件。如果网络可用,最小网络支持将被加载
WindowsisstartingthecommandpromptonlyWindows正在启动命令字符模式
WindowswillpromptyoutoconfirmeachstartupcommandWindows将提示你确认每一个启动命令
Thecompressiondrivercannotbesetupcorrectly压缩驱动程序不能被正确加载
GetaversionfromyourvendorthatiscompatiblewiththisversionofWindows从你的供应商那里获得一个与Windows版本相兼容的版本
Processthesystemregistry运行系统注册表
Createastartuplogfile(BOOTLOG.TXT)创建启动日志BOOTLOG.TXT文件
Processyourstartupdevicedrivers(CONFIG.SYS)运行CONFIG.SYS文件中的设备驱动
Processyourstartupcommandfile(AUTOEXEC.BAT)运行AUTOEXEC.BAT中的启动命令
LoadtheWindowsgraphicaluserinterface装入WINDOWS图形用户界面(GDI)
Warning:Windowshasdetectedaregistry/configurationerror.Choose,Commandpromptonly,andrunSCANREG警告:Windows检测到一个注册表或配置错误选择DOS操作模式,运行SCANREG
Warning:Windowshasdetectedacompresseddriveaccesserror.ChooseSafemodecommandpromptonly,tohelpyouidentifytheproblem警告:Windows检测到一个访问压缩驱动器错误,选择安全模式下的MS-DOS,来帮助你识别这个问题
Warning:Windowsdidnotfinishloadingonthepreviousattempt.ChooseSafemode,tostartWindowswithaminimalsetofdrivers警告:Windows在刚才尝试后不能够完成加载,选择安全模式,用最小驱动启动你的电脑
Warning:Windowsmulti-bootmaynotfunctioncorrectly
Checkforsystemfilesinyourrootdirectorywithconflictingextensions警告:Windows不能够正确完成多系统启动,在你的ROOT目录里检查不一致的系统文件
Warning:thesystemconfigurationmanagerfailedtorun.Someofyourreal-modedevicedriversmaynotinitializeproperly警告:系统管理器运行失败,一些实模式驱动可能没有正确初始化
TheBUFFERSsetting(s)inyourCONFIG.SYSfilearetoolarge.Defaultbuffersettingswillbeusedinstead在你的CONFIG.SYS里BUFFER设置太大。默认的BUFFER缓冲器设置将被使用
Amemoryallocationerroroccurredduringstartup
>RestartyourcomputerandselectInteractiveStarttoidentifytheproblem在启动过程中内存分配发生错误,请重新启动你的计算机然后选用交互式方式启动来确定故障原因
Warning:thehighmemoryarea(HMA)isnotavailableAdditionallowmemory(below640K)willbeusedinstead警告:高端内存不可用,低端内存将被代替使用
ThereisnotenoughmemoryforWindows.RemovesomedriversfromyourCONFIG.SYSfile,andthentryagain对Windows来说没有足够的内存,请从CONFIG.SYS文件中移去部分加载的程序,然后再试着启动你的读入计算机
YourpreviousMS-DOSfileswerenotfound.旧的MS-DOS文件没有找到,这常见于使用双启动的系统,另一个系统是DOS6
YourpreviousMS-DOSversionisnotsupported.MS-DOSstartupfailed旧的MS-DOS版本不支持。MS-DOS启动失败
NowloadingyourpreviousversionofMS-DOS,pleasewait现在正在加载旧的MS-DOS版本,请等待
InvalidsettingintheMSDOS.SYSfile:AninternalstackoverflowhascausedthissessiontobehaltedMSDOS.SYS文件中无效的设置选项,系统堆栈溢出,系统挂起
ChangetheSTACKSsettinginyourCONFIG.SYSfile,andthentryagain请改变CONFIG.SYS文件中的STACK设置,然后再试
Stackoverflow堆栈溢出
Integerdivideby0 整数被0除
Notenoughspaceforenvironment 没有足够的空间
Run-timeerror运行错误
Floating-pointsupportnotloaded 浮点运算支持没有装入
Nullpointerassignment 无效的断点分配
六、COMMAND中的提示信息
提示信息说明
Stackoverflow堆栈溢出
Integerdivideby0分母是0
Notenoughspaceforenvironment磁盘没有足够的空间
Run-timeerror时钟错误
Nofixeddiskspresent没有硬盘存在
Insufficientmemory内存不足
Invaliddrivespecification无效的驱动器名
Invalidcharactersinvolumelabel无效的盘符
InvalidVolumeID无效的卷标ID号
Notargetdrivespecified目标驱动器没有定义
missingoperatingsystem系统文件丢失
NonsystemDiskorDiskError不是系统盘或磁盘错误
ReplaceDiskandPressAnykeyToReboot替换磁盘,按任意键重新启动
七、SYS传送系统文件时的提示信息
提示信息说明
CannotfindSystemFiles不能找到系统文件
Systemtransferred系统传送完毕
Diskunsuitableforsystemdisk目标磁盘不适合做系统盘
八、FORMAT中的提示信息
提示信息说明
UnabletowriteBOOT.不能写引导扇区
Invalidmediatype无效的磁盘类型
Parametersnotsupported参数不支持
Cannotformatanetworkdrive不能格式化网络驱动器
InvalidmediaorTrack0bad-diskunusable无效的磁盘或0磁道损坏,磁盘不可用
Formatterminated格式化终止
DiskBootfailure磁盘BOOT区错误
Invalidsystemdisk无效的系统磁盘
DiskI/Oerror,Replacethedisk,andthen
pressanykey磁盘I/O错误,请换盘后按任意键。通常在读写软盘时出现此类错误
Non-Systemdiskordiskerror,Replaceandstrikeanykeywhenready无系统的磁盘或磁盘错误,请替换后按任意键
Toomanyopenfiles打开的文件太多
AccessdeniedInsufficientmemory没有足够的内存访问被拒绝
Invaliddrivespecification无效的盘符说明
ParseErrorXX分析错误XX
IncorrectMS-DOSversion不正确的MS-DOS版本
X%percentcompleted格式化正在进行,已经完成X%
Formatcomplete.格式化完成
XXbytesinbadsectorsXX字节的坏扇区
XXbytestotaldiskspace磁盘的全睰占湮猉X字节
XXbytesavailableondisk磁盘的可使用空间为XX字
Volumelabel(11characters,ENTERfornone)?请输入磁盘卷标(总共11个字符,按回车为空)
FormatnotsupportedondriveXFormat命令不支持驱动器X
Invaliddeviceparametersfromdevicedriver无效的设备驱动参数
ErrorwritingFAT写FAT表错误
Errorwritingdirectory写DIR目录表错误
CannotformatanASSIGNorSUBSTdrive不能格式化ASSIGN和SUBST指定的驱动器
CannotfindSystemFiles没有找到系统文件
Cannotformatanetworkdrive不能格式化网络驱动器
Invalidcharactersinvolumelabel磁盘卷标中存在非法字符
Parametersnotsupported参数不支持
Formatterminated格式化意外终止
Diskunsuitableforsystemdisk这个磁盘不适合作为系统盘
UnabletowriteBOOT不能写BOOT区
Errorreadingdirectory读DIR目录区错误
Notargetdrivespecified目的驱动器没有说明
andpressENTERwhenready准备好后按回车键
Systemtransferred系统传送完毕
EntercurrentvolumelabelfordriveX请为驱动器X输入磁盘的卷标
Parametersnotcompatiblewithfixeddisk参数与硬盘不兼容
VolumeSerialNumberisXXXX分区格式化时随机产生的序列号是:XXXX
Formatbroken格式化被强行终止
FormatnotavailableondriveXX不能在驱动器XX上使用格式化命令
on-Systemdiskordiskerror没有系统盘或磁盘错误
BadPartitionTable错误的硬盘分区表
Parametersnotsupportedbydrive该驱动器不支持这些参数
InsertDOSdiskindriveA请在驱动器A中插入DOS系统盘
WARNING,ALLDATAONNON-REMOVABLEDISKDRIVE%1:WILLBELOST!Proceedwith警告:硬盘分区X中的所有数据都将丢失,确定执行格式化命令吗?
Formatanother(Y/N)?!还格式化其他盘吗
Errorreadingpartitiontable读硬盘分区表错误
Errorwritingpartitiontable写硬盘分区表错误
Parametersnotcompatible参数不匹配
XXallocationunitsavailableondisk磁盘上有XX可用簇
XXbytesineachallocationunit每个簇有大小为XX字节
Sameparameterenteredtwice相同的参数被输入两次
Mustenterboth/Tand/Nparameters/T和/N参数必须同时使用
Thereisnotenoughroomtocreatearestorefile没有足够的空间来建立恢复文件
YouwillnotbeabletousetheUNFORMATutility你将不能使用UNFORMAT工具来恢复你的硬盘数据
Thereisnotenoughdiskspaceforsystemfiles没有足够的空间来存储系统文件
Thisdiskcannotbeunformatted这个磁盘不能被反格式化
Therewasanerrorcreatingtheformatrecoveryfile在建立恢复文件时产生了一个错误
Volumelabelisnotsupportedwith/8parameter该驱动器不支持/8参数
Insufficientmemorytoloadsystemfiles没有足够的内存装入系统文件
Insufficientmemory.内存不足
Calculatingfreespace(thismaytakeseveralminutes)...正在统计磁盘空间(可能要花上几分钟时间)
九、FDISK中的提示信息
提示信息说明
DeletePrimaryDOSPartition删除主DOS分区
DeleteExtendedDOSPartition删除扩展DOS分区
DeleteLogicalDOSDrive(s)intheExtendedDOSPartition删除逻辑DOS分区
WARNING!DatainthedeletedPrimaryDOSPartitionwillbelost警告:主DOS分区的数据将全部丢失
WARNING!DatainthedeletedExtendedDOSPartitionwillbelost.Doyouwishtocontinue(Y/N)?警告:扩展分区中的数据将全部丢失,你真的继续吗
DeleteLogicalDOSDrive(s)intheExtendedDOSPartition删除扩展分区下的逻辑分区
WARNING!DatainadeletedLogicalDOSDrivewillbelostWhatdrivedoyouwanttodelete?Areyousure(Y/N)?警告:逻辑分区中的所有数据都将丢失,你删除哪个驱动器?你真的确定吗
DisplayPartitionInformation显示分区内容信息
TheExtendedDOSPartitioncontainsLogicalDOSDrives.扩展DOS分区中包含DOS逻辑分区
Doyouwanttodisplaythelogicaldriveinformation(Y/N)?你想显示逻辑分区的信息吗
DisplayLogicalDOSDriveInformation显示逻辑分区信息
EnterpartitionsizeinMbytesorpercentofdiskspace(%)tocreateaPrimaryDOSPartition...输入所建立主DOS分区的容量,单位是百分比
EnterlogicaldrivesizeinMbytesorpercentofdiskspace(%)...请输入逻辑分区容量大小或磁盘空间的百分比
EnterVolumeLabel?请输入卷标
EnterpartitionsizeinMbytesorpercentofdiskspace(%)tocreateanExtendedDOSPartition....请输入扩展DOS分区的容量大小或磁盘空间的百分比
DoyouwishtousethemaximumavailablesizeforaPrimaryDOSPartition你想使用全部磁盘空间作为主DOS分区吗
ChangeCurrentFixedDiskDrive.EnterFixedDiskDriveNumber(X)当你挂接两个以上硬盘时,在FDISK的系统菜单中将多一个第5项,选择第5项时,将提示:请输入硬盘的序号
FixedDiskDriveStatus硬盘状态
DatainthedeletedPrimaryDOSPartitionwillbelost.Whatprimarypartitiondoyouwanttodelete?Doyouwishtocontinue(Y/N)?DOS主分区中的数据将全部丢失。你准备删除哪个主分区?你想继续吗
DatainthedeletedNon-DOSPartitionwillbelost.WhatNon-DOSpartitiondoyouwanttodelete?Doyouwishtocontinue(Y/N)?非DOS分区的数据将全部丢失。你准备删除哪一个非DOS分区?你要继续吗
YouMUSTrestartyoursystemforyourchangestotakeeffect你必须重新启动你的计算机你所做的所有改变才有效
Anydrivesyouhavecreatedorchangedmustbeformatted你新建的分区或改变的分区必须被格式化后才可用
ShutdownWindowsbeforerestarting在重启之前请关闭Windows
Yourcomputerhasadisklargerthan512MB.ThisversionofWindowsincludesimprovedsupportforlargedisks,resultinginmoreefficientuseofdiskspaceonlargedrives,andallowingdisksover2GBtobe formattedasasingledrive你的计算机挂接了一个大于512M的硬盘。该版本的Windows支持大硬盘,能够更有效的使用大硬盘上的磁盘空间,并充许单个分区大于2GB
PressEsctoexitFDISK按Esc退出FDISK
ThisdriveisFAT16bydefault,switchtoFAT32(Y/N)?这个驱动器默认是FAT16模式,真的转换为FAT32模式吗
ThisdrivemustbeFAT32becauseitssizeis>2048MB当分区的空间大于2048MB时,这个分区必须使用FAT32磁盘数据格式
ThisdrivemustbeFAT16becauseitssizeistoosmalltobeFAT32因为这个分区对于FAT32格式来说太小,所以这个分区只能使用FAT16格式
Verifyingdriveintegrity,complete正在校验分区的完整性,完成
YourcomputerhasNTFSpartitionswhichmayrequirelargedrivesupport.Ifyouareusinganotheroperatingsystem,suchasWindowsNT,whichsupportslargedrivesyoushouldenabletreatingthesepartitionsaslarge你的计算机有一个NTFS分区可能需要大分区支持,如果你正在使用其他操作系统,如WindowsNT,你应该把这些分区分大一些
NOTE:IfyouanswerYandthepartitiondisplaylooksincorrectorahangorcrashoccursdonothing,runFDISKagain,andanswerNtothisquestion注意:如果你回答Y,可能显示的分区信息看上去不正确或系统挂起或什么动作也没有,这时你可再次运行FDISK,对这个问题用N回答
ShouldNTFSpartitionsonalldrivesbetreatedaslarge(Y/N)?对所有的NTFS分区使用大分区模式
Drivedeleted驱动器被删除
Partitionmadeactive分区X被设置为活动
PrimaryDOSPartitioncreated主DOS分区被建立
ExtendedDOSPartitioncreated扩展DOS分区被建立
LogicalDOSDrivecreated,driveletterschangedoradded逻辑分区被建立,驱动器的盘符可能被改变或有新的增加
Nopartitionsdefined没有分区被定义
Nologicaldrivesdefined没有逻辑驱动器被说明
Drivelettershavebeenchangedordeleted驱动器盘符已经被改变或删除
Noactivepartitions没有设置活动分区,该硬盘不能被启动
Nofixeddiskspresent没有硬盘
Errorreadingfixeddisk读硬盘错误
Errorwritingfixeddisk写硬盘错误
Writeprotecterrorwritingfixeddisk写硬盘写保护错误
IncorrectDOSversion不正确的DOS版本
CannotFDISKwithnetworkloaded不能对网络驱动器使用FDISK
NospacetocreateaDOSpartition没有足够的空间建立DOS分区
Requestedlogicaldrivesizeexceedsthemaximumavailablespace输入的逻辑驱动器的容量超过了最大可用的磁盘空间容量
Requestedpartitionsizeexceedsthemaximumavailablespace输入的分区的容量超过了最大可用的磁盘空间容量
Nopartitionstodelete已经没有分区可以被删除
TheonlystartablepartitiononDriveisalreadysetactive硬盘X上的惟一的可启动分区已经被标记为活动(扩展分区不能被设置为活动)
Nopartitionstomakeactive没有分区可以被标记为活动分区
Partitionselectedisnotstartable,activepartitionnotchanged输入的分区X不可启动,当前的活动分区没有被改变
CannotcreateExtendedDOSPartitionwithoutPrimaryDOSPartitionondisk X在硬盘X上没有主DOS分区时不能建立扩展DOS分区
AllavailablespaceintheExtendedDOSPartitionisassignedtologicaldrives扩展分区的全部有效空间都分配给逻辑驱动器吗?还可以再分为主分区或其他分区
CannotdeleteExtendedDOSPartitionwhilelogicaldrivesexist因为扩展分区下有逻辑分区存在,所以扩展分区不能被删除
AlllogicaldrivesdeletedintheExtendedDOSPartitionisnotachoice.PleaseenterX确定删除哪一个逻辑分区,请输入X
WARNING!Thepartitionsetactiveisnotstartable警告:被设置为活动的分区是不可启动的
Onlynon-startablepartitionsexist只有不可启动的分区存在
OnlypartitionsonDrive1canbemadeactive只有硬盘1上的分区可被设置为活动。当你挂接两个以上硬盘时,使用FDISK命令时只能设置硬盘1中的某个主分区为活动,其他硬盘上的主分区都不能设置为活动
MaximumnumberofLogicalDOSDrivesinstalled逻辑驱动器的所有盘符都使用,意思是你在分区时驱动器的盘符超过了Z
Cannotcreateazerosizepartition不能建立大小空间为0的分区
UnabletoaccessDriveX不能访问驱动器X
CannotdeletePrimaryDOSPartitionondrive1whenanExtendedDOSPartitionexists当扩展分区存在时不能删除硬盘1上的主DOS分区
Invalidentry无效的输入
Volumelabeldoesnotmatch标不匹配,当你删除分区时,系统会提示输入该分区的卷标,如果输入的巻标和你要删除的分区的卷标不一致时,操作将不能进行
CannotcreateLogicalDOSDrivewithoutanExtendedDOSPartitiononthecurrentdrive没有扩展分区时不能在当前硬盘上建立逻辑驱动器
Couldnotchangepartitions,becausethediskcouldnotbelocked不能改变分区,因为该硬盘不能被锁定
Internalerror内部错误
IncorrectMS-DOSversion不正确的MS-DOS版本
Invalidparameter无效的参数
CannotFDISKwithnetworkloaded不能对网络驱动器进行FDISK
ThemasterbootcodehasNOTbeenupdated
/STATUSDisplayspartitioninformation主引导区代码没有被更新。显示分区信息
/XIgnoresextendeddisk-accesssupport.Usethisswitch不使用扩展磁盘访问指令支持。当你使用
ifyoureceivediskaccessorstackoverflowmessages这个参数时你将看到堆栈溢出的信息
/MBRand/CMBRcannotbothbespecified/MBR和/CMBR这两个参数不能同时使用
/MBRonlyoperatesondrive1,use/CMBRforotherdrives只能对硬盘1使用,/CMBR对其他硬盘使用/MBR
Youmustspecifyadrivenumberwith/CMBR
Invalidpartitiontable在使用/CMBR参数时必须说明硬盘的序号。无效的硬盘分区表
Errorloadingoperatingsystem在装入操作系统时错误
Missingoperatingsystem操作系统文件丢失
stackoverflow堆栈溢出
integerdivideby0整数被0除
run-timeerror运行错误
notenoughspaceforenvironment没有足够的空间
使用GDI+在内存中转换图片类型(修正)
作者:卢伟
微软新推出的GDI+功能强大,本文仅对图片转换加以讨论,不足之处请大家指出,本人QQ:394777271。
需要的GdiPlus.h等头文件和GdiPlus.lib等文件可以在.NET的安装文件夹C:"Program Files"Microsoft Visual Studio 8"VC"PlatformSDK"Include和C:"Program Files"Microsoft Visual Studio 8"VC"PlatformSDK"Lib中找到。(附件中已上传!!!)
如果使用的是VC6.0,把头文件放置到C:"Program Files"Microsoft Visual Studio 6.0"VC98"Include。
lib文件放置到C:"Program Files"Microsoft Visual Studio 6.0"VC98"Lib或者放置到项目所在的位置。
图片类型的转换支持:bmp、dib、png、gif、jpeg/jpg、tiff、emf等。以下是详细步骤。
首先,在StdAfx.h中静态调用diplus.lib,即由编译系统完成对DLL的加载,应用程序结束时卸载DLL的编码。如下:
#ifndef ULONG_PTR
#define ULONG_PTR unsigned long*
#include "GdiPlus.h"
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib")
#include <atlconv.h>
#endif
GdiplusStartupInput m_gdiplusStartupInput; ULONG_PTR m_gdiplusToken;
然后在OnCreate()函数中加入初始化GDI+的函数:
GdiplusStartup(&m_gdiplusToken, &m_gdiplusStartupInput, NULL); 在OnDestroy()函数中加入结束GDI+使用的函数:
GdiplusShutdown(m_gdiplusToken);
接下来定义GetImageCLSID函数:
BOOL GetImageCLSID(const WCHAR* format, CLSID* pCLSID);
接着,定义转换函数:
BOOL MBmpToMImage(CMemFile& cbfBmp, CMemFile& cbfImage, CString strType);
其中:
CMemFile& cbfBmp表示原位图文件; CMemFile& cbfImage表示转换后的图形文件; CString strType表示转换的图片类型。
该函数中主要的处理为以下几步:
- 将原位图文件转换为IStream
- 定义Image类实例,并使用第1步获得的IStream初始化
- 获取转换的图片类型的CLSID
- 将Image以转换的图片类型保存到IStream中
- 将IStream转换为CMemFile内存文件(也可为CFile)
详细代码如下:
BOOL GetImageCLSID(const WCHAR* format, CLSID* pCLSID)
{
UINT num = 0;
UINT size = 0;
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0){
return FALSE;
}
pImageCodecInfo = (ImageCodecInfo *)(malloc(size));
if(pImageCodecInfo == NULL)
return FALSE;
GetImageEncoders(num, size, pImageCodecInfo);
// Find for the support of format for image in the windows
for(UINT i = 0; i < num; ++i)
{
//MimeType: Depiction for the program image
if( wcscmp(pImageCodecInfo[i].MimeType, format) == 0)
{
*pCLSID = pImageCodecInfo[i].Clsid;
free(pImageCodecInfo);
return TRUE;
}
}
free(pImageCodecInfo);
return FALSE;
}
BOOL MBmpToMImage(CMemFile& cbfBmp, CMemFile& cbfImage, CString strType)
{
int iBmpSize = cbfBmp.GetLength();
HGLOBAL hMemBmp = GlobalAlloc(GMEM_FIXED, iBmpSize);
if (hMemBmp == NULL) return FALSE;
IStream* pStmBmp = NULL;
CreateStreamOnHGlobal(hMemBmp, FALSE, &pStmBmp);
if (pStmBmp == NULL)
{
GlobalFree(hMemBmp);
return FALSE;
}
BYTE* pbyBmp = (BYTE *)GlobalLock(hMemBmp);
cbfBmp.SeekToBegin();
cbfBmp.Read(pbyBmp, iBmpSize);
Image* imImage = NULL;
imImage = Image::FromStream(pStmBmp, FALSE);
if (imImage == NULL)
{
GlobalUnlock(hMemBmp);
GlobalFree(hMemBmp);
return FALSE;
}
USES_CONVERSION;
CLSID clImageClsid;
GetImageCLSID(A2W(("image/"+strType).GetBuffer(0)), &clImageClsid);
HGLOBAL hMemImage = GlobalAlloc(GMEM_MOVEABLE, 0);
if (hMemImage == NULL)
{
pStmBmp->Release();
GlobalUnlock(hMemBmp);
GlobalFree(hMemBmp);
if (imImage != NULL) delete imImage;
return FALSE;
}
IStream* pStmImage = NULL;
CreateStreamOnHGlobal(hMemImage, TRUE, &pStmImage);
if (pStmImage == NULL)
{
pStmBmp->Release();
GlobalUnlock(hMemBmp);
GlobalFree(hMemBmp);
GlobalFree(hMemImage);
if (imImage != NULL) delete imImage;
return FALSE;
}
imImage->Save(pStmImage, &clImageClsid);
if (pStmImage == NULL)
{
pStmBmp->Release();
pStmImage->Release();
GlobalUnlock(hMemBmp);
GlobalFree(hMemBmp);
GlobalFree(hMemImage);
if (imImage != NULL) delete imImage;
return FALSE;
}
LARGE_INTEGER liBegin = {0};
pStmImage->Seek(liBegin, STREAM_SEEK_SET, NULL);
BYTE* pbyImage = (BYTE *)GlobalLock(hMemImage);
cbfImage.SeekToBegin();
cbfImage.Write(pbyImage, GlobalSize(hMemImage));
if (imImage != NULL) delete imImage;
pStmBmp->Release();
pStmImage->Release();
GlobalUnlock(hMemBmp);
GlobalUnlock(hMemImage);
GlobalFree(hMemBmp);
GlobalFree(hMemImage);
return TRUE;
}
CONVERT BMP TO JPEG
The code has been improved to handle all bitmap formats. The earlier version did not handle 32-bit or 16-bit DIB's.
Acknowledgements: This code depends on the jpeg.lib code written by the Independent JPEG Group (Thomas G. Lane and company).
The code assumes that you have installed their jpeg.lib library project.
To get jpeg.lib:
1) Go to Ulrich von Zadow's "Class library for image file decoding" article, and click on the "Links" link.
2) Click on the "LIBJPEG ver 6b" link to download the "vanilla" libjpeg.
3) Do not use Mr. von Zadow's "Paintlib" version of libjpeg, because he seems to have made some internal changes for his own use.
The jpeg.lib project (LibJpeg) is set up to compile under numerous operating systems. The download includes a file called install.doc. Read the sections relevant to VC++/Windows. You have to rename a couple of files, etc.
The first time I installed and compiled libjpeg, it took a long time and was very aggravating. I then re-read the instructions, and it took about fifteen minutes. I'm sure there's a lesson here...
Testing Notes:
To test the code, I used MS Paint to create bitmap files in the four supported formats.
I then set up a VC++ project which read the bitmap files, and used the functions below to turn them into JPEG files (.jpg).
I then used Julian Smart's CImage demo application to display them.
I also used Internet Explorer as well as MS Word to display them.
I also created a jpeg file by loading an internal resource (see the example below). The largest bitmap file I dealt with was about 390 KB.
Error trapping:
libjpeg includes a number of provisions for improving error detection, and reporting them in a good manner. I have not investigated these, and the code does not include any of them. Right now, an internal error will simply cause the code to stop cold.
To get at jpeg.lib properly in your project:
1. Under tools->options, directories tab:
a. Set the Include Files to include the path to your jpeg.lib project
b. Set the Library Files to include the path to your jpeg.lib release directory
2. Under Project->Settings, Link tab:
a. Add jpeg.lib to the Object/library modules list
Note on Quality Settings:
For the IDB_TEST resource, I tried two quality settings: 10 and 100. At "10", the image had a lot of black spottiness, and took up 1 KB. At "100" the image looked very good, and took up 4 KB.
/////////////////////////////////////////////////////////////
//Example of use:
/////////////////////////////////////////////////////////////
void MakeJpeg()
{
CBitmap cBitmap;
BITMAP bm;
CString csMsg = "";
cBitmap.LoadBitmap(IDB_TEST);
cBitmap.GetBitmap(&bm);
HANDLE hDib = DDBToDIB((HBITMAP)cBitmap,
BI_RGB,
NULL); //Use default palette
//Turn DIB into JPEG file
if (hDib != NULL)
{
if (!JpegFromDib(hDib,
100, //Quality setting
"test.jpg",
&csMsg))
{
AfxMessageBox(csMsg);
}
else
AfxMessageBox("test.jpg created");
::GlobalFree(hDib);
}
else
AfxMessageBox("Failed to load IDB_TEST");
}
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//Header file code
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//In the relevant header file (up near the top):
#include "jpeglib.h"
BOOL JpegFromDib(HANDLE hDib, //Handle to DIB
int nQuality, //JPEG quality (0-100)
CString csJpeg, //Pathname to target jpeg file
CString* pcsMsg); //Error msg to return
BOOL BuildSamps(HANDLE hDib,
int nSampsPerRow,
struct jpeg_compress_struct cinfo,
JSAMPARRAY jsmpArray,
CString* pcsMsg);
RGBQUAD QuadFromWord(WORD b16);
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//Source file code
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
extern "C"
{
#include "jpeglib.h"
}
#include
struct ima_error_mgr
{
struct jpeg_error_mgr pub; //"public" fields
jmp_buf setjmp_buffer; //for return to caller
};
////////////////////////////////////////////////////////////////////////////
//This function takes the contents of a DIB
//and turns it into a JPEG file.
//
//The DIB may be monochrome, 16-color, 256-color, or 24-bit color.
//
//Any functions or data items beginning with "jpeg_" belong to jpeg.lib,
//and are not included here.
//
//The function assumes 3 color components per pixel.
/////////////////////////////////////////////////////////////////////////////
BOOL JpegFromDib(HANDLE hDib, //Handle to DIB
int nQuality, //JPEG quality (0-100)
CString csJpeg, //Pathname to jpeg file
CString* pcsMsg) //Error msg to return
{
//Basic sanity checks...
if (nQuality < 0 || nQuality > 100 ||
hDib == NULL ||
pcsMsg == NULL ||
csJpeg == "")
{
if (pcsMsg != NULL)
*pcsMsg = "Invalid input data";
return FALSE;
}
*pcsMsg = "";
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)hDib;
byte *buf2 = 0;
//Use libjpeg functions to write scanlines to disk in JPEG format
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE* pOutFile; //Target file
int nSampsPerRow; //Physical row width in image buffer
JSAMPARRAY jsmpArray; //Pixel RGB buffer for JPEG file
cinfo.err = jpeg_std_error(&jerr); //Use default error handling (ugly!)
jpeg_create_compress(&cinfo);
if ((pOutFile = fopen(csJpeg, "wb")) == NULL)
{
*pcsMsg = "Cannot open ";
*pcsMsg += csJpeg;
jpeg_destroy_compress(&cinfo);
return FALSE;
}
jpeg_stdio_dest(&cinfo, pOutFile);
cinfo.image_width = lpbi->biWidth; //Image width and height, in pixels
cinfo.image_height = lpbi->biHeight;
cinfo.input_components = 3; //Color components per pixel
//(RGB_PIXELSIZE - see jmorecfg.h)
cinfo.in_color_space = JCS_RGB; //Colorspace of input image
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo,
nQuality, //Quality: 0-100 scale
TRUE); //Limit to baseline-JPEG values
jpeg_start_compress(&cinfo, TRUE);
//JSAMPLEs per row in output buffer
nSampsPerRow = cinfo.image_width * cinfo.input_components;
//Allocate array of pixel RGB values
jsmpArray = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo,
JPOOL_IMAGE,
nSampsPerRow,
cinfo.image_height);
if (DibToSamps(hDib,
nSampsPerRow,
cinfo,
jsmpArray,
pcsMsg))
{
//Write the array of scan lines to the JPEG file
(void)jpeg_write_scanlines(&cinfo,
jsmpArray,
cinfo.image_height);
}
jpeg_finish_compress(&cinfo); //Always finish
fclose(pOutFile);
jpeg_destroy_compress(&cinfo); //Free resources
if (*pcsMsg != "")
return FALSE;
else
return TRUE;
}
////////////////////////////////////////
//This function turns a 16-bit pixel
//into an RGBQUAD value.
////////////////////////////////////////
RGBQUAD QuadFromWord(WORD b16)
{
BYTE bytVals[] =
{
0, 16, 24, 32, 40, 48, 56, 64,
72, 80, 88, 96, 104,112,120,128,
136,144,152,160,168,176,184,192,
200,208,216,224,232,240,248,255
};
WORD wR = b16;
WORD wG = b16;
WORD wB = b16;
wR <<= 1; wR >>= 11;
wG <<= 6; wG >>= 11;
wB <<= 11; wB >>= 11;
RGBQUAD rgb;
rgb.rgbReserved = 0;
rgb.rgbBlue = bytVals[wB];
rgb.rgbGreen = bytVals[wG];
rgb.rgbRed = bytVals[wR];
return rgb;
}
VC中使用Gdi+合并jpg图片
合并两张jpg图片为一张jpg图片,思路是先把两张图片jpg图片都转化成bmp图片,然后把两张bmp图片合并成一张bmp图片,然后是把这张bmp图片转化为jpg图片。
一。jpg,bmp互相转化
/*********************************
format:bmp转为jpg, format为image/jpeg,jpg转为bmp,format为image/bmp
strDst为最终转化结果的图片路径
strSrc为原来图片的路径
**********************************/
BOOL ConvertPic(const WCHAR *format, const CString &strDst, const CString &strSrc)
{
BOOL bConvert = false;
CLSID clsid;
int nRet = 0;
nRet = GetEncoderClsid(format,&clsid); //得到CLSID
USES_CONVERSION;
if (nRet>=0)
{
Image image(A2W(strSrc));
image.Save(A2W(strDst),&clsid,NULL);
bConvert = true;
}
return bConvert;
}
//其中GetEncoderClsid函数如下:
/*****************************************************
返回值为-1表示失败,其他为成功
******************************************************/
int GetEncoderClsid(const WCHAR *format, CLSID *pClsid)
{
int nRet = -1;
ImageCodecInfo * pCodecInfo = NULL;
UINT nNum = 0,nSize = 0;
GetImageEncodersSize(&nNum,&nSize);
if (nSize<0)
{
return nRet;
}
pCodecInfo = new ImageCodecInfo[nSize];
if (pCodecInfo==NULL)
{
return nRet;
}
GetImageEncoders(nNum,nSize,pCodecInfo);
for (UINT i=0; i<nNum; i++)
{
if (wcscmp(pCodecInfo[i].MimeType,format)==0)
{
*pClsid = pCodecInfo[i].Clsid;
nRet = i;
delete[] pCodecInfo;
return nRet;
}
else
{
continue;
}
}
delete[] pCodecInfo;
return nRet;
}
//bmp转化为jpg
ConvertPic(L"image/jpeg","c:"1.jpg","c:"1.bmp")
//jpg转化为bmp
ConvertPic(L"image/bmp","c:"1.bmp","c:"1.jpg")
//二。bmp图片合并
BOOL CombinePic(const WCHAR *format, const CString &strDst, const CString &strPic1,
const CString &strPic2)
{
BOOL bCombine = false;
int nRet = 0;
CLSID clsid;
nRet = GetEncoderClsid(format,&clsid);
if (nRet>=0)
{
USES_CONVERSION;
Bitmap bmp1(A2W(strPic1));
Bitmap bmp2(A2W(strPic2));
int nWidth = 0, nHeight = 0;
nWidth = bmp1.GetWidth(); //假设两图片大小同
nHeight = bmp1.GetHeight();
Bitmap bmpCombine(2*nWidth,nHeight); //高不变,宽*2,水平合并
Graphics * pG = NULL;
pG = Graphics::FromImage(&bmpCombine);
if (pG!=NULL)
{
pG->DrawImage(&bmp1,0,0);
pG->DrawImage(&bmp2,nWidth,0);
bmpCombine.Save(A2W(strDst),&clsid,NULL);
}
}
return bCombine;
}
有了上面的功能,其他的就没问题了。
例子:CombinePic(L"image/bmp","12.bmp","1.bmp","2.bmp");
//TCP的服务器端:(TcpSrv.cpp)
#include "Winsock2.h"
#include "stdio.h"
void main()
{
//初始化socket编程环境
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 )
{
WSACleanup( );
return;
}
//创建Tcp服务器socket
SOCKET sockSrv = socket( AF_INET , SOCK_STREAM , 0 );
//服务器地址
SOCKADDR_IN addrSrv ;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY) ;
addrSrv.sin_family = AF_INET ;
addrSrv.sin_port = htons(4000) ;
//将socket与地址绑定在一起
bind( sockSrv ,(SOCKADDR*)&addrSrv , sizeof(SOCKADDR) ) ;
//开始监听客户端请求,最大连接数为5
listen( sockSrv , 5 ) ;
//用于存放客户端地址
SOCKADDR_IN addrClient ;
int len = sizeof( SOCKADDR_IN ) ;
//不断接收客户端发送的请求
while ( 1 )
{
//接收到的客户端请求socket
SOCKET sockConn = accept( sockSrv , (SOCKADDR *)&addrClient , &len ) ;
//发送数据
char sendBuf[100] ;
sprintf( sendBuf , "Weclome %s to here:" , inet_ntoa(addrClient.sin_addr) ) ;
send( sockConn , sendBuf , strlen(sendBuf)+1 , 0 ) ;
//接收数据
char recvBuf[100] ;
recv( sockConn , recvBuf , 100 , 0 ) ;
printf("%s"n" , recvBuf ) ;
//关闭socket
closesocket( sockConn ) ;
}
//清理socket编程环境
WSACleanup() ;
}
//TCP的客户端:(TcpClient.cpp)
# include "Winsock2.h"
# include "stdio.h"
void main()
{
//初始化socket编程环境
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 )
{
WSACleanup( );
return;
}
//建立客户端socket
SOCKET sockClient = socket( AF_INET ,SOCK_STREAM , 0 ) ;
//服务器地址
SOCKADDR_IN addrSrv ;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1") ;
addrSrv.sin_family = AF_INET ;
addrSrv.sin_port = htons( 4000 ) ;
//连接服务器
connect( sockClient , (SOCKADDR*)&addrSrv , sizeof(SOCKADDR)) ;
//等待接收服务器的响应
char recvBuf[100];
recv( sockClient , recvBuf , 100 , 0 ) ;
printf( "%s"n" , recvBuf ) ;
char sendBuf[100] ;
sprintf( sendBuf , "%s" , "this is zhang san" ) ;
send( sockClient , sendBuf , strlen(sendBuf)+1 , 0 ) ;
closesocket( sockClient ) ;
WSACleanup() ;
}
Operating System Error Codes
Last reviewed: 04/09/2004
Article ID: R10307
Code |
Description |
Name |
0 |
The operation completed successfully. |
ERROR_SUCCESS |
1 |
Incorrect function. |
ERROR_INVALID_FUNCTION |
2 |
The system cannot find the file specified. |
ERROR_FILE_NOT_FOUND |
3 |
The system cannot find the path specified. |
ERROR_PATH_NOT_FOUND |
4 |
The system cannot open the file. |
ERROR_TOO_MANY_OPEN_FILES |
5 |
Access is denied. |
ERROR_ACCESS_DENIED |
6 |
The handle is invalid. |
ERROR_INVALID_HANDLE |
7 |
The storage control blocks were destroyed. |
ERROR_ARENA_TRASHED |
8 |
Not enough storage is available to process this command. |
ERROR_NOT_ENOUGH_MEMORY |
9 |
The storage control block address is invalid. |
ERROR_INVALID_BLOCK |
10 |
The environment is incorrect. |
ERROR_BAD_ENVIRONMENT |
11 |
An attempt was made to load a program with an incorrect format. |
ERROR_BAD_FORMAT |
12 |
The access code is invalid. |
ERROR_INVALID_ACCESS |
13 |
The data is invalid. |
ERROR_INVALID_DATA |
14 |
Not enough storage is available to complete this operation. |
ERROR_OUTOFMEMORY |
15 |
The system cannot find the drive specified. |
ERROR_INVALID_DRIVE |
16 |
The directory cannot be removed. |
ERROR_CURRENT_DIRECTORY |
17 |
The system cannot move the file to a different disk drive. |
ERROR_NOT_SAME_DEVICE |
18 |
There are no more files. |
ERROR_NO_MORE_FILES |
19 |
The media is write protected. |
ERROR_WRITE_PROTECT |
20 |
The system cannot find the device specified. |
ERROR_BAD_UNIT |
21 |
The device is not ready. |
ERROR_NOT_READY |
22 |
The device does not recognize the command. |
ERROR_BAD_COMMAND |
23 |
Data error (cyclic redundancy check). |
ERROR_CRC |
24 |
The program issued a command but the command length is incorrect. |
ERROR_BAD_LENGTH |
25 |
The drive cannot locate a specific area or track on the disk. |
ERROR_SEEK |
26 |
The specified disk or diskette cannot be accessed. |
ERROR_NOT_DOS_DISK |
27 |
The drive cannot find the sector requested. |
ERROR_SECTOR_NOT_FOUND |
28 |
The printer is out of paper. |
ERROR_OUT_OF_PAPER |
29 |
The system cannot write to the specified device. |
ERROR_WRITE_FAULT |
30 |
The system cannot read from the specified device. |
ERROR_READ_FAULT |
31 |
A device attached to the system is not functioning. |
ERROR_GEN_FAILURE |
32 |
The process cannot access the file because it is being used by another process. |
ERROR_SHARING_VIOLATION |
33 |
The process cannot access the file because another process has locked a portion of the file. |
ERROR_LOCK_VIOLATION |
34 |
The wrong diskette is in the drive. Insert %2 (Volume Serial Number: %3) into drive %1. |
ERROR_WRONG_DISK |
36 |
Too many files opened for sharing. |
ERROR_SHARING_BUFFER_EXCEEDED |
38 |
Reached the end of the file. |
ERROR_HANDLE_EOF |
39 |
The disk is full. |
ERROR_HANDLE_DISK_FULL |
50 |
The network request is not supported. |
ERROR_NOT_SUPPORTED |
51 |
The remote computer is not available. |
ERROR_REM_NOT_LIST |
52 |
A duplicate name exists on the network. |
ERROR_DUP_NAME |
53 |
The network path was not found. |
ERROR_BAD_NETPATH |
54 |
The network is busy. |
ERROR_NETWORK_BUSY |
55 |
The specified network resource or device is no longer available. |
ERROR_DEV_NOT_EXIST |
56 |
The network BIOS command limit has been reached. |
ERROR_TOO_MANY_CMDS |
57 |
A network adapter hardware error occurred. |
ERROR_ADAP_HDW_ERR |
58 |
The specified server cannot perform the requested operation. |
ERROR_BAD_NET_RESP |
59 |
An unexpected network error occurred. |
ERROR_UNEXP_NET_ERR |
60 |
The remote adapter is not compatible. |
ERROR_BAD_REM_ADAP |
61 |
The printer queue is full. |
ERROR_PRINTQ_FULL |
62 |
Space to store the file waiting to be printed is not available on the server. |
ERROR_NO_SPOOL_SPACE |
63 |
Your file waiting to be printed was deleted. |
ERROR_PRINT_CANCELLED |
64 |
The specified network name is no longer available. |
ERROR_NETNAME_DELETED |
65 |
Network access is denied. |
ERROR_NETWORK_ACCESS_DENIED |
66 |
The network resource type is not correct. |
ERROR_BAD_DEV_TYPE |
67 |
The network name cannot be found. |
ERROR_BAD_NET_NAME |
68 |
The name limit for the local computer network adapter card was exceeded. |
ERROR_TOO_MANY_NAMES |
69 |
The network BIOS session limit was exceeded. |
ERROR_TOO_MANY_SESS |
70 |
The remote server has been paused or is in the process of being started. |
ERROR_SHARING_PAUSED |
71 |
No more connections can be made to this remote computer at this time because there are already as many connections as the computer can accept. |
ERROR_REQ_NOT_ACCEP |
72 |
The specified printer or disk device has been paused. |
ERROR_REDIR_PAUSED |
80 |
The file exists. |
ERROR_FILE_EXISTS |
82 |
The directory or file cannot be created. |
ERROR_CANNOT_MAKE |
83 |
Fail on INT 24. |
ERROR_FAIL_I24 |
84 |
Storage to process this request is not available. |
ERROR_OUT_OF_STRUCTURES |
85 |
The local device name is already in use. |
ERROR_ALREADY_ASSIGNED |
86 |
The specified network password is not correct. |
ERROR_INVALID_PASSWORD |
87 |
The parameter is incorrect. |
ERROR_INVALID_PARAMETER |
88 |
A write fault occurred on the network. |
ERROR_NET_WRITE_FAULT |
89 |
The system cannot start another process at this time. |
ERROR_NO_PROC_SLOTS |
100 |
Cannot create another system semaphore. |
ERROR_TOO_MANY_SEMAPHORES |
101 |
The exclusive semaphore is owned by another process. |
ERROR_EXCL_SEM_ALREADY_OWNED |
102 |
The semaphore is set and cannot be closed. |
ERROR_SEM_IS_SET |
103 |
The semaphore cannot be set again. |
ERROR_TOO_MANY_SEM_REQUESTS |
104 |
Cannot request exclusive semaphores at interrupt time. |
ERROR_INVALID_AT_INTERRUPT_TIME |
105 |
The previous ownership of this semaphore has ended. |
ERROR_SEM_OWNER_DIED |
106 |
Insert the diskette for drive %1. |
ERROR_SEM_USER_LIMIT |
107 |
The program stopped because an alternate diskette was not inserted. |
ERROR_DISK_CHANGE |
108 |
The disk is in use or locked by another process. |
ERROR_DRIVE_LOCKED |
109 |
The pipe has been ended. |
ERROR_BROKEN_PIPE |
110 |
The system cannot open the device or file specified. |
ERROR_OPEN_FAILED |
111 |
The file name is too long. |
ERROR_BUFFER_OVERFLOW |
112 |
There is not enough space on the disk. |
ERROR_DISK_FULL |
113 |
No more internal file identifiers available. |
ERROR_NO_MORE_SEARCH_HANDLES |
114 |
The target internal file identifier is incorrect. |
ERROR_INVALID_TARGET_HANDLE |
117 |
The IOCTL call made by the application program is not correct. |
ERROR_INVALID_CATEGORY |
118 |
The verify-on-write switch parameter value is not correct. |
ERROR_INVALID_VERIFY_SWITCH |
119 |
The system does not support the command requested. |
ERROR_BAD_DRIVER_LEVEL |
120 |
This function is not valid on this platform. |
ERROR_CALL_NOT_IMPLEMENTED |
121 |
The semaphore time-out period has expired. |
ERROR_SEM_TIMEOUT |
122 |
The data area passed to a system call is too small. |
ERROR_INSUFFICIENT_BUFFER |
123 |
The filename, directory name, or volume label syntax is incorrect. |
ERROR_INVALID_NAME |
124 |
The system call level is not correct. |
ERROR_INVALID_LEVEL |
125 |
The disk has no volume label. |
ERROR_NO_VOLUME_LABEL |
126 |
The specified module could not be found. |
ERROR_MOD_NOT_FOUND |
127 |
The specified procedure could not be found. |
ERROR_PROC_NOT_FOUND |
128 |
There are no child processes to wait for. |
ERROR_WAIT_NO_CHILDREN |
129 |
The %1 application cannot be run in Windows NT mode. |
ERROR_CHILD_NOT_COMPLETE |
130 |
Attempt to use a file handle to an open disk partition for an operation other than raw disk I/O. |
ERROR_DIRECT_ACCESS_HANDLE |
131 |
An attempt was made to move the file pointer before the beginning of the file. |
ERROR_NEGATIVE_SEEK |
132 |
The file pointer cannot be set on the specified device or file. |
ERROR_SEEK_ON_DEVICE |
133 |
A JOIN or SUBST command cannot be used for a drive that contains previously joined drives. |
ERROR_IS_JOIN_TARGET |
134 |
An attempt was made to use a JOIN or SUBST command on a drive that has already been joined. |
ERROR_IS_JOINED |
135 |
An attempt was made to use a JOIN or SUBST command on a drive that has already been substituted. |
ERROR_IS_SUBSTED |
136 |
The system tried to delete the JOIN of a drive that is not joined. |
ERROR_NOT_JOINED |
137 |
The system tried to delete the substitution of a drive that is not substituted. |
ERROR_NOT_SUBSTED |
138 |
The system tried to join a drive to a directory on a joined drive. |
ERROR_JOIN_TO_JOIN |
139 |
The system tried to substitute a drive to a directory on a substituted drive. |
ERROR_SUBST_TO_SUBST |
140 |
The system tried to join a drive to a directory on a substituted drive. |
ERROR_JOIN_TO_SUBST |
141 |
The system tried to SUBST a drive to a directory on a joined drive. |
ERROR_SUBST_TO_JOIN |
142 |
The system cannot perform a JOIN or SUBST at this time. |
ERROR_BUSY_DRIVE |
143 |
The system cannot join or substitute a drive to or for a directory on the same drive. |
ERROR_SAME_DRIVE |
144 |
The directory is not a subdirectory of the root directory. |
ERROR_DIR_NOT_ROOT |
145 |
The directory is not empty. |
ERROR_DIR_NOT_EMPTY |
146 |
The path specified is being used in a substitute. |
ERROR_IS_SUBST_PATH |
147 |
Not enough resources are available to process this command. |
ERROR_IS_JOIN_PATH |
148 |
The path specified cannot be used at this time. |
ERROR_PATH_BUSY |
149 |
An attempt was made to join or substitute a drive for which a directory on the drive is the target of a previous substitute. |
ERROR_IS_SUBST_TARGET |
150 |
System trace information was not specified in your CONFIG.SYS file, or tracing is disallowed. |
ERROR_SYSTEM_TRACE |
151 |
The number of specified semaphore events for DosMuxSemWait is not correct. |
ERROR_INVALID_EVENT_COUNT |
152 |
DosMuxSemWait did not execute; too many semaphores are already set. |
ERROR_TOO_MANY_MUXWAITERS |
153 |
The DosMuxSemWait list is not correct. |
ERROR_INVALID_LIST_FORMAT |
154 |
The volume label you entered exceeds the label character limit of the target file system. |
ERROR_LABEL_TOO_LONG |
155 |
Cannot create another thread. |
ERROR_TOO_MANY_TCBS |
156 |
The recipient process has refused the signal. |
ERROR_SIGNAL_REFUSED |
157 |
The segment is already discarded and cannot be locked. |
ERROR_DISCARDED |
158 |
The segment is already unlocked. |
ERROR_NOT_LOCKED |
159 |
The address for the thread ID is not correct. |
ERROR_BAD_THREADID_ADDR |
160 |
The argument string passed to DosExecPgm is not correct. |
ERROR_BAD_ARGUMENTS |
161 |
The specified path is invalid. |
ERROR_BAD_PATHNAME |
162 |
A signal is already pending. |
ERROR_SIGNAL_PENDING |
164 |
No more threads can be created in the system. |
ERROR_MAX_THRDS_REACHED |
167 |
Unable to lock a region of a file. |
ERROR_LOCK_FAILED |
170 |
The requested resource is in use. |
ERROR_BUSY |
173 |
A lock request was not outstanding for the supplied cancel region. |
ERROR_CANCEL_VIOLATION |
174 |
The file system does not support atomic changes to the lock type. |
ERROR_ATOMIC_LOCKS_NOT_SUPPORTED |
180 |
The system detected a segment number that was not correct. |
ERROR_INVALID_SEGMENT_NUMBER |
182 |
The operating system cannot run %1. |
ERROR_INVALID_ORDINAL |
183 |
Cannot create a file when that file already exists. |
ERROR_ALREADY_EXISTS |
186 |
The flag passed is not correct. |
ERROR_INVALID_FLAG_NUMBER |
187 |
The specified system semaphore name was not found. |
ERROR_SEM_NOT_FOUND |
188 |
The operating system cannot run %1. |
ERROR_INVALID_STARTING_CODESEG |
189 |
The operating system cannot run %1. |
ERROR_INVALID_STACKSEG |
190 |
The operating system cannot run %1. |
ERROR_INVALID_MODULETYPE |
191 |
Cannot run %1 in Windows NT mode. |
ERROR_INVALID_EXE_SIGNATURE |
192 |
The operating system cannot run %1. |
ERROR_EXE_MARKED_INVALID |
193 |
Is not a valid application. |
ERROR_BAD_EXE_FORMAT |
194 |
The operating system cannot run %1. |
ERROR_ITERATED_DATA_EXCEEDS_64k |
195 |
The operating system cannot run %1. |
ERROR_INVALID_MINALLOCSIZE |
196 |
The operating system cannot run this application program. |
ERROR_DYNLINK_FROM_INVALID_RING |
197 |
The operating system is not presently configured to run this application. |
ERROR_IOPL_NOT_ENABLED |
198 |
The operating system cannot run %1. |
ERROR_INVALID_SEGDPL |
199 |
The operating system cannot run this application program. |
ERROR_AUTODATASEG_EXCEEDS_64k |
200 |
The code segment cannot be greater than or equal to 64K. |
ERROR_RING2SEG_MUST_BE_MOVABLE |
201 |
The operating system cannot run %1. |
ERROR_RELOC_CHAIN_XEEDS_SEGLIM |
202 |
The operating system cannot run %1. |
ERROR_INFLOOP_IN_RELOC_CHAIN |
203 |
The system could not find the environment option that was entered. |
ERROR_ENVVAR_NOT_FOUND |
205 |
No process in the command subtree has a signal handler. |
ERROR_NO_SIGNAL_SENT |
206 |
The filename or extension is too long. |
ERROR_FILENAME_EXCED_RANGE |
207 |
The ring 2 stack is in use. |
ERROR_RING2_STACK_IN_USE |
208 |
The global filename characters, * or ?, are entered incorrectly or too many global filename characters are specified. |
ERROR_META_EXPANSION_TOO_LONG |
209 |
The signal being posted is not correct. |
ERROR_INVALID_SIGNAL_NUMBER |
210 |
The signal handler cannot be set. |
ERROR_THREAD_1_INACTIVE |
212 |
The segment is locked and cannot be reallocated. |
ERROR_LOCKED |
214 |
Too many dynamic-link modules are attached to this program or dynamic-link module. |
ERROR_TOO_MANY_MODULES |
215 |
Can't nest calls to LoadModule. |
ERROR_NESTING_NOT_ALLOWED |
216 |
The image file %1 is valid, but is for a machine type other than the current machine. |
ERROR_EXE_MACHINE_TYPE_MISMATCH |
230 |
The pipe state is invalid. |
ERROR_BAD_PIPE |
231 |
All pipe instances are busy. |
ERROR_PIPE_BUSY |
232 |
The pipe is being closed. |
ERROR_NO_DATA |
233 |
No process is on the other end of the pipe. |
ERROR_PIPE_NOT_CONNECTED |
234 |
More data is available. |
ERROR_MORE_DATA |
240 |
The session was canceled. |
ERROR_VC_DISCONNECTED |
254 |
The specified extended attribute name was invalid. |
ERROR_INVALID_EA_NAME |
255 |
The extended attributes are inconsistent. |
ERROR_EA_LIST_INCONSISTENT |
259 |
No more data is available. |
ERROR_NO_MORE_ITEMS |
266 |
The copy functions cannot be used. |
ERROR_CANNOT_COPY |
267 |
The directory name is invalid. |
ERROR_DIRECTORY |
275 |
The extended attributes did not fit in the buffer. |
ERROR_EAS_DIDNT_FIT |
276 |
The extended attribute file on the mounted file system is corrupt. |
ERROR_EA_FILE_CORRUPT |
277 |
The extended attribute table file is full. |
ERROR_EA_TABLE_FULL |
278 |
The specified extended attribute handle is invalid. |
ERROR_INVALID_EA_HANDLE |
282 |
The mounted file system does not support extended attributes. |
ERROR_EAS_NOT_SUPPORTED |
288 |
Attempt to release mutex not owned by caller. |
ERROR_NOT_OWNER |
298 |
Too many posts were made to a semaphore. |
ERROR_TOO_MANY_POSTS |
299 |
Only part of a ReadProcessMemory or WriteProcessMemory request was completed. |
ERROR_PARTIAL_COPY |
317 |
The system cannot find message text for message number 0x%1 in the message file for %2. |
ERROR_MR_MID_NOT_FOUND |
487 |
Attempt to access invalid address. |
ERROR_INVALID_ADDRESS |
534 |
Arithmetic result exceeded 32 bits. |
ERROR_ARITHMETIC_OVERFLOW |
535 |
There is a process on other end of the pipe. |
ERROR_PIPE_CONNECTED |
536 |
Waiting for a process to open the other end of the pipe. |
ERROR_PIPE_LISTENING |
994 |
Access to the extended attribute was denied. |
ERROR_EA_ACCESS_DENIED |
995 |
The I/O operation has been aborted because of either a thread exit or an application request. |
ERROR_OPERATION_ABORTED |
996 |
Overlapped I/O event is not in a signaled state. |
ERROR_IO_INCOMPLETE |
997 |
Overlapped I/O operation is in progress. |
ERROR_IO_PENDING |
998 |
Invalid access to memory location. |
ERROR_NOACCESS |
999 |
Error performing inpage operation. |
ERROR_SWAPERROR |
1001 |
Recursion too deep; the stack overflowed. |
ERROR_STACK_OVERFLOW |
1002 |
The window cannot act on the sent message. |
ERROR_INVALID_MESSAGE |
1003 |
Cannot complete this function. |
ERROR_CAN_NOT_COMPLETE |
1004 |
Invalid flags. |
ERROR_INVALID_FLAGS |
1005 |
The volume does not contain a recognized file system. Please make sure that all required file system drivers are loaded and that the volume is not corrupted. |
ERROR_UNRECOGNIZED_VOLUME |
1006 |
The volume for a file has been externally altered so that the opened file is no longer valid. |
ERROR_FILE_INVALID |
1007 |
The requested operation cannot be performed in full-screen mode. |
ERROR_FULLSCREEN_MODE |
1008 |
An attempt was made to reference a token that does not exist. |
ERROR_NO_TOKEN |
1009 |
The configuration registry database is corrupt. |
ERROR_BADDB |
1010 |
The configuration registry key is invalid. |
ERROR_BADKEY |
1011 |
The configuration registry key could not be opened. |
ERROR_CANTOPEN |
1012 |
The configuration registry key could not be read. |
ERROR_CANTREAD |
1013 |
The configuration registry key could not be written. |
ERROR_CANTWRITE |
1014 |
One of the files in the registry database had to be recovered by use of a log or alternate copy. The recovery was successful. |
ERROR_REGISTRY_RECOVERED |
1015 |
The registry is corrupted. The structure of one of the files that contains registry data is corrupted, or the system's image of the file in memory is corrupted, or the file could not be recovered because the alternate copy or log was absent or corrupted. |
ERROR_REGISTRY_CORRUPT |
1016 |
An I/O operation initiated by the registry failed unrecoverably. The registry could not read in, or write out, or flush, one of the files that contain the system's image of the registry. |
ERROR_REGISTRY_IO_FAILED |
1017 |
The system has attempted to load or restore a file into the registry, but the specified file is not in a registry file format. |
ERROR_NOT_REGISTRY_FILE |
1018 |
Illegal operation attempted on a registry key that has been marked for deletion. |
ERROR_KEY_DELETED |
1019 |
System could not allocate the required space in a registry log. |
ERROR_NO_LOG_SPACE |
1020 |
Cannot create a symbolic link in a registry key that already has subkeys or values. |
ERROR_KEY_HAS_CHILDREN |
1021 |
Cannot create a stable subkey under a volatile parent key. |
ERROR_CHILD_MUST_BE_VOLATILE |
1022 |
A notify change request is being completed and the information is not being returned in the caller's buffer. The caller now needs to enumerate the files to find the changes. |
ERROR_NOTIFY_ENUM_DIR |
1051 |
A stop control has been sent to a service that other running services are dependent on. |
ERROR_DEPENDENT_SERVICES_RUNNING |
1052 |
The requested control is not valid for this service. |
ERROR_INVALID_SERVICE_CONTROL |
1053 |
The service did not respond to the start or control request in a timely fashion. |
ERROR_SERVICE_REQUEST_TIMEOUT |
1054 |
A thread could not be created for the service. |
ERROR_SERVICE_NO_THREAD |
1055 |
The service database is locked. |
ERROR_SERVICE_DATABASE_LOCKED |
1056 |
An instance of the service is already running. |
ERROR_SERVICE_ALREADY_RUNNING |
1057 |
The account name is invalid or does not exist. |
ERROR_INVALID_SERVICE_ACCOUNT |
1058 |
The specified service is disabled and cannot be started. |
ERROR_SERVICE_DISABLED |
1059 |
Circular service dependency was specified. |
ERROR_CIRCULAR_DEPENDENCY |
1060 |
The specified service does not exist as an installed service. |
ERROR_SERVICE_DOES_NOT_EXIST |
1061 |
The service cannot accept control messages at this time. |
ERROR_SERVICE_CANNOT_ACCEPT_CTRL |
1062 |
The service has not been started. |
ERROR_SERVICE_NOT_ACTIVE |
1063 |
The service process could not connect to the service controller. |
ERROR_FAILED_SERVICE_CONTROLLER_CONNECT |
1064 |
An exception occurred in the service when handling the control request. |
ERROR_EXCEPTION_IN_SERVICE |
1065 |
The database specified does not exist. |
ERROR_DATABASE_DOES_NOT_EXIST |
1066 |
The service has returned a service-specific error code. |
ERROR_SERVICE_SPECIFIC_ERROR |
1067 |
The process terminated unexpectedly. |
ERROR_PROCESS_ABORTED |
1068 |
The dependency service or group failed to start. |
ERROR_SERVICE_DEPENDENCY_FAIL |
1069 |
The service did not start due to a logon failure. |
ERROR_SERVICE_LOGON_FAILED |
1070 |
After starting, the service hung in a start-pending state. |
ERROR_SERVICE_START_HANG |
1071 |
The specified service database lock is invalid. |
ERROR_INVALID_SERVICE_LOCK |
1072 |
The specified service has been marked for deletion. |
ERROR_SERVICE_MARKED_FOR_DELETE |
1073 |
The specified service already exists. |
ERROR_SERVICE_EXISTS |
1074 |
The system is currently running with the last-known-good configuration. |
ERROR_ALREADY_RUNNING_LKG |
1075 |
The dependency service does not exist or has been marked for deletion. |
ERROR_SERVICE_DEPENDENCY_DELETED |
1076 |
The current boot has already been accepted for use as the last-known-good control set. |
ERROR_BOOT_ALREADY_ACCEPTED |
1077 |
No attempts to start the service have been made since the last boot. |
ERROR_SERVICE_NEVER_STARTED |
1078 |
The name is already in use as either a service name or a service display name. |
ERROR_DUPLICATE_SERVICE_NAME |
1079 |
The account specified for this service is different from the account specified for other services running in the same process. |
ERROR_DIFFERENT_SERVICE_ACCOUNT |
1100 |
The physical end of the tape has been reached. |
ERROR_END_OF_MEDIA |
1101 |
A tape access reached a filemark. |
ERROR_FILEMARK_DETECTED |
1102 |
The beginning of the tape or partition was encountered. |
ERROR_BEGINNING_OF_MEDIA |
1103 |
A tape access reached the end of a set of files. |
ERROR_SETMARK_DETECTED |
1104 |
No more data is on the tape. |
ERROR_NO_DATA_DETECTED |
1105 |
Tape could not be partitioned. |
ERROR_PARTITION_FAILURE |
1106 |
When accessing a new tape of a multivolume partition, the current block size is incorrect. |
ERROR_INVALID_BLOCK_LENGTH |
1107 |
Tape partition information could not be found when loading a tape. |
ERROR_DEVICE_NOT_PARTITIONED |
1108 |
Unable to lock the media eject mechanism. |
ERROR_UNABLE_TO_LOCK_MEDIA |
1109 |
Unable to unload the media. |
ERROR_UNABLE_TO_UNLOAD_MEDIA |
1110 |
The media in the drive may have changed. |
ERROR_MEDIA_CHANGED |
1111 |
The I/O bus was reset. |
ERROR_BUS_RESET |
1112 |
No media in drive. |
ERROR_NO_MEDIA_IN_DRIVE |
1113 |
No mapping for the Unicode character exists in the target multibyte code page. |
ERROR_NO_UNICODE_TRANSLATION |
1114 |
A dynamic link library (DLL) initialization routine failed. |
ERROR_DLL_INIT_FAILED |
1115 |
A system shutdown is in progress. |
ERROR_SHUTDOWN_IN_PROGRESS |
1116 |
Unable to abort the system shutdown because no shutdown was in progress. |
ERROR_NO_SHUTDOWN_IN_PROGRESS |
1117 |
The request could not be performed because of an I/O device error. |
ERROR_IO_DEVICE |
1118 |
No serial device was successfully initialized. The serial driver will unload. |
ERROR_SERIAL_NO_DEVICE |
1119 |
Unable to open a device that was sharing an interrupt request (IRQ) with other devices. At least one other device that uses that IRQ was already opened. |
ERROR_IRQ_BUSY |
1120 |
A serial I/O operation was completed by another write to the serial port. The IOCTL_SERIAL_XOFF_COUNTER reached zero.) |
ERROR_MORE_WRITES |
1121 |
A serial I/O operation completed because the time-out period expired. (The IOCTL_SERIAL_XOFF_COUNTER did not reach zero.) |
ERROR_COUNTER_TIMEOUT |
1122 |
No ID address mark was found on the floppy disk. |
ERROR_FLOPPY_ID_MARK_NOT_FOUND |
1123 |
Mismatch between the floppy disk sector ID field and the floppy disk controller track address. |
ERROR_FLOPPY_WRONG_CYLINDER |
1124 |
The floppy disk controller reported an error that is not recognized by the floppy disk driver. |
ERROR_FLOPPY_UNKNOWN_ERROR |
1125 |
The floppy disk controller returned inconsistent results in its registers. |
ERROR_FLOPPY_BAD_REGISTERS |
1126 |
While accessing the hard disk, a recalibrate operation failed, even after retries. |
ERROR_DISK_RECALIBRATE_FAILED |
1127 |
While accessing the hard disk, a disk operation failed even after retries. |
ERROR_DISK_OPERATION_FAILED |
1128 |
While accessing the hard disk, a disk controller reset was needed, but even that failed. |
ERROR_DISK_RESET_FAILED |
1129 |
Physical end of tape encountered. |
ERROR_EOM_OVERFLOW |
1130 |
Not enough server storage is available to process this command. |
ERROR_NOT_ENOUGH_SERVER_MEMORY |
1131 |
A potential deadlock condition has been detected. |
ERROR_POSSIBLE_DEADLOCK |
1132 |
The base address or the file offset specified does not have the proper alignment. |
ERROR_MAPPED_ALIGNMENT |
1140 |
An attempt to change the system power state was vetoed by another application or driver. |
ERROR_SET_POWER_STATE_VETOED |
1141 |
The system BIOS failed an attempt to change the system power state. |
ERROR_SET_POWER_STATE_FAILED |
1142 |
An attempt was made to create more links on a file than the file system supports. |
ERROR_TOO_MANY_LINKS |
1150 |
The specified program requires a newer version of Windows. |
ERROR_OLD_WIN_VERSION |
1151 |
The specified program is not a Windows or MS-DOS program. |
ERROR_APP_WRONG_OS |
1152 |
Cannot start more than one instance of the specified program. |
ERROR_SINGLE_INSTANCE_APP |
1153 |
The specified program was written for an earlier version of Windows. |
ERROR_RMODE_APP |
1154 |
One of the library files needed to run this application is damaged. |
ERROR_INVALID_DLL |
1155 |
No application is associated with the specified file for this operation. |
ERROR_NO_ASSOCIATION |
1156 |
An error occurred in sending the command to the application. |
ERROR_DDE_FAIL |
1157 |
One of the library files needed to run this application cannot be found. |
ERROR_DLL_NOT_FOUND |
1200 |
The specified device name is invalid. |
ERROR_BAD_DEVICE |
1201 |
The device is not currently connected but it is a remembered connection. |
ERROR_CONNECTION_UNAVAIL |
1202 |
An attempt was made to remember a device that had previously been remembered. |
ERROR_DEVICE_ALREADY_REMEMBERED |
1203 |
No network provider accepted the given network path. |
ERROR_NO_NET_OR_BAD_PATH |
1204 |
The specified network provider name is invalid. |
ERROR_BAD_PROVIDER |
1205 |
Unable to open the network connection profile. |
ERROR_CANNOT_OPEN_PROFILE |
1206 |
The network connection profile is corrupt. |
ERROR_BAD_PROFILE |
1207 |
Cannot enumerate a noncontainer. |
ERROR_NOT_CONTAINER |
1208 |
An extended error has occurred. |
ERROR_EXTENDED_ERROR |
1209 |
The format of the specified group name is invalid. |
ERROR_INVALID_GROUPNAME |
1210 |
The format of the specified computer name is invalid. |
ERROR_INVALID_COMPUTERNAME |
1211 |
The format of the specified event name is invalid. |
ERROR_INVALID_EVENTNAME |
1212 |
The format of the specified domain name is invalid. |
ERROR_INVALID_DOMAINNAME |
1213 |
The format of the specified service name is invalid. |
ERROR_INVALID_SERVICENAME |
1214 |
The format of the specified network name is invalid. |
ERROR_INVALID_NETNAME |
1215 |
The format of the specified share name is invalid. |
ERROR_INVALID_SHARENAME |
1216 |
The format of the specified password is invalid. |
ERROR_INVALID_PASSWORDNAME |
1217 |
The format of the specified message name is invalid. |
ERROR_INVALID_MESSAGENAME |
1218 |
The format of the specified message destination is invalid. |
ERROR_INVALID_MESSAGEDEST |
1219 |
The credentials supplied conflict with an existing set of credentials. |
ERROR_SESSION_CREDENTIAL_CONFLICT |
1220 |
An attempt was made to establish a session to a network server, but there are already too many sessions established to that server. |
ERROR_REMOTE_SESSION_LIMIT_EXCEEDED |
1221 |
The workgroup or domain name is already in use by another computer on the network. |
ERROR_DUP_DOMAINNAME |
1222 |
The network is not present or not started. |
ERROR_NO_NETWORK |
1223 |
The operation was canceled by the user. |
ERROR_CANCELLED |
1224 |
The requested operation cannot be performed on a file with a user-mapped section open. |
ERROR_USER_MAPPED_FILE |
1225 |
The remote system refused the network connection. |
ERROR_CONNECTION_REFUSED |
1226 |
The network connection was gracefully closed. |
ERROR_GRACEFUL_DISCONNECT |
1227 |
The network transport endpoint already has an address associated with it. |
ERROR_ADDRESS_ALREADY_ASSOCIATED |
1228 |
An address has not yet been associated with the network endpoint. |
ERROR_ADDRESS_NOT_ASSOCIATED |
1229 |
An operation was attempted on a nonexistent network connection. |
ERROR_CONNECTION_INVALID |
1230 |
An invalid operation was attempted on an active network connection. |
ERROR_CONNECTION_ACTIVE |
1231 |
The remote network is not reachable by the transport. |
ERROR_NETWORK_UNREACHABLE |
1232 |
The remote system is not reachable by the transport. |
ERROR_HOST_UNREACHABLE |
1233 |
The remote system does not support the transport protocol. |
ERROR_PROTOCOL_UNREACHABLE |
1234 |
No service is operating at the destination network endpoint on the remote system. |
ERROR_PORT_UNREACHABLE |
1235 |
The request was aborted. |
ERROR_REQUEST_ABORTED |
1236 |
The network connection was aborted by the local system. |
ERROR_CONNECTION_ABORTED |
1237 |
The operation could not be completed. A retry should be performed. |
ERROR_RETRY |
1238 |
A connection to the server could not be made because the limit on the number of concurrent connections for this account has been reached. |
ERROR_CONNECTION_COUNT_LIMIT |
1239 |
Attempting to log in during an unauthorized time of day for this account. |
ERROR_LOGIN_TIME_RESTRICTION |
1240 |
The account is not authorized to log in from this station. |
ERROR_LOGIN_WKSTA_RESTRICTION |
1241 |
The network address could not be used for the operation requested. |
ERROR_INCORRECT_ADDRESS |
1242 |
The service is already registered. |
ERROR_ALREADY_REGISTERED |
1243 |
The specified service does not exist. |
ERROR_SERVICE_NOT_FOUND |
1244 |
The operation being requested was not performed because the user has not been authenticated. |
ERROR_NOT_AUTHENTICATED |
1245 |
The operation being requested was not performed because the user has not logged on to the network. The specified service does not exist. |
ERROR_NOT_LOGGED_ON |
1246 |
Caller to continue with work in progress. |
ERROR_CONTINUE |
1247 |
An attempt was made to perform an initialization operation when initialization has already been completed. |
ERROR_ALREADY_INITIALIZED |
1248 |
No more local devices. |
ERROR_NO_MORE_DEVICES |
1300 |
Not all privileges referenced are assigned to the caller. |
ERROR_NOT_ALL_ASSIGNED |
1301 |
Some mapping between account names and security IDs was not done. |
ERROR_SOME_NOT_MAPPED |
1302 |
No system quota limits are specifically set for this account. |
ERROR_NO_QUOTAS_FOR_ACCOUNT |
1303 |
No encryption key is available. A well-known encryption key was returned. |
ERROR_LOCAL_USER_SESSION_KEY |
1304 |
The password is too complex to be converted to a LAN Manager password. The LAN Manager password returned is a NULL string. |
ERROR_NULL_LM_PASSWORD |
1305 |
The revision level is unknown. |
ERROR_UNKNOWN_REVISION |
1306 |
Indicates two revision levels are incompatible. |
ERROR_REVISION_MISMATCH |
1307 |
This security ID may not be assigned as the owner of this object. |
ERROR_INVALID_OWNER |
1308 |
This security ID may not be assigned as the primary group of an object. |
ERROR_INVALID_PRIMARY_GROUP |
1309 |
An attempt has been made to operate on an impersonation token by a thread that is not currently impersonating a client. |
ERROR_NO_IMPERSONATION_TOKEN |
1310 |
The group may not be disabled. |
ERROR_CANT_DISABLE_MANDATORY |
1311 |
There are currently no logon servers available to service the logon request. |
ERROR_NO_LOGON_SERVERS |
1312 |
A specified logon session does not exist. It may already have been terminated. |
ERROR_NO_SUCH_LOGON_SESSION |
1313 |
A specified privilege does not exist. |
ERROR_NO_SUCH_PRIVILEGE |
1314 |
A required privilege is not held by the client. |
ERROR_PRIVILEGE_NOT_HELD |
1315 |
The name provided is not a properly formed account name. |
ERROR_INVALID_ACCOUNT_NAME |
1316 |
The specified user already exists. |
ERROR_USER_EXISTS |
1317 |
The specified user does not exist. |
ERROR_NO_SUCH_USER |
1318 |
The specified group already exists. |
ERROR_GROUP_EXISTS |
1319 |
The specified group does not exist. |
ERROR_NO_SUCH_GROUP |
1320 |
Either the specified user account is already a member of the specified group, or the specified group cannot be deleted because it contains a member. |
ERROR_MEMBER_IN_GROUP |
1321 |
The specified user account is not a member of the specified group account. |
ERROR_MEMBER_NOT_IN_GROUP |
1322 |
The last remaining administration account cannot be disabled or deleted. |
ERROR_LAST_ADMIN |
1323 |
Unable to update the password. The value provided as the current password is incorrect. |
ERROR_WRONG_PASSWORD |
1324 |
Unable to update the password. The value provided for the new password contains values that are not allowed in passwords. |
ERROR_ILL_FORMED_PASSWORD |
1325 |
Unable to update the password because a password update rule has been violated. |
ERROR_PASSWORD_RESTRICTION |
1326 |
Logon failure: unknown user name or bad password. |
ERROR_LOGON_FAILURE |
1327 |
Logon failure: user account restriction. |
ERROR_ACCOUNT_RESTRICTION |
1328 |
Logon failure: account logon time restriction violation. |
ERROR_INVALID_LOGON_HOURS |
1329 |
Logon failure: user not allowed to log on to this computer. |
ERROR_INVALID_WORKSTATION |
1330 |
Logon failure: the specified account password has expired. |
ERROR_PASSWORD_EXPIRED |
1331 |
Logon failure: account currently disabled. |
ERROR_ACCOUNT_DISABLED |
1332 |
No mapping between account names and security IDs was done. |
ERROR_NONE_MAPPED |
1333 |
Too many local user identifiers (LUIDs) were requested at one time. |
ERROR_TOO_MANY_LUIDS_REQUESTED |
1334 |
No more local user identifiers (LUIDs) are available. |
ERROR_LUIDS_EXHAUSTED |
1335 |
The subauthority part of a security ID is invalid for this particular use. |
ERROR_INVALID_SUB_AUTHORITY |
1336 |
The access control list (ACL) structure is invalid. |
ERROR_INVALID_ACL |
1337 |
The security ID structure is invalid. |
ERROR_INVALID_SID |
1338 |
The security descriptor structure is invalid. |
ERROR_INVALID_SECURITY_DESCR |
1340 |
The inherited access control list (ACL) or access control entry (ACE) could not be built. |
ERROR_BAD_INHERITANCE_ACL |
1341 |
The server is currently disabled. |
ERROR_SERVER_DISABLED |
1342 |
The server is currently enabled. |
ERROR_SERVER_NOT_DISABLED |
1343 |
The value provided was an invalid value for an identifier authority. |
ERROR_INVALID_ID_AUTHORITY |
1344 |
No more memory is available for security information updates. |
ERROR_ALLOTTED_SPACE_EXCEEDED |
1345 |
The specified attributes are invalid, or incompatible with the attributes for the group as a whole. |
ERROR_INVALID_GROUP_ATTRIBUTES |
1346 |
Either a required impersonation level was not provided, or the provided impersonation level is invalid. |
ERROR_BAD_IMPERSONATION_LEVEL |
1347 |
Cannot open an anonymous level security token. |
ERROR_CANT_OPEN_ANONYMOUS |
1348 |
The validation information class requested was invalid. |
ERROR_BAD_VALIDATION_CLASS |
1349 |
The type of the token is inappropriate for its attempted use. |
ERROR_BAD_TOKEN_TYPE |
1350 |
Unable to perform a security operation on an object that has no associated security. |
ERROR_NO_SECURITY_ON_OBJECT |
1351 |
Indicates a Windows NT Server could not be contacted or that objects within the domain are protected such that necessary information could not be retrieved. |
ERROR_CANT_ACCESS_DOMAIN_INFO |
1352 |
The security account manager (SAM) or local security authority (LSA) server was in the wrong state to perform the security operation. |
ERROR_INVALID_SERVER_STATE |
1353 |
The domain was in the wrong state to perform the security operation. |
ERROR_INVALID_DOMAIN_STATE |
1354 |
This operation is only allowed for the Primary Domain Controller of the domain. |
ERROR_INVALID_DOMAIN_ROLE |
1355 |
The specified domain did not exist. |
ERROR_NO_SUCH_DOMAIN |
1356 |
The specified domain already exists. |
ERROR_DOMAIN_EXISTS |
1357 |
An attempt was made to exceed the limit on the number of domains per server. |
ERROR_DOMAIN_LIMIT_EXCEEDED |
1358 |
Unable to complete the requested operation because of either a catastrophic media failure or a data structure corruption on the disk. |
ERROR_INTERNAL_DB_CORRUPTION |
1359 |
The security account database contains an internal inconsistency. |
ERROR_INTERNAL_ERROR |
1360 |
Generic access types were contained in an access mask which should already be mapped to nongeneric types. |
ERROR_GENERIC_NOT_MAPPED |
1361 |
A security descriptor is not in the right format (absolute or self-relative). |
ERROR_BAD_DESCRIPTOR_FORMAT |
1362 |
The requested action is restricted for use by logon processes only. The calling process has not registered as a logon process. |
ERROR_NOT_LOGON_PROCESS |
1363 |
Cannot start a new logon session with an ID that is already in use. |
ERROR_LOGON_SESSION_EXISTS |
1364 |
A specified authentication package is unknown. |
ERROR_NO_SUCH_PACKAGE |
1365 |
The logon session is not in a state that is consistent with the requested operation. |
ERROR_BAD_LOGON_SESSION_STATE |
1366 |
The logon session ID is already in use. |
ERROR_LOGON_SESSION_COLLISION |
1367 |
A logon request contained an invalid logon type value. |
ERROR_INVALID_LOGON_TYPE |
1368 |
Unable to impersonate using a named pipe until data has been read from that pipe. |
ERROR_CANNOT_IMPERSONATE |
1369 |
The transaction state of a registry subtree is incompatible with the requested operation. |
ERROR_RXACT_INVALID_STATE |
1370 |
An internal security database corruption has been encountered. |
ERROR_RXACT_COMMIT_FAILURE |
1371 |
Cannot perform this operation on built-in accounts. |
ERROR_SPECIAL_ACCOUNT |
1372 |
Cannot perform this operation on this built-in special group. |
ERROR_SPECIAL_GROUP |
1373 |
Cannot perform this operation on this built-in special user. |
ERROR_SPECIAL_USER |
1374 |
The user cannot be removed from a group because the group is currently the user's primary group. |
ERROR_MEMBERS_PRIMARY_GROUP |
1375 |
The token is already in use as a primary token. |
ERROR_TOKEN_ALREADY_IN_USE |
1376 |
The specified local group does not exist. |
ERROR_NO_SUCH_ALIAS |
1377 |
The specified account name is not a member of the local group. |
ERROR_MEMBER_NOT_IN_ALIAS |
1378 |
The specified account name is already a member of the local group. |
ERROR_MEMBER_IN_ALIAS |
1379 |
The specified local group already exists. |
ERROR_ALIAS_EXISTS |
1380 |
Logon failure: the user has not been granted the requested logon type at this computer. |
ERROR_LOGON_NOT_GRANTED |
1381 |
The maximum number of secrets that may be stored in a single system has been exceeded. |
ERROR_TOO_MANY_SECRETS |
1382 |
The length of a secret exceeds the maximum length allowed. |
ERROR_SECRET_TOO_LONG |
1383 |
The local security authority database contains an internal inconsistency. |
ERROR_INTERNAL_DB_ERROR |
1384 |
During a logon attempt, the user's security context accumulated too many security IDs. |
ERROR_TOO_MANY_CONTEXT_IDS |
1385 |
Logon failure: the user has not been granted the requested logon type at this computer. |
ERROR_LOGON_TYPE_NOT_GRANTED |
1386 |
A cross-encrypted password is necessary to change a user password. |
ERROR_NT_CROSS_ENCRYPTION_REQUIRED |
1387 |
A new member could not be added to a local group because the member does not exist. |
ERROR_NO_SUCH_MEMBER |
1388 |
A new member could not be added to a local group because the member has the wrong account type. |
ERROR_INVALID_MEMBER |
1389 |
Too many security IDs have been specified. |
ERROR_TOO_MANY_SIDS |
1390 |
A cross-encrypted password is necessary to change this user password. |
ERROR_LM_CROSS_ENCRYPTION_REQUIRED |
1391 |
Indicates an ACL contains no inheritable components. |
ERROR_NO_INHERITANCE |
1392 |
The file or directory is corrupted and non-readable. |
ERROR_FILE_CORRUPT |
1393 |
The disk structure is corrupted and non-readable. |
ERROR_DISK_CORRUPT |
1394 |
There is no user session key for the specified logon session. |
ERROR_NO_USER_SESSION_KEY |
1395 |
The service being accessed is licensed for a particular number of connections. No more connections can be made to the service at this time because there are already as many connections as the service can accept. |
ERROR_LICENSE_QUOTA_EXCEEDED |
1400 |
Invalid window handle. |
ERROR_INVALID_WINDOW_HANDLE |
1401 |
Invalid menu handle. |
ERROR_INVALID_MENU_HANDLE |
1402 |
Invalid cursor handle. |
ERROR_INVALID_CURSOR_HANDLE |
1403 |
Invalid accelerator table handle. |
ERROR_INVALID_ACCEL_HANDLE |
1404 |
Invalid hook handle. |
ERROR_INVALID_HOOK_HANDLE |
1405 |
Invalid handle to a multiple-window position structure. |
ERROR_INVALID_DWP_HANDLE |
1406 |
Cannot create a top-level child window. |
ERROR_TLW_WITH_WSCHILD |
1407 |
Cannot find window class. |
ERROR_CANNOT_FIND_WND_CLASS |
1408 |
Invalid window, it belongs to another thread. |
ERROR_WINDOW_OF_OTHER_THREAD |
1409 |
Hot key is already registered. |
ERROR_HOTKEY_ALREADY_REGISTERED |
1410 |
Class already exists. |
ERROR_CLASS_ALREADY_EXISTS |
1411 |
Class does not exist. |
ERROR_CLASS_DOES_NOT_EXIST |
1412 |
Class still has open windows. |
ERROR_CLASS_HAS_WINDOWS |
1413 |
Invalid index. |
ERROR_INVALID_INDEX |
1414 |
Invalid icon handle. |
ERROR_INVALID_ICON_HANDLE |
1415 |
Using private DIALOG window words. |
ERROR_PRIVATE_DIALOG_INDEX |
1416 |
The list box identifier was not found. |
ERROR_LISTBOX_ID_NOT_FOUND |
1417 |
No wildcards were found. |
ERROR_NO_WILDCARD_CHARACTERS |
1418 |
Thread does not have a clipboard open. |
ERROR_CLIPBOARD_NOT_OPEN |
1419 |
Hot key is not registered. |
ERROR_HOTKEY_NOT_REGISTERED |
1420 |
The window is not a valid dialog window. |
ERROR_WINDOW_NOT_DIALOG |
1421 |
Control ID not found. |
ERROR_CONTROL_ID_NOT_FOUND |
1422 |
Invalid message for a combo box because it does not have an edit control. |
ERROR_INVALID_COMBOBOX_MESSAGE |
1423 |
The window is not a combo box. |
ERROR_WINDOW_NOT_COMBOBOX |
1424 |
Height must be less than 256. |
ERROR_INVALID_EDIT_HEIGHT |
1425 |
Invalid device context (DC) handle. |
ERROR_DC_NOT_FOUND |
1426 |
Invalid hook procedure type. |
ERROR_INVALID_HOOK_FILTER |
1427 |
Invalid hook procedure. |
ERROR_INVALID_FILTER_PROC |
1428 |
Cannot set nonlocal hook without a module handle. |
ERROR_HOOK_NEEDS_HMOD |
1429 |
This hook procedure can only be set globally. |
ERROR_GLOBAL_ONLY_HOOK |
1430 |
The journal hook procedure is already installed. |
ERROR_JOURNAL_HOOK_SET |
1431 |
The hook procedure is not installed. |
ERROR_HOOK_NOT_INSTALLED |
1432 |
Invalid message for single-selection list box. |
ERROR_INVALID_LB_MESSAGE |
1433 |
LB_SETCOUNT sent to non-lazy list box. |
ERROR_SETCOUNT_ON_BAD_LB |
1434 |
This list box does not support tab stops. |
ERROR_LB_WITHOUT_TABSTOPS |
1435 |
Cannot destroy object created by another thread. |
ERROR_DESTROY_OBJECT_OF_OTHER_THREAD |
1436 |
Child windows cannot have menus. |
ERROR_CHILD_WINDOW_MENU |
1437 |
The window does not have a system menu. |
ERROR_NO_SYSTEM_MENU |
1438 |
Invalid message box style. |
ERROR_INVALID_MSGBOX_STYLE |
1439 |
Invalid system-wide (SPI_*) parameter. |
ERROR_INVALID_SPI_VALUE |
1440 |
Screen already locked. |
ERROR_SCREEN_ALREADY_LOCKED |
1441 |
All handles to windows in a multiple-window position structure must have the same parent. |
ERROR_HWNDS_HAVE_DIFF_PARENT |
1442 |
The window is not a child window. |
ERROR_NOT_CHILD_WINDOW |
1443 |
Invalid GW_* command. |
ERROR_INVALID_GW_COMMAND |
1444 |
Invalid thread identifier. |
ERROR_INVALID_THREAD_ID |
1445 |
Cannot process a message from a window that is not a multiple document interface (MDI) window. |
ERROR_NON_MDICHILD_WINDOW |
1446 |
Popup menu already active. |
ERROR_POPUP_ALREADY_ACTIVE |
1447 |
The window does not have scroll bars. |
ERROR_NO_SCROLLBARS |
1448 |
Scroll bar range cannot be greater than 0x7FFF. |
ERROR_INVALID_SCROLLBAR_RANGE |
1449 |
Cannot show or remove the window in the way specified. |
ERROR_INVALID_SHOWWIN_COMMAND |
1450 |
Insufficient system resources exist to complete the requested service. |
ERROR_NO_SYSTEM_RESOURCES |
1451 |
Insufficient system resources exist to complete the requested service. |
ERROR_NONPAGED_SYSTEM_RESOURCES |
1452 |
Insufficient system resources exist to complete the requested service. |
ERROR_PAGED_SYSTEM_RESOURCES |
1453 |
Insufficient quota to complete the requested service. |
ERROR_WORKING_SET_QUOTA |
1454 |
Insufficient quota to complete the requested service. |
ERROR_PAGEFILE_QUOTA |
1455 |
The paging file is too small for this operation to complete. |
ERROR_COMMITMENT_LIMIT |
1456 |
A menu item was not found. |
ERROR_MENU_ITEM_NOT_FOUND |
1457 |
Invalid keyboard layout handle. |
ERROR_INVALID_KEYBOARD_HANDLE |
1458 |
Hook type not allowed. |
ERROR_HOOK_TYPE_NOT_ALLOWED |
1459 |
This operation requires an interactive window station. |
ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION |
1460 |
This operation returned because the time-out period expired. |
ERROR_TIMEOUT |
1500 |
The event log file is corrupted. |
ERROR_EVENTLOG_FILE_CORRUPT |
1501 |
No event log file could be opened, so the event logging service did not start. |
ERROR_EVENTLOG_CANT_START |
1502 |
The event log file is full. |
ERROR_LOG_FILE_FULL |
1503 |
The event log file has changed between read operations. |
ERROR_EVENTLOG_FILE_CHANGED |
1700 |
The string binding is invalid. |
RPC_S_INVALID_STRING_BINDING |
1701 |
The binding handle is not the correct type. |
RPC_S_WRONG_KIND_OF_BINDING |
1702 |
The binding handle is invalid. |
RPC_S_INVALID_BINDING |
1703 |
The RPC protocol sequence is not supported. |
RPC_S_PROTSEQ_NOT_SUPPORTED |
1704 |
The RPC protocol sequence is invalid. |
RPC_S_INVALID_RPC_PROTSEQ |
1705 |
The string universal unique identifier (UUID) is invalid. |
RPC_S_INVALID_STRING_UUID |
1706 |
The endpoint format is invalid. |
RPC_S_INVALID_ENDPOINT_FORMAT |
1707 |
The network address is invalid. |
RPC_S_INVALID_NET_ADDR |
1708 |
No endpoint was found. |
RPC_S_NO_ENDPOINT_FOUND |
1709 |
The time-out value is invalid. |
RPC_S_INVALID_TIMEOUT |
1710 |
The object universal unique identifier (UUID) was not found. |
RPC_S_OBJECT_NOT_FOUND |
1711 |
The object universal unique identifier (UUID) has already been registered. |
RPC_S_ALREADY_REGISTERED |
1712 |
The type universal unique identifier (UUID) has already been registered. |
RPC_S_TYPE_ALREADY_REGISTERED |
1713 |
The RPC server is already listening. |
RPC_S_ALREADY_LISTENING |
1714 |
No protocol sequences have been registered. |
RPC_S_NO_PROTSEQS_REGISTERED |
1715 |
The RPC server is not listening. |
RPC_S_NOT_LISTENING |
1716 |
The manager type is unknown. |
RPC_S_UNKNOWN_MGR_TYPE |
1717 |
The interface is unknown. |
RPC_S_UNKNOWN_IF |
1718 |
There are no bindings. |
RPC_S_NO_BINDINGS |
1719 |
There are no protocol sequences. |
RPC_S_NO_PROTSEQS |
1720 |
The endpoint cannot be created. |
RPC_S_CANT_CREATE_ENDPOINT |
1721 |
Not enough resources are available to complete this operation. |
RPC_S_OUT_OF_RESOURCES |
1722 |
The RPC server is unavailable. |
RPC_S_SERVER_UNAVAILABLE |
1723 |
The RPC server is too busy to complete this operation. |
RPC_S_SERVER_TOO_BUSY |
1724 |
The network options are invalid. |
RPC_S_INVALID_NETWORK_OPTIONS |
1725 |
There is not a remote procedure call active in this thread. |
RPC_S_NO_CALL_ACTIVE |
1726 |
The remote procedure call failed. |
RPC_S_CALL_FAILED |
1727 |
The remote procedure call failed and did not execute. |
RPC_S_CALL_FAILED_DNE |
1728 |
A remote procedure call (RPC) protocol error occurred. |
RPC_S_PROTOCOL_ERROR |
1730 |
The transfer syntax is not supported by the RPC server. |
RPC_S_UNSUPPORTED_TRANS_SYN |
1732 |
The universal unique identifier (UUID) type is not supported. |
RPC_S_UNSUPPORTED_TYPE |
1733 |
The tag is invalid. |
RPC_S_INVALID_TAG |
1734 |
The array bounds are invalid. |
RPC_S_INVALID_BOUND |
1735 |
The binding does not contain an entry name. |
RPC_S_NO_ENTRY_NAME |
1736 |
The name syntax is invalid. |
RPC_S_INVALID_NAME_SYNTAX |
1737 |
The name syntax is not supported. |
RPC_S_UNSUPPORTED_NAME_SYNTAX |
1739 |
No network address is available to use to construct a universal unique identifier (UUID). |
RPC_S_UUID_NO_ADDRESS |
1740 |
The endpoint is a duplicate. |
RPC_S_DUPLICATE_ENDPOINT |
1741 |
The authentication type is unknown. |
RPC_S_UNKNOWN_AUTHN_TYPE |
1742 |
The maximum number of calls is too small. |
RPC_S_MAX_CALLS_TOO_SMALL |
1743 |
The string is too long. |
RPC_S_STRING_TOO_LONG |
1744 |
The RPC protocol sequence was not found. |
RPC_S_PROTSEQ_NOT_FOUND |
1745 |
The procedure number is out of range. |
RPC_S_PROCNUM_OUT_OF_RANGE |
1746 |
The binding does not contain any authentication information. |
RPC_S_BINDING_HAS_NO_AUTH |
1747 |
The authentication service is unknown. |
RPC_S_UNKNOWN_AUTHN_SERVICE |
1748 |
The authentication level is unknown. |
RPC_S_UNKNOWN_AUTHN_LEVEL |
1749 |
The security context is invalid. |
RPC_S_INVALID_AUTH_IDENTITY |
1750 |
The authorization service is unknown. |
RPC_S_UNKNOWN_AUTHZ_SERVICE |
1751 |
The entry is invalid. |
EPT_S_INVALID_ENTRY |
1752 |
The server endpoint cannot perform the operation. |
EPT_S_CANT_PERFORM_OP |
1753 |
There are no more endpoints available from the endpoint mapper. |
EPT_S_NOT_REGISTERED |
1754 |
No interfaces have been exported. |
RPC_S_NOTHING_TO_EXPORT |
1755 |
The entry name is incomplete. |
RPC_S_INCOMPLETE_NAME |
1756 |
The version option is invalid. |
RPC_S_INVALID_VERS_OPTION |
1757 |
There are no more members. |
RPC_S_NO_MORE_MEMBERS |
1758 |
There is nothing to unexport. |
RPC_S_NOT_ALL_OBJS_UNEXPORTED |
1759 |
The interface was not found. |
RPC_S_INTERFACE_NOT_FOUND |
1760 |
The entry already exists. |
RPC_S_ENTRY_ALREADY_EXISTS |
1761 |
The entry is not found. |
RPC_S_ENTRY_NOT_FOUND |
1762 |
The name service is unavailable. |
RPC_S_NAME_SERVICE_UNAVAILABLE |
1763 |
The network address family is invalid. |
RPC_S_INVALID_NAF_ID |
1764 |
The requested operation is not supported. |
RPC_S_CANNOT_SUPPORT |
1765 |
No security context is available to allow impersonation. |
RPC_S_NO_CONTEXT_AVAILABLE |
1766 |
An internal error occurred in a remote procedure call (RPC). |
RPC_S_INTERNAL_ERROR |
1767 |
The RPC server attempted an integer division by zero. |
RPC_S_ZERO_DIVIDE |
1768 |
An addressing error occurred in the RPC server. |
RPC_S_ADDRESS_ERROR |
1769 |
A floating-point operation at the RPC server caused a division by zero. |
RPC_S_FP_DIV_ZERO |
1770 |
A floating-point underflow occurred at the RPC server. |
RPC_S_FP_UNDERFLOW |
1771 |
A floating-point overflow occurred at the RPC server. |
RPC_S_FP_OVERFLOW |
1772 |
The list of RPC servers available for the binding of auto handles has been exhausted. |
RPC_X_NO_MORE_ENTRIES |
1773 |
Unable to open the character translation table file. |
RPC_X_SS_CHAR_TRANS_OPEN_FAIL |
1774 |
The file containing the character translation table has fewer than 512 bytes. |
RPC_X_SS_CHAR_TRANS_SHORT_FILE |
1775 |
A null context handle was passed from the client to the host during a remote procedure call. |
RPC_X_SS_IN_NULL_CONTEXT |
1777 |
The context handle changed during a remote procedure call. |
RPC_X_SS_CONTEXT_DAMAGED |
1778 |
The binding handles passed to a remote procedure call do not match. |
RPC_X_SS_HANDLES_MISMATCH |
1779 |
The stub is unable to get the remote procedure call handle. |
RPC_X_SS_CANNOT_GET_CALL_HANDLE |
1780 |
A null reference pointer was passed to the stub. |
RPC_X_NULL_REF_POINTER |
1781 |
The enumeration value is out of range. |
RPC_X_ENUM_VALUE_OUT_OF_RANGE |
1782 |
The byte count is too small. |
RPC_X_BYTE_COUNT_TOO_SMALL |
1783 |
The stub received bad data. |
RPC_X_BAD_STUB_DATA |
1784 |
The supplied user buffer is not valid for the requested operation. |
ERROR_INVALID_USER_BUFFER |
1785 |
The disk media is not recognized. It may not be formatted. |
ERROR_UNRECOGNIZED_MEDIA |
1786 |
The workstation does not have a trust secret. |
ERROR_NO_TRUST_LSA_SECRET |
1787 |
The SAM database on the Windows NT Server does not have a computer account for this workstation trust relationship. |
ERROR_NO_TRUST_SAM_ACCOUNT |
1788 |
The trust relationship between the primary domain and the trusted domain failed. |
ERROR_TRUSTED_DOMAIN_FAILURE |
1789 |
The trust relationship between this workstation and the primary domain failed. |
ERROR_TRUSTED_RELATIONSHIP_FAILURE |
1790 |
The network logon failed. |
ERROR_TRUST_FAILURE |
1791 |
A remote procedure call is already in progress for this thread. |
RPC_S_CALL_IN_PROGRESS |
1792 |
An attempt was made to logon, but the network logon service was not started. |
ERROR_NETLOGON_NOT_STARTED |
1793 |
The user's account has expired. |
ERROR_ACCOUNT_EXPIRED |
1794 |
The redirector is in use and cannot be unloaded. |
ERROR_REDIRECTOR_HAS_OPEN_HANDLES |
1795 |
The specified printer driver is already installed. |
ERROR_PRINTER_DRIVER_ALREADY_INSTALLED |
1796 |
The specified port is unknown. |
ERROR_UNKNOWN_PORT |
1797 |
The printer driver is unknown. |
ERROR_UNKNOWN_PRINTER_DRIVER |
1798 |
The print processor is unknown. |
ERROR_UNKNOWN_PRINTPROCESSOR |
1799 |
The specified separator file is invalid. |
ERROR_INVALID_SEPARATOR_FILE |
1800 |
The specified priority is invalid. |
ERROR_INVALID_PRIORITY |
1801 |
The printer name is invalid. |
ERROR_INVALID_PRINTER_NAME |
1802 |
The printer already exists. |
ERROR_PRINTER_ALREADY_EXISTS |
1803 |
The printer command is invalid. |
ERROR_INVALID_PRINTER_COMMAND |
1804 |
The specified data type is invalid. |
ERROR_INVALID_DATATYPE |
1805 |
The environment specified is invalid. |
ERROR_INVALID_ENVIRONMENT |
1806 |
There are no more bindings. |
RPC_S_NO_MORE_BINDINGS |
1807 |
The account used is an interdomain trust account. Use your global user account or local user account to access this server. |
ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT |
1808 |
The account used is a computer account. Use your global user account or local user account to access this server. |
ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT |
1809 |
The account used is a server trust account. Use your global user account or local user account to access this server. |
ERROR_NOLOGON_SERVER_TRUST_ACCOUNT |
1810 |
The name or security ID (SID) of the domain specified is inconsistent with the trust information for that domain. |
ERROR_DOMAIN_TRUST_INCONSISTENT |
1811 |
The server is in use and cannot be unloaded. |
ERROR_SERVER_HAS_OPEN_HANDLES |
1812 |
The specified image file did not contain a resource section. |
ERROR_RESOURCE_DATA_NOT_FOUND |
1813 |
The specified resource type cannot be found in the image file. |
ERROR_RESOURCE_TYPE_NOT_FOUND |
1814 |
The specified resource name cannot be found in the image file. |
ERROR_RESOURCE_NAME_NOT_FOUND |
1815 |
The specified resource language ID cannot be found in the image file. |
ERROR_RESOURCE_LANG_NOT_FOUND |
1816 |
Not enough quota is available to process this command. |
ERROR_NOT_ENOUGH_QUOTA |
1817 |
No interfaces have been registered. |
RPC_S_NO_INTERFACES |
1818 |
The server was altered while processing this call. |
RPC_S_CALL_CANCELLED |
1819 |
The binding handle does not contain all required information. |
RPC_S_BINDING_INCOMPLETE |
1820 |
Communications failure. |
RPC_S_COMM_FAILURE |
1821 |
The requested authentication level is not supported. |
RPC_S_UNSUPPORTED_AUTHN_LEVEL |
1822 |
No principal name registered. |
RPC_S_NO_PRINC_NAME |
1823 |
The error specified is not a valid Windows NT RPC error code. |
RPC_S_NOT_RPC_ERROR |
1824 |
A UUID that is valid only on this computer has been allocated. |
RPC_S_UUID_LOCAL_ONLY |
1825 |
A security package specific error occurred. |
RPC_S_SEC_PKG_ERROR |
1826 |
Thread is not canceled. |
RPC_S_NOT_CANCELLED |
1827 |
Invalid operation on the encoding/decoding handle. |
RPC_X_INVALID_ES_ACTION |
1828 |
Incompatible version of the serializing package. |
RPC_X_WRONG_ES_VERSION |
1829 |
Incompatible version of the RPC stub. |
RPC_X_WRONG_STUB_VERSION |
1830 |
The idl pipe object is invalid or corrupted. |
RPC_X_INVALID_PIPE_OBJECT |
1831 |
The operation is invalid for a given idl pipe object. |
RPC_X_INVALID_PIPE_OPERATION |
1832 |
The idl pipe version is not supported. |
RPC_X_WRONG_PIPE_VERSION |
1898 |
The group member was not found. |
RPC_S_GROUP_MEMBER_NOT_FOUND |
1899 |
The endpoint mapper database could not be created. |
EPT_S_CANT_CREATE |
1900 |
The object universal unique identifier (UUID) is the nil UUID. |
RPC_S_INVALID_OBJECT |
1901 |
The specified time is invalid. |
ERROR_INVALID_TIME |
1902 |
The specified form name is invalid. |
ERROR_INVALID_FORM_NAME |
1903 |
The specified form size is invalid. |
ERROR_INVALID_FORM_SIZE |
1904 |
The specified printer handle is already being waited on |
ERROR_ALREADY_WAITING |
1905 |
The specified printer has been deleted. |
ERROR_PRINTER_DELETED |
1906 |
The state of the printer is invalid. |
ERROR_INVALID_PRINTER_STATE |
1907 |
The user must change his password before he logs on the first time. |
ERROR_PASSWORD_MUST_CHANGE |
1908 |
Could not find the domain controller for this domain. |
ERROR_DOMAIN_CONTROLLER_NOT_FOUND |
1909 |
The referenced account is currently locked out and may not be logged on to. |
ERROR_ACCOUNT_LOCKED_OUT |
1910 |
The object exporter specified was not found. |
OR_INVALID_OXID |
1911 |
The object specified was not found. |
OR_INVALID_OID |
1912 |
The object resolver set specified was not found. |
OR_INVALID_SET |
1913 |
Some data remains to be sent in the request buffer. |
RPC_S_SEND_INCOMPLETE |
2000 |
The pixel format is invalid. |
ERROR_INVALID_PIXEL_FORMAT |
2001 |
The specified driver is invalid. |
ERROR_BAD_DRIVER |
2002 |
The window style or class attribute is invalid for this operation. |
ERROR_INVALID_WINDOW_STYLE |
2003 |
The requested metafile operation is not supported. |
ERROR_METAFILE_NOT_SUPPORTED |
2004 |
The requested transformation operation is not supported. |
ERROR_TRANSFORM_NOT_SUPPORTED |
2005 |
The requested clipping operation is not supported. |
ERROR_CLIPPING_NOT_SUPPORTED |
2202 |
The specified user name is invalid. |
ERROR_BAD_USERNAME |
2250 |
This network connection does not exist. |
ERROR_NOT_CONNECTED |
2401 |
This network connection has files open or requests pending. |
ERROR_OPEN_FILES |
2402 |
Active connections still exist. |
ERROR_ACTIVE_CONNECTIONS |
2404 |
The device is in use by an active process and cannot be disconnected. |
ERROR_DEVICE_IN_USE |
3000 |
The specified print monitor is unknown. |
ERROR_UNKNOWN_PRINT_MONITOR |
3001 |
The specified printer driver is currently in use. |
ERROR_PRINTER_DRIVER_IN_USE |
3002 |
The spool file was not found. |
ERROR_SPOOL_FILE_NOT_FOUND |
3003 |
A StartDocPrinter call was not issued. |
ERROR_SPL_NO_STARTDOC |
3004 |
An AddJob call was not issued. |
ERROR_SPL_NO_ADDJOB |
3005 |
The specified print processor has already been installed. |
ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED |
3006 |
The specified print monitor has already been installed. |
ERROR_PRINT_MONITOR_ALREADY_INSTALLED |
3007 |
The specified print monitor does not have the required functions. |
ERROR_INVALID_PRINT_MONITOR |
3008 |
The specified print monitor is currently in use. |
ERROR_PRINT_MONITOR_IN_USE |
3009 |
The requested operation is not allowed when there are jobs queued to the printer. |
ERROR_PRINTER_HAS_JOBS_QUEUED |
3010 |
The requested operation is successful. Changes will not be effective until the system is rebooted. |
ERROR_SUCCESS_REBOOT_REQUIRED |
3011 |
The requested operation is successful. Changes will not be effective until the service is restarted. |
ERROR_SUCCESS_RESTART_REQUIRED |
4000 |
WINS encountered an error while processing the command. |
ERROR_WINS_INTERNAL |
4001 |
The local WINS can not be deleted. |
ERROR_CAN_NOT_DEL_LOCAL_WINS |
4002 |
The importation from the file failed. |
ERROR_STATIC_INIT |
4003 |
The backup failed. Was a full backup done before? |
ERROR_INC_BACKUP |
4004 |
The backup failed. Check the directory to which you are backing the database. |
ERROR_FULL_BACKUP |
4005 |
The name does not exist in the WINS database. |
ERROR_REC_NON_EXISTENT |
4006 |
Replication with a nonconfigured partner is not allowed. |
ERROR_RPL_NOT_ALLOWED |
6118 |
The list of servers for this workgroup is not currently available |
ERROR_NO_BROWSER_SERVERS_FOUND |
THE INFORMATION PROVIDED IN THE SOFTWARE TECHNOLOGY, INC. KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. SOFTWARE TECHNOLOGY, INC. DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SOFTWARE TECHNOLOGY, INC. OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF SOFTWARE TECHNOLOGY, INC. OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.
1999-2005 Software Technology, Inc. All rights reserved. Terms of Use
The maker of Tabs3 and PracticeMaster
e-Mail Suggestions for the Knowledge Base to: kb@Tabs3.com
Technical Support via e-mail is not available.
Knowledge Base: http://support.Tabs3.com
Web Site: http://www.Tabs3.com
关于FLOAT类型结尾的零的问题
在C++中,将float类型的数字转化为字符串一般用sprintf或strcpy等等的方法,这其中涉及到FLOAT类型的格式化问题。
一般情况下,使用%f可以把FLOAT类型转化为字符串,不过这个转化是有缺点的,缺点就是这个数字是有固定的默认小数位数的,这个默认位数是6,即当你把FLOAT型数字1用%f格式化时,得到的字符串是1.000000。一定让人很不舒服。一般的解决方法是使用位数限定标记。比如当你想保留两位小数时使用%.2f,那麽得到的字符串就是1.00。是不是稍微好一点?但是仍然让人不满意,难道就没有方法能够去除小数结尾多余的零吗?
有的。方法就是%g,使用这种格式化方法可以得到人们所熟悉的小数,不必再为多余的小数后的零操心了。而且%g的格式化方法和%f有相同之处,即在%f上使用的格式限定标记,在%g上同样适用。比如我们用%.4f来格式化小数1.01000,得到的字符串是1.0100,同样,使用%.4g来格式化1.01000得到的字符串是1.01。是不是很方便呢?
Effective STL条款31 理解你的排序操作
作者: winter
排序一直是数据结构中的常用算法,STL提供的排序算法非常丰富,如何有效使用就值得探讨。在网上没有找到条款31的翻译,于是我自己翻译了。--Winter
如何进行排序?让我数数有几种方法。
一旦程序员需对容器元素进行排序,sort算法马上就会出现在他的脑海(可能有些程序员会想到qsort,但详细阅读条款46后,他们会放弃使用qsort的想法,转而使用sort算法)。
sort是一个非常优秀的算法,但并当你并不真正需要它的时候,其实就是一种浪费。有时你并不需要一个完整的排序(简称为全排序)。例如,你现有一个包含Widget对象(Widget意为“小挂件”)的vector容器,并且你想把其中质量最好的20个Widget送给你最好的客户,那么你需做的只是找出这20个质量最好的Widget元素,剩下的并不需关心它们的顺序。这时你需要的是部分排序(相对于全排序),恰好在算法中就有一个名副其实的部分排序函数函数:partial_sort:
bool qualityCompare(const Widget& lhs, const Widget& rhs)
{
// lhs的质量不比rhs的质量差时返回true,否则返回false
}
…
partial_sort (widgets.begin(), // 把质量最好的20元素
widgets.begin() + 20, // 顺序放入widgets容器中
widgets.end(),
qualityCompare);
… // 使用 widgets...
通过调用partial_sort,容器中开始的20个元素就是你需要的质量最好的20个Widget,并按顺序排列,质量排第一的就是widgets[0], 紧接着就是widgets[1],依次类推。这样你就可以把质量第一的Widget送给你最好的顾客,质量第二的Widget就可以送给下一个顾客,很方便吧,更方便的还在后面呢。
如果你只是想把这20个质量最好的Widget礼物送给你最好的20位顾客,而不需要给他们一一对应,partial_sort在这里就有些显得大材小用了。因为在这里,你只需要找出这20个元素,而不需要对它们本身进行排序。这时你需要的不是partial_sort,而是nth_element。
nth_element排序算法只是对一个区间进行排序,一直到你指定的第n个位置上放上正确的元素为止,也就是说,和你进行全排序和nth_element排序相比,其共同点就是第n个位置是同一个元素。当nth_element函数运行后,在全排序中应该在位置n之后的元素不会出现在n的前面,应该在位置n前面的元素也不会出现在n的后面。听起来有些费解,主要是我不得不谨慎地使用我的语言来描述nth_element的功能。别着急,呆会儿我会给你解释为什么,现在先让我们来看看nth_element是如何让质量最好的20个widget礼物排在vector容器的前面的:
nth_element (widgets.begin(), // 把质量最好的20元素放在
widgets.begin() + 20, // widgets容器的前面,
widgets.end(), // 但并不关心这20个元素
qualityCompare); //本身内部的顺序
你可以看出,调用nth_element函数和调用partial_sort函数在本质上没有区别,唯一的不同在于partial_sort把前20个元素还进行排列了,而nth_element并不关系他们内部的顺序。两个算法都实现了同样的功能:把质量最好的20个元素放在vector容器的开始部分。
这样引起了一个重要的问题:要是质量一样的元素,排序算法将会如何处理呢?假设有12个元素的质量都为1(最好等级),15个元素的质量等级为2(质量次之),如果要选择20个最好的Widget,则先选12个质量为1的元素,然后从15个中选出8个质量为2的元素。到底nth_element和partial_sort如何从15个中选出8个,依据何在?换句话说,当多个元素有同样的比较值时,排序算法如何决定谁先谁后?
对于partial_sort和nth_element算法来说,你无法控制它们,对于比较值相同的元素它们想怎么排就怎么排(查看条款19,了解两个值相等的定义)。在我们上面的例子中,面对需要从15个等级为2的元素选出8个增加到top 20中去,他们会任意选取。这样做也有它的道理:如果你要求得到20个质量最好的Widget,同时有些Widget的质量又一样,当你得到20个元素至少不比剩下的那些质量差,这已经达到你的要求,你就不能抱怨什么了。
假如对于全排序,你倒是可以得到更多的控制权。一些排序算法是“稳定的”(stable),在一个“稳定”的排序算法中,如果两个元素有相同的值,它们的相对位置在排序后也会保持不变。例如:如果在未排序时Widget A在Widget B之前,而且都有相同的质量等级,那么“稳定”排序算法就可以保证在排序之后,Widget A仍然在Widget B之前。而非“稳定”排序算法就不能保证这一点。
partial_sort和nth_element都不是“稳定”排序算法,真正的“稳定”排序算法是stable_sort,从名字上看就知道它是“稳定”的。如果你在排序的时候需要保证相同元素的相对位置,你最好使用stable_sort,在STL中没有为partial_sort和nth_element算法提供对应的“稳定”版本。
说到nth_element,名字确实很怪,但是功能却是不少,除了让你找到无关顺序的top n个元素外,它还能找到某个范围的中值,或者找到在某个特定百分点的值。
vector<Widget>::iterator begin(widgets.begin()); // widgets的第一个
vector<Widget>::iterator end(widgets.end()); //和最后一个迭代器
//
vector<Widget>::iterator goalPosition; // 需要定位的那个迭代器
//以下代码用来得到质量排在中间的那个元素的迭代器
goalPosition = begin + widgets.size() / 2; // 要找的那个元素应该
//在vector的中部。
nth_element(begin, goalPosition, end, // 找到容器widgets元素的中值
qualityCompare); //
… // goalPosition现在指向中值元素
//以下代码用来得到质量排在75%的元素
vector<Widget>::size_type goalOffset = // 计算出要找的值
0.25 * widgets.size(); //离begin迭代器的距离。
//
nth_element( begin, begin + goalOffset, end, // 得到质量排在75%的元素
qualityCompare); //
… // goalPosition 现在指向质量排在75%的元素。
当你需要把一个集合由无序变成有序时,可选用sort, stable_sort或partial_sort,当你只需得到top n或者在某个特定位置的元素,你就可以使用nth_element。或许有时你的需求比nth_element提供的还要少,例如:你并不需要得到质量最好的前20个Widget,而只需要识别那些质量等级为1或者等级为2的Widget。当然,你可以对整个vector按照Widget的质量等级进行全排序,然后查找到第一个质量等级低于等级2的元素。
问题就在于全排序太耗费资源,而且大部分工作都是无用功。这种情况下最好选择partition算法,partition只是给你确定一个区间,把符合特定条件的元素放到这个区间中。举例来说,要把质量等级好于等于等级2的Widget的元素放在widget容器的前端,我们可以定义一个用于识别Widget质量等级的函数:
bool hasAcceptableQuality(const Widget& w)
{
//如果w的质量等于或好于2,返回true,否则返回false
}
然后把这个判断函数传递给partion算法:
vector<Widget>::iterator goodEnd = // 把所有满足hasAcceptableQuality
partition(widgets.begin(), // 条件的放在widgets容器的前面,
widgets.end(), // 返回第一个不满足条件的
hasAcceptableQuality); //元素的位置
这样一来,所有在迭代器widgets.begin()和迭代器goodEnd之间的元素都是满足需求的元素:其质量等级好于或等于2。而在 goodEnd 到 widgets.end() 之间的元素的质量等级都会低于质量等级2。如果你对质量等级相同元素的相对位置很关心的话,你可以选择stable_partition算法来代替partition。
需要注意的是sort, stable_sort, partial_sort, 和nth_element算法都需要以随机迭代器(random access
iterators)为参数,因此这些算法能只能用于vector, string, deque, 和array等容器,对于标准的关联容器map、set、multmap、multset等,这些算法就有必要用了,这些容器本身的比较函数使得容器内所有元素一直都是有序排列的。对于容器list,看似可以用这些排序算法,其实也是不可用的(其iterator的类型并不是随机迭代器),不过在需要的时候可以使用list自带的排序函数sort(有趣的是list::sort函数和一个“稳定”排序函数的效果一样)。如果你想对一个list容器使用partial_sort或nth_element,你只能间接使用。一个可选的方法是把list中的元素拷贝到带有随机迭代器的容器中,然后再使用这些算法;另一种是生成一个包含list::iterator的容器,直接对容器内的list::iterator进行排序,然后通过list::iterator得到所指的元素;第三种方法,借助一个包含iterator的有序容器,然后反复把list中的元素连接到你想要链接的位置。看见了吧,你可以选择的方式还是比较多的。
partition 和stable_partition函数与sort、stable_sort、partial_sort、nth_element不一样,要求没有那么严格,输入参数只需是双向迭代器(bidirectional iterator)。因此你可以对所有的标准序列容器使用partition和stable_partition算法。
让我们来总结一下你的排序操作:
-
若需对vector, string, deque, 或 array容器进行全排序,你可选择sort或stable_sort;
-
若只需对vector, string, deque, 或 array容器中取得top n的元素,部分排序partial_sort是首选.
-
若对于vector, string, deque, 或array容器,你需要找到第n个位置的元素或者你需要得到top n且不关系top n中的内部顺序,nth_element是最理想的;
-
若你需要从标准序列容器或者array中把满足某个条件或者不满足某个条件的元素分开,你最好使用partition或stable_partition;
-
若使用的list容器,你可以直接使用partition和stable_partition算法,你可以使用list::sort代替sort和stable_sort排序。若你需要得到partial_sort或nth_element的排序效果,你必须间接使用。正如上面介绍的有几种方式可以选择。
另外,你可以使用标准的关联容器来保证容器中所有元素在操作过程中一直有序。你还可考虑非标准的STL容器priority_queue,它同样可以保证其元素在所有操作过程中一直有序(priority_queue在传统意义上属于STL的一部分,但根据“STL”定义,需要STL容器支持迭代器,而priority_queue并不支持迭代器,故不能能称为标准STL容器)。
这时你或许会问:“性能如何?”非常好的问题。广义的讲,算法做的工作越多,花的时间越长,“稳定”性排序算法比“非稳定”性排序算法要耗时。我们可以按照其消耗的资源(时间和空间)的多少,对本文中讨论的排序算法作个排序,消耗资源越少的排在前面:
-
1. partition
-
2. stable_partition
-
3. nth_element
-
4. partial_sort
-
5. sort
-
6. stable_sort
选择这些算法的依据是你的需求而不是它们的性能。若你能选择一个算法恰好满足你的需求(如用部分排序代替全排序),不仅会清晰表达你的意图,而且能高效的使用STL。
详细解说 STL 排序(Sort)
作者Winter0 前言: STL,为什么你必须掌握
对于程序员来说,数据结构是必修的一门课。从查找到排序,从链表到二叉树,几乎所有的算法和原理都需要理解,理解不了也要死记硬背下来。幸运的是这些理论都已经比较成熟,算法也基本固定下来,不需要你再去花费心思去考虑其算法原理,也不用再去验证其准确性。不过,等你开始应用计算机语言来工作的时候,你会发现,面对不同的需求你需要一次又一次去用代码重复实现这些已经成熟的算法,而且会一次又一次陷入一些由于自己疏忽而产生的bug中。这时,你想找一种工具,已经帮你实现这些功能,你想怎么用就怎么用,同时不影响性能。你需要的就是STL, 标准模板库!
西方有句谚语:不要重复发明轮子!
STL几乎封装了所有的数据结构中的算法,从链表到队列,从向量到堆栈,对hash到二叉树,从搜索到排序,从增加到删除......可以说,如果你理解了STL,你会发现你已不用拘泥于算法本身,从而站在巨人的肩膀上去考虑更高级的应用。
排序是最广泛的算法之一,本文详细介绍了STL中不同排序算法的用法和区别。
1 STL提供的Sort 算法
C++之所以得到这么多人的喜欢,是因为它既具有面向对象的概念,又保持了C语言高效的特点。STL 排序算法同样需要保持高效。因此,对于不同的需求,STL提供的不同的函数,不同的函数,实现的算法又不尽相同。
1.1 所有sort算法介绍
所有的sort算法的参数都需要输入一个范围,[begin, end)。这里使用的迭代器(iterator)都需是随机迭代器(RadomAccessIterator), 也就是说可以随机访问的迭代器,如:it+n什么的。(partition 和stable_partition 除外)如果你需要自己定义比较函数,你可以把你定义好的仿函数(functor)作为参数传入。每种算法都支持传入比较函数。以下是所有STL sort算法函数的名字列表:
函数名 | 功能描述 |
---|---|
sort | 对给定区间所有元素进行排序 |
stable_sort | 对给定区间所有元素进行稳定排序 |
partial_sort | 对给定区间所有元素部分排序 |
partial_sort_copy | 对给定区间复制并排序 |
nth_element | 找出给定区间的某个位置对应的元素 |
is_sorted | 判断一个区间是否已经排好序 |
partition | 使得符合某个条件的元素放在前面 |
stable_partition | 相对稳定的使得符合某个条件的元素放在前面 |
1.2 sort 中的比较函数
当你需要按照某种特定方式进行排序时,你需要给sort指定比较函数,否则程序会自动提供给你一个比较函数。vector < int > vect; //... sort(vect.begin(), vect.end()); //此时相当于调用 sort(vect.begin(), vect.end(), less<int>() );
名称 | 功能描述 |
---|---|
equal_to | 相等 |
not_equal_to | 不相等 |
less | 小于 |
greater | 大于 |
less_equal | 小于等于 |
greater_equal | 大于等于 |
less<int>() greater<int>()当你的容器中元素时一些标准类型(int float char)或者string时,你可以直接使用这些函数模板。但如果你时自己定义的类型或者你需要按照其他方式排序,你可以有两种方法来达到效果:一种是自己写比较函数。另一种是重载类型的'<'操作赋。
#include <iostream> #include <algorithm> #include <functional> #include <vector> using namespace std; class myclass { public: myclass(int a, int b):first(a), second(b){} int first; int second; bool operator < (const myclass &m)const { return first < m.first; } }; bool less_second(const myclass & m1, const myclass & m2) { return m1.second < m2.second; } int main() { vector< myclass > vect; for(int i = 0 ; i < 10 ; i ++){ myclass my(10-i, i*3); vect.push_back(my); } for(int i = 0 ; i < vect.size(); i ++) cout<<"("<<vect[i].first<<","<<vect[i].second<<")"n"; sort(vect.begin(), vect.end()); cout<<"after sorted by first:"<<endl; for(int i = 0 ; i < vect.size(); i ++) cout<<"("<<vect[i].first<<","<<vect[i].second<<")"n"; cout<<"after sorted by second:"<<endl; sort(vect.begin(), vect.end(), less_second); for(int i = 0 ; i < vect.size(); i ++) cout<<"("<<vect[i].first<<","<<vect[i].second<<")"n"; return 0 ; }
(10,0) (9,3) (8,6) (7,9) (6,12) (5,15) (4,18) (3,21) (2,24) (1,27) after sorted by first: (1,27) (2,24) (3,21) (4,18) (5,15) (6,12) (7,9) (8,6) (9,3) (10,0) after sorted by second: (10,0) (9,3) (8,6) (7,9) (6,12) (5,15) (4,18) (3,21) (2,24) (1,27)
1.3 sort 的稳定性
你发现有sort和stable_sort,还有 partition 和stable_partition, 感到奇怪吧。其中的区别是,带有stable的函数可保证相等元素的原本相对次序在排序后保持不变。或许你会问,既然相等,你还管他相对位置呢,也分不清楚谁是谁了?这里需要弄清楚一个问题,这里的相等,是指你提供的函数表示两个元素相等,并不一定是一摸一样的元素。例如,如果你写一个比较函数:
bool less_len(const string &str1, const string &str2) { return str1.length() < str2.length(); }
1.4 全排序
全排序即把所给定范围所有的元素按照大小关系顺序排列。用于全排序的函数有
template <class RandomAccessIterator> void sort(RandomAccessIterator first, RandomAccessIterator last); template <class RandomAccessIterator, class StrictWeakOrdering> void sort(RandomAccessIterator first, RandomAccessIterator last, StrictWeakOrdering comp); template <class RandomAccessIterator> void stable_sort(RandomAccessIterator first, RandomAccessIterator last); template <class RandomAccessIterator, class StrictWeakOrdering> void stable_sort(RandomAccessIterator first, RandomAccessIterator last, StrictWeakOrdering comp);
班上有10个学生,我想知道他们的成绩排名。
#include <iostream> #include <algorithm> #include <functional> #include <vector> #include <string> using namespace std; class student{ public: student(const string &a, int b):name(a), score(b){} string name; int score; bool operator < (const student &m)const { return score< m.score; } }; int main() { vector< student> vect; student st1("Tom", 74); vect.push_back(st1); st1.name="Jimy"; st1.score=56; vect.push_back(st1); st1.name="Mary"; st1.score=92; vect.push_back(st1); st1.name="Jessy"; st1.score=85; vect.push_back(st1); st1.name="Jone"; st1.score=56; vect.push_back(st1); st1.name="Bush"; st1.score=52; vect.push_back(st1); st1.name="Winter"; st1.score=77; vect.push_back(st1); st1.name="Andyer"; st1.score=63; vect.push_back(st1); st1.name="Lily"; st1.score=76; vect.push_back(st1); st1.name="Maryia"; st1.score=89; vect.push_back(st1); cout<<"------before sort..."<<endl; for(int i = 0 ; i < vect.size(); i ++) cout<<vect[i].name<<":"t"<<vect[i].score<<endl; stable_sort(vect.begin(), vect.end(),less<student>()); cout <<"-----after sort ...."<<endl; for(int i = 0 ; i < vect.size(); i ++) cout<<vect[i].name<<":"t"<<vect[i].score<<endl; return 0 ; }
------before sort... Tom: 74 Jimy: 56 Mary: 92 Jessy: 85 Jone: 56 Bush: 52 Winter: 77 Andyer: 63 Lily: 76 Maryia: 89 -----after sort .... Bush: 52 Jimy: 56 Jone: 56 Andyer: 63 Tom: 74 Lily: 76 Winter: 77 Jessy: 85 Maryia: 89 Mary: 92sort采用的是成熟的"快速排序算法"(目前大部分STL版本已经不是采用简单的快速排序,而是结合内插排序算法)。注1,可以保证很好的平均性能、复杂度为n*log(n),由于单纯的快速排序在理论上有最差的情况,性能很低,其算法复杂度为n*n,但目前大部分的STL版本都已经在这方面做了优化,因此你可以放心使用。stable_sort采用的是"归并排序",分派足够内存是,其算法复杂度为n*log(n), 否则其复杂度为n*log(n)*log(n),其优点是会保持相等元素之间的相对位置在排序前后保持一致。
1.5 局部排序
局部排序其实是为了减少不必要的操作而提供的排序方式。其函数原型为:template <class RandomAccessIterator> void partial_sort(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last); template <class RandomAccessIterator, class StrictWeakOrdering> void partial_sort(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last, StrictWeakOrdering comp); template <class InputIterator, class RandomAccessIterator> RandomAccessIterator partial_sort_copy(InputIterator first, InputIterator last, RandomAccessIterator result_first, RandomAccessIterator result_last); template <class InputIterator, class RandomAccessIterator, class StrictWeakOrdering> RandomAccessIterator partial_sort_copy(InputIterator first, InputIterator last, RandomAccessIterator result_first, RandomAccessIterator result_last, Compare comp);
stable_sort(vect.begin(), vect.end(),less<student>()); 替换为: partial_sort(vect.begin(), vect.begin()+5, vect.end(),less<student>());
------before sort... Tom: 74 Jimy: 56 Mary: 92 Jessy: 85 Jone: 56 Bush: 52 Winter: 77 Andyer: 63 Lily: 76 Maryia: 89 -----after sort .... Bush: 52 Jimy: 56 Jone: 56 Andyer: 63 Tom: 74 Mary: 92 Jessy: 85 Winter: 77 Lily: 76 Maryia: 89这样的好处知道了吗?当数据量小的时候可能看不出优势,如果是100万学生,我想找分数最少的5个人......
partial_sort采用的堆排序(heapsort),它在任何情况下的复杂度都是n*log(n). 如果你希望用partial_sort来实现全排序,你只要让middle=last就可以了。
partial_sort_copy其实是copy和partial_sort的组合。被排序(被复制)的数量是[first, last)和[result_first, result_last)中区间较小的那个。如果[result_first, result_last)区间大于[first, last)区间,那么partial_sort相当于copy和sort的组合。
1.6 nth_element 指定元素排序
nth_element一个容易看懂但解释比较麻烦的排序。用例子说会更方便:班上有10个学生,我想知道分数排在倒数第4名的学生。
如果要满足上述需求,可以用sort排好序,然后取第4位(因为是由小到大排), 更聪明的朋友会用partial_sort, 只排前4位,然后得到第4位。其实这是你还是浪费,因为前两位你根本没有必要排序,此时,你就需要nth_element:
template <class RandomAccessIterator> void nth_element(RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last); template <class RandomAccessIterator, class StrictWeakOrdering> void nth_element(RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last, StrictWeakOrdering comp);
stable_sort(vect.begin(), vect.end(),less<student>()); 替换为: nth_element(vect.begin(), vect.begin()+3, vect.end(),less<student>());
------before sort... Tom: 74 Jimy: 56 Mary: 92 Jessy: 85 Jone: 56 Bush: 52 Winter: 77 Andyer: 63 Lily: 76 Maryia: 89 -----after sort .... Jone: 56 Bush: 52 Jimy: 56 Andyer: 63 Jessy: 85 Mary: 92 Winter: 77 Tom: 74 Lily: 76 Maryia: 89第四个是谁?Andyer,这个倒霉的家伙。为什么是begin()+3而不是+4? 我开始写这篇文章的时候也没有在意,后来在ilovevc 的提醒下,发现了这个问题。begin()是第一个,begin()+1是第二个,... begin()+3当然就是第四个了。
1.7 partition 和stable_partition
好像这两个函数并不是用来排序的,'分类'算法,会更加贴切一些。partition就是把一个区间中的元素按照某个条件分成两类。其函数原型为:template <class ForwardIterator, class Predicate> ForwardIterator partition(ForwardIterator first, ForwardIterator last, Predicate pred) template <class ForwardIterator, class Predicate> ForwardIterator stable_partition(ForwardIterator first, ForwardIterator last, Predicate pred);
stable_sort(vect.begin(), vect.end(),less<student>()); 替换为: student exam("pass", 60); stable_partition(vect.begin(), vect.end(), bind2nd(less<student>(), exam));
------before sort... Tom: 74 Jimy: 56 Mary: 92 Jessy: 85 Jone: 56 Bush: 52 Winter: 77 Andyer: 63 Lily: 76 Maryia: 89 -----after sort .... Jimy: 56 Jone: 56 Bush: 52 Tom: 74 Mary: 92 Jessy: 85 Winter: 77 Andyer: 63 Lily: 76 Maryia: 89看见了吗,Jimy,Jone, Bush(难怪说美国总统比较笨 )都没有及格。而且使用的是stable_partition, 元素之间的相对次序是没有变.
2 Sort 和容器
STL中标准容器主要vector, list, deque, string, set, multiset, map, multimay, 其中set, multiset, map, multimap都是以树结构的方式存储其元素详细内容请参看:学习STL map, STL set之数据结构基础. 因此在这些容器中,元素一直是有序的。
这些容器的迭代器类型并不是随机型迭代器,因此,上述的那些排序函数,对于这些容器是不可用的。上述sort函数对于下列容器是可用的:
- vector
- string
- deque
对于list容器,list自带一个sort成员函数list::sort(). 它和算法函数中的sort差不多,但是list::sort是基于指针的方式排序,也就是说,所有的数据移动和比较都是此用指针的方式实现,因此排序后的迭代器一直保持有效(vector中sort后的迭代器会失效).
3 选择合适的排序函数
为什么要选择合适的排序函数?可能你并不关心效率(这里的效率指的是程序运行时间), 或者说你的数据量很小, 因此你觉得随便用哪个函数都无关紧要。
其实不然,即使你不关心效率,如果你选择合适的排序函数,你会让你的代码更容易让人明白,你会让你的代码更有扩充性,逐渐养成一个良好的习惯,很重要吧 。
如果你以前有用过C语言中的qsort, 想知道qsort和他们的比较,那我告诉你,qsort和sort是一样的,因为他们采用的都是快速排序。从效率上看,以下几种sort算法的是一个排序,效率由高到低(耗时由小变大):
- partion
- stable_partition
- nth_element
- partial_sort
- sort
- stable_sort
- 若需对vector, string, deque, 或 array容器进行全排序,你可选择sort或stable_sort;
- 若只需对vector, string, deque, 或 array容器中取得top n的元素,部分排序partial_sort是首选.
- 若对于vector, string, deque, 或array容器,你需要找到第n个位置的元素或者你需要得到top n且不关系top n中的内部顺序,nth_element是最理想的;
- 若你需要从标准序列容器或者array中把满足某个条件或者不满足某个条件的元素分开,你最好使用partition或stable_partition;
- 若使用的list容器,你可以直接使用partition和stable_partition算法,你可以使用list::sort代替sort和stable_sort排序。若你需要得到partial_sort或nth_element的排序效果,你必须间接使用。正如上面介绍的有几种方式可以选择。
4 小结
讨论技术就像个无底洞,经常容易由一点可以引申另外无数个技术点。因此需要从全局的角度来观察问题,就像观察STL中的sort算法一样。其实在STL还有make_heap, sort_heap等排序算法。本文章没有提到。本文以实例的方式,解释了STL中排序算法的特性,并总结了在实际情况下应如何选择合适的算法。