2/05/2006

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兼容。


参见


Jiangsheng的CSDN Digest (Jan 21 2006)


CSDN 讨论总结系列:






inet控件下载ftp文件后,下载的文件被存放在IE缓存文件夹中,如何自动清除(VB 基础类)





http://vbnet.mvps.org/index.html?code/internet/deleteurlcache.htm


通过API访问IE Cache :

http://blog.csdn.net/technofantasy/archive/2002/03/29/2962.aspx





微软的MSWORD,MSEXCEL可以嵌入在IE中,Outlook可以吗? (VC/MFC ATL/ActiveX/COM )





Word and Excel are ActiveX Document servers which is why you can embed them within a web page.
Outlook is not an ActiveX Document server.





ATL的线程函数中 FIRE EVENT出错,其它类中FIRE EVENT 就OK ? (VC/MFC 进程/线程/DLL )





ATL和MFC都不是线程安全的
参考http://www.mvps.org/vcfaq/com/11.htm





以不同身份登陆域以获取不同的服务权限 (C++ Builder 基础类 )





如何可以做到不转换Windows用户的情况下,以另一个用户的身份访问域服务器,并且结束时可以注销该用户身份。
参看了一些资料,可能要用到LogonUser,DuplicateTokenEx,CreateProcessAsUser这些函数,有谁可以提供一些思路或上述函数的执行代码。谢谢!





你可以用CreateProcessAsUser函数另外启动一个进程,之后使用进程间通讯(http://msdn.microsoft.com/library/en-us/ipc/base/interprocess_communications.asp )的方法来控制启动的程序。用LogonUser、CreateProcessAsUser,要SE_TCB_NAME权限,且即使你是administrator,也很难在程序中得到要SE_TCB_NAME权限(我从未成功过)
建议用CreateProcessWithLogon.

其实,还有一个最简的方法,就是用系统的runas命令:
如:
runas /env /user:user@domain.microsoft.com "notepad \"my file.txt\""


你可以在控制面板->管理工具->本地安全设置->用户权利指派中将"以操作系统方式运行"赋给你要设SE_TCB_NAME权限的用户,这样你上面的程序应该可以运行,但有安全隐患,因为SE_TCB_NAME是系统的最高权限





请教一个多国语言字符串转换的问题 (VC/MFC 基础类 )





我的程序中要求一个功能,当从其他窗口,比如ie中,将一段文字拖到本窗口时,要接收到字符串并显示出来。
现在功能已经基本实现,拖过来英文,中文,日文,俄文,法文都没问题,但是德文却总有些字符显示乱码。
我试了其他的一些软件,qq和vc6都显示不正确,但是ie,msn,vs.net,office等都可以正确显示。
我想是我接收到字符串后没有做正确的转换,但是不明白该如何转换,接收到的字符串是以什么格式存储的。
希望有过类似经验的能指点一下,不胜感激。

我接收的代码大致如下:
BOOL CTmp3View::OnDrop(COleDataObject *pDataObject, DROPEFFECT dropEffect, CPoint point)
{
STGMEDIUM stgm;
FORMATETC fm = {CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
char* cpData = NULL;
DWORD dwSize = 0;

//得到数据和数据的字节数
pDataObject->GetData(CF_TEXT, &stgm, &fm);
cpData = (char*)GlobalLock(stgm.hGlobal);
dwSize = GlobalSize(stgm.hGlobal);

//处理数据并显示
... ---->> //我想这里应该对cpData有所处理

return CView::OnDrop(pDataObject, dropEffect, point);
}





CF_UNICODETEXT





vc6+ado+access删除表内所有记录时, 如何复位自动增加的整型字型至0? (VC/MFC 基础类)






也就是说, 再次增加记录时, 自动增加的整型字段从1开始计数, 而非紧接原序号.





需要重构数据库。参见http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odeopg/html/deovrencryptingdatabase.asp





Access数据库函数问题(C++ Builder 数据库及相关技术 )





使用Access数据库,用MS Access建一个查询Table1_View,SQL语句如下:
SELECT MyName, StrReverse(MyName) AS ReMyName FROM Table1 ORDER BY StrReverse(MyName)

在MS Access中运行正常,可以打开Table1_View记录集,但要在C Builder中打开这个记录集时,返回错误信息:表达式中‘StrReverse’函数未定义。

请问是什么原因?如何解决?





Access中不支持从外部直接访问这个函数。参考http://support.microsoft.com/?kbid=210439





于OCX调用方面的问题(.NET技术 VB.NET)





我现在有一个用DELPHI做的OCX控件,用VB.net开发一个网站,需要使用它显示特定图形,图的数据是从服务器中的某个目录中获取的,应当如何做?
我通过工具栏增加OCX后,其Runat属性是灰的,服务器脚本中不能使用;我通过项目引用后使用CREATOBJECT的方法能创建,但又无法与页网中放置的控件联系起来,这是为啥呀????
OCX是不是不能做为服务器端组件运行?如果不能的话,那要实现我的想法的话有何高招??





在客户端显示图形的OCX为什么要在server上运行?
自己写个user control封装一下<Object>代码就可以了





如何获得网页中的元素位置信息 (VC/MFC HTML/XML )





打开一个网页,浏览器知道图片对应的位置,知道一个表的对应位置,现在我要在程序中解析一个页面中的每个元素的坐标信息,应该用那个组件啊,





http://blog.csdn.net/jiangsheng/archive/2003/11/09/3795.aspx

You can also determine the location, width, and height of an object by using a combination of the IHTMLElement::offsetLeft, IHTMLElement::offsetTop, IHTMLElement::offsetHeight, and IHTMLElement::offsetWidth properties. These numeric properties specify the physical coordinates and dimensions of the object relative to the object's offset parent.

For more information about how to access the dimension and location of objects on the page through the Dynamic HTML (DHTML) Document Object Model (DOM), see Measuring Element Dimension and Location.

http://msdn.microsoft.com/workshop/author/om/measuring.asp





IPicture中get_Width得到的OLE_XSIZE_HIMETRIC到底是什么含义(VC/MFC 图形处理/算法 )





MSDN里解释如下:
OLE_XSIZE_HIMETRIC (long) The width of the picture

照片的宽度?
我使用一幅640*480的图片,得到的width、height分别为16933、12700。
请问这个数值有什么实际的含义?





phisical size of the picture converted into HIMETRIC
reference:
http://msdn.microsoft.com/msdnmag/issues/01/10/c/
http://msdn.microsoft.com/library/en-us/gdi/cordspac_3qsz.asp





使用CListCtrl显示大批量缩略图(VC/MFC 界面 )


不想删除ClistCtrl的自定义数据,该怎么办?






由于工作需要大批量显示SolidWorks文件的缩略图(一万个左右),我现在使用的是虚CListCtrl既style = LVS_ICON|LVS_ALIGNTOP|LVS_OWNERDATA|WS_VISIBLE|WS_BORDER|WS_CHILD|LVS_SINGLESEL, 对于SolidWorks文件可以读成bitmap,由于文件太多内存存不下,我现在想利用文件缓存,来缓存我生成的这些bitmap,有没有这样的方法,最好有例子.


我看了vcmute(横秋)有关这方面的解释提道了IExtractImage,不知道能解决我的问提吗?
http://community.csdn.net/Expert/topic/4134/4134216.xm8l?temp=.4045069
提供的连接没打开.





既然用了虚拟列表,一次显示的缩略图也不会超过几百个吧,这样对内存的需求就减少了很多。至于缓存,不需要加载所有的缩略图,使用后马上释放掉就可以了,利用双缓冲,绘制到一个内存DC上,刷新的时候再调取需要的缩略图。


我用LV_ITEM Item的方式insert到一个list,其中new了一个自己的在doc中定义的数据结构,这个结构由于在其他地方还要用到,所以当 clistctrl有更新时不能删除,但要更新一个ClistCtrl必须先调用clistctrl::deleteallitem,在 ClistCtrl::OnDeleteitem中就要删除条目才能将新增加的item给加进去,现在我只想更新条目,由想留住自己的在doc中定义的数据结构供下次调用,请问ClistCtrl有没有什么函数只清空显示区域,而不删除我的数据结构?

难道我只得定义两个结构吗?一个用于list显示,一个用于list历史数据得保存?





http://blog.csdn.net/jiangsheng/archive/2003/11/20/3796.aspx


虚列表没有自己的数据,你需要自己把数据排好之后通知虚列表更新显示。文件大小可以用用IShellFolder2::GetDetailsEx获得


对于你需要的排序,你可以用CRT的qsort函数。使用IShellFolder::EnumObjects的原因是性能。用Win32的 FindAPI的话,在函数返回之前会搜索完整个目录,但是用虚列表的话,可以只获取正在显示的文件的信息(当然,在我的代码中还缓存了文件列表的开头和末尾,这是用户经常查看的位置)。



这个示例没有在空闲时释放缓存,你可以自己决定缓存的大小上限,然后改写PrepCache函数。


你也可以使用资源管理起来显示图片http://www.codeproject.com/shell/thumbextract.asp





请问在 VBscript 中如何调用 windows API ?有现成的ActiveX封装吗?(VC/MFC ATL/ActiveX/COM )





我想使用脚本语言做一些自动操作其他应用程序界面的东西,有点类似 rational robot
首先想到了 wsh 和 vbs, 但是好像 vbs 中不能直接调用 PostMessageA 之类的 API 函数,
借贵宝地问一下:
1. 是否有现成的 ActiveX 封装了 windows API ?
2. 除了使用robot之类的商业软件,您是否有更好的方法建议我?





try host implementation instead
see http://blog.csdn.net/jiangsheng/archive/2003/11/09/3795.aspx
That is basically imposing scripting support to an MDI version of MFCIE.





DLL中的DoDataExchange (VC/MFC 基础类 )





我程序中加载了两个DLL,都封装了窗口类,并且都影射了控件变量
只加载其中的一个没问题,可是两个都加上之后,在DoDataExchange的时候就会出错
通过GetDlgItem也找不到该控件,这是为什么?





sounds like resource conflict.
Reference:
http://support.microsoft.com/default.aspx?scid=kb;EN-US;150121





ActiveX和DLL利用回调通讯,如何在回调中产生事件(VC/MFC ATL/ActiveX/COM)





我要调用一个DLL进行一些工作,调用和返回不在一个线程中。所以我使用回调函数的方法返回数据。联系DLL的是一个ActiveX控件,因为我使用了回调函数,所以我必须向DLL提供一个静态函数地址。而我要在这个回调函数中产生一个事件,ATL的事件是在类中生成的,我无法在静态函数中调用。





You can use an IDispatch parameter instead of a callback function (in other words, event handler), and call IDispatch::Invoke to call the function (in other words, fire the event), which calls your function indirectly in its implementation. A typical example is AJAX (Asynchronous javascript and XML).



COM 支持连接点对象的啊,功能类似回调函数。
继承 IDISPATCH 做一个出接口就行了,很方便的





用wininet做个多线程的浏览器。线程少的时候正常,多至30的时候锁死(VC/MFC 进程/线程/DLL )





主线程浏览网页,根据网页里的图片联机,产生子线程,下载玩就自动清除。
当多至30的时候就都挤死在chttpfile::sendrequest了。对底层网络访问控制是由ntdll完成的。





INFO: WinInet Limits Connections Per Server
http://support.microsoft.com/kb/q183110/





如何在VC,BC环境下获取按键(C/C++ C语言 )





我有TC版的一个五子棋游戏,现在必须要在VC环境或BC环境下编译。
但是TC下有bios.h,和很多相关的函数,比如bioskey来获得按键内容。
而这个头文件在VC,BC上是没有的。请问在VC环境,BC环境下有相应功能的替代函数么?
或者告诉我,如果要获得上,下,左,右,enter,space按键,在VC环境,BC环境怎么做?

注:我这里的VC,BC并不是MFC等等,只是一个C++编译环境。





这些都是Borland的库函数。你可以自己实现这些函数。举例来说,你可以用getch来获取按键,或者处理WM_KEYDOWN消息。没有gotoxy的话,不过你可以自己写一个:

#include <windows.h>
void gotoxy(int x, int y)
{
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
 


windows 下面,你想取得按键信息。处理 WM_KEYDOWN 消息就行了。例如:
case WM_KEYDOWN:
switch(wParam):
{
case VK_RETURN://回车
.........
}
上下左右键分别是VK_UP,VK_DOWN,VK_LEFT,VK_RIGHT
Enter, Space键分别是VK_RETURN,VK_SPACE

只要处理这些消息就可以了。






怎么获得iframe中的内容?(VC/MFC HTML/XML)





当一个页面中的iframe内容来自另外一个网站的时候,通过IHTMLFramesCollection2。item获得 IHTMLWindow2指针,然后调用IHTMLWindow2::get_document获得的总是空指针。同样的情况出现在遨游浏览器的 viewPage插件里。在这种情况下,viewpage得不到ifram中的链接。
同过得到ifram内容的url,再navigate(url)似乎可以,但是有没有不用再次连接的方法呢?





基于IE的安全性设定,跨框架的DHTML访问可能被拒绝。参见Internet Explorer的安全设置。