Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2003.11.27;
Скачать: CL | DM;

Вниз

Типа передача данных первой копии программы от второй   Найти похожие ветки 

 
NailMan   (2003-10-01 12:19) [0]

Необходимо передать данные(до нескольких килобайт) первому экземпляру программы от других вторичных экземпляров.

Сам алгоритм передачи сделал по принципу клиент-сервер, а именно:
Первая копия при запуске входит в режим сервера и собсно работает с юзером(это видеоплеер). Последующие копии, до создания всех форм определяют наличие сервера и входят в режим клиента, тоесть начинают процедуру передачи параметров(предварительно зарегистрировав в системе соответсвующие сообщения).

Реализовал это так:

Процедура (у клиента) передачи пайпа серверу
Procedure ProcessTransmitParameters;
var Msg : TMsg;
s:shortstring;
i:integer;
begin
nuldd:=0;
//Циклически посылаем сообщение о желании передать пайп до тех
//пор пока сервер не ответит о готовности принять пайп от клиента
//сервер посылает о занятости если уже работает с другим клиентом
repeat
Postmessage(FirstInstanceHandle, PIPE_GETHANDLE, Application.Handle, 0);
waitmessage;
PeekMessage(msg,Application.handle,0,0,PM_REMOVE);
until msg.message=PIPE_READY;

//пишем данные в пайп
//пишем в пайп число параметров коммандной строки
<Запись cParamcount>
For i:=1 to cParamcount do
begin
s:=paramstr(i);
//Запись в пайп самого параметра коммандной строки из shortstring
<Запись из буфера s, 256 байт>
end;
//Посылаем хэндл пайпа серверу
Postmessage(FirstInstanceHandle,PIPE_TAKEHANDLE,PipeRHandle,0);

//Ожидаем получения подтверждения от сервера, о том что данные пайпа обработаны.
repeat
waitmessage;
GetMessage(msg,Application.handle,0,0);
until msg.message=PIPE_HANDLED;
//Со спокойной душой идем на завершение работы программы
end;


Далее как сделано на сервере:

Сделал левый объект:
TYPE
TWindowHandler = CLASS
procedure OnMessage(var Msg: TMsg; var Handled: Boolean);
END;


И в начале программы, до создания форм метод Application.OnMessage заменяю на TWindowHandler.OnMessage, тоесть
Application.OnMessage:=WindowHandler.OnMessage;
(в принципе клиент это тоже делает, но после этого он проваливается в режим клиента)

Далее сервер грузится нормальным образом(создает формы и проваливается в Applicarion.run)

Вот что содержит обработчик(TWindowHandler.OnMessage) на приеме уже самого хэндла пайпа(остальное не интересует нас):


If (msg.message=PIPE_TAKEHANDLE) then
begin
RemotePipeHandle:=msg.wParam;
<Читаем число строк данных из пайпа>
For i:=1 to c do
begin
<Читаем сами строки>
PlayList.AddFile(s);
end;
//репортуем что данные обработали
postmessage(RemoteInstanceHandle,PIPE_HANDLED,0,0);
Pipe_Blocked:=False; //разблокируем себя для приема запросов от других клиентов
end;


Собсно система работает гладко(сам алгоритм передачи), но не получается перенести сами данные.

Пробовал использовать CreateFileMapping для создания временного файла, который создавался на каждой копии программы, но как видно из вышеприведенного кода заполнялся данными на клиенте. Хэндл передается серверу существующим(<>0), но при попытке сервером считать в обработчике из него данные при помощи FileRead(WinAPIшного ессно), то даные оттуда не вынимаются, тоесть число считаных байт возвращается нулевым и в переменной "C" тоже ничего нет нужного. В цикл считывания самих строк он ессно не входит.

Пробовал делать просто пайп CreatePipe, но при попытке чтения сервером данных, на клиенте вылетал AV(клиент был запущен отдельно из проводника, а сервер я пошагово отрабатывал в дельфе).

Вобщем каким способом наиболее безопасно передать эти данные, так как заранее не извесно в какой ОС будет работать программа(NT, 9x)? Чую задницей, какой то подвох со стророны прав доступа сервера к этим данным клиента. Нужен универсальный способ, чтоб работал и в w2k/xp и в 9x/me без гемора.

Создавать на винте не предлагать - не по взрослому имхо. Да и в один момент на сервер могут ломиться сразу несколько копий программы - юзер выделил в проводнике несколько файлов AVI и запустил открытие моей прогой(расширение уже зарезервировано в системе на мою прогу) и все они хотят отдать уже запущенному серверу(или первой запущенной программе из этого списка, которая стала сервером первой). Вот и логично все это дело передавать через память, но что-то путевого на это счет придумать не могу.

Вобщем нид хелп.


 
NailMan   (2003-10-01 12:21) [1]

Да забыл сказать!

Создание временного файла посредством CreateFileMapping брал из местного FAQ из статьи про определение загруженной копии приложения.


 
clickmaker   (2003-10-01 12:22) [2]

Попробуй WM_COPYDATA поюзать


 
NailMan   (2003-10-01 12:31) [3]

Я так понял с помощью WM_COPYDATA можно передать до 32бит данных - маловато блин, это ж надо послать 56 сообщений, для передачи строки в 256 байт(+4 - это число строк = 1). Тормозно походу будет.


 
clickmaker   (2003-10-01 12:56) [4]

Хелп читаешь невнимательно

dwData
Specifies up to 32 bits of data to be passed to the receiving application.

cbData
Specifies the size, in bytes, of the data pointed to by the lpData member.

lpData
Pointer to data to be passed to the receiving application. This member can be NULL.


В lpData можешь передать сколько душе угодно, только в cbData размер буфера укажи


 
NailMan   (2003-10-01 13:20) [5]

Нда, в хелпе(winapi), который идет к Delphi 6, написано так
lpData
Points to data to be passed to the receiving application. This member can be NULL.

Ну я понял так, что dwData - это если надо передать меньше или (=) 4б, а если надо больше, то в lpData передаем указатель на буфер, в котором лежит наше яблочко, но...
Типа я не догоняю, я выделяю память у клиента и передаю указатель серверу или данные копируются куда-то. Сможет ли сервер считать данные у клиента без AV? Данные с точки зрения сервера лежат в чужой области данных и типа мне чё-то непонятно как он их сможет проюзать? Или я что-то недопонимаю в этом деле?


 
Verg   (2003-10-01 13:20) [6]


> Хэндл передается серверу существующим(<>0), но при попытке
> сервером считать в обработчике из него данные при помощи
> FileRead(WinAPIшного ессно)


По-моему в этом борще не хватает duplicatehandle


 
clickmaker   (2003-10-01 13:36) [7]

> NailMan © (01.10.03 13:20) [5]

Главное отправить его SendMessage, а не PostMessage. И буфер нельзя убивать, пока принимающая сторона его не всосет. А будет это GlobalAlloc или статический буфер - не суть важно


 
MBo   (2003-10-01 13:57) [8]

> Сможет ли сервер считать данные у клиента без AV
При посылке WM_COPYDATA - да, сможет. Это специальное сообщение, данные скрыто маппируются в оба процесса.


 
NailMan   (2003-10-02 09:35) [9]

Вобщем весь вечер пробовал сделать и так и сяк - результат тот же.

сообщение WM_COPYDATA сервер вообще не получает, хотя любое другое на ура. С клиента посылаю SendMessage(ServHandle,WM_COPYDATA,Application.Handle,DWORD(@CD));
Где CD : COPYDATASTRUCT;
В нее соответственно занес:
CD dwData = 0;
cbData = 65536;
lpData = указатель на выделенный буфер с данными.

Сам буфер убивается после получения клиентом сообщение PIPE_HANDLED(данные обрабротаны).

Вобщем ну никак не хочет.

Мож все таки как-то через CreateFileMapping? Но если я делаю как в примере из FAQ ( http://www.delphimaster.ru/cgi-bin/faq.pl?look=1&id=988619824&n=15), то запись/чтение в созданный хэндл посредством FileWrite/FileRead приводят к ошибке(GetLastError) = 6 что есть Invalid handle.

Что тут еще можно попробовать?

Или хотя бы рабочим(правильным) примерчиком юзания WM_COPYDATA киньте в меня.


 
Verg   (2003-10-02 10:32) [10]


> то запись/чтение в созданный хэндл посредством FileWrite/FileRead
> приводят к ошибке(GetLastError) = 6 что есть Invalid handle.
> Что тут еще можно попробовать?


Я ж тебе говорю - ты про DuplicateHandle не забыл?

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


 
MBo   (2003-10-02 12:08) [11]

>Сам буфер убивается
Данные в приемнике нужно скопировать в обработчике сообщения, так как после выхода из него адрес будет недействительным


 
NailMan   (2003-10-02 16:11) [12]

Verg ©
Так, пробовал делать duplicatehandle:

вводил следующие параметры:

DuplicateHandle(ClientWNDHandle,
RemotePipeHandle,
Application.Handle,
@PipeHandle,
FILE_MAP_ALL_ACCESS,
False,
0);

И в итоге получался точно такой же невалидный хэндл.

Правильно ли я задал параметры?

Далее я читал Windows.ReadFile-ом из полученного PipeHandle(ну и писал на клиенте тоже windows.writefile).


 
Verg   (2003-10-02 16:32) [13]

Нет, ну вот ты смотри:

Клиентский процесс C создает некий handle (файла, pipe-а, filemappinga или еще чего). Этот handle легален ТОЛЬКО в котексте процесса C. Для процесса S - он будет просто чиселком, таким же как в анекдоте про "петька, прибор! - семь! -чего семь?".
Для того, чтобы в процессе S появилось понятие ссылки на тот же объект ядра, на который ссылается запись под номером handle в процессе C, процесс C должен вызвать duplicatehandle, указав в качестве источника свой process-handle, process-S и получить новое чиселко, которое будет в процессе S иметь смысл описателя того же объекта ядра.
И вот это уже новое число мы и должны передать процессу S.

Насчет параметров этой процедуры почитай WIN32 Prog. Ref.

Кстати, существует же по крайней мере три способа использования несколькими процессами описателя одного объекта ядра^
1. Именование (именованные объекты)
2. Наследование (Это как при CreateProcess)
3. DuplicateHandle - это о чем я сейчас говорил.

Читай http://rosigma.chat.ru/richter/head3.htm#head3top3


 
MBo   (2003-10-02 16:39) [14]

С безымянным пайпом это можно делать, но довольно муторно.
В subj указана XP - так может, именованные пайпы использовать?
А вообще пайпы неявно MMF используют - так самому через MMF передавать еще проще будет.


 
NailMan   (2003-10-02 16:41) [15]

Ахга, скачал статью, спасибо буду сегодня читать.


 
NailMan   (2003-10-03 09:42) [16]

Хм, вобщем пробовал я сделать так как в той статье - хрен вам.

- создавал объект файл-маппинга,
- дупликейтил его DuplicateHandle и в результате был нулевой хэндл.

Функция GetCurrentProcess возвращает мне значение хэндла процесса INVALID_HANDLE_VALUE. Это для каждого из 2 учавствующих процессов. Видимо из-за этого дублицирование не канает. В статье примеры на C++.

Как собсно его корректно этот хэндл получить под Дельфи?

Пробовал также делать через GlobalAlloc/GlobalLock/GlobalUnLock/GlobalFree.

В клиенте создаю, лочу/пишу/анлочу передаю хэндл серверу, а он лочит/читает/анлочит. Но собсно данные в буфере уже какие-то левые.

Ну какие мылси будут? Есть где-ньть дельфийские примеры по этому вопросу?


 
Polevi   (2003-10-03 09:59) [17]

не надо хендлы передавать, передавай данные
сделай на сокетах в конце концов,


 
FireHack   (2003-10-03 10:20) [18]

Почитай внимательно
http://www.delphikingdom.com/helloworld/filemapping.htm
и
http://www.delphikingdom.com/treasury/sharedstream01.htm

У меня все работало как часы :)


 
Verg   (2003-10-03 11:18) [19]

Ну хорошо, вот накидал тут примерчик на скору руку, если я правильно понял твою задачу. По-простому однопоточный сервер. Первая запущенная копия - это сервер, все остальные - клиенты. Клиенты через filemapping передают серверу свой консольный ввод:

//Однопоточный сервер
program Cons;
{$APPTYPE CONSOLE}
uses Windows,Sysutils,MEssages;
const TryMsg = WM_USER+1;
WAIT_MESSAGE =WAIT_TIMEOUT+1;

var Msg : TMSG;
ExitEvent,
InputHandle,
MapFile,
MyProcess,
Sprocess,
SMap : THandle;
Ir : TInputRecord;
Readed,
SThread : DWORD;
PidFName,S : string;
Mp : pointer;
F : text;
Server : boolean;
WaitHandles : Array of THandle;

function Handler(dwCtrlType : DWORD):BOOL; stdcall;
begin
// Ловушка всех управляющих команд
Result:=TRUE;
SetEvent(ExitEvent);
end;

function CheckObject(Ev : THandle) : boolean;
begin
Result:=WaitForSingleObject(Ev, 0)=WAIT_OBJECT_0;
end;

function WaitForObjects(Objects:array of THandle; wTimeOut : DWORD; wMsg:boolean):DWORD;
begin
if wMsg then
begin
Result:=MsgWaitForMultipleObjects(High(Objects)+1, Objects, false, wTimeOut, QS_ALLINPUT);
if Result=High(Objects)+1 then Result:=WAIT_MESSAGE;
end else Result:= WaitForMultipleObjects(High(Objects)+1, @Objects ,false, wTimeOut);
end;

procedure SendToServer(const S : string);
begin
Move(pchar(S)^, pchar(Mp)^, length(S)+1);
postthreadmessage(sThread, tryMSG,0, GetCurrentThreadId);
end;

procedure ProcessConsoleInput;
begin
if CheckObject(InputHandle) then
begin
readconsoleinput(InputHandle, Ir,1, Readed);
if Server then exit;
case Ir.EventType of
KEY_EVENT: begin
if Ir.Event.KeyEvent.bKeyDown then
if Ir.Event.KeyEvent.AsciiChar<>#0 then SendToServer(Ir.Event.KeyEvent.AsciiChar);
end;
_MOUSE_EVENT: begin
SendToServer(intToStr(ir.Event.MouseEvent.dwMousePosition.x)+":"+IntToStr(ir.Event.MouseEvent.dwMousePosition.y));
end;
end;
end;
end;

begin
PidFName:=ExtractFilePath(ParamStr(0))+"Cons.pid";
if FileExists(PidFName) then // ждем пока сервер закончит формирование pid файла,
// FileMapping-a и очереди
while not RenameFile(PidFName, PidFName) do sleep(100);
Assign(F, PidFName);
Exitevent := CreateEvent(nil, true, false, nil);
InputHandle:=GetStdHandle(STD_INPUT_HANDLE);
if InputHandle = INVALID_HANDLE_VALUE then exit;
SetConsoleMode(InputHandle, ENABLE_MOUSE_INPUT+ENABLE_PROCESSED_INPUT);
//Сейчас получим "настоящий" handle своего же процесса
MyProcess:=OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessid);
Server:=false;
try
Reset(F);
Readln(F, SProcess, // pid сервера
SThread, // id серверного потока
SMap); // FileMapping объект для обмена с сервером
SProcess:=OpenProcess(PROCESS_ALL_ACCESS, false, SProcess); // получим handle процесса сервера
if SProcess=0 then
begin
DeleteFile(PidFName);
Exit;
end;
// получим handle FileMapping-объекта, созданного процессом сервера
if not DuplicateHandle(SProcess, SMap, MyProcess, @MapFile, 0, false, DUPLICATE_SAME_ACCESS) then
begin
// Упс! че-то не срослось
// Скорее всего Pid файл существует, а сервер не запущен (остановлен таск-манагером)
Writeln(GetLastError);
Readln;
Exit; //Короче, злые вы все! Уйду я от вас... :((
end;
postthreadmessage(SThread,tryMSG,0, GetCurrentThreadId);
except
// Ага. Кто первый встал - тот и сервер!
Rewrite(F);
MapFile:=CreateFileMapping($FFFFFFFF, // вот здесь по-началу дикое желание поставить
// INVALID_HANDLE_VALUE, однако, перечитав win32 Prog. Ref.
// делаем так, как там написано
nil,
PAGE_READWRITE or SEC_COMMIT,
0,
256, // Думаю, что хватит...
nil);
Writeln(F,GetCurrentProcessid," ", GetCurrentThreadId," ", MapFile);
PeekMessage(Msg, 0,0,0, PM_NOREMOVE); // Образуем себе очередь
Server:=true;
end;
Close(f);
Try
Mp:=MapViewOfFile(MapFile,FILE_MAP_ALL_ACCESS,0,0,0);
SetLength(WaitHandles, 2);
WaitHandles[0]:=ExitEvent;
WaitHandles[1]:=InputHandle;
if not Server then
begin
SetLength(WaitHandles, 3);
WaitHandles[2]:=SProcess;
end;
if SetConsoleCtrlHandler(@Handler, true) then
while true do
// Подождем мою маму? Подождем твою мать...
case WaitForObjects(WaitHandles, INFINITE, true) of
WAIT_MESSAGE :begin
while PeekMessage(Msg, 0,0,0, PM_REMOVE) do
begin
case MSG.message of
tryMSG:begin
if Server then write(pchar(Mp)," ");
end;
end;
end;
end;
WAIT_OBJECT_0: break; // Так и хочется сказать "Отстаньте все от меня!!!", А НЕКОМУ...
WAIT_OBJECT_0+1: begin
// Покажем, что мы еще чем-то можем заняться
ProcessConsoleInput;
end;
WAIT_OBJECT_0+2: begin
exit; // Сервер помер, надо сваливать
end;
else exit;
end;
finally
if Server then DeleteFile(PidFName);
// как законопослушные граждане освобождаем ресурсы
// (хотя и так всё отнимут)
if Mp<>nil then UnmapViewOfFile(Mp);
CloseHandle(MapFile);
CloseHandle(MyProcess);
CloseHandle(SProcess);
CloseHandle(ExitEvent);
end;
end.


 
NailMan   (2003-10-03 13:11) [20]

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


 
Verg   (2003-10-03 13:21) [21]

Обрати внимание, что эту задачу (в примере) можно было бы сделать значительно проще - достаточно было FileMapping сделать именованым. Но раз уж речь пошла о

> Функция GetCurrentProcess возвращает мне значение хэндла
> процесса INVALID_HANDLE_VALUE. Это для каждого из 2 учавствующих
> процессов. Видимо из-за этого дублицирование не канает.
> В статье примеры на C++.
>
> Как собсно его корректно этот хэндл получить под Дельфи?

..., то я и показал как применяется duplicatehandle и что в его параметрах надо подставлять.



Страницы: 1 вся ветка

Текущий архив: 2003.11.27;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.008 c
1-89927
UNick
2003-11-18 11:26
2003.11.27
Как упорядочить закладки в TabControl e


1-89943
SkyRanger
2003-11-18 02:54
2003.11.27
Delphi IDE OpenAPI


1-89850
azazello
2003-11-16 10:09
2003.11.27
Вопрос про версию программы и дату создания билда


1-89839
TIER
2003-11-16 22:54
2003.11.27
<I>Как сделать чтобы работало несколько циклов одновременно?</I>


1-89962
romeo
2003-11-13 22:42
2003.11.27
Не хватает памяти для печати (или у меня мозгов? :-)





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский