I have a class inside my DLL. And this DLL provides an "interface" to create objects of this class and call their methods.
Class code (simplified):
TLogger = class
private
//
public
function AddToLog(sBuf: PWideChar): Word;
constructor Create(Name: PWideChar);
destructor Destroy; override;
end;
constructor Create(Name: PWideChar);
begin
//
end;
destructor TLogger.Destroy;
begin
//
end;
function TLogger.AddToLog(sBuf: PWideChar): Word;
var
Temp1 : WideString;
Temp2 : AnsiString;
begin
Result := NO_ERRORS;
WaitForSingleObject( FMutex, INFINITE );
Temp1 := 'a';
Temp2 := Temp1;
//
ReleaseMutex( FMutex );
end;
DLL code
function CreateLogger( LogFileName: PWideChar; PLogger: PCardinal): Word; stdcall;
begin
try
PLogger^ := Cardinal(TLogger.Create(LogFileName));
Result := NO_ERRORS;
except
Result := ERR_CREATE_OBJECT;
end;
end;
function DestroyLogger( PLogger: Cardinal ): Word; stdcall;
begin
try
TLogger(PLogger).Free;
Result := NO_ERRORS;
except
Result := ERR_OBJECT_NOT_FOUND;
end;
end;
function AddToLog( PLogger: Cardinal; BufStr: PWideChar ): Word; stdcall;
begin
try
Result := TLogger(PLogger).AddToLog( BufStr );
except
Result := ERR_OBJECT_NOT_FOUND;
end;
end;
When I'm trying to use this library from 1 thread - everything is OK. The problems start when I create many threads that call function AddToLog
with random periods (each thread has it's own object of the class). In some time I catch Access Violation
or Invalid pointer operation
.
I've made some research and pointed out that if variable Temp2
has WideString
type everything is OK. Another solution is to move mutex to the library code (it's just a "research" code):
function AddToLog( PLogger: Cardinal; BufStr: PWideChar ): Word; stdcall;
begin
WaitForSingleObject( TLogger(PLogger).FMutex, INFINITE );
Result := TLogger(PLogger).AddToLog( BufStr );
ReleaseMutex( TLogger(PLogger).FMutex );
end;
The second solution is bad for me because each object has it's own mutex (idea is that if two objects must work with one file, they have the same mutex to wait each other; if two objects must work with different files, they have different mutexes and work in parallel).
I'm trying to solve this problem for 2 days but I can't understand what goes wrong. How string cast can cause such problem?
Put the following line:
IsMultiThread := True;
as the first line in your DLL project's main code block. This will instruct the memory manager to switch to a thread-safe mode.
This would explain a behaviour difference between AnsiString
and WideString
because AnsiString
is allocated by the Delphi memory manager, and WideString
is allocated on the COM heap. When IsMultiThread
is False
, the Delphi memory manager is not thread-safe, but the COM heap is always thread-safe.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With