如何识别已调用多少级别的CoInitialize?

Jer*_*dge 8 delphi activex

我正在对一个凌乱的项目进行一些调试,这个项目是我以前的开发人员不知道他们在做什么的,而主要问题是尝试多线程应用程序失败.我现在正在清理乱七八糟的东西,试图找出出错的地方.其中一个问题是CoInitialize为了使用ADO组件而不一致的调用.

继续我之前的问题,我怎样才能确定CoInitialize调用了多少级别?

例如,考虑以下代码:

CoInitialize(nil);
try
  CoInitialize(nil);
  try
    //2 levels have been called, how to programatically check this?
  finally
    CoUninitialize;
  end;
finally
  CoUninitialize;
end;
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 10

如果让我来解决这个问题,我想通过插于电话解决它CoInitialize,CoInitializeExCoUninitialize.我会挂钩对这些函数的调用,并使用线程局部变量来计算调用.

您可以通过将以下单元添加到项目中来完成此操作.

unit InstrumentCOMinit;

interface

uses
  SysUtils, Windows, ComObj, ActiveX;

threadvar
  COMinitCount: Integer;

implementation

function CoInitialize(pvReserved: Pointer): HResult; stdcall; external 'ole32.dll';
function CoInitializeEx(pvReserved: Pointer; coInit: Longint): HResult; stdcall; external 'ole32.dll';
procedure CoUninitialize; stdcall; external 'ole32.dll';

function InstrumentedCoInitialize(pvReserved: Pointer): HResult; stdcall;
begin
  Result := CoInitialize(pvReserved);
  if Succeeded(Result) then
    inc(COMinitCount);
end;

function InstrumentedCoInitializeEx(pvReserved: Pointer; coInit: Longint): HResult; stdcall;
begin
  Result := CoInitializeEx(pvReserved, coInit);
  if Succeeded(Result) then
    inc(COMinitCount);
end;

procedure InstrumentedCoUninitialize; stdcall;
begin
  CoUninitialize;
  dec(COMinitCount);
end;

procedure Fail;
begin
  raise EAssertionFailed.Create('Fixup failed.');
end;

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
var
  OldProtect: DWORD;
begin
  if not VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then begin
    Fail;
  end;
  Move(NewCode, Address^, Size);
  FlushInstructionCache(GetCurrentProcess, nil, 0);
  if not VirtualProtect(Address, Size, OldProtect, @OldProtect) then begin
    Fail;
  end;
end;

type
  PInstruction = ^TInstruction;
  TInstruction = packed record
    Opcode: Byte;
    Offset: Integer;
  end;

procedure RedirectProcedure(OldAddress, NewAddress: Pointer);
var
  NewCode: TInstruction;
begin
  NewCode.Opcode := $E9;//jump relative
  NewCode.Offset := NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode);
  PatchCode(OldAddress, NewCode, SizeOf(NewCode));
end;

initialization
  RedirectProcedure(@ActiveX.CoInitialize, @InstrumentedCoInitialize);
  RedirectProcedure(@ActiveX.CoInitializeEx, @InstrumentedCoInitializeEx);
  RedirectProcedure(@ActiveX.CoUninitialize, @InstrumentedCoUninitialize);
  ComObj.CoInitializeEx := InstrumentedCoInitializeEx;

end.
Run Code Online (Sandbox Code Playgroud)

与Serg的方法不同,这种技术不会改变程序的语义.


klu*_*udg 6

你可以这样做:

function CoInitializeCount: Integer;
var
  HR: HResult;
  I: Integer;

begin
  Result:= 0;
  repeat
    HR:= CoInitialize(nil);
    if (HR and $80000000 <> 0) then begin
      Result:= -1;
      Exit;
    end;
    CoUnInitialize;
    if (HR <> S_OK) then begin
      CoUnInitialize;
      Inc(Result);
    end
    else Break;
  until False;
  for I:= 0 to Result - 1 do
    CoInitialize(nil);
end;
Run Code Online (Sandbox Code Playgroud)

警告!由于上面的函数关闭COM,它不能在COM应用程序中使用,只能在调试时回答特定问题.

  • @Jeroen的评论毫无意义.这段代码是线程安全的.但是,它很可能会破坏应用程序,因为现在调用此函数时存在的任何COM对象都存在于不同的COM实例中.公寓模型可能已经改变了! (5认同)