为了学习多线程,我在COM线程(
这是我的组件工厂:
在线程内部,我不需要调用CoInitialize来使用TADOQuery.Create、.Open和.Exec。我阅读到需要在调用库函数之前在线程上初始化COM库,除了CoGetMalloc函数外,以获取指向标准分配器和内存分配函数的指针。但是,在这种情况下,缺少CoInitialize并没有给我带来任何麻烦。
这是否与线程模型有关?我在哪里可以找到关于这个主题的解释?
更新:当我说“内部”,它意味着在COM方法上下文内部。
一些原因(可能是由于 TComponentFactory ?)使得 CoCreateInstance 确认 TWorker 与 TServerConn2 在同一上下文中,因此不会引发错误?
TRemoteDataModule
)中创建了一个线程。这是我的组件工厂:
TComponentFactory.Create(ComServer, TServerConn2, Class_ServerConn2, ciMultiInstance, tmApartment);
在线程内部,我不需要调用CoInitialize来使用TADOQuery.Create、.Open和.Exec。我阅读到需要在调用库函数之前在线程上初始化COM库,除了CoGetMalloc函数外,以获取指向标准分配器和内存分配函数的指针。但是,在这种情况下,缺少CoInitialize并没有给我带来任何麻烦。
这是否与线程模型有关?我在哪里可以找到关于这个主题的解释?
更新:当我说“内部”,它意味着在COM方法上下文内部。
interface
type
TWorker = class(TThread);
TServerConn2 = class(TRemoteDataModule, IServerConn2)
public
procedure Method(); safecall;
end;
implementation
procedure TServerConn2.Method();
var W: TWorker;
begin
W := TWorkerTread.Create(Self);
end;
更新 2:
用于连接数据库的TADOConnection
当前在COM线程上下文中创建(TThread.Create构造函数
)。 尽管如此,TADOConnection.Open
和TADOQuery.Create/.Open
都是在TThread.Execute
内执行的。
更新 3 - 模拟
接口:
type
TServerConn2 = class;
TWorker = class(TThread)
private
FDB: TADOConnection;
FOwner: TServerConn2;
protected
procedure Execute; override;
public
constructor Create(Owner: TServerConn2);
destructor Destroy; override;
end;
TServerConn2 = class(TRemoteDataModule, IServerConn2)
ADOConnection1: TADOConnection;
procedure RemoteDataModuleCreate(Sender: TObject);
private
{ Private declarations }
protected
class procedure UpdateRegistry(Register: Boolean; const ClassID, ProgID: string); override;
procedure CheckException; safecall;
public
User, Pswd, Str: String;
Ok: Boolean;
end;
实现:
class procedure TServerConn2.UpdateRegistry(Register: Boolean; const ClassID, ProgID: string);
begin
if Register then
begin
inherited UpdateRegistry(Register, ClassID, ProgID);
EnableSocketTransport(ClassID);
EnableWebTransport(ClassID);
end else
begin
DisableSocketTransport(ClassID);
DisableWebTransport(ClassID);
inherited UpdateRegistry(Register, ClassID, ProgID);
end;
end;
{ TWorker }
constructor TWorker.Create(Owner: TServerConn2);
begin
inherited Create(False);
FreeOnTerminate := True;
FDB := TADOConnection.Create(nil);
FOwner := Owner;
end;
destructor TWorker.Destroy;
begin
FDB.Free;
FOwner.Ok := True;
inherited;
end;
procedure TWorker.Execute;
var Qry: TADOQuery;
begin
FDB.LoginPrompt := False;
FDB.ConnectionString := FOwner.Str;
FDB.Open(FOwner.User, FOwner.Pswd);
Qry := TADOQuery.Create(nil);
try
Qry.Connection := FDB;
Qry.LockType := ltReadOnly;
Qry.SQL.Text := 'SELECT TOP 1 * FROM MyTable';
Qry.Open;
finally
Qry.Free;
end;
end;
procedure TServerConn2.CheckException;
var W: TWorker;
begin
W := TWorker.Create(Self);
while not Ok do Sleep(100);
end;
procedure TServerConn2.RemoteDataModuleCreate(Sender: TObject);
begin
User := 'user';
Pswd := 'pass';
Str := ADOConnection1.ConnectionString;
end;
initialization
TComponentFactory.Create(ComServer, TServerConn2,
Class_ServerConn2, ciMultiInstance, tmApartment);
end.
更新 4
错误应该发生在这里:
function CreateADOObject(const ClassID: TGUID): IUnknown;
var
Status: HResult;
FPUControlWord: Word;
begin
asm
FNSTCW FPUControlWord
end;
Status := CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, IUnknown, Result);
asm
FNCLEX
FLDCW FPUControlWord
end;
if (Status = REGDB_E_CLASSNOTREG) then
raise Exception.CreateRes(@SADOCreateError) else
OleCheck(Status);
end;
一些原因(可能是由于 TComponentFactory ?)使得 CoCreateInstance 确认 TWorker 与 TServerConn2 在同一上下文中,因此不会引发错误?
CoInitialize
? - noseratio - open to workCoInitialize
! - EProgrammerNotFoundtmApartment
指定线程安全的类工厂,但它创建的COM对象实例不是线程安全的。如果您手动创建一个线程,确实应该在其上调用CoInitialize
,并且从该线程中不应直接调用在主线程或其他线程上创建的任何对象。您可以使用COM封送来解决这个问题。 - noseratio - open to workExecute
方法在哪里?你使用TWorkerTread.Create(self)
创建了它 - 这甚至无法编译(线程不是组件,它的可选参数是布尔值 -CreateSuspended
)。你还使用了完全不同的类名;TWorker
怎么变成了TWorkerTread
?一个TApartmentThread
将以IClassFactory
作为构造函数参数,并且也可以分配给TThread
变量 - 这只是让我们回到我的答案,即你正在使用TApartmentThread
。 - J...