jiangsheng

http://www.csdn.net/develop/author/netauthor/jiangsheng/
随笔 - 142, 评论 - 629, 引用 - 27

导航

关于

 
这下要维护3个BLOG了,faint 其他的地址:

所有的文章版权归原文作者所有,任何人需要转载文章,必须征得原文作者授权。
我的MVP配置

标签

每月存档

最新留言

广告

BUG,规范,断言和调试

好长时间没更新BLOG了,向大家拜个晚年先。最近没怎么写代码,转几篇在网易虚拟社区发的文章过来充数。

对于BUG的自信

Donald E. Knuth(高德纳)在TeX: The Program的前言中说:
"我相信,在1985年11月27日,TeX代码里面的最后一个BUG已经被发现和解决了。但是,如果代码中仍旧有BUG,我很高兴付给任何第一个发现BUG的人20.48美元(这是前一个金额的两倍,而且我计划在一年内把它翻倍。你看,我很自信!)" 

想知道后来发生了什么吗?

http://truetex.com/knuthchk.htm可以看到他写出去的支票的金额是从2.56美元开始翻倍的。微基百科中关于这种支票的文章(http://en.wikipedia.org/wiki/Knuth_reward_check)说,截至2001年10月为止,他写出去了超过两千张这样的支票,但是他的BUG支票是如此有名,以至于很多人把他的支票收藏起来而不是拿出去兑现(http://www.tug.org/whatis.html)。

有多少程序员在发布产品的时候可以这样自信地声明产品没有问题?

遗憾的是,现在的程序员经常把发现BUG的责任推给测试人员——“不用担心,测试人员会发现所有BUG的,这是他们的工作”。实际上,测试人员并没有开发人员的条件,他们不可能进行源代码级别的调试,很大程度上只能靠运气——没错,是靠运气,如果一个BUG很容易被发现,程序员不太可能自己没有发现它——来发现BUG。

还有一些人干脆就认为BUG是不可避免的,或者认为不值得这么精益求精(参见网易虚拟社区http://p5.club.163.com/viewArticleByWWW.m?boardId=clanguage&articleId=clanguage_108eacc622169e7&boardOffset=0的讨论),但是实际上防止BUG出现的最好的时机,就是在编写代码的时候。在编写代码一段时间之后,即使是编写者本人也可能需要一段时间来理解代码(如果不习惯写注释的话,这段时间会更长),更别说定位问题所在了。在编写代码时,如果具有良好的习惯,可以免去很多在之后消灭BUG的困难。

规范不是语法

太多人把不要使用goto奉为圣旨,从来不想去打破。他们会争论,goto会造成难以维护的难读的代码,以及使编译器无法进行优化。这两点在很大程度上是真的,但是也有使用goto可以增加程序可读性和效率时候。在这种情况下,遵循“不使用goto语句”规范会产生更糟糕的代码。

一些人喜欢在成员函数后面加const,但是另外一些人没有养成这个习惯。一个直接的结果就是,一些看起来对对象完全没有影响的函数不能在const函数里面使用。这时候应该怎么办?看看Paul DiLascia建议的,把this指针强行转化为一个非const指针(http://www.microsoft.com/msj/archive/S126E.aspx)。如果函数实际上会对对象成员造成影响(例如CToolBar::GetItemRect),这也会带来潜在危险。

为了和ANSI标准之前编写的代码兼容,ANSI C中的memchr函数的声明为

void *memchr( const void *buf, int c, size_t count );


这里c是一个字符。很明显,标准为了兼容性放弃了明确性和更强的类型检查。如果放弃兼容性,这个函数应该声明为如下形式

void *memchr( const void *buf, unsigned char c, size_t count );



微软的很多代码使用一种叫做匈牙利表示法的命名规范。这使得标识符的含义和类型更加明确——但是这是从广义的角度来说的。考虑如下函数声明

char *strcpy( char *strDestination, const char *strSource );



如果严格遵循原始的匈牙利表示法,那么两个参数的声明应该是pch开头。但是以str开头给这两个参数更多含义:它们指向以\0为结束符的字符串。
   
规范是用来在大部分时间里遵循,以及在可以得到更好的结果时打破的。

编译警告的意义

智能化的编译器开始将语法正确的语句列为警告:

while(size-->0);//注意这里有个分号 *pTo++=*pFrom++;


编译器会报告空循环问题。
但是对于以0结尾的字符串复制

while(*pTo++=*pFrom++);


,这样的警告是多余的。
更加常见的警告是在条件判断语句中

if(ch='\0') EndOfString();


为了绕过这个警告,需要添加额外的运算或者语句,或者更正错误的赋值。

while((*pTo++=*pFrom++)!='\0'){} if(ch=='\0')


一些程序员甚至将比较语句修改成

if('\0'==ch)


这样作的原因显而易见:为了减少潜在的BUG。如果你的编译器没有这样的警告,那么你可以使用一些工具来检查那些语法正确但是有潜在BUG的代码。LintProject (http://www.codeproject.com/tools/lintproject.asp)就是其中一个。但是,良好的编程习惯还是减少BUG出现的最好的方法。

在觉得警告消息太烦人的时候,不妨想想编译器的开发人员为什么要编写这么多警告消息,而不是仅仅寻求关闭警告的方法。

P.S. Visual C++的默认警告等级是3级。发布软件之前应该改成4级,之后检查所有的编译警告。

无处不在的断言

使用编译器来捕获BUG的主意很好,Visual Studio 2005甚至会报告定义的变量不符合命名规范(Warning 1 CA1709 : Microsoft.Naming : Correct the casing of type name 'welcome'.);但是我敢打赌你检查BUG列表的时候,你会发现只有一小部分BUG会被编译器抓到。很多BUG在程序运行过程中很少会出现,例如内存分配失败的问题

char* strBuffer=new char [length]; MyZeroMemory(strBuffer,length);



这段代码在绝大多数情况下会成功,但是在虚拟内存不足的时候,Windows会报告“您的系统虚拟内存太低,WINDOWS会增加虚拟内存页面文件的大小。在这个过程中,一些应用程序的内存请求会被拒绝”然后开始增加虚拟内存,在这个过程中,new这样的内存分配可能会因为内存不足而失败,而MyZeroMemory则可能会造成访问越界。如果你足够幸运,你会在产品发布之前发现这个BUG,否则,你的用户会代替你发现这个BUG。要是用户刚好没有备份的习惯,丢失了几十分钟甚至是几小时的工作进度,用户会很生气,后果很严重。

编译器不能捕获这种运行时才会出现的错误(顺便说一下,我在CSDN上居然还看到有人抱怨编译器不会报告除0错误);也不能捕获算法中的BUG和检验参数中的数据。但要是你知道怎么做的话,这类问题很容易被发现。你可以用SetProcessWorkingSetSize函数或者msconfig工具减少虚拟内存大小,或者使用Virtual PC之类的虚拟机或者磁盘配额策略来模拟内存和磁盘空间不足的情况。

你有可能想在这种极限情况下调试你的代码,但是大多数时间内,内存分配不会失败,而设置条件断点又太麻烦了。这时候可以在代码里面加上一段用来在内存分配失败时触发调试器的断言代码

void MyZeroMemory(char* strBuffer, int length) { assert(strBuffer!=NULL); }



如果使用的是MFC或者ATL,建议使用对应的宏ASSERT和ATLASSERT。现在你可以编写健壮的代码使得程序在strBuffer这块内存分配失败时也能够正常运行。

现在的问题是,加入的这些代码增加了应用程序的大小,减慢了运行速度。在解决了内存分配失败造成的程序崩溃的问题之后,有必要在发布的版本中去掉这些断言代码。一个简单的办法是使用预处理标识符:

void MyZeroMemory(char* strBuffer, int length) { #ifdef DEBUG assert(strBuffer!=NULL); #endif }



这样你可以只维护同一份代码。当然,这也意味着调试的代码在发行版中会被去除,所以为了避免不可预料的行为,为了调试而加入的代码应该尽可能少地影响应用程序的行为。

你有可能需要重新定义assert来实现扩展的行为——例如在assert断言失败中断程序时打开源文件并且跳到assert那一行——这时候你可以编写自己的断言函数,然后重定义assert为这个断言函数。

#ifdef DEBUG /*display a dialog and if the user selected break , jump to the assert line*/ void _assert(char*,unsigned int); #define assert(f)\ if(f)\ {}\ else _assert(__FILE__,__LINE__) #else #define assert(f) #endif


空的if语句块可能看起来有点奇怪,但是这可以避免和宏外的if-else产生冲突。同样,最后一行语句没有结束的分号,因为在使用的时候再加上会更加自然。

assert最有用的地方就是用来检验函数的参数——但是也可以在其他地方起作用。在程序中的断言语句越多,异常的情况就越容易被侦测到。

既然assert是代码,它不可避免的需要注释。即使是自己写的代码,过了六个月之后再来审视也可能需要一点时间来重新理解这部分代码。一个简单的注释可以把这部分时间减少:

void MyZeroMemory(char* strBuffer, int length) { /*should not be called when buffer allocation failed*/ #ifdef DEBUG assert(strBuffer!=NULL); #endif }


在编写完函数之后,应该审视函数中的代码,之后在函数的开头验证函数正常运行所需的条件。如果你在写一个库函数,那么应该在函数的文档中加入函数正常运行所需的条件——否则就会增加使用者发现BUG的难度。举例来说,Windows API的文档不可谓不详尽,但是我在用汇编调用Windows API的时候,也花了很长时间才发现调用Windows API之前栈顶要设置成4的倍数。

注意不要把一些条件当成默认成立的了。assert(sizeof(int)==4);这样的语句在一些人看来很荒谬,但是在Windows开发中通常是32位的long在一些64位平台上已经是64位的了,而在目前还不知道sizeof(int)在什么时候会升位。如果你的代码依赖于int的大小,那么写上这行可以在未来升位之后更快发现问题。

一些保守的程序员在参数错误时会让函数继续运行——返回一个错误码——但是不报告错误。在编写核心模块时这可能很有必要,但是这也经常会把BUG藏起来——在多层函数返回之后时候,错误码经常会丢失或者被替代。尽量不要使用保守编程来替代断言,如果你认为保守编程会造成定位问题的困难,那么就加上断言代码。

在一些时候,校验参数数据似乎是不可能的事情——想象一下那些被设计来搞糊涂解密者的加密算法的中间数据——但是校验这种复杂算法的方法也不是没有。为了确认手算和心算的正确性,我们会使用电子计算器的结果来进行比较,反过来,我们也可以编写另外一个的算法来断言计算结果的正确性。这种方法也可以被用来断言一个函数的汇编版本和C版本的一致性——为了获取最大性能,函数的汇编版本的算法可能和C版本的有很大差异。当然,不是每个函数都有必要用这种方式来验证,实际上,只有极其重要的算法和对性能极其敏感的代码才会需要这种双保险来验证。同样,为了调试而加入的算法也应该尽可能少地影响应用程序的行为。

最后,你不应该在发布程序时从代码中去掉断言语句,而是把它们留在那里以供你升级或者查找BUG时使用。帖来自于网易社区:http://p5.club.163.com/viewArticleByWWW.m?boardId=clanguage&articleId=clanguage_10938d0575c4afe

消灭不可预料的行为

断言并不能抓住所有BUG——它们都是人写出来的,而是人就会犯错误。一些常见的错误包括:

  • 使用状态不确定的资源
  • 在释放资源之后继续访问资源
  • 在资源重新定位之后继续引用旧的资源
  • 申请资源之后丢失对资源的引用
  • 访问时未注意是否越界
  • 忽略错误信息

这些并不是杞人忧天的问题——实际上,这些问题属于日常开发中最常见的问题。这些问题的特点是,它们并不是时常造成程序行为的异常,并且症状不可重复。以内存为例,释放内存之后编译器和操作系统通常不会自动去擦除内存中的内容,所以继续访问内存不太可能造成程序行为的异常——直到内存被重新分配出去,而另一块代码开始重写这块数据。申请资源之后丢失对资源的引用可能只是造成长时间运行之后系统资源不足而已。另外,这些问题都是算法的问题,而编译器并不会替你校验你的算法,你自己也不太可能会怀疑你自己的算法。

不要认为职业的程序员就不会犯这类错误。举几个例子来说明这些问题。

  • 在IE4.0中,MSHTML的HTMLDocument对象的IPersistStreamInit::Load假定传入的IStream流的访问指针已经定位到开头——而在IE5.0中,IPersistStreamInit::Load会自行调用IStream::Seek
  • 在Visual C++ 6.0中,CHTMLView类有字符串资源未释放问题(http://support.microsoft.com/kb/241750)
  • 在Visual C++.Net中,CHTMLView类有两个BUG:
    • 参数传递错误:
      HRESULT CHtmlView::ExecFormsCommand(DWORD dwCommandID, VARIANT* pVarIn, VARIANT* pVarOut) { HRESULT hr = E_FAIL; CComPtr spDoc = (IHTMLDocument2*) GetHtmlDocument(); if (spDoc != NULL) { CComQIPtr spCmdTarget = spDoc; if (spCmdTarget != NULL) hr = spCmdTarget->Exec(&CMDSETID_Forms3, dwCommandID, OLECMDEXECOPT_DONTPROMPTUSER, pVarOut, pVarIn); } return hr; }
      COM指针未释放错误:
      void CHtmlView::OnFilePrint() { // get the HTMLDocument if (m_pBrowserApp != NULL) { CComPtr spDisp = GetHtmlDocument(); if (spDisp != NULL) { // the control will handle all printing UI CComQIPtr spTarget = spDisp; if (spTarget != NULL) spTarget->Exec(NULL, OLECMDID_PRINT, 0, NULL, NULL); } } }

这些不确定的行为是很大一部分不可重复(因此也很难跟踪)的BUG的根源。举例来说,释放一块内存之后,在操作系统切换到另外一个线程之前,重新分配同一块内存,并且写入数据这个事件发生的可能性十分之小。为了解决这些问题,Visual C++编译器采取了一种保护性的措施,在调试模式下再分配和释放内存时将内存用一般不会用到的值填充,例如0xccccccc,0xdddddddd和0xfefefefe(参见编译器文档中的/RTC参数的说明)。这样你可以减少不可预料的程序行为,强迫BUG重现。如果你的编译器没有这么做,你可以自己编写一个调试模式下专用的内存管理程序进行这样的工作。为什么选择这样的值?在Intel系统中,0xcc的含义是int 3中断(参见http://blogs.msdn.com/oldnewthing/archive/2004/11/11/255800.aspx)——如果不小心执行了这块数据,那么程序会马上中断并且提示用户,其他的值则是典型的非法数据。如果你在为其他环境编写程序。你可能需要查阅一些资料来决定使用什么值来在调试模式下填充内存。

MFC的另一个保护措施是内存泄漏监测器——这也是在每个文件开头要加上#define new DEBUG_NEW的原因——但是这也变更了应用程序的行为。举例来说,为了检查内存泄漏,MFC总是分配比所需要多的内存,然后加入调试信息。如果你的程序有访问越界的代码,那么有可能擦除一部分额外分配的内存,可能的结果就是在调试模式下运行正常,而在发布模式下程序崩溃。当然,这是必须的。如果你的发布版本的程序依赖于这些额外字节,那么你就有麻烦了。

在发布模式下程序的崩溃有助于你发现问题,但是也造成定位问题的困难。你可以在发布模式下加入调试信息(没错,在工程的C++和连接选项中选中Program Database和Generate Debug Info)来生成一个中间版本;MSDN文章Generating and Deploying Debug Symbols with Microsoft Visual C++ 6.0(http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp)甚至教你怎么怎么发布这样的版本,但是也要注意这样的版本和最终发布版本还是可能有区别的——特别是在程序中有BUG的情况下。另一个办法是在调试时将EIP寄存器修改成崩溃信息中的值,这样可以很容易在源代码中定位造成崩溃的代码的位置(参见http://www.codeproject.com/debug/XCrashReportPt1.asp)。

MFC开发中另一个比较有用的定位内存访问越界方法是,将数据封装成对象成员变量,尽量可能让所有类都从CObject派生,并且在代码中大量加上ASSERT_VALID。如果成员变量被越界的访问重写了,那么CObject指向AssertValid的虚函数表会被改写,而ASSERT_VALID会报告这个错误。

不要发布调试版本,这对用户来说并无意义。虽然这么说可能是多此一举,但是我在玩游戏的时候还真看见过断言对话框。调试信息是被设计用来发现问题的,不是用来隐藏问题的。如果你确实需要这么做(微软就定期发布核心模块的调试信息以供软件开发人员定位问题),那么你需要让用户认识到调试版本和最终发布版本的性能差异,例如在程序开始时显示一个消息。

广告时间:

最新Windows SDK不支持Visual C++ 6.0

可能大部分人已经知道了,但是CSDN论坛上仍旧不断出现关于这个兼容性的提问。最新的支持Visual C++ 6.0的版本是2003年2月版,下载地址是http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdk-full.htm

取消对Visual C++ 6.0的支持的原因是为了支持新的/GS参数。XP SP2和Windows Server 2003 SP 1都增加了很多安全特性,以致于新的Windows SDK中包含的编译器和库文件不再和Visual C++ 6.0兼容。

参见

posted on 2006-02-05 17:48:00 by jiangsheng  评论(7) 阅读(13922)

MSN7.5发布

在时间上和Google Talk的推出时间在同一天。新功能和以前泄漏的测试版中描述的相同。这估计会降低对Google Talk的发布的注意力。

Google Talk的中文支持似乎有待改进,中文状态信息显示不完整。希望在正式版中有所改善。

 

posted on 2005-08-24 18:42:00 by jiangsheng  评论(3) 阅读(26819)

把WinDbg集成到Visual Studio中

在我的前一篇BLOG(http://blog.joycode.com/jiangsheng/archive/2005/06/08/53126.aspx)中我提到了如何使用WinDbg来调试Visual C++程序。但是,从IDE到命令行之间的切换比较麻烦,为了偷懒起见,可以把WinDbg加到Visual Studio的工具菜单中,这样就可以直接从IDE启动WinDbg来进行调试了。下面是外部工具配置界面的设置

标题 : WinDbg
 
命令行: C:\masm32\debug\windbg.exe (这不是一个典型路径,但是既然MASM里面有了,我也懒得再去下一个来装,如果是单独下载安装的话,命令行可能像这样:"C:\Program Files\Debugging Tools for Windows\windbg.exe")
 
参数:-ee c++ -G -i "$(TargetDir)"  -y "$(TargetDir)" -QY -logo -QSY -sdce -WF "$(ProjectFileName).WEW" "$(TargetPath)"
 
(命令行太长的时候windbg会工作不正常。至于这些参数的含义——呃,还是去下一个Debug Tools For Windows回来装上看文档比较好。)
 
初始目录: $(ProjectDir)
 
当然,调试的代码不限于C++,实际上,我主要是用windbg来调试汇编代码。

posted on 2005-06-30 22:14:00 by jiangsheng  评论(0) 阅读(3366)

使用WinDbg调试VC程序

使用WinDbg调试VC程序

虽然在VC6.0中可以通过安装Visual C++ Toolkit(网站:http://msdn.microsoft.com/visualc/vctoolkit2003/)来编写基于最新版本的平台SDK、DirectX SDK的程序以及托管代码,但是VC6附带的调试器并不支持新版本的调试信息,所以实际上是不能用VC6来调试新版本编译器生成的程序的。一个替代的解决方案是使用新版本的Windows调试工具Windbg(网站:http://www.microsoft.com/whdc/DevTools/Debugging/default.mspx)。Windbg的调试功能基本和Visual C++中的相同,但是需要手动设定源文件和调试符号文件的搜索路径(可以参考VC6.0中的对应设置)。一些代码,例如MFC的代码比较难于定位,这时可以双击调用堆栈中的函数名称来打开文件并定位到函数所在位置。Windbg可以进行有限的托管代码调试,但是调试过程比较麻烦。在没有安装Visual Studio的计算机上调试,例如进行远程调试的时候可能还需要部署调试符号(Generating and Deploying Debug Symbols With Visual C++ 6.0)。

调试Visual Studio .Net 的程序需要用户是管理员或者"Debug User"用户组成员。如果登录用户不是该组成员,那么也可以用WinDbg来调试程序。

posted on 2005-06-08 23:53:00 by jiangsheng  评论(2) 阅读(13457)

Visual Studio 2005 Beta 2发布

本来想问为什么版本这么少的,刚刚又刷了一遍下载列表,Beta2相关的产品居然就不见了……再刷一遍又出来了……不知道MSDN在搞什么……

目前在MSDN订阅者站点可以下载标准版和VS Team System的多个版本。安装之前要删除所有Beta 2之前的Visual Studio 2005组件。根据Express开发小组的blog(http://blogs.msdn.com/express/archive/2005/04/19/409689.aspx ),Express版本的开发工具也会已经更新到Beta 2版本,下载地址如下:

Product Name

URL

 VB Express Bootstrapper

http://download.microsoft.com/download/E/1/C/E1C75061-A229-49EA-BD0C-D0A7A22E8BA4/vbsetup.exe

 VC Express Bootstrapper

http://download.microsoft.com/download/1/C/3/1C3CCC8E-0FCF-400F-BD5C-56C4D65961E9/vcsetup.exe

 VC# Express Bootstrapper

http://download.microsoft.com/download/C/0/7/C07E9E42-0D34-45D7-890F-810964216A62/vcssetup.exe

 VJ# Express Bootstrapper

http://download.microsoft.com/download/6/3/8/638C4A4A-8343-4360-9FE8-78AE03037057/vjssetup.exe

 VWD Express Bootstrapper

http://download.microsoft.com/download/3/A/0/3A017815-B515-4807-87E5-F2DC57191C97/vwdsetup.exe

信息来源:http://blogs.msdn.com/aalialikoski/archive/2005/04/16/408801.aspx

PS:Blog spam又开始泛滥了,不过这次比较隐蔽,留言的内容是赞扬,但是留言姓名和URL部分是广告。博客堂各位堂主记得检查一下。

posted on 2005-04-16 16:56:00 by jiangsheng  评论(6) 阅读(11649)

Get Ready? Win 64 is coming!

在AMD公司于2003年发布64位CPU芯片时,人们期望在一年内微软会发布一个基于64位CPU芯片的Windows。

这个期望并没有实现——或者说被推迟了,或许是因为同时有太多的项目在开发,例如Windows XP SP2,Longhorn以及Visual Studio 2005。但是上周Jim Allchin在Intel开发论坛说64位的Windows将会在4月份就绪:“新的64位Windows XP将会在四月初发布。服务版会在四月底发布。”

预计64位Windows的发布将会刺激对Intel和AMD的64位芯片的需求。

对于开发人员来说,虽然平台SDK( http://www.microsoft.com/msdownload/platformsdk/sdkupdate)在其2003版本中就包含了对64位Windows的支持,但是除了增加了Windows XP2 SDK的升级之外,已经有两年多没有更新了。新的SDK预计应该和Visual Studio 2005一起发布。等不及的人可以写email去要新的头文件和库文件。

参考

Microsoft says 64-bit Windows due in April(http://www.zdnet.com.au/news/software/0,2000061733,39182994,00.htm)

在Visual C++ 2005 Express(October 2004 Community Technical Preview )中使用平台SDK(http://blog.joycode.com/jiangsheng/archive/2004/12/17/41283.aspx)

How to obtain the 64-bit version of the Visual C++ 7.1 libraries and build toolshttp://support.microsoft.com/kb/875446

posted on 2005-03-08 12:46:00 by jiangsheng  评论(1) 阅读(3057)

重新启动服务

最近常去的论坛挂了,看起来是IIS进程系统资源占用太多了;服务器管理员又度周末去了,不能重启IIS,郁闷。CSDN服务器的IIS可能重启过于频繁了,分论坛页面经常来不及更新,自己发的帖子出现在列表里面的时候已经沉了,还是郁闷。

微软知识库文章Q194916?Restarting?Web?Services?and?Scheduled?Tasks?with?a?Batch?File(http://support.microsoft.com/?kbid=194916?)中描述了定时用命令行重新启动IIS的方法,有想偷懒的网管可以试试。

如果用程序来重新启动IIS的话,可以使用Shell对象的IShellDispatch2接口的ServiceStop 和ServiceStart方法。要使用Shell对象,可以调用CoCreateInstance,传递Shell对象的CLSID CLSID_SHELL来创建对象,然后查询其IShellDispatch/IShellDispatch2等接口。

另外,WMI类Win32_ApplicationService?也提供了控制服务的方法StartService和StopService。cideguru上面有一个示例Using WMI to Extract Management Information(http://www.codeguru.com/Cpp/W-P/system/misc/article.php/c5675/)。


当然,使用Windows服务API也是可以的——尽管需要OpenService之后再ControlService和StartService,看起来不是很简洁。平台SDK中的示例Sending Control Requests to a Service(http://msdn.microsoft.com/library/en-us/dllproc/base/sending_control_requests_to_a_service.asp)描述了这一点。

这些方法都可以应用于其它服务。要查询服务的短名称的话,可以参考微软知识库文章Q271362?How?to?Find?the?Short?Names?of?Services?(http://support.microsoft.com/?kbid=271362
参考文档
Browse?for?a?Folder?the?COM?Way(Visual?C++?Developer,?Aug. 1999)

More Windows 2000 UI Goodies: Extending Explorer Views by Customizing Hypertext Template Fileshttp://msdn.microsoft.com/msdnmag/issues/0600/w2kui2/default.aspx

posted on 2004-06-19 20:24:00 by jiangsheng  评论(6) 阅读(4467)

WinZip发现严重漏洞

原文在 http://tech.sina.com.cn/s/n/2004-02-29/1050298844.shtml

下面一段是节选

WinZip程序处理的文件一般是不能够执行的,因此很多用户都毫不犹豫地打开这种文件,即使这个文件的来历不明也不在意。因此,这个安全漏洞本身的危险性就更大。

幸好我很多年之前就改用WinRAR了……

posted on 2004-02-29 12:55:00 by jiangsheng  评论(1) 阅读(1902)

微软发Office免费许可 开放其XML文件格式

终于不用另存为HTML了……

当地时间本周一,微软公司宣布将免费许可其在最新版的Office软件包使用的基于XML的文件格式。

  这一举措旨在使企业能够更好地访问使用Office2003创建的文件。微软公司表示,它是应软件合作伙伴以及企业和政府部门客户的要求采取这一措施的,这使得它们能够更好地在应用中集成Office软件包。

全文参见

http://tech.sina.com.cn/it/2003-11-18/0739257264.shtml

posted on 2003-11-18 13:09:00 by jiangsheng  评论(2) 阅读(1685)

Powered by: Joycode.MVC引擎 0.5.2.0