Конференция "WinAPI" » Последовательный доступ к MMF [D7, XP]
 
  • Muxa/Ibl4 (14.07.09 22:12) [0]
    Уважаемые коллеги, прошу помочь в реализации.

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

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

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

    Как быть? Если кто то швырнет в меня примером библиотечки (желательно с возможность последовательного доступа) - то я буду очень счастливым человеком.
  • Rouse_ © (14.07.09 22:14) [1]

    > если 1приложение работает со совей копией DLL то 2 уже не
    > может работать со своей.

    SHARE_ права выставилил при открытии?
  • Loginov Dmitry © (14.07.09 22:29) [2]
    Отсутствует наиболее важное:
    Как пытался получить
  • Юрий Зотов © (14.07.09 22:31) [3]
    Не понял насчет копий DLL. Почему каждый EXE не может загрузить одну и ту же DLL и с ней работать?
  • Muxa/Ibl4 (14.07.09 22:36) [4]
    Долго объяснять но так требуют великие боги.
  • Muxa/Ibl4 (14.07.09 22:36) [5]
    Вот подобие моего говнокода

    library DllMmfDate;
    uses
     ShareMem,
     SysUtils,
     windows,
     md5 in 'md5.pas';

    type
       User = record
    sName : string;
    iId :   integer;
       end;
       
       aUser = array of User;

       Command = record
    sTitle : string;
    iId:     integer;
       end;

       aCommand = array of Command;

       info = record
    ListUser : aUser;
           ListCommand: aCommand;
       end;
       

       tinfo = ^info;

    var

     SendMMF: THandle;
     SendData: PChar;

    const
     NameAreaForWwork = 'trulala';

    function writeMMF ( param : info ) : boolean;
    var
     rw : tinfo;
    begin
     if (MapViewOfFile(SendMMF, FILE_MAP_WRITE, 0, 0, 0) = nil ) then Begin
       SendMMF := CreateFileMapping( $FFFFFFFF, Nil, PAGE_READWRITE, 0, SizeOf(DWORD), NameAreaForWwork);
     end;

     rw := MapViewOfFile(SendMMF, FILE_MAP_WRITE, 0, 0, 0);
     rw^ := param;

     Result :=true;
    end;

    function readMMF() : tinfo;
    begin
     if ( MapViewOfFile(SendMMF, FILE_MAP_READ, 0, 0, 0) = nil ) then Begin
         SendMMF := CreateFileMapping( $FFFFFFFF, Nil, PAGE_READWRITE, 0, SizeOf(DWORD), NameAreaForWwork);
     end else begin
         SendMMF := OpenFileMapping(FILE_MAP_READ, False, NameAreaForWwork);
     end;

         Result := MapViewOfFile(SendMMF, FILE_MAP_READ, 0, 0, 0);
    end;

    function addUser( sUserName, iIdUser : PChar ): PChar; stdcall;
    var
     r : info;
     rp : tinfo;

     aUsers : aUser;
    begin
     rp := readMMF();
     r := rp^;

     aUsers := r.aUser;

     setLength(termW, Length(aUsers)+1);
     termW[Length(aUsers)-1].sName := sUserName;
     termW[Length(aUsers)-1].terminalName := iIdUser;

     r.aUser := aUsers;

     writeMMF( r );

     //.........

     Result := 'тут текстовый результ';
    end;

    function getUserAll(): aUser; stdcall;
    var
     r : info;
     rp : tinfo;
    begin
     rp := readMMF();
     r := rp^;
     Result := r.aUser;
    end;

    exports    addUser, getUserAll;
    begin
    end.

  • Игорь Шевченко © (14.07.09 22:42) [6]
    program files\borland\bds\x.0\demos\DelphiWin32\vclwin32\IPCDemos\*.*
  • Loginov Dmitry © (14.07.09 22:44) [7]
    Это именно говнокод. Все верно.

    Здесь
    http://pda.delphimaster.net/?n=6&id=1247596906
    можно в целях изучения MMF посмотреть, что когда-то предлагали Тейксера и Пачека.
  • Muxa/Ibl4 (14.07.09 23:00) [8]
    спасибо за ссылки, но у меня вопросов пару
    подскажите
    при такой реализации (я про DLL) - я же не могу 1 раз создать mmf и хранить его Handle т.к. в dll это не получиться и мне необходимо каждый раз как то проверять есть ли уже созданный файл или нет. Я прав или неверно мыслю?
  • Muxa/Ibl4 (14.07.09 23:02) [9]
    после записи или чтения в mmf я тоже не могу "закрыть" его. Т.к. при следующем вызове функции мне тогда придется создавать его заново, т.е. потеря данных?
  • Игорь Шевченко © (14.07.09 23:04) [10]

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


    на время загрузки DLL вполне получится. в инициализации DLL создать MMF, дальше пользоваться, при выгрузке освободить
  • Игорь Шевченко © (14.07.09 23:04) [11]
    Muxa/Ibl4   (14.07.09 23:02) [9]

    MMF (и данные в нем) будут жить, пока не закроется последний его Handle
  • Muxa/Ibl4 (14.07.09 23:08) [12]
    но еще один момент, как я и сказал для каждой программы должна быть своя "личная" копия dll, как быть в этом случае?
  • Игорь Шевченко © (14.07.09 23:11) [13]
    Muxa/Ibl4   (14.07.09 23:08) [12]

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

    Я ссылку на Demos не просто так дал, а со смыслом.
  • Muxa/Ibl4 (14.07.09 23:22) [14]
    блин, вроде и понятно, но что то я запутался(
  • Loginov Dmitry © (15.07.09 00:36) [15]
    > блин, вроде и понятно, но что то я запутался


    То, что в [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 можно сделать процедурой. Возвращаемый ею результат все-равно бесполезен.

    Итого - весь код с нуля нужно переделывать, учесть все замечания всех ответивших в этой ветке.
  • Юрий Зотов © (15.07.09 09:36) [16]
    > Muxa/Ibl4   (14.07.09 22:36) [4]

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

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

    PS
    По коду даже не говорю ничего. Потому что слишком много говорить придется. Документацию на используемые функции надо читать внимательнее.
  • Muxa/Ibl4 (21.07.09 22:33) [17]
    Все сдаюсь, прошу если есть возможность дать пример _dllки_ где есть метод чтения и метод записи MMF
  • Сергей М. © (21.07.09 22:39) [18]

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


    Что, вот прямо так и кричит "не могу" ?
  • Muxa/Ibl4 (21.07.09 22:52) [19]
    я уже согласен на такую реализацию, там конечно есть трудности но на это я забил.
  • Сергей М. © (21.07.09 23:04) [20]
    Михалыч, ты на вопрос в [18] соизволишь ответить ?)
  • antonn © (22.07.09 00:29) [21]
    вместо $FFFFFFFF правильнее использовать INVALID_HANDLE_VALUE
  • Игорь Шевченко © (22.07.09 01:11) [22]
    antonn ©   (22.07.09 00:29) [21]


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


    const
     INVALID_HANDLE_VALUE = DWORD(-1);

    почему правильнее ?
  • antonn © (22.07.09 01:22) [23]
    потому что на 64-битной системе будет не -1 :)
  • Игорь Шевченко © (22.07.09 02:05) [24]
    antonn ©   (22.07.09 01:22) [23]

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

    Значит, ты прав :)
  • Юрий Зотов © (22.07.09 02:06) [25]
    > 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
    И богам своим покажи. Решение они приняли, панимашь... обхохотаться можно над такими решениями.
  • Германн © (22.07.09 02:48) [26]

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

    Я точно не буду таким добрым. :)
  • Юрий Зотов © (22.07.09 02:49) [27]
    Небольшой комментарий. По сути, процедура WriteData не нужна. После загрузки DLL тестовая программа может вызвать функцию ReadData, от нее получить указатель на общую область памяти и запомнить этот указатель - таким образом, она получает прямой доступ к этой общей области и может писать в нее (и читать из нее) что угодно, без всяких посредников.

    В таком варианте функцию ReadData правильнее было бы обозвать, например, GetDataAddress, а в тестовой программе вместо переменной Data:TData ввести переменную DataAddress:PData.
 
Конференция "WinAPI" » Последовательный доступ к MMF [D7, XP]
Есть новые Нет новых   [134434   +26][b:0][p:0.003]