2010年4月16日星期五

我觉得不错的一篇delphi多线程入门(转)

摘自:http://www.360doc.com/content/10/0102/09/681839_12482238.shtml

多线程编程(1) - 先入门再说。

  多线程应该是编程工作者的基础技能, 但这个基础对我来讲的确有点难(起码昨天以前是这样).

  开始本应该是一篇洋洋洒洒的文字, 不过我还是提倡先做起来, 在尝试中去理解.

  先试试这个:

procedure TForm1.Button1Click(Sender: TObject);
var
 i: Integer;
begin
 for i := 0 to 500000 do
 begin
  Canvas.TextOut(10, 10, IntToStr(i));
 end;
end;

  上面程序运行时, 我们的窗体基本是 "死" 的, 可以在你在程序运行期间拖动窗体试试...

  Delphi 为我们提供了一个简单的办法(Application.ProcessMessages)来解决这个问题:

procedure TForm1.Button1Click(Sender: TObject);
var
 i: Integer;
begin
 for i := 0 to 500000 do
 begin
  Canvas.TextOut(10, 10, IntToStr(i));
  Application.ProcessMessages;
 end;
end;

  这个 Application.ProcessMessages; 一般用在比较费时的循环中, 它会检查并先处理消息队列中的其他消息.

  但这算不上多线程, 譬如: 运行中你拖动窗体, 循环会暂停下来...

  在使用多线程以前, 让我们先简单修改一下程序:

function MyFun: Integer;
var
 i: Integer;
begin
 for i := 0 to 500000 do
 begin
  Form1.Canvas.Lock;
  Form1.Canvas.TextOut(10, 10, IntToStr(i));
  Form1.Canvas.Unlock;
 end;
 Result := 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
 MyFun;
end;

细数上面程序的变化:

  1、首先这还不是多线程的, 也会让窗体假 "死" 一会;

  2、把执行代码写在了一个函数里, 但这个函数不属于 TForm1 的方法, 所以使用 Canvas 是必须冠以名称(Form1);

  3、既然是个函数, (不管是否必要)都应该有返回值;

  4、使用了 500001 次 Lock 和 Unlock.

  Canvas.Lock 好比在说: Canvas(绘图表面)正忙着呢, 其他想用 Canvas 的等会;

  Canvas.Unlock : 用完了, 解锁!

  在 Canvas 中使用 Lock 和 Unlock 是个好习惯, 在不使用多线程的情况下这无所谓, 但保不准哪天程序会扩展为多线程的; 我们现在学习多线程, 当然应该用.

  在 Delphi 中使用多线程有两种方法: 调用 API、使用 TThread 类; 使用 API 的代码更简单.

function MyFun(p: Pointer): Integer; stdcall;
var
 i: Integer;
begin
 for i := 0 to 500000 do
 begin
  Form1.Canvas.Lock;
  Form1.Canvas.TextOut(10, 10, IntToStr(i));
  Form1.Canvas.Unlock;
 end;
 Result := 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
 ID: THandle;
begin
 CreateThread(nil, 0, @MyFun, nil, 0, ID);
end;

  代码分析:

  CreateThread 一个线程后, 算上原来的主线程, 这样程序就有两个线程、是标准的多线程了;

  CreateThread 第三个参数是函数指针, 新线程建立后将立即执行该函数, 函数执行完毕, 系统将销毁此线程从而结束多线程的故事.

  CreateThread 要使用的函数是系统级别的, 不能是某个类(譬如: TForm1)的方法, 并且有严格的格式(参数、返回值)要求, 不管你暂时是不是需要都必须按格式来;

因为是系统级调用, 还要缀上 stdcall, stdcall 是协调参数顺序的, 虽然这里只有一个参数没有顺序可言, 但这是使用系统函数的惯例.

  CreateThread 还需要一个 var 参数来接受新建线程的 ID, 尽管暂时没用, 但这也是格式; 其他参数以后再说吧.

  这样一个最简单的多线程程序就出来了, 咱们再用 TThread 类实现一次

type
 TMyThread = class(TThread)
 protected
  procedure Execute; override;
 end;
procedure TMyThread.Execute;
var
 i: Integer;
begin
 FreeOnTerminate := True; {这可以让线程执行完毕后随即释放}
 for i := 0 to 500000 do
 begin
  Form1.Canvas.Lock;
  Form1.Canvas.TextOut(10, 10, IntToStr(i));
  Form1.Canvas.Unlock;
 end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
 TMyThread.Create(False);
end;

  TThread 类有一个抽象方法(Execute), 因而是个抽象类, 抽象类只能继承使用, 上面是继承为 TMyThread.

  继承 TThread 主要就是实现抽象方法 Execute(把我们的代码写在里面), 等我们的 TMyThread 实例化后, 首先就会执行 Execute 方法中的代码.

  按常规我们一般这样去实例化:

procedure TForm1.Button1Click(Sender: TObject);
var
 MyThread: TMyThread;
begin
 MyThread := TMyThread.Create(False);
end;

  因为 MyThread 变量在这里毫无用处(并且编译器还有提示), 所以不如直接写做 TMyThread.Create(False);

  我们还可以轻松解决一个问题, 如果: TMyThread.Create(True) ?


procedure TForm1.Button1Click(Sender: TObject);
var
 MyThread: TMyThread;
begin
 MyThread := TMyThread.Create(True);
 MyThread.Resume;
end;
//可简化为:
procedure TForm1.Button1Click(Sender: TObject);
begin
 with TMyThread.Create(True) do Resume;
end;

  使用 TThread 类时, Delphi 有提供的模板, 但用 IDE 写代码很方便, 我重写一遍录下来给大家看:

  多线程编程(1) - 先入门再说

  期间使用了 Ctrl+J、Shift+Ctrl+C、Ctrl+Alt+P 等快捷键.

  重要的修正与补充: 在 TThread 类的例子中, 应该有这句: FreeOnTerminate := True; (原来漏掉, 代码中已加上; 但动画加不上了).

  先说它什么意思:

  类 Create 了就要 Free;

  但 TThread(的子类) 有特殊性, 很多时候我们不能确定新建的线程什么时候执行完(也就是什么时候该释放);

  如果线程执行完毕自己知道释放就好了, 所以 TThread 给了一个布尔属性 FreeOnTerminate, 如果为 True, 线程执行完毕后就会自释放.

  我怎么会忽略了这么重要的问题呢? 原因有二:

  1、我一直在追求最精炼的代码;

  2、我手头上不只一本书上介绍说: FreeOnTerminate 的默认值是 True(错误!), 经落实, 应该是 False, 起码在 Delphi 2007 和 2009 中是这样; 或许以前的某个版本和现在不一样

多线程编程(2) - 从 CreateThread 说起。

function CreateThread(
 lpThreadAttributes: Pointer;      {安全设置}
 dwStackSize: DWORD;          {堆栈大小}
 lpStartAddress: TFNThreadStartRoutine; {入口函数}
 lpParameter: Pointer;         {函数参数}
 dwCreationFlags: DWORD;        {启动选项}
 var lpThreadId: DWORD         {输出线程 ID }
): THandle; stdcall;           {返回线程句柄}

  在 Windows 上建立一个线程, 离不开 CreateThread 函数;

  TThread.Create 就是先调用了 BeginThread (Delphi 自定义的), BeginThread 又调用的 CreateThread.

  既然有建立, 就该有释放, CreateThread 对应的释放函数是: ExitThread, 譬如下面代码:

procedure TForm1.Button1Click(Sender: TObject);
begin
 ExitThread(0); {此句即可退出当前程序, 但不建议这样使用}
end;

  代码注释:

  当前程序是一个进程, 进程只是一个工作环境, 线程是工作者;

  每个进程都会有一个启动线程(或叫主线程), 也就是说: 我们之前大量的编码都是写给这个主线程的;

  上面的 ExitThread(0); 就是退出这个主线程;

  系统不允许一个没有线程的进程存在, 所以程序就退出了.

  另外: ExitThread 函数的参数是一个退出码, 这个退出码是给之后的其他函数用的, 这里随便给个无符号整数即可.

  或许你会说: 这个 ExitThread 挺好用的; 其实不管是用 API 还是用 TThread 类写多线程, 我们很少用到它; 因为:

  1、假如直接使用 API 的 CreateThread, 它执行完入口函数后会自动退出, 无需 ExitThread;

2、用 TThread 类建立的线程又绝不能使用 ExitThread 退出; 因为使用 TThread 建立线程时会同时分配更多资源(譬如你自定义的成员、还有它的祖先类(TObject)分配的资源等等), 如果用 ExitThread 给草草退出了, 这些资源将得不到释放而导致内存泄露. 尽管 Delphi 提供了 EndThread(其内部调用 ExitThread), 这也不需要我们手动操作(假如非要手动操作也是件很麻烦的事情, 因为很多时候你不知道线程是什么时候执行完毕的).

  除了 CreateThread, 还有一个 CreateRemoteThread, 可在其他进程中建立线程, 这不应该是现在学习的重点;

  现在先集中精力把 CreateThread 的参数搞彻底.

  倒着来吧, 先谈谈 CreateThread 将要返回的 "线程句柄".

  "句柄" 类似指针, 但通过指针可读写对象, 通过句柄只是使用对象;

  有句柄的对象一般都是系统级别的对象(或叫内核对象); 之所以给我们的是句柄而不是指针, 目的只有一个: "安全";

  貌似通过句柄能做很多事情, 但一般把句柄提交到某个函数(一般是系统函数)后, 我们也就到此为止很难了解更多了; 事实上是系统并不相信我们.

  不管是指针还是句柄, 都不过是内存中的一小块数据(一般用结构描述), 微软并没有公开句柄的结构细节, 猜一下它应该包括: 真实的指针地址、访问权限设置、引用计数等等.

  既然 CreateThread 可以返回一个句柄, 说明线程属于 "内核对象".

  实际上不管线程属于哪个进程, 它们在系统的怀抱中是平等的; 在优先级(后面详谈)相同的情况下, 系统会在相同的时间间隔内来运行一下每个线程, 不过这个间隔很小很小, 以至于让我们误以为程序是在不间断地运行.

  这时你应该有一个疑问: 系统在去执行其他线程的时候, 是怎么记住前一个线程的数据状态的?

有这样一个结构 TContext, 它基本上是一个 CPU 寄存器的**, 线程是数据就是通过这个结构切换的, 我们也可以通过 GetThreadContext 函数读取寄存器看看.

  附上这个结构 TContext(或叫: CONTEXT、_CONTEXT) 的定义Context = ^TContext;
_CONTEXT = record
 ContextFlags: DWORD;
 Dr0: DWORD;
 Dr1: DWORD;
 Dr2: DWORD;
 Dr3: DWORD;
 Dr6: DWORD;
 Dr7: DWORD;
 FloatSave: TFloatingSaveArea;
 SegGs: DWORD;
 SegFs: DWORD;
 SegEs: DWORD;
 SegDs: DWORD;
 Edi: DWORD;
 Esi: DWORD;
 Ebx: DWORD;
 Edx: DWORD;
 Ecx: DWORD;
 Eax: DWORD;
 Ebp: DWORD;
 Eip: DWORD;
 SegCs: DWORD;
 EFlags: DWORD;
 Esp: DWORD;
 SegSs: DWORD;
end;

  CreateThread 的最后一个参数是 "线程的 ID";

  既然可以返回句柄, 为什么还要输出这个 ID? 现在我知道的是:

  1、线程的 ID 是唯一的; 而句柄可能不只一个, 譬如可以用 GetCurrentThread 获取一个伪句柄、可以用 DuplicateHandle 复制一个句柄等等.

  2、ID 比句柄更轻便.

  在主线程中 GetCurrentThreadId、MainThreadID、MainInstance 获取的都是主线程的 ID.

  �哩�嗦一大些, 才说了 CreateThread 一个参数, 下篇继续.

  线程的学习还在入门中, 把我的理解写在这, 最期望的收获是得到指正.

多线程编程(3) - 从 CreateThread 说起[续]。                       

               function CreateThread(
 lpThreadAttributes: Pointer;
 dwStackSize: DWORD;
 lpStartAddress: TFNThreadStartRoutine;
 lpParameter: Pointer;
 dwCreationFlags: DWORD; {启动选项}
 var lpThreadId: DWORD
): THandle; stdcall;
  CreateThread 的倒数第二个参数 dwCreationFlags(启动选项) 有两个可选值:
  0: 线程建立后立即执行入口函数;
  CREATE_SUSPENDED: 线程建立后会挂起等待.
  可用 ResumeThread 函数是恢复线程的运行; 可用 SuspendThread 再次挂起线程.
  这两个函数的参数都是线程句柄, 返回值是执行前的挂起计数.
  什么是挂起计数?
  SuspendThread 会给这个数 +1; ResumeThread 会给这个数 -1; 但这个数最小是 0.
  当这个数 = 0 时, 线程会运行; > 0 时会挂起.
  如果被 SuspendThread 多次, 同样需要 ResumeThread 多次才能恢复线程的运行.
  在下面的例子中, 有新线程不断给一个全局变量赋随机值;
  同时窗体上的 Timer 控件每隔 1/10 秒就把这个变量写在窗体标题;
  在这个过程中演示了 ResumeThread、SuspendThread 两个函数.
  运行效果图:
  
  代码文件:unit Unit1;
interface
uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, ExtCtrls;
type
 TForm1 = class(TForm)
  Button1: TButton;
  Button2: TButton;
  Button3: TButton;
  Timer1: TTimer;
  procedure Button1Click(Sender: TObject);
  procedure Button2Click(Sender: TObject);
  procedure Button3Click(Sender: TObject);
  procedure FormCreate(Sender: TObject);
  procedure Timer1Timer(Sender: TObject);
 end;
var
 Form1: TForm1;
implementation
{$R *.dfm}
var
 hThread: THandle; {线程句柄}
 num: Integer;   {全局变量, 用于记录随机数}
{线程入口函数}
function MyThreadFun(p: Pointer): Integer; stdcall;
begin
 while True do {假如线程不挂起, 这个循环将一直循环下去}
 begin
  num := Random(100);
 end;
 Result := 0;
end;
{建立并挂起线程}
procedure TForm1.Button1Click(Sender: TObject);
var
 ID: DWORD;
begin
 hThread := CreateThread(nil, 0, @MyThreadFun, nil, CREATE_SUSPENDED, ID);
 Button1.Enabled := False;
end;
{唤醒并继续线程}
procedure TForm1.Button2Click(Sender: TObject);
begin
 ResumeThread(hThread);
end;
{挂起线程}
procedure TForm1.Button3Click(Sender: TObject);
begin
 SuspendThread(hThread);
end;
{下面是窗体代码}
procedure TForm1.FormCreate(Sender: TObject);
begin
 Timer1.Interval := 100;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
 Text := IntToStr(num);
end;
end.

2010年4月15日星期四

三个字叠在一起组成的字

�(三个土,读音yao,意:山高,多用於人名。)
�(三条牛,读音bēn,意:同"奔 ",急走,跑,紧赶,逃跑等。)
聂(三只耳朵,读音nie,意: )
磊(三块石头,读音lěi,本义:石头多)
�(三条小狗,读音biāo,意:疾风,暴风。同"飙" )
�(三个宝贝,读音bi,意:传说中的一种动物,像龟。旧时大石碑的石座多雕刻成��形状 )
奸(三个女字重叠,读音jian,意:同"奸")
�(三匹马,读音piāo,意:许多马跑的样子 )
�(三只羊,读音shān,意:群羊 )
�(三个吉,读音zhe,意:同"哲"。 )
�(三个言,读音ta,意:说话快。 )
�(三条舌头,读音qi,意:播弄是非 )
�(三只眼,读音mo,意:美目 )
�(三只香,读音xīn,意:古同"馨"。 )
�(三条泉水,读音xun,意:下雨而泉水出 )
�(三个响雷,读音bing,意:〔��〕雷声。 )
�(三个条田,读音lei,意:田间的土地,古代一种藤制的筐子。 )
轰(三个车,读音hōng,意:见"轰"。)
�(三个白,读音xiǎo,意:皎洁,明亮:"天�无云。")
�(三颗心,读音suǒ,意:沮丧的样子。)
�(三个小,读音mo,意:古同"麽"。)
�(三个鹿,读音cū,意:同"粗"。)
�(三个龙,读音da ,意古同"�",龙腾飞的样子。)
鑫(三个金,读音xīn,意旧时商店字号、人名用字,取其金多兴旺之意)
森(三个木,读音sēn,意稠密)
淼(三个水,读音miǎo,意(水广阔无际的样子)
�(三个土,读音yao,意山高。多用于人名)
�(三个士,读音zhuang,意古同"壮")
�(三个私的右半部分,读音lěi,意垒土块为墙。)
�(三只手,读音pa,意 俗称扒手为三只手,故写作"�")
品(三口之家,读音pǐn,本义:众多)
晶(三日,读音jīng,意清明;晴朗)
�(三子,读音zhuǎn,意谨慎)
众(上四下豕,读音zhong,意同"众")
毳(三根毛,读音cui,意指兽毛皮)
�(三条鱼,读音xiān ,意古同"鲜")
�(三只羊,读音shān ,意古同"膻")
矗(三个直,读音chu,意高耸)
�(三根草,读音hui,意古同"卉")
�(三个止,读音se,意古同"涩")
�(三个隼,读音za,意群鸟)
�(三个又,读音ruo,意古同"若")
�(三股阴风,读音xiū,意惊跑的样子)
�(三个飞机,读音fēi,意义未详。)
�(三把尖刀,读音li ,意:姓氏。)
焱(三把火,读音yan ,意:火花,火焰。)

2010年4月14日星期三

Delphi正则表达式使用方法(TPerlRegEx)(转)

摘自:http://blog.csdn.net/farce/archive/2010/02/05/5291820.aspx

正 文:

    使用Delphi开发软件无疑是一种比较高效率的方式,完全对象的语言,严格控制的语法,可 视化开发环境的性能,编译器的速度和已编译代码的效率,编程语言的功能及其复杂性,数据库结构的灵活性和可扩展性,框架对设计和使用模式的扩充无疑都是 delphi身上的光芒。     最近飘易使用Delphi开发了一套工具,由于和网络应用相关的东西较多,需要使用正则表达式,而目前主流的delphi下的正则表达式,应该 是 PerlRegEx 。     官方网站: http://www.regular-expressions.info/delphi.html
    直 接下载: http://www.regular-expressions.info/download/TPerlRegEx.zip     安装方法(飘易使用的delphi版本是 delphi2007): 1、先把解压的 TPerlRegEx 文件夹放在 Delphi 的根目录下的 Imports 目录中。 2、目前最新 For Win32 的版本是可以在 Delphi7,Delphi 2006,Delphi 2007,Delphi 2009 下使 用。飘易用的是2007,于是就打开 PerlRegExD2007.dpk ,在 Project Manager 窗口中 的 PerlRegExD2007.bpl 上点击右键,执行 Install。这时在 Tool Palette 的列表中已经有 了 TPerlRegEx,在 JGsoft 组。 3、 Tools -> Options -> Environment Options -> Delphi Options -> Library- Win32 -> Library path -> 添加路径:$(BDS)\Imports\TPerlRegEx (注:就是刚才放置的 路径)。 4、打开delphi根目录下 Imports\TPerlRegEx\pcre ,把 pcre3.dll 文件复制到 C:\WINDOWS \system32 文件夹下,这个文件夹里是windows系统存放 dll 文件的大本营。如果不复制pcre3.dll,在应 用 TPerlRegEx 正则的时候,系统提示出错。 5、用delphi打开 Imports\TPerlRegEx 目录下的 pcre.pas 文件,找 到 //{$DEFINE PCRE_STATICLINK} ,把之前的注释 // 删除;找到 {$DEFINE PCRE_LINKDLL} ,把 它注释掉,即改为 //{$DEFINE PCRE_LINKDLL} 。 注意:这点非常重要,如果不这样改的话,在软件开发完成后,分发到其他电脑上的时候,软件就会提示“缺少pcre3.dll错误”。这样修改的原因,是把 正则直接封包到 exe 文件里,而不需要链接 pcre3.dll 了;封包进 exe,付出的代价是软件增大了 100KB 左右的大小,当然比起必 须携带 pcre3.dll来说,这是有意义的付出。  具体的原因,看下面的官方解释:
// Define PCRE_STATICLINK to link the OBJ files with PCRE 7.7.
// Due to bugs in the Delphi compiler, you may get an internal error if install TPerlRegEx into a design time package,
// and you don't put TPerlRegEx into a runtime package either.
// You can use PCRE_STATICLINK if you don't use packages at all (which means you don't install it into the IDE and you don't drop TPerlRegEx on a form)
// You can also use PCRE_STATICLINK if you put the component into a runtime package
6、可以使用了! 直接 uses PerlRegEx 或从 Tool Palette 添加都可以。  (以上部分方法参考)     TPerlRegEx 安装设置完成后,就可以舒心地使用了,一些简单的使用方法如下:
//查找是否存在
var
  reg: TPerlRegEx;
begin
  reg := TPerlRegEx.Create(nil);
  reg.Subject := 'CodeGear Delphi 2007 for Win32';
  reg.RegEx   := '\d';
  if reg.Match then
    ShowMessage(' 找到了')
  else
    ShowMessage('没找到');
  FreeAndNil(reg);
end;
//替换一般字符串
var
reg: TPerlRegEx;
begin
reg := TPerlRegEx.Create(nil);
reg.Subject := ' 我爱DELPHI, 但Delphi不爱我!';
reg.RegEx   := 'Delphi';
reg.Replacement := '◆';
reg.ReplaceAll;
ShowMessage(reg.Subject); // 返回: 我爱DELPHI, 但◆不爱我!          
FreeAndNil(reg);
end;
//分别显示找到的每一个和总数
var
  reg: TPerlRegEx;
  num: Integer; // 用 num 来计数
begin
  reg := TPerlRegEx.Create(nil);   reg.Subject := 'CodeGear Delphi 2007 for Win32';
  reg.RegEx   := '\d';   num := 0;
  while reg.MatchAgain do  //MatchAgain 是下一个
  begin
    ShowMessage(reg.MatchedExpression); // 将分别显示: 2 0 0 7 3 2
    Inc(num);
  end;
    ShowMessage(IntToStr(num)); //6   FreeAndNil(reg);
end;
//目标字符串的位置与长度
var
  reg: TPerlRegEx;
begin
  reg := TPerlRegEx.Create(nil);   reg.Subject := 'CodeGear Delphi 2007 for Win32';
  reg.RegEx   := 'Delphi';   while reg.MatchAgain do  //很明显: 本例只能找到一个结果
  begin
    ShowMessage(reg.MatchedExpression); // 找到的字符串: Delphi
    ShowMessage(IntToStr(reg.MatchedExpressionOffset)); // 它所在的位置: 10
    ShowMessage(IntToStr(reg.MatchedExpressionLength)); // 它的长度: 6
  end;   FreeAndNil(reg);
end;

2010年4月13日星期二

delphi7 手动安装 indy10.2.3(转)

摘自:http://delphibasic.blogbus.com/logs/34742922.html

delphi7下的安装成 功,delphi11和delphi6的安装没有试用

【自动安装】
Lib\Fulld7.bat
双击运行即可。
自动会把bpl文件拷到C:\windows\system32
重新打开delphi7会自动加载 dclIndyCore70.bpl dclIndyProtocols70.bpl


【手工安装】
1.windows的path路径中增加D7的路径
在启用delphi7时,要能加载dclIndyCore70.bpl这个文件
如果是自动安装,这个文件会拷到C:\windows\system32\dclIndyCore70.bpl,所以不用改path

2.delphi7菜单中Library路径中
添加indy10\LIB路径下的System、Core、Protocols下的三个目录
添加indey10\D7 因为这个目录下有bpl和dcu文件

3.删除delphi7目录Bin下的indy*.BPL文件,这是官方旧版本的文件,放心删除。

4.删除delphi7目录下的 Id*.DCU文件,一般都是在lib目录里。


5.打开indy10\lib目录,安装DPK包顺序如下:

(1)编译 System\IndySystem70.dpk       (只需要compile)
          (这个不是设计包,不需要install)

(2)编译 Core\IndyCore70.dpk           (只需要compile)
   编译安装Core\dclIndyCore70.dpk   (设计包,先complie后install)

(3)编译 Protocols\IndyProtocols70.dpk   (只需要compile)
   编译安装Protocols\dclIndyProtocols70.dpk (设计包,先complie后install)

6.完成

【第二次手工安装】

第一步:
indy10\D7目录下的5个文件
复制到delphiD:\Delphi7_Ent\Projects\Bpl
IndySystem70.bpl
IndyCore70.bpl
IndyProtocols70.bpl
dclIndyCore70.bpl
dclIndyProtocols70.bpl

第二步:
打开delphi7菜单
Component/install packpages/
add dclIndyCore70.bpl

add dclIndyProtocols70.bpl


第三步
打开delphi7菜单中Library路径中
添加indy10\LIB路径下的System、Core、Protocols下的三个目录

完成!更简单。前提是要有现成bpl的文件,如果没有bpl文件,还得需要dpk编译

2010年4月12日星期一

xml中正确包含html代码


怎么样才能在xml中包含html代码,并正确使用呢?

在xml中用正确的格式包含html代码    


如:<![CDATA[ html代码 ]]>
<?xml version="1.0" encoding="UTF-8"?>
<urlset>
<url>
    <loc><![CDATA[<DIV>测量检测:具有最全的测量和检测产品,没有其他公司可以提供如此之多的测量检测产品,包括具有超高精度的L-GAGE激光<a href="8308.html" target="_blank">测量传感器</a>,多功能U-GAGE超声波传感器,A-GAGE测量光幕;</DIV>]]></loc>
    <lastmod>2010-03-10</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.6</priority>
</url>
<urlset>

新手业务员销售技巧(转)

摘自:http://blog.china.alibaba.com/blog/pc165a/article/b0-i10328456.html

第一,正确的心态

想一辈子做销售工作的人是很少的,大多数人只是把销售作为一个锻炼、学习,从而提高自身竞争力,以及了解社会的一个机会;也可能是求职过程中一个 无奈的过渡。无论原因是什么,应该明白的一点是,在做销售的过程中,你可以获得较其他行业更快的个人成长。如果你明白了这一点,那么你就能以积极的心态面 对可能的失败及挫折。客户的拒绝是销售人员的家常便饭,而做销售最大的困扰就来自于此,许多人不能坚持,原因不在于他能力多差,而是他(她)误解了销售的 目的,将销售业绩作为自己销售成功与否的唯一标杆――这个就是大多数销售人员容易犯的心态错误。其实,面对失败,我们可以换个角度来看,如果在失败后,我 们能及时反省,找到失误的原因,在下次加以改进,你的能力不就在无形中提升了吗,也就是说,你有了把握更多机会的能力;

第二个要素是个人的精神面貌

可以将这个作为上个条件的延伸,不多说,积极、乐观,这个是基本要求,积极的情绪可以感化对方,甚至控制局面,自己以后可以慢慢体会;具备了良好 的心态,这个就不难;

第三,敏锐的观察、全面的思考、充分的准备

观察--知己知彼,百战不殆

思考--运筹帷幄,决胜千里

准备--细节决定成败

具备上述三点条件,你就获得赢取对方信任的内部因素。任何一个谈判对手都不会轻视你。

下面再强调几个我认为很重要的技巧:

1、不论对方是谁,我和他(她)是身份对等的,是生意合作者,不要畏缩,也不要太强势,态度须不卑不亢;

2、做最坏的打算,做最好的准备,提前做好任何可能出现的意外情况处理方案;

3、恭维是必要的谈话技巧,但不能滥用,否则会让别人产生轻浮的印象;同时,自己的意见不要过多,言多必失,多听听对方的想法,既是对对方的尊 重,也是了解对方真实想法的最直接途径,切记,听比说重要!!!

4、不要直接否定对方的想法,需要技巧性处理;也不要说竞争对手的坏话;

5、有些机会是需要自己去创造的,多动动大脑。

总的来说,个人对销售的流程有几个字来概括:先用心(正确的心态;待人以诚)、后用脑(先了解,再分析,完善方案)、最后用腿(有的放矢,一击中 的),这样的话,你的业务成功率会很高。

请允许我自夸一下――免费说这么多,还不知道是否采用,当福利吧:)――我曾做过很多单位,国营、合资、集体、民营的,不乏国内顶级企业。卖过的 产品(服务)也是五花八门,业绩一直还不错,国内某知名商务网站的单笔金额最高的合同就是我签的(辞职前),但我并不是"跑"得最勤的,原因在于我喜欢总 结,喜欢思考,以脑带动腿,效率自然要高一点。

2010年4月9日星期五

在eclipse下编译运行 nutch(转)

摘自:http://zhangxiang390.javaeye.com/blog/257373

关键字: eclipse 编译 nutch

1.       下载Nutch 并解压到某个目录下,建议在根目录下。


2.       修改 Nutch\conf 目录下的 nutch-site.xml crawl-urlfilter.txt 两个文件,具体的修改方法如下:

(1) nutch-site.xml:  

在<configuration></configuration> 中间插入以下内容:

<property>

 <name>http.agent.name</name>

 <value>Jennifer</value>

 <description>HTTP 'User-Agent' request header. MUST NOT be empty -

 please set this to a single word uniquely related to your organization.

 NOTE: You should also check other related properties:


      http.robots.agents

      http.agent.description

      http.agent.url

      http.agent.email

      http.agent.version


 and set their values appropriately.

 </description>

</property>


<property>

 <name>http.agent.description</name>

 <value>Jennifer</value>

 <description>Further description of our bot- this text is used in

 the User-Agent header. It appears in parenthesis after the agent name.

 </description>

</property>


<property>

 <name>http.agent.url</name>

 <value>Jennifer</value>

 <description>A URL to advertise in the User-Agent header. This will

    appear in parenthesis after the agent name. Custom dictates that this

    should be a URL of a page explaining the purpose and behavior of this

    crawler.

 </description>

</property>


<property>

 <name>http.agent.email</name>

 <value>Jennifer</value>

 <description>An email address to advertise in the HTTP 'From' request

    header and User-Agent header. A good practice is to mangle this

    address (e.g. 'info at example dot com') to avoid spamming.

 </description>

</property>

 

并修改<value></value> 中间的值,这里的设置是因为Nutch 遵守了robots 协议,在获取response 时,把自己的相关信息提交给被爬行的网站,以供识别。所以设置成任何你喜欢的值都可以的。

(2) crawl-urlfilter.txt:

 

找 到"+^http://([a-z0-9]*\.)* MY.DOMAIN.NAME/ ,将其中的' MY.DOMAIN.NAME/' 直接删除。  

为了后面的方便,建议在修改完成后将conf 文件夹复制一下,并保存在硬盘的其他的地方。

 

3.       到下面两个页面去下载两个jar 文件,他们分别是:

http://nutch.cvs.sourceforge.net/nutch/nutch/src/plugin/parse-mp3/lib/

http://nutch.cvs.sourceforge.net/nutch/nutch/src/plugin/parse-rtf/lib/

下载其中的jid3lib-0.5.1.jarrtf-parser.jar 两个文件,并分别把他们拷贝到Nutch\src\plugin\parse-mp3\libNutch\src\plugin\parse-rtf\lib 两个文件夹下。

 

4.       准备工作做好以后就可以在Eclipse 中配置Nutch 了。打开Eclipse 后,我们开始建立Java project 。具体的做法是:

File > New > Project > Java project 创建Eclipse 项目,给我们的项目命名,然后选择Create project from existing source ,并指向你的Nutch 录。

 

5.       点击 Next 后我们就能看到用于定义 Java build 的设置环境。在第一个选项卡 source 下面, 选择conf, 在下面可以看到三个连接,我们需要选择第三个: Add project ‘Nutch’ to build path , 完成以上步骤之后我们就将conf 加入到了classpath 中。注意:这里不是指上面的第三个选项卡。

 

6.       现在需要 我们选择Default output folder ,在这里我们必须选择Nutch/conf 作为它的Default output folder ,否则就会找不到crawl-urlfilter.txt ,从而不能完成网站的爬行,就会出现我之前一只没有解决的问题,会出现以下提示:

Generator: 0 records selected for fetching, exiting ...

Stopping at depth=0 - no more URLs to fetch.

No URLs to fetch - check your seed list and URL filters.

 

 

7.       点击Finish ,这时我们就完成了前续的配置工作,这时我们就可点击运行,选择Java Application 点击OK ,开始让Eclipse 自己寻找项目的主类。我们选择Crawl- org.apache.nutch.crawl ,点击OK

 

8.       这时我们可以回到Nutch\conf 目录下看下,我们可以看到里面的内容发生了改变,这时就需要我们还原原来conf 下的内容,这时我们在第2 步中另外保存的conf 文件夹就起到了作用。(当然,新生成的东西并不影响我们的后续爬行工作,我们可以只考虑将原来conf 下的内容复制回来,但是我个人觉得为了文件夹的管理和查找方便,我建议将新生成的内容全部删除后在将原来conf 下的内容复制回来。 还有一点需要我们注意,conf 里面不可以有org 文件夹,如果有的话delete ,否则的话会影响index )。在nutch下建立文件夹myURL,新建文本文件url.txt,键入爬行的起始 URL,保存关闭。

 

9.      点击菜单中的Run > Open run dialog ,选择Java Application 分支,选中Arguments 选项卡。

Program arguments 中填写爬行命令,如: myURL -dir myPages -depth 2 -topN 50

VM arguments 中填写:-Dhadoop.log.dir=logs -Dhadoop.log.file=hadoop.log

填写完成后,点击apply 完成提交。由于我们到目前为止还没有建立需要我们爬行的url 地址,所以我们在提交完成后,点击close ,然后我们就可以建立需要Nutch 爬行的url 地址,建好之后,只要我们在点击工具栏上的运行按钮,我们就可以在Console 的透视图中看到Nutch 爬行的结果了。当然如果我们之前已经建立好了我们需要爬行的url 地址,我们就可以直接点击run ,这时我们也可以在Console 的透视图中看到Nutch 爬行的结果。

至 此,我们的工作全部完成了,当然我建议在Eclipse 里面配置Nutch 之前,我们最好能够熟悉在Shell 的环境下运行Nutch 。如果有什么不对的地方,欢迎大家指正,我也很希望能和大家一起学习有关Nutch 的相关内容。