只有10分了,问两个问题,以后有分了一定补上。(10分)

  • 主题发起人 主题发起人 ildg
  • 开始时间 开始时间
I

ildg

Unregistered / Unconfirmed
GUEST, unregistred user!
1、如何实现象flashget或nettransport那样的 图形/日志?
就是用图形的方法显示已下载的数据,我认为就是在某些地方贴上不同
的图片,请问用什么控件可以方便地实现?
2、如何在delphi中使用异步事件,我在bdn上面下了一个单元,却不知道如何使用,
单元如下:
unit ThreadNotify;
{
Copyright Martin Harvey 2003.

This unit defines a simple component that can be used by multithreaded
programs to implement asynchronous notification between worker threads
and the VCL thread.

By default, one application wide instance of the class is created,
known as "GlobalNotifier".

Since there is only one VCL thread, this should be sufficient for
most applications.

4 functions exported are to be used by worker threads:

- AmVCLThread lets a thread know whether it should use these functions,
or whether it can just call the event handler directly.

- GetNotifyHandleForEvent will map an event handler or function in the VCL to a
notification handle in the worker thread. If the notification handle
already exists, then the existing handle is returned, otherwise a new handle
is created.

- FreeNotifyHandle unmaps a particular event handler in the VCL to asynchronous
notification in the thread.

- Note that you do not necessarily have to keep track of notification handles
in your thread: they get cleaned up when your thread quits, and you can
always obtain the handle just by knowing which event you want to trigger.

- AsynchronousNotify posts a message from the worker thread to the VCL thread
which then calls the event handler.

Needless to say there were subtleties and pitfalls when designing this
unit. The two main ones are documented here, to help those that might later
want to modify it.

1.The first issue concerns the functions used to get a *Notify* Handle. It has
two parts to it:

1a
It is possible to have a set of functions that allow one to create and close
notify handles, individually - this code will do that if each thread knows
what notifications it wishes to make to the VCL thread.

Unfortunately, this is not very useful for OO programming, because one
might wish to use this notification mechanism in a 3rd party threadsafe class.
The 3rd party class won't know how many threads might be using it, and
the threads might not know what 3rd party classes need to keep track of
these notifications.

By using the functions "AmVCLThread" and "GetNotifyHandleForEvent",
all the code has to know is the event handler that one wishes to call,
and this class will keep track of all the handles. An example use of this
might be:

if AmVCLThread then
EventHandler(self)
else
begin
if GetNotifyHandleForEvent(NotifyHandleForEvent, EventHandler) then
AsynchronousNotify(EventHandler);
end;

1b
In simple uses of this class, a thread may allocate and free a notification
handles under its own control, HOWEVER:

If notify handles are being created by classes that don't necessarily know
how many threads are using them, and the code that creates threads doesn't
necessarily know about the implicit use of notify handles that may be hidden
away in 3rd party classes, then the only place that knows about all the
handles and all the threads is in this class. This then requires that we
have a mechanism of cleaning up. In this case, it's a "Watcher Thread."

I could have created custom descendants of TThread to do some appropriate
notification, but this seems more general (for all you BeginThread
addicts out there!). Every time a handle is added or removed (or more
specifically, a set of thread notifiers is added or removed), then the watcher
thread is woken up, so that it can perform a wait operation on the
thread handles of all threads currently using notifiers. When a thread
quits, then the watcher thread can determine that none of the notify
handles for that thread are required any more, and it can clean them up.
This removes the necessity for any other code to keep track of the handles,
and brings us to the next little conundrum:

2.Ensuring that the watcher thread always detects existing threads. I figured
I'd better document this, because it's not at all apparent looking at the code.

The initial problem was tha the threads could quit in a variety of ways:

- They might or might not release their notify handles.
- The thread would then terminate.
- The thread handle as used by the VCL (TThread.Handle) might be closed
almost immediately, or it might not be closed for a while.

The problem with this is that in order for the watcher thread to detect all
thread terminations, it has to use a valid thread handle to wait on.

Initial attempts were using the TThread handle in the VCL. However, in
order to get this to work, we would need to avoid a condition:

- If the VCL handle is signalled and then closed when the watcher thread
happens to to be looping round as a result of being woken up
then the watcher would miss a notification.

The only way to guarantee this seemed to be to allow only one operation that
could affect thread handles to be performed at once, AND to ensure that
the flow of control did not return out of this class until the watcher thread
had confirmed that it had caught up with the change in handles, and was
waiting on them.

The former could have been achieved via more general use of a critical section.
The latter only seemed possible using the "AtomicSignalAndWait" function.
(At last I had figured out why one might want to use that function!).
Unfortunately, the AtomicSignalAndWait function only allows you to wait on
ONE synchronisation object - not what we want when waiting for multiple
threads, and a wake-up call!

In the end, it's all made much more simple by understanding the Win32 handle
reference counting mechanism. By using the "DuplicateHandle" API call, we can
create our own copy of thread handles that we always know are valid. A thread
can create a NotifyHandle, and when we create our internal structures, we
duplicate the Thread handle to obtain our own copy of it. This then means that
the thread could quit and the VCL thread object be destroyed immediately,
and later on, when it gets round to it, our watcher thread will still have a
valid handle to the thread (in the signalled state) even though the TThread
object does not exist, and hence, will detect the thread closure properly.

Problem solved. This also allows us to use a minimum of locking between
the threads, which improves performance.
}
interface

uses Windows, SysUtils, Classes, Messages;

const
WM_THREAD_NOTIFY = WM_APP + 1;

type
TNotifyHandle = integer;

TThreadNotifiers = record
ThreadID: DWORD;
ThreadHandle: THandle;
Notifiers: TList;
end;
PThreadNotifiers = ^TThreadNotifiers;

TThreadNotify = record
NotifyHandle: TNotifyHandle;
Event: TNotifyEvent;
HandleRegistered: boolean;
UseRefCount: integer;
end;
PThreadNotify = ^TThreadNotify;

TWaitList = array of THandle;
TWaitDesc = record
ThreadID: DWORD;
IsThreadHandle: boolean;
end;
TDescList = array of TWaitDesc;

TThreadNotifier = class;

TWatcherThread = class(TThread)
private
FParentNotifier: TThreadNotifier;
FWaitList: TWaitList;
FDescList: TDescList;
protected
procedure ClearWaitList;
procedure RebuildWaitList(CloseThread: boolean; CloseThreadID: DWORD);
public
procedure Execute; override;
published
end;

TThreadNotifier = class(TComponent)
private
FNotifyHandle: TNotifyHandle;
FDestroying: boolean;
FHandlesChanged: THandle;
FCrit: TRTLCriticalSection;
FVCLTHreadID: DWORD;
FThreadNotifiers: TList;
FWatcherThread: TWatcherThread;
FHWND: THandle;
protected
procedure MessageHandler(var Msg: TMessage);

//No critical sections around these internal helper functions.
function AllocNotifyHandle:TNotifyHandle;
//Garbage collect indicates whether removed thread handle,
//necessitating wakeup of watcher thread.
function GarbageCollect:boolean;
function FindThreadNotifiers(ThreadID: DWORD):PThreadNotifiers;
function FindThreadNotifyByEvent(Notifiers:PThreadNotifiers; Event:TNotifyEvent):PThreadNotify;
function FindThreadNotifyByHandle(Notifiers: PThreadNotifiers; Handle:TNotifyHandle): PThreadNotify;
procedure WatcherThreadInterlock;
function ClearRefsForClosedThread(ThreadID: DWORD): boolean;
procedure ClearAllRefCounts;

public
constructor Create(AOwner: TComponent);override;
destructor Destroy;override;
//Externally accesible functions (do use crit sect).
function GetNotifyHandleForEvent(var NotifyHandle: TNotifyHandle; Event:TNotifyEvent):boolean;
function FreeNotifyHandle(NotifyHandle: TNotifyHandle): boolean;
function AsynchronousNotify(NotifyHandle: TNotifyHandle): boolean;
function AmVCLThread:boolean;
published
end;

var
GlobalNotifier: TThreadNotifier;

implementation

uses Forms;

const
DEFAULT_FINITE_TIMEOUT = 5000;
S_NOTIFIER_WAIT_TIMED_OUT =
'Wait for watcher thread timed out! Continuing...';
S_ARRAY_NOT_CLEARED =
'Not all thread notification structures cleaned up!';
S_NOTIFY_FUNC_CALLED_BY_VCL_THREAD =
'Notification function called by VCL thread!';
S_NOTIFY_FUNC_CALLED_DESTROYING =
'Notification function called whilst notifier is being freed!';
S_TRIED_TO_REGISTER_NIL_EVENT =
'Thread tried to register a nil event handler for notification!';
S_DUP_HANDLE_FAILED =
'Thread notifier couldn''t duplicate thread handle!';
S_NOTIFY_HANDLE_INVALID =
'Couldn''t find notification handle.';
S_MSG_HANDLER_NO_EVENT =
'Asynchronous message handler couldn''t find the event to fire!';
S_MULTIPLE_WAIT_FAILED =
'WaitForMultipleObjects failed in watcher thread.';

(************************************
* TWatcherThread *
************************************)

procedure TWatcherThread.ClearWaitList;
begin
SetLength(FWaitList, 0);
SetLength(FDescList, 0);
end;

procedure TWatcherThread.RebuildWaitList(CloseThread: boolean; CloseThreadID: DWORD);
var
NewSize: integer;
Idx: integer;
ThreadNotifiers: PThreadNotifiers;
begin
EnterCriticalSection(FParentNotifier.FCrit);
try
if CloseThread then
FParentNotifier.ClearRefsForClosedThread(CloseThreadID);

NewSize := FParentNotifier.FThreadNotifiers.Count + 1;
SetLength(FWaitList, NewSize);
SetLength(FDescList, NewSize);
for Idx := 0 to NewSize - 2 do
begin
ThreadNotifiers := PThreadNotifiers(FParentNotifier.FThreadNotifiers.Items[Idx]);
FWaitList[Idx] := ThreadNotifiers.ThreadHandle;
FDescList[Idx].ThreadID := ThreadNotifiers.ThreadID;
FDescList[Idx].IsThreadHandle := true;
end;
Idx := Pred(NewSize);
FWaitList[Idx] := FParentNotifier.FHandlesChanged;
FDescList[Idx].ThreadID := 0;
FDescList[Idx].IsThreadHandle := false;
finally
LeaveCriticalSection(FParentNotifier.FCrit);
end;
end;

procedure TWatcherThread.Execute;
var
WaitRet:DWORD;
ArrayIdx: integer;
CloseThreadID: DWORD;
CloseThread: boolean;
begin
try
CloseThread := false;
CloseThreadID := 0;
while not Terminated do
begin
RebuildWaitList(CloseThread, CloseThreadID);
WaitRet := WaitForMultipleObjects(Length(FWaitList),@FWaitList[0],false, INFINITE);
//Assert and comment to remove compiler warnings.
Assert(WAIT_OBJECT_0 = 0);
if {(WaitRet >= WAIT_OBJECT_0) and} (WaitRet < WAIT_OBJECT_0 + DWORD(Length(FWaitList))) then
begin
ArrayIdx := WaitRet - WAIT_OBJECT_0;
CloseThread := FDescList[ArrayIdx].IsThreadHandle;
CloseThreadID := FDescList[ArrayIdx].ThreadID;
end
else
begin
Assert(false, S_MULTIPLE_WAIT_FAILED);
Terminate;
end;
end;
finally
ClearWaitList;
end;
end;

(************************************
* TThreadNotifier *
************************************)

constructor TThreadNotifier.Create(AOwner: TComponent);
begin
inherited;
//Initial count of handles changed to zero - we can
//block the first time around.
FHandlesChanged := CreateSemaphore(nil, 0, High(Integer), nil);
InitializeCriticalSection(FCrit);
FVCLThreadID := GetCurrentThreadID;
FThreadNotifiers := TList.Create;
FHWND := AllocateHWND(MessageHandler);
FWatcherThread := TWatcherThread.Create(true);
FWatcherThread.FParentNotifier := self;
FWatcherThread.Resume;
end;

destructor TThreadNotifier.Destroy;
var
ReleaseRet: BOOL;
begin
FWatcherThread.Terminate;
ReleaseRet := ReleaseSemaphore(FHandlesChanged, 1, nil);
Assert(ReleaseRet);
FWatcherThread.WaitFor;
FWatcherThread.Free;
FWatcherThread := nil;
EnterCriticalSection(FCrit);
try
ClearAllRefCounts;
FDestroying := true;
if FThreadNotifiers.Count <> 0 then
Assert(false, S_ARRAY_NOT_CLEARED);
finally
LeaveCriticalSection(FCrit);
CloseHandle(FHandlesChanged);
DeleteCriticalSection(FCrit);
FThreadNotifiers.Free;
FThreadNotifiers := nil;
DeallocateHWnd(FHWND);
end;
end;

procedure TThreadNotifier.MessageHandler(var Msg: TMessage);
var
SendThreadID: DWORD;
SendHandle: TNotifyHandle;
SendThreadNotifiers: PThreadNotifiers;
SendThreadNotify: PThreadNotify;
EvtToFire: TNotifyEvent;
begin
if Msg.Msg <> WM_THREAD_NOTIFY then
exit;
SendThreadId := Msg.WParam;
SendHandle := Msg.LParam;
EnterCriticalSection(FCrit);
try
SendThreadNotify := nil;
SendThreadNotifiers := FindThreadNotifiers(SendThreadID);
if Assigned(SendThreadNotifiers) then
SendThreadNotify := FindThreadNotifyByHandle(SendThreadNotifiers, SendHandle);
if Assigned(SendThreadNotify) then
begin
EvtToFire := SendThreadNotify.Event;
Dec(SendThreadNotify.UseRefCount);
Assert(SendThreadNotify.UseRefCount >= 0);
if GarbageCollect then
WatcherThreadInterlock;
end
else
begin
Assert(false, S_MSG_HANDLER_NO_EVENT);
EvtToFire := nil;
end;
finally
LeaveCriticalSection(FCrit);
end;
if Assigned(EvtToFire) then
EvtToFire(self);
end;

//No critical sections around these internal helper functions.
function TThreadNotifier.AllocNotifyHandle:TNotifyHandle;
begin
result := FNotifyHandle;
if FNotifyHandle = High(TNotifyHandle) then
FNotifyHandle := Low(TNotifyHandle)
else
Inc(FNotifyHandle);
end;

function TThreadNotifier.GarbageCollect:boolean;
var
CurNotifiers: PThreadNotifiers;
CurNotify: PThreadNotify;
Idx1, Idx2: integer;
NeedsPack1, NeedsPack2: boolean;
begin
//2 stage process - free all unused single notifications
//and then free all required thread ID's if no notifications.
NeedsPack1 := false;
for Idx1 := 0 to Pred(FThreadNotifiers.Count) do
begin
CurNotifiers := PThreadNotifiers(FThreadNotifiers.Items[Idx1]);
NeedsPack2 := false;
//Stage 1 - Free all unused single notifications.
for Idx2 := 0 to Pred(CurNotifiers.Notifiers.Count) do
begin
CurNotify := PThreadNotify(CurNotifiers.Notifiers.Items[Idx2]);
if (not CurNotify.HandleRegistered) and (CurNotify.UseRefCount <= 0) then
begin
Dispose(CurNotify);
CurNotifiers.Notifiers.Items[Idx2] := nil;
NeedsPack2 := true;
end;
end;
if NeedsPack2 then
CurNotifiers.Notifiers.Pack;
//Stage 2 - if no singles left for this thread, destroy structures for
//this thread.
if CurNotifiers.Notifiers.Count = 0 then
begin
//Deallocate all required resources.
CloseHandle(CurNotifiers.ThreadHandle);
CurNotifiers.Notifiers.Free;
Dispose(CurNotifiers);
FThreadNotifiers.Items[Idx1] := nil;
NeedsPack1 := true;
end;
end;
if NeedsPack1 then
FThreadNotifiers.Pack;
result := NeedsPack1;
end;

function TThreadNotifier.FindThreadNotifiers(ThreadID: DWORD):PThreadNotifiers;
var
Idx: integer;
CurNotifiers: PThreadNotifiers;
begin
for Idx := 0 to Pred(FThreadNotifiers.Count) do
begin
CurNotifiers := PThreadNotifiers(FThreadNotifiers.Items[Idx]);
if CurNotifiers.ThreadID = ThreadID then
begin
result := CurNotifiers;
exit;
end;
end;
result := nil;
end;

function TThreadNotifier.FindThreadNotifyByEvent(Notifiers:PThreadNotifiers; Event:TNotifyEvent):PThreadNotify;
var
Idx: integer;
CurNotify: PThreadNotify;
begin
for Idx := 0 to Pred(Notifiers.Notifiers.Count) do
begin
CurNotify := PThreadNotify(Notifiers.Notifiers.Items[Idx]);
if (TMethod(CurNotify.Event).Code = TMethod(Event).Code) and
(TMethod(CurNotify.Event).Data = TMethod(Event).Data) then
begin
result := CurNotify;
exit;
end;
end;
result := nil;
end;

function TThreadNotifier.FindThreadNotifyByHandle(Notifiers: PThreadNotifiers; Handle:TNotifyHandle): PThreadNotify;
var
Idx: integer;
CurNotify: PThreadNotify;
begin
for Idx := 0 to Pred(Notifiers.Notifiers.Count) do
begin
CurNotify := PThreadNotify(Notifiers.Notifiers.Items[Idx]);
if CurNotify.NotifyHandle = Handle then
begin
result := CurNotify;
exit;
end;
end;
result := nil;
end;

//Helper function for sequencing interlock with watcher thread.
procedure TThreadNotifier.WatcherThreadInterlock;
begin
ReleaseSemaphore(FHandlesChanged, 1, nil);
end;

function TThreadNotifier.ClearRefsForClosedThread(ThreadID: DWORD): boolean;
var
CurThreadNotifiers:PThreadNotifiers;
CurThreadNotify:PThreadNotify;
Idx: integer;
begin
{It's not necessarily a problem here if we can't find the threadID that
we're meant to close - it may be that a thread has in fact closed its own
handles *and* quit before the watcher thread has even got round to
waking up and calling this function. In addition, just because another
thread signals "wake up watcher" before quitting does not necessarily
mean that WaitForMultipleObjects will unblock on the wake-up event
before the quit event }
CurThreadNotifiers := FindThreadNotifiers(ThreadID);
result := Assigned(CurThreadNotifiers);
if result then
begin
for Idx := 0 to Pred(CurThreadNotifiers.Notifiers.Count) do
begin
CurThreadNotify := PThreadNotify(CurThreadNotifiers.Notifiers.Items[Idx]);
CurThreadNotify.HandleRegistered := false;
//Don't clear use ref count;
end;
//No watcher interlock here - we're in the watcher thread!
GarbageCollect;
end;
end;

procedure TThreadNotifier.ClearAllRefCounts;
var
CurThreadNotifiers: PThreadNotifiers;
CurThreadNotify:PThreadNotify;
Idx1, Idx2: integer;
begin
{ Assertions are in this function mainly for debug use
but could be removed. In most sensible implementations,
all the worker threads will have quit and their structures
been closed before this function gets called.
However, some implementations might want to destroy this object
with threads still running, in which case, assertions can be removed. }
try
for Idx1 := 0 to Pred(FThreadNotifiers.Count) do
begin
CurThreadNotifiers := PThreadNotifiers(FThreadNotifiers.Items[Idx1]);
for Idx2 := 0 to Pred(CurThreadNotifiers.Notifiers.Count) do
begin
CurThreadNotify := PThreadNotify(CurThreadNotifiers.Notifiers.Items[Idx2]);
Assert(not CurThreadNotify.HandleRegistered);
Assert(CurThreadNotify.UseRefCount = 0);
CurThreadNotify.HandleRegistered := false;
CurThreadNotify.UseRefCount := 0;
end;
end;
except
on EAssertionFailed do ;
end;
//No need for interlock here - watcher thread doesn't exist.
GarbageCollect;
end;

//Externally accesible functions (do use crit sect).
function TThreadNotifier.GetNotifyHandleForEvent(var NotifyHandle: TNotifyHandle; Event:TNotifyEvent):boolean;
var
CurThreadID: DWORD;
CurThreadNotifiers: PThreadNotifiers;
CurThreadNotify: PThreadNotify;
begin
result := false;
CurThreadID := GetCurrentThreadID;
EnterCriticalSection(FCrit);
try
if CurThreadID = FVCLThreadID then
begin
Assert(false, S_NOTIFY_FUNC_CALLED_BY_VCL_THREAD);
exit;
end;
if FDestroying then
begin
Assert(false, S_NOTIFY_FUNC_CALLED_DESTROYING);
exit;
end;
if not Assigned(Event) then
begin
Assert(false, S_TRIED_TO_REGISTER_NIL_EVENT);
exit;
end;
//OK - do we have an entry for this thread?
CurThreadNotifiers := FindThreadNotifiers(CurThreadID);
if not Assigned(CurThreadNotifiers) then
begin
//Okay, we'll have to add one.
New(CurThreadNotifiers);
CurThreadNotifiers.ThreadID := CurThreadID;
//This handle is independent from TThread.Handle and
//(mercifully) will be valid even when the TThread object
//has both terminated and been freed.
if not DuplicateHandle(GetCurrentProcess, GetCurrentThread,
GetCurrentProcess,
@CurThreadNotifiers.ThreadHandle,
0, false, DUPLICATE_SAME_ACCESS) then
begin
Assert(false, S_DUP_HANDLE_FAILED);
Dispose(CurThreadNotifiers);
exit;
end;
CurThreadNotifiers.Notifiers := TList.Create;
FThreadNotifiers.Add(CurThreadNotifiers);
WatcherThreadInterlock;
end;
//OK, now have a pointer to notifications for this thread.
CurThreadNotify := FindThreadNotifyByEvent(CurThreadNotifiers, Event);
if not Assigned(CurThreadNotify) then
begin
New(CurThreadNotify);
CurThreadNotify.NotifyHandle := AllocNotifyHandle;
CurThreadNotify.Event := Event;
CurThreadNotify.UseRefCount := 0;
CurThreadNotifiers.Notifiers.Add(CurThreadNotify);
end;
//Because of notifies in transit, possible for handle not registered
//and still have an entry available for use, in which case, re-use
//handle and fix up HandleRegistered.
CurThreadNotify.HandleRegistered := true;
//Woohoo, now have an actual notification structure - let caller know &amp; quit.
result := true;
NotifyHandle := CurThreadNotify.NotifyHandle;
finally
LeaveCriticalSection(FCrit);
end;
end;

function TThreadNotifier.FreeNotifyHandle(NotifyHandle: TNotifyHandle): boolean;
var
CurThreadID: DWORD;
CurThreadNotifiers: PThreadNotifiers;
CurThreadNotify: PThreadNotify;
begin
CurThreadID := GetCurrentThreadID;
EnterCriticalSection(FCrit);
try
result := false;
if CurThreadID = FVCLThreadID then
begin
Assert(false, S_NOTIFY_FUNC_CALLED_BY_VCL_THREAD);
exit;
end;
if FDestroying then
begin
Assert(false, S_NOTIFY_FUNC_CALLED_DESTROYING);
exit;
end;
CurThreadNotifiers := FindThreadNotifiers(CurThreadID);
if Assigned(CurThreadNotifiers) then
begin
CurThreadNotify := FindThreadNotifyByHandle(CurThreadNotifiers, NotifyHandle);
//Possible to have entry with de-registered handle, but entry
//still in use because notifications in transit.
result := Assigned(CurThreadNotify) and (CurThreadNotify.HandleRegistered);
if result then
begin
CurThreadNotify.HandleRegistered := false;
if GarbageCollect then
WatcherThreadInterlock;
end
else
Assert(false, S_NOTIFY_HANDLE_INVALID);
end;
finally
LeaveCriticalSection(FCrit);
end;
end;

function TThreadNotifier.AsynchronousNotify(NotifyHandle: TNotifyHandle): boolean;
var
CurThreadID: DWORD;
CurThreadNotifiers: PThreadNotifiers;
CurThreadNotify: PThreadNotify;
begin
result := false;
CurThreadID := GetCurrentThreadID;
EnterCriticalSection(FCrit);
try
if CurThreadID = FVCLThreadID then
begin
Assert(false, S_NOTIFY_FUNC_CALLED_BY_VCL_THREAD);
exit;
end;
if FDestroying then
begin
Assert(false, S_NOTIFY_FUNC_CALLED_DESTROYING);
exit;
end;
CurThreadNotify := nil;
CurThreadNotifiers := FindThreadNotifiers(CurThreadID);
if Assigned(CurThreadNotifiers) then
CurThreadNotify := FindThreadNotifyByHandle(CurThreadNotifiers, NotifyHandle);
if not Assigned(CurThreadNotify) then
begin
Assert(false, S_NOTIFY_HANDLE_INVALID);
result := false;
exit;
end;
result := true;
Inc(CurThreadNotify.UseRefCount);
PostMessage(FHWND, WM_THREAD_NOTIFY, CurThreadID, NotifyHandle);
finally
LeaveCriticalSection(FCrit);
end;
end;

function TThreadNotifier.AmVCLThread:boolean;
begin
EnterCriticalSection(FCrit);
try
result := GetCurrentThreadID = FVCLThreadID;
finally
LeaveCriticalSection(FCrit);
end;
end;

initialization
GlobalNotifier := TThreadNotifier.Create(nil);
finalization
GlobalNotifier.Free;
GlobalNotifier := nil;
end.
————————————————————
请问怎么使用上面的单元?
 
第一问
http://2ccc.com/article.asp?articleid=297
 
呵呵,楼上推荐的东西很好用哦,谢谢了。
 
请问archonwang:
怎么用?能告诉我吗?拜托啦。
 
非常感谢gyh75,
你到这个地方re一下,
我就给你分:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=2584937
80分。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
704
import
I
I
回复
0
查看
793
import
I
后退
顶部