1 (изменено: Freeman, 05.12.2020 в 17:53)

Тема: Shared Memory

Пример показывает использование разделяемой памяти.

Запустите несколько копий этой программы.
Нажимайте на кнопки для выбора цвета окна.
Все окна запущенных копий этой программы будут окрашены в выбранный цвет.

misc.php?action=pun_attachment&item=76&download=0

Так как у каждого процесса своё адресное пространство, то мы не можем просто так читать одну и ту же область памяти из разных процессов.
Стоит здесь заметить, что речь именно о процессах, а не потоках — у потоков одного процесса и адресное пространство общее.
Так вот, если всё же такая возможность(стырить байт из соседского процесса) нужна, то можно использовать разделяемую память(Shared Memory).

Открыть или создать её можно с помощью функции OpenSharedMemory.
Существуют флаги для этой функции, их можно найти в модуле KolibriOS

  // OpenSharedMemory open\access flags
  SHM_OPEN             = $00;
  SHM_OPEN_ALWAYS      = $04;
  SHM_CREATE           = $08;
  SHM_READ             = $00;
  SHM_WRITE            = $01;

Для закрытия области существует функция CloseSharedMemory. 

В этом примере создаётся область разделяемой памяти.
В эту область записывается значение для цвета окна.
При перерисовке цвет читается из области разделяемой памяти и окно окрашивается в этот цвет.
Теперь можно запустить несколько копий программы, и все они будут читать одну и ту же область памяти, и, следовательно,
их окна будут окрашены в одинаковый цвет.

Для того чтобы после нажатия кнопки сразу же произошла перерисовка всех наших окон,
мы проверяем, совпадает ли имя процесса с именем нашего процесса.
Если совпадает, то активируем его окно, затем вызываем перерисовку своего окна(On_Redraw).

Исходный код:

program SharedMem;

uses
  KolibriOS;

const
  COLOR_BLUE    = $000000FF;
  COLOR_RED     = $00FF0000;
  COLOR_GREEN   = $0000FF00;
  COLOR_BLACK   = $00000000;
  COLOR_DEFAULT = $00F0F0F0;

  BLACK_BUTTON  = 1000;
  BLUE_BUTTON   = 2000;
  GREEN_BUTTON  = 3000;
  RED_BUTTON    = 4000;

  MY_SHARED_MEMORY_SIZE = 100; // in bytes
  MY_SHARED_MEMORY_NAME = 'MySharedMemoryForTest'; // maximum 31 symbol with terminating zero byte

type
  PDwordArray = ^DwordArray;
  DwordArray = array [0..0] of LongWord;

var
  WndLeft, WndTop: LongInt;
  WndWidth: LongWord = 300;
  WndHeight: LongWord = 150;
  MySharedMemory: Pointer;

function StrCmp(Str1, Str2: PAnsiChar): LongInt; stdcall;
asm
  PUSH EDI
  PUSH ESI
  MOV ESI, Str1
  MOV EDI, Str2
  XOR EAX, EAX
  MOV ECX, $FFFFFFFF
  REPNE SCASB
  MOV EDI, Str2
  NOT ECX
  REPE CMPSB
  MOV AL, [ESI - 1]
  SUB AL, [EDI - 1]
  POP ESI
  POP EDI
end;

procedure SetBackColor(Color: LongWord);
begin
  // write Color value to shared memory
  PDwordArray(MySharedMemory)[0] := Color;
end;

function GetBackColor: LongWord;
begin
  // read Color value from shared memory
  GetBackColor := PDwordArray(MySharedMemory)[0];
end;

procedure On_Redraw;
begin
  BeginDraw;
  DrawWindow(WndLeft, WndTop, WndWidth, WndHeight, 'Shared Memory', GetBackColor,
    WS_SKINNED_FIXED + WS_CLIENT_COORDS + WS_CAPTION, CAPTION_MOVABLE);
  DrawText(10, 10, 'Press button to select window color', $000000FF, $00FFFFFF, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  DrawButton(10, 30, 50, 30, COLOR_RED, 0, RED_BUTTON);
  DrawButton(70, 30, 50, 30, COLOR_GREEN, 0, GREEN_BUTTON);
  DrawButton(10, 70, 50, 30, COLOR_BLUE, 0, BLUE_BUTTON);
  DrawButton(70, 70, 50, 30, COLOR_BLACK, 0, BLACK_BUTTON);
  EndDraw;
end;

procedure RedrawAllMyWindows;
var
  MyThreadInfo, ThreadInfo: TThreadInfo;
  MaxSlot: LongWord;
  Slot: LongWord;
begin
// get maximum slot number of threads and get ThreadInfo about _Current_ Thread
  MaxSlot := GetThreadInfo($FFFFFFFF, MyThreadInfo);
// we go through all the thread slots and if Name = NameOfCurrentThread then Activate
  for Slot := 1 to MaxSlot do
  begin
    GetThreadInfo(Slot, ThreadInfo);
    if StrCmp(MyThreadInfo.Name, ThreadInfo.Name) = 0 then
      ActivateWindow(Slot);
  end;
// activate window of current thread  
  ActivateWindow(GetSlotByID(MyThreadInfo.Identifier));
  On_Redraw;
end;

begin
  HeapInit;
  // try to create new shared memory, if it fails then
  //   just open existing memory
  // else
  //   set back color by default
  // NOTE: you can also use the SHM_OPEN_ALWAYS flag, which means "open existing or create new if not exist"
  MySharedMemory := OpenSharedMemory(MY_SHARED_MEMORY_NAME, MY_SHARED_MEMORY_SIZE, SHM_CREATE + SHM_WRITE);
  if MySharedMemory = nil then
    MySharedMemory := OpenSharedMemory(MY_SHARED_MEMORY_NAME, MY_SHARED_MEMORY_SIZE, SHM_OPEN + SHM_WRITE)
  else
    SetBackColor(COLOR_DEFAULT);

  with GetScreenSize do
  begin
    WndLeft := (Width - WndWidth) div 2;
    WndTop := (Height - WndHeight) div 2;
  end;

  while True do
    case WaitEvent of
      REDRAW_EVENT:
        On_Redraw;
      KEY_EVENT:
        GetKey;
      BUTTON_EVENT:
        begin
          case GetButton.ID of
            BLUE_BUTTON:
              SetBackColor(COLOR_BLUE);
            GREEN_BUTTON:
              SetBackColor(COLOR_GREEN);
            RED_BUTTON:
              SetBackColor(COLOR_RED);
            BLACK_BUTTON:
              SetBackColor(COLOR_BLACK);
            else
              Break;
          end;
          RedrawAllMyWindows;
        end;
    end;
end.

Примеров использования можно придумать гораздо больше, чем просто чтение\запись цвета окна — это могут быть любые данные.
Если это интересно, то пишите в комментариях — добавлю другие примеры  по Shared Memory.

Вообще-то если бы стояла задача — окрашивать окна в цвет под указателем мыши, то решить её можно было проще:
  • отслеживаем события мыши

  • при нажатии получаем значение пикселя под указателем с текущими координатами

  • делаем перерисовку окна

так как события мыши могут приходить всем окнам, а не только активному, то перерисовываться будут все окна.

Post's attachments

Иконка вложений shared.gif 344.7 Кб, 63 скачиваний с 2020-12-04 

Иконка вложений SharedMem.kex 945 b, 112 скачиваний с 2020-12-04