spiegel-keyman/windows/src/engine/keyman/Keyman.System.SharedBuffers.pas
2018-10-29 16:34:00 +11:00

125 lines
3 KiB
ObjectPascal

unit Keyman.System.SharedBuffers;
interface
uses
Winapi.Windows;
const
MAX_SELECTKEYBOARDTSF_CIRCULARBUFFER_SIZE = 16;
GLOBAL_SHAREDBUFFER_FILE_MAPPING_NAME = 'KeymanEngine_SharedBuffer';
type
{$ALIGN 4}
TSelectKeyboardBuffer = record
LangID: WORD;
CLSID: TGUID;
GUIDProfile: TGUID;
end;
{$ALIGN 4}
TSharedBuffer = record
SelectKeyboardBuffer: array[0..MAX_SELECTKEYBOARDTSF_CIRCULARBUFFER_SIZE-1] of TSelectKeyboardBuffer;
end;
PSharedBuffer = ^TSharedBuffer;
TSharedBufferManager = class
private
class var FIdentity: TSharedBufferManager;
private
FSelectKeyboardIndex: Integer;
hMMF: THandle;
pSharedData: PSharedBuffer;
public
constructor Create;
destructor Destroy; override;
function WriteSelectKeyboardBuffer(skb: TSelectKeyboardBuffer): Integer;
class function Identity: TSharedBufferManager;
end;
implementation
uses
System.SysUtils,
Keyman.System.Security;
{ TSharedBufferManager }
constructor TSharedBufferManager.Create;
begin
inherited Create;
hMMF := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE or SEC_COMMIT, 0, sizeof(TSharedBuffer),
PChar(GLOBAL_SHAREDBUFFER_FILE_MAPPING_NAME));
if hMMF = 0 then
RaiseLastOSError;
SetObjectToLowIntegrity(hMMF);
// Most apps only need read access. TODO: reduce general access permission to READ for other processes;
// but doing so for application packages is a good start
GrantPermissionToAllApplicationPackages(hMMF, FILE_MAP_READ);
pSharedData := PSharedBuffer(MapViewOfFile(hMMF, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TSharedBuffer)));
if not Assigned(pSharedData) then
RaiseLastOSError;
end;
destructor TSharedBufferManager.Destroy;
begin
if Assigned(pSharedData) then
begin
if not UnmapViewOfFile(pSharedData) then
RaiseLastOSError;
end;
if hMMF <> 0 then
begin
if not CloseHandle(hMMF) then
RaiseLastOSError;
end;
inherited Destroy;
end;
class function TSharedBufferManager.Identity: TSharedBufferManager;
begin
if not Assigned(FIdentity) then
FIdentity := TSharedBufferManager.Create;
Result := FIdentity;
end;
//
// This comes from the Windows SDK and is a simple way of guaranteeing
// that the processor cache is flushed on a multiprocessor machine, so
// that we can be assured that the circular buffer is coherent before
// we notify the target thread. (Given that we post a message to the
// target thread and don't use synchronisation primitives, this is
// probably not going to be a problem anyway but it's the right way to
// do it.
//
procedure MemoryBarrier; inline;
var
Barrier: LONG;
begin
InterlockedOr(Barrier, 0);
end;
function TSharedBufferManager.WriteSelectKeyboardBuffer(
skb: TSelectKeyboardBuffer): Integer;
begin
Result := FSelectKeyboardIndex;
pSharedData.SelectKeyboardBuffer[Result] := skb;
MemoryBarrier;
Inc(FSelectKeyboardIndex);
if FSelectKeyboardIndex = MAX_SELECTKEYBOARDTSF_CIRCULARBUFFER_SIZE then
FSelectKeyboardIndex := 0;
end;
initialization
finalization
FreeAndNil(TSharedBufferManager.FIdentity);
end.