??xml version="1.0" encoding="utf-8" standalone="yes"?> Delphi中的U程c?br /> Delphi中有一个线E类TThread是用来实现多U程~程的,q个l大多数Delphi书藉都有说到Q但基本上都是对 TThreadcȝ几个成员作一单介l,再说明一下Execute的实现和Synchronize的用法就完了。然而这q不是多U程~?br />E的全部Q我写此文的目的在于Ҏ作一个补充?/p> U程本质上是q程中一Dƈ发运行的代码。一个进E至有一个线E,x谓的ȝE。同时还可以有多个子U程?br />当一个进E中用到过一个线E时Q就是所谓的“多U程”?br />那么q个所谓的“一D代码”是如何定义的呢Q其实就是一个函数或q程Q对Delphi而言Q?br />如果用Windows API来创建线E的话,是通过一个叫做CreateThread的API函数来实现的Q它的定义ؓQ?br />HANDLE CreateThread( 其各参数如它们的名称所_分别是:U程属性(用于在NT下进行线E的安全属性设|,?X下无效)Q堆栈大, 因ؓCreateThread参数很多Q而且是Windows的APIQ所以在C Runtime Library里提供了一个通用的线E函敎ͼ理论?br />可以在Q何支持线E的OS中用)Q?br />unsigned long _beginthread(void (_USERENTRY *__start)(void *), unsigned __stksize, void *__arg); Delphi也提供了一个相同功能的cM函数Q?br />function BeginThread( q三个函数的功能是基本相同的Q它们都是将U程函数中的代码攑ֈ一个独立的U程中执行。线E函C一般函数的 Windows APIQ?br />VOID ExitThread( DWORD dwExitCode ); C Runtime LibraryQ?br />void _endthread(void); Delphi Runtime LibraryQ?br />procedure EndThread(ExitCode: Integer); Z记录一些必要的U程数据Q状?属性等Q,OS会ؓU程创徏一个内部ObjectQ如在Windows中那个Handle便是q?br />个内部Object的HandleQ所以在U程l束的时候还应该释放q个Object?/p> 虽然说用API或RTL(Runtime Library)已经可以很方便地q行多线E编E了Q但是还是需要进行较多的l节处理Qؓ?br />Delphi在Classes单元中对U程作了一个较好的装Q这是VCL的线E类QTThread 本文接下来要讨论的是TThreadcL如何对线E进行封装的Q也是深入研究一下TThreadcȝ实现。因为只是真正地 TThread = class TThreadcdDelphi的RTL里算是比较简单的c,cL员也不多Q类属性都很简单明白,本文只对几个比较重要的c?br />成员Ҏ和唯一的事ӞOnTerminate作详l分析?br />首先是构造函敎ͼ 同样有一个对应的RemoveThreadQ?br />procedure RemoveThread; 以加一Z来说明二者实现细节上的不同: 而用InterlockIncrementq程则没有这个问题,因ؓ所谓“原语”是一U不可中断的操作Q即操作pȝ能保证在一?br />“原语”执行完毕前不会q行U程切换。所以在上面那个例子中,只有当线EA执行完将数据存入内存后,U程B才可 前面那个例子也说明一U“线E访问冲H”的情况Q这也就是ؓ什么线E之间需要“同步”(SynchronizeQ,关于q?br />个,在后面说到同步时q会再详l讨论?/p> 说到同步Q有一个题外话Q加拿大滑铁卢大学的教授李明曑ְSynchronize一词在“线E同步”中被译作“同步”提?br />q异议,个h认ؓ他说的其实很有道理。在中文中“同步”的意思是“同时发生”,而“线E同步”目的就是避免这 扯远了,回到TThread的构造函CQ接下来最重要是q句了: 现在来看TThread的核心:U程函数ThreadProc。有意思的是这个线E类的核心却不是U程的成员,而是一个全局函数 function ThreadProc(Thread: TThread): Integer; 所以说QExecute是U程cM的线E函敎ͼ所有在Execute中的代码都需要当作线E代码来考虑Q如防止讉K冲突{?br />如果Execute发生异常Q则通过AcquireExceptionObject取得异常对象Qƈ存入U程cȝFFatalException成员中?br />最后是U程l束前做的一些收ַ作。局部变量FreeThread记录了线E类的FreeOnTerminated属性的讄Q然后将U?br />E返回D|ؓU程cȝq回值属性的倹{然后执行线E类的DoTerminateҎ?/p> DoTerminateҎ的代码如下: 很简单,是通过Synchronize来调用CallOnTerminateҎQ而CallOnTerminateҎ的代码如下,是单地调用 因ؓOnTerminate事g是在Synchronize中执行的Q所以本质上它ƈ不是U程代码Q而是ȝE代码(具体见后面对 执行完OnTerminate后,线E类的FFinished标志讄为True。接下来执行SignalSyncEventq程Q其代码如下Q?br />procedure SignalSyncEvent; 也很单,是讄一下一个全局EventQSyncEventQ关于Event的用,本文在后文详述Q而SyncEvent的用途将 然后ҎFreeThread中保存的FreeOnTerminate讄军_是否释放U程c,在线E类释放Ӟq有一些些操作Q详见接 在线E对象被释放前,首先要检查线E是否还在执行中Q如果线E还在执行中Q线EID不ؓ0Qƈ且线E结束标志未?br />|)Q则调用Terminateq程l束U程。Terminateq程只是单地讄U程cȝTerminated标志Q如下面的代码: procedure TThread.Terminate; 所以线E仍然必ȝl执行到正常l束后才行,而不是立即终止线E,q一点要注意?/p> 在这里说一炚w外话Q很多h都问q我Q如何才能“立即”终止线E(当然是指用TThread创徏的线E)。结果当然是 当然如果你一定要能“立即”退出线E,那么TThreadcM是一个好的选择Q因为如果用API强制l止U程的话Q最l?br />会导致TThreadU程对象不能被正释放,在对象析构时出现Access Violation。这U情况你只能用API或RTL函数来创 如果U程处于启动挂v状态,则将U程转入q行状态,然后调用WaitForq行{待Q其功能是{待到线E结束后才 U程l束后,关闭U程HandleQ正常线E创建的情况下Handle都是存在的)Q释放操作系l创建的U程对象?br />然后调用TObject.Destroy释放本对象,q攑ַl捕L异常对象Q最后调用RemoveThread减小q程的线E数?/p> 其它关于Suspend/Resume及线E优先讄{方面,不是本文的重点,不再赘述。下面要讨论的是本文的另两个重点 但是在介l这两个函数之前Q需要先介绍另外两个U程同步技术:事g和界区?/p> 事gQEventQ与Delphi中的事g有所不同。从本质上说QEvent其实相当于一个全局的布变量。它有两个赋值操?br />QSet和ResetQ相当于把它讄为True或False。而检查它的值是通过WaitFor操作q行。对应在Windowsq_上,是三 q三个都是原语,所以Event可以实现一般布变量不能实现的在多U程中的应用。Set和Reset的功能前面已l说q了 WaitFor的功能是查Event的状态是否是Set状态(相当于TrueQ,如果是则立即q回Q如果不是,则等待它变ؓSet 当Event从Reset状态向Set状态{换时Q唤醒其它由于WaitForq个Event而挂LU程Q这是它ؓ什么叫Event的原 当然用一个受保护Q见下面的界区介绍Q的布尔变量也能实现cM的功能,只要用一个@环检查此布尔值的代码?br />代替WaitFor卛_。从功能上说完全没有问题Q但实际使用中就会发玎ͼq样的等待会占用大量的CPU资源Q降低系l?br />性能Q媄响到别的U程的执行速度Q所以是不经的Q有的时候甚臛_能会有问题。所以不q样用?/p> 临界区(CriticalSectionQ则是一共享数据访问保护的技术。它其实也是相当于一个全局的布变量。但对它的操 用界区保护׃n数据的方法很单:在每ơ要讉K׃n数据之前调用Enter讄q入临界区标志,然后再操作数据, 以前面那个InterlockedIncrementZQ我们用CriticalSectionQWindows APIQ来实现它: 现在再来看前面那个例子: 临界区就是这样保护共享数据的讉K?/p> 关于临界区的使用Q有一点要注意Q即数据讉K时的异常情况处理。因为如果在数据操作时发生异常,导致Leave?br />作没有被执行Q结果将使本应被唤醒的线E未被唤醒,可能造成E序的没有响应。所以一般来_如下面这样用 EnterCriticalSection 最后要说明的是QEvent和CriticalSection都是操作pȝ资源Q用前都需要创建,使用完后也同样需要释放。如 ׃在TThread中都是用API来操作Event和CriticalSection的,所以前面都是以APIZQ其实Delphi已经提供了对?br />们的装Q在SyncObjs单元中,分别是TEventcdTCriticalSectioncR用法也与前面用API的方法相差无几。因?br />TEvent的构造函数参数过多,Z单v见,Delphiq提供了一个用默认参数初始化的Eventc:TSimpleEvent?/p> Z再介l一下另一个用于线E同步的c:TMultiReadExclusiveWriteSynchronizerQ它是在SysUtils单元中定义的 有了前面对Event和CriticalSection的准备知识,可以正式开始讨论Synchronize和WaitFor了?br />我们知道QSynchronize是通过部分代码放CU程中执行来实现U程同步的,因ؓ在一个进E中Q只有一个主U程 procedure TThread.Synchronize(Method: TThreadMethod); 其中FSynchronize是一个记录类型: 用于q行U程和主U程之间q行数据交换Q包括传入线E类对象Q同步方法及发生的异常?br />在Synchronize中调用了它的一个重载版本,而且q个重蝲版本比较特别Q它是一个“类Ҏ”。所谓类ҎQ是一U?br />Ҏ的类成员ҎQ它的调用ƈ不需要创建类实例Q而是像构造函数那P通过cd调用。之所以会用类Ҏ来实?br />它,是因Zؓ了可以在U程对象没有创徏时也能调用它。不q实际中是用它的另一个重载版本(也是cL法)和另一 class procedure TThread.Synchronize(ASyncRec: PSynchronizeRecord); q段代码略多一些,不过也不太复杂?br />首先是判断当前线E是否是ȝE,如果是,则简单地执行同步Ҏ后返回?br />如果不是ȝE,则准备开始同步过E?br />通过局部变量SyncProc记录U程交换数据Q参敎ͼ和一个Event HandleQ其记录l构如下Q?br />TSyncProc = record 然后创徏一个EventQ接着q入临界区(通过全局变量ThreadLockq行Q因为同时只能有一个线E进入Synchronize?br />态,所以可以用全局变量记录Q,然后是把这个记录数据存入SyncListq个列表中(如果q个列表不存在的话,?br />创徏它)。可见ThreadLockq个临界区就是ؓ了保护对SyncList的访问,q一点在后面介绍CheckSynchronize时会?br />ơ看到?/p> 再接下就是调用SignalSyncEventQ其代码在前面介lTThread的构造函数时已经介绍q了Q它的功能就是简单地?br />SyncEvent作一个Set的操作。关于这个SyncEvent的用途,在后面介绍WaitFor时再详述?/p> 接下来就是最主要的部分了Q调用WakeMainThread事gq行同步操作。WakeMainThread是一个TNotifyEventcd的全 procedure TApplication.HookSynchronizeWakeup; procedure TApplication.UnhookSynchronizeWakeup; 上面两个Ҏ分别是在TApplicationcȝ构造函数和析构函数中被调用?br />q就是在Application对象中WakeMainThread事g响应的代码,消息是在这里被发出的,它利用了一个空消息来实玎ͼ procedure TApplication.WakeMainThread(Sender: TObject); 而这个消息的响应也是在Application对象中,见下面的代码Q删除无关的部分Q: 其中的CheckSynchronize也是定义在Classes单元中的Q由于它比较复杂Q暂时不详细说明Q只要知道它是具体处?br />Synchronize功能的部分就好,现在l箋分析Synchronize的代码?br />在执行完WakeMainThread事g后,退Z界区Q然后调用WaitForSingleObject开始等待在q入临界区前创徏的那?br />Event。这个Event的功能是{待q个同步Ҏ的执行结束,关于q点Q在后面分析CheckSynchronize时会再说明?br />注意在WaitForSingleObject之后又重新进入界区Q但没有做Q何事退ZQ似乎没有意义,但这是必ȝQ?br />因ؓ临界区的Enter和Leave必须严格的一一对应。那么是否可以改成这样呢Q?/p> if Assigned(WakeMainThread) then 上面的代码和原来的代码最大的区别在于把WaitForSingleObject也纳入界区的限制中了。看上去没什么媄响,q 因ؓ我们知道Q在Enter临界区后Q如果别的线E要再进入,则会被挂赗而WaitForҎ则会挂v当前U程Q直到等 回到前面CheckSynchronizeQ见下面的代码: function CheckSynchronize(Timeout: Integer = 0): Boolean; 首先Q这个方法必dȝE中被调用(如前面通过消息传递到ȝE)Q否则就抛出异常?br />接下来调用ResetSyncEventQ它与前面SetSyncEvent对应的,之所以不考虑WaitForSyncEvent的情况,是因为只有在 再来看对同步Ҏ的处理:首先是从列表中移出(取出q从列表中删除)W一个同步方法调用数据。然后退Z界区 最后来说一下WaitForQ它的功能就是等待线E执行结束。其代码如下Q?br />function TThread.WaitFor: LongWord; 如果不是在主U程中执行WaitFor的话Q很单,只要调用WaitForSingleObject{待此线E的Handle为Signaled状?br />卛_?/p> 如果是在ȝE中执行WaitFor则比较麻烦。首先要在Handle数组中增加一个SyncEventQ然后@环等待,直到U程l?br />束(即MsgWaitForMultipleObjectsq回WAIT_OBJECT_0Q详见MSDN中关于此API的说明)?br />在@环等待中作如下处理:如果有消息发生,则通过PeekMessage取出此消息(但ƈ不把它从消息循环中移除)Q然?br />调用MsgWaitForMultipleObjects来等待线EHandle或SyncEvent出现Signaled状态,同时监听消息QQS_SENDMESSAGE
转脓?华夏黑客同盟 http://www.77169.org
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
起始地址Q参敎ͼ创徏标志Q用于设|线E创建时的状态)Q线EIDQ最后返回线EHandle。其中的起始地址是U?br />E函数的入口Q直至线E函数结束,U程也就l束了?/p>
SecurityAttributes: Pointer;
StackSize: LongWord;
ThreadFunc: TThreadFunc;
Parameter: Pointer;
CreationFlags: LongWord;
var ThreadId: LongWord
): Integer;
最大不同在于,U程函数一启动Q这三个U程启动函数p回了Q主U程l箋向下执行Q而线E函数在一个独立的U?br />E中执行Q它要执行多久,什么时候返回,ȝE是不管也不知道的?br />正常情况下,U程函数q回后,U程q止了。但也有其它方式Q?/p>
使用q个cM很简单,大多数的Delphi书籍都有_基本用法是:先从TThreadz一个自qU程c(因ؓTThread
是一个抽象类Q不能生成实例)Q然后是Override抽象ҎQExecuteQ这是U程函数Q也是在线E中执行的代?br />部分Q,如果需要用到可视VCL对象Q还需要通过Synchronizeq程q行。关于之斚w的具体细节,q里不再赘述Q请
参考相关书c?/p>
了解了它Q才更好C用它?br />下面是DELPHI7中TThreadcȝ声明Q本文只讨论在Windowsq_下的实现Q所以去掉了所有有关Linuxq_部分的代?br />Q:
private
FHandle: THandle;
FThreadID: THandle;
FCreateSuspended: Boolean;
FTerminated: Boolean;
FSuspended: Boolean;
FFreeOnTerminate: Boolean;
FFinished: Boolean;
FReturnValue: Integer;
FOnTerminate: TNotifyEvent;
FSynchronize: TSynchronizeRecord;
FFatalException: TObject;
procedure CallOnTerminate;
class procedure Synchronize(ASyncRec: PSynchronizeRecord); overload;
function GetPriority: TThreadPriority;
procedure SetPriority(Value: TThreadPriority);
procedure SetSuspended(Value: Boolean);
protected
procedure CheckThreadError(ErrCode: Integer); overload;
procedure CheckThreadError(Success: Boolean); overload;
procedure DoTerminate; virtual;
procedure Execute; virtual; abstract;
procedure Synchronize(Method: TThreadMethod); overload;
property ReturnValue: Integer read FReturnValue write FReturnValue;
property Terminated: Boolean read FTerminated;
public
constructor Create(CreateSuspended: Boolean);
destructor Destroy; override;
procedure AfterConstruction; override;
procedure Resume;
procedure Suspend;
procedure Terminate;
function WaitFor: LongWord;
class procedure Synchronize(AThread: TThread; AMethod: TThreadMethod); overload;
class procedure StaticSynchronize(AThread: TThread; AMethod: TThreadMethod);
property FatalException: TObject read FFatalException;
property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;
property Handle: THandle read FHandle;
property Priority: TThreadPriority read GetPriority write SetPriority;
property Suspended: Boolean read FSuspended write SetSuspended;
property ThreadID: THandle read FThreadID;
property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;
end;
constructor TThread.Create(CreateSuspended: Boolean);
begin
inherited Create;
AddThread;
FSuspended := CreateSuspended;
FCreateSuspended := CreateSuspended;
FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);
if FHandle = 0 then
raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(GetLastError)]);
end;
虽然q个构造函数没有多代码,但却可以是最重要的一个成员,因ؓU程是在这里被创徏的?br />在通过Inherited调用TObject.Create后,W一句就是调用一个过E:AddThreadQ其源码如下Q?br />procedure AddThread;
begin
InterlockedIncrement(ThreadCount);
end;
begin
InterlockedDecrement(ThreadCount);
end;
它们的功能很单,是通过增减一个全局变量来统计进E中的线E数。只是这里用于增减变量的q不是常用的
Inc/Decq程Q而是用了InterlockedIncrement/InterlockedDecrementq一对过E,它们实现的功能完全一P都是
对变量加一或减一。但它们有一个最大的区别Q那是InterlockedIncrement/InterlockedDecrement是线E安全的?br />卛_们在多线E下能保证执行结果正,而Inc/Dec不能。或者按操作pȝ理论中的术语来说Q这是一对“原语”操作?/p>
一般来_对内存数据加一的操作分解以后有三个步骤Q?br />1?从内存中d数据
2?数据加一
3?存入内存
现在假设在一个两个线E的应用中用Incq行加一操作可能出现的一U情况:
1?U程A从内存中d数据Q假设ؓ3Q?br />2?U程B从内存中d数据Q也?Q?br />3?U程AҎ据加一Q现在是4Q?br />4?U程BҎ据加一Q现在也?Q?br />5?U程A数据存入内存(现在内存中的数据?Q?br />6?U程B也将数据存入内存Q现在内存中的数据还?Q但两个U程都对它加了一Q应该是5才对Q所以这里出C
错误的结果)
以开始从中取数ƈq行加一操作Q这样就保证了即使是在多U程情况下,l果也一定会是正的?/p>
U“同时发生”的事情。而在英文中,Synchronize的意思有两个Q一个是传统意义上的同步QTo occur at the same
timeQ,另一个是“协调一致”(To operate in unisonQ。在“线E同步”中的Synchronize一词应该是指后面一U?br />意思,即“保证多个线E在讉K同一数据Ӟ保持协调一_避免出错”。不q像q样译得不准的词在IT业还有很?br />Q既然已l是U定俗成了,本文也将l箋沿用Q只是在q里说明一下,因ؓ软g开发是一细致的工作Q该弄清楚的
Q绝不能含糊?/p>
FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);
q里qC前面说到的Delphi RTL函数BeginThreadQ它有很多参敎ͼ关键的是W三、四两个参数。第三个参数是
前面说到的线E函敎ͼ卛_U程中执行的代码部分。第四个参数则是传递给U程函数的参敎ͼ在这里就是创建的U程
对象Q即SelfQ。其它的参数中,W五个是用于讄U程在创建后xP不立x行(启动U程的工作是?br />AfterConstruction中根据CreateSuspended标志来决定的Q,W六个是q回U程ID?/p>
Q因为BeginThreadq程的参数约定只能用全局函数Q。下面是它的代码Q?/p>
var
FreeThread: Boolean;
begin
try
if not Thread.Terminated then
try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
end;
finally
FreeThread := Thread.FFreeOnTerminate;
Result := Thread.FReturnValue;
Thread.DoTerminate;
Thread.FFinished := True;
SignalSyncEvent;
if FreeThread then Thread.Free;
EndThread(Result);
end;
end;
虽然也没有多代码,但却是整个TThread中最重要的部分,因ؓq段代码是真正在U程中执行的代码。下面对代码?br />逐行说明Q?br />首先判断U程cȝTerminated标志Q如果未被标志ؓl止Q则调用U程cȝExecuteҎ执行U程代码Q因为TThread
是抽象类QExecuteҎ是抽象方法,所以本质上是执行派生类中的Execute代码?/p>
procedure TThread.DoTerminate;
begin
if Assigned(FOnTerminate) then Synchronize(CallOnTerminate);
end;
OnTerminate事gQ?br />procedure TThread.CallOnTerminate;
begin
if Assigned(FOnTerminate) then FOnTerminate(Self);
end;
Synchronize的分析)?/p>
begin
SetEvent(SyncEvent);
end;
在WaitForq程中说明?/p>
下来的析构函数实现?br />最后调用EndThreadl束U程Q返回线E返回倹{至此,U程完全l束?br />说完构造函敎ͼ再来看析构函敎ͼ
destructor TThread.Destroy;
begin
if (FThreadID <> 0) and not FFinished then begin
Terminate;
if FCreateSuspended then
Resume;
WaitFor;
end;
if FHandle <> 0 then CloseHandle(FHandle);
inherited Destroy;
FFatalException.Free;
RemoveThread;
end;
begin
FTerminated := True;
end;
不行Q终止线E的唯一办法是让ExecuteҎ执行完毕Q所以一般来_要让你的U程能够快l止Q必d
ExecuteҎ中在较短的时间内不断地检查Terminated标志Q以便能及时地退出。这是设计线E代码的一个很重要的原
则!
建线E?/p>
l向下执行。关于WaitFor的实玎ͼ放到后面说明?/p>
QSynchronize和WaitFor?/p>
个API函数QSetEvent、ResetEvent、WaitForSingleObjectQ实现WaitFor功能的APIq有几个Q这是最单的一个)?/p>
Q现在来说一下WaitFor的功能:
状态,在等待期_调用WaitFor的线E处于挂L态。另外WaitFor有一个参数用于超时设|,如果此参Cؓ0Q则?br />{待Q立卌回Event的状态,如果是INFINITE则无限等待,直到Set状态发生,若是一个有限的数|则等待相应的
毫秒数后q回Event的状态?/p>
因。所谓“事件”就是指“状态的转换”。通过Event可以在线E间传递这U“状态{换”信息?/p>
作有所不同Q它只有两个操作QEnter和LeaveQ同样可以把它的两个状态当作True和FalseQ分别表C现在是否处于
界区中。这两个操作也是原语Q所以它可以用于在多U程应用中保护共享数据,防止讉K冲突?/p>
最后调用Leaved临界区。它的保护原理是q样的:当一个线E进入界区后,如果此时另一个线E也要访问这个数
据,则它会在调用EnterӞ发现已经有线E进入界区Q然后此U程׃被挂P{待当前在界区的线E调?br />Leaved临界区,当另一个线E完成操作,调用Leaved后,此线E就会被唤醒Qƈ讄临界区标志,开始操作数
据,q样防止了讉K冲突?/p>
Var
InterlockedCrit : TRTLCriticalSection;
Procedure InterlockedIncrement( var aValue : Integer );
Begin
EnterCriticalSection( InterlockedCrit );
Inc( aValue );
LeaveCriticalSection( InterlockedCrit );
End;
1. U程Aq入临界区(假设数据?Q?br />2. U程Bq入临界区,因ؓA已经在界区中,所以B被挂?br />3. U程AҎ据加一Q现在是4Q?br />4. U程Ad临界区,唤醒U程BQ现在内存中的数据是4Q?br />5. U程B被唤醒,Ҏ据加一Q现在就?了)
6. U程Bd临界区,现在的数据就是正的了?/p>
界区才是正确的做法:
Try
// 操作临界区数?br />Finally
LeaveCriticalSection
End;
TThreadcȝ到的一个全局EventQSyncEvent和全局CriticalSectionQTheadLockQ都是在
InitThreadSynchronization和DoneThreadSynchronization中进行创建和释放的,而它们则是在Classes单元?br />Initialization和Finalization中被调用的?/p>
。据我所知,q是Delphi RTL中定义的最长的一个类名,q好它有一个短的别名:TMREWSync。至于它的用处,我想?br />看名字就可以知道了,我也׃多说了?/p>
。先来看看Synchronize的实玎ͼ
begin
FSynchronize.FThread := Self;
FSynchronize.FSynchronizeException := nil;
FSynchronize.FMethod := Method;
Synchronize(@FSynchronize);
end;
PSynchronizeRecord = ^TSynchronizeRecord;
TSynchronizeRecord = record
FThread: TObject;
FMethod: TThreadMethod;
FSynchronizeException: TObject;
end;
个类ҎStaticSynchronize。下面是q个Synchronize的代码:
var
SyncProc: TSyncProc;
begin
if GetCurrentThreadID = MainThreadID then
ASyncRec.FMethod
else begin
SyncProc.Signal := CreateEvent(nil, True, False, nil);
try
EnterCriticalSection(ThreadLock);
try
if SyncList = nil then
SyncList := TList.Create;
SyncProc.SyncRec := ASyncRec;
SyncList.Add(@SyncProc);
SignalSyncEvent;
if Assigned(WakeMainThread) then
WakeMainThread(SyncProc.SyncRec.FThread);
LeaveCriticalSection(ThreadLock);
try
WaitForSingleObject(SyncProc.Signal, INFINITE);
finally
EnterCriticalSection(ThreadLock);
end;
finally
LeaveCriticalSection(ThreadLock);
end;
finally
CloseHandle(SyncProc.Signal);
end;
if Assigned(ASyncRec.FSynchronizeException) then
raise ASyncRec.FSynchronizeException;
end;
end;
SyncRec: PSynchronizeRecord;
Signal: THandle;
end;
局事g。这里之所以要用事件进行处理,是因为SynchronizeҎ本质上是通过消息Q将需要同步的q程攑ֈȝE中
执行Q如果在一些没有消息@环的应用中(如Console或DLLQ是无法使用的,所以要使用q个事gq行处理?br />而响应这个事件的是Application对象Q下面两个方法分别用于设|和清空WakeMainThread事g的响应(来自Forms单元Q:
begin
Classes.WakeMainThread := WakeMainThread;
end;
begin
Classes.WakeMainThread := nil;
end;
begin
PostMessage(Handle, WM_NULL, 0, 0);
end;
procedure TApplication.WndProc(var Message: TMessage);
?br />begin
try
?br /> with Message do
case Msg of
?br /> WM_NULL:
CheckSynchronize;
?br /> except
HandleException(Self);
end;
end;
WakeMainThread(SyncProc.SyncRec.FThread);
WaitForSingleObject(SyncProc.Signal, INFINITE);
finally
LeaveCriticalSection(ThreadLock);
end;
代码大大化了Q但真的可以吗?
事实上是不行Q?/p>
待别的线ESetEvent后才会被唤醒。如果改成上面那L代码的话Q如果那个SetEvent的线E也需要进入界区的话
Q死锁(DeadlockQ就发生了(关于死锁的理论,误行参考操作系l原理方面的资料Q?br />死锁是线E同步中最需要注意的斚w之一Q?br />最后释攑ּ始时创徏的EventQ如果被同步的方法返回异常的话,q会在这里再ơ抛出异常?/p>
var
SyncProc: PSyncProc;
LocalSyncList: TList;
begin
if GetCurrentThreadID <> MainThreadID then
raise EThread.CreateResFmt(@SCheckSynchronizeError, [GetCurrentThreadID]);
if Timeout > 0 then
WaitForSyncEvent(Timeout)
else
ResetSyncEvent;
LocalSyncList := nil;
EnterCriticalSection(ThreadLock);
try
Integer(LocalSyncList) := InterlockedExchange(Integer(SyncList), Integer(LocalSyncList));
try
Result := (LocalSyncList <> nil) and (LocalSyncList.Count > 0);
if Result then begin
while LocalSyncList.Count > 0 do begin
SyncProc := LocalSyncList[0];
LocalSyncList.Delete(0);
LeaveCriticalSection(ThreadLock);
try
try
SyncProc.SyncRec.FMethod;
except
SyncProc.SyncRec.FSynchronizeException := AcquireExceptionObject;
end;
finally
EnterCriticalSection(ThreadLock);
end;
SetEvent(SyncProc.signal);
end;
end;
finally
LocalSyncList.Free;
end;
finally
LeaveCriticalSection(ThreadLock);
end;
end;
Linux版下才会调用带参数的CheckSynchronizeQWindows版下都是调用默认参数0的CheckSynchronizeQ?br />现在可以看出SyncList的用途了Q它是用于记录所有未被执行的同步Ҏ的。因ZU程只有一个,而子U程可能?br />很多个,当多个子U程同时调用同步ҎӞȝE可能一时无法处理,所以需要一个列表来记录它们?br />在这里用一个局部变量LocalSyncList来交换SyncListQ这里用的也是一个原语:InterlockedExchange。同Pq里
也是用界区对SyncList的访问保护v来?br />只要LocalSyncList不ؓI,则通过一个@环来依次处理累积的所有同步方法调用。最后把处理完的LocalSyncList?br />放掉Q退Z界区?/p>
Q原因当然也是ؓ了防止死锁)?br />接着是真正的调用同步方法了?br />如果同步Ҏ中出现异常,被捕获后存入同步方法数据记录中?br />重新q入临界区后Q调用SetEvent通知调用U程Q同步方法执行完成了Q详见前面Synchronize中的
WaitForSingleObject调用Q?br />xQ整个Synchronize的实Cl完成?/p>
var
H: array[0..1] of THandle;
WaitResult: Cardinal;
Msg: TMsg;
begin
H[0] := FHandle;
if GetCurrentThreadID = MainThreadID then begin
WaitResult := 0;
H[1] := SyncEvent;
repeat
{ This prevents a potential deadlock if the background thread does a SendMessage to the foreground thread }
if WaitResult = WAIT_OBJECT_0 + 2 then
PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
WaitResult := MsgWaitForMultipleObjects(2, H, False, 1000, QS_SENDMESSAGE);
CheckThreadError(WaitResult <> WAIT_FAILED);
if WaitResult = WAIT_OBJECT_0 + 1 then
CheckSynchronize;
until WaitResult = WAIT_OBJECT_0;
end else
WaitForSingleObject(H[0], INFINITE);
CheckThreadError(GetExitCodeThread(H[0], Result));
end;
参数Q详见MSDN中关于此API的说明)。可以把此API当作一个可以同时等待多个Handle的WaitForSingleObject。如?br />是SyncEvent被SetEventQ返回WAIT_OBJECT_0 + 1Q,则调用CheckSynchronize处理同步Ҏ?br />Z么在ȝE中调用WaitFor必须用MsgWaitForMultipleObjectsQ而不能用WaitForSingleObject{待U程l束呢?
因ؓ防止死锁。由于在U程函数Execute中可能调用Synchronize处理同步ҎQ而同步方法是在主U程中执行的Q如
果用WaitForSingleObject{待的话Q则ȝE在q里被挂P同步Ҏ无法执行Q导致线E也被挂P于是发生死锁?br />而改用WaitForMultipleObjects则没有这个问题。首先,它的W三个参CؓFalseQ表C只要线EHandle或SyncEvent
中只要有一个Signaled卛_使主U程被唤醒,至于加上QS_SENDMESSAGE是因为Synchronize是通过消息传到ȝE来?br />Q所以还要防止消息被d。这P当线E中调用SynchronizeӞȝE就会被唤醒q处理同步调用,在调用完成后
l箋q入挂v{待状态,直到U程l束?br />xQ对U程cTThread的分析可以告一个段落了Q对前面的分析作一个ȝQ?br />1?U程cȝU程必须按正常的方式l束Q即Execute执行l束Q所以在其中的代码中必须在适当的地方加入够多
的对Terminated标志的判断,q及旉出。如果必要“立即”退出,则不能用线E类Q而要改用API或RTL函数?br />2?对可视VCL的访问要攑֜Synchronize中,通过消息传递到ȝE中Q由ȝE处理?br />3?U程׃n数据的访问应该用临界行保护(当然用Synchronize也行Q?br />4?U程通信可以采用Eventq行Q当然也可以用Suspend/ResumeQ?br />5?当在多线E应用中使用多种U程同步方式Ӟ一定要心防止出现死锁?br />6?{待U程l束要用WaitForҎ?/p>
//////////////////////////////////////////////
//---------取得utf8字符的长?--------------//
//Str:String 源字W串
//Result:Integer utf8字符串长?br>class function TPduPush.getUTF8Len(Str: string): Integer;
var
i: integer;
tmpChar: Pchar;
begin
tmpChar := pchar(str);
i := 0;
result := 0;
while i < length(tmpChar) do begin
if ord(tmpChar[i]) < $80 then begin
i := i + 1;
result := result + 1;
end else begin
i := i + 2;
result := result + 3;
end;
end;
end;
////////////////////////////////////////////////
//----------取得字符串中的字W个?-----------//
//str:String 源字W串
//Result:Integer 字符个数,兼容中文双字?br>class function TPduPush.getAnsiLen(Str: string): integer;
var
i: integer;
tmpChar: Pchar;
begin
tmpChar := pchar(str);
i := 0;
result := 0;
while i < length(tmpChar) do begin
if ord(tmpChar[i]) < $80 then
i := i + 1
else
i := i + 2;
result := result + 1;
end;
end;
/////////////////////////////////////////////////
//---------截取指定长度的utf8字符?-----------//
//str:string 源字W串
//count:Integer 指定长度 一个汉字占三个字节,长度只能?不能?br>//Result:string 截取后的utf8字符?br>class function TPduPush.getUTF8String(Str: string; count: Integer): string;
var
i, j: integer;
tmpChar: Pchar;
begin
tmpChar := pchar(str);
i := 0;
j := 0;
result := '';
while i < length(tmpChar) do begin
if j >= count then break; //英文转码后不能超q指定的位数
if ord(tmpChar[i]) < $80 then begin
result := result + string(tmpChar[i]);
i := i + 1;
j := j + 1;
end else begin
if j + 2 >= count then break; //汉字转码后不能超q指定的位数
result := result + string(tmpChar[i]) + string(tmpChar[i + 1]);
i := i + 2;
j := j + 3;
end;
end;
end;
量让name和url短点Q这L码后可以攑ֈ一条短消息里面Q而不需要把一个设|拆分成多个短消息体。大致的一个封装是把xml文g转成wbxmlQ然后再在外面封装WSP层,最外面是WDP层?/p>
WDP的一般格式是?B0504C34FC002000304xxyy”,其中xx是整个数据包的ȝ断数目,而yy表示当前片断是第几个片断。D个例子,一个简单的bookmark全部攑֜一个sms中这样xxQ?1QyyQ?1?br>下面是每个byte的意?
# 0B | User-Data-Header (UDHL) Length = 11 bytes
# 05 | UDH IE identifier: Port numbers
# 04 | UDH port number IE length
# C3 | Destination port (high)
# 4F | Destination port (low)
# C0 | Originating port (high)
# 02 | Originating port (low)
# 00 | UDH IE identifier: SAR
# 03 | UDH SAR IE length
# 04 | Datagram ref no.
#
# Two variable bytes, intentionally missing from WDP header, user must
# calculate and add at send time.
#
# xx | Total number of segments in datagram
# yy | Segment count
bookmark的WSP层的格式一般是"01062D1F2A6170706C69636174696F6E2F782D7761702D70726F762E62726F777365722D626F6F6B6D61726B730081EA"
每个byte的具体意思是Q?/p>
# 01 | Transaction ID /
# 06 | PDU type (push)
# xx | Header length (content type headers)
# 1F | Value length quote length greater than 30
# 2A | Value length (value name not used)
# xx | Mimetype encoded, variable bytes | application/x-wap-prov.browser-{bookmarks | settings}
# 00 | Null termination of content type string |
# 81 | Charset | Well known PARM. (short integer)
# EA | UTF-8 (using short integer)
最里层是WBXML了,首先必须有个xml的头"01016A00"
# 01 | Version | WBXML 1.1
# 01 | Unknown public identifier |
# 6A | Charset | UTF-8
# 00 | String table length |
http://www.cnblogs.com/zhengyun_ustc/archive/2005/09/05/otawapbookmark.html
[SMS&WAP]实例讲解制作OTA短信来自动配|手机WAP书签[附源码]
摘要:OTAQ即Over The AirQ国内翻译ؓIZ下蝲?
OTA标准q立信和诺Z共同制订。OTA늛了许多范_比如Kjava中的应用E序下蝲也是通过OTA。我们这文章主要讲的是Q通过短信方式IZ下蝲配置信息Q参考的文档是OTA_settings_general_7_0.pdf?
规范中定义了三种SettingQ?
? 览器设|?
? 览器的书签讄
? SyncML讄
也就是说Q你通过发送短信可以帮助用h机配|这三种讄?
原则上,你只要看了OTA_settings_general_7_0.pdfQƈ参照OTA_service_settings_example_v11.pdfQ就可以L地制作出W合规范的OTA短信?
但是Q本文档的目的就是让你简单粗暴地直奔主题Q看完这文档后Q就了解了OTA短信的概念,通过以下代码Q?
OTAMessage
OTAMessage message = new OTAMessage();
txtOTAResult.Text = message.Get
服务器端代码如下:
׃比较?所以不贴注释了,如果有什么不懂d地方,大家对着
unit Listener;
interface
uses
SysUtils, Controls, Forms, winsock, Classes, ComCtrls, StdCtrls;
const ASYNC_EVENT = $0400 + 1;
SO_CONDITIONAL_ACCEPT = $3002;
type
TCMSocketMessage = record //select 消息l构
Msg: Cardinal; //pȝ消息
Socket: TSocket; //产生消息的源socket 句柄
SelectEvent: Word; //select消息
SelectError: Word; //错误
Result: Longint;
end;
type
TMain = class(TForm)
SBar: TStatusBar;
Memo1: TMemo;
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
s: TSocket;
SClinent: TSocket;
procedure bindAddr;
procedure CMIncCount(var Msg: TCMSocketMessage); message ASYNC_EVENT;
procedure listenAddr;
{ Private declarations }
public
{ Public declarations }
end;
var
Main: TMain;
implementation
{$R *.dfm}
procedure TMain.FormDestroy(Sender: TObject);
begin
closeSocket(s);
WSACleanup();
end;
procedure TMain.FormCreate(Sender: TObject);
var
wsa: TWSaData;
flag: integer;
begin
SClinent := 0;
//SysUtils.BoolToStr()
flag := WSAStartup($0202, wsa); //加蝲winsock
if flag <> 0 then begin
SBar.Panels[2].Text := format('错误?%d', [WSAGetLastError()]);
SBar.Panels[1].Text := 'Winsock库加载失?;
end;
bindAddr;
listenAddr;
end;
procedure TMain.bindAddr;
var
addr: TSockAddrIn;
flag: integer;
begin
s := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //服务器端的socket
addr.sin_port := htons(45531);
addr.sin_family := AF_INET;
addr.sin_addr.S_addr := INADDR_ANY; //inet_addr(pchar(host));
flag := bind(s, addr, sizeof(addr));
if flag = SOCKET_ERROR then begin
SBar.Panels[2].Text := format('错误?%d', [WSAGetLastError()]);
SBar.Panels[1].Text := 'IPl定错误';
end else begin
flag := WSAAsyncSelect(s, Handle, ASYNC_EVENT, FD_ACCEPT or FD_CONNECT or FD_CLOSE or FD_READ or FD_WRITE);
if flag = SOCKET_ERROR then begin
SBar.Panels[2].Text := format('错误?%d', [WSAGetLastError()]);
SBar.Panels[1].Text := 'WSAAsyncSelect错误';
end;
end;
end;
procedure TMain.listenAddr;
var flag: integer;
begin
flag := listen(s, 10);
if flag = SOCKET_ERROR then begin
SBar.Panels[2].Text := format('错误?%d', [WSAGetLastError()]);
SBar.Panels[1].Text := '监听p|';
end;
end;
procedure TMain.CMIncCount(var Msg: TCMSocketMessage);
var
addr: TSockAddrIn;
len: integer;
SendBuf: array[1..1024] of AnsiChar;
recvBuf: array[1..1024] of AnsiChar;
str: string;
OldOpenType {, NewOpenType}: integer;
begin
len := 0;
str := '';
case Msg.SelectEvent of
FD_READ: begin
len := sizeof(recvBuf);
ioctlsocket(SClinent, FIONREAD, Longint(len));
fillchar(recvBuf, sizeof(recvBuf), 0);
recv(SClinent, recvBuf, sizeof(recvBuf), 0);
Memo1.Lines.Add(string(recvBuf));
Memo1.Lines.Add('read');
if Memo1.Lines.Count > 10 then
memo1.Clear;
sleep(10);
fillchar(SendBuf, sizeof(SendBuf), 0);
Strcopy(@SendBuf, pansichar('OK'));
Send(SClinent, sendbuf, sizeof(sendbuf), 0);
end;
FD_WRITE: begin
Memo1.Lines.Add('write');
end;
FD_ACCEPT: begin
len := sizeof(OldOpenType);
if getsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, PChar(@OldOpenType), len) = 0 then begin
try
len := sizeof(addr);
SClinent := accept(s, @addr, @len);
if SClinent = INVALID_SOCKET then begin
Memo1.Lines.Add('无效的socket:' + inttostr(SClinent));
end;
Memo1.Lines.Add('accept');
finally
len := sizeof(OldOpenType);
setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, PChar(@OldOpenType), len);
end;
end;
WSAAsyncSelect(SClinent, handle, ASYNC_EVENT, $33);
end;
FD_CONNECT: begin
Memo1.Lines.Add('connect');
end;
FD_CLOSE: begin
Memo1.Lines.Add('close');
end;
end;
end;
end.
//׃服务器端没有~存机制,所以多个clientq接的时?W二个client的socket会覆盖前一个的,大家看情冉|改就行了,|络上大把代码都是用控g或者其他封装好dcL写d,所以资料郁h?
客户端代?
program Client;
{$APPTYPE CONSOLE}
uses
SysUtils,
windows,
winsock;
var
addr: TSockAddrIn;
wsa: TWSaData;
flag: integer;
s: TSocket;
Host: string;
Port: Word;
BufSend: array[1..1024] of Ansichar; //中间信息
BufRev: array[1..1024] of Ansichar;
i: Integer;
begin
{ TODO -oUser -cConsole Main : Insert code here }
Host := '127.0.0.1';
port := 45531;
flag := WSAStartup($0202, wsa); //加蝲winsock
if flag <> 0 then begin
Writeln(format('错误?%d', [WSAGetLastError()]));
Writeln('Winsock库加载失?);
end else begin
Writeln('Winsock库加载成?)
end;
//s := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //服务器端的socket
s := socket(PF_INET, SOCK_STREAM, 0);
FillChar(addr, sizeof(addr), 0); //初始化地址I间
addr.sin_port := htons(port);
addr.sin_family := AF_INET;
addr.sin_addr.S_addr := {INADDR_ANY; } inet_addr(pchar(host));
if connect(s, addr, sizeof(addr)) = 0 then begin
Writeln('L:' + Host + ' q接成功')
end else begin
Writeln('L:' + Host + ' q接p|');
end;
FillChar(BufSend, 1024, 0);
StrPCopy(@BufSend, '试信息?);
for i := 0 to 100 do begin
Writeln(inttostr(s));
if Send(s, Bufsend, Length(BufSend), 0) <> SOCKET_ERROR then begin
Writeln('消息已发?);
sleep(500);
FillChar(BufRev, 1024, 0);
//strcopy(bufsend,pansichar('a'))
if recv(s, BufRev, Length(BufSend), 0) <> SOCKET_ERROR then begin
writeln('接收到的信息:' + trim(string(BufRev)));
end else begin
Writeln('接收消息p|!')
end;
end else begin
Writeln('消息发送失?)
end;
end;
if closeSocket(s) = 0 then begin
Writeln('已经关闭socket')
end else begin
Writeln('关闭socket 出错')
end;
WSACleanup();
Readln;
end.
关于delphi 的函数调用和参数传递方式深入研I?/p>
delphi 代码如下:
program Project1;
uses windows, SysUtils;
function a(d, dd: word; s, j, f: string): word;
begin
d := d - 1;
result := d + dd;
messagebox(0, pchar(inttostr(d) + s), pchar(inttostr(dd)), $0);
end;
var s: string;
begin
s := 'sssssssssss';
{ asm
mov dx,$11
mov ax,$33
call a;
end; }
a($33, $11, 'sssss', s, s);
end.
汇编代码:
00407CA8 . 447C4000 DD Project1.00407C44
00407CAC > $ 55 PUSH EBP //保存栈顶
00407CAD . 8BEC MOV EBP,ESP //初始化堆?br>00407CAF . 83C4 F0 ADD ESP,-10 //开?0个字节的?br>00407CB2 . B8 6C7C4000 MOV EAX,Project1.00407C6C
00407CB7 . E8 34C7FFFF CALL Project1.004043F0
00407CBC . 33C0 XOR EAX,EAX //EAX?
00407CBE . 55 PUSH EBP //保存栈顶
00407CBF . 68 037D4000 PUSH Project1.00407D03 //保存q回地址
00407CC4 . 64:FF30 PUSH DWORD PTR FS:[EAX] //
00407CC7 . 64:8920 MOV DWORD PTR FS:[EAX],ESP //
00407CCA . B8 48984000 MOV EAX,Project1.00409848
00407CCF . BA 187D4000 MOV EDX,Project1.00407D18 ; ASCII "sssssssssss" //初始化局部变?br>00407CD4 . E8 2FB9FFFF CALL Project1.00403608
00407CD9 . 68 2C7D4000 PUSH Project1.00407D2C ; ASCII "jjjjjjjjjj"
00407CDE . 68 407D4000 PUSH Project1.00407D40 ; ASCII "ffffffffffff"
00407CE3 . B9 587D4000 MOV ECX,Project1.00407D58 ; ASCII "sssss"
00407CE8 . 66:BA 1100 MOV DX,11
00407CEC . 66:B8 3300 MOV AX,33
00407CF0 . E8 9BFEFFFF CALL Project1.00407B90 //调用函数
00407CF5 . 33C0 XOR EAX,EAX
00407CF7 . 5A POP EDX
00407CF8 . 59 POP ECX
00407CF9 . 59 POP ECX
00407CFA . 64:8910 MOV DWORD PTR FS:[EAX],EDX
00407CFD . 68 0A7D4000 PUSH Project1.00407D0A
00407D02 > C3 RETN ; RET used as a jump to 00407D0A
00407D03 .^E9 D4B2FFFF JMP Project1.00402FDC
00407D08 .^EB F8 JMP SHORT Project1.00407D02
00407D0A E8 DB E8
00407D0B B5 DB B5
00407D0C B7 DB B7
00407D0D FF DB FF
00407D0E FF DB FF
00407D0F 00 DB 00
00407D10 . FFFFFFFF DD FFFFFFFF
00407D14 . 0B000000 DD 0000000B
00407D18 . 73 73 73 73 73>ASCII "sssssssssss",0
00407D24 . FFFFFFFF DD FFFFFFFF
00407D28 . 0A000000 DD 0000000A
00407D2C . 6A 6A 6A 6A 6A>ASCII "jjjjjjjjjj",0
00407D37 00 DB 00
00407D38 . FFFFFFFF DD FFFFFFFF
00407D3C . 0C000000 DD 0000000C
00407D40 . 66 66 66 66 66>ASCII "ffffffffffff",0
00407D4D 00 DB 00
00407D4E 00 DB 00
00407D4F 00 DB 00
00407D50 . FFFFFFFF DD FFFFFFFF
00407D54 . 05000000 DD 00000005
00407D58 . 73 73 73 73 73>ASCII "sssss",0
function a(d, dd: word; s, j, f: string): word;
函数的代?
00407B90 /$ 55 PUSH EBP
00407B91 |. 8BEC MOV EBP,ESP
00407B93 |. 6A 00 PUSH 0
00407B95 |. 6A 00 PUSH 0
00407B97 |. 6A 00 PUSH 0
00407B99 |. 53 PUSH EBX
00407B9A |. 56 PUSH ESI
00407B9B |. 57 PUSH EDI
00407B9C |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX
00407B9F |. 8BF2 MOV ESI,EDX
00407BA1 |. 8BD8 MOV EBX,EAX
00407BA3 |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
00407BA6 |. E8 49BDFFFF CALL Project1.004038F4
00407BAB |. 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
00407BAE |. E8 41BDFFFF CALL Project1.004038F4
00407BB3 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
00407BB6 |. E8 39BDFFFF CALL Project1.004038F4
00407BBB |. 33C0 XOR EAX,EAX
00407BBD |. 55 PUSH EBP
00407BBE |. 68 317C4000 PUSH Project1.00407C31
00407BC3 |. 64:FF30 PUSH DWORD PTR FS:[EAX]
00407BC6 |. 64:8920 MOV DWORD PTR FS:[EAX],ESP
00407BC9 |. 4B DEC EBX
00407BCA |. 8D3C1E LEA EDI,DWORD PTR DS:[ESI+EBX]
00407BCD |. 6A 00 PUSH 0
00407BCF |. 8D55 F8 LEA EDX,DWORD PTR SS:[EBP-8]
00407BD2 |. 0FB7C6 MOVZX EAX,SI
00407BD5 |. E8 5AD6FFFF CALL Project1.00405234
00407BDA |. 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]
00407BDD |. E8 22BDFFFF CALL Project1.00403904
00407BE2 |. 50 PUSH EAX
00407BE3 |. 8D55 F4 LEA EDX,DWORD PTR SS:[EBP-C]
00407BE6 |. 0FB7C3 MOVZX EAX,BX
00407BE9 |. E8 46D6FFFF CALL Project1.00405234
00407BEE |. 8D45 F4 LEA EAX,DWORD PTR SS:[EBP-C]
00407BF1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
00407BF4 |. E8 33BCFFFF CALL Project1.0040382C
00407BF9 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
00407BFC |. E8 03BDFFFF CALL Project1.00403904
00407C01 |. 50 PUSH EAX ; |Text
00407C02 |. 6A 00 PUSH 0 ; |hOwner = NULL
00407C04 |. E8 23C9FFFF CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA //q里stdcall方式调用messagebox
00407C09 |. 33C0 XOR EAX,EAX
00407C0B |. 5A POP EDX
00407C0C |. 59 POP ECX
00407C0D |. 59 POP ECX
00407C0E |. 64:8910 MOV DWORD PTR FS:[EAX],EDX
00407C11 |. 68 387C4000 PUSH Project1.00407C38
00407C16 |> 8D45 F4 LEA EAX,DWORD PTR SS:[EBP-C]
00407C19 |. BA 03000000 MOV EDX,3
00407C1E |. E8 B5B9FFFF CALL Project1.004035D8
00407C23 |. 8D45 08 LEA EAX,DWORD PTR SS:[EBP+8]
00407C26 |. BA 02000000 MOV EDX,2
00407C2B |. E8 A8B9FFFF CALL Project1.004035D8
00407C30 \. C3 RETN
debug registers :
/** ȝ?包含几个转码的函?/font>
* 作?刘昆
* 最后修Ҏ? 2004-11-18
* 以上代码免费,若直接引用一下代码请告知,q保留此注释
* 作ؓ一名程序员应该有最基本的职业道?/
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TFormMain = class(TForm)
Panel1: TPanel;
Memo1: TMemo;
ComboBox1: TComboBox;
Button1: TButton;
Memo2: TMemo;
procedure Button1Click(Sender: TObject);
private
function StrToUTF8(str: WideString): string;
function StrToASC(Str: string): string;
function GB2Unicode(Str: WideString): string; overload;
//function GB2Unicode(Str: string): string; overload;
function U2GB(Str: string): string;
function UTF8ToStr(const str: UTF8String): string;
function HexToInt(const Str: string): integer;
function HexIndex(const c: Char): Integer;
{ Private declarations }
public
{ Public declarations }
end;
var
FormMain: TFormMain;
implementation
{$R *.dfm}
{ TFormMain }
function TFormMain.StrToASC(Str: string): string;
var
TmpStr: string;
TmpPchar: Pchar;
i: integer;
begin
result := '';
TmpStr := '';
TmpPchar := pchar(Str);
for i := 0 to length(TmpPchar) - 1 do
TmpStr := TmpStr + format('%2.2x', [ord(TmpPchar[i])]);
result := TmpStr;
end;
function TFormMain.StrToUTF8(str: WideString): string;
var
s: pchar;
i: integer;
tmp: string;
begin
tmp := '';
result := '';
s := pchar(Utf8encode(str));
for i := 0 to strlen(s) do begin
tmp := tmp + format('%2.2x', [ord(s[i])]);
end;
result := tmp;
end;
function TFormMain.UTF8ToStr(const str: UTF8String): string;
var
s: pchar;
i: integer;
tmp: string;
begin
tmp := '';
result := '';
s := PChar(str);
i := 0;
while i < length(s) do begin
tmp := tmp + chr(HexToInt(s[i] + s[i + 1]));
inc(i, 2);
end;
result := Utf8Decode(tmp);
end;
function TFormMain.GB2Unicode(Str: WideString): string;
var
i: Integer;
begin
Result := '';
for i := 1 to Length(Str) do
Result := Result + Format('%4.4x', [ord(Str[i])]);
end;
procedure TFormMain.Button1Click(Sender: TObject);
begin
case ComboBox1.ItemIndex of
0: memo2.Lines.Add(GB2Unicode(memo1.Lines.Text));
1: memo2.Lines.Add(StrToUTF8(memo1.Lines.Text));
2: memo2.Lines.Add(UTF8ToStr(memo1.Lines.Text));
3: memo2.Lines.Add(U2GB(StringReplace(memo1.Lines.Text, '\u', '', [rfReplaceAll])));
4: memo2.Lines.Add(StrToASC(memo1.Lines.Text));
end;
end;
function TFormMain.HexToInt(const Str: string): integer;
var p: pchar;
begin
result := -1;
if length(str) > 2 then exit;
p := pchar(str);
if (HexIndex(p[0]) <> -1) and (HexIndex(p[1]) <> -1) then
result := HexIndex(p[0]) * $10 + HexIndex(p[1]);
end;
function TFormMain.HexIndex(const c: Char): Integer;
const Digits: array[0..15] of Char = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
var i: integer;
begin
result := -1;
if (not (UpCase(c) in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'])) then
exit;
for i := 0 to high(digits) do
if Digits[i] = UpCase(c) then begin
result := i;
break;
end;
end;
function TFormMain.U2GB(Str: string): string;
var s: pchar;
i: integer;
tmp: string;
begin
tmp := '';
result := '';
s := PChar(str);
i := 0;
while i < length(s) do begin
tmp := tmp + chr(HexToInt(s[i + 2] + s[i + 3])) + chr(HexToInt(s[i] + s[i + 1]));//unicode转换?高低位互?br> inc(i, 4);
end;
result := widechartostring(pWideChar(tmp + #0#0#0#0));
end;
end.
/** E序的核?一个postU程,用于提交xml数据?/font>
* 作?刘昆
* 最后修Ҏ? 2004-9-23
* 以上代码免费,若直接引用一下代码请告知,q保留此注释
* 作ؓ一名程序员应该有最基本的职业道?/
unit HTTPGetThread;
interface
uses classes, SysUtils, wininet, windows;
type
TOnProgressEvent = procedure(TotalSize, Readed: Integer) of object;
THTTPGetThread = class(TThread)
private
FTAcceptTypes: string; //接收文gcd *.*
FTAgent: string; //览器名 Nokia6610/1.0 (5.52) Profile/MIDP-1.0 Configuration/CLDC-1.02
FTURL: string; // url
FTFileName: string; //文g?br> FTStringResult: AnsiString;
FTUserName: string; //用户?br> FTPassword: string; //密码
FTPostQuery: string; //Ҏ?post或者get
FTReferer: string;
FTBinaryData: Boolean;
FTUseCache: Boolean; //是否从缓存读数据
FTMimeType: string; //Mimecd
FTResult: Boolean;
FTFileSize: Integer;
FTToFile: Boolean; //是否文g
BytesToRead, BytesReaded: LongWord;
FTProgress: TOnProgressEvent;
procedure ParseURL(URL: string; var HostName, FileName: string; var portNO: integer); //取得url的主机名和文件名
procedure UpdateProgress;
protected
procedure Execute; override;
public
procedure setResult(FResult: boolean);
function getResult(): boolean;
function getFileName(): string;
function getToFile(): boolean;
function getFileSize(): integer;
function getStringResult(): AnsiString;
constructor Create(aAcceptTypes, aMimeType, aAgent, aURL, aFileName,
aUserName, aPassword, aPostQuery, aReferer: string; aBinaryData,
aUseCache: Boolean; aProgress: TOnProgressEvent; aToFile: Boolean);
end;
implementation
{ THTTPGetThread }
constructor
THTTPGetThread.Create(aAcceptTypes, aMimeType, aAgent, aURL, aFileName,
aUserName, aPassword, aPostQuery, aReferer: string; aBinaryData,
aUseCache: Boolean; aProgress: TOnProgressEvent; aToFile: Boolean);
begin
FreeOnTerminate := True;
inherited Create(True);
FTAcceptTypes := aAcceptTypes;
FTAgent := aAgent;
FTURL := aURL;
FTFileName := aFileName;
FTUserName := aUserName;
FTPassword := aPassword;
//FTPostQuery := aPostQuery;
FTPostQuery := StringReplace(aPostQuery, #13#10, '', [rfReplaceAll]);
FTReferer := aReferer;
FTProgress := aProgress;
FTBinaryData := aBinaryData;
FTUseCache := aUseCache;
FTMimeType := aMimeType;
FTToFile := aToFile;
Resume;
end;
procedure THTTPGetThread.Execute;
var
hSession: hInternet; //回话句柄
hConnect: hInternet; //q接句柄
hRequest: hInternet; //h句柄
Host_Name: string; //L?br> File_Name: string; //文g?br> port_no: integer;
RequestMethod: PChar;
InternetFlag: longWord;
AcceptType: PAnsiChar;
dwBufLen, dwIndex: longword;
Buf: Pointer; //~冲?br> f: file;
Data: array[0..$400] of Char;
TempStr: AnsiString;
mime_Head: string;
procedure CloseHandles;
begin
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hSession);
end;
begin
inherited;
buf := nil;
try
try
ParseURL(FTURL, Host_Name, File_Name, port_no);
if Terminated then begin
FTResult := False;
Exit;
end;
//建立会话
hSession := InternetOpen(pchar(FTAgent), //lpszCallerName指定正在使用|络函数的应用程?br> INTERNET_OPEN_TYPE_PRECONFIG, //参数dwAccessType指定讉Kcd
nil, //服务器名QlpszProxyNameQ?accesstype为GATEWAY_PROXY_INTERNET_ACCESS和CERN_PROXY_ACCESS?br> nil, //NProxyPort参数用在CERN_PROXY_INTERNET_ACCESS中用来指定用的端口数。用INTERNET_INVALID_PORT_NUMBER相当于提供却省的端口数?br> 0); //讄额外的选择。你可以使用INTERNET_FLAG_ASYNC标志LCZ用返回句句柄的将来的Internet函数ؓ回调函数发送状态信息,使用InternetSetStatusCallbackq行此项讄
//建立q接
hConnect := InternetConnect(hSession, //会话句柄
PChar(Host_Name), //指向包含Internet服务器的L名称Q如http://www.mit.eduQ或IP地址Q如202.102.13.141Q的字符?br> port_no, //INTERNET_DEFAULT_HTTP_PORT, //是将要连l到的TCP/IP的端口号
PChar(FTUserName), //用户?br> PChar(FTPassword), //密码
INTERNET_SERVICE_HTTP, //协议
0, // 可选标讎ͼ讄为INTERNET_FLAG_SECUREQ表CZ用SSL/PCT协议完成事务
0); //应用E序定义的|用来回的句柄标识应用E序讑֤场境
if FTPostQuery = '' then RequestMethod := 'GET'
else RequestMethod := 'POST';
if FTUseCache then InternetFlag := 0
else InternetFlag := INTERNET_FLAG_RELOAD;
AcceptType := PChar('Accept: ' + FTAcceptTypes);
//建立一个httph句柄
hRequest := HttpOpenRequest(hConnect, //InternetConnectq回的HTTP会话句柄
RequestMethod, //指向在申请中使用?动词"的字W串Q如果设|ؓNULLQ则使用"GET"
PChar(File_Name), //指向包含动词的目标对象名U的字符Ԍ通常是文件名U、可执行模块或搜索说明符
'HTTP/1.0', //指向包含HTTP版本的字W串Q如果ؓNULLQ则默认?HTTP/1.0"Q?br> PChar(FTReferer), //指向包含文档地址QURLQ的字符Ԍ甌的URL必须是从该文档获取的
@AcceptType, //指向客户接收的内容的cd
InternetFlag,
0);
mime_Head := 'Content-Type: ' + FTMimeType;
if FTPostQuery = '' then
FTResult := HttpSendRequest(hRequest, nil, 0, nil, 0)
else
//发送一个指定请求到httpserver
FTResult := HttpSendRequest(hRequest,
pchar(mime_Head), //mime ?br> length(mime_Head), //头长?br> PChar(FTPostQuery), //附加数据~冲区,可ؓI?br> strlen(PChar(FTPostQuery))); //附加数据~冲区长?/font>
if Terminated then
begin
//CloseHandles;
FTResult := False;
Exit;
end;
dwIndex := 0;
dwBufLen := 1024;
GetMem(Buf, dwBufLen);
//接收header信息和一个httph
FTResult := HttpQueryInfo(hRequest,
HTTP_QUERY_CONTENT_LENGTH,
Buf, //指向一个接收请求信息的~冲区的指针
dwBufLen, //HttpQueryInfo内容的大?br> dwIndex); //d的字节数
if Terminated then begin
FTResult := False;
Exit;
end;
if FTResult or not FTBinaryData then begin //如果h
if FTResult then
FTFileSize := StrToInt(StrPas(Buf));
BytesReaded := 0;
if FTToFile then begin
AssignFile(f, FTFileName);
Rewrite(f, 1);
end else FTStringResult := '';
while True do begin
if Terminated then begin
FTResult := False;
Exit;
end;
if not InternetReadFile(hRequest,
@Data, //数据内容
SizeOf(Data), //大小
BytesToRead) //d的字节数
then Break
else
if BytesToRead = 0 then Break
else begin
if FTToFile then
BlockWrite(f, Data, BytesToRead) //读出的数据写入文g
else begin
TempStr := Data;
SetLength(TempStr, BytesToRead);
FTStringResult := FTStringResult + TempStr;
end;
inc(BytesReaded, BytesToRead);
if Assigned(FTProgress) then //执行回调函数
Synchronize(UpdateProgress);
end;
end;
if FTToFile then
FTResult := FTFileSize = Integer(BytesReaded)
else begin
// SetLength(FTStringResult, BytesReaded);
FTResult := BytesReaded <> 0;
end;
end;
except
end;
finally
if FTToFile then CloseFile(f);
if assigned(Buf) then FreeMem(Buf);
CloseHandles;
end;
end;
function THTTPGetThread.getFileName: string;
begin
result := FTFileName;
end;
function THTTPGetThread.getFileSize: integer;
begin
result := FTFileSize;
end;
function THTTPGetThread.getResult: boolean;
begin
result := FTResult;
end;
function THTTPGetThread.getStringResult: AnsiString;
begin
result := FTStringResult;
end;
function THTTPGetThread.getToFile: boolean;
begin
result := FTToFile;
end;
procedure THTTPGetThread.ParseURL(URL: string; var HostName, FileName: string; var portNO: integer);
var
i: Integer;
begin
if Pos('http://', LowerCase(URL)) <> 0 then
Delete(URL, 1, 7);
i := Pos('/', URL);
HostName := Copy(URL, 1, i);
FileName := Copy(URL, i, Length(URL) - i + 1);
i := pos(':', hostName);
if i <> 0 then begin
portNO := strtoint(copy(hostName, i + 1, length(hostName) - i - 1));
hostName := copy(hostName, 1, i - 1);
end else portNO := 80;
if (Length(HostName) > 0) and (HostName[Length(HostName)] = '/') then SetLength(HostName, Length(HostName) - 1);
end;
procedure THTTPGetThread.setResult(FResult: boolean);
begin
FTResult := FResult;
end;
procedure THTTPGetThread.UpdateProgress;
begin
FTProgress(FTFileSize, BytesReaded);
end;
end.
/** 主要用来做线E和界面的交?/font>
* 作?刘昆
* 最后修Ҏ? 2004-9-23
* 以上代码免费,若直接引用一下代码请告知,q保留此注释
* 作ؓ一名程序员应该有最基本的职业道?/
unit MyHttpGet;
interface
uses HTTPGetThread, windows;
type
TOnDoneFileEvent = procedure(FileName: string; FileSize: Integer) of object;
TOnDoneStringEvent = procedure(Result: AnsiString) of object;
THttpGet = class
private
F_URL: string; //目标url
F_GetURLThread: THTTPGetThread; //取数据的U程
F_Accept_Types: string;
F_Agent: string;
F_Binary_Data: Boolean;
F_Use_Cache: Boolean; //是否ȝ?br> F_File_Name: string;
F_User_Name: string; //用户?br> F_Password: string; //密码
F_PostQuery: string; //Ҏ?br> F_Referer: string;
F_Mime_Type: string;
F_Wait_Thread: Boolean;
FResult: Boolean;
FProgress: TOnProgressEvent;
FDoneFile: TOnDoneFileEvent;
FDoneString: TOnDoneStringEvent;
procedure ThreadDone(Sender: TObject);
public
constructor Create();
destructor Destroy(); override;
procedure getFile();
procedure GetString();
procedure Abort();
published
property WaitThread: Boolean read F_Wait_Thread write F_Wait_Thread;
property AcceptTypes: string read F_Accept_Types write F_Accept_Types;
property Agent: string read F_Agent write F_Agent;
property BinaryData: Boolean read F_Binary_Data write F_Binary_Data;
property URL: string read F_URL write F_URL;
property UseCache: Boolean read F_Use_Cache write F_Use_Cache;
property FileName: string read F_File_Name write F_File_Name;
property UserName: string read F_User_Name write F_User_Name;
property Password: string read F_Password write F_Password;
property PostQuery: string read F_PostQuery write F_PostQuery;
property Referer: string read F_Referer write F_Referer;
property MimeType: string read F_Mime_Type write F_Mime_Type;
property OnDoneFile: TOnDoneFileEvent read FDoneFile write FDoneFile;
property OnDoneString: TOnDoneStringEvent read FDoneString write FDoneString;
end;
implementation
{ THttpGet }
procedure THttpGet.Abort;
begin
if Assigned(F_GetURLThread) then
begin
F_GetURLThread.Terminate;
F_GetURLThread.setResult(false);
end;
end;
constructor THttpGet.Create;
begin
F_Accept_Types := '*/*';
F_Agent := 'Nokia6610/1.0 (5.52) Profile/MIDP-1.0 Configuration/CLDC-1.02';
end;
destructor THttpGet.Destroy;
begin
end;
procedure THttpGet.getFile;
var
Msg: TMsg;
begin
if not Assigned(F_GetURLThread) then
begin
F_GetURLThread := THTTPGetThread.Create(F_Accept_Types,F_Mime_Type,
F_Agent, F_URL, F_File_Name, F_User_Name, F_Password, F_PostQuery,
F_Referer, F_Binary_Data, F_Use_Cache, FProgress, true);
F_GetURLThread.OnTerminate := ThreadDone;
if F_Wait_Thread then
while Assigned(F_GetURLThread) do
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end
end;
procedure THttpGet.GetString;
var
Msg: TMsg;
begin
if not Assigned(F_GetURLThread) then
begin
F_GetURLThread :=
THTTPGetThread.Create(F_Accept_Types,F_Mime_Type,F_Agent, F_URL,
F_File_Name, F_User_Name, F_Password, F_PostQuery, F_Referer,
F_Binary_Data, F_Use_Cache, FProgress, False);
F_GetURLThread.OnTerminate := ThreadDone;
if F_Wait_Thread then
while Assigned(F_GetURLThread) do
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end
end;
procedure THttpGet.ThreadDone(Sender: TObject);
begin
FResult := F_GetURLThread.getResult;
if FResult then
if F_GetURLThread.getToFile then begin
if Assigned(FDoneFile) then FDoneFile(F_GetURLThread.getFileName, F_GetURLThread.getFileSize)
end else
if Assigned(FDoneString) then FDoneString(F_GetURLThread.getStringResult);
//end else if Assigned(FError) then FError(Self);
F_GetURLThread := nil;
end;
end.
/** E序ȝ?/font>
* 作?刘昆
* 最后修Ҏ? 2004-9-23
* 以上代码免费,若直接引用一下代码请告知,q保留此注释
* 作ؓ一名程序员应该有最基本的职业道?/
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, MyHttpGet;
type
TMain = class(TForm)
Panel1: TPanel;
Edit1: TEdit;
Label1: TLabel;
Panel2: TPanel;
Panel3: TPanel;
GroupBox1: TGroupBox;
MeSend: TMemo;
GroupBox2: TGroupBox;
MeReceive: TMemo;
Button1: TButton;
CbSave: TCheckBox;
Edit2: TEdit;
Label2: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure onGetString(Result: AnsiString);
procedure onGetFile(FileName: string; FileSize: Integer);
public
{ Public declarations }
end;
var
Main: TMain;
implementation
{$R *.dfm}
procedure TMain.Button1Click(Sender: TObject);
var hg: THttpGet;
strs: TStrings;
i: Integer;
begin
hg := nil;
strs := nil;
try
strs := TStringList.Create;
hg := THttpGet.Create;
hg.WaitThread := false;
hg.AcceptTypes := '*.*';
hg.Agent := 'Nokia6610/1.0 (5.52) Profile/MIDP-1.0 Configuration/CLDC-1.02';
hg.BinaryData := false;
hg.URL := 'Http://' + Edit1.Text;
hg.UseCache := false;
hg.FileName := 'provison.xml';
hg.UserName := '';
hg.Password := '';
for i := 0 to MeSend.Lines.Count - 1 do
strs.Add(trim(MeSend.Lines[i]));
hg.PostQuery := strs.Text;
hg.Referer := 'Http://' + Edit1.Text; //text/plain
hg.MimeType := Edit2.Text;
hg.OnDoneString := onGetString;
hg.OnDoneFile := onGetFile;
hg.GetString;
finally
strs.Free;
hg.Free;
end;
end;
procedure TMain.onGetFile(FileName: string; FileSize: Integer);
begin
end;
procedure TMain.onGetString(Result: AnsiString);
begin
MeReceive.Lines.Text := Result;
end;
end.
我发?要让q些把技术拽在手里饿?
?能写个把软g了不起啊,
q个写完了以?照旧开?照旧不用W三Ҏ件或class
如果你引用或者修改以下代?请不要去掉注?q个涉及C个程序员的职业道德问?/p>
转蝲h?/p>
/** 本代码ؓ日志class
* 作?刘昆
* 最后修Ҏ? 2004-9-23
* 以上代码免费,若直接引用一下代码请告知,q保留此注释
* 作ؓ一名程序员应该有最基本的职业道?/
unit pushLog;
interface
uses classes, sysutils, windows;
var
ThreadLock: TRTLCriticalSection; //临界?/p>
const PathDelim = '\';
DriveDelim = ':';
type
Tlog = class
private
//logfile: file;
fileName: string;
function dirExist(const DirName: string): boolean;
function getDirName(const fileName: string): string;
function LastDelimiter(const Delimiters, S: string): Integer;
procedure createLogDir();
public
constructor Create(const filename: string);
destructor Destroy(); override;
procedure addLog(p: Pchar);
end;
implementation
{ Tlog }
procedure Tlog.addLog(p: Pchar);
var log_Line: pchar;
log_len: integer;
handle: Thandle;
des_Len: longword;
begin
EnterCriticalSection(ThreadLock);
log_Line := nil;
handle := $0;
des_Len := $0;
try
createLogDir;
log_len := strlen(p);
getmem(log_Line, log_len);
strcopy(log_Line, p);
handle := createfile(
pchar(fileName), //文g?br> GENERIC_READ or GENERIC_WRITE, //期望存取模式 通用d
FILE_SHARE_READ or FILE_SHARE_WRITE, //׃n模式
nil, //定义文g安全Ҏ的指针Q前提:操作pȝ支持Q?br> OPEN_ALWAYS, //打开和创建文件方式?br> FILE_ATTRIBUTE_NORMAL or FILE_FLAG_RANDOM_ACCESS, //要打开文g的标志和属性(如:隐藏Q系l等Q?br> 0); //模板文g句柄
if handle <> INVALID_HANDLE_VALUE then begin
SetFilePointer(handle, 0, nil, FILE_END);
WriteFile(handle, log_Line^, log_len, des_Len, nil);
end;
finally
CloseHandle(handle);
freeMem(log_Line);
LeaveCriticalSection(ThreadLock);
end;
end;
constructor Tlog.Create(const filename: string);
begin
self.fileName := filename;
end;
procedure Tlog.createLogDir;
var dir_Name: string;
begin
dir_Name := getDirName(fileName) + '\log';
if not DirExist(dir_Name) then begin //日志目录是否存?br> mkdir(dir_Name);
end;
end;
destructor Tlog.Destroy;
begin
inherited;
end;
function Tlog.DirExist(const DirName: string): boolean;
var
Handle: THandle;
FindData: TWin32FindData;
begin
result := false;
Handle := FindFirstFile(PChar(DirName), FindData);
if Handle <> INVALID_HANDLE_VALUE then begin
FindClose(Handle);
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = $10 then begin
result := true;
end;
end;
end;
function Tlog.getDirName(const fileName: string): string;
var
I: Integer;
begin
I := LastDelimiter(':\', Filename);
if (I > 1) and (FileName[I] = PathDelim) and (((FileName[I - 1]
<> PathDelim) and (FileName[I - 1] <> DriveDelim)) or
(ByteType(FileName, I - 1) = mbTrailByte)) then
Dec(I);
while (ByteType(FileName, I - 1) = mbTrailByte) and (I > 0) do
Dec(I);
Result := Copy(FileName, 1, I);
end;
function Tlog.LastDelimiter(const Delimiters, S: string): Integer;
var
P: PChar;
begin
Result := Length(S);
P := PChar(Delimiters);
while Result > 0 do
begin
if (S[Result] <> #0) and (StrScan(P, S[Result]) <> nil) then // 最后一个字W是否ؓ '\'或?:'
if (ByteType(S, Result) = mbTrailByte) then
Dec(Result)
else
Exit;
Dec(Result);
end;
end;
initialization
InitializeCriticalSection(ThreadLock);
finalization
DeleteCriticalSection(ThreadLock);
end.
调用Ҏ
procedure TMain.Button1Click(Sender: TObject);
var
log: Tlog;
begin
log := Tlog.Create(ExtractFileDir(Application.Exename) + '\' + 'aa.log');
log.addLog(pchar('好的' + #13#10));
log.addLog(pchar('aaaaaaaaaaaaaaaaaaaa' + #13#10));
log.Free;
end;
文本文g是由若干行组成的Q若q个字符串组成一行,一行的l尾由回车换行符表示。如果对文本文gq行操作Q则首先应通过调用AssignFileq? E徏立文件变量与外部文g的联p,q且使用Reset或ReWrite或AppendҎ打开。由于文本文件是以行为单位进行读写操作的Qƈ且每一行的? 度不一定相同,所以不能计出指定行在文g中的准确位置Q因此对于文件只能顺序的d。要Ҏ件进行读写操作,必须相应的对文gq行以读或写的方式打开Q? 也就是对一个文本文件只能单独进行读或写的操作,而不能同时进行?/p>
1?以添加方式打开文g(Append)
通过调用函数Append可打开一个已l存在的文g以便在文件末添加文本。如果在文g最后的128个字节块中,存在字符〈ctrl?〈z〉(ASCII26Q,那么文g在字节处插入,q且覆盖该字W?/p>
Appendq程的声明如下:
procedure Append(var F:text);
? 中F是一个Q意文件类型的变量Qƈ且必d用AssignFile函数打开的外部文件相联系Q如果指定的文g不存在,则会产生错误Q如果指定的文g已经? 开Q则先关闭再重新打开。当前文件的位置讄在文件末。如果分配给F的是一个空名字Q则在调用Append函数后,文g变量QFQ将同将同标准输出文? 建立联系?/p>
2 、文本文件的d和写?/p>
文本文g通过调用q程Reset后以只读方式打开后,可以用Read或Readlnq程来读取文件数据了。文本文仉过调用Write或Writelnq程来打开一文g后就可以使用或过E来写入数据?/p>
(1) 用Readq程d数据
通过调用Readq程可以从文本文件中d或数字。其声明如下Q?br>Procedure Read([var F:text;]v1 [,v2,?vn,]);
? 中F是一个文件变量,v1 ,v2,?vn用于存储d的数据,其必Mؓ相同的类型。当v1 ,v2,?vn定义为字W串型或字符型变量时Q则Readq程按照定义的长度d字符。当v1 ,v2,?vn定义为整数或实数变量Ӟ则Readq程以I格作ؓ分隔W,如果在数字中出现逗号、分h其他字符生异常?/p>
(2) 用Readlnq程d数据
通过调用Readln q程可以从文本文件中d字符丌Ӏ字W或数字Q直C行结束。其声明如下Q?br>Procedure readln([var F:text;]v1 [,v2 ,…]);
其中F是一个文件变量,v1 ,v2,?vn用于存储d的数?/p>
(3) 用Writeq程写入数据
通过调用Writeq程可以向文件中写入数据。其声明如下Q?br>Procedure Write([var F:text;]p1[,p2,…]);
其中F是一个文件变量,p1 ,p2,?pn用于存储写入的数?/p>
(4) Writeln用过E写入数?/p>
通过调用Writelnq程可以向文件中写入一行数据,q在l尾处输入回车符。声明如下:
Procedure Writeln([var F:text;]P1[,P2,…]):
3?文g的基本操?/p>
Ҏ本文件进行操作的基本函数与过E见表:
Ҏ 说明
Procedure AssignPrn(var F:text); 建立文本文g同打印机的联p?nbsp;
Function Eoln(var F:text):Boolean; 文件指针是否指向行?nbsp;
Procedure Flush(var F:text); 清空以输出方式(ReWite或AppendQ打开的文件缓冲区Q以保写入的文件字W都被写入外部文?nbsp;
Function SeekEof(var F:text): boolean; q回文g?nbsp;
Function SeekEoln(var F:text):boolean; q回文g行尾状?nbsp;
Procedure SetTextBuf(var F :text;var buf [;size:integer]); 讄文g~冲?/p>