Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Последовательный доступ к MMF [D7, XP]


Страницы:   1 2

Muxa/Ibl4 ©   (2009-07-14 22:12:00) 

Уважаемые коллеги, прошу помочь в реализации.

Что_имеем:2 программы(или более) на одном компе, каждая имеет некую DLL(по сути это одна библиотечка просто для портативности для каждой проги нужна копия DLL).

Что_нуна:Реализовать в DLL механизм чтения/записи данных в MMF (можно последовательный - но не обязательно).

Что_получилось:получилось реализовать 2 приложения работающие с DLL, и саму DLL. Но проблема в том что если 1приложение работает со совей копией DLL то 2 уже не может работать со своей.

Как быть? Если кто то швырнет в меня примером библиотечки (желательно с возможность последовательного доступа) - то я буду очень счастливым человеком.


Юрий Зотов   (2009-07-22 02:49:00) [1]

Небольшой комментарий. По сути, процедура WriteData не нужна. После загрузки DLL тестовая программа может вызвать функцию ReadData, от нее получить указатель на общую область памяти и запомнить этот указатель - таким образом, она получает прямой доступ к этой общей области и может писать в нее (и читать из нее) что угодно, без всяких посредников.

В таком варианте функцию ReadData правильнее было бы обозвать, например, GetDataAddress, а в тестовой программе вместо переменной Data:TData ввести переменную DataAddress:PData.


Германн   (2009-07-22 02:48:00) [2]


> Юрий Зотов ©   (22.07.09 02:06) [25]
>
> > Muxa/Ibl4   (21.07.09 22:33) [17]
>
> Лови, я сегодня добрый. И давно на Delphi не писал

Я точно не буду таким добрым. :)


Юрий Зотов   (2009-07-22 02:06:00) [3]

> Muxa/Ibl4   (21.07.09 22:33) [17]

Лови, я сегодня добрый. И давно на Delphi не писал, соскучился даже - вот и захотелось размяться слегка. Писал в Delphi 7.

Только не копируй бездумно - разберись в каждой строчке и в каждой букве - что она делает, зачем она это делает,  как она это делает и почему она это делает именно тут, а не в другом месте. Справка и MSDN тебе в помощь, там все описано. А что останется неясным - спрашивай здесь.

1. Полный код общего юнита (используется и в DLL, и в тестовой программе)

unit DataType;

interface

type
 TData = packed record
   Int: integer;
   Str: ShortString
 end;

 PData = ^TData;

 TWriteData = procedure(Source: PData); stdcall;
 TReadData = function: PData; stdcall;

const
 DLL_NAME = 'MMF_DLL';
 ReadDataProcName = 'ReadData';
 WriteDataProcName = 'WriteData';

implementation

end.


2. Полный код DLL

library MMF_DLL;

uses
 Windows,
 SysUtils,
 DataType in 'DataType.pas';

const
 MMF_NAME = 'MySharedData';

var
 HData: THandle = 0;
 DataPtr: PData = nil;

procedure EntryProc;
begin
 HData := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TData), MMF_NAME);
 Win32Check(HData <> 0);
 DataPtr := MapViewOfFile(HData, FILE_MAP_ALL_ACCESS, 0, 0, 0);
 Win32Check(DataPtr <> nil)
end;

procedure ExitProc;
begin
 UnmapViewOfFile(DataPtr);
 CloseHandle(HData)
end;

procedure MyDllProc(Reason: integer);
begin
 case Reason of
   DLL_PROCESS_ATTACH: EntryProc;
   DLL_PROCESS_DETACH: ExitProc
 end
end;

procedure WriteData(Source: PData); stdcall;
begin
 DataPtr^ := Source^
end;

function ReadData: PData; stdcall;
begin
 Result := DataPtr
end;

exports
 ReadData name ReadDataProcName,
 WriteData name WriteDataProcName;

begin
 if @DllProc = nil then
   DllProc := MyDllProc;
 MyDllProc(DLL_PROCESS_ATTACH)
end.


3. Код главной формы тестовой программы (на форму бросить 2 кнопки и назначить им OnClick)

uses
 DataType;

var
 Data: TData;
 HLib: THandle = 0;
 ReadData: TReadData = nil;
 WriteData: TWriteData = nil;

procedure TForm1.FormCreate(Sender: TObject);
begin
 HLib := LoadLibrary(DLL_NAME);
 Win32Check(HLib <> 0);
 @ReadData := GetProcAddress(HLib, ReadDataProcName);
 Win32Check(@ReadData <> nil);
 @WriteData := GetProcAddress(HLib, WriteDataProcName);
 Win32Check(@WriteData <> nil);
 Randomize
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 FreeLibrary(HLib)
end;

procedure TForm1.ReadButtonClick(Sender: TObject);
begin
 Data := ReadData^;
 Caption := Format('Прочитано: < %d %s >', [Data.Int, Data.Str])
end;

procedure TForm1.WriteButtonClick(Sender: TObject);
begin
 Data.Int := Random(10);
 if Data.Int > 4 then
   Data.Str := 'Привет Богам!'
 else
   Data.Str := 'А DLL-то одна!';
 WriteData(@Data);
 Caption := Format('Записано: < %d %s >', [Data.Int, Data.Str])
end;


4. Что с этим делать.

Вначале все компилируем в один каталог (любой). Потом запускаем 2 копии тестовой программы и в обоих жмем кнопки Write и Read. Убеждаемся, что каждая программа читает из MMF точно то, что перед этим в MMF записала либо она сама же, либо другая программа.

PS
И богам своим покажи. Решение они приняли, панимашь... обхохотаться можно над такими решениями.


Игорь Шевченко   (2009-07-22 02:05:00) [4]

antonn ©   (22.07.09 01:22) [23]

До Delphi 64 еще дожить надо. Хотя специально посмотрел в своих промышленных проектах - таки INVALID_HANDLE_VALUE использую.

Значит, ты прав :)


antonn   (2009-07-22 01:22:00) [5]

потому что на 64-битной системе будет не -1 :)


Игорь Шевченко   (2009-07-22 01:11:00) [6]

antonn ©   (22.07.09 00:29) [21]


> вместо $FFFFFFFF правильнее использовать INVALID_HANDLE_VALUE


const
 INVALID_HANDLE_VALUE = DWORD(-1);

почему правильнее ?


antonn   (2009-07-22 00:29:00) [7]

вместо $FFFFFFFF правильнее использовать INVALID_HANDLE_VALUE


Сергей М.   (2009-07-21 23:04:00) [8]

Михалыч, ты на вопрос в [18] соизволишь ответить ?)


Muxa/Ibl4   (2009-07-21 22:52:00) [9]

я уже согласен на такую реализацию, там конечно есть трудности но на это я забил.


Сергей М.   (2009-07-21 22:39:00) [10]


> 2 уже не может работать со своей


Что, вот прямо так и кричит "не могу" ?


Muxa/Ibl4   (2009-07-21 22:33:00) [11]

Все сдаюсь, прошу если есть возможность дать пример _dllки_ где есть метод чтения и метод записи MMF


Юрий Зотов   (2009-07-15 09:36:00) [12]

> Muxa/Ibl4   (14.07.09 22:36) [4]

Богам, возможно, виднее, но даже и простым смертным известно, что каждая запущенная в Windows программа грузит библиотеку Kernel32.dll. Запустили 100 программ - библиотека 100 раз загружена.

Одна и та же. И никаких копий. И все замечательно работает. И так со всеми DLL. И непонятно, на кой ляд богам понадобилось принимать такие (skipped) решения.

PS
По коду даже не говорю ничего. Потому что слишком много говорить придется. Документацию на используемые функции надо читать внимательнее.


Loginov Dmitry   (2009-07-15 00:36:00) [13]

> блин, вроде и понятно, но что то я запутался


То, что в [5] долго еще будешь распутывать. А ведь
обмен между процессамы задумывается, верно?
Я лишь предложу перечень замечаний по [5], но, думаю, они
помогут разобраться в ошибках и уйти от них. Нет так нет.

1. По допустимым типам данных. Переменная rw : tinfo это по сути
указатель на разделяемую между приложениями область памяти. И что
мы хотим хранить в этой области памяти? Переменные ListUser : aUser и
ListCommand: aCommand. НО! Обе переменные - это массивы. Динамические.
(Их размер можно менять). По сути обе переменные - это указатели на области
памяти, в которых хранятся массивы. НО! Области памяти, в которых хранятся
массивы мы не расшариваем! Таким образом если какой-то обмен и будет - то
только ссылками на неизвестно что, взятое из чужого адресного пространства.
Выход: использовать статические массивы и короткие строки.

2. По использованию функции CreateFileMapping(). Что мы получим при таком вызове:
SendMMF := CreateFileMapping( $FFFFFFFF, Nil, PAGE_READWRITE, 0, SizeOf(DWORD), NameAreaForWwork);
будет создан соответствующий объект Windows, про который известно, что он не будет хранится в каком-либо
файле на диске, и его размер = 4 байта. В итоге получим хэндл этого объекта. И что дальше? А дальше идет
rw := MapViewOfFile(SendMMF, FILE_MAP_WRITE, 0, 0, 0);
что в данном случае приведет либо к выделению области памяти в 4 байта для записи, причем эта память будет
доступна для любого приложения, либо же функция вернет указатель на область памяти, которая ранее уже была
выделена для другого приложения. Причем функция может вернуть и вовсе NIL, если ей неправильно воспользовались.
(это - пояснение, а не замечание).

3. По реализации readMMF. Здесь все просто. Можно использовать одну лишь функцию CreateFileMapping(). Функцию
OpenFileMapping() используют немного в других ситуациях: когда требуется получить хэндл файлового отображения,
которое ранее уже было создано (а если оно не создано, то выполняется соответствующая обработка, а в каких-то
ситуациях может и CreateFileMapping). Если же просто нужен хэндл на файловое отображение не зависимо от того,
было ли одно до этого, или нет, то достаточно функции CreateFileMapping(). Она либо создаст объект, либо вернет
хэндл для существующего объекта.

4. По реализации addUser. Нельзя возвращать результат как PChar, предварительно не выделив под него память. В
данном случае ошибки может не возникнуть, поскольку есть явное присвоение
Result := 'тут текстовый результ'. Строка является константной, она всегда находится в памяти в
одном и том же месте. А вот если вместо константной строки передать динамическую строку, то скорее всего будет
капут, т.к. память, выделенная под динамическую строку, освободится сразу же, как только завершится функция, и
в итоге функция вернет указатель на ничто. Вместо PChar нужно использовать string, тем более что подключен модуль
ShareMem.

5. Совсем мелкие замечания:
   - имена типов в Delphi обычно начинаются с буквы T (например, TInfo)
   - указатели на типизированные структуры начинаются с буквы P (например PInfo)
   - stdcall определяет порядок вызова фукнции, при этом для передачи параметров используется стэк вместо
     высокоскоростных регистров процессора. В данном случае оно ни в EXE ни в DLL не нужно.
   - функцию writeMMF можно сделать процедурой. Возвращаемый ею результат все-равно бесполезен.

Итого - весь код с нуля нужно переделывать, учесть все замечания всех ответивших в этой ветке.


Muxa/Ibl4   (2009-07-14 23:22:00) [14]

блин, вроде и понятно, но что то я запутался(


Игорь Шевченко   (2009-07-14 23:11:00) [15]

Muxa/Ibl4   (14.07.09 23:08) [12]

А какая разница - у объекта FileMapping есть имя - пусть каждая DLL открывает его, кто не открыл по OpenFileMapping, пусть создает по CreateFileMapping.

Я ссылку на Demos не просто так дал, а со смыслом.


Muxa/Ibl4   (2009-07-14 23:08:00) [16]

но еще один момент, как я и сказал для каждой программы должна быть своя "личная" копия dll, как быть в этом случае?


Игорь Шевченко   (2009-07-14 23:04:00) [17]

Muxa/Ibl4   (14.07.09 23:02) [9]

MMF (и данные в нем) будут жить, пока не закроется последний его Handle


Игорь Шевченко   (2009-07-14 23:04:00) [18]


> при такой реализации (я про DLL) - я же не могу 1 раз создать
> mmf и хранить его Handle т.к. в dll это не получиться и
> мне необходимо каждый раз как то проверять есть ли уже созданный
> файл или нет. Я прав или неверно мыслю?


на время загрузки DLL вполне получится. в инициализации DLL создать MMF, дальше пользоваться, при выгрузке освободить


Muxa/Ibl4   (2009-07-14 23:02:00) [19]

после записи или чтения в mmf я тоже не могу "закрыть" его. Т.к. при следующем вызове функции мне тогда придется создавать его заново, т.е. потеря данных?


Muxa/Ibl4   (2009-07-14 23:00:00) [20]

спасибо за ссылки, но у меня вопросов пару
подскажите
при такой реализации (я про DLL) - я же не могу 1 раз создать mmf и хранить его Handle т.к. в dll это не получиться и мне необходимо каждый раз как то проверять есть ли уже созданный файл или нет. Я прав или неверно мыслю?


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо