星期五, 十月 17, 2008

单机环境下vmware与本机操作系统共享adsl网络设置

单机环境,ADSL拨号上网,安装VMware后,客户机如何与宿主机共享上网?朋友老问这个问题,其实已经不是什么新问题了。为了方便大家还有新手,今天有功夫就写下来,希望方便大家参考。为了具有针对性,我就拿自己的机器做为参照对象。

Product:VMware WorkStation(英文版)
Version:6.0.0 build-45731
硬件配置:三星Q70-AV01笔记本、Intel Core2 Duo(Merom) T7300(2.0G)、DDR2
667(2G)
宿主机系统:Microsoft Windows XP Professional Service Pack 2 (原版)
客户机系统:Fedora 7 Linux
上网方式:北京网通ADSL(512K)、无路由器、Modem也无路由功能。利用XP自带的PPPoE宽带拨号软件新建名为"ADSL"的连接拨号上网。

网络适配器及其配置:
VMware Network Adapter VMnet8:
IP Address. . . . . . . . . . . . : 192.168.168.1
Subnet Mask . . . . . . . . . . . : 255.255.255.0

VMware Network Adapter VMnet1:
IP Address. . . . . . . . . . . . : 192.168.58.1
Subnet Mask . . . . . . . . . . . : 255.255.255.0

本地连接(物理网卡):(自动获取IP地址和DNS)
ADSL(PPPoE) :(由XP新建网络连接向导获得)

以下仅介绍Bridged和NAT两种共享上网方式:
准备工作(必须):为客户机Fedora 7 Linux系统安装VMware Tools

一、Bridged方式共享上网

1、关闭管理工具--服务中的VMware DHCP Service和VMware NAT Service两个服务
2、先断开ADSL连接,控制面板--网络连接--ADSL属性--高级-勾选"Internet连接共享",下拉列表中然后选择"本地连接"。确定,不 要管提示。系统会自动把"本地连接"设置为
192.168.0.1,子网掩码设置为255.255.255.0. 以后192.168.0.1这个IP地址就是Fedora
7 Linu的默认网关地址和DNS地址了。
3、连接ADSL并拨号,启动Fedora 7 Linux,设置IP和DNS
IP设置为192.168.0.X (X就是2到254之间的数都可以)
子网掩码255.255.255.0
网关192.268.0.1
DNS:192.168.0,1
之后重启Fedora 7 Linu或重新激活网络就可以上网了(注意:Linux网络必须激活才能使用,好多人改了配置都不激活,肯定上不了网,时刻检查一下网络是否处于激活状态,而且必须设置为计算机启动时候就激活设备)

二、NAT方式共享上网

1、开启管理工具--服务中的VMware DHCP Service和VMware NAT Service两个服务
2、取消ADSL连接的Internet连接共享。
3、连接ADSL并拨号,启动Fedora 7 Linux,设置IP和DNS
IP设置为192.168.58.X (X就是128到254之间的数都可以)
子网掩码255.255.255.0
网关192.268.58.2
DNS:192.168.58.2
之后重启Fedora 7 Linu或重新激活网络就可以上网了.

提示:或者更简单的办法就是,开启管理工具--服务中的VMware DHCP Service和VMware
NAT Service两个服务后,直接把客户机中的网络设置为DHCP获取IP方式,即可上网
这里需要注意的是VMware对于VMnet8采用如下规则(就以本机的192.168.58.0网段为例):


第一个地址(192.168.58.1):静态地址,分配给了宿主机Xp的VMware Network Adapter
VMnet8适配器使用。
第二个地址(192.168.58.2):静态地址,分配给了NAT设备使用。
(192.168.58.3192.168.58.127)静态地址,保留。
(192.168.58.128192.168.58.254):DHCP作用域地址范围,分配给虚拟机使用。
最后一个地址(192.168.58.255)广播地址。
VMware默认安装后的规则都是如此。

单机ADSL拨号环境,不是路由器或是局域网环境,ADSL的Internet连接共享这步是选择Bridged方式共享上网的关键

如果你还想宿主机和客户机之间能相互Ping通和共享,就先把"文件和打印机共享"添加到Windows防火墙例外,然后把管理工具--服务中的Server、Computer Browser、两个服务打开。

关于GuestOS共享主机文件
选择你的Guest OS
点击Edit virtual machine settings--->Options-->Shared Folders

选择Always enables,然后点 Add 选择你要共享的主机磁盘或文件夹

FreeBSD下USB打印机的安装经验谈

使用的是HP DeskJet 3420,所以我只能说安装这台打印机的经验了。供你们参考吧!
1、编译内核
在安装打印机前先检查内核看看内核中是否支持USB端口。
如果没有就加入如下:
#USB Stuff
device usb
device uhci
device ulpt
如果你无法确认是uhci还是ohci你可以使用dmesg命令查看:
#dmesg | less
或者直接输入下面:
#dmesg | grep uhub

2、编辑rc.conf

内核编译结束后编辑/etc/rc.conf,
添加如下内容: usbd_enable="YES"

3、重新启动计算机,以启用新内核。

4、查验设备节点

这时你到 /dev下看看是否存在ulpt0这个文件,
如果没有就需要用如下方式创建他:
#cd /dev #/dev/MAKEDEV ulpt0

5、安装打印机软件和驱动。
我安装了如下软件:
/usr/ports/print/Ghostscript-gnu
/usr/ports/print/hpijs
/usr/ports/print/apsfilter
/usr/ports/print/cups
/usr/ports/print/foomatic-db
下面的这个我没装: /usr/ports/print/foomatic-db-engine
但我还是建议你在安装cups之前安装上他们。
我直接下载了foomatic-rip和foomatic-gswrapper到/usr/local/bin目录(你也可以到http://www.Linuxprinting.org上下载 foomatic-filters的packages到本地进行安装): #cd /usr/local/bin #fetch
http://www.linuxprinting.org/foomatic-rip
#fetch
http://www.linuxprinting/foomatic-gswrapper
然后运行如下命令:
#chmod 755 foomatic-rip foomatic-gswrapper
#ln -s /usr/local/bin/foomatic-rip
/usr/local/libexec/cups/filter/foomatic-rip
好了该准备我的打印机驱动了,
我的打印机是HP Deskjet 3420。驱动可以在如下网址搜索到(这里有很多品牌打印机驱动的哦!):
http://www.linuxprinting.org/printer_list.cgi 驱动是PPD文件,
将PPD文件下载到/usr/local/share/cups/model,
然后对其设置权限:
chown root:wheel HP-DeskJet_3420-hpijs.ppd
chmod 644 HP-DeskJet_3420-hpijs.ppd

6、准备工作结束,

现在开始配置打印机。 打开浏览器在地址栏输入:
http://localhost:631
或者 http://localhost:631/admin
如果你是用的前者进入,那么你进入页面中的如下链接"Do Administration
Tasks",这时你需要输入管理员名称(User Name)和密码(PassWord)。如果你用后者进入则会直接要求输入管理员名称(User Name)和密码(Password)。
我的:http://127.0.0.1:631 或者 http://127.0.0.1:631/admin
我用的后者。管理员名称(User Name)用你机器的超级管理员root ,密码(Password)当然就是root的密码了!
第一次使用CUPS当然要点击 Add Printer 了。
之后出现一个表单,表单的含义如下(以我的HP Deskjet 3420为例):
Name:3420 (你喜欢怎么叫你的打印机呢?)
Location:Local printer (就这么填就行了!)
Description:hp DeskJet3420 (描述一下你的打印机)
然后点Continue按钮,现在的这个界面是打印机联接端口的选择。
我的选择的是: USB Printer #1
然后点Continue按钮,现在的这个界面是选择打印机厂商的界面,
我的当然选择:HP了。
然后点Continue按钮,好了仔细寻找和你打印机匹配的驱动吧。
我选择HP DeskJet 3420 Foomatic/hpijs (recommended) (en)。
然后点Continue按钮结束配置。

7、启动CUPS 先su到超级管理员,
之后执行如下操作:
#/usr/local/etc/rc.d/cups.sh start OK,
现在你用如下命令测试一下:
#lp <文件名(可以自己编辑一个小文本文件)>

8、OpenOffice的打印机配置

使用如下命令: $openoffice-1.1-spadmin
在弹出窗口中点击"新增打印机"。
在新窗口中选中"配置一个打印机"。
点击"继续"后在新窗口中看看已有的驱动中是不是有和你打印机匹配的。
如果有就选中然后点击继续。如果没有,点击"输入"按钮。
在新弹出的窗口中在如下目录中搜寻打印机驱动:
/usr/local/etc/cups/ppd/ 应该找到了吧,
一路"确定"、"继续"就OK了。

Freebsd7.0 release 安装 diablo-jdk1.6.0

.最小化安装freebsd.
2.到http://www.freebsdfoundation.org/downloads/java.shtml 下载

Diablo Caffe JDK 1.6.0-7 FreeBSD 7.x/i386 End-User 61M
diablo-jdk-freebsd7.i386.1.6.0.07.02.tbz
3.敲pkg_add diablo* 后发现少了几个东西,xtrans-1.0.4,xproto-7.0.10_1,xextproto-7.0.2,javavmwrapper-2.3.2,需要安装相应的包,在dist1那张盘里面有,javavmwrapper-2.3.2我直接到ftp.freebsd.org上面下。

4.安装完上面那几个后,就可以直接安装了,pkg_add diablo*。中间会有提示,敲YES.
5.设置JAVA环境:
我用的root用户,因此需要修改.cshrc文件。
vi~/.cshrc
在set path = (/sbin /bin /usr/sbin /usr/bin /usr/games /usr/local/sbin
/usr/local/bin $HOME/bin)这一行前面加 上:
setenv JAVA_HOME /usr/local/diablo-jdk1.6.0
setenv CLASSPATH .:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
然后在set path这一行的那些路径最后面加上jdk的bin路径/usr/local/diablo-jdk1.6.0/bin,就是下面这样:
set path = (/sbin /bin /usr/sbin /usr/bin /usr/games /usr/local/sbin
/usr/local/bin $HOME/bin /usr/local /diablo-jdk1.6.0/bin)

退出再登陆一下就ok了。

6.重新登录后,敲java -version,看看版本信息。

星期二, 十月 14, 2008

PostgreSQL分类汇总方法

我们知道MySQL中有分类汇总函数GROUP_CONCAT来实现将组内相关值的连接,但是在PostgreSQL没有此类的函数,但是我们可以使用以下两种方法来方便的达到我们的目的:

我们首先创建一个测试表,并插入一些测试数据:

create table test(id int,name varchar(100));

insert into test values(1,'aa');
insert into test values(1,'bb');
insert into test values(2,'cc');
insert into test values(3,'dd');
insert into test values(2,'ff');

1.使用数组函数两组内的列表转换成数组

select id,array_to_string(
array(select name from test where id=a.id),',')
from test a
group by id;

--结果

3 | dd
2 | cc,ff
1 | aa,bb

是不是看起来非常的简单呢,呵呵,非常的cool!

2.使用自定义的聚集函数

PostgreSQL除了提供给我们的一些预定义函数之外,还有创建聚集函数的DDL语句,非常的灵活!

--首先创建一个状态传递函数:实现将统计结果传递连接

create function pg_concat( text, text ) returns text as '
begin
if $1 isnull then
return $2;
else
return $1 || $2;
end if;
end;' language 'plpgsql';

--创建一个最终的结果函数:实现将最后一个连接符号清除

create function pg_concat_fin(text) returns text as '
begin
return substr($1,1,length($1)-1);
end;' language 'plpgsql';

--创建聚集函数,入口参数basetype:text,状态传递函数:pg_concat,状态变量类型:text,结果函数finalfunc:pg_concat_fin
create aggregate pg_concat (
basetype = text,
sfunc = pg_concat,
stype = text,
finalfunc = pg_concat_fin);

--使用聚集函数进行统计:

select id,pg_concat(name||',')
from test
group by id;

--结果
3 | dd
2 | cc,ff
1 | aa,bb

和第一种方法得到的结果是一样的,非常的方便。

PostgreSQL的许多小技巧

1.使用空间索引进行快速间隔数据类型的搜索.

间隔搜索有时候很慢,大部分原因是索引优化器不使用索引,并且在开始列和结束列比较独立。一个解决方案是使用空间索引,它可以把两个独立的值当做一个值来使用。

postgres=# EXPLAIN ANALYZE SELECT * FROM testip WHERE 19999999 BETWEEN
startip AND endip;

QUERY PLAN ----------------------------------------------------------------
Seq Scan on testip (cost=0.00..19902.00 rows=200814 width=12) (actual
time=3.457..434.218 rows=1 loops=1) Filter: ((19999999 >= startip) AND
(19999999 <= endip)) Total runtime: 434.299 ms (3 rows) Time: 435,865 ms

结论:根据以上的执行计划,可以知道上边的查询使用的是序列扫描,花费的时间是:435,865
ms

postgres=# CREATE INDEX ggg ON testip USING gist
((box(point(startip,startip),point(endip,endip))) box_ops);

使用如下的查询:
EXPLAIN ANALYZE SELECT * FROM testip WHERE
box(point(startip,startip),point(endip,endip)) @> box(point
(19999999,19999999), point(19999999,19999999));

结论:执行计划使用的是Bitmap Index Scan on ggg,花费的时间是:2,805 ms。可见相比以前的查询,使用空间索引的查询效率大大的提高了。

2.16进制到10进制的转换

我们已经有了系统函数将10进制转换成16进制:to_hex(11) result: b 下边的函数实现将16进制的数转换成10进制。非常的简单:

create or replace function to_dec(text)
returns integer as $$
declare r int;
begin
execute E'select x\''||$1|| E'\'::integer' into r;
return r;
end
$$ language plpgsql;

--测试
select to_dec('ff');

--结果
255

3.ALTER TABLE ALTER COLUMN USING 语法

在PostgreSQl里边,我们不能将varchar类型直接转换到bool,但是我们可以使用Using语法加判断后进行转换。

CREATE TABLE foo(a varchar);

INSERT INTO foo VALUES ('ano');

--更改数据类型,会报错误信息
ALTER TABLE foo ALTER COLUMN a TYPE boolean ;
ERROR: column "a" cannot be cast to type "pg_catalog.bool"

--使用Using语法更改数据类型
ALTER TABLE foo
ALTER COLUMN a TYPE boolean
USING CASE a
WHEN 'ano' THEN true
ELSE false END;

--更改成功
SELECT * FROM foo;

4.Quote_ident 的使用

使用双引号是一种防止SQL注入的方法,quote_ident 可以检查参数,如果参数中包含任何非法的字符,它会在参数两边加上""
非常简单和有效,但是问题是schema.name,因为中间有点分割。问题如下:

select quote_ident('public.foo');

他不能在schema和name两边加上双引号。

我们可以通过使用函数来按点分割上边的对象名称,在每个单独的对象上使用quote_ident来完成我们的目的:

--对数组进行表转换,针对每一列来使用quote_ident
CREATE OR REPLACE FUNCTION quote_array(text[])
RETURNS text AS $$
SELECT array_to_string(array(SELECT quote_ident($1[i])
FROM generate_series(1, array_upper($1,1))
g(i)),
'.')
$$ LANGUAGE SQL IMMUTABLE;

--创建函数按点进行拆分字符串
CREATE OR REPLACE FUNCTION quote_schema_ident(text)
RETURNS text AS $$
SELECT quote_array(string_to_array($1,'.'))
$$ LANGUAGE SQL IMMUTABLE;

--测试
select quote_schema_ident('public.foo tab');


5.我们已经习惯使用PostgreSQL的exception来捕捉错误,但是错误信息一直不知道如何取得,SQLERRM变量可以给我们详细的信息
以下是一个具体的示例:

CREATE OR REPLACE FUNCTION fn_log_error(p_function varchar, p_location int,
p_error varchar)
RETURNS void AS $$
DECLARE
v_sql varchar;
v_return varchar;
v_error varchar;
BEGIN
--连接数据库
PERFORM dblink_connect('connection_name', 'dbname=...');

--拼凑插入的字符串
v_sql := 'INSERT INTO error_log (function_name, location, error_message,
error_time) '
|| 'VALUES (''' || p_function_name || ''', '
|| p_location || ', ''' || p_error || ''', clock_timestamp())';
--远程执行
SELECT INTO v_return *
FROM dblink_exec('connection_name', v_sql, false);

--获取远程的错误信息
SELECT INTO v_error *
FROM dblink_error_message('connection_name');
--如果出现错误则抛出异常
IF position('ERROR' in v_error) > 0 OR position('WARNING' in v_error) > 0
THEN
RAISE EXCEPTION '%', v_error;
END IF;

PERFORM dblink_disconnect('connection_name');
EXCEPTION
WHEN others THEN
--使用SQLERRM 来显示错误信息
PERFORM dblink_disconnect('connection_name');
RAISE EXCEPTION '(%)', SQLERRM;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

SQLERRM是一个非常有用的变量,可以详细记录错误的具体信息,帮助我们分析执行中发现的错误。

6.循环优化技巧

plpgsql对于非SQL操作效率不是特别高。Plpgsql 不喜欢字符或者字符数组的累计操作,当我们也不能用Perl,因此我们只能用SQL

--使用循环,结果会比较慢的函数

CREATE OR REPLACE FUNCTION SlowList(int) -- slow function, usable for N <=
100
RETURNS varchar AS $$
DECLARE s varchar = '';
BEGIN
FOR i IN 1..$1 LOOP
s := '<item>' || i || '</item>'; -- slow is s := s || ..
END LOOP;
RETURN s;
END; $$ LANGUAGE plpgsql IMMUTABLE;

--使用SQL,结果会比较快的函数

CREATE OR REPLACE FUNCTON FastList(int) -- fast function
RETURNS varchar AS $$
BEGIN
RETURN array_to_string(ARRAY(SELECT '<item>' || i || '</item>'
FROM generate_series(1, $1) g(i)),
'');
END; $$ LANGUAGE plpgsql IMMUTABLE;

--结果:在循环100以下的时候差别并不是很大,当循环更多的时候,差距就非常明显,都来试试吧!

7.查询一组之中的头n条记录

我们一般的做法是使用子查询如下:

SELECT * FROM people WHERE id IN (
SELECT id FROM people s
WHERE people.category = s.category
ORDER BY age LIMIT 2)
ORDER BY category, age;

使用连接我们也可以达到同样的效果如下:

SELECT s1.*
FROM people s1
LEFT JOIN
people s2
ON s1.category = s2.category AND s1.age < s2.age
GROUP BY s1.id, s1.category
HAVING COUNT(s2.id) <= 1
ORDER BY s1.category, COUNT(s2.id);

说明:这个SQL语句的含义是找到同一类比自己的age大的记录,最后判断比自己大的记录的个数,如果是0,那么应该排名第一,
如果是1,那么排名第二(HAVING COUNT(s2.id) <= 1)

slony1-1.2.10配置简介

PostgreSQL支持许多复制机制,但是个人觉得slony应该是一个不错的选择,因为slony支持异步,单向,级联的将数据从一个源头复制到多个订阅者手中.

异步的意思是一个订阅者的数据不一定是最新的,订阅者可以离线,而并不影响数据源的复制.而当订阅者再次连线后,它可以接收到所有的数据.当然,如果订阅者离线了很长的时间,那么它所要接收的数据将对网络和数据源产生很大的负担.

单向意味着你只能在数据源对表进行更改,并将结果复制到订阅者,而不能在订阅者来对表进行更改.

级连意味着一个订阅者也可以作为数据的源头来进行数据的发布,从而分担了主数据源的负担.

下边我们就开始配置slony, 我是在FreeBSD下配置的,并且已经分别在两台机器上安装好了PostgreSQL 8.2.4和slonyI-1.2.10,注意这里的版本是非常重要的,因为我在以前装
slony1-1.2.9来进行配置的时候会报版本错误.所以还是推荐大家使用最新的版本.

在配置之前我们还需要把设置两台PostgreSQL的配置文件pg_hba.conf ,添加如下可以让对方使用用户,密码的访问方式.

host all all 10.218.143.17/32 password

host all all 10.218.143.35/32 password

下边我们就开始配置slony,配置slony的过程就是通过slonik来输入配置的命令,我们把slonik的配置命令都写成脚本的形式来执行,方便复用.

第一个脚本:preamble.sk

define CLUSTER test;
define dc35 1;
define dc17 2;
define fqn fully qualified name;
define SUCCESS 0;
define FAILURES 1;

cluster name = @CLUSTER;
node @dc35 admin conninfo = 'dbname=portal host=10.218.143.35 user=pgsql
password=pgsql';
node @dc17 admin conninfo = 'dbname=portal host=10.218.143.17 user=pgsql
password=pgsql';

脚本很简单,就是定义一些常量,集群的名称和节点的信息.注意以后的脚本都要包含这个脚本来引用这些常量.

第二个脚本:initCluster.sk

#!/usr/local/bin/slonik

include <preamble.sk>;

#初始话集群和主节点

init cluster (id=@dc35,comment='primary node');

#保存节点信息

store node
(
id = @dc17,
comment = 'secondary node - dc17'
);

#配置连接信息,就是告诉订阅服务器如何来访问数据源的服务器

store path
(
server = @dc35,
client = @dc17,
conninfo ='dbname=portal host=10.218.143.35 user=pgsql
password=pgsql'
);

store path
(
server = @dc17,
client = @dc35,
conninfo ='dbname=portal host=10.218.143.17 user=pgsql
password=pgsql'
);

第三个文件:buildSet.sk

#!/usr/local/bin/slonik

include<preamble.sk>;

#创建复制集,复制集是slony最小的复制单位.

create set (id=1,origin=@dc35,comment='dc35');

#向复制集中添加表

set add table (set id=1,origin=@dc35,id=1,fully qualified
name='public.f_slony');

第四个文件:subscribeSet.sk

#!/usr/local/bin/slonik

include<preamble.sk>;

#创建订阅,设置数据源和数据目标.

subscribe set (id=1,provider=@dc35,receiver=@dc17,forward=yes);

执行完以上4个文件后,slony的整体结构我们基本上已经配置好了.接下来就要启动slon进程来开始复制的工作了,首先我们要写一个配置文件,让slon进行来连接哪个数据库.

文件:dc17.slon

cluster_name='test'
conn_info='dbname=portal host=10.218.143.17 user=pgsql password=pgsql'

文件:dc35.slon

cluster_name='test'
conn_info='dbname=portal host=10.210.143.35 user=pgsql password=pgsql'

启动slon进程:

slon -f dc35.slon >dc35.log &

slon -f dc17.slon >dc17.log &

启动之后观察进程的log信息,如果没有什么错误提示,那就基本上配置成功了,接下来我们就可以测试.我的测试表是f_slony,我们可以对f_slony进行删除修改操作,然后观察订阅的结果.默认slon的扫描频率是2s,我们可以设置这个时间间隔.

星期三, 十月 08, 2008

技术两则

1、计算机加入域的时候,提示限制已经满,无法加入的时候,可在域服务器上执行adsiedit.msc(此工具存在于windows安装盘的supporttools中的support tools中。展开DOMAIN NC节点,选择"DC="对象,右键选择
属性,选择ms-DS-MachineAccountQutota进行编辑即可。
2、ISA中RADMIN服务。装了一台代理服务器,配置windows2003 R2 SP2,代理软件为ISA
2006 SP1,服务器上装有软件(radmin ( remote administrator 2.2),可怎么也联不上。于是不停的实验,最后成功(不过联接速度不快)。
主要是元素为TCP 入站 端口为RADMIn的端口号(inbound,outbound)设成一致即可。还一个主要的就是要在辅助要设tcp 出站 端口为radmin 端口。
原因:一般都设前面的,后面不设。当你发起联接的时候,也有数据流出(要不怎么双向?)不知道大家明白没?

星期二, 十月 07, 2008

PostgreSQL 在 FreeBSD 上的大版本升级

唉!升级...升级...升级...这一阵子一直升级,这次轮到 PostgreSQL 大升级。PostgreSQL 不像其它软件,即使是 8.2 升级到 8.3,也必须大改,所以无法直接用 upgrade 工具直接升。即使是 Windows 版本,也必须劳心费力一番。这次我用 FreeBSD 版本的升级做为例子,来说明最简单也是最正确的大改版升级步骤。
首先,请先把原本数据库的数据完全备份下来。如果你是 Windows 的使用者或是 Linux 的使用者,你可以考虑使用 pgAdmin III 的「工具」选单中的「备份」或「备份全域」来做,这样简单很多,但是由于 pgAdmin III 的说明太少,我实在看不懂「备份」对话框中选项的含意,所以干脆不用。最正式的方法是使用在 console 下的工具:pg_dump 和 pg_dumpall。两者的差别在于,前者可以针对某个特定的 database 做备份,而后者一般用来把整个服务器数据做完整的备份。我建议直接使用 pg_dumpall 比较方便和快速。方法如下:
$ pg_dumpall > filename
而 filename 你可以自己取,比如 host_a.20080206.full.dump 之类的。名称中加入日期可以让你更清楚知道这个备份档备份的日期。加入 full 字眼,可以提醒自己,这是完整备份,而非一般维护时所做的连续性部份备份的一份。如果你所管理的 PostgreSQL 有超过一台以上,那么最好连数据库主机名称都注明。
对了,差点忘了一件极重要的事!在 Windows 上,只要你使用 administrators 群组中的任何一位使用者登入,然后从开始菜单→PostgreSQL 8.3→Command Prompt,就可以打开「命令提示字符」,而且可以下达 pg_dumpall 指令;但是,在 FreeBSD,一般安装时,预设 root 是不允许执行 PostgreSQL 的相关指令的,所以请先切换成 pgsql 用户,如果你没更动 pgsql 的密码,那么预设的密码很「随机」,也就是任你怎么猜也都猜不出来,但是你可以先用 su 切成 root,再用 su - pgsql 切换成 pgsql 使用者,不用输入任何密码即可,这时就可以执行上述指令。但是请注意,这时最好把这个备份文件移到安全位置,以免继续进行的安装过程中不傎误砍。所以这时候你最好是先跳回 root,请下 exit 指令,然后用 cp 指令把刚才那个 dump 檔 copy 到任何你暂存的位置。
接着正式执行安装。在安装新版本之前,必须先移除旧版本,否则两个 daemon 会抢同一个 TCP port,开机时会有无法预知的状况出现。首先先看一下你的服务器中,目前装了哪些和 PostgreSQL 相关的 package:
# pkg_info grep postgresql
这时候你至少可以看到两个 package 出现在清单中,而我多了一个:
postgresql-client-8.2.6_1 PostgreSQL database (client)postgresql-contrib-8.2.6 The contrib utilities from the PostgreSQL distributionpostgresql-server-8.2.6 The most advanced open-source database available anywhere
一般来说,如果你没有安装Server instrumentation 功能,那么应该只有 client 和 server 两个 package。但是这个小清单并不完整,如果你还有安装 php 和 PostgreSQL 相关的驱动程序,那么这时候并不会出现在这个清单当中。不用担心,你若移除的顺序不对,出现相依的套件,那么把相依套件移除,再来移除目标套件即可。只是这时候你最好拿只笔记下你一共移除了哪些套件,将来才能再一一重新安装回去。我们先以这三个 package 来说好了,如果你没移除旧版本的 server,那么直接移除 client 会被提醒,而且停止进行安装动作。同样的,如果你没先移除旧版本的 contrib 和 php 相关 package,你在移除 client 时也会因为相依套件尚未被移除,而被停止移除 client 的动作。所以若以上述三者为例,则移除的顺序为:
# /usr/local/etc/rc.d/postgresql stop <-- 先停掉目前的 PostgreSQL server# pkg_delete postgresql-contrib-8.2.6# pkg_delete postgresql-server-8.2.6# pkg_delete postgresql-client-8.2.6_1
这时请把 /usr/local/pgsql/data 整个目录移走,像我只是把它改个名称,而让它继续留着,说不定哪天还会用到目录中的某些档案:mv /usr/local/pgsql/data /usr/local/pgsql/data_8.2。然后就可以开始使用 port 来安装 PostgreSQL 8.3 了。在安装前,请先确定你的服务器上的 port tree 是否已是最新的版本,如果你的 port tree 是在 2008/2/5 天亮前更新的,可能 postgresql-server-8.3 还不是最新版本,请先手动更新 port tree。如果已是最新版本,那么切到该 port 目录:
# cd /usr/ports/databases/postgresql83-server# make install clean
FreeBSD 会自动帮你将 8.3 版的 server 和 client 都一并装好。接着把 contrib package 也装起来:
# cd ../postgresql-contrib# make install clean
如果你曾经把 php5 的 driver 移除掉或 JDBC 等等等之前移除掉的套件,现在也请用同样的方法把所有新的版本一一装回去。这样就好了吗?还早,请先看一下你的 /usr/local/pgsql 下是否已经建好新的数据库?
# ls /usr/local/pgsql
如果这个目录下只出现一个刚才更名的 data_8.2,那么代表 Makefile 并没有自动帮你做 initdb,请执行:
# /usr/local/etc/rc.d/postgresql initdb [1]
这时你再下 ls /usr/local/pgsql 指令时,就可以看到多出一个 data 目录了,这样才能正确启动 PostgreSQL:
# /usr/local/etc/rc.d/postgresql start [2]
如果没出现任何错误讯息,那么先恭喜你,安装好了。但是工作才完成一半,再来是把原来备份的数据回存回数据库中:
# su - pgsql$ psql -f filename postgres
也就是先切成 pgsql 使用者,接着执行 PostgreSQL 的交互式指令输入工具:psql,然后把刚才你的 dump 档的名称(这里当然是完整路径名称)取代上面的 filename。接着一些讯息代表着数据库回存的动作,正在做什么动作。如果一切错误讯息都没有出现过,那么恭喜你,工作完成了 80%。对于 Windows 的使用者来说,只要记住最后一条指令,就可以把刚才 pg_dumpall 所备份的数据回存了。Windows 的使用者真幸福。
什么?才完成 80%?是的,有两个档可能之前的版本你有更动过,分别是 postgresql.conf 和 pg_hba.conf,它们在 FreeBSD 中应该位于 /usr/local/pgsql/data 目录下,请把之前曾经更动过的改回去,但是请把 release notes 先读过一遍,因为 postgresql.conf 中很多管理项目都更动过了,可能之前你所更改的项目,8.3 版已经把该项和其它项目合并,并取了新的项目名称了,先读过 release notes 的 E.1.2.2. Configuration Parameters 是很重要的。还有,刚才如果把旧的 8.2 版的 /usr/local/pgsql/data 目录直接删掉,那此刻就糗了,你必须用你的记忆力回想你曾经更动过的项目和内容,所以我才会建议只需把它更名,保留下来。
若是你对 PostgreSQL 的备份和回存有兴趣,英文程序也还可以,那么请进一步参考 PostgreSQL 的在线说明:Chapter 24. Backup and Restore
附注:[1] 当然啰,如果新的 data 目录已经生成,也就是 Makefile 已经自动帮你执行 initdb 的动作了,那么 initdb 这个动作就可以省了。我会特别这样提的原因是,我现这两天的抢先版本的 Makefile 怪怪的,有时不会自动执行 initdb 动作。 [2] 如果你没有像我这么麻烦的用 pg_dumpall 和 psql 来备份回存,直接升级(还是得先移除旧版本再安装新版本),表面上看起来还是可以升级,但是,事实上 Makefile 会把你旧版本的 postgresql.conf 和 pg_hba.conf 复制一份到新的 data 目录中。如我文中所说的,postgresql.conf 在这个版本中有大更动,因此,当你打算启动数据库时,会出现 FATAL ERROR!解决方法很简单,用不着重灌,把新的 data 目录移走(一样用更名的),执行 /usr/local/etc/postgresql initdb,重做一次 initdb,就会有一个全新版本的 data 了。[3] 这篇文章仅适用于负载不大的服务器上,倘若你的服务器是 mission critical 的大用量服务器,虽然基本的升级概念是一样的,也就是数据库大升级等同重新安装,所以服务势必一定得中断,但是事前的演练和良好的计划,可以让停机服务的时间减少。在这种情况,建议必须得增加备援服务器,在主服务器升级的同时,让备援服务器先暂时服务。说到这里,就得牵涉到「数据库复制」(replication) 的主题了。等我有空再来写一篇 PostgreSQL 和 replication 相关的文章。

FreeBSD 安装 PostgreSQL 的笔记

本文是笔者用来记录 FreeBSD 安装 PostgreSQL 的笔记,文中有提到为了增加 PostgreSQL 的 max_connections 数目,而调整了 FreeBSD 系统的 /etc/sysctl.conf & /boot/loader.conf 两个档案。如果你只是想安装 PostgreSQL 玩玩,而不需要调整 max_connections 数目,你可以忽略修改上述两个档案的内容。
1.修改 FreeBSD 系统参数
修改 /etc/sysctl.conf 增加以下指令kern.ipc.shmmax=134217728kern.ipc.shmall=32768kern.ipc.semmap=256
修改 /boot/loader.conf 增加以下指令kern.ipc.semmni=256kern.ipc.semmns=512kern.ipc.semmnu=256
2.安装 PostgreSQLcd /usr/ports/databases/postgresql83-servermake install
安装 libxml2 模块cd /usr/ports/textproc/libxml2make install
安装 libxslt 模块cd /usr/ports/textproc/libxsltmake install
安装 XML for PostgreSQLcd /usr/ports/databases/postgresql83-server/work/postgresql-8.3.3/contrib/xml2gmake install
安装 adminpackcd /usr/ports/databases/postgresql83-server/work/postgresql-8.3.3/contrib/adminpackgmake install
3.修改 /etc/rc.conf 增加以下指令,以便开机自动执行 PostgreSQL
postgresql_enable=”YES”# optionalpostgresql_data=”/pgdb”postgresql_flags=”-w -s -m fast”postgresql_initdb_flags=”–encoding=utf-8 –lc-collate=C”postgresql_class=”default”
4.初始 PostgreSQL 数据库建立放置 PostgreSQL 数据库档案的目录,同时修改该数据匣权限,改完权限后便初始化 PostgreSQL 数据库
mkdir /pgdb
chown pgsql:pgsql /pgdb
su -l pgsql -c “initdb -D /pgdb -E utf8″
5.修改 /pgdb/postgresql.conf 以便符合实际系统运作需求,这边大家可以斟酌调整listen_addresses = ‘*’ //聆听其它主机联机讯号max_connections = 200 //增加同时联机数量#log_destination = ’syslog’ // 批注log_destination = ’stderr’ // 标准输出 loglog_directory = ‘pg_log’log_filename = ‘postgresql-%Y-%m-%d_%H%M%S.log’log_rotation_age = 1dlog_rotation_size = 10MBclient_min_messages = noticelog_min_messages = noticelog_error_verbosity = defaultlog_min_error_statement = errorlog_duration = onlog_line_prefix = ‘%u %d %h %i’log_statement = ‘mod’stats_start_collector = onstats_row_level = on
6.修改 /pgdb/pg_hba.conf 以便可以透过网络连接该 PostgreSQL 服务器。# “local” is for Unix domain socket connections onlylocal all all trust# IPv4 local connections:host all all 127.0.0.1/32 trusthost all all 10.3.1.21/32 trusthost all all 10.3.1.22/32 trusthost all all 10.3.1.23/32 trusthost all all 10.1.1.0/32 trusthost all all 10.2.1.0/24 md5# IPv6 local connections:host all all ::1/128 md5host all smart_teacher 10.3.1.0/24 md5host all postgres 10.3.1.0/24 md5host all ischool 10.3.1.0/24 md5
7.启动 PostgreSQL/usr/local/etc/rc.d/postgresql start
8.建立预设使用者su -l pgsql -c “createuser -P”
账号名称:postgres
9.启用其它相关外挂套件
启用 adminpacksu -l pgsql -c “psql -U postgres postgres < /usr/local/share/postgresql/contrib/adminpack.sql”
启用 XML 支援 for PostgreSQLsu -l pgsql -c “psql -U postgres postgres < /usr/local/share/postgresql/contrib/pgxml.sql”
完成以上步骤,基本上你的 PostgreSQL 应该就可以正常运作了。
— 2008/08/13 新增 —
PostgreSQL 安装时修改 Makefile 档案,可以解决 xslt_process 无法安装的问题
找到底下设定
CONFIGURE_ARGS+=–with-libraries=${LOCALBASE}/lib \–with-includes=${LOCALBASE}/include \–enable-thread-safety \–with-docdir=${DOCSDIR} \–with-libxslt \–with-libxml增加红色的部份

ASP.Net2.0连接PostgreSQL数据库

PostgreSQL 是一种非常先进的对象-关系型数据库管理系统(ORDBMS),目前功能最强大,特性最丰富和最先进的自由软件数据库系统。有些特性甚至连商业数据库都不具备。这个起源于伯克利(BSD)的数据库研究计划目前已经衍生成一项国际开发项目,并且有非常广泛的用户。自从MySQL 被Sun 收购后,相信很多对该收购不放心的朋友会转而看好PostgreSQL 的前途。之前我曾经博客中介绍过Ubuntu中通过源码安装编译安装PostgreSQL。在ASP.Net中使用PostgreSQL数据库作为后端数据存储,连接方法有以下两种:

一、采用商业组件的方式PostgreSQLDirect .NET目前商业组件使用较多的是PostgreSQLDirect .NET,不过这个需要付费,最便宜的标准版网上报价也要1000元人民币。PostgreSQLDirect .NET是一款为Microsoft .NET Framework提供直接PostgreSQL数据库连接的数据发生器控件。它完全基于ADO.NET方法,因此您完全可以采用标准ADO.NET数据提供的方法来使用它。安装之后会在Visual Studio 2005工具栏出现PostgreSQLDirect组件包含了PgSqlConnection PgSqlCommand PgSqlDataAdapter 等控件(如下图),然后在项目里添加引用:CoreLab.Data和CoreLab.PostgreSql,可以拖放控件连接数据库,使用方法2005的和自带控件基本相同。也可以写代码连接数据库,具体代码如下:
PgSqlDataSet ds = new PgSqlDataSet();string sql = "SELECT * FROM test WHERE tid=3000";PgSqlConnection con = new PgSqlConnectio("user id=username;Password=pwd;host=hostname;database=test");PgSqlDataAdapter da = new PgSqlDataAdapter(sql, con);da.Fill(ds);this.dataGridView1.DataSource = ds;this.dataGridView1.DataMember = ds.Tables[0].ToString();

二、使用开源组件Npgsql
Npgsql是一个为开源数据库PostgreSQL提供.NET Framework平台下数据驱动服务的库函数程序集(.NET Data Provider),它允许用户在.NET Framework平台下建立用于访问PostgreSQL数据库的应用。
解压后将其中的两个dll文件复制到应用程序的Bin目录下,
然后在项目里添加引:Mono.Security和NPgSQL,
在代码里添加using NpgSQL;
具体代码如下:
string sql = "SELECT * FROM test WHERE tid=3000";
NpgsqlConnection con = new NpgsqlConnection("server=hostname;uid=username;pwd=pwd;database=test");
NpgsqlDataAdapter da = new NpgsqlDataAdapter(sql,con);
DataSet ds = new DataSet();
da.Fill(ds);
this.dataGridView1.DataSource = ds;
this.dataGridView1.DataMember = ds.Tables[0].ToString();

相关链接:
PostgreSQLDirect .NET官方网站 http://www.crlab.com/pgsqlnet/
Npgsql官方网站 http://pgfoundry.org/projects/npgsql
PostgreSQL官方网站 http://www.postgresql.org/

星期一, 九月 15, 2008

FlyTreeView V4.3.1.43破解手记


   官方网址:http://www.9rays.net/
    未破解前,会有45天的试用期限制。超其以后运行时会有异常:“9Rays.Net FlyTreeView for ASP.NET 2.0 evaluation period has

expired.”
    破解工具:ildasm,ilasm,StrongNameRemove20, UEdit, Reflector(个人习惯使用,这个随便了) [注] ildasm[vs2003版]可以到看雪下载

修改版,原版的有限制
    这个DLL的破解的关键是让它永不过期或者修改一个足够大的时间值让我们使用就可以了,网上有破解的就是直接修改它的过期期限,使用

用户可以使用的期限加长,达到破解。在本例中,我使用的是直接把过期异常干掉,使得永不过期。

  1. 用ildasm打开NineRays.WebControls.FlyTreeView.dll,转存为aaa.il;

  2. 找到用UEdit打开aaa.il,并找到"has expired",来到如下的代码处理
// 为了便于分析,我把reflactor的反编译代码贴出来
public FlyTreeView()
{
    this.NodeEvents = new List();
    DateTime maxValue = DateTime.MaxValue;
   
    // 当然这个异常我们也可以去掉,但在本次过程中,我们主要去掉下面那个过期的异常部分
    try
    {
        maxValue = File.GetLastWriteTime(base.GetType().Assembly.Location);
    }
    catch
    {
        throw new Exception("Unknown TRIAL version error has occurred.";
    }

    // 我们最关心的是过期的异常,所以这个异常是关键部分
    if (maxValue < DateTime.Now.AddDays(-45))
    {
        throw new Exception("9Rays.Net FlyTreeView for ASP.NET 2.0 evaluation period has expired.";
    }
    this._flyControlCommon = new FlyControlCommon(this, this.ViewState, this.Context);
    this._dataBindings = new FlyNodeBindingCollection();
    this._nodes = new FlyTreeNodeCollection(this);
    this._nodeTypes = new FlyNodeTypeCollection();
    this.ShadowNodes = new FlyTreeNodeCollection(this);
}

看了反编译的代码,简单一点儿,我们只要把
    if (maxValue < DateTime.Now.AddDays(-45))
    {
        throw new Exception("9Rays.Net FlyTreeView for ASP.NET 2.0 evaluation period has expired.";
    }
这段代码干掉就可以了啊?当然,我们也可以把
    try
    {
        maxValue = File.GetLastWriteTime(base.GetType().Assembly.Location);
    }
    catch
    {
        throw new Exception("Unknown TRIAL version error has occurred.";
    }
这段代码也干掉,呵呵。
那我们就开始吧,
下面我们结合反编译代码对下面的程序进行分析,并去掉关键的异常代码部分

// 源代码如下
  .method public hidebysig specialname rtspecialname
          instance void  .ctor() cil managed
  {
    // 代码大小       173 (0xad)
    .maxstack  5
    .locals init (valuetype [mscorlib]System.DateTime V_0,
             string V_1,
             valuetype [mscorlib]System.DateTime V_2)

    //***     this.NodeEvents = new List(); 对应的IL代码开始   ***//
    IL_0000:  ldarg.0
    IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1
NineRays.WebControls.FlyTreeNodeEventArgs>::.ctor()
    IL_0006:  stfld      class [mscorlib]System.Collections.Generic.List`1

NineRays.WebControls.FlyTreeView::NodeEvents
    //***     this.NodeEvents = new List(); 对应的IL代码结束   ***//

//**************** 获取最大的时间值,并保存到本地变量maxValue里对应IL就是V_0
    //***     DateTime maxValue = DateTime.MaxValue; 对应的IL代码开始   ***//
    IL_000b:  ldarg.0
    IL_000c:  call       instance void [System.Web]System.Web.UI.WebControls.HierarchicalDataBoundControl::.ctor()
    IL_0011:  ldsfld     valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::MaxValue
    IL_0016:  stloc.0
    //***     DateTime maxValue = DateTime.MaxValue; 对应的IL代码结束   ***//




// ************************************   第一个异常的代码对应 开始  ***********************************//
//************ 取文件的最后修改时间值,正常的话保存到本地变量maxValue里对应IL就是V_0,如果错误,则异常
*  reflector反编译代码
*    try
*    {
*        maxValue = File.GetLastWriteTime(base.GetType().Assembly.Location);
*    }
*    catch
*    {
*        throw new Exception("Unknown TRIAL version error has occurred.";
*    }

* 对应的IL 代码
    .try
    {
      IL_0017:  ldarg.0
      IL_0018:  call       instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
      IL_001d:  callvirt   instance class [mscorlib]System.Reflection.Assembly [mscorlib]System.Type::get_Assembly()
      IL_0022:  callvirt   instance string [mscorlib]System.Reflection.Assembly::get_Location()
      IL_0027:  stloc.1
      IL_0028:  ldloc.1
      IL_0029:  call       valuetype [mscorlib]System.DateTime [mscorlib]System.IO.File::GetLastWriteTime(string)
      IL_002e:  stloc.0
      IL_002f:  leave.s    IL_003d

    }  // end .try
    catch [mscorlib]System.Object
    {
      IL_0031:  pop
      IL_0032:  ldstr      "Unknown TRIAL version error has occurred."
      IL_0037:  newobj     instance void [mscorlib]System.Exception::.ctor(string)
      IL_003c:  throw

    }  // end handler
// ************************************   第一个异常的代码对应 结束  ***********************************//



// *************************************  第二个异常的代码对应 开始  ***********************************//
//************ maxValue跟当前时间-45天相比较,如果在试用期内,则正常,不在试用期内,则异常
*  reflector反编译代码
*    if (maxValue < DateTime.Now.AddDays(-45))
*    {
*        throw new Exception("9Rays.Net FlyTreeView for ASP.NET 2.0 evaluation period has expired.";
*    }

* IL 代码开始
    // ******** 这里开始是判断当前时间与文件创建时间的比较,如果在试用期内,则正常试用,否则则抛出过期的异常
    IL_003d:  ldloc.0
    IL_003e:  call       valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
    IL_0043:  stloc.2
    IL_0044:  ldloca.s   V_2
    IL_0046:  ldc.r8     -45.
    IL_004f:  call       instance valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::AddDays(float64)
    IL_0054:  call       bool [mscorlib]System.DateTime:p_LessThan(valuetype [mscorlib]System.DateTime,
                                                                     valuetype [mscorlib]System.DateTime)
    IL_0059:  brfalse.s  IL_0066        // 如果没超出试用期,则跳转,否则抛异常
// 我们的思路,不管是否超出试用期都不让它抛出异常,那么我们最简单的方法,就是把抛异常的代码段干掉
// 那我们还等什么呀,直接把下面的关键部分代码注释掉不就行了吗,呵呵:) 是不是很Easy的啊
//*************************   关键位置开始  ^_^  *******************************************************//
    //IL_005b:  ldstr      "9Rays.Net FlyTreeView for ASP.NET 2.0 evaluation p"
    //+ "eriod has expired."                // 查找到的位置***************************
    //IL_0060:  newobj     instance void [mscorlib]System.Exception::.ctor(string)
    //IL_0065:  throw
//*************************   关键位置结束 ^_^   *******************************************************//



// *************************************  其他类域的初始化代码 开始  ***********************************//
*  reflector反编译代码
*    this._flyControlCommon = new FlyControlCommon(this, this.ViewState, this.Context);
*    this._dataBindings = new FlyNodeBindingCollection();
*    this._nodes = new FlyTreeNodeCollection(this);
*    this._nodeTypes = new FlyNodeTypeCollection();
*    this.ShadowNodes = new FlyTreeNodeCollection(this);

* IL 代码开始
    IL_0066:  ldarg.0
    IL_0067:  ldarg.0
    IL_0068:  ldarg.0
    IL_0069:  callvirt   instance class [System.Web]System.Web.UI.StateBag [System.Web]System.Web.UI.Control::get_ViewState()
    IL_006e:  ldarg.0
    IL_006f:  callvirt   instance class [System.Web]System.Web.HttpContext [System.Web]System.Web.UI.Control::get_Context()
    IL_0074:  newobj     instance void class NineRays.WebControls.FlyControlCommon`1
NineRays.WebControls.FlyTreeView>::.ctor(!0,
                                                                                                                              

      class [System.Web]System.Web.UI.StateBag,
                                                                                                                              

      class [System.Web]System.Web.HttpContext)
    IL_0079:  stfld      class NineRays.WebControls.FlyControlCommon`1

NineRays.WebControls.FlyTreeView::_flyControlCommon
    IL_007e:  ldarg.0
    IL_007f:  newobj     instance void NineRays.WebControls.FlyNodeBindingCollection::.ctor()
    IL_0084:  stfld      class NineRays.WebControls.FlyNodeBindingCollection NineRays.WebControls.FlyTreeView::_dataBindings
    IL_0089:  ldarg.0
    IL_008a:  ldarg.0
    IL_008b:  newobj     instance void NineRays.WebControls.FlyTreeNodeCollection::.ctor(object)
    IL_0090:  stfld      class NineRays.WebControls.FlyTreeNodeCollection NineRays.WebControls.FlyTreeView::_nodes
    IL_0095:  ldarg.0
    IL_0096:  newobj     instance void NineRays.WebControls.FlyNodeTypeCollection::.ctor()
    IL_009b:  stfld      class NineRays.WebControls.FlyNodeTypeCollection NineRays.WebControls.FlyTreeView::_nodeTypes
    IL_00a0:  ldarg.0
    IL_00a1:  ldarg.0
    IL_00a2:  newobj     instance void NineRays.WebControls.FlyTreeNodeCollection::.ctor(object)
    IL_00a7:  stfld      class NineRays.WebControls.FlyTreeNodeCollection NineRays.WebControls.FlyTreeView::ShadowNodes

// *************************************  其他类域的初始化代码 结束  ***********************************//

    IL_00ac:  ret
  } // end of method FlyTreeView::.ctor

  3. 别忘了看一下文件开头有没有加publickey,还真的有呀,我这里找到如下的代码
  .publickey = (00 24 00 00 04 80 00 00 94 00 00 00 06 02 00 00   // .$..............
                00 24 00 00 52 53 41 31 00 04 00 00 01 00 01 00   // .$..RSA1........
                EB FD B1 7F 49 35 9E C0 95 39 EE 11 CD D2 30 A3   // ....I5...9....0.
                72 D3 A0 72 DD 10 42 86 EA 59 60 AB 5F C4 3F 7F   // r..r..B..Y`._.?.
                09 A7 6C 5E FA D0 54 FF B9 B7 12 C6 13 DD 6D C3   // ..l^..T.......m.
                D3 5B E4 90 76 EC CC 92 CD B0 7E 9B 22 A5 A4 71   // .[..v.....~."..q
                D0 EA 1A EE 0D 6B BE 82 55 D6 7E B2 7F B1 32 DB   // .....k..U.~...2.
                50 5B F7 50 07 19 91 59 22 BF FB 82 A9 0B F7 DE   // P[.P...Y".......
                87 36 F9 6C 19 CA D9 63 55 63 78 44 C6 33 6C 55   // .6.l...cUcxD.3lU
                39 00 7B 0A 89 8E C2 C5 8E 4A 52 C2 8E 23 37 B3 ) // 9.{......JR..#7.
  .hash algorithm 0x00008004

  还在想什么呢?直接删除呀,嘿嘿

  4. 到现在破解就完工了,可以编译新的程序了。ilasm /dll /resource=aaa.res aaa.il,得到的aaa.dll即为完美破解版了。

  顺便说一句,使用的时候,最好先安装原版,再把破解版覆盖,就好了
  如果感觉好用,请支持正版

hook compileMethod的几种常见形式


compileMethod算是.NET中的万能断点,对付一般的.NET加密壳均可采用hook compileMethod的方式得到IL代码和相关信息,保护层次较深的.NET壳除外。

我们自己在编写程序hook该方法时,一般有以下几种形式。

第一种形式是:my_compileMethod(既需要替换的方法)采用compileMethod的原型。这种形式代码漂亮,结构工整,编程方便,推荐使用。通常代码可以如下定义:

int __stdcall my_compileMethod(ULONG_PTR classthis,

                                                     ICorJitInfo *comp,

                                                     CORINFO_METHOD_INFO *info,

                                                      unsigned flags,BYTE **nativeEntry,

                                                     ULONG   *nativeSizeOfCode)

{

//你的代码

//调用原始的compileMethod

int nRet = compileMethod(classthis, comp, info, flags, nativeEntry, nativeSizeOfCode);

return nRet;

}

上面的代码还有一个优点,就是可直接兼容x64位

    第二种形式:naked。既调用约定不采用__stdcall,而是用naked。代码如下:

void __declspec(naked) mycompileMethod()

{

//你的代码

}

这种形式明显低级一些,许多工作要自己做,比如取CORINFO_METHOD_INFO *时,就要从堆栈中取值:

__asm

{

pushad

pushfd

mov eax,DWORD PTR[esp+48]

push eax

pop pmethodinfo

}

还有就是,直接使用x86汇编代码则无法兼容x64,需编写两套程序。当然,也不是一点好处没有,比如很多hook程序在开始喜欢保存全部的寄存器和状态,这里就可以使用pushad和pushfd了。

上面两种方法,在具体hook时,通常采取替换CILJit::'vtable'的第一项: dd offset CILJit::compileMethod的地址实现。该值可以通过getJit()函数获得。

第三种方法就更直接了,在JIT的路径中找到合适的位置后,直接Jmp。这种naked+jmp的方式,最底层,也最灵活,在对于某些hook位置较深的壳来说,这也是唯一的方法。不过代码编写时,工作量就比前两种要稍大一些了。

Btw:在编写.NET内核hook程序时,C++/CLI是不二之选。真得很爽!


    compileMethod方法在各类.NET内核的文章中出现频率相当高,因为它是JIT引擎工作的关键函数。其原型如下(参考sscli代码):

代码:
CorJitResult __stdcall FJitCompiler::compileMethod (                ICorJitInfo*                  compHnd,               /* IN */                CORINFO_METHOD_INFO*           info,                  /* IN */                unsigned                   flags,                 /* IN */                BYTE **                    entryAddress,          /* OUT */                ULONG     *                   nativeSizeOfCode       /* OUT */                )
     一般的用法是通过该函数的第二个参数COFINFO_METHOD_INFO取得代码的IL和大小。但其实还可以更加深入,注意第一个传入参数很有意思,指向了ICorJitInfo接口。该接口定义如下:
代码:
/*********************************************************************************  * a ICorJitInfo is the main interface that the JIT uses to call back to the EE and  *      get information  *********************************************************************************/ class ICorJitInfo : public virtual ICorDynamicInfo {//省略}
     这说明ICorJitInfo继承了ICorDynamicInfo接口。而后者的定义如下:
代码:
/*****************************************************************************  * ICorDynamicInfo contains EE interface methods which return values that may  * change from invocation to invocation.     They cannot be embedded in persisted  * data; they must be requeried each time the EE is run.  *****************************************************************************/  class ICorDynamicInfo : public virtual ICorStaticInfo {//省略}
     这说明ICorDynamicInfo又继承了ICorStaticInfo。接着来,继续看ICorStaticInfo的代码:

代码:
/*****************************************************************************  * ICorStaticInfo contains EE interface methods which return values that are  * constant from invocation to invocation.     Thus they may be embedded in  * persisted information like statically generated code. (This is of course  * assuming that all code versions are identical each time.)  *****************************************************************************/ class ICorStaticInfo : public virtual ICorMethodInfo, public virtual ICorModuleInfo,                           public virtual ICorClassInfo,     public virtual ICorFieldInfo,                           public virtual ICorDebugInfo,     public virtual ICorArgInfo,                           public virtual ICorLinkInfo,      public virtual ICorErrorInfo
     这一次牛了,ICorStaticInfo继承了8个接口的方法,其中第一个为ICorMethodInfo。该接口定义了许多与方法相关的函数,如果能调用它,在hook中是非常爽的。那可不可以调用呢?当然可以,compileMethod的第一个参数就是我们需要的。
     比如我们需要取得当前hook方法的方法名,可以调用ICorMethodInfo中的如下方法:
代码:
       virtual const char* __stdcall getMethodName (                CORINFO_METHOD_HANDLE          ftn,           /* IN */                const char                   **moduleName     /* OUT */                ) = 0;
     这里需要传入CORINFO_METHOD_HANDLE这个参数,该参数是.NET内核中表示方法的核心结构,在sscli中也未公开。不过它的值是可以取得的,从compileMethod第二个参数CORINFO_METHOD_INFO结构的第一项中便可以取得:
代码:
struct CORINFO_METHOD_INFO {     CORINFO_METHOD_HANDLE          ftn;        CORINFO_MODULE_HANDLE          scope;               BYTE *                         ILCode;        unsigned                       ILCodeSize;        unsigned short                 maxStack;        unsigned short                 EHcount;        CorInfoOptions                 options;        CORINFO_SIG_INFO               args;        CORINFO_SIG_INFO               locals; };
     同样,在调用ICorModuleInfor接口中的许多方法时,也需要传入CORINFO_MODULE_HANDLE作为参数,同样可以从CORINFO_METHOD_INFO结构中获得该值。比如调用findClass函数,以取得CORINFO_CLASS_HANDLE,定义如下:
代码:
       virtual CORINFO_CLASS_HANDLE __stdcall findClass (                CORINFO_MODULE_HANDLE          module,        /* IN     */                unsigned                       metaTOK,       /* IN     */                CORINFO_CONTEXT_HANDLE         context,       /* IN     */                CorInfoTokenKind               tokenKind = CORINFO_TOKENKIND_Default /* IN     */                ) = 0;
     其中出现了CORINFO_CONTEXT_HANDLE。该结构也非常容易取得,具体参考sscli,就不详述了。
     具体在VS中编程时,可以添加corinfo.h和corjit.h,并在同一目录下添加corhdr.h,便可顺利编译通过。
     最后还有一个问题,sscli毕竟是早期框架了,还是精简版,现在还能直接使用吗?不妨分析一下。随便运行一个.NET程序,用WinDbg调试并中断在compileMethod处,查看ICorJitInfo值所指的内存:
代码:
0012ea38 79f10654 mscorwks!CEEJitInfo::`vbtable' 0012ea3c 00174d18  0012ea40 00997850  0012ea44 00107210  0012ea48 00000000  0012ea4c 0018a610  0012ea50 00000000  0012ea54 00000000  0012ea58 00000000  0012ea5c 0012ea40  0012ea60 00000000  0012ea64 00000000  0012ea68 00000000  0012ea6c 79f105b8 mscorwks!CEEJitInfo::`vftable' 0012ea70 00000000  0012ea74 79f10584 mscorwks!CEEJitInfo::`vftable' 0012ea78 00000000  0012ea7c 79f104e0 mscorwks!CEEJitInfo::`vftable' 0012ea80 00000000  0012ea84 79f104bc mscorwks!CEEJitInfo::`vftable' 0012ea88 00000000  0012ea8c 79f104a4 mscorwks!CEEJitInfo::`vftable' 0012ea90 00000000  0012ea94 79f10498 mscorwks!CEEJitInfo::`vftable' 0012ea98 00000000  0012ea9c 79f10494 mscorwks!CEEJitInfo::`vftable' ...//下略
     这里又涉及到VC编译器对类的vftable和vbtable在内存中的布局问题了,最先两项是类自身定义的虚方法表和虚基址表,相关资料请自己查阅。我们跟进vftable:
代码:
79f10624 79f106ac mscorwks!CEEJitInfo::getMemoryManager 79f10628 79f11d82 mscorwks!CEEJitInfo::allocMem 79f1062c 79f11f39 mscorwks!CEEJitInfo::allocGCInfo 79f10630 7a12b8cb mscorwks!CEEJitInfo::getEHInfo 79f10634 7a12b6cf mscorwks!CEEJitInfo::yieldExecution 79f10638 79f16373 mscorwks!CEEJitInfo::setEHcount 79f1063c 79f16491 mscorwks!CEEJitInfo::setEHinfo 79f10640 7a12ed41 mscorwks!CEEJitInfo::logMsg 79f10644 7a27ffcc mscorwks!ZapperModule::doAssert 79f10648 7a12eeb6 mscorwks!CEEJitInfo::allocBBProfileBuffer 79f1064c 7a2c2fa1 mscorwks!MDInternalRO::ConvertTextSigToComSig 79f10650 79f0efee mscorwks!CEEJitInfo::isVerifyOnly 79f10654 fffffffc
       这些地址在静态编译时就已经确定了,因此如果用IDA反编译mscorwks.dll,同样会得到这些值。这便是最新的.NET框架内核中ICorJitInfo定义的方法。和sscli对比下(在corjit.h文件中),完全一样!(也可以再对比其它关键接口的方法,应该也是一样,我没有详细对比了。)
     这样,我们就得到如下的结论:.NET内核框架从2.0开始,内核变化不大,包括sscli的内核代码,这些可以从内部函数的定义看出来;通过hook compileMethod,可以得到ICorJitInfo等关键接口,并调用其中的许多内部方法;具体编写时,可以在VS中引入sscli的corinfo.h、corjit.h和corhdr.h等文件,之后便可以直接调用。因此,通过compileMethod,我们可以做的事很多很多,远不限于仅获得某个方法的IL。

Net 2.0 的泛型小结

微软在.Net FrameWork 2.0中,引入了范型,相比.Net FrameWork 1.1中的三个集合类。
范型具有类型安全、无需GC的优点,对值类型无需进行性能损失很大的装箱与拆箱操作。
主要看 System.Collections.Generic 命名空间,略作总结:
ArrayList --> List
Hashtable --> Dictionary
SortedList --> SortedDictionary
Stack --> Stack
Queue --> Queue
LinkedList 无对应类

使用Reflactor反编译.Net程序的经验

相信大多数.Net程序员都有使用Reflactor的经历。无论出于什么目的,当用Reflactor反编译托管程序后,还想对其代码加以修改,那么本文所列举的可能是一份有用的参考。

用Reflactor的FileGenerator插件反编译代码后可以得到包括项目文件的源代码,但代码中存在各种问题,一般无法一次编译通过,以下将详谈这些问题:
枚举问题

为了代码可读,可能需要花点时间查阅metadata把int值修改回枚举值,尤其是想利用窗体设计器的,VS2008可能还不理解int值。
属性问题

比如一个叫Names的属性被反编译后,可能还原为的set_Names(names),get_Names()方法,逐个替换可能很慢,可采用正则表达式整体替换。对于set_Xxx(xxx)方法,可替换set_{[a-z]*}\(为\1 = (对于get_Xxx()方法,可替换get_{[a-z]*}\(\)为\1然后,再修复个别被误换的方法。
委托和回调函数问题

一般会被还原为add_Xxx(MethodsName)方法,需要改为 += MethodsName
资源问题

需要使用.Net Framework SDK 下的 resgen.exe 工具,反编译嵌入资源文件*.resources为*.resx文件,语法为:ResGen.exe *.resources *.resx,然后将*.resx包含入项目,就会自动和同名的窗体文件*.cs关联,如果没有关联可采用先排除再添加大法,一一搞定。
命名空间问题

如果需要切换到IDE的窗体设计器,而不出错,则还需要在*.cs中添加比如System.Windows.Forms的命名空间前缀。
窗体设计器识别问题

需要把以下代码ComponentResourceManager manager = new ComponentResourceManager(typeof(ClassName));替换为System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ClassName));窗体设计器才能正常识别。

星期日, 六月 29, 2008

windows2003 免费杀毒软件

AntiVir Personal Edition 中文名叫"小红伞",是一款相当优秀的免费杀毒软件,主要特色是系统资源占用很少,内存占用很低,杀毒能力不一般。在系统资源占用上只有NOD32能与其匹敌,但NOD32在木马查杀方面却很弱。我在使用了Avira+LNS(一款防火墙)后电脑几乎没有中过毒。主要缺点是有时会有误杀现象,不过,现在这种现象越来越少。07年4月份,小红伞升级版本,不支持windows 2003 操作系统,但通过以下设
置我们发现可以实现完美安装。

第一种方法:下载 AntiVir Personal Edition 老版-07年以前的版本,安装后,下载
AntiVir Personal Edition 最新版本(Avira AntiVir PersonalEdition Classic
Windows 2000 / XP / Vista 32 Bit and 64 Bit),覆盖安装即可。

第二种方法:下载AntiVir Personal Edition 老版,安装后到这里下载License file(授权文件)-这是小红伞的官方网站。下载后可将该文件直接放到Avira Antivir 的安装目录下。重新启动,升级病毒库
后,你就会发现你的小红伞已经更新到最新版了。

下面的图片是我的windows 2003中Avira Antivir 所占的内存,你看是不是很省内存?

当然,你一切安装完毕后,需要对它进行一下设置。下载这个德国小红伞设置图,参照设置一下就可以了

星期三, 五月 21, 2008

EXcel多表之间操作(VBA)

"from [产品资料$] a,[进货$] b,[销售$] c where a.产品代码=b.产品代码 and a.产品代码=c.产品代码"
'[a65536].End(xlUp).Offset(1, 0).CopyFromRecordset conn.Execute(Sql)
 
Sub 汇总()
Range("A2:J100").ClearContents
Set conn = CreateObject("adodb.connection")
conn.Open "provider=microsoft.jet.oledb.4.0;extended properties=excel 8.0;data source=" & ThisWorkbook.FullName
Sql = "select A.产品代码,A.名称,sum(B.进货数量),B.进货单价,sum(B.进货金额),sum(C.销售数量),C.销售单价,sum(C.销售金额),sum(C.销售数量)*(C.销售单价-B.进货单价),sum(B.进货数量)-sum(C.销售数量) from [产品资料$] as A,[进货$] as B,[销售$] as C where A.产品代码=B.产品代码 and B.产品代码=C.产品代码 group by A.产品代码,A.名称,B.进货单价,C.销售单价"
Sheet2.[a2].CopyFromRecordset conn.Execute(Sql)
conn.Close
Set conn = Nothing
End Sub
 

Excel表格下面的SQL对比

在excel工具--宏下面--VBA然后在模块中加入模块1,然后显示窗体设计器下面加入一个按钮
Sub 查询()
  Dim MYSTR As String
   Range("A2:AT1500").ClearContents
   Set CONN = CreateObject("adodb.connection")
   CONN.Open "provider=microsoft.jet.oledb.4.0;extended properties=excel 8.0;data source=" & ThisWorkbook.FullName
  ' For I = 1 To Sheets.Count - 2
   '    sq1 = sq1 & "select * from [" & Sheets(I).Name & "$] where 工资级别='6级'" & " UNION "
  ' Next I
  ' sq1 = Left(sq1, Len(sq1) - 7)
  Sq1 = "select * from [" & Sheets(2).Name & "$] where 监管条件 like '%/B'  and b2 not in (select b2 from [" & Sheets(1).Name & "$])"
  [a2].CopyFromRecordset CONN.Execute(Sq1)
  CONN.Close
  Set CONN = Nothing
 End Sub

星期二, 五月 20, 2008

C#--事件教程


本教程展示如何在 C# 中声明事件、调用事件和挂接到事件。
 
C# 中的"事件"是当对象发生某些有趣的事情时,类向该类的客户提供通知的一种方法。事件最常见的用途是用于图形用户界面;通常,表示界面中的控件的类具有一些事件,当用户对控件进行某些操作(如单击某个按钮)时,将通知这些事件。
 
但是事件未必只用于图形界面。事件为对象提供一种通常很有用的方法来发出信号表示状态更改,这些状态更改可能对该对象的客户很有用。事件是创建类的重要构造块,这些类可在大量的不同程序中重复使用。
 
使用委托来声明事件。如果您尚未学习"委托教程",您应先学习它,然后再继续。请回忆委托对象封装一个方法,以便可以匿名调用该方法。事件是类允许客户为其提供方法(事件发生时应调用这些方法)的委托的一种方法。事件发生时,将调用其客户提供给它的委托。
 
除声明事件、调用事件和与事件挂钩的示例以外,本教程还介绍下列主题:
 
事件和继承
接口中的事件
.NET Framework 指南
示例 1
下面的简单示例展示一个 ListWithChangedEvent 类,该类类似于标准的 ArrayList 类,而且,每当列表内容更改时,该类均调用 Changed 事件。这样一个通用用途的类可在大型程序中以多种方式使用。
 
例如,某字处理器可能包含打开的文档的列表。每当该列表更改时,可能需要通知字处理器中的许多不同对象,以便能够更新用户界面。使用事件,维护文档列表的代码不需要知道需要通知谁,一旦文档列表发生了更改,将自动调用该事件,正确通知每个需要通知的对象。使用事件提高了程序的模块化程度。
 
// events1.cs
using System;
namespace MyCollections
{
   using System.Collections;
 
   // A delegate type for hooking up change notifications.
   public delegate void ChangedEventHandler(object sender, EventArgs e);
 
   // A class that works just like ArrayList, but sends event
   // notifications whenever the list changes.
   public class ListWithChangedEvent: ArrayList
   {
      // An event that clients can use to be notified whenever the
      // elements of the list change.
      public event ChangedEventHandler Changed;
 
      // Invoke the Changed event; called whenever list changes
      protected virtual void OnChanged(EventArgs e)
      {
         if (Changed != null)
            Changed(this, e);
      }
 
      // Override some of the methods that can change the list;
      // invoke event after each
      public override int Add(object value)
      {
         int i = base.Add(value);
         OnChanged(EventArgs.Empty);
         return i;
      }
 
      public override void Clear()
      {
         base.Clear();
         OnChanged(EventArgs.Empty);
      }
 
      public override object this[int index]
      {
         set
         {
            base[index] = value;
            OnChanged(EventArgs.Empty);
         }
      }
   }
}
 
namespace TestEvents
{
   using MyCollections;
 
   class EventListener
   {
      private ListWithChangedEvent List;
 
      public EventListener(ListWithChangedEvent list)
      {
         List = list;
         // Add "ListChanged" to the Changed event on "List".
         List.Changed += new ChangedEventHandler(ListChanged);
      }
 
      // This will be called whenever the list changes.
      private void ListChanged(object sender, EventArgs e)
      {
         Console.WriteLine("This is called when the event fires.");
      }
 
      public void Detach()
      {
         // Detach the event and delete the list
         List.Changed -= new ChangedEventHandler(ListChanged);
         List = null;
      }
   }
 
   class Test
   {
      // Test the ListWithChangedEvent class.
      public static void Main()
      {
      // Create a new list.
      ListWithChangedEvent list = new ListWithChangedEvent();
 
      // Create a class that listens to the list's change event.
      EventListener listener = new EventListener(list);
 
      // Add and remove items from the list.
      list.Add("item 1");
      list.Clear();
      listener.Detach();
      }
   }
}输出
This is called when the event fires.
This is called when the event fires.代码讨论
声明事件    
    若要在类内声明事件,首先必须声明该事件的委托类型(如果尚未声明的话)。
   public delegate void ChangedEventHandler(object sender, EventArgs e);
   委托类型定义传递给处理该事件的方法的一组参数。
   多个事件可共享相同的委托类型,因此仅当尚未声明任何合适的委托类型时才需要执行该步骤。
 
接下来,声明事件本身。
 
       public event ChangedEventHandler Changed;
       声明事件的方法与声明委托类型的字段类似,只是关键字 event 在事件声明前面,在修饰符后面。
       事件通常被声明为公共事件,但允许任意可访问修饰符。
 
调用事件  
        类声明了事件以后,可以就像处理所指示的委托类型的字段那样处理该事件。
          如果没有任何客户将委托与该事件挂钩,该字段将为空;否则该字段引用应在调用该事件时调用的委托。
        因此,调用事件时通常先检查是否为空,然后再调用事件。
 if (Changed != null)
 Changed(this, e);调用事件只能从声明该事件的类内进行。
 
与事件挂钩  
 从声明事件的类外看,事件像个字段,但对该字段的访问是非常受限制的。只可进行如下操作:
 在该字段上撰写新的委托。
 从字段(可能是复合字段)移除委托。
 使用 += 和 -= 运算符完成此操作。为开始接收事件调用,客户代码先创建事件类型的委托,
 该委托引用应从事件调用的方法。然后它使用 += 将该委托写到事件可能连接到的其他任何委托上。
 
 // Add "ListChanged" to the Changed event on "List":
 List.Changed += new ChangedEventHandler(ListChanged);
 
 当客户代码完成接收事件调用后,它将使用运算符 -= 从事件移除其委托。
 // Detach the event and delete the list:
 List.Changed -= new ChangedEventHandler(ListChanged);
 
事件和继承
 当创建可以从中派生的通用组件时,事件中有时出现似乎会成为问题的情况。由于事件只能从声明它们的类中调用,
 因此派生类不能直接调用在基类内声明的事件。虽然这有时符合需要,但通常使派生类能够自由调用事件更合适。
 这通常通过为事件创建受保护的调用方法来实现。通过调用该调用方法,派生类便可以调用此事件。
 为获得更大的灵活性,调用方法通常声明为虚拟的,这允许派生类重写调用方法。
 这使得派生类可以截获基类正在调用的事件,有可能对这些事件执行它自己的处理。
 
 在前面的示例中,这已用 OnChanged 方法实现。如果需要,派生类可调用或重写该方法。
 
接口中的事件
事件和字段之间的另一个差异是,事件可放在接口中,而字段不能。当实现接口时,实现类必须在实现接口的类中提供相应的事件。
 
.NET Framework 指南
尽管 C# 语言允许事件使用任意委托类型,但".NET Framework"对于应为事件使用的委托类型有一些更严格的指南。
 
如果打算将您的组件与".NET Framework"一起使用,您可能希望遵守这些指南。
 
".NET Framework"指南指示用于事件的委托类型应采用两个参数:指示事件源的"对象源"参数和封装事件的其他任何相关信息的"e"参数。
"e"参数的类型应从 EventArgs 类派生。对于不使用其他任何信息的事件,".NET Framework"已定义了一个适当的委托类型:EventHandler。
 
示例 2
下面的示例是"示例 1"的修改版本,它遵守".NET Framework"指南。该示例使用 EventHandler 委托类型。
 
// events2.cs
using System;
namespace MyCollections
{
   using System.Collections;
 
   // A class that works just like ArrayList, but sends event
   // notifications whenever the list changes:
   public class ListWithChangedEvent: ArrayList
   {
      // An event that clients can use to be notified whenever the
      // elements of the list change:
      public event EventHandler Changed;
 
      // Invoke the Changed event; called whenever list changes:
      protected virtual void OnChanged(EventArgs e)
      {
         if (Changed != null)
            Changed(this,e);
      }
 
      // Override some of the methods that can change the list;
      // invoke event after each:
      public override int Add(object value)
      {
         int i = base.Add(value);
         OnChanged(EventArgs.Empty);
         return i;
      }
 
      public override void Clear()
      {
         base.Clear();
         OnChanged(EventArgs.Empty);
      }
 
      public override object this[int index]
      {
         set
         {
            base[index] = value;
            OnChanged(EventArgs.Empty);
         }
      }
   }
}
 
namespace TestEvents
{
   using MyCollections;
 
   class EventListener
   {
      private ListWithChangedEvent List;
 
      public EventListener(ListWithChangedEvent list)
      {
         List = list;
         // Add "ListChanged" to the Changed event on "List":
         List.Changed += new EventHandler(ListChanged);
      }
 
      // This will be called whenever the list changes:
      private void ListChanged(object sender, EventArgs e)
      {
         Console.WriteLine("This is called when the event fires.");
      }
 
      public void Detach()
      {
         // Detach the event and delete the list:
         List.Changed -= new EventHandler(ListChanged);
         List = null;
      }
   }
 
   class Test
   {
      // Test the ListWithChangedEvent class:
      public static void Main()
      {
      // Create a new list:
      ListWithChangedEvent list = new ListWithChangedEvent();
 
      // Create a class that listens to the list's change event:
      EventListener listener = new EventListener(list);
 
      // Add and remove items from the list:
      list.Add("item 1");
      list.Clear();
      listener.Detach();
      }
   }
}输出
This is called when the event fires.
This is called when the event fires.
 
 
 

C#--委托教程

 本教程演示委托类型。它说明如何将委托映射到静态方法和实例方法,以及如何组合委托(多路广播)。
 
C# 中的委托类似于 C 或 C++ 中的函数指针。使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与 C 或 C++ 中的函数指针不同,委托是面向对象、类型安全的,并且是安全的。
 
委托声明定义一种类型,它用一组特定的参数以及返回类型封装方法。对于静态方法,委托对象封装要调用的方法。对于实例方法,委托对象同时封装一个实例和该实例上的一个方法。如果您有一个委托对象和一组适当的参数,则可以用这些参数调用该委托。
 
委托的一个有趣且有用的属性是,它不知道或不关心自己引用的对象的类。任何对象都可以;只是方法的参数类型和返回类型必须与委托的参数类型和返回类型相匹配。这使得委托完全适合"匿名"调用。
 
注意   委托是在调用方的安全权限下运行而不是声明方的权限下运行。
此教程包括两个示例:
 
示例 1 展示如何声明、实例化和调用委托。
示例 2 展示如何组合两个委托。
此外,还讨论以下主题:
 
委托和事件
委托与接口
示例 1
下面的示例阐释声明、实例化和使用委托。BookDB 类封装一个书店数据库,它维护一个书籍数据库。它公开 ProcessPaperbackBooks 方法,该方法在数据库中查找所有平装书,并为每本书调用一个委托。所使用的 delegate 类型称为 ProcessBookDelegate。Test 类使用该类输出平装书的书名和平均价格。
 
委托的使用促进了书店数据库和客户代码之间功能的良好分隔。客户代码不知道书籍的存储方式和书店代码查找平装书的方式。书店代码也不知道找到平装书后将对平装书进行什么处理。
 
// bookstore.cs
using System;
 
// A set of classes for handling a bookstore:
namespace Bookstore d
{
   using System.Collections;
 
   // Describes a book in the book list:
   public struct Book
   {
      public string Title;        // Title of the book.
      public string Author;       // Author of the book.
      public decimal Price;       // Price of the book.
      public bool Paperback;      // Is it paperback?
 
      public Book(string title, string author, decimal price, bool paperBack)
      {
         Title = title;
         Author = author;
         Price = price;
         Paperback = paperBack;
      }
   }
 
   // Declare a delegate type for processing a book:
   public delegate void ProcessBookDelegate(Book book);
 
   // Maintains a book database.
   public class BookDB
   {
      // List of all books in the database:
      ArrayList list = new ArrayList(); 
 
      // Add a book to the database:
      public void AddBook(string title, string author, decimal price, bool paperBack)
      {
         list.Add(new Book(title, author, price, paperBack));
      }
 
      // Call a passed-in delegate on each paperback book to process it:
      public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
      {
         foreach (Book b in list)
         {
            if (b.Paperback)
            // Calling the delegate:
               processBook(b);
         }
      }
   }
}
 
// Using the Bookstore classes:
namespace BookTestClient
{
   using Bookstore;
 
   // Class to total and average prices of books:
   class PriceTotaller
   {
      int countBooks = 0;
      decimal priceBooks = 0.0m;
 
      internal void AddBookToTotal(Book book)
      {
         countBooks += 1;
         priceBooks += book.Price;
      }
 
      internal decimal AveragePrice()
      {
         return priceBooks / countBooks;
      }
   }
 
   // Class to test the book database:
   class Test
   {
      // Print the title of the book.
      static void PrintTitle(Book b)
      {
         Console.WriteLine("   {0}", b.Title);
      }
 
      // Execution starts here.
      static void Main()
      {
         BookDB bookDB = new BookDB();
 
         // Initialize the database with some books:
         AddBooks(bookDB);    
 
         // Print all the titles of paperbacks:
         Console.WriteLine("Paperback Book Titles:");
         // Create a new delegate object associated with the static
         // method Test.PrintTitle:
         bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));
 
         // Get the average price of a paperback by using
         // a PriceTotaller object:
         PriceTotaller totaller = new PriceTotaller();
         // Create a new delegate object associated with the nonstatic
         // method AddBookToTotal on the object totaller:
         bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));
         Console.WriteLine("Average Paperback Book Price: ${0:#.##}",
            totaller.AveragePrice());
      }
 
      // Initialize the book database with some test books:
      static void AddBooks(BookDB bookDB)
      {
         bookDB.AddBook("The C Programming Language",
            "Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true);
         bookDB.AddBook("The Unicode Standard 2.0",
            "The Unicode Consortium", 39.95m, true);
         bookDB.AddBook("The MS-DOS Encyclopedia",
            "Ray Duncan", 129.95m, false);
         bookDB.AddBook("Dogbert's Clues for the Clueless",
            "Scott Adams", 12.00m, true);
      }
   }
}输出
Paperback Book Titles:
   The C Programming Language
   The Unicode Standard 2.0
   Dogbert's Clues for the Clueless
Average Paperback Book Price: $23.97代码讨论
声明委托   以下语句:
public delegate void ProcessBookDelegate(Book book);声明一个新的委托类型。每个委托类型都描述参数的数目和类型,以及它可以封装的方法的返回值类型。
每当需要一组新的参数类型或新的返回值类型时,都必须声明一个新的委托类型。
 
实例化委托   声明了委托类型后,必须创建委托对象并使之与特定方法关联。与所有其他对象类似,新的委托对象用 new 表达式创建。但是当创建委托时,传递给 new 表达式的参数很特殊:它的编写类似于方法调用,但没有方法的参数。
下列语句:
 
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));创建与静态方法 Test.PrintTitle 关联的新的委托对象。下列语句:
 
bookDB.ProcessPaperbackBooks(new
   ProcessBookDelegate(totaller.AddBookToTotal));创建与对象 totaller 上的非静态方法 AddBookToTotal 关联的新的委托对象。在两个例子中,新的委托对象都立即传递给 ProcessPaperbackBooks 方法。
 
请注意一旦创建了委托,它所关联到的方法便永不改变:委托对象不可改变。
 
调用委托   创建委托对象后,通常将委托对象传递给将调用该委托的其他代码。通过委托对象的名称(后面跟着要传递给委托的参数,括在括号内)调用委托对象。下面是委托调用的示例:
processBook(b);在此示例中,可以通过使用 BeginInvoke 和 EndInvoke 方法同步或异步调用委托。
 
示例 2
本示例演示组合委托。委托对象的一个有用属性是,它们可以"+"运算符来组合。组合的委托可调用组成它的那两个委托。只有相同类型的委托才可以组合。
 
"-"运算符可用来从组合的委托移除组件委托。
 
// compose.cs
using System;
 
delegate void MyDelegate(string s);
 
class MyClass
{
    public static void Hello(string s)
    {
        Console.WriteLine("  Hello, {0}!", s);
    }
 
    public static void Goodbye(string s)
    {
        Console.WriteLine("  Goodbye, {0}!", s);
    }
 
    public static void Main()
    {
        MyDelegate a, b, c, d;
 
        // Create the delegate object a that references
        // the method Hello:
        a = new MyDelegate(Hello);
        // Create the delegate object b that references
        // the method Goodbye:
        b = new MyDelegate(Goodbye);
        // The two delegates, a and b, are composed to form c:
        c = a + b;
        // Remove a from the composed delegate, leaving d,
        // which calls only the method Goodbye:
        d = c - a;
 
        Console.WriteLine("Invoking delegate a:");
        a("A");
        Console.WriteLine("Invoking delegate b:");
        b("B");
        Console.WriteLine("Invoking delegate c:");
        c("C");
        Console.WriteLine("Invoking delegate d:");
        d("D");
    }
}输出
Invoking delegate a:
  Hello, A!
Invoking delegate b:
  Goodbye, B!
Invoking delegate c:
  Hello, C!
  Goodbye, C!
Invoking delegate d:
  Goodbye, D!委托和事件
委托非常适合于用作事件(从一个组件就该组件中的更改通知"侦听器")。有关将委托用于事件的更多信息,请参见事件教程。
 
委托与接口
委托和接口的类似之处是,它们都允许分隔规范和实现。多个独立的作者可以生成与一个接口规范兼容的多个实现。类似地,委托指定方法的签名,多个作者可以编写与委托规范兼容的多个方法。何时应使用接口,而何时应使用委托呢?
 
委托在以下情况下很有用:
 
调用单个方法。
一个类可能希望有方法规范的多个实现。
希望允许使用静态方法实现规范。
希望类似事件的设计模式(有关更多信息,请参见事件教程)。
调用方不需要知道或获得在其上定义方法的对象。
实现的提供程序希望只对少数选择组件"分发"规范实现。
需要方便的组合。
接口在以下情况下很有用:
 
规范定义将调用的一组相关方法。
类通常只实现规范一次。
接口的调用方希望转换为接口类型或从接口类型转换,以获得其他接口或类。
 
 

P903 X500 重启

P903硬启的方式和其他PPC手机相比较为"独特",需要用户同时按下开关以及RESET键,然后同时松开,再按下"挂断键"约5秒左右,屏幕便会提示询问是否清除本机内存,接下来要在倒计时结束时按住左软键不放,直到屏幕提示"yes",便可以硬启手机.

星期五, 五月 16, 2008

3DES加解密

///    <summary>   
        ///   3des加密字符串   
        ///    </summary>   
        ///    <param   name="a_strString">要加密的字符串 </param>   
        ///    <param   name="a_strKey">密钥 </param>   
        ///    <returns>加密后并经base64编码的字符串 </returns>   
        ///    <remarks>静态方法,采用默认ascii编码 </remarks>   
        public static string Encrypt3DES(string a_strString, string a_strKey)
        {
            TripleDESCryptoServiceProvider DES = new TripleDESCryptoServiceProvider();
            MD5CryptoServiceProvider hashMD5 = new MD5CryptoServiceProvider();

            DES.Key = hashMD5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(a_strKey));
            DES.Mode = CipherMode.ECB;

            ICryptoTransform DESEncrypt = DES.CreateEncryptor();

            byte[] Buffer = ASCIIEncoding.ASCII.GetBytes(a_strString);
            return Convert.ToBase64String(DESEncrypt.TransformFinalBlock(Buffer, 0, Buffer.Length));
        }//end   method   

///    <summary>   
        ///   3des解密字符串   
        ///    </summary>   
        ///    <param   name="a_strString">要解密的字符串 </param>   
        ///    <param   name="a_strKey">密钥 </param>   
        ///    <returns>解密后的字符串 </returns>   
        ///    <exception   cref="">密钥错误 </exception>   
        ///    <remarks>静态方法,采用默认ascii编码 </remarks>   
        public static string Decrypt3DES(string a_strString, string a_strKey)
        {
            TripleDESCryptoServiceProvider DES = new
            TripleDESCryptoServiceProvider();
            MD5CryptoServiceProvider hashMD5 = new MD5CryptoServiceProvider();

            DES.Key = hashMD5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(a_strKey));
            DES.Mode = CipherMode.ECB;

            ICryptoTransform DESDecrypt = DES.CreateDecryptor();

            string result = "";
            try
            {
                byte[] Buffer = Convert.FromBase64String(a_strString);
                result = ASCIIEncoding.ASCII.GetString(DESDecrypt.TransformFinalBlock(Buffer, 0, Buffer.Length));
            }
            catch (Exception e)
            {
                throw (new Exception("不是有效的 base64  字符串", e));
            }

            return result;
        }//end   method   

星期四, 五月 08, 2008

down oracle patch

需要oracle补丁的请进来,oracle 补丁下载新方法。

 

oracle patchset

Tips--Oacle 9i Database 所有大版本补丁集列表
 
有朋友问到补丁集的号是多少,个人通常都是用FTP来download补丁集,这里也顺便记录一下9i的所有补丁集的号。方便自己和需要的朋友查看:
4547809 Oracle PATCH SET FOR DATABASE SERVER 9.2.0.8 25-AUG-2006
4163445 Oracle PATCH SET FOR DATABASE SERVER 9.2.0.7 23-SEP-2005
3948480 Oracle PATCH SET FOR DATABASE SERVER 9.2.0.6 13-DEC-2004
3501955 Oracle PATCH SET FOR DATABASE SERVER 9.2.0.5 26-MAR-2004
3301544 Oracle PATCH SET FOR DATABASE SERVER 9.0.1.5 08-JAN-2004
3095277 Oracle PATCH SET FOR DATABASE SERVER 9.2.0.4 05-SEP-2003
2761332 Oracle PATCH SET FOR DATABASE SERVER 9.2.0.3 03-APR-2003
2632931 Oracle PATCH SET FOR DATABASE SERVER 9.2.0.2 13-NOV-2002
2517300 Oracle PATCH SET FOR DATABASE SERVER 9.0.1.4 17-AUG-2002
2271678 Oracle PATCH SET FOR DATA SERVER 9.0.1.3 19-MAR-2002
2072050 Oracle PATCH SET FOR DATA SERVER 9.0.1.2 15-NOV-2001
1968474 Oracle PATCH SET FOR DATA SERVER 9.0.1.1 12-SEP-2001
 
Tips--Oracle 8174的patch set还是可download
 

有很多的客户,还是跑在Oracle 8i上,所以它的patch备一份还是有必要的,要不然哪一天oracle不提供下载了,不知道到哪里找呢,patch set number 是2376472

C:Documents and SettingsAdministrator>ftp updates.oracle.com
Connected to bigip-aru.oracle.com.
220 FTP server ready.
User (bigip-aru.oracle.com:(none)): vongates
331 Username OK, please send password.
Password:
230-
230- Welcome to the Oracle Patch Download FTP Server
230-
230- For detailed help, use command "quote site help".
230
ftp> cd 2376472
250 Changed directory OK.
ftp> ls
200 PORT command OK.
150 Opening data connection for file listing.
Tips--oracle 10g 所有大版本 patchset 列表(原)

 
今天有一兄弟问我10.1.0.3的patch set number是多少。说实在的只是在10gR1刚出来的时候有自己测试过R1版本。后来没有怎么用过。现在用10gR2的比较多,接着2007OOW以后,大约在2007年的9月应该可以拿到11g的正式安装源CD吧。这里记录一下到现在的所有大的patchset number供大家方便查看:

5337014 Patchset 10.2.0.3 22-FEB-2007
4547817 Patchset 10.2.0.2 03-MAR-2006
4505133 Patchset 10.1.0.5 05-FEB-2006
4163362 Patchset 10.1.0.4 18-MAY-2005
3761843 Patchset 10.1.0.3 16-AUG-2004