Google

星期二, 四月 21, 2009

用rsync对网站进行镜像备份(转)

用rsync对网站进行镜像备份

by inburst
http://xfocus.org

对系统管理员来说,平时的工作重心应该集中在维护系统正常运转,能够正常提供服务上,这里往往牵涉到一个数据备份的问题,在我所了解

的情况中,有80%的系统管理员不是太关心自己服务器的安全性,但往往对备分镜像的技术相当感兴趣,但由于商业产品的软硬件价格都相当高

昂,因此往往会选择自由软件。这里准备介绍的rsync就是这样的软件,它可以满足绝大多数要求不是特别高的备份需求。
http://www.xfocus.net/articles/200107/214.html

标签: , , , ,

星期二, 三月 03, 2009

实战集体智慧

〈集体智慧编程〉(即 Programming Collective Intelligence: Building Smart Web 2.0
Applications
)还没读完,Collective
Intelligence in
Action又来了。事实上,这本书目前在日本的书泉等书店也是作为计算机版块主推书而出现,书名翻译为<集合知プログラミング>。
Collective Intelligence in Action的前言介绍为

There's a great deal of wisdom in a crowd, but how do you listen to a
thousand people talking at once? Identifying the wants, needs, and knowledge
of internet users can be like listening to a mob.

In the Web 2.0 era, leveraging the collective power of user contributions,
interactions, and feedback is the key to market dominance. A new category of
powerful programming techniques lets you discover the patterns,
inter-relationships, and individual profiles—the collective
intelligence—locked in the data people leave behind as they surf websites,
post blogs, and interact with other users.

*Collective Intelligence in Action* is a hands-on guidebook for implementing
collective-intelligence concepts using Java. It is the first Java-based book
to emphasize the underlying algorithms and technical implementation of vital
data gathering and mining techniques like analyzing trends, discovering
relationships, and making predictions. It provides a pragmatic approach to
personalization by combining content-based analysis with collaborative
approaches.

This book is for Java developers implementing collective intelligence in
real, high-use applications. Following a running example in which you
harvest and use information from blogs, you learn to develop software that
you can embed in your own applications. The code examples are immediately
reusable and give the Java developer a working collective intelligence
toolkit.

https://groups.google.com/group/pongba/browse_thread/thread/f0d6aaa5e3eb19da?pli=1

标签:

星期二, 一月 06, 2009

[学习]Delphi控制并行端口位操作

并行端口简称并口,它有3个端口:数据端口、状态端口、控制端口,常用的并口为LPT1,它的3个端口的地址分别为:378H、379H和37AH。

  一、并口读写

  在汇编语言中,可以用 in、out 指令操作并口,而在Delphi中并没有相对应的函数、方法可对并口进行读写,幸运的是Delphi可以嵌入汇编程序,通过直接嵌入汇编指令 in、out 可方便地对并口进行读写。我们还可以通过调用 Windows API 函数或第三方提供的DLL、VXD来访问并口,但通过使用嵌入汇编的方法对并口进行读写更方便、快捷。

  使用下面的 ReadPort 函数和 WritePort 过程可以读写并口,参数 Port 为要操作的端口地址。

function ReadPort(Port:WORD):BYTE;

var

B:BYTE;

begin

ASM

MOV DX, Port;

IN AL, DX;

MOV B, AL;

END;

Result:=B;

end;

procedure WritePort(Port:WORD;ConByte:BYTE);

begin

ASM

MOV DX, Port;

MOV AL, ConByte;

OUT DX, AL;

END;

end;

二、位操作

  要按位来控制并口,可以先读取并口的数据,再进行位操作,最后再重新写入并口,就可以实现对并口的位的控制。

  逻辑运算符and对两个要操作的数执行按位的逻辑与运算:即中有1“与”1的结果为1,其它的0“与”1、1“与”0、0“与”0的结果都为0。

  逻辑运算符or对两个要操作的数执行按位的逻辑或运算:即只要相“或”的两位有一位是1,结果就为1;否则“或”的结果为0。


  使用and运算符可以对指定的位置0,例如:十六进制84H的二进制为:10000100,它的第三位为1,若要将第三位置为0,且其它位不变,可以使用:$84 and $FB = $80,80H的二进制值为10000000。

  使用or运算符可以对指定的位置1,例如:十六进制84H的第二位为0,若要将第二位置为1,且其它位不变,可以使用:$84 or $02 = $86,86H的二进制值为10000110。

  例子:

  1、将数据端口378H的D2位的电位设置为低,即置0:

B:=ReadPort($378);

B:=B and $FB;

WritePort($378,B);

  2、将数据端口378H的D2位的电位设置为高,即置1:

B:=ReadPort($378);

B:=B or $04;

WritePort($378,B);

  3、判断数据端口378H的D2位的电位高低:

B:=ReadPort($378);

if ((B and $04)=$04) then

//电位为高时的代码

else

//电位为低时的代码

或:

B:=ReadPort($378);

if ((B or $FB)=$FF) then

//电位为高时的代码

else

//电位为低时的代码

  三、具体实现

  下面的例子是控制并口的数据端口378H的各个位的电位高低。数据端口的8个位:D0~D7分别对应并行接口的2~9脚,关于并行接口其它的引脚的说明就查看相关资料,这里就不多说了。

  首先运行 Delphi,新建一个工程,按一下F12在Form1的单元文件Unit1中加入读写端口的代码:

function ReadPort(Port:WORD):BYTE;

procedure WritePort(Port:WORD;ConByte:BYTE);

function ReadPort(Port:WORD):BYTE;

var

B:BYTE;

begin

ASM

MOV DX, Port;

IN AL, DX;

MOV B, AL;

END;

Result:=B;

end;

procedure WritePort(Port:WORD;ConByte:BYTE);

begin

ASM

MOV DX, Port;

MOV AL, ConByte;

OUT DX, AL;

END;

end;


  添加8个 CheckBox 组件,修改它们的 Caption(标题)属性分别为 D0 到 D7 ,将它们按右到左进行排列好。

  双击 CheckBox1,在CheckBox1组件的OnClick(单击)事件中加入以下的程序代码:

procedure TForm1.CheckBox1Click(Sender: TObject);

var

b:BYTE;

begin

b:=0;

if CheckBox1.Checked then

b:=b or $01;

if CheckBox2.Checked then

b:=b or $02;

if CheckBox3.Checked then

b:=b or $04;

if CheckBox4.Checked then

b:=b or $08;

if CheckBox5.Checked then

b:=b or $10;

if CheckBox6.Checked then

b:=b or $20;

if CheckBox7.Checked then

b:=b or $40;

if CheckBox8.Checked then

b:=b or $80;

WritePort($378,b); //写数据端口

end;


  输入完成后,把CheckBox2到CheckBox8这7个CheckBox组件的OnClick事件设置为CheckBox1的OnClick事件:CheckBox1Click。

  此时编译运行程序,已经可以通过点击这8个CheckBox来控制LPT1的数据端口的各个位的电位高低了。

  下面再加入监控并口的数据端口状态的功能。

  在Form1中加入一个Timer组件:Timer1,修改它的Enabled属性为False,Interval属性为1。

  在Timer1的OnTimer事件中加入:

procedure TForm1.Timer1Timer(Sender: TObject);

var

B:BYTE;

begin

B:=ReadPort($378); //读数据端口

CheckBox1.Checked:=((B or $FE)=$FF);

CheckBox2.Checked:=((B or $FD)=$FF);

CheckBox3.Checked:=((B or $FB)=$FF);

CheckBox4.Checked:=((B or $F7)=$FF);

CheckBox5.Checked:=((B or $EF)=$FF);

CheckBox6.Checked:=((B or $DF)=$FF);

CheckBox7.Checked:=((B or $BF)=$FF);

CheckBox8.Checked:=((B or $7F)=$FF);

end;


  再加入一个CheckBox组件,修改的Caption属性为“监控并口”,并在它的OnClick事件中加入:

procedure TForm1.CheckBox9Click(Sender: TObject);

begin

Timer1.Enabled:=CheckBox9.Checked;

end;


  编译运行程序,点击“监控并口”,就可以监控并口LPT1数据端口378H的状态,并可以实时地修改它的状态。

  为了方便查看、验证数据端口378H的状态,我做了一个小小的并口测试电路,该电路使用了一个打印接口、8个LED(发光二极管)和8个1K的电阻,连接线路如图所示:




  按照电路图制作完成后,安装到电脑的并口上,运行编写好的程序就可以方便地查看数据端口378H的各个位的电位高低了。

  最后,我们再来做一个走马灯实验。

  先声明一个全局变量 tb:在“Form1:TForm1”的下面加上“tb:BYTE”:

var

Form1: TForm1;

tb:BYTE;

  再在Form1中加入一个Timer和一个CheckBox,修改Timer2的Enabled属性为False,修改Interval属性为300,双击Timer2,在它的OnTimer事件中加入:

procedure TForm1.Timer2Timer(Sender: TObject);

var

B:BYTE;

begin

if tb=0 then

tb:=1

else

tb:=tb * 2;

WritePort($378,tb);

B:=ReadPort($378);

CheckBox1.Checked:=((B or $FE)=$FF);

CheckBox2.Checked:=((B or $FD)=$FF);

CheckBox3.Checked:=((B or $FB)=$FF);

CheckBox4.Checked:=((B or $F7)=$FF);

CheckBox5.Checked:=((B or $EF)=$FF);

CheckBox6.Checked:=((B or $DF)=$FF);

CheckBox7.Checked:=((B or $BF)=$FF);

CheckBox8.Checked:=((B or $7F)=$FF);

end;

  修改CheckBox10的Caption属性为“走马灯演示”,再双击CheckBox10,在它OnClick事件中加入:

procedure TForm1.CheckBox10Click(Sender: TObject);

begin

Timer2.Enabled:=CheckBox10.Checked;

end;


  编译运行程序。




  点击“走马灯演示”,有没有看到“走马灯”的效果?通过修改Timer2的Interval可以调节速度,更多、更Cool的效果就看你的创意了。

  注意:以上嵌入汇编访问并口的方法只能在Win9X下使用,若要在WinNT/2K下访问并口应该使用 Windows API 函数或专门读写并口的DLL、VXD。

  以上程序在Win98+Delphi6.0下测试通过

http://www.yesky.com/20020801/1623061_1.shtml

标签: , ,

星期二, 三月 11, 2008

wxWidgets.Book简体中文翻译版发布

wxWidgets是开源的图形界面库,原生C++编写跨平台的应用程序的理想库。支持绝大多数C++编译器和多种桌面操作系统及部分嵌入式操作系统。
翻译者:wesleywang
下载地址:
http://cnwesleywang.googlepages.com/wxWidgets.pdf
http://www.woodpecker.org.cn/share/doc/Python/_PDF/wxWidgets_061031.pdf
非常感谢
wesleywang付出自己的时间和心血为开源软件做出贡献。


http://blog.csdn.net/DonJikn/archive/2006/11/03/1363831.aspx

或者在这里下载(仅能用工具下载)
http://www.fairybean.com/downloads/wxWidgets_061031.pdf

标签: ,

[学习]wxWidgets学习笔记之一:使用mingw在Windows下安装和编译wxWidgets

最近想做个类似Yahoo Widget的开源软件,为了使得软件可以方便的移植到不同的OS上,所以选择了wxWidgets来搭建软件的整个UI部分。由于第一次使用 wxWidgets,所以我找了本书《Cross-Platform GUI Programming with wxWidgets》来学习,在这个BLOG中我会定期将学习笔记贴出来与大家一起分享。
另外既然是做开源软件,那么我觉得至少所使用的编译器和IDE都应该是免费的:)(Windows就没办法了,因为我还要使用它来打魔兽世界=_=b),所以选择了mingw+GCC和Eclipse CDT作为这个软件的Compiler和IDE。
首先在这里先总结下如何安装和编译wxWidgets:

1、安装
在http://www.wxwidgets.org/downloads/下载最新的wxWidgets版本,目前最新版本是2.8.0

2、编译
在http://www.mingw.org/download.shtml下载以下必备的工具:
  • gcc-g++-3.4.2-20040916-1.tar.gz
  • gcc-core-3.4.2-20040916-1.tar.gz
  • mingw32-make-3.80.0-3.tar.gz
  • mingw-runtime-3.9.tar.gz
  • mingw32-make-3.80.0-3.tar.gz
  • binutils-2.15.91-20040904-1.tar.gz
  • w32api-3.6.tar.gz
下载完成后,首先将他们拷贝到同一目录下,并将它们解压在同一目录下面(例如我的mingw安装目录是d:\mingw,那么需要将这7个压缩文件全部直接在d:\mingw解压)。然后设置环境变量确保在PATH环境变量中包含"d:\mingw\bin",最后可以打开DOS控制台输入gcc来验证环境变量是否设置正确。如果你的DOS控制台显示“no input file”,那么恭喜你,你的mingw环境已经设置好了

mingw环境准备好后,我们开始编译wxWidgets。首先进入wxWidgets安装目录(我的目录是D:\wxWidgets-2.8.0 \wxWidgets-2.8.0),由于是在Windows下面编译,所以需要进入到wxWidgets Windows版本的目录(我的机器是
D:\wxWidgets-2.8.0\wxWidgets-2.8.0\build \msw),在D:\wxWidgets-2.8.0\wxWidgets-2.8.0\build\msw目录中包含了VC6.0、GCC、 WATCOM C++和DMC四个编译器的编译脚本,我使用GCC来编译的所以只关注config.gcc和makefile.gcc两个文件。这里要说下这两个文件的 用途:
1、config.gcc文件:包含了编译wxWidget的一些可选参数,常用的有以下几个:
  • MONOLITHIC:是否将wxWidget编译成一个大的库文件,还是将其编译成多个不同的库文件。这里我设置为1,将wxWidget编译为一个大的库文件,这样以后编写程序时会方便很多。
  • UNICODE: 是否使用UNICODE字符。建议将这个参数设置为1,这样程序在NT/2000/XP下运行就不需要进 行ANSI到UNICODE之间的转换,提升程序运行效率。但95/98/me不支持UNICODE,所以在编译程序时需要加入在LIB参数中加入 unicows.lib即可
  • MSLU:在编译UNICODE版本时候是否使用MSLU(Microsoft Layer for Unicode on Windows 95/98/ME Systems)
2、makefile.gcc文件:这个没什么特别的,就是gcc的make文件了


OK一切准备好之后就可以进行编译了。进入D:\wxWidgets-2.8.0\wxWidgets-2.8.0\build\msw目录,输入 mingw32-g++ -f makefile.gcc,然后泡杯茶听听歌慢慢等吧,编译的时间很长呢:(。编译好后在D:\wxWidgets-2.8.0\wxWidgets- 2.8.0\lib\gcc_dll目录下生成了wxWidgets的DLL文件和A文件,接下来我们就可以使用这个编译好的wxWidgets库来编写 我们的程序了

http://blog.csdn.net/jatom/archive/2007/02/04/1502187.aspx


# shada 发表于2007-09-09 15:25:24 IP: 220.203.1.*
mingw32-g++ -f makefile.gcc
这样不行,错误如下:
makefile.gcc: file not recognized: File format not recognized

修改为如下:
mingw32-make -f makefile.gcc
这样就好了。

标签: , , ,

星期四, 一月 24, 2008

Apcmag推出一月开源游戏TOP5

有没有想过免费的开源游戏同商业游戏一样的精彩?并且还能够根据您的需要度身订制哦。Apcmag网站为我们带来了2008年1月开源游戏TOP5,下面请您随我一起来看看。

  1. 《神秘玛莉历代记》(Secret Maryo Chronicles)

  让大家重温了美好时光,前身是“Super Mario Clone”,经典游戏Mario Brother的克隆版,基于SDL和OpenGL函数库让它可以在各个平台运行,同时这款游戏还有重绘的完整图像,新编曲的音乐。用户可以下载关卡编辑器,制作自己的关卡,如果反应不错就有可能整合进入官方版本。

  最新版的下载地址为:http://www.secretmaryo.org/index.php?page=new_1_4&sid=

  2. 《不明飞行物:异形入侵》(UFO: Alien Invasion)

  游戏发生在2084年,你控制了一个秘密组织来保卫地球,抵御外来敌人。建立你的基地,准备你的团队。这是一个小队战术策略游戏,结合军事写实与科幻学小说风格来描写外星人侵略。精心建造各种系统,控制你的小队。

  两个主要的游戏模式: geoscape模式和战术模式。

  在geoscape模式中,游戏主要任务是基地管理与策略——管理活动和财务状况,控制基地、设施、飞机、武装反应部队。研究新的技术来对抗外星人。启动您的拦截战机击落不明飞行物,并派遣dropships针对外星人的活动。你可以建造、购买、生产、geoscape模式有专门按键来控制时间的流逝速度和地球的转速。

  在战术模式中,指挥你的小队参与各项任务。战术模式采用了转动系统,而您的团队和外星人轮流做出动作。轮到你时,你可以命令你的部队四处走动,消防手中武器,投掷手榴弹或使用其他设备等,每名军人获得一定数量的时间单位。一切行动,在战术模式需要时间单位执行。
如果没有你,人类将陷于厄运。

  该游戏支持局域网或者英特网对战,最多支持6个玩家。

  3. 《Freeciv》

  是著名的回合策略游戏CivilizATIon(文明)的开源版本,其规则构架与《文明2》基本相同,并在游戏画面,操作和联网上作了许多改进。并且有简体中文版的呦。

  最新版的下载地址为:http://prdownloads.sourceforge.net/freeciv/Freeciv_2.1.2_OSX_SDL_UB.dmg?download

  4. 《Alien Arena》

  Alien Arena是一款第一人称3D射击游戏,基于id Software的QuakeII引擎,id在2001年以GPL许可协议开源,新版的Alien Arena增加了七个竞技场,拥有更好的武器,以及新的1vs1决斗模式。Linux.com称其为目前最好的开源游戏。

  最新版的下载地址为:http://red.planetarena.org/

  5. 《韦诺之战》(Battle for Wesnoth)

  韦诺之战是一款主要基于Python语言的回合制SLG游戏,使用SDL作为图形引擎,目前已经一直到了Windows、Linux、FreeBSD和Mac OS X等多种操作系统上,有16种不同部族,可以联机,最多支持8人在线。

  最新版的下载地址为:http://www.wesnoth.org/wiki/Download#Development_.281.3_branch.29

http://gamedev.csdn.net/page/72caa157-e464-4373-9314-af2c2c4dd7b6

标签: ,

星期二, 十二月 18, 2007

Learning Assembler with Delphi 1

Abstract
The purpose of this article is to fill some of the gaps left by the original documentation provided with Borland's Delphi Developer, however, at the time of writing, all of the code and concepts discussed herewithin are fully applicable to all variants of Delphi. This article was originally written in 1997.
The Principle area of discussion will be the use of assembler within Object Pascal. Nevertheless, other aspects of programming will be covered in passing, as required for the examples given.
For simplicity, only a subset of Intel's instructions will be introduced, thus enabling a more generalized approach with few special cases. Furthermore, all the examples of assembler given will be contained in Pascal wrappers with portability in mind. Issues such as File access in assembler will not be covered, as this is more reliably achieved using standard Object Pascal. Indeed the document will seek to emphasis that one should only use assembler when necessary.
In general, the document will take the following form. First an idea will be introduced, followed immediately by a relevent example, concluding with an explanation of greater depth. This format is abandoned where clarity demands.

Using Assembler in Borland's Delphi
Before we start, I should like to state the level of knowledge which I shall assume of the reader. Writing Object Pascal should be second nature. One should be familiar with Delphi's built in debugging facilities. Finally a general understanding of what is meant by terms such as instantiation, null pointer and memory allocation, is a must. If any of the above encourages feelings of doubt, please tread very carefully. Furthermore, only 32bit code will be discussed, and as such Delphi 2.0 is a necessity.

Why use Assembler?
I have always found Object Pascal to produce fast and efficient code, add to this the Rapid Development Environment of Delphi, and the need to use assembler becomes questionable. In all of my work with Delphi, I have come across just two situations where I have felt one should consider the use of low level code.

(1) Processing large quantities of data. Nb. I exclude from this any situation where a data query language is employed. For reasons of compatibility one should not tinker.

(2) High speed display routines. Nb. I refer here to quick easy routines that sit well with pascal, not the esoteric C++ headers, external function libraries and hardware demands of DirectX.

I hope to introduce an example or two by the end of this article which meet the above criteria, and in doing so demonstrate not only how and when to use assembler, but also the seamless manner in which Delphi incorporates this code.

What is Assembler?
I will assume that you have a rough idea of how a cpu goes about it's business. Basically we have a fancy calculator with a big memory. The memory is no more than an ordered sequence of binary digits arranged in blocks of eight bits, each forming a byte. Thus each byte can store an integer in the range of 0 to 255, and each byte's position in the sequence of memory gives it an unique address by which the cpu may change or recover it's value. The cpu also has a number of registers (you may like to think of these as global variables) with which to play. For example eax,ebx,ecx and edx are the general 32bit registers and throughout this article, these are the only ones we shall use. This means the largest number we can store in register eax is 2 to the power 32 minus 1, or 4294967295, for those of us brought up on 8bit machines, this is shear luxury.
The cpu has the ability to manipulate the values of the registers, so to add 10 to the value of eax, one would issue the hexadecimal operation code
05/0a/00/00/00
this is machine code, there being a specific 'number' for each function the cpu can implement. To say that writing machine code is tedious would be an understatement, and as for debugging ! Assembler Language is just an easy way of remembering what machine code operations are available. The job of converting to machine code is done by an Assembler. Borland's Turbo Assembler is built in to Delphi, and is rather more pleasant to use. If we look again at adding 10 to eax, the appropriate assembler instruction is
add eax,10 {a := a + 10}
Similarly, to subtract the value of ebx from eax
sub eax,ebx {a := a - b }
To save a value for a rainy day, we can move it to another register mov eax,ecx {a := c }
or even better, save the value to a memory address
mov [1536],eax {store the value of eax at address 1536}
and of course to retrieve it
mov eax,[1536]

A quick aside on memory addresses, please bear in mind the size of the values you are moving about. The mov [1536],eax instruction affects not only memory address 1536, but 1537,1538 and 1539 as well, because as you will recall eax is 32bits long, or rather 4 bytes. Memory is always addressed in bytes.

What does the Compiler do with all those variables ? In every program you will have written, the compiler will have had to cope with a number of variables.Consider the pascal line
Count := 0;
To the compiler, this is just a value it has to remember. Consequently it sets aside a memory address to store this value, and to make sure it doesn't get confused later, calls this memory address 'Count'. This means that the code generated by the compiler for this line is something like this mov eax,0
mov Count,eax
The complier can't use a line such as
mov Count,0
because at least one parameter of the instruction must be a register.
If we were to consider the line
Count := Count + 1;
we would get something like this
mov eax,Count
add eax,1
mov Count,eax
For variables of types other than integer, matters can become more complex. So more of that later, and lets use what we've learnt so far.

Our first snippet of assembler. Forgive the trivial nature of this example, but we've got to start somewhere. Consider the following lines of pascal code.

function Sum(X,Y:integer):integer;
begin
Result := X+Y;
end;



Object Pascal provides the asm .. end block as a method of introducing assembler to our code. So we could rewrite the function Sum thus

function Sum(X,Y:integer):integer;
begin
asm
mov eax,X
add eax,Y
mov Result,eax
end;
end;



This works, but there is little point. There is no speed gain and we've lost the readability of our code. There are, however, instances where our currently limited knowledge of assembler can be put to good use. Lets say we wish to convert explicit Red,Green,and Blue values into a colour of type TColor suitable for use in Delphi. The TColor type describes a 24bit True Colour stored in the format of an integer ie. four bytes, the highest of which is zero, thereafter in the order Red, Green, Blue.

function GetColour(Red,Green,Blue:integer):TColor;
begin
asm
{ecx will hold the value of TColor}
mov ecx,0
{start with the Red component}
mov eax,Red
{make sure Red is in range 0<=Red<=255}
and eax,255
{shift the Red value to the correct position}
shl eax,16
{adjust value of TColor}
xor ecx,eax
{same again with Green component}
mov eax,Green
and eax,255
shl eax,8
xor ecx,eax
{and again with Blue}
mov eax,Blue
and eax,255
xor ecx,eax
mov Result, ecx
end;
end;



You'll have noticed that I've introduced a few binary operations. These operations are straightforward and are also defined in Object Pascal itself, but for clairity let's explicity define all the operations introduced thus far and a few more.

标签: ,

Learning Assembler with Delphi 3

A proper example
Until now we have achieved little substantial. However, all of the basic instructions have been introduced, so let's use them.

Suppose we have to display the output of some function dependant upon two variables. You might imagine this as a three-dimensional map, where the coordinates [x,y] correspond to a height h. When we plot the point [x,y] on the screen we need to give the impression of depth. This can be achieved by using colours of differing intensity, in our example, blue below sea level and green above. What is needed is a function that will convert a given height into the appropriate depth of color for a given sea level.

First we should plan the ranges of the variables involved. It is reasonable to use the integer type to store the height and sea level, given the range of a 32-bit value. The true colour range available, limits us to a maximum color depth for blue and green to 256, so we will have to scale the results accordingly. If we assume a maximum height above, and depth below sea level to be 65536 feet, we can use shl and shr for some fast scaling, and we will get a change in colour depth every 256 feet. The function will of course, have to return a TColor type to be compatible with Delphi.

function ColorMap
(Height, Sea : integer):TColor;
begin
asm
mov eax,Height
cmp eax,Sea
{jump to section dealing with above sea level}
jg @Above


{ Height is beneath Sea Level }
mov ecx,Sea
{ ecx is depth beneath sea }
sub ecx,eax
{ divide depth by 256 }
shr ecx,8
cmp ecx, 256
{ ecx should not exceed 255 }
jl @NotMaxDepth
mov ecx,255
@NotMaxDepth:
{ ecx now holds color }
jmp @ColorDone

@Above:
{ eax is height above sea level }
sub eax,Sea
{ divide height by 256 }
shr eax,8
cmp eax,256
{ eax should not exceed 255 }
jl @NotMaxHeight
mov eax,255
@NotMaxHeight:
{ eax now holds green color depth }
{ eax now holds color }
shl eax,8
{ ecx now holds color for
compatibility with beneath
sea level routine}

mov ecx,eax
@ColorDone:
mov Result,ecx
end;
end;

As it happens, the above routine can be written with Delphi's assembler directive. This method of writing assembler removes a lot of the protection provided by the complier, but as compensation, speed improves.

Here is the above routine using the assembler directive.

function ColorMap(Height,Sea:integer):TColor;assembler;
asm
cmp eax,edx
jg @Above
sub edx,eax
shr edx,8
cmp edx,256
jl @NotMaxDepth
mov edx,255
@NotMaxDepth:
mov eax,edx
jmp @ColorDone
@Above:
sub eax,edx
shr eax,8
cmp eax,256
jl @NotMaxHeight
mov eax,255
@NotMaxHeight:
shl eax,8
@ColorDone:
end;

At first sight it is a little difficult to see what's going on. The registers are set with certain values before entering the function or procedure. How these are set depends on how the function or procedure was defined. There are two possibilities.

Stand alone, or explicitly defined procedures and functions
On entry,
eax holds the value of the first parameter of the function or procedure if such exists.
ebx holds the address of the data block of the function or procedure. You must be careful when using ebx, for it must hold its initial value whenever you refer to a function or procedure's parameters or data in your assembler code block. Furthermore ebx must hold its initial value when exiting. The Delphi manual actually says don't touch.
ecx holds the value of the third parameter.
edx holds the second parameter value.

On exit, eax holds the result of the function, or in the case of a procedure, convention states it holds the value of any relevant error code you may define.
ebx must hold its initial value. Failure to ensure this will cause a system crash.

Object method, procedures and functions
On entry,
eax holds the address of the parent object's data block. You don't need to maintain this value, however it is needed whenever you wish to access or change the values of the parent object's fields.
ebx is the same as above.
ecx holds the second parameter value.
edx holds the value of the first parameter.

On exit, the register values are as for a stand alone procedure or function.

With the above information, you should now be able to work your way through the ColorMap function. On the face of it, we seem to have reduced the number of lines of assembler from 18 to 15, which is not much of a saving, and the code is not as readable. However this is not the whole story. The complier generates a fail-safe entry and exit code block for any function or procedure defined with the usual begin..end block. By using the assembler directive, the complier employs only minimal entry and exit code. In the case of the ColorMap function this means it's code size has roughly halved, as has its execution time. These are the levels of performance gain that make writing assembler worthwhile.

Implementing local variables
It is clear, that with just four registers, implementing any half serious algorithm will not be a trivial matter. If a routine is fast, this is usually because every constant used has been calculated before entering any loops. In Object Pascal, this is where local variables are used. For a procedure or function defined with the assembler directive, the same declaration format is used. For example, the following is valid,

function Test:integer:assembler;
var first,second,third:integer;
asm

{some code, remembering that
ebx must be it's initial value}

end;

Local constants, and constant variables may also be defined just as one would in Object Pascal. The complier just reserves a block of memory, whose address is stored in ebx on entry. Thereafter the local variable names refer to an offset value from the base address of the data block. This allows the complier to use the index addressing provided by the cpu. An index address is of the form [reg1+reg2] or [reg1+exp1] for example [ebx+edx] . You will find indexing the easiest way of addressing data such as strings and arrays but more of this later. In the case of a function definition, a reference to a local variable is implemented as an indexed address irrespective of the operation, consequently the operation mov eax,second is actually mov eax,[ebx+4] where 4 is the offset to the address of the value of the second variable. You could write either, but the former offers greater clarity. I hope you are now starting to appreciate the importance of maintaining the value of ebx.

There is also a quicker method of temporary value storage, and this brings us to the stack.

The Stack
The stack is just what it says. Think of a stack of books on a table, we can place another book on the stack or take a book off the stack. To get at the third book down, we must first remove the top two. This is what we do with register values, and there are two appropriate instructions.

push reg1 place the value of reg1 on top of the stack.

pop reg1 remove the top value of the stack, and place this in reg1.

Windows uses the stack to pass parameters to the api functions, and as such great care must be exercised when using the stack. Each push must have an associated pop in all the code you write, get this wrong and once again the system will crash.

We will use the stack, indeed it is necessary when implementing recursive functions, but generally we shall only use the stack for temporary storage. For example when using the mul and div operations there are register value changes all over the place,

...
{save edx}
push edx
{eax := eax*ecx,
edx holds any overflow}

mul ecx
{dump any overflow
value and restore edx}

pop edx
...

An aside on recursion. Avoid it at all costs. Recursive functions may look elegant and appear to use very little code to achieve a great deal, but they are the signature of the inexperienced. A recursive function will commonly overload the stack because at design time you cannot be certain how many recursions are required and as such the memory demands are unknown. How do you explain to an end-user that your 100K program requires 8mb to run, when an equivalent iterative routine may produce a 500K program needing just 1mb. Furthermore, discovering the cause of a stack failure is problematic, making debugging a painful process. The only time I can think of, where recursion is justified, is in the implementation of artificial intelligence algorithms. It might also be noted that generally, I have found iteration to be quicker.

Beyond integers
Until now we have discussed only 32bit integer values. This would seem to impose a limitation on any code we may wish to write, but this is not so. When Object Pascal passes a string as a parameter, for example, it is a 32bit integer value that is used. In this particular case the value passed is the memory address at which the contents of the string are stored. This value is what is referred to as a pointer and is the approach used for any data type, whether string, array or user-defined object. Indeed, even floating point values are referred to in this way, as they are considered to be strings.

What's in a pointer
In a normal pascal procedural definition, the values of the parameters are copied to the data area of the procedure. If the value is an integer the value is passed explicitly whenever the parameter is referred to. In the case of other data structures, the pointer passed, points to the appropriate part of the data area. In neither scenario, is the value of any variable passed as a parameter changed. Object Pascal allows one to define parameters with the var directive, whence pointers to the actual variables are passed. Consequently, changes to actual variable values may be made. The same result is achieved by defining parameters to be of pointer type, each pointer pointing to the appropriate data structure. This latter approach has the advantage of coping with dynamically instantiated data, the price to pay being that care with null pointers and memory allocation should be taken.

标签: ,

Delphi中类的运行期TypeInfo信息结构说明

Delphi中类的运行期TypeInfo信息结构说明
作者:刘啸
CnPack开发组 http://www.cnpack.org
关键字:RTTI, TypeInfo, TypeData, PropInfo
(转载请注明出处并保持完整)
一、引子

Delphi运行期间,一个对象变量实际上是一个四字节指针,指向内存中此对象具体占据的一片区域,而区域的首个四字节又是一个指针指向该类的VMT,所有该类的实例对象的区域的首四字节指针都指向同一个VMT,故此一个VMT基本上就可以代表类本身。而每个类的VMT前面(VMT指针所指处的负偏移处)保存了该类的一些运行期信息,包括-44(vmtClassName)处的指向ClassName的字符串指针,-40(vmtInstanceSize)处的对象实例大小InstanceSize等。而本文专门讲述其-60(vmtTypeInfo)处的TypeInfo/ClassInfo指针所指的、本类的属性的RTTI信息。

二、TTypeInfo及其结构

TypInfo单元中声明的TTypeInfo结构描述了所有带RTTI的基本类型信息,而不光是针对类的。一个类的VMT首部偏移-60(vmtTypeInfo)处的四字节是一个TypeInfo/ClassInfo指针,指向一个TTypeInfo结构。
TTypeInfo在TypInfo中的定义与加的注释如下:

TTypeInfo = record
Kind: TTypeKind; // 该类型信息所描述的类型,是类则为tkClass
Name: ShortString; // 该类型信息所描述的类型名,是类时则为类名。
{TypeData: TTypeData}
end;

虽然看起来它定义得挺简单,只有两个成员,但它在运行期却是个巨大的复杂结构,因为它后面实际上紧接着一个TTypeData结构。TTypeData 结构是个大的共用体,对于类来说,它的定义和注释节选一段如下:

TTypeData = packed record
...
case TTypeKind of
tkClass: (
ClassType: TClass;
ParentInfo: PPTypeInfo; // 指向父类的 TypeInfo 结构
PropCount: SmallInt; // 本类的总属性数目,包括父类的属性数
UnitName: ShortStringBase; // 本类所在的单元名
{PropData: TPropData});
...
end;

这个结构除了这四个成员外,后面在运行期间跟着一个TPropData结构,这个结构则存储了所有属性的类型信息。TPropData的结构定义和注释如下:

TPropData = packed record
PropCount: Word; // 本类的属性数目,不包括父类
PropList: record end;
{PropList: array[1..PropCount] of TPropInfo}
end;

它其中就一个PropCount,后面是个不定长的PropList的数组,每个元素是一个属性描述结构TPropInfo。
TPropInfo定义又如下:

PPropInfo = ^TPropInfo;
TPropInfo = packed record
PropType: PPTypeInfo;
GetProc: Pointer;
SetProc: Pointer;
StoredProc: Pointer;
Index: Integer;
Default: Longint;
NameIndex: SmallInt;
// NameIndex 是本属性在本类所有属性中的排名。
// 一个类的所有直属属性的排名可能不是从0开始的,因为父类可能有属性。
Name: ShortString;
end;

这样,以上几个结构便嵌套而组成了一个类的巨大的属性信息,所有内容全是顺序排列,连ShortString都是。
需要说明的是,这儿所写的ShortString在实际场合并不是固定的长255,而是个可变长的字符串,第0个字节是长度,从字符串第一位开始跳过长度所指明的距离便到了下一个成员。这样的字符串紧凑结构有利于节省内存。

三、图示

以上介绍难免不够直观,这里用文本画一个图以指明它们的关系:


|---------|
|ClassInfo|---|
|---------| |
Object Ref |---------| |
|-------| | ... | |
| Ref | Object |---------| |
|-------|----->|-------|0 |---------| |
|VMT Ptr|----->|---------|0 |
|Field1 | | VM 1 | |
|Field2 | | VM 2 | |
|-------| |---------| |
|
|
|-------------------------------------------
|
|
|--->|TTypeInfo--------------------------|0
|Kind: TTypeKind; |
|Name: ShortString; // 不定长 |
| |TTypeData------------------------|
| |ClassType: TClass; |
| |ParentInfo: PPTypeInfo; |// 指向父类的ClassInfo
| |PropCount: SmallInt; |
| |UnitName: ShortStringBase; |// 不定长
| | |TPropData----------------------|
| | |PropCount: Word; |
| | | |PropList(TPropInfo array)----|
| | | | |1PropType: PPTypeInfo; |
| | | | |1GetProc: Pointer; |
| | | | |1SetProc: Pointer; |
| | | | |1StoredProc: Pointer; |
| | | | |1Index: Integer; |
| | | | |1Default: Longint; |
| | | | |1NameIndex: SmallInt; |
| | | | |1Name: ShortString; |// 不定长
| | | | |2PropType: PPTypeInfo; |
| | | | |2GetProc: Pointer; |
| | | | |2SetProc: Pointer; |
| | | | |2StoredProc: Pointer; |
| | | | |2Index: Integer; |
| | | | |2Default: Longint; |
| | | | |2NameIndex: SmallInt; |
| | | | |2Name: ShortString; |
| | | | |... |
| | | | |... |

四、获取属性信息的系统函数分析

这里分析几个运行期获得类属性的RTTI信息的函数,以加深对本文的理解。

1.GetTypeData 从一个类的 TypeInfo/ClassInfo 指针得到一个类的 TypeData 指针。

function GetTypeData(TypeInfo: PTypeInfo): PTypeData; assembler;
asm
{ -> EAX Pointer to type info }
{ <- EAX Pointer to type data }
{ it's really just to skip the kind and the name }
XOR EDX,EDX
MOV DL,[EAX].TTypeInfo.Name.Byte[0]
LEA EAX,[EAX].TTypeInfo.Name[EDX+1]
end;

这个函数比较简单,就是从TTypeInfo中跳过Kind和Name,直接到TypeData的指针。代码中的注释也说明了这一点。

2. GetPropInfos

本函数将一个类的所有属性信息的地址转存到一个预先分配好的列表中,其内在机制稍微复杂一点,简而言之是遍历本类以及父类的属性数组并把遍历到的每一处的属性地址写入列表中。详见注释:

procedure GetPropInfos(TypeInfo: PTypeInfo; PropList: PPropList); assembler;
asm
{ -> EAX Pointer to type info }
{ EDX Pointer to prop list }
{ <- nothing }

PUSH EBX
PUSH ESI
PUSH EDI

XOR ECX,ECX
MOV ESI,EAX // ESI 指向 TypeInfo
MOV CL,[EAX].TTypeInfo.Name.Byte[0]
MOV EDI,EDX
XOR EAX,EAX
MOVZX ECX,[ESI].TTypeInfo.Name[ECX+1].TTypeData.PropCount
// 跳过类型名,得到后面的TypeData
REP STOSD
// 根据本类的总属性数目(已经包括了父类了),将目的数组初始化填0

@outerLoop:
MOV CL,[ESI].TTypeInfo.Name.Byte[0]
// 跳过 Name 字符串长度
LEA ESI,[ESI].TTypeInfo.Name[ECX+1]
// ESI 得到一个类的TypeData,循环开始时是本类的TypeData,
// 下一个循环时可能是父类的TypeData
MOV CL,[ESI].TTypeData.UnitName.Byte[0]
// 跳过UnitName字符串的长度
MOVZX EAX,[ESI].TTypeData.UnitName[ECX+1].TPropData.PropCount
// 得到本类的属性数目,不包括父类
TEST EAX,EAX
JE @parent // 如果本类无属性则跳到寻找父类处
LEA EDI,[ESI].TTypeData.UnitName[ECX+1].TPropData.PropList
// 准备写入PropList

@innerLoop: // 第一次进入时,EDI 指向 PropList中的第一个元素,此后 EDI 递增。

MOVZX EBX,[EDI].TPropInfo.NameIndex
// EBX 获得 EDI 指向的属性的 Index
MOV CL,[EDI].TPropInfo.Name.Byte[0]
CMP dword ptr [EDX+EBX*4],0
// 查该PropList的Index位置上是否已经存了指针了。
JNE @alreadySet
MOV [EDX+EBX*4],EDI // 没存过,则存

@alreadySet:
LEA EDI,[EDI].TPropInfo.Name[ECX+1]
// 跳过一个Name的ShortString,EDI便指向PropList中的下一个元素了。
DEC EAX
JNE @innerLoop

@parent:
MOV ESI,[ESI].TTypeData.ParentInfo
// 寻找父类的,如果有父类的信息,则 ESI 指向父类的 TypeInfo
XOR ECX,ECX
TEST ESI,ESI
JE @exit
MOV ESI,[ESI]
JMP @outerLoop
@exit:
POP EDI
POP ESI
POP EBX

end;

五、总结

本文是作者在写代码过程中的一些研究总结的结果,主要以D5/D7为准。其他版本IDE的VCL源码的相关部分和本文中的应该也没多大本质区别,欢迎一起讨论。

标签: ,

Delphi的对象机制浅探

前几天开始阅读 VCL 源代码,可是几个基类的继承代码把我看得头大。在大富翁请教了几位仁兄后,我还是对Delphi对象的创建和方法调用原理不太清楚。最后只好临时啃了一下汇编,把Delphi对象操作的几个关键的方法勘察了一遍。

你可以通过以下链接知道我为什么要做这件事:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=2385681

这是我花费一个晚上的测试结果,更多的细节只能以后在学习中再去了解。

主要测试项目为:
⊙ 测试目标:查看 TObject.Create 的编译器实现
⊙ 测试目标:查看 constructor 函数中 inherited 的编译器实现
⊙ 测试目标:以 object reference 和 class reference 调用构造函数的编译器实现
⊙ 测试目标:考查 Object 和 Class 在调用 class method 时的编译器实现
⊙ 测试目标:考查 ShortString 返回值类型的函数没有赋值时编译器的实现


我把测试的细节记录在后文,一是自己留作参考,二是给对此有兴趣的朋友参考。其实更重要的是,大家可以帮忙检查我的分析有没有错误。我一直是用 Delphi 的组件拖放编程,真正的功底只是这几天阅读 Object Pascal Reference 和 VCL 得来的,汇编更是临时抱佛脚,所以错误难免。我清楚自己的水平,所以写下结论后非常担心。尽管如此,我的目的是为了学习,希望你发现错误后帮我指出来。

主要的结论是:
(*) TObject.Create确实是个空函数,Borland 并没有隐藏 TObject.Create 的代码。TObject.Create的汇编代码是由 constructor directive 指示编译器形成的,编译器对每个class 都一视同仁。
(*) dl 和 eax 是 constructor Create 实现的关键寄存器。Borland 将对象的创建过程设计得精妙而清晰(个人感觉,因为我不知道其他的语言比如C++是如何实现的)。
(*) 一个对象的正常的创建(Obj := TMyClass.Create)过程是这样的:
1. 编译器保证第一个 constructor 调用之前 dl = 1
编译器保证 inherited Create 调用之前 dl = 0
2. dl = 1 时 编译器保证 Create 时 eax = pointer to class VMT
dl = 0 时 编译器保证 Create 时 eax = pointer to current object
3. 编译器保证任何层次的 constructor 调用后 eax = pointer to current object
4. dl = 1 时 编译器保证 Create 调用 System._ClassCreate,并与 constructor 相同的方式使用 eax
dl = 1 时 编译器保证 Create 调用 System._AfterConstruction,并且调用前后 eax = pointer to current object
dl = 0 时 编译器保证 Create 不会调用 System._ClassCreate
dl = 0 时 编译器保证 Create 不会调用 System._AfterConstruction
5. System._ClassCreate 中设置结构化异常处理,在 Create 即将结束时关闭结构化异常处理。
如果出错则会(1)释放由编译器分配的内存(2)恢复堆栈至创建对象之前(3)调用 TSomeClass.Destroy。
(*) object reference 方式的 constructor 调用,编译器尝试实现为 inherited 调用,结果当然是错误。
(*) class method 的调用隐含参数 eax 为指向 VMT 的指针,不管是用 class 还是 object 方式调用,编译器都会正确地把指向 class VMT 的指针传递给 eax。


要读懂下文的测试过程,可能需要相关基础,推荐阅读 Object Pascal Reference 以下章节:
Parameter passing
Function results
Calling conventions (register缺省调用约定,constructor 和 destructor 函数必须采用 register 约定)
Inline assambly code
《Delphi的原子世界》非常值得一读。



以下是测试内容:

=================================================
⊙ 测试目标:查看 TObject.Create 的编译器实现
=================================================
⊙ 测试代码及反汇编代码:
procedure Test; register;
var
Obj: TObject;
begin
push ebp ; 前2句用于设置堆栈指针
mov ebp, esp
push ecx ; 保存 ecx (无用的语句)
Obj := TObject.Create;
mov dl, $01 ; 设置 dl = 1,通知 TObject.Create 这是一次新建对象的调用
mov eax, [$004010a0] ; 把指向 TObject class VMT 的指针存入 eax,
; 作为 TObject.Create 隐含的 Self 参数
call TObject.Create ; 调用 TObject.Create 函数
mov [ebp-$04], eax ; TObject.Create 返回新建对象的指针至 Obj
end;
pop ecx ; 恢复堆栈并返回
pop ebp
ret

⊙ TObject.Create 的反汇编代码:
; 函数进入时 eax = pointer to VMT (dl = 1)
eax = pointer to instance (dl = 0)
; 函数返回时 eax = pointer to instance
test dl, dl ; 检查 dl 是否 = 0
jz +$08 ; dl = 0则跳至 @@1
add esp, -$10 ; 增加 16 字节的堆栈,每次调用 _ClassCreate 之前都会进行
; 用于 System._ClassCreate 设置结构化异常处理
call @ClassCreate ; 调用 System._ClassCreate
@@1:
test dl, dl ; 检查 dl 是否 = 0
jz +$0f ; dl = 0则跳到 end 结束过程
call @AfterConstruction ; dl <> 0 则调用 System._AfterConstruction
; (注意不是 TObject.AfterConstruction)
pop dword ptr fs:[$00000000] ; fs:[0] 指向结构化异常处理的函数,此即取消最后一次的 try..except设置
; 这个 try..except 在 System._ClassCreate 中创建
; 用于在出错时自动恢复堆栈/释放内存分配/并调用 TObject.Free
add esp, $0c ; 恢复堆栈,注意只恢复了 12 字节的堆栈,还有4字节由上句 pop 了
ret

注意:以上汇编代码中重复出现了 test dl,dl,说明 Borland 并没有特别对待 TObject.Create,TObject.Create确实是个空函数。TObject.Create的汇编代码是由 constructor directive 指示编译器形成的,编译器对每个class 都一视同仁。
注意:这段 TObject.Create 代码是在 PC 机上编译的结果,严格地说应该是在 Win32 操作系统上的实现之一。查看System._ClassCreate 就知道 Borland 还有其他的异常处理实现机制,产生的 TObject.Create 代码也不相同。

⊙ System._AfterContruction 函数的代码:
function _AfterConstruction(Instance: TObject): TObject;
begin
Instance.AfterConstruction;
Result := Instance;
end;

⊙ System._ClassCreate 函数的代码:
function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject;
asm
{ -> EAX = pointer to VMT }
{ <- EAX = pointer to instance }
PUSH EDX ; 保存寄存器
PUSH ECX
PUSH EBX
TEST DL,DL ; 如果 dl = 0 则不调用 TObject.NewInstance
JL @@noAlloc
CALL DWORD PTR [EAX] + VMTOFFSET TObject.NewInstance ; 调用 TObject.NewInstance
@@noAlloc:
{$IFNDEF PC_MAPPED_EXCEPTIONS} ; 设置 PC 架构的结构化异常处理
XOR EDX,EDX
LEA ECX,[ESP+16]
MOV EBX,FS:[EDX]
MOV [ECX].TExcFrame.next,EBX
MOV [ECX].TExcFrame.hEBP,EBP
MOV [ECX].TExcFrame.desc,offset @desc
MOV [ECX].TexcFrame.ConstructedObject,EAX { trick: remember copy to instance }
MOV FS:[EDX],ECX
{$ENDIF}
POP EBX ; 恢复寄存器
POP ECX
POP EDX
RET

{$IFNDEF PC_MAPPED_EXCEPTIONS} ; 设置非 PC 架构的结构化异常处理
@desc:
JMP _HandleAnyException

{ destroy the object }

MOV EAX,[ESP+8+9*4]
MOV EAX,[EAX].TExcFrame.ConstructedObject
TEST EAX,EAX
JE @@skip
MOV ECX,[EAX]
MOV DL,$81
PUSH EAX
CALL DWORD PTR [ECX] + VMTOFFSET TObject.Destroy
POP EAX
CALL _ClassDestroy
@@skip:
{ reraise the exception }
CALL _RaiseAgain
{$ENDIF}
end;


==============================================================
⊙ 测试目标:查看 constructor 函数中 inherited 的编译器实现
==============================================================
⊙ 测试代码及反汇编代码:
type
TMyClass = class(TObject)
constructor Create;
end;
constructor TMyClass.Create;
begin
inherited; // 考查此句的实现
Beep;
end;

procedure Test; register;
var
Obj: TMyClass;
begin
Obj := TMyClass.Create;
mov dl, $01 ; class reference 时编译器设置 dl = 1
mov eax, [$004600ec] ; 设置 eax 为指向 TMyClass 的 VMT pointer
call TMyClass.Create ; 调用 TMyClass.Create
mov [ebp-$04], eax ; 保存 新建对象的指针
end;

constructor TMyClass.Create 的反汇编代码:
; 函数进入时 eax = pointer to VMT (dl = 1)
eax = pointer to instance (dl = 0)
; 函数返回时 eax = pointer to instance
begin
push ebp ; 这3句用于保存堆栈指针和创建堆栈
mov ebp, esp
add esp, -$08
test dl, dl ; 如果 dl = 0 则跳到 @ClassCreate 之后 @@1 处执行
jz +$08
add esp, -$10 ; 为 _ClassCreate 调用准备堆栈
call @ClassCreate ; 调用 System._ClassCreate,执行完成后 eax = 新建对象的指针
@@1:
mov [ebp-$05], dl ; 将 dl 值保存到堆栈中的 1 字节中,因为后面的 inherited TObject.Create
; 可能会改变 edx 的值
mov [ebp-$04], eax ; 保存 eax 到堆栈, eax = pointer to instance
inherited;
xor edx, edx ; 将 edx 清零(dl = 0),以通知 TObject.Create 不用再调用
; _ClassCreate 和 AfterConstructor (编译器实现)
mov eax, [ebp-$04] ; 将 eax 的值还原为前面保存在堆栈的 eax 值
; (这句是多余的,但在其它情况下可能必须执行此句)
call TObject.Create ; 调用 TObject.Create
Beep;
call Beep ; 继承类中 inherited 之后实现的功能
mov eax, [ebp-$04] ; 将 eax 的值还原为前面保存在堆栈的 eax 值
cmp byte ptr [ebp-$05], $00 ; (间接)检查 dl 是否 = 0
jz +$0f ; dl = 0 则跳过 _AfterConstruction 到 @@2 处
call @AfterConstruction ; 调用 System._AfterConstruction
pop dword ptr fs:[$00000000] ; 这2句恢复为 _ClassCreate 创建的堆栈空间
add esp, $0c
@@2:
mov eax, [ebp-$04] ; 返回 pointer to instance
end;
pop ecx
pop ecx
pop ebp
ret

结论:真是精妙!一个对象的正常的创建(Obj := TMyObj.Create, 与后面不正常的调用相对)过程是这样的:
1. 编译器保证第一个 constructor 调用之前 dl = 1
编译器保证 inherited Create 调用之前 dl = 0
2. dl = 1 时 编译器保证 Create 时 eax = pointer to class VMT
dl = 0 时 编译器保证 Create 时 eax = pointer to current object
3. 编译器保证任何层次的 constructor 调用后 eax = pointer to current object
4. dl = 1 时 编译器保证 Create 调用 System._ClassCreate,并与 constructor 相同的方式使用 eax
dl = 1 时 编译器保证 Create 调用 System._AfterConstruction,并且调用前后 eax = pointer to current object
dl = 0 时 编译器保证 Create 不会调用 System._ClassCreate
dl = 0 时 编译器保证 Create 不会调用 System._AfterConstruction
5. System._ClassCreate 中设置结构化异常处理,在 Create 即将结束时关闭结构化异常处理。
如果出错则会(1)释放由编译器分配的内存(2)恢复堆栈至创建对象之前(3)调用 TSomeClass.Destroy。

看上去有点繁杂,可是如果读懂了上面 TObject.Create 和 TMyObject.Create 则会感觉对象的创建非常清晰。



==================================================================================
⊙ 测试目标:以 object reference 和 class reference 调用构造函数的编译器实现
==================================================================================
⊙ static constructor 测试代码及反汇编代码 (省略了begin 和 end 后面的堆栈分配代码):
procedure Test; register;
var
Obj: TObject;
begin
Obj := TObject.Create;
mov dl, $01 ; 采用 class reference 时编译器自动设置 dl = 1
mov eax, [$004010a0] ; 把指向 TObject class VMT 的指针存入 eax,用于下一行调用
call TObject.Create
mov [ebp-$04], eax
Obj := Obj.Create;
or edx, -$01 ; 采用 object reference 时编译器自动设置 edx 的所有 bit 都为 1
mov eax, [ebp-$04] ; 把 Obj 指针的所指的区域(即对象内存空间)存入 eax,用于下一行调用
call TObject.Create
mov [ebp-$04], eax
end;

⊙ virtual constructor测试代码及反汇编代码 (省略了begin 和 end 后面的堆栈分配代码):
procedure Test; register;
var
Comp: TComponent;
begin
Comp := TComponent.Create(nil);
xor ecx, ecx ; 设置 参数 = nil
mov dl, $01 ; 设置 dl = 1
mov eax, [$00412eac] ; 设置 eax = class VMT pointer
call TComponent.Create ; 调用 TComponent.Create
mov [ebp-$04], eax ; 保存 新建的对象至 Comp
Comp := Comp.Create(nil);
xor ecx, ecx ; 同上
or edx, -$01 ; 设置 edx 所有位为 1
mov eax, [ebp-$04] ; 这句和下句 设置 ebx 为 TComponent class 的 VMT pointer
mov ebx, [eax] ; (如果 Comp 已经实例化了,则 ebx 的值是对的)
call dword ptr [ebx+$2c] ; 可能是调用 TComponent.Create(Comp, -1, nil);
mov [ebp-$04], eax ; 保存 新建的对象至 Comp
end;

结论:object reference 方式的 constructor 调用,编译器尝试实现为 inherited 调用,结果当然是错误。


=======================================================================
⊙ 测试目标:考查 Object 和 Class 在调用 class method 时的编译器实现
=======================================================================
⊙ 测试代码及反汇编代码 (省略了begin 和 end 后面的堆栈分配代码):
procedure Test; register;
var
Com: TComponent;
Str: String[255];
begin
Com := TComponent.Create(nil);
xor ecx, ecx
mov dl, $01
mov eax, [$00412eac] ; eax = pointer to class VMT
call TComponent.Create
mov [ebp-$04], eax
Str := Com.ClassName;
lea edx, [ebp-$00000104]
mov eax, [ebp-$04] ; eax = pointer to object
mov eax, [eax] ; eax = pointer to VMT
call TObject.ClassName
Str := TComponent.ClassName;
lea edx, [ebp-$00000104] ; edx = address of Str
; ShortString 类型的返回值是以 var 类型的参数传递的
mov eax, [$00412eac] ; eax = pointer to class VMT
call TObject.ClassName
end;

结论:class method 的调用隐含参数 eax 为指向 VMT 的指针,不管是用 class 还是 object 方式调用,编译器都会正确地把指向 class VMT 的指针传递给 eax。


========================================================================
⊙ 测试目标:考查 ShortString 返回值类型的函数没有赋值时编译器的实现
========================================================================
procedure Test; register;
begin
TComponent.ClassName;
lea edx, [ebp-$00000100] ; 编译器会在堆栈中创建256 byte 的临时空间,以保证 edx 不会为非法值
mov eax, [$00412eac]
call TObject.ClassName
end;

⊙ TObject.ClassName 函数代码:
class function TObject.ClassName: ShortString;
{$IFDEF PUREPASCAL}
begin
Result := PShortString(PPointer(Integer(Self) + vmtClassName)^)^;
end;
{$ELSE}
asm
{ -> EAX VMT }
{ EDX Pointer to result string }
PUSH ESI
PUSH EDI
MOV EDI,EDX ; EDX 是返回值串的指针
MOV ESI,[EAX].vmtClassName
XOR ECX,ECX
MOV CL,[ESI] ; 设置 result string 的 length
INC ECX
REP MOVSB
POP EDI
POP ESI
end;
{$ENDIF}

结论:这只是我想了解字符串返回值的传递方式。

标签: ,

星期五, 十一月 23, 2007

用UpdateResource修改EXE文件图标的多源码(已修正)

注:转帖请包函作者信息.(作者:菜新)

鄙视下百度空间,他姥姥...竟然限量字符40000字节!靠....

一年前初学VB时我对这个API就特感兴趣,听说这个API可以更改图标资源,就更感兴趣了,后来试了试,发现修改其它资源貌似没多大问题,唯独修改图标时无果,我发现所修改的图虽说已经写入到资源文件中了,但是就是无法显示。后来到网上查了下,发现用UpdateResource修改EXE图标的没一个成功的,大致都是发生成功写入,无法正常显示的问题。罢矣,当时就琢磨着把该问题先放放,等日后有时间再好好折腾。

无奈时间过得太快,忽忽悠悠就过了一年了,前几天,在整理去年的一些源码时发现了这个遗留在硬盘中的代码,一年前无奈自己所学浅溥,啥都不知道,但现在已经对API有了较深厚的认识,再加上对汇编的一些了解,我想此时不解决更待何时。

在折腾这个API的期间也发生不少问题,最让我自责的就是差点被 CreateFile 这个API给Game Over,这个小伟知道(又是小伟?没办法啊,谁要咱和小伟太有缘了~)。还好自己最终醒悟,否则真的要好好鄙视鄙视自己。最初修改时还是和一年前一个样,这时我一直在回想一样年遇到这个问题的问题:所写图标的数据是不是完整的写到了资源文件中?想到此,我用eXeScope(一个PE资源文件查看工具)看了下写入到资源文件中的十六进制,又用UltraEdit-32以十六进制查看ico文件中的数据,发现没问题啊?一字节一字节都对得上,那问题出在哪了?没法,继续在Google游荡,终于找了一份有效的资料(网址现在不知扔哪去了),全E文,看得难受,不过大致的意思是说ICON是由一个结构组成,同PE那些什么NT头,DOS头的差不多,而所显示的图像数据包函于ICON类型结构的dwImageOffset偏移处。呵,这下总算搞明白为什么直接把ICON文件写入到资源文件中显示不了的问题了,也就是说在dwImageOffset偏移位置处才是咱所需要的图像数据,这不就啥都OK了么,爷爷的,原来咱从一开始就被ICON文件整得稀里糊涂,靠MS,当然也鄙视下自己的无知。另外还好找到的那份资料有点人性,把结构给咱标出来了,那么现在一切都顺理成章,不说多了,上代码:

===============================================

Delphi Code:

===============================================

//请自行添加到 Type 处
PICONDIRENTRY = ^ICONDIRENTRY;
ICONDIRENTRY = packed record
bWidth: Byte;
bHeight: Byte;
bColorCount: Byte;
bReserved: Byte;
wPlanes: Word;
wBitCount: Word;
dwBytesInRes: DWORD;
dwImageOffset: DWORD;
end;

PICONDIR = ^ICONDIR;
ICONDIR = packed record
idReserved: Word;
idType: Word;
idCount: Word;
idEntries: ICONDIRENTRY;
end;

PGRPICONDIRENTRY = ^GRPICONDIRENTRY;
GRPICONDIRENTRY = packed record
bWidth: Byte;
bHeight: Byte;
bColorCount: Byte;
bReserved: Byte;
wPlanes: Word;
wBitCount: Word;
dwBytesInRes: DWORD;
nID: Word;
end;

PGRPICONDIR = ^GRPICONDIR;
GRPICONDIR = packed record
idReserved: Word;
idType: Word;
idCount: Word;
idEntries: GRPICONDIRENTRY;
end;

//////////////////////////////////////////////
//函数说明:修改EXE图标
//
//参 数:IconFile 图标文件
// ExeFile 被修改的EXE文件
//
//返回值: 成功为True,否则False
/////////////////////////////////////////////
function ChangeExeIcon(IcoFile, ExeFile: string): Boolean;
var
stID: ICONDIR;
stGID: GRPICONDIR;

pGrpIcon: PBYTE;
pIcon: PBYTE;
hUpdate: DWORD;
nSize, nGSize: DWORD;
hFile: THandle;
dwReserved: DWORD;
ret: Boolean;
begin
Result := False;

hFile := CreateFile(PChar(IcoFile), GENERIC_READ, 0, nil, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
if hFile = INVALID_HANDLE_VALUE then
Exit;

try
ReadFile(hFile, stID, Sizeof(ICONDIR), dwReserved, nil);

nSize := stID.idEntries.dwBytesInRes;
GetMem(pIcon, nSize);
SetFilePointer(hFile, stID.idEntries.dwImageOffset, nil, FILE_BEGIN);
ReadFile(hFile, pIcon^, nSize, dwReserved, nil);

stGID.idType := 1;
stGID.idCount := stID.idCount;
stGID.idReserved := 0;
CopyMemory(@stGID.idEntries.bWidth, @stID.idEntries.bWidth, 12);
stGID.idEntries.nID := 0;

nGSize := Sizeof(GRPICONDIR);
GetMem(pGrpIcon, nGSize);
CopyMemory(pGrpIcon, @stGID, nGSize);

hUpdate := BeginUpdateResource(PChar(ExeFile), False);
try
ret := UpdateResource(hUpdate, RT_GROUP_ICON, MAKEINTRESOURCE(1), 0, pGrpIcon, nGSize);
ret := UpdateResource(hUpdate, RT_ICON, MAKEINTRESOURCE(1), 0, pIcon, nSize);
finally
EndUpdateResource(hUpdate, False);
end;
finally
CloseHandle(hFile);
end;

Result := ret;
end;

===============================================

VB Code:

===============================================

Option Explicit

Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Any) As Long
Private Declare Function SetFilePointer Lib "kernel32" (ByVal hFile As Long, ByVal lDistanceToMove As Long, lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Private Declare Function BeginUpdateResource Lib "kernel32" Alias "BeginUpdateResourceA" (ByVal pFileName As String, ByVal bDeleteExistingResources As Long) As Long
Private Declare Function UpdateResource Lib "kernel32" Alias "UpdateResourceA" (ByVal hUpdate As Long, ByVal lpType As Long, ByVal lpName As Long, ByVal wLanguage As Long, lpData As Any, ByVal cbData As Long) As Long
Private Declare Function EndUpdateResource Lib "kernel32" Alias "EndUpdateResourceA" (ByVal hUpdate As Long, ByVal fDiscard As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function GetLastError Lib "kernel32" () As Long

Private Const INVALID_HANDLE_VALUE = -1
Private Const GENERIC_READ = &H80000000
Private Const FILE_ATTRIBUTE_NORMAL = &H80
Private Const FILE_BEGIN = 0
Private Const OPEN_EXISTING = 3
Private Const RT_ICON = 3&
Private Const DIFFERENCE As Long = 11
Private Const RT_GROUP_ICON As Long = (RT_ICON + DIFFERENCE)


Private Type ICONDIRENTRY
bWidth As Byte
bHeight As Byte
bColorCount As Byte
bReserved As Byte
wPlanes As Integer
wBitCount As Integer
dwBytesInRes As Long
dwImageOffset As Long
End Type

Private Type ICONDIR
idReserved As Integer
idType As Integer
idCount As Integer
'idEntries As ICONDIRENTRY
End Type

Private Type GRPICONDIRENTRY
bWidth As Byte
bHeight As Byte
bColorCount As Byte
bReserved As Byte
wPlanes As Integer
wBitCount As Integer
dwBytesInRes As Long
nID As Integer
End Type

Private Type GRPICONDIR
idReserved As Integer
idType As Integer
idCount As Integer
idEntries As GRPICONDIRENTRY
End Type

'//////////////////////////////////////////////
'//函数说明:修改EXE图标
'//
'//参 数:IconFile 图标文件
'// ExeFile 被修改的EXE文件
'//
'//返回值: 成功为True,否则False
'/////////////////////////////////////////////////////
Private Function ChangeExeIcon(ByVal IconFile As String, ByVal ExeFile As String) As Boolean
On Error GoTo cw

Dim stID As ICONDIR
Dim stIDE As ICONDIRENTRY
Dim stGID As GRPICONDIR

Dim hFile As Long
Dim pIcon() As Byte, pGrpIcon() As Byte
Dim nSize As Long, nGSize As Long
Dim dwReserved As Long
Dim hUpdate As Long
Dim ret As Long

hFile = CreateFile(IconFile, GENERIC_READ, 0, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
If hFile = INVALID_HANDLE_VALUE Then Exit Function

ret = ReadFile(hFile, stID, Len(stID), dwReserved, ByVal 0&)
If ret = 0 Then GoTo cw

ret = ReadFile(hFile, stIDE, Len(stIDE), dwReserved, ByVal 0&)

nSize = stIDE.dwBytesInRes
ReDim pIcon(nSize - 1)
SetFilePointer hFile, stIDE.dwImageOffset, ByVal 0&, FILE_BEGIN
ret = ReadFile(hFile, pIcon(0), nSize, dwReserved, ByVal 0&)
If ret = 0 Then GoTo cw

With stGID
.idType = 1
.idCount = stID.idCount
.idReserved = 0
CopyMemory stGID.idEntries, stIDE, 12
.idEntries.nID = 0
End With

nGSize = Len(stGID)
ReDim pGrpIcon(nGSize - 1)
CopyMemory pGrpIcon(0), stGID, nGSize


hUpdate = BeginUpdateResource(ExeFile, False)
ret = UpdateResource(hUpdate, RT_GROUP_ICON, 1, 0, pGrpIcon(0), nGSize)
ret = UpdateResource(hUpdate, RT_ICON, 1, 0, pIcon(0), nSize)
EndUpdateResource hUpdate, False

If ret = 0 Then GoTo cw
ChangeExeIcon = True

cw:
CloseHandle hFile

End Function

===============================================

VC++ Code:

===============================================

#include
#include
#include


struct ICONDIRENTRY
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
DWORD dwImageOffset;
};


struct ICONDIR
{
WORD idReserved;
WORD idType;
WORD idCount;
//ICONDIRENTRY idEntries;
};


struct GRPICONDIRENTRY
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
WORD nID;
};

struct GRPICONDIR
{
WORD idReserved;
WORD idType;
WORD idCount;
GRPICONDIRENTRY idEntries;
};


//////////////////////////////////////////////
//函数说明:修改EXE图标
//
//参 数:IconFile 图标文件
// ExeFile 被修改的EXE文件
//
//返回值: 成功为True,否则False
/////////////////////////////////////////////
bool ChangeExeIcon(LPWSTR IconFile, LPWSTR ExeFile)
{
ICONDIR stID;
ICONDIRENTRY stIDE;
GRPICONDIR stGID;
HANDLE hFile;
DWORD nSize, nGSize, dwReserved;
HANDLE hUpdate;
PBYTE pIcon, pGrpIcon;
BOOL ret;

hFile = CreateFile(IconFile, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return false;
}

ZeroMemory(&stID, sizeof(ICONDIR));
ret = ReadFile(hFile, &stID, sizeof(ICONDIR), &dwReserved, NULL);

ZeroMemory(&stIDE, sizeof(ICONDIRENTRY));
ret = ReadFile(hFile, &stIDE, sizeof(ICONDIRENTRY), &dwReserved, NULL);

nSize = stIDE.dwBytesInRes;
pIcon = (PBYTE)malloc(nSize);
SetFilePointer(hFile, stIDE.dwImageOffset, NULL, FILE_BEGIN);
ret = ReadFile(hFile, (LPVOID)pIcon, nSize, &dwReserved, NULL);
if (!ret)
{
CloseHandle(hFile);
return false;
}

ZeroMemory(&stGID, sizeof(GRPICONDIR));
stGID.idCount = stID.idCount;
stGID.idReserved = 0;
stGID.idType = 1;
CopyMemory(&stGID.idEntries, &stIDE, 12);
stGID.idEntries.nID = 0;

nGSize = sizeof(GRPICONDIR);
pGrpIcon = (PBYTE)malloc(nGSize);
CopyMemory(pGrpIcon, &stGID, nGSize);


hUpdate = BeginUpdateResource(ExeFile, false);
ret = UpdateResource(hUpdate, RT_GROUP_ICON, MAKEINTRESOURCE(1), 0, (LPVOID)pGrpIcon, nGSize);
ret = UpdateResource(hUpdate, RT_ICON, MAKEINTRESOURCE(1), 0, (LPVOID)pIcon, nSize);
EndUpdateResource(hUpdate, false);
if (!ret)
{
CloseHandle(hFile);
return false;
}

CloseHandle(hFile);
return true;
}

===============================================

ASM Code:

===============================================


.386
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib

ICONDIRENTRY STRUCT
bWidth BYTE ?
bHeight BYTE ?
bColorCount BYTE ?
bReserved BYTE ?
wPlanes WORD ?
wBitCount WORD ?
dwBytesInRes DWORD ?
dwImageOffset DWORD ?
ICONDIRENTRY ENDS

ICONDIR STRUCT
idReserved WORD ?
idType WORD ?
idCount WORD ?
;idEntries ICONDIRENTRY <>
ICONDIR ENDS

GRPICONDIRENTRY STRUCT
bWidth BYTE ?
bHeight BYTE ?
bColorCount BYTE ?
bReserved BYTE ?
wPlanes WORD ?
wBitCount WORD ?
dwBytesInRes DWORD ?
nID WORD ?
GRPICONDIRENTRY ENDS

GRPICONDIR STRUCT
idReserved WORD ?
idType WORD ?
idCount WORD ?
idEntries GRPICONDIRENTRY <>
GRPICONDIR ENDS

.data

szIcon db 'a.ico', 0
szFile db 'a.exe', 0

.code

//////////////////////////////////////////////
//函数说明:修改EXE图标
//
//参 数:IconFile 图标文件
// ExeFile 被修改的EXE文件
//
//返回值: 成功为True,否则False
/////////////////////////////////////////////

_ChangeExeIcon proc IconFile, ExeFile

local @stID: ICONDIR
local @stIDE: ICONDIRENTRY
local @stGID: GRPICONDIR

local @hFile: DWORD
local @dwReserved: DWORD
local @nSize: DWORD
local @nGSize: DWORD
local @pIcon: DWORD
local @pGrpIcon: DWORD
local @hUpdate: DWORD
local @ret: DWORD

invoke CreateFile, IconFile, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
mov @hFile, eax

.if eax == INVALID_HANDLE_VALUE
xor eax, eax
ret
.endif

invoke RtlZeroMemory, addr @stID, sizeof @stID
invoke ReadFile, @hFile, addr @stID, sizeof @stID, addr @dwReserved, NULL

invoke RtlZeroMemory, addr @stIDE, sizeof @stIDE
invoke ReadFile, @hFile, addr @stIDE, sizeof @stIDE, addr @dwReserved, NULL

push @stIDE.dwBytesInRes
pop @nSize

invoke GlobalAlloc, GPTR, @nSize
mov @pIcon, eax

invoke SetFilePointer, @hFile, @stIDE.dwImageOffset, NULL, FILE_BEGIN
invoke ReadFile, @hFile, @pIcon, @nSize, addr @dwReserved, NULL
cmp eax, 0
je err

invoke RtlZeroMemory, addr @stGID, sizeof @stGID
push @stID.idCount
pop @stGID.idCount
mov @stGID.idReserved, 0
mov @stGID.idType, 1
invoke RtlMoveMemory, addr @stGID.idEntries, addr @stIDE, 12
mov @stGID.idEntries.nID, 0

mov @nGSize, sizeof @stGID
invoke GlobalAlloc, GPTR, @nGSize
mov @pGrpIcon, eax
invoke RtlMoveMemory, @pGrpIcon, addr @stGID, @nGSize


;开始修改
invoke BeginUpdateResource, ExeFile, FALSE
mov @hUpdate, eax
invoke UpdateResource, @hUpdate, RT_GROUP_ICON, 1, 0, @pGrpIcon, @nGSize
invoke UpdateResource, @hUpdate, RT_ICON, 1, 0, @pIcon, @nSize
mov @ret, eax
invoke EndUpdateResource, @hUpdate, FALSE

.if @ret == FALSE
jmp err
.endif

;成功后到此一日游
invoke GlobalFree, @pIcon
invoke CloseHandle, @hFile
mov eax, 1
ret

err:
;失败后到此一日游
invoke GlobalFree, @pIcon
invoke CloseHandle, @hFile
xor eax, eax
ret

_ChangeExeIcon endp

;==========================程序入口=============================
start:

invoke _ChangeExeIcon, offset szIcon, offset szFile
invoke ExitProcess, NULL

end start


以上分别用四种语言写的一个用UpdateResource修改EXE文件图标代码示例,希望对你有用。你不会VB?那你可用Delphi的,你不会 Delphi?那你可用VC++的,你不会VC++的?那就用ASM的,你不会用ASM的?那你就用VB的。。。如果你都不会,那么可无视本文。

最近驱动也没怎么学,主要就是练习用ASM与C/C++写代码,俺是先打好基础,然后再猛追花花与炉子,要不然还怎么在0Ginr混??花花、炉子、小伟?你们说呢??嘿嘿~~

http://hi.baidu.com/cxwr/blog/item/9d7f53387efe5af1b211c780.html
感谢google,感谢baidu,感谢菜新。

标签: , ,

星期二, 九月 25, 2007

Windows中删除自己的程序(C/C++版本)

这才是真正能删除自己的程序
////////////////////////////////////////////////////////

#include
#include

BOOL SelfDelete()
{
SHELLEXECUTEINFO sei;

TCHAR szModule [MAX_PATH],
szComspec[MAX_PATH],
szParams [MAX_PATH];

// get file path names:
if((GetModuleFileName(0,szModule,MAX_PATH)!=0) &&
(GetShortPathName(szModule,szModule,MAX_PATH)!=0) &&
(GetEnvironmentVariable("COMSPEC",szComspec,MAX_PATH)!=0))
{
// set command shell parameters
lstrcpy(szParams,"/c del ");
lstrcat(szParams, szModule);
lstrcat(szParams, " > nul");

// set struct members
sei.cbSize = sizeof(sei);
sei.hwnd = 0;
sei.lpVerb = "Open";
sei.lpFile = szComspec;
sei.lpParameters = szParams;
sei.lpDirectory = 0;
sei.nShow = SW_HIDE;
sei.fMask = SEE_MASK_NOCLOSEPROCESS;

// increase resource allocation to program
SetPriorityClass(GetCurrentProcess(),
REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(),
THREAD_PRIORITY_TIME_CRITICAL);

// invoke command shell
if(ShellExecuteEx(&sei))
{
// suppress command shell process until program exits
SetPriorityClass(sei.hProcess,IDLE_PRIORITY_CLASS);
SetProcessPriorityBoost(sei.hProcess,TRUE);

// notify explorer shell of deletion
SHChangeNotify(SHCNE_DELETE,SHCNF_PATH,szModule,0);
return TRUE;
}
else // if error, normalize allocation
{
SetPriorityClass(GetCurrentProcess(),
NORMAL_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(),
THREAD_PRIORITY_NORMAL);
}
}
return FALSE;
}
全文:http://www.codeguru.com/cpp/w-p/win32/article.php/c4533/#more

标签: ,

星期三, 九月 19, 2007

搭建跨平台编程环境Code::Blocks+wxWidgets

Windows下
  1. 到Code::Blocks官方网站http://www.codeblocks.org下载Code::Blocks+MingGW安装包,下载后用默认设置安装。

  2. 设 置环境变量,给Path环境变量加上C:\Program Files\CodeBlocks\bin。对于Windows XP,具体方法是鼠标右击“我的电脑”,在弹出菜单中选择“属性”;在出现的对话框点选“高级”标签,然后点击下方的“环境变量”按钮,在“系统变量”中 选中“Path”并点击“编辑”按钮;在弹出对话框的“变量值”一栏的末尾加上“;C:\Program Files\CodeBlocks\bin”(引号不用加);“确定”所有对话框即可。建议重启一次计算机以使新环境变量彻底生效。

  3. 到wxWidgets官方网站http://www.wxwidgets.org下载wxWidgets安装包,下载后用默认设置安装。

  4. 打开一个命令行控制台(开始菜单->程序/所有程序->附件->命令提示符),执行下列代码进行编译:

    1. C:
    2. cd \wxWidgets-2.6.2\build\msw\
    3. mingw32-make -f makefile.gcc BUILD=release clean
    4. mingw32-make -f makefile.gcc BUILD=release

    这时候漫长的编译过程,先去干点别的

  5. ……(干别的事情)

  6. 待编译完成,打开Code::Blocks,新建一个wxWidgets工程,编译看看能不能运行。

  7. 如果以Using wxWidgets DLL建立的工程,可能运行的时候会报告缺少wxmsw26_gcc_custom.dll文件。此时可选如下三种做法之一使得程序能找到该dll:

    1. 复制C:\wxWidgets-2.6.2\lib\gcc_dll\wxmsw26_gcc_custom.dll到C:\WINDOWS\system32\;

    2. 复制C:\wxWidgets-2.6.2\lib\gcc_dll\wxmsw26_gcc_custom.dll到工程所在的文件夹;

    3. 给Path环境变量加上C:\wxWidgets-2.6.2\lib\gcc_dll\,方法仿上文所述。

  8. 如 果以Using static wxWidgets library建立的工程,可能编译(build)的时候会报告ld找不到wxmsw库。此时在菜单中选择Project -> Build options,在弹出对话框中点选“Linker”标签;点击列表中的“wxmsw”,并点击“Edit”按钮,然后将其改为“wxbase26”并确 定;点击“Add”按钮,在弹出对话框输入“wxmsw26_core”并确定,然后用旁边的三角按钮将其提升到最顶端;再编译工程即可。如果程序还用到 其它的库,还要在此添加,并注意先后顺序。

FreeBSD下
  1. 从ports安装Code::Blocks

    以root身份执行:

    1. cd /usr/ports/devel/codeblocks/
    2. make install clean

    按理执行此命令后即会自动安装wxgtk2和wxgtk2-common两个port。如果没有安装请自行安装。

  2. 回复普通用户身份。打开Code::Blocks(可以在命令行下执行codeblocks打开,如果是csh的shell,刚安装完时需要先执行rehash),新建一个wxWidgets工程并尝试编译,如果能通过,则安装成功。

  3. 如果编译时报告`wxgtk2-2.6-config: No such file or directory,那么打开一个term,执行:

    1. wxgtk2-2.6-config --cflags

    执 行后不要关闭该term。点击Code::Blocks菜单Project->Build options,在弹出的对话框中的Compiler标签中点选Other options标签,用刚才term中输出的内容替换掉`wxgtk2-2.6-config --cflags`这一句。再在term中执行:

    1. wxgtk2-2.6-config --libs

    然后在Code::Blocks中刚才的Build options对话框里点选Linker标签,在Other linker options中用term中的输出替换掉`wxgtk2-2.6-config --libs`这一句。

  4. 重新编译工程就应该能通过了。

http://blog.csdn.net/lixinye0123/archive/2006/12/10/1437638.aspx

标签: , , ,

星期日, 八月 26, 2007

vim能完整支援utf-8的文件

在主�上使用 vim 修改程式已�是家常便�了。�常�遇到的困�是�修 utf-8 格式的文件在 vim ���成��。

�初是用 google 找到解�方案的,不�忘�在那�找到的 :P 。�然我不��你,但是我��你!

把方法�下�如下:

其�很��,只是加上一些�定到 .vimrc �定��面。如下:

set encoding=utf-8
set fileencodings=ucs-bom,utf-8,big5,latin1
set fileencoding=big5
set termencoding=big5

重新�� vim �,就可以正常�示及�� utf-8 的文件了。

这篇更好,直接修改vimrc就不用每次修改了。不过是BIG5编码的。

编码不同,不过还是不要忘记原地址:http://blog.nlhs.tyc.edu.tw/post/2/13

标签: ,

星期四, 八月 23, 2007

一个vimrc文件

位置c:\document and settings\用户名下,一个文件名叫:_vimrc
出处http://www.amix.dk/vim/vimrc.html
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" _
" __ | \
" / | /
" \__ | \
" by Amix - http://amix.dk/
"
" Maintainer: Amir Salihefendic <amix3k at gmail.com>
" Version: 2.7
" Last Change: 12/10/06 00:09:21
"
" Sections:
" ----------------------
" General
" Colors and Fonts
" Fileformats
" VIM userinterface
" Statusline
" Visual
" Moving around and tabs
" General Autocommands
" Parenthesis/bracket expanding
" General Abbrevs
" Editing mappings etc.
" Command-line config
" Buffer realted
" Files and backups
" Folding
" Text options
" Indent
" Spell checking
" Plugin configuration
" Yank ring
" File explorer
" Minibuffer
" Tag list (ctags) - not used
" LaTeX Suite things
" Filetype generic
" Todo
" VIM
" HTML related
" Ruby & PHP section
" Python section
" Cheetah section
" Vim section
" Java section
" JavaScript section
" C mappings
" SML
" Scheme bindings
" Snippets
" Python
" javaScript
" Cope
" MISC
"
" Tip:
" If you find anything that you can't understand than do this:
" help keyword OR helpgrep keywords
" Example:
" Go into command-line mode and type helpgrep nocompatible, ie.
" :helpgrep nocompatible
" then press <leader>c to see the results, or :botright cw
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" General
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Get out of VI's compatible mode..
set nocompatible

"Sets how many lines of history VIM har to remember
set history=400

"Enable filetype plugin
filetype plugin on
filetype indent on

"Set to auto read when a file is changed from the outside
set autoread

"Have the mouse enabled all the time:
set mouse=a

"Set mapleader
let mapleader = ","
let g:mapleader = ","

"Fast saving
nmap <leader>w :w!<cr>
nmap <leader>f :find<cr>

"Fast reloading of the .vimrc
map <leader>s :source ~/vim_local/vimrc<cr>
"Fast editing of .vimrc
map <leader>e :e! ~/vim_local/vimrc<cr>
"When .vimrc is edited, reload it
autocmd! bufwritepost vimrc source ~/vim_local/vimrc


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Colors and Fonts
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Enable syntax hl
syntax enable

"Set font to Monaco 10pt
if MySys() == "mac"
set gfn=Bitstream\ Vera\ Sans\ Mono:h14
set nomacatsui
set termencoding=macroman
elseif MySys() == "linux"
set gfn=Monospace\ 11
endif

if has("gui_running")
set guioptions-=T
let psc_style='cool'
colorscheme ps_color
else
set background=dark
colorscheme zellner
endif

"Some nice mapping to switch syntax (useful if one mixes different
languages in one file)
map <leader>1 :set syntax=cheetah<cr>
map <leader>2 :set syntax=xhtml<cr>
map <leader>3 :set syntax=python<cr>
map <leader>4 :set ft=javascript<cr>
map <leader>$ :syntax sync fromstart<cr>

autocmd BufEnter * :syntax sync fromstart

"Highlight current
if has("gui_running")
set cursorline
hi cursorline guibg=#333333
hi CursorColumn guibg=#333333
endif

"Omni menu colors
hi Pmenu guibg=#333333
hi PmenuSel guibg=#555555 guifg=#ffffff


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Fileformats
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Favorite filetypes
set ffs=unix,dos,mac

nmap <leader>fd :se ff=dos<cr>
nmap <leader>fu :se ff=unix<cr>

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" VIM userinterface
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Set 7 lines to the curors - when moving vertical..
set so=7

"Turn on WiLd menu
set wildmenu

"Always show current position
set ruler

"The commandbar is 2 high
set cmdheight=2

"Show line number
set nu

"Do not redraw, when running macros.. lazyredraw
set lz

"Change buffer - without saving
set hid

"Set backspace
set backspace=eol,start,indent

"Bbackspace and cursor keys wrap to
set whichwrap+=<,>,h,l

"Ignore case when searching
set ignorecase
set incsearch

"Set magic on
set magic

"No sound on errors.
set noerrorbells
set novisualbell
set t_vb=

"show matching bracets
set showmatch

"How many tenths of a second to blink
set mat=2

"Highlight search things
set hlsearch

""""""""""""""""""""""""""""""
" Statusline
""""""""""""""""""""""""""""""
"Always hide the statusline
set laststatus=2

function! CurDir()
let curdir = substitute(getcwd(), '/Users/amir/', "~/", "g")
return curdir
endfunction

"Format the statusline
set statusline=\ %F%m%r%h\ %w\ \ CWD:\ %r%{CurDir()}%h\ \ \ Line:\ %l/%L:%c

""""""""""""""""""""""""""""""
" Visual
""""""""""""""""""""""""""""""
" From an idea by Michael Naumann
function! VisualSearch(direction) range
let l:saved_reg = @"
execute "normal! vgvy"
let l:pattern = escape(@", '\\/.*$^~[]')
let l:pattern = substitute(l:pattern, "\n$", "", "")
if a:direction == 'b'
execute "normal ?" . l:pattern . "^M"
else
execute "normal /" . l:pattern . "^M"
endif
let @/ = l:pattern
let @" = l:saved_reg
endfunction

"Basically you press * or # to search for the current selection !! Really useful
vnoremap <silent> * :call VisualSearch('f')<CR>
vnoremap <silent> # :call VisualSearch('b')<CR>


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Moving around and tabs
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Map space to / and c-space to ?
map <space> /
map <c-space> ?

"Smart way to move btw. windows
map <C-j> <C-W>j
map <C-k> <C-W>k
map <C-h> <C-W>h
map <C-l> <C-W>l

"Actually, the tab does not switch buffers, but my arrows
"Bclose function ca be found in "Buffer related" section
map <leader>bd :Bclose<cr>
map <down> <leader>bd
"Use the arrows to something usefull
map <right> :bn<cr>
map <left> :bp<cr>

"Tab configuration
map <leader>tn :tabnew %<cr>
map <leader>te :tabedit
map <leader>tc :tabclose<cr>
map <leader>tm :tabmove
try
set switchbuf=usetab
set stal=2
catch
endtry

"Moving fast to front, back and 2 sides ;)
imap <m-$> <esc>$a
imap <m-0> <esc>0i
imap <D-$> <esc>$a
imap <D-0> <esc>0i


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" General Autocommands
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Switch to current dir
map <leader>cd :cd %:p:h<cr>


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Parenthesis/bracket expanding
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
vnoremap $1 <esc>`>a)<esc>`<i(<esc>
")
vnoremap $2 <esc>`>a]<esc>`<i[<esc>
vnoremap $3 <esc>`>a}<esc>`<i{<esc>
vnoremap $$ <esc>`>a"<esc>`<i"<esc>
vnoremap $q <esc>`>a'<esc>`<i'<esc>
vnoremap $w <esc>`>a"<esc>`<i"<esc>

"Map auto complete of (, ", ', [
inoremap $1 ()<esc>:let leavechar=")"<cr>i
inoremap $2 []<esc>:let leavechar="]"<cr>i
inoremap $4 {<esc>o}<esc>:let leavechar="}"<cr>O
inoremap $3 {}<esc>:let leavechar="}"<cr>i
inoremap $q ''<esc>:let leavechar="'"<cr>i
inoremap $w ""<esc>:let leavechar='"'<cr>i
au BufNewFile,BufRead *.\(vim\)\@! inoremap " ""<esc>:let leavechar='"'<cr>i
au BufNewFile,BufRead *.\(txt\)\@! inoremap ' ''<esc>:let leavechar="'"<cr>i

imap <m-l> <esc>:exec "normal f" . leavechar<cr>a
imap <d-l> <esc>:exec "normal f" . leavechar<cr>a


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" General Abbrevs
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"My information
iab xdate <c-r>=strftime("%d/%m/%y %H:%M:%S")<cr>
iab xname Amir Salihefendic


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Editing mappings etc.
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Remap VIM 0
map 0 ^

"Move a line of text using control
nmap <M-j> mz:m+<cr>`z
nmap <M-k> mz:m-2<cr>`z
vmap <M-j> :m'>+<cr>`<my`>mzgv`yo`z
vmap <M-k> :m'<-2<cr>`>my`<mzgv`yo`z

if MySys() == "mac"
nmap <D-j> <M-j>
nmap <D-k> <M-k>
vmap <D-j> <M-j>
vmap <D-k> <M-k>
endif

func! DeleteTrailingWS()
exe "normal mz"
%s/\s\+$//ge
exe "normal `z"
endfunc
autocmd BufWrite *.py :call DeleteTrailingWS()

set completeopt=menu

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Command-line config
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
func! Cwd()
let cwd = getcwd()
return "e " . cwd
endfunc

func! DeleteTillSlash()
let g:cmd = getcmdline()
if MySys() == "linux" || MySys() == "mac"
let g:cmd_edited = substitute(g:cmd, "\\(.*\[/\]\\).*", "\\1", "")
else
let g:cmd_edited = substitute(g:cmd, "\\(.*\[\\\\]\\).*", "\\1", "")
endif
if g:cmd == g:cmd_edited
if MySys() == "linux" || MySys() == "mac"
let g:cmd_edited = substitute(g:cmd, "\\(.*\[/\]\\).*/", "\\1", "")
else
let g:cmd_edited = substitute(g:cmd,
"\\(.*\[\\\\\]\\).*\[\\\\\]", "\\1", "")
endif
endif
return g:cmd_edited
endfunc

func! CurrentFileDir(cmd)
return a:cmd . " " . expand("%:p:h") . "/"
endfunc

"Smart mappings on the command line
cno $h e ~/
cno $d e ~/Desktop/
cno $j e ./

cno $q <C-\>eDeleteTillSlash()<cr>

cno $c e <C-\>eCurrentFileDir("e")<cr>

cno $tc <C-\>eCurrentFileDir("tabnew")<cr>
cno $th tabnew ~/
cno $td tabnew ~/Desktop/

"Bash like
cnoremap <C-A> <Home>
cnoremap <C-E> <End>
cnoremap <C-K> <C-U>


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Buffer realted
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Fast open a buffer by search for a name
map <c-q> :sb

"Open a dummy buffer for paste
map <leader>q :e ~/buffer<cr>

"Restore cursor to file position in previous editing session
set viminfo='10,\"100,:20,%,n~/.viminfo
au BufReadPost * if line("'\"") > 0|if line("'\"") <=
line("$")|exe("norm '\"")|else|exe "norm $"|endif|endif

" Buffer - reverse everything ... :)
map <F9> ggVGg?

" Don't close window, when deleting a buffer
command! Bclose call <SID>BufcloseCloseIt()

function! <SID>BufcloseCloseIt()
let l:currentBufNum = bufnr("%")
let l:alternateBufNum = bufnr("#")

if buflisted(l:alternateBufNum)
buffer #
else
bnext
endif

if bufnr("%") == l:currentBufNum
new
endif

if buflisted(l:currentBufNum)
execute("bdelete! ".l:currentBufNum)
endif
endfunction


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Files and backups
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Turn backup off
set nobackup
set nowb
set noswapfile


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Folding
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Enable folding, I find it very useful
set nofen
set fdl=0


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Text options
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
set expandtab
set shiftwidth=2

map <leader>t2 :set shiftwidth=2<cr>
map <leader>t4 :set shiftwidth=4<cr>
au FileType html,python,vim,javascript setl shiftwidth=2
au FileType html,python,vim,javascript setl tabstop=2
au FileType java setl shiftwidth=4
au FileType java setl tabstop=4

set smarttab
set lbr
set tw=500

""""""""""""""""""""""""""""""
" Indent
""""""""""""""""""""""""""""""
"Auto indent
set ai

"Smart indet
set si

"C-style indeting
set cindent

"Wrap lines
set wrap


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Spell checking
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
map <leader>sn ]s
map <leader>sp [s
map <leader>sa zg
map <leader>s? z=


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Plugin configuration
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
""""""""""""""""""""""""""""""
" Vim Grep
""""""""""""""""""""""""""""""
let Grep_Skip_Dirs = 'RCS CVS SCCS .svn'
let Grep_Cygwin_Find = 1

""""""""""""""""""""""""""""""
" Yank Ring
""""""""""""""""""""""""""""""
map <leader>y :YRShow<cr>

""""""""""""""""""""""""""""""
" File explorer
""""""""""""""""""""""""""""""
"Split vertically
let g:explVertical=1

"Window size
let g:explWinSize=35

let g:explSplitLeft=1
let g:explSplitBelow=1

"Hide some files
let g:explHideFiles='^\.,.*\.class$,.*\.swp$,.*\.pyc$,.*\.swo$,\.DS_Store$'

"Hide the help thing..
let g:explDetailedHelp=0


""""""""""""""""""""""""""""""
" Minibuffer
""""""""""""""""""""""""""""""
let g:miniBufExplModSelTarget = 1
let g:miniBufExplorerMoreThanOne = 2
let g:miniBufExplModSelTarget = 0
let g:miniBufExplUseSingleClick = 1
let g:miniBufExplMapWindowNavVim = 1
let g:miniBufExplVSplit = 25
let g:miniBufExplSplitBelow=1

let g:bufExplorerSortBy = "name"

autocmd BufRead,BufNew :call UMiniBufExplorer


""""""""""""""""""""""""""""""
" Tag list (ctags) - not used
""""""""""""""""""""""""""""""
"let Tlist_Ctags_Cmd = "/sw/bin/ctags-exuberant"
"let Tlist_Sort_Type = "name"
"let Tlist_Show_Menu = 1
"map <leader>t :Tlist<cr>


""""""""""""""""""""""""""""""
" LaTeX Suite things
""""""""""""""""""""""""""""""
set grepprg=grep\ -nH\ $*
let g:Tex_DefaultTargetFormat="pdf"
let g:Tex_ViewRule_pdf='xpdf'

"Bindings
autocmd FileType tex map <silent><leader><space> :w!<cr> :silent!
call Tex_RunLaTeX()<cr>

"Auto complete some things ;)
autocmd FileType tex inoremap $i \indent
autocmd FileType tex inoremap $* \cdot
autocmd FileType tex inoremap $i \item
autocmd FileType tex inoremap $m \[<cr>\]<esc>O


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Filetype generic
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Todo
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
au BufNewFile,BufRead *.todo so ~/vim_local/syntax/amido.vim

""""""""""""""""""""""""""""""
" VIM
""""""""""""""""""""""""""""""
autocmd FileType vim map <buffer> <leader><space> :w!<cr>:source %<cr>


""""""""""""""""""""""""""""""
" HTML related
""""""""""""""""""""""""""""""
" HTML entities - used by xml edit plugin
let xml_use_xhtml = 1
"let xml_no_auto_nesting = 1

"To HTML
let html_use_css = 1
let html_number_lines = 0
let use_xhtml = 1


""""""""""""""""""""""""""""""
" Ruby & PHP section
""""""""""""""""""""""""""""""
autocmd FileType ruby map <buffer> <leader><space> :w!<cr>:!ruby %<cr>
autocmd FileType php compiler php
autocmd FileType php map <buffer> <leader><space> <leader>cd:w<cr>:make %<cr>


""""""""""""""""""""""""""""""
" Python section
""""""""""""""""""""""""""""""
"Run the current buffer in python - ie. on leader+space
au FileType python so ~/vim_local/syntax/python.vim
autocmd FileType python map <buffer> <leader><space> :w!<cr>:!python %<cr>
autocmd FileType python so ~/vim_local/plugin/python_fold.vim

"Set some bindings up for 'compile' of python
autocmd FileType python set makeprg=python\ -c\ \"import\
py_compile,sys;\ sys.stderr=sys.stdout;\ py_compile.compile(r'%')\"
autocmd FileType python set efm=%C\ %.%#,%A\ \ File\ \"%f\"\\,\
line\ %l%.%#,%Z%[%^\ ]%\\@=%m

"Python iMaps
au FileType python set cindent
au FileType python inoremap <buffer> $r return
au FileType python inoremap <buffer> $s self
au FileType python inoremap <buffer> $c ##<cr>#<space><cr>#<esc>kla
au FileType python inoremap <buffer> $i import
au FileType python inoremap <buffer> $p print
au FileType python inoremap <buffer> $d """<cr>"""<esc>O

"Run in the Python interpreter
function! Python_Eval_VSplit() range
let src = tempname()
let dst = tempname()
execute ": " . a:firstline . "," . a:lastline . "w " . src
execute ":!python " . src . " > " . dst
execute ":pedit! " . dst
endfunction
au FileType python vmap <F7> :call Python_Eval_VSplit()<cr>

""""""""""""""""""""""""""""""
" Cheetah section
"""""""""""""""""""""""""""""""
autocmd FileType cheetah set ft=xml
autocmd FileType cheetah set syntax=cheetah

"""""""""""""""""""""""""""""""
" Vim section
"""""""""""""""""""""""""""""""
autocmd FileType vim set nofen

"""""""""""""""""""""""""""""""
" Java section
"""""""""""""""""""""""""""""""
au FileType java inoremap <buffer> <C-t> System.out.println();<esc>hi

"Java comments
autocmd FileType java source ~/vim_local/macros/jcommenter.vim
autocmd FileType java let b:jcommenter_class_author='Amir
Salihefendic (amix@amix.dk)'
autocmd FileType java let b:jcommenter_file_author='Amir
Salihefendic (amix@amix.dk)'
autocmd FileType java map <buffer> <F2> :call JCommentWriter()<cr>

"Abbr'z
autocmd FileType java inoremap <buffer> $pr private
autocmd FileType java inoremap <buffer> $r return
autocmd FileType java inoremap <buffer> $pu public
autocmd FileType java inoremap <buffer> $i import
autocmd FileType java inoremap <buffer> $b boolean
autocmd FileType java inoremap <buffer> $v void
autocmd FileType java inoremap <buffer> $s String

"Folding
function! JavaFold()
setl foldmethod=syntax
setl foldlevelstart=1
syn region foldBraces start=/{/ end=/}/ transparent fold keepend extend
syn match foldImports /\(\n\?import.\+;\n\)\+/ transparent fold

function! FoldText()
return substitute(getline(v:foldstart), '{.*', '{...}', '')
endfunction
setl foldtext=FoldText()
endfunction
au FileType java call JavaFold()
au FileType java setl fen

au BufEnter *.sablecc,*.scc set ft=sablecc

""""""""""""""""""""""""""""""
" JavaScript section
"""""""""""""""""""""""""""""""
au FileType javascript so ~/vim_local/syntax/javascript.vim
function! JavaScriptFold()
setl foldmethod=syntax
setl foldlevelstart=1
syn region foldBraces start=/{/ end=/}/ transparent fold keepend extend

function! FoldText()
return substitute(getline(v:foldstart), '{.*', '{...}', '')
endfunction
setl foldtext=FoldText()
endfunction
au FileType javascript call JavaScriptFold()
au FileType javascript setl fen

au FileType javascript imap <c-t> console.log();<esc>hi
au FileType javascript imap <c-a> alert();<esc>hi
au FileType javascript setl nocindent
au FileType javascript inoremap <buffer> $r return

au FileType javascript inoremap <buffer> $d //<cr>//<cr>//<esc>ka<space>
au FileType javascript inoremap <buffer> $c /**<cr><space><cr>**/<esc>ka


""""""""""""""""""""""""""""""
" HTML
"""""""""""""""""""""""""""""""
au FileType html,cheetah set ft=xml
au FileType html,cheetah set syntax=html


""""""""""""""""""""""""""""""
" C mappings
"""""""""""""""""""""""""""""""
autocmd FileType c map <buffer> <leader><space> :w<cr>:!gcc %<cr>


"""""""""""""""""""""""""""""""
" SML
"""""""""""""""""""""""""""""""
autocmd FileType sml map <silent> <buffer> <leader><space>
<leader>cd:w<cr>:!sml %<cr>


""""""""""""""""""""""""""""""
" Scheme bidings
""""""""""""""""""""""""""""""
autocmd BufNewFile,BufRead *.scm map <buffer> <leader><space>
<leader>cd:w<cr>:!petite %<cr>
autocmd BufNewFile,BufRead *.scm inoremap <buffer> <C-t>
(pretty-print )<esc>i
autocmd BufNewFile,BufRead *.scm vnoremap <C-t>
<esc>`>a)<esc>`<i(pretty-print <esc>


""""""""""""""""""""""""""""""
" SVN section
"""""""""""""""""""""""""""""""
map <F8> :new<CR>:read !svn diff<CR>:set syntax=diff buftype=nofile<CR>gg


""""""""""""""""""""""""""""""
" Snippets
"""""""""""""""""""""""""""""""
"You can use <c-j> to goto the next <++> - it is pretty smart ;)

"""""""""""""""""""""""""""""""
" Python
"""""""""""""""""""""""""""""""
autocmd FileType python inorea <buffer> cfun
<c-r>=IMAP_PutTextWithMovement("def <++>(<++>):\n<++>\nreturn
<++>")<cr>
autocmd FileType python inorea <buffer> cclass
<c-r>=IMAP_PutTextWithMovement("class <++>:\n<++>")<cr>
autocmd FileType python inorea <buffer> cfor
<c-r>=IMAP_PutTextWithMovement("for <++> in <++>:\n<++>")<cr>
autocmd FileType python inorea <buffer> cif
<c-r>=IMAP_PutTextWithMovement("if <++>:\n<++>")<cr>
autocmd FileType python inorea <buffer> cifelse
<c-r>=IMAP_PutTextWithMovement("if <++>:\n<++>\nelse:\n<++>")<cr>


"""""""""""""""""""""""""""""""
" JavaScript
"""""""""""""""""""""""""""""""
autocmd FileType cheetah,html,javascript inorea <buffer> cfun
<c-r>=IMAP_PutTextWithMovement("function <++>(<++>) {\n<++>;\nreturn
<++>;\n}")<cr>
autocmd filetype cheetah,html,javascript inorea <buffer> cfor
<c-r>=IMAP_PutTextWithMovement("for(<++>; <++>; <++>)
{\n<++>;\n}")<cr>
autocmd FileType cheetah,html,javascript inorea <buffer> cif
<c-r>=IMAP_PutTextWithMovement("if(<++>) {\n<++>;\n}")<cr>
autocmd FileType cheetah,html,javascript inorea <buffer> cifelse
<c-r>=IMAP_PutTextWithMovement("if(<++>) {\n<++>;\n}\nelse
{\n<++>;\n}")<cr>


"""""""""""""""""""""""""""""""
" HTML
"""""""""""""""""""""""""""""""
autocmd FileType cheetah,html inorea <buffer> cahref
<c-r>=IMAP_PutTextWithMovement('<a href="<++>"><++></a>')<cr>
autocmd FileType cheetah,html inorea <buffer> cbold
<c-r>=IMAP_PutTextWithMovement('<b><++></b>')<cr>
autocmd FileType cheetah,html inorea <buffer> cimg
<c-r>=IMAP_PutTextWithMovement('<img src="<++>" alt="<++>" />')<cr>
autocmd FileType cheetah,html inorea <buffer> cpara
<c-r>=IMAP_PutTextWithMovement('<p><++></p>')<cr>
autocmd FileType cheetah,html inorea <buffer> ctag
<c-r>=IMAP_PutTextWithMovement('<<++>><++></<++>>')<cr>
autocmd FileType cheetah,html inorea <buffer> ctag1
<c-r>=IMAP_PutTextWithMovement("<<++>><cr><++><cr></<++>>")<cr>


"""""""""""""""""""""""""""""""
" Java
"""""""""""""""""""""""""""""""
autocmd FileType java inorea <buffer> cfun
<c-r>=IMAP_PutTextWithMovement("public<++> <++>(<++>) {\n<++>;\nreturn
<++>;\n}")<cr>
autocmd FileType java inorea <buffer> cfunpr
<c-r>=IMAP_PutTextWithMovement("private<++> <++>(<++>)
{\n<++>;\nreturn <++>;\n}")<cr>
autocmd FileType java inorea <buffer> cfor
<c-r>=IMAP_PutTextWithMovement("for(<++>; <++>; <++>)
{\n<++>;\n}")<cr>
autocmd FileType java inorea <buffer> cif
<c-r>=IMAP_PutTextWithMovement("if(<++>) {\n<++>;\n}")<cr>
autocmd FileType java inorea <buffer> cifelse
<c-r>=IMAP_PutTextWithMovement("if(<++>) {\n<++>;\n}\nelse
{\n<++>;\n}")<cr>
autocmd FileType java inorea <buffer> cclass
<c-r>=IMAP_PutTextWithMovement("class <++> <++> {\n<++>\n}")<cr>
autocmd FileType java inorea <buffer> cmain
<c-r>=IMAP_PutTextWithMovement("public static void main(String[] argv)
{\n<++>\n}")<cr>


"Presse c-q insted of space (or other key) to complete the snippet
imap <C-q> <C-]>


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Cope
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"For Cope
map <silent> <leader><cr> :noh<cr>

"Orginal for all
map <leader>n :cn<cr>
map <leader>p :cp<cr>
map <leader>c :botright cw 10<cr>
map <c-u> <c-l><c-j>:q<cr>:botright cw 10<cr>


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" MISC
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Remove the Windows ^M
noremap <Leader>m mmHmt:%s/<C-V><cr>//ge<cr>'tzt'm

"Paste toggle - when pasting something in, don't indent.
set pastetoggle=<F3>

"Remove indenting on empty lines
map <F2> :%s/\s*$//g<cr>:noh<cr>''

"Super paste
inoremap <C-v> <esc>:set paste<cr>mui<C-R>+<esc>mv'uV'v=:set nopaste<cr>

"A function that inserts links & anchors on a TOhtml export.
" Notice:
" Syntax used is:
" Link
" Anchor
function! SmartTOHtml()
TOhtml
try
%s/&amp;quot;\s\+\*&gt; \(.\+\)</" <a href="#\1" style="color: cyan">\1<\/a></g
%s/&quot;\(-\|\s\)\+\*&gt; \(.\+\)</" \ \ <a href="#\2"
style="color: cyan;">\2<\/a></g
%s/&quot;\s\+=&gt; \(.\+\)</" <a name="\1" style="color: #fff">\1<\/a></g
catch
endtry
exe ":write!"
exe ":bd"
endfunction

标签: ,

辽ICP备05003652号
流风洄雪听天籁,轻云蔽日看落花

Powered by Blogger