-
Хочу отправить командную строку программы в виде массива в другой экземпляр своего же приложения(т.е сделал запрет на запуск более одной копии):
type
PCmdArgs = ^TCmdArgs;
TCmdArgs = array [0..$FF] of PWideChar;
procedure SendArgs(argc: Integer; argv: PCmdArgs);
var
I, Offset: Integer;
Buf: PWideChar;
cds: TCopyDataStruct;
hMem: THandle;
begin
if (argc <= 0) then
Exit;
Offset := SizeOf(PWideChar) * argc;
cds.cbData := Offset;
for I := 0 to argc - 1 do
Inc(cds.cbData, (lstrlenW(argv[I]) + 1) * SizeOf(WideChar));
hMem := GlobalAlloc(GHND, cds.cbData);
if (hMem <> 0) then
begin
cds.dwData := 12345;
cds.lpData := GlobalLock(hMem);
for I := 0 to argc - 1 do
begin
Buf := PWideChar(Integer(cds.lpData) + Offset);
lstrcpyW(Buf, argv[I]);
Inc(Offset, (lstrlenW(argv[I]) + 1) * SizeOf(WideChar));
PCmdArgs(cds.lpData)^[I] := Buf;
end;
SendMessageW(Handle, WM_COPYDATA, 0, Integer(@cds));
GlobalUnlock(hMem);
GlobalFree(hMem);
end;
end;
Сообщение приходит, но данные нет.... в отладке показывает что массив формируется нормально(при отправке), а получаю мусор. Если отправляю и получаю внутри одной и той же программы, то всё ок. В чём проблема то? %) Delphi7. Win7.
-
> В чём проблема то? %)
возможно, в этом GlobalUnlock(hMem); GlobalFree(hMem);
как принимающая сторона получает данные?
-
> как принимающая сторона получает данные?
procedure OnCopyData(pcds: PCopyDataStruct);
var
Args: PCmdArgs;
begin
if (pcds^.dwData <> 12345) then
Exit;
Args := pcds^.lpData;
...
end;
-
Очевидно, проблемы связаны с ошибками или тонкостями в выделении памяти. Может, не мудритьс GlobalXXX, а просто сформировать одну строку, в которой отдельные аргументы будут разделены нулями, и передать ее тело. Собственно, командная строка в таком виде уже, видимо, и была до разделения на кусочки...
-
> neversleep
Версия Windows какая?
-
-
> Версия Windows какая?
В конце первого поста. > Может, не мудритьс GlobalXXX, а просто сформировать одну > строку, в которой отдельные аргументы будут разделены нулями, > и передать ее тело.
На самом деле с массивом работать мне будет гораздо удобней. > http://msdn.microsoft.com/en-us/library/ms649011%28v=vs. > 85%29.aspx#5
Дело не в этом, но спасибо, не знал про UIPI и про то что он может блокировать сообщения. ---- До конца не понял в чём дело(видимо всё таки в моей криворукости), но как выяснилось данные приходили, но массив содержал не верные указатели, поэтому в отладчике я видел мусор... продолжаю разбираться...
-
Действительно, указатели на стороне приёма оказались не верными, поэтому пришлось делать вот такой костыль: Отправка:
procedure SendArgs(argc: Integer; argv: PCmdArgs);
var
I, Offset: Integer;
Buf: PWideChar;
cds: TCopyDataStruct;
hMem: THandle;
begin
if (argc <= 0) then
Exit;
Offset := SizeOf(Integer) + SizeOf(PWideChar) * argc;
cds.cbData := Offset;
for I := 0 to argc - 1 do
Inc(cds.cbData, (lstrlenW(argv[I]) + 1) * SizeOf(WideChar));
hMem := GlobalAlloc(GHND, cds.cbData);
if (hMem <> 0) then
begin
cds.dwData := 12345;
cds.lpData := GlobalLock(hMem);
PInteger(cds.lpData)^ := argc;
for I := 0 to argc - 1 do
begin
Buf := PWideChar(Integer(cds.lpData) + Offset);
lstrcpyW(Buf, argv[I]);
PCmdArgs(Integer(cds.lpData) + SizeOf(Integer))^[I] := Buf;
Inc(Offset, (lstrlenW(argv[I]) + 1) * SizeOf(WideChar));
end;
SendMessageW(Handle, WM_COPYDATA, 0, Integer(@cds));
GlobalUnlock(hMem);
GlobalFree(hMem);
end;
end;
Приём:
procedure OnCopyData(pcds: PCopyDataStruct);
var
argc, I, Offset: Integer;
argv: PCmdArgs;
begin
if (pcds^.dwData <> 12345) then
Exit;
argc := PInteger(pcds^.lpData)^; argv := PCmdArgs(Integer(pcds^.lpData) + SizeOf(Integer));
Offset := SizeOf(Integer) + SizeOf(PWideChar) * argc;
for I := 0 to argc - 1 do begin
argv^[I] := PWideChar(Integer(pcds^.lpData) + Offset);
Inc(Offset, (lstrlenW(argv[I]) + 1) * SizeOf(WideChar));
end;
end;
Не нравится мне это... Но всем спасибо за помощь и если есть какие-то идеи - велком :)
-
> Хочу отправить командную строку программы в виде массива > в другой экземпляр своего же приложения
Вот можно еще так (код старый, поправить надо под новые Delphi):
function MakeDrop(const FileNames: array of string): THandle;
var
I, Size: Integer;
Data: PDragInfoA;
P: PChar;
begin
Size := SizeOf(TDragInfoA) + 1;
for I := 0 to High(FileNames) do
Inc(Size, Length(FileNames[I]) + 1);
Result := GlobalAlloc(GHND or GMEM_SHARE, Size);
if Result <> 0 then
begin
Data := GlobalLock(Result);
if Data <> nil then
try
Data.uSize := SizeOf(TDragInfoA);
P := PChar(@Data.grfKeyState) + 4;
Data.lpFileList := P;
for I := 0 to High(FileNames) do
begin
Size := Length(FileNames[I]);
Move(Pointer(FileNames[I])^, P^, Size);
Inc(P, Size + 1);
end;
finally
GlobalUnlock(Result);
end
else
begin
GlobalFree(Result);
Result := 0;
end;
end;
end;
Drop := MakeDrop([ParamStr(1)]);
if Drop <> 0 then PostMessage(hMainWin, wm_DropFiles, Drop, 0);
GlobalFree(Drop);
-
Гораздо удобнее для передачи информации пользоваться атомами, а не мучаться с глобальной кучей.
-
> Dimka Maslov © (21.11.11 10:30) [9]
> Гораздо удобнее для передачи информации пользоваться атомами
Т.к. разницы между локальной и глобальной кучей нет, проще пользоваться дельфийским менеджером памяти, бо пересылаемое живо до выхода из обработчика WM_COPYDATA (обрабатываемого синхронно).
Да и, во-ще, атомы для передачи информации не очень хороши, т.к. время их жизни ограничено сессией, а у того же mmf - процессом.
-- Regards, LVT.
-
...В итоге забил на WM_COPYDATA, и использовал WM_DROPFILES :) Спасибо DVM за подсказку, так даже лучше, ибо PostMessage, да и на приёме удобно... Получилось примерно так:
procedure DropArgs(hWnd: HWND; argc: Integer; argv: PCmdArgs);
var
pdf: PDropFiles;
hMem: THandle;
I, Offset, Len: Integer;
begin
if (argc <= 0) then
Exit;
Len := 0;
for I := 0 to argc - 1 do
Inc(Len, lstrlenW(argv[I]) + 1);
Inc(Len, Len + SizeOf(WideChar) + SizeOf(TDropFiles));
hMem := GlobalAlloc(GHND, Len);
if (hMem <> 0) then
begin
pdf := GlobalLock(hMem);
pdf^.pFiles := SizeOf(TDropFiles);
pdf^.fWide := True;
Offset := pdf^.pFiles;
for I := 0 to argc - 1 do
begin
Len := lstrlenW(argv[I]) + 1;
lstrcpynW(PWideChar(Integer(pdf) + Offset), argv[I], Len);
Inc(Offset, Len * SizeOf(WideChar));
end;
GlobalUnlock(hMem);
if (not PostMessageW(hWnd, WM_DROPFILES, hMem, 0)) then
GlobalFree(hMem);
end;
end;
-
> DVM © (20.11.11 22:18) [8]
> Вот можно еще так
Это вовсе неочевидно, что можно. Особенно смущает
if Drop <> 0 then PostMessage(hMainWin, wm_DropFiles, Drop, 0);
GlobalFree(Drop);
Оно должно быть так: WM_DROPFILES message is sent .. & An application should return zero if it processes this message Да и эти GHND or GMEM_SHARE мне не нравятся. -- Regards, LVT.
-
> neversleep (21.11.11 12:04) [11]
> Получилось примерно так:
Получилось некузяво, IMHO. Ты хоть сам понял, что понаписал?
-- Regards, LVT.
-
> neversleep (21.11.11 12:04) [11]
> ...В итоге забил на WM_COPYDATA
Надо вернуться к WM_COPYDATA. Там всего-то надо одну строку передать: GetCommandLineW, (если уж unicode).
-- Regards, LVT.
-
> Ты хоть сам понял, что понаписал?
После такого вопроса, я начал сомневаться :) Что не так?
> Надо вернуться к WM_COPYDATA. > Там всего-то надо одну строку передать: GetCommandLineW, > > (если уж unicode). >
Собственно argc и argv в параметрах DropArgs, это у меня есть результат CommandLineToArgvW + GetCommandLineW. Кажется я понял, надо передать GetCommandLineW, а на стороне приёма CommandLineToArgvW.
-
> > Кажется я понял, надо передать GetCommandLineW, а на стороне > приёма CommandLineToArgvW. Хотел сказать отправлять...
-
> neversleep (21.11.11 17:23) [15]
> я понял, надо передать GetCommandLineW,
Ну, да. Отправителю выделять память даже не потребуется. Размер пакета cbData это lstrlenW(GetCommandLineW). А принимающий парсит полученную строку PWChar(...lpData), которая будет валидна вплоть до выхода из обработчика WM_COPYDATA.
Да, и обработчик это не procedure OnCopyData(pcds: PCopyDataStruct); а procedure TFormX.WMCOPYDATA(var msg: TMsg); // message WM_COPYDATA;
-- Regards, LVT.
-
> Размер пакета cbData это lstrlenW(GetCommandLineW).
Спасибо, только cbData = (lstrlenW(GetCommandLineW) + 1) * SizeOf(WideChar). Финальный вариант:
const
IDCD_COMMANDLINE = 12345;
procedure SendCmdLine(hWnd: HWND);
var
cds: TCopyDataStruct;
begin
cds.dwData := IDCD_COMMANDLINE;
cds.lpData := GetCommandLineW;
cds.cbData := (lstrlenW(cds.lpData) + 1) * SizeOf(WideChar);
SendMessageW(hWnd, WM_COPYDATA, 0, Integer(@cds));
end;
procedure OnCopyData(pcds: PCopyDataStruct); var
argc: Integer;
argv: PCmdArgs;
begin
if (pcds^.dwData <> IDCD_COMMANDLINE) then
Exit;
argv := PCmdArgs(CommandLineToArgvW(pcds^.lpData, argc));
Всё гениальное - просто! (c)
-
> Leonid Troyanovsky © (21.11.11 16:27) [12]
> Это вовсе неочевидно, что можно.
Код паршивый, конечно, но оно работает, я проверил. Если его подрихтовать, то по-моему вполне удобно получается - один обработчик и для перетаскиваемых на форму файлов и для файлов отправленных из второй копии.
> Да и эти GHND or GMEM_SHARE мне не нравятся.
пережитки Win3.1, вероятно его писали давно.
> Оно должно быть так: WM_DROPFILES message is sent .. > & An application should return zero if it processes this > message
Ну про то, что надо возвращать 0 написано у доброй половины оконных сообщений Windows и лишь некоторым это действительно нужно. Этому точно не требуется.
-
>Финальный вариант [3] ? ;)
-
> neversleep (21.11.11 20:26) [18]
> Спасибо, только cbData = (lstrlenW(GetCommandLineW) + 1) > * SizeOf(WideChar).
Я упустил, sorry.
> DVM © (21.11.11 23:51) [19] > Код паршивый, конечно, но оно работает, я проверил
Он может и ничего, но как-то стремно после PostMessage освобождать память. Если для SendMessage у виндов встречается магия при передаче данных (как, например у того же WM_COPYDATA или WM_GETTEXT), то для PM подобного точно нет.
-- Regards, LVT.
-
> MBo > [3] ? > ;)
Почти, но не совсем... мне в тот момент смекалки не хватило развить идею, голова забита была ;)
> Он может и ничего, но как-то стремно после PostMessage > освобождать память.
К слову, когда я писал код в [11], в случае если PostMessage успешно отработает, GlobalFree завершается неудачей с GetLastError = ERROR_INVALID_HANDLE, поэтому if (not PostMessage...). Хотя, мб в старых виндах оно по-другому себя вело.
-
> К слову, когда я писал код в [11], в случае если PostMessage > успешно отработает, GlobalFree завершается неудачей с GetLastError > = ERROR_INVALID_HANDLE, поэтому if (not PostMessage...)
Шаманские пляски. А что значит "если PostMessage успешно отработает"?
-
> DVM © (21.11.11 23:51) [19]
> Ну про то, что надо возвращать 0 написано у доброй половины > оконных сообщений Windows и лишь некоторым это действительно > нужно. Этому точно не требуется.
Оно потребуется для определения успешности доставки.
Т.к. WM_DROPFILES < WM_USER, то пересылыемые данные должны маршаллиться. Значит все GlobalAlloc/GlobalFree излишни и можно пользовать дельфийский менеджер памяти. Т.е., правильно заполняем _DROPFILES и делаем SendMessage.
Принимающая сторона парсит путем DragQueryFile, делает DragFinish и возвращает Msg.Result := 0.
Как-то так.
Возможно, что так и получится совместить обработку посылаемого проводником и своим приложением.
Не тестировалось. Думаю, что те кто используют подобную магию должны внимательно во всем этом разобраться ;)
-- Regards, LVT.
-
> Германн © (22.11.11 08:12) [23]
> А что значит "если PostMessage успешно отработает"?
msdn: Return value Type: BOOL If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError. GetLastError returns ERROR_NOT_ENOUGH_QUOTA when the limit is hit.
Remarks When a message is blocked by UIPI the last error, retrieved with GetLastError, is set to 5 (access denied).
-- Regards, LVT.
|