2007年2月2日星期五

为何不去作文章?

    要写论文了。但我的指尖总在写就的论文大纲晃动。为什么呢?因为我想要一个合乎性情的写作方案。何也?就是可以将科学论文、数学公式、程序代码、工作小 记、思想片段等一系列的元素组织在一个简单的系统中,并能快速地进行打印、电子邮件与博客发布。这样我就可以放开自己的思绪,按照"非线性"的方法进行论 文编写与思想记录了。
    首先我排除了 Word ,虽然其是许多人管理发布文档的"标准手段",我却不欣赏。Word 在编辑数学公式与版式设计上是如此的繁琐,而且由于 DOC 文件是二进制文件,其不能用 CVS 来进行版本管理与文本比对,实在不适合进行多个文档的长期的交叉式的编辑。这时 Latex 进入我的学习视线。 Latex 是采用标识语法符号来进行排版,其类似于程序编写,很适合我;而且其是文本编辑,可以进行 CVS 版本管理。优点多多,而且在不断地使用中会有更深刻的认知与更高明的技巧,实在是"可玩"的文档排版方案。所以我对 Latex 进行 了近2个月的系统学习,有些"玩物丧志"了。
    但这至多解决了文档排版的问题,如何记录自己思想的点滴,并能进行方便地汇总呢?我尝试了几种方案:一是用 DiaryOne 电 子笔记本进行记录。 DiaryOne 笔记本很方便,而且可以添加附件与录音。但其在信息组织上很是简陋,只能按目录进行存放,不适合多线交叉管理。然后我又用了 Wiki Wiki 是一种非常利于进行非线性编辑的工具,其可以将所写文档以链接的方式进行交叉管理;由于我多是用来记录文本,所以在尝试了一些需要安装或需要 PHP 支持的 Wiki 程序后,最后确定使用 TiddlyWiki ——这个单文件的 Wiki 作为记录之用。很神奇的一个小软件,将 JavaScript 显示技巧应用得很出色。我建议每个学习使用 Wiki 的人最好从 TiddlyWiki 开始。要使用好这个软件,应安装 Firefox,否则无法实现其强大的信息管理功 能。但可惜的是 TiddlyWiki 不支持附件,这给我带来太多的困扰。思量很久,终放弃。随后我将目光转动近年来如火如荼的 Blog 上。虽然我有许多的 Blog ,但使用率并不高,主要原因是其需要太多的 Web 编辑时间,而只有 MSNSpaceBlogger 支持我最喜爱的电邮发布。 MSNSpace 虽然是个很好的 Blog 系统,但其最大的问题是访问速度。可能 MS 将其与操作系统捆绑太多,或是国内访问较慢,每次我登录它都要等待很长时间。而 Blogger 的国内访问以前一直不稳定,我申请后就未认真打理。但这次我重新评估 Blogger ,不仅它的 beta 字样消失了,而且其在国内访问速度很快,与 GoogleGmailDoc 等结合的更好。在新版本中 Blogger 提供了全方位的模板代码编辑与电邮输入输出功能,这强烈地吸引了我。我手写了模板 CSS 代码,并且定义了电邮发布与电邮输出,从而可以同步更新 BloggerMSNSpace ,这是太好了。经过上面的一番探索,我决定采用 Email 的方式来发布 Blog 。利用 Blog 的 TAG 功能来组织自己的思想片段。
    在 Windows 平台上最常用的电邮工具是 OutLook 。虽然它很好用,但我仍不满意,因为它不支持 HTML 代码输入,就是不能在编写 HTML 邮件正文中直接插入 HTML 代码。于是我想到 Foxmail,它曾是我经常使用的电邮工具。我下载了最新版的 Foxmail 6,可惜它也不支 持 HTML 代码直接插入功能。最后我发现 Firefox 阵营的 Thunderbird ,它虽然没有 Firefox 那样有名,但其品质却很出众。不仅有强大的自定义的 Filter 功能,而且支持 HTML 代码插入。更出色的是它也是基于 Add-On 的,可以从网上下载许多有用的小插件,自己也可以编写一个。我下载了一个 pasteCode 的插件,其可以在粘贴 C、C+ +、Java 代码时自动为关键字等加上高亮颜色,很有趣。
   "磨刀不误砍柴工",通过前面的探索,现在我对自己的这一系列编辑方案进行总结,就是 Thunderbird + Gmail + Blogger + Latex(CTex) + Editplus 。其间的交流与联动,我写了一些 Python 小程序来完成。对于流程图,我试用了不少工具,但目前看来还是 MSVisio 最好,只是一想到它竟然不能发布为 EPS 格式就很切齿。

BCB中的计时与显示

在程序开发中常要实现计时与计时显示。在BCB中一般方式是利用Timer控件来进行计时。样例代码如下:
void __fastcall TForm1::Timer1Timer(TObject *Sender) {
    // timer interval callback function
    iTimerCount ++ ;
    if ( iTimerCount > 1000 ) { Timer1->Enabled = false; }
}
但Timer控件有两个缺陷:
(1)其不是一个精确的计时器,其只能保证秒级的精确性,即只有设置其Interval属性为1000的倍数是才是比较准确的。
(2)其运行级别很低,这样在多线程程序中会造成高级别线程堵塞了Timer线程从而造成计时不准确。
认识到以上两点,可将Timer控件只作为时间显示来用,而不作为计时,在每次显示时获取当前时间,并用MilliSecondsBetween函数与开始计时时间进行比较,得到毫秒级的差值,然后进行格式化显示。
样例代码如下:
(1)开始计时代码:
  iTimerCount = 0;
  startTime = Time();//< startTime 是一个 TDataTime 变量
(2)计时代码:
  void __fastcall TFrmMain::Timer1Timer(TObject *Sender) {
    // timer interval callback function
    double msValue = MilliSecondsBetween( Time(),startTime );
    Label5->Caption = AnsiString( msValue / 1000.0 );
    iTimerCount ++ ;
    if ( iTimerCount > 1000 ) { Timer1->Enabled = false; }
}
 

2007年1月30日星期二

“博客园”技术博客转移成功!

今天我将原来在"博客园"中的技术博客进行整理,转移到Google的Blogger中,以便更方便地用Gmail来发布与管理博客。

牵模式的手——《模式的乐趣》阅读笔记

很显然,从一开始我并未想到这样薄薄的一本书竟然配我走过两个多月的时光,而且让我时时激起购买它的欲望(现在我还是遵循"书非借不能读"的古训)。想起那天和Decland谈起《DesignPattern》,他感慨于该书Scholar气息太浓,Sample太少。现在我也有同感了。的确《DesignPattern》太过于严谨,对于OO的新手来说缺少亲近它的欲望。而这本小册子,则很能成为阅读《DesignPattern》的辅助读物,像筵席外的一份零食。
"简而言之,模式也是世界上出现的一种事物,其规则告诉我们如何创建这种事物,以及何时必须创建它。它既是过程,也是事物;既是对存在的事物的描述,也是对创建这种事物的过程的描述。——Alexander[1979]"
"对于软件来说,算法、数据结构是基本构件,模式也是,甚至更像是定义算法和数据结构的模具"
"模式的一个主要目标就是,以一种别人容易接受的方式,捕捉那些重复出现的问题的解决方案(以及可以使用这些解决方案的约束和场景)",从语义表达角度,模式所能传达的信息更多,更精密。这也是其难于掌握的原因。像情侣间一个细微的眼神,如果没有日日夜夜的厮守,是无法达到的。
"每一种模式都应该是对其他模式的补充。每增加一种模式都应该让系统更加强壮。这样的系统可以不断发展,且不会增加维护的压力,也不会降低整个系统的性能——系统增大后通常会导致这种结果。我们希望创建一个各部分互为补充的框架,希望每一个新增的部分都能让其他部分更加强壮"
"模式不会存在于真空中。实际上,模式不仅依赖于其自身,而且依赖于整个体系结构中所有其他模式。每种模式本身都由相互依赖的其他成分组成"。模式更像软件的社会化属性,在良好模式设计的系统中,众多简单对象合同协作,完成复杂任务。可见,模式可以由人的管理模式进行映射。善于了解模式的人一定也是善于发现社会协同机制的人。
"没有经验的开发人员常常不理解设计与实现之间的区别,所以这些习惯语法就成了他们表达设计思想的唯一途径""如果你唯一的工具是锤子,你就会把所有的东西都当作钉子"
"模式是不依赖于编程语言的(与之相反的习惯语法是编程语言特有的)。从某种程度上讲,模式构成了一种语言,它比编程语言更进了一步,使开发人员可以彼此交流设计思想。……模式本身超越了任何具体的语言,这绝不是要忽视选择编程语言的重要性,只是希望你认识到,编程语言不应该限制我们提供有价值的技术的能力"
"当一个人从事设计活动时,他的行为完全受当时他心中已有模式语言的支配。当然,随着经验的增长,每个人心目中的模式语言都会随着时间而变化。但是,在必须做出设计决定的那个特定时刻,他要完全依赖在此之前他已经积累的模式语言。不论他的设计多么小或多么复杂,他的设计活动将完全受他心目中已有的模式以及他将这些模式组合新的设计的能力的支配——Alexander[1979]"

有知者无畏!-读《与熊共舞》

当我合上这本书的黑色封面的时候,不由想起《老人与海》中的那句名言:人不是生来就被打败的。的确,在这本书中,洋溢着一种人类理性无比的自信。不是吗?
风险管理,在许多人看来,是如此虚无缥缈的东西,人们似乎无法完整地定义风险,也无法确切地预知未来。在风险的面前,除了迷惘与祈求,大多数时候总会如鸵鸟一样将头埋在沙中,欺骗自己没有看到风险。但作者却没有这样,他在最艰涩的地方开凿,在最朦胧的地方勾绘蓝图。虽然我还不能完全理解那些曲线间的确切含义,但我明了在这些曲线的后面是一颗充满理性与必胜信念的心。
其实不仅在软件界有风险管理,在其他领域,甚至于在生活的方方面面都应存在如是的"管理"。在问题面前不回避,在挫折面前不动摇,用理性的力量与经验的承继去解决它们。这是一种伟大的性格,一种光辉的精神。
[写此篇以自励]

在脚边的程序段

这几日总是为Floater菜单的不运行而烦恼,因为我找不到产生错误的原因。我总是认为是脚本有误,但今天参考去年我编写的精品课程网站脚本,发现其没有什么问题。静下心来又细查了一遍,突然发现去年的网页头部没有
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

突然我知道问题症结了,原来是Dreamweaver8自动设置的Web头比较严格,其阻止了浏览器解释一些特殊Javascript代码。解决很方便,只要将文件头替换掉就可以了。
现在知道要解决问题,往往不要仅关注问题本身或附近,要有抬头四顾的能力。只有这样才会在不经意间找到真正的原因。

JavaScript CodeSnippets——获取Cookie

function getCookie(Name) {
  var search = Name + "="
  var returnvalue = "";
  if (document.cookie.length > 0) {
  offset = document.cookie.indexOf(search)
  if (offset != -1) {
  offset += search.length
  end = document.cookie.indexOf(";", offset);
  if (end == -1)
  end = document.cookie.length;
  returnvalue=unescape(document.cookie.substring(offset, end))
}

用法:
if( getCookie("myCookie") ){
//Todo something
}

BCB中执行SQLServer远程存储过程

BCB中执行SQLServer的远程存储过程,应采用TADOConnection与TADOStoreProc组件组合使用。其中TADOConnection组件作为连接数据库,而TADOStoreProc组件则作为运行存储过程。
1.连接数据库
void __fastcall TDataModule::OpenADOConnection()
{
ADOConnection1->ConnectionString = "...";
ADOConnection1->LoginPrompt = false;

try
{
ADOConnection1->Connected = true;
}catch(...)
{
//TODO: Show Database Open Error Message
return;
}
return;
}

2. 设置存储过程
(1)在BCB中最好采用设计器来设置TADOStoreProc组件的存储过程名,这样可以自动将存储过程的参数添加。记得在Build前将TADOConnection的Connected设为false就可以了。
(2)在运行期采用Parameters属性来设置存储过程参数
StoredProc1->Close();
StoredProc1->ParamByName("@ParaID")->AsString = edtID->Text;
//其它参数

(3)采用ExecProc方法或Open方式运行存储过程
if(!StoreProc1->Prepared)
StoreProc1->Prepare();
StoreProc1->ExecProc();

(4)最后可以通过参数或者TDataset来获取结果
Label1->Caption = StoreProc1->ParamByName("@ReturnValue")->AsString;

SQL Server安装文件挂起错误解决办法【转帖】

现象:
安装SQL Server的时候,系统提示。"以前的某个程序安装已在安装计算机上创建挂起的文件操作。运行安装程序之前必须重新启动计算机。" ,但是重启依旧不能解决。
解决:
打开注册表编辑器,在
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\ Control\ Session Manager
中找到PendingFileRenameOperations项目,并删除它。这样就可以清除安装暂挂项目。

AccessSQL语句中的时间处理

在Access中如果要比较日期,应该用DateValue函数来进行格式归一化。比如:
delete from sf_guestonline where lastactivity < DateValue('2006-9-24 下午 01:46:16')
在雪人论坛(access)版中存在用#来标示时间的用法,由于Access驱动的问题,在一些机器上(比如我的)就不能用,所以应该采用DateValue函数的方式。

Linux环境下可视化开发[翻译]

原文:Visual Development on Linux
http://openubuntu.blogspot.com/2006/11/visual-development-of-linux.html
对于许多比较依赖鼠标的人来说,印象中Linux下开发不是件容易事。如果你乐于命令行则另当别论。现在你有了选择。已经有一大堆的Linux可视化开发环境等着你用呐!下面我来介绍几款....(此处省略原文N个词,老外就是啰嗦呀)

1.NetBeans
一个JAVA开发环境!作为一个玩JAVA的,你不一会儿就会爱上她!wo!拖拖拉拉就完成一个程序啦!
她支持:
一般的Java程序或库开发
Web程序开发
EJB开发
WebService、WebService客户端
J2ME MIDP程序
JAVA图形化用户界面开发
NetBeans 插件

2.QT/KDevelip Desinger
QT是KDE上最好的开发环境,其也可用于Gnome。只要托拖拽拽就可以完成一个数据驱动的C++程序咯{译者注:KDevelop更好用一些,我在Rethat Enterprise 环境的C++开发都用KDevelop}

3.Gambas
一个VisualBasic的克隆。对于要转入Linux的VB程序员是非常有用的哟!美观简捷的界面,可通过Gambas方便地连接主流数据库(MySQL,PostgreSQL等)

4.NVU
一个取代Macromedia产品的网页编辑器。WYSIWYG,且可直接FTP到你的网站

5.Eric
一个简便高效的PythonIDE哟!

6.Eclipse
最流行的开源开发环境{译者注:这个就不用再说啦,网上连篇累牍}

7.JDeveloper
一个免费非开源的Java编辑环境,可以像VB/Delphi一样开发Java程序。其来源自Oracle,JDeveloper使用Sun的JDK。

8.Planner
一个类似Project的项目管理软件,可以轻松绘制甘特图。

9.RealBasic
一个商业的IDE。可以像VB一样的开发。

10.IntellijIDEA 6.0
一个开发Java的商业环境

{译者注:我在Linux下一般就使用KDevelop和Eclipse就可以了,当然KEdit、Vim和Emacs也是常用的编辑器,但不能可视化开发。毕竟Linux不是Windows,操控性比可视化更重要!不是吗?}

《Win32多线程程序设计》笔录

读后感:

这本书是一本Win32下多线程的抗鼎之作,其涵盖了多线程编程的方方面面。我阅读的原因在于期望能了解原汁原味的Win32下多线程编程。而我以前只是使用BCB的TThread等对象进行Win32多线程快速开发。阅读后的确对于Win32下的许多概念清楚明了不少,但也产生许多新的疑问。但我知道,这些疑问许多并不能在这本书中获得求解。在.Net下的Windows多线程开发已经有了新的发展,需要我继续努力学习。
我以前对侯捷老师的译注总有些不适应,总感觉侯老师严谨有余,亲和力不足。但这本书中,侯老师的精彩译注,以及对于原著错误的指正,使我的阅读受益匪浅。我才真正感受到严谨的魅力。感谢侯老师。
下面是我的笔录,其中{}为我的随感。

笔录:

P4
多线程,使程序得以将其工作分开,独立运作,不互相影响

P12
线程廉价:线程启动快,退出快,对系统资源冲击小,且线程彼此分享了大部分核心对象的拥有权。

P26
WinBASE.H -->CreateThread 产生一个线程

P33
1.多线程程序无法预期-->掌握多线程程序的预期是一项很高级的分析编程能力
2.执行次序无法保证
3.线程对于小的改变有高度的敏感-->调试器会引入不确定
4.线程并不是立刻启动
5.线程上的资源泄露更常见且更致命的
CloseHandle释放核心对象;GetExitCodeThread 线程结束代码;
ExitThread 强制结束线程

P47
主线程:Primary Thread
1.负责 GUI(Graphic User Interface)
2.该线程结束会使其它线程强迫结束、无从清理;

P48
经验显示,线程的各种相关函数是错误高危险群,而适当的错误处理可以阻止挫败并产生一个比较可信赖的程序;

P51
Microsoft Threading Model --> GUI Thread、Worker Thread

P53
设计目标:简单和安全、更甚于复杂和速度;
在线程之间以最低表面积来设计程序;
[最低表面积:线程共享的数据结构]

P59
成功秘诀:
1.各线程的数据要分离开来,避免使用全局变量;
2.不要在线程之间共享GDI对象;
3.确定你知道你的线程状态,不要径自结束程序而不等待它们的结束;
4.让主线程处理用户界面(GUI)

P62
绝不要在Win32中使用BusyLoop;
[应该用WaitForMultipleObjects\MsgWaitForMultipleObjects]
[控制自由触发Event为非自由触发Event,这样可以来避免系统的死锁]

P75
Unix的Signal-->立即的消息信号,有冲击
Windows Signaled Object -->内部状态的改变,无冲击

P94
在Windows系统中:PostMessage() 异步 并行;SendMessage() 同步 串行

P95
最容易的同步机制:Critical Sections
InitializeCritical Section \ DeleteCriticalSection \ EnterCriticalSection \ LeaveCriticalSection
[EnterCriticalSection 可嵌套]
[加上CriticalSection保护后可以实现数据操作的"原子化"]

P100
不要长时间锁住一份资源;而CriticalSection是不知道线程的生死的-->Mutex(互斥器)-->WaitForMultipleObjects

P108
Mutex-->核心对象,牺牲存储保证运行稳定;可跨线程\跨进程使用;Mutex有可能被"不负责"的线程"遗弃"。
[Mutex是Windows所特有,在其它系统中则使用Semaphore{旗语}

P120
EventObject(事件)-->最具弹性的通步机制;Event的状态完全在你的控制之下;可以精确地告诉一个Event对象做什么事,以及什么时候去作;其应用于多种类型的高级IO操作中。
CreateEvent\SetEvent\ResetEvent\PulseEvent

P128
同步机制摘要*

P132
TerminateThread -->放弃一个线程-->最糟糕的想法,永远不要这么用。

P135
Windows中终结Thread的标准操作方式:建立一个Event对象来自检Thread消亡,即周期性地检查一个Event对象,以决定要不要结束自己。

P140
优先级:线程的社会阶层

P157
Overlapped IO是一个高层技术:Windows给于我们具有Thread能力的IO函数

P198
要加上Volatile量来表明其在多线程下不能被编译器错误优化。
{对单线程有效优化对于多线程常带来BUG}

P200
{引入数据库的Transaction(事务)来解决多线程的数据一致性问题}
{侯捷先生的译著中这样的拓展译注很有用,且尊重出处,当效之}

P204
{在不考虑效率下,将锁定范围扩大会解决很多棘手问题。如将一个树型数据集根点锁定以避免其它线程的非法操作}-->排他锁定

P215
{作者给出一个Read/Write锁定方案,没看懂}
Fine Granularity{逐个锁}:很容易产生死锁、效率瓶颈的情况尚可令人放心、"锁定"所花费的时间可能招致高度浪费。 Coarse Granularity{全部锁}:使用简单、死锁风险极低、容易产生效率瓶颈。两者都是十分极端的。

P260
一个非静态的类成员函数都有一个隐藏起来的参数被推入堆栈中,当编译器需要处理类的成员变量时,它需要这个隐藏参数的帮助,这就是"this"参数(一个指针)

P261
为了以一个成员函数启动一个线程,要么你就使用静态成员函数,要么你就得使用C函数(而非C++成员函数)

P274
异常情况处理机制的好处在于,它在标准函数调用和返回(Call/Return)模式之外运作。

P277
如果要在MFC程序中产生一个线程,而该线程将调用MFC函数或使用MFC的任何数据,那么你必须以AfxBeginThread或CWinThread::CreateThread来产生这些代码

P278
面对Windows程序设计中的线程,有下列三种层次:
1.Win32API CreateThread() EndThread()
2.C Runtime Library _beginthreadex() _endthreadex()
3.MFC AfxBeginThread() {MFC方式封装了上述两种方法}

P286
{MFC给我们作了许多封装,在该页给出一个NakedVersion来说明封装细节}

P294
你不能放一个指针(指向一个CWnd)到结构中。而该结构被一个Worker线程使用。你也不能把一个指向CDialog或CView的指针交给另一个线程
{这是享受MFC方便同时的代价}

P300
虽然MFC的同步控制类对于基本结构的锁定很有帮助,但它们面对比较复杂的工作时却黯然失色。{这是享受MFC方便同时的代价}

P304
所有传送给某一个窗口之消息,将由产生该窗口之线程负责处理。{这是一条设计原则,这样可以使UI设计更清晰}

P311
欲在一个MDI程序中有效率地使用多个线程,我的建议是以一个线程处理所有的用户输入,以及所有用户界面的管理,然后使用一个以上的线程来负责诸如重绘、打印等工作。......你的主线程(负责主框架窗口)总是应该能够有所回应,不会陷入长时间计算的泥沼中。

P318
所有这些问题或许都可以绕道解决。但很明显我正在强迫系统做一些它并没有被设计那么做的事情。我越是想办法挖东墙补西墙,整个机制就越是变得脆而虚弱。......这个例子提醒我不适当使用线程所带来的危险。不幸的是,在"适当使用"与"不适当使用"之间,界限模糊。
{清楚了解系统内部机制是很重要的。不要用蛮力,要从内部挖掘,攻"心"为上}

P321
对于一个多线程程序调试,如果没有经过小心的策划,很可能就像抓蝴蝶一样。你可以看到问题,接近它,但是当你几乎已经就要用手抓着它时,它飞走了,然后在另一个地方出现。

P325
运转记录:程序全身插满printf();{要注意I/O是很慢的操作,这样就会对程序本身有冲击}

P336
进程可用来作为"隔板"机制。这有点像船舱被分割为防水壁和许多船壳一样。利用不同的进程所产生出来的边界,可以阻止错误到处蔓延。

P336
在同一进程的不同线程之间搬移数据,最简单的一种做法就是利用消息队列。......Windows定义了一个消息,名为 WM_COPYDATA,专门用来在线程之间搬移数据--不管两个线程是否同属一个进程,和其它所有的消息一样,你必须指定一个窗口,也就是一个 HWnd,当作消息的目的地。所以接受此消息的线程必须有一个窗口(译注:也就是它必须是个UI线程)

P339
进入C++世界,情况又更复杂些了,绝不可以把"指向某个拥有虚函数的对象"的指针当作lpData来传递{这样就很麻烦了,因为在MFC中,由于继承,多数对象都具有virtual的析构函数,如CString。这样就无法直接使用它们作为WM_COPYDATA的传递参数来在进程间传递数据。如此看来WM_COPYDATA局限性很大,是"戴着脚镣跳舞"呀}

P344
WM_COPYDATA是唯一一个可以在16位程序和32位程序之间搬移数据的方法;但其效率低,不能传递给没有窗体的线程,不支持PostThreadMessage

P345
Win32的一个基础观念就是,进程之间要有严密的保护。每一个进程认为它拥有整部机器,从一个进程中要看到另一个进程的地址空间的任何一部分,都是不可能的。

P346
所谓共享内存,是一块在设计时即打算给一个以上的进程在同一时间都看到的内存区域。

P346
设定一个共享内存区域(Shared Memory Area)需要两个步骤:
(1)产生一个所谓的file-mapping核心对象,并指定共享区域的大小-->CreateFileMapping
(2)将共享区域映射到你的进程的地址空间中。-->MapViewofFile/UnmapViewofFile-->OpenFileMapping/CloseHandle

P354
任何Collection Class(译注:用于array、list、map之C++类),不论来自MFC或其它地方,都不能够安全地使用于共享内存中。

P361
使用共享内存不是件容易的事{对于多线程,也是一样}

Delphi开启Windows目录对话框代码

今天整理以往的项目代码,发现自己以前在网上找到的一段Delphi开启Windows目录对话框的代码,觉得对许多Delphi初学者很有用,特转贴出来。由于时间过久已经无法知道是从什么地方摘录的,在此特向原作者表示感谢!

Windows目录对话框是一个标准的WindowsUI控件,其可以列出一个目录列表,并且可以显示新增按钮。由于Delphi中并没有提供对于该控件的封装,所以新手(包括当年的我)大多使用Win31目录下的DriverList、DirectoryList、FileList和 FileFilterList四个控件进行组合来获取当前目录,这样操作复杂且不美观。有的高手可以直接用WindowsAPI调用Windows目录对话框,但我确直到找到该段代码前还是使用最原始的方法{叹自己的懒惰呀!}

该段代码分为两个部分:(1)控件代码 (2)调用代码
(1)控件代码:由于该控件没有封装成Delphi控件格式,其使用方法有些原始,就是将它的源文件BrowseForFolderU.pas复制到当前项目目录并添加到当前项目中。代码如下{是别人高手写的,致敬呀......}
unit BrowseForFolderU;

interface
function BrowseForFolder(const browseTitle: String;
const initialFolder: String = ''): String;

implementation
uses Windows, shlobj;
var
lg_StartFolder: String;

///////////////////////////////////////////////////////////////////
// Call back function used to set the initial browse directory.
///////////////////////////////////////////////////////////////////
function BrowseForFolderCallBack(Wnd: HWND; uMsg: UINT;
lParam, lpData: LPARAM): Integer stdcall;
begin
if uMsg = BFFM_INITIALIZED then
SendMessage(Wnd,BFFM_SETSELECTION,1,Integer(@lg_StartFolder[1]));
result := 0;
end;

///////////////////////////////////////////////////////////////////
// This function allows the user to browse for a folder
//
// Arguments:-
// browseTitle : The title to display on the browse dialog.
// initialFolder : Optional argument. Use to specify the folder
// initially selected when the dialog opens.
//
// Returns: The empty string if no folder was selected ( i.e. if the
// user clicked cancel), otherwise the full folder path.
///////////////////////////////////////////////////////////////////
function BrowseForFolder(const browseTitle: String;
const initialFolder: String =''): String;
const
BIF_NEWDIALOGSTYLE = $40;
var
browse_info: TBrowseInfo;
folder: array[0..MAX_PATH] of char;
find_context: PItemIDList;
begin
FillChar(browse_info,SizeOf(browse_info),#0);
lg_StartFolder := initialFolder;
browse_info.pszDisplayName := @folder[0];
browse_info.lpszTitle := PChar(browseTitle);
browse_info.ulFlags := BIF_RETURNONLYFSDIRS or BIF_NEWDIALOGSTYLE;
if initialFolder <> '' then
browse_info.lpfn := BrowseForFolderCallBack;
find_context := SHBrowseForFolder(browse_info);
if Assigned(find_context) then
begin
if SHGetPathFromIDList(find_context,folder) then
result := folder
else
result := '';
GlobalFreePtr(find_context);
end
else
result := '';
end;

end.

(2)调用代码:和一般函数调用很类似。先在调用窗口pas文件前引用一下该控件文件
uses
 BrowseForFolderU;
然后是"八股"代码
procedure TForm1.BtnSampleExecute(Sender: TObject); //演示按钮Click事件函数
var
oPath,oPath1,outMsg:String;
begin
oPath1 := 'c:\';//目录对话框的初始目录
outMsg := 'Windows目录对话框演示';//目录对话框上显示的提示信息
oPath := BrowseForFolder(outMsg,oPath1);//启动Windows目录对话框,一句话就解决了。
if oPath = '' then //如果返回地址为空,则报错
begin
outMsg := '你没有选取任何目录!';
Application.MessageBox(PChar(outMsg),'WARNING',0);
Exit;
end;
else //否则由oPath中提取选取目录地址,进行操作
begin
   //你的操作代码
end;
end;

就这么简单咯,希望能给Delphi初学者一点帮助。

Delphi中使用JPEG图片

这是个"过期"技术问题,我仅作为存档贴出来。以备日后参考。
Delphi中读取JPEG图片可使用TJPEGImage对象,样例代码如下:
var
   jpeg :TJpegImage ;
begin
   jpeg := TJPEGImage.Create;
   try
    jpeg.LoadFromFile(FileName);
    Image1.Canvas.Draw (0,0,jpeg);
   finally
    jpeg.Free;
   end;
end

Delphi中压缩文件

Delphi中压缩文件的最好方式是使用ZipTV控件。其样例代码如下:
procedure TForm1.Button1Click(Sender: TObject);
var
  FileCompressed,I: Integer;
  FileName:String;
begin
  if SaveDialog1.Execute then
  begin
    Zip1.ArchiveFile := SaveDialog1.FileName;
    Zip1.DateAttribute := daFileDate; // default value
    Zip1.StoredDirNames := sdRelative; // default value
    Zip1.CompressMethod := cmDeflate; // default value

    Zip1.FileSpec.Clear ;
    for I := 0 to ListBox1.Items.Count - 1 do // Iterate
    begin
        Zip1.FileSpec.Add(ListBox1.Items[i]);
    end; // for

    FileCompressed := Zip1.Compress;
  end;
end;

VBA小试牛刀

今天在看一本电子书《诸葛亮的十堂哲学课》时,突然想将其重新编辑成PDF格式。当我将书的第一章内容复制到Word中时才发现字符中混杂了太多的回车符。这时我想起前几天学习的VBA课程,在这里小试牛刀咯。
下面就是我写的一段VBA程序,可以将文本中的多余空行删除。其算法是逐行扫描文档,当发现该行只有一个符号(必为回车符)时就将其删除。
iCount = 0
Dim ps As Paragraph
Dim rngRange As Range
For Each ps In ActiveDocument.Paragraphs
iCount = iCount + 1
Set rngRange = ps.Range

If rngRange.Characters.Count = 1 Then
'MsgBox (rngRange.Characters(1))
rngRange.Delete
End If
Next

但我还没想出如何能删除段末尾的回车符,我不知道如何判断,虽然可以通过
If rngRange.Characters(rngRange.Characters.Count) = Chr(13) Then
来判断段落结尾处的字符是否是回车符,但如果简单删除,则会将原有的段落去除。且这样会引起循环的错误,在进行段落遍历的时候产生错误。
我想到可以用一个数组来存放单个回车符的位置,然后利用原文档中段落间有两个回车符的特点,将单回车符删除就可以了。
下面是代码,其可以在短文档中用,在长文档时还是会有数组溢出。要重新考虑算法咯。
Sub ClearBlankReturnChar()
Dim iCount, iIndex, iParagraphsNum
iCount = 0
iIndex = 0
Dim aLines(10000)

Dim ps As Paragraph
Dim rngRange As Range
For Each ps In ActiveDocument.Paragraphs
iCount = iCount + 1
Set rngRange = ps.Range
If rngRange.Characters.Count > iMaxRangeCount Then
iMaxRangeCount = rngRange.Characters.Count
End If

If rngRange.Characters.Count > 1 Then
iIndex = iIndex + 1
aLines(iIndex) = rngRange.End
End If
Next

For i = 1 To iIndex - 1
Selection.SetRange aLines(i) - i, aLines(i) - i + 1
'MsgBox (i)
Selection.Delete
Next
End Sub

夜已经很深了,早点睡咯!2006年8月13日

WindowsXP定时关机脚本

我以前写得一个脚本,每次重装机器都要用到,现贴在这里,以免下次重装要到备份光盘里找!
at 22:00 /every:m,t,w,th,f,s,su Shutdown -s -f

删除CVS版本控制目录

今天要将一个Dreamweaver制作的网站进行更新,但由于这次采用CVS来管理网站源文件,所以在系统目录中混入了许多CVS子目录。更新时因为这些细碎的文件夹造成FTP上载速度很慢,于是想到可以先复制一个目录然后采用批处理脚本删除其中所有的CVS文件夹。参考Zeali的Blog(http://www.zeali.net/entry/351 )中的BAT批处理文件,编写如下的批处理文件 ClearCVS.bat。
@echo On
@Rem 删除CVS版本控制目录
@PROMPT [Com]#
@echo Find CVS
@for /r . %%a in (.) do @if exist "%%a\CVS" @echo "%%a\CVS"
@echo Find CVS Dir....OK
@pause
@for /r . %%a in (.) do @if exist "%%a\CVS" rd /s /q "%%a\CVS"
@echo Clear CVS Dir Mission Completed
@pause


而且我发现Dreamweaver也会在站点目录中存放许多_notes目录,参考上面的脚本再编写一个新的ClearNotes.bat如下
@echo On
@Rem 删除_notes目录
@PROMPT [Com]#
@echo Find _notes
@for /r . %%a in (.) do @if exist "%%a\_notes" @echo "%%a\_notes"
@echo Check _notes Dir ....OK
@pause
@for /r . %%a in (.) do @if exist "%%a\_notes" rd /s /q "%%a\_notes"
@echo Clear _notes Dir Mission Completed
@pause


这样就可以轻松解决目录冗余的问题了。
=======================================================================
我编写了一个Python程序,也可以完成清除CVS目录的工作
# yankchina@gmail.com
# Clear CVS Folder
def ClearCVSFolder( inFolderName ):
''' Clear CVS Folder
'''

import os
for root, dirs, files in os.walk( inFolderName , topdown = False ):
for name in dirs:
cvsname = root +'\\' + name + '\\cvs\\'
if os.path.isdir( cvsname ):
os.remove( cvsname )
print 'Remove CVS DirectoryName:' + cvsname

# InputMain Function
def inputMain():
import sys
if len(sys.argv)>=2:
currentDir = sys.argv[1]
else:
print "Please Input Folder Name: "
currentDir = raw_input()
print "Clear CVS Label Folders"
ClearCVSFolder(currentDir)
print "Any Key To Return..."
currentDir = raw_input()

if __name__ == "__main__":
inputMain()

2007年1月29日星期一

微软课程学习笔记

2006 八月 05, 10:34 上午
Word下的VBA学习笔记
=======================================================================
参考陈锐《Word下的VBA开发》
=======================================================================
1.VBA简介:一个内嵌于Office的编程扩展。要了解VB/VBA/VBScript之间的关系。
2.采用MS的VBASDK可以在自己的软件中增加VBA控制。
3.最简单的VBA操作就是录制宏。在录制宏时,会"过忠实"地保留你的操作,产生大量冗余代码,这要求我们来进行一些清除工作。
4.VBA的资源以Project(工程方式)进行管理,包括模版\代码等
5.命令窗口可快速执行代码,并可以输出一些感兴趣的内容。如
?ActiveDocument.Tables.Count
可直接在命令窗口中直接输出结果
6.建立一个新的VBA可以从录制宏开始。这里可以做一个将该文件中的所有表格的第一个行进行合并,然后文本居中加粗。在录制宏后,可以立即阅读宏记录,如果遇到不能理解的对象或操作,可以用Ctrl+F1调出快捷帮助来。下面是一段样例代码:

'yankchina@gmail.com 2006-08-05
'陈锐《Word下的VBA开发》
Dim t As Table
For Each t In ActiveDocument.Tables
'循环操作表格
t.Rows(1).Cells.Merge
t.Rows(1).Cells.Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
t.Rows(1).Cells(1).Range.Bold = wdToggle
Next

在VBA中点选不同的过程,即只运行该过程。
7.Word对象模型是掌握VBA操作的基本知识,而且要有OOP的知识结构。
8.在Word中创建一个Office对象,可以使用
Set olApp = CreateObject("outlook.application")
来创建,陈锐的例子都是很棒的,这个例子可以根据Word中的表格来创建Outlook联系人。我可以根据这个例子来创建一个VBA脚本,来在Word中 创建一个Outlook的联系人表。也可以在Outlook中编写VBA来自动根据Word中的表格来发送Email信息。
=======================================================================
我要收集一些自己在日常工作中的自动化需求,然后用VBA或.Net方式来编程实现
=======================================================================
《程序员2006》学习笔记
=======================================================================
这是微软WebCast上一期节目,回眸2005,展望2006。
1.微软新的认证体系
2.Microsoft网站上有一个ScoreReportAndLearningPlan,这样可用来进行个人技能检测。
3.Microsoft E-Learning 免费的培训学习资料/视频。
=======================================================================
《VisualStudioTeamSystem情景演示》学习笔记
=======================================================================
1.方法论+工具
(1)有法可依:XP、RUP、MSF...
(2)有法必依:工具:BugManagerSystem、SourceControl
(3)执法必严:集成,各种工具集成工作
(4)违法必究:企业管理制度
2.可以下载Microsoft的MSF文档来学习一些微软的项目开发经验。
3.在VSTS中有MSF的模版,这样可以自动组织到CMM3水平。这说明在微软中的软件开发系统已经是完全信息化了。(目前我看到的许多软件开发团队和开发项目其仍然是手工化管理,好奇异的事情,不自动的软件系统来创建自动项目)
4.在VSTS中用Excel来管理项目需求(Title/Kind/State/Priority),可要参考MSF文档来确定其结构。
5.将Excel需求导入Project,在Project中进行工程安排。记得上次学习Project可以直接导入Excel文件的。
6.架构师主要完成架构设计,并且应向代码设计人员下发逻辑实现。
7.项目经理可以通过VSTS来访问察看项目进度,也可以从团队网站中进行察看。
8.VisualStudio2005中的类图更契合.NET语言的特性。
9.NUnit一个成员加入MS,这样在VSTS中会生成NUnit单元测试框架。且其可以通过代码的背景来看到测试的结果,而不是某个测试函数是否测试通过(很Cool的单元测试)。
10.VSTS中可以使用Shelve方式将代码挂起在服务器端。
11.VSTS中可对代码进行统计分析。并可以通过自定义规则方式来检查团队代码的命名规则。
12.在VSTS中有LoadAndStressTest/ManualTest/UnitTest/WebTest等不同测试项目。且提供了虚拟测试环 境,这样就可以构建Win98/Win2000/WinXP等不同软硬件环境下的测试。可以方便地生成图表,找到软件BUG和瓶颈。
13.MSF4.0包含的轻快开发软件进程是我当前要学习的重点。

细线条框表格的CSS代码

今天朋友问我如何制作一个细线条框。在以前的方法是通过两个嵌套表格来实现细线条表格的制作,但这种方法与W3C网络标准不符,所以应该采用css的方法来制作单线条框。原理是利用定义对象上下左右边线,内部的单元格绘制上边和左边,外部的表格绘制下边和右边。具体CSS代码如下:
<style type="text/css">
<!--
.frameTable {
border-right-width: 1px;
border-bottom-width: 1px;
border-top-style: none;
border-right-style: solid;
border-bottom-style: solid;
border-left-style: none;
border-right-color: #000000;
border-bottom-color: #000000;
}
.frameTable td{
border-top-style: solid;
border-top-width:1px;
border-left-width:1px;
border-top-color: #000000;
border-right-style: none;
border-left-style: solid;
border-left-color: #000000;
border-bottom-style: none;
}
-->
</style>

调用的方法如下:
<table width="900" border="0" cellspacing="0" cellpadding="0" class="frameTable">
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
</table>