|
||||
|
APIПеременные средыКак раскрыть строки с подстановками вида '%SystemRoot%\IOSUBSYS\'?Nomadic советует: Используй вызов ExpandEnvironmentStrings(LPCTSTR lpSrc, LPTSTR lpDst, DWORD nSize); Изменение системного времени из Delphi II
Можно. Попробуйте следующий код: Procedure settime(hour, min, sec, hundreths : byte); assembler; asm mov ch, hour mov cl, min mov dh, sec mov dl, hundreths mov ah, $2d int $21 end; Procedure setdate(year : word; month, day : byte); assembler; asm mov cx, year mov dh, month mov dl, day mov ah, $2b int $21 end; Завершение работы WindowsОпределение завершения работы Windows
Существует ли возможность определения завершения работы Windows для нормального завершения работы работающего приложения Delphi? Самым простым решением является создание обработчика события главной формы OnCloseQuery. Данное событие возникает как результат сообщения WM_QUERYENDSESSION, которое посылается всем работающим приложениям Windows в момент инициализации процесса окончания работы Windows. Логическая переменная CanClose, передаваемая обработчику как var-параметр, может позволить программе (и Windows) завершить свою работу, если имеет значение True, значение же False не позволит программе завершить свою работу. Следующий код демонстрирует как можно воспользоваться данным событием. Демонстрационный код procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin {Спрашиваем пользователя, если инициировано завершение работы.} if MessageDlg('Вы уверены?', mtConfirmation, mbYesNoCancel, 0) = mrYes then CanClose := true {Разрешаем завершение работы.} else CanClose := false; {Не разрешаем завершение работы.} end; Как консольное приложение может узнать, что Винды завершаются?Nomadic рекомендует следующий код: Все процессы получают сигналы CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT и CTRL_SHUTDOWN_EVENT. А делается это (грубо говоря :) так: BOOL Ctrl_Handler(DWORD Ctrl) { if ((Ctrl == CTRL_SHUTDOWN_EVENT) || (Ctrl == CTRL_LOGOFF_EVENT)) { // Вау! Юзер обламывает! } else { // Тут что-от другое можно творить. А можно и не творить :-) } return TRUE; } function Ctrl_Handler(Ctrl: Longint): LongBool; begin if Ctrl in [CTRL_SHUTDOWN_EVENT, CTRL_LOGOFF_EVENT] then begin // Вау, вау end else begin // Am I creator? end; Result := true; end; А где-то в программе: SetConsoleCtrlHandler(Ctrl_Handler, TRUE); Таких обработчиков можно навесить кучу. Если при обработке какого-то из сообщений обработчик возвращает FALSE, то вызывается следующий обработчик. Можно настроить таких этажерок, что ого-го :-))) Короче, смотри описание SetConsoleCtrlHandler — там всё есть. Как корректно перехватить сигнал выгрузки операционной системы, если в моей программе нет окна?Nomadic рекомендует следующий способ: Используй GetMessage(), в качестве HWND окна пиши NULL (на Паскале — 0). Если в очереди сообщений следующее — WM_QUIT, то эта функция фозвращает FALSE. Если ты пишешь программу для Win32, то запихни это в отдельный поток, организующий выход из программы. Постепенное умираниеThe_Sprite пишет: Вопрос: А как реализовать в одном компоненте такие функции как выключение компьютера, перезагрузка, завершение сеанса работы пользователя, Eject CD, выключение питания монитора и т.д.? Ответ: предлагаем посмотреть следующий пример… Совместимость: все версии Delphi Пример: procedure TForm1.Button1Click(Sender: TObject); begin PowerControl1.Action:=actCDEject;// Или... actLogOFF, actShutDown... PowerControl1.Execute; end Component Code: unit PowerControl; interface uses WinTypes, WinProcs, Messages, SysUtils, Classes, Controls,Forms, Graphics,MMSystem; type TAction =(actLogOFF,actShutDown,actReBoot,actForce,actPowerOFF, actForceIfHung,actMonitorOFF,actMonitorON,actCDEject,actCDUnEject); type TPowerControl = class(TComponent) private FAction : TAction; procedure SetAction(Value : TAction); protected public function Execute :Boolean; published property Action :TAction read FAction write SetAction; end; procedure Register; implementation procedure Register; begin RegisterComponents('K2',[TPowerControl]); end; procedure TPowerControl.SetAction(Value : TAction); begin FAction := Value; end; function TPowerControl.Execute : Boolean; begin with (Owner as TForm) do case FAction of actLogOff: ExitWindowsEx(EWX_LOGOFF, 1); actShutDown: ExitWindowsEx(EWX_SHUTDOWN, 1); actReBoot:ExitWindowsEx(EWX_REBOOT, 1); actForce:ExitWindowsEx(EWX_FORCE, 1); actPowerOff:ExitWindowsEx(EWX_POWEROFF, 1); actForceIfHung:ExitWindowsEx(EWX_FORCEIFHUNG, 1); actMonitorOFF:SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, 0); actMonitorON: SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, -1); actCDEject: mciSendstring('SET CDAUDIO DOOR OPEN WAIT', nil, 0, Handle); actCDUnEject: mciSendstring('SET CDAUDIO DOOR CLOSED WAIT', nil, 0, Handle); end; {Case} Result := True; end; end. РазноеКак не допустить запуск второй копии программы VIIIИгорь Пролис рекомендует следующий код: {*******************************************************} { } { HTMLCoolEdit } { } { Copyright (c) 1999-2000 PROFOX } { } {*******************************************************} unit multinst; interface uses Forms, Windows, Dialogs, SysUtils; const MI_NO_ERROR = 0; MI_FAIL_SUBCLASS = 1; MI_FAIL_CREATE_MUTEX = 2; function GetMIError: Integer; function InitInstance : Boolean; implementation uses RegWork, FileWork; var UniqueAppStr : PChar; MessageId: Integer; WProc: TFNWndProc = Nil; MutHandle: THandle = 0; MIError: Integer = 0; function GetMIError: Integer; begin Result := MIError; end; function NewWndProc(Handle: HWND; Msg: Integer; wParam, lParam: Longint): Longint; StdCall; begin Result := 1; if Msg = MessageID then begin if IsIconic(Application.Handle) then OpenIcon(Application.Handle) else SetForegroundWindow(Application.Handle); FileWork.LoadFileName(RegWork.RWGetParamStr1); end else Result := CallWindowProc(WProc, Handle, Msg, wParam, lParam); end; procedure SubClassApplication; begin WProc := TFNWndProc(SetWindowLong(Application.Handle, GWL_WNDPROC, Longint(@NewWndProc))); if WProc = Nil then MIError := MIError or MI_FAIL_SUBCLASS; end; procedure DoFirstInstance; begin SubClassApplication; MutHandle := CreateMutex(Nil, False, UniqueAppStr); if MutHandle = 0 then MIError := MIError or MI_FAIL_CREATE_MUTEX; end; procedure BroadcastFocusMessage; begin Application.ShowMainForm := False; PostMessage(HWND_BROADCAST, MessageId, 0, 0); end; function InitInstance : Boolean; begin MutHandle := OpenMutex(MUTEX_ALL_ACCESS, False, UniqueAppStr); if MutHandle = 0 then begin ShowWindow(Application.Handle, SW_ShowNormal); Application.ShowMainForm:=True; DoFirstInstance; result := True; end else begin RegWork.RWSetParamStr1; BroadcastFocusMessage; result := False; end; end; initialization begin UniqueAppStr := PChar(Application.ExeName); MessageID := RegisterWindowMessage(UniqueAppStr); ShowWindow(Application.Handle, SW_Hide); Application.ShowMainForm:=FALSE; end; finalization begin if WProc <> Nil then SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(WProc)); end; end. Как не допустить запуск второй копии программы IXYoungHacker рекомендует следующий код: Был взят из кулибы и доработан, поскольку возникали ситуации когда программа, по HotKey назначенным на ярлык, запускалась дважды и более раз. Связано с тем что поиск мутекса и его создание разнесены во времени и пока в одном приложении мутекс не нашелся но еще не создался второе приложение тоже не находит мутекса и инициирует его создание Поиск окон и создание их нарываются на те-же проблемы. Из RxLib Функция тоже не обходит этой ситуации. Мой вариант немного дорабатывает уже значительно переработанное то что предоставили разработчики Delphi 2 Пачека (Pacheco) и Тайхайра (Teixeira). и находится в файле TPrevInstUnit. В файле проекта пишется следующий вызов: begin //– Найти предыдущую версию программы if (initinstance) then begin … Application.Initialize; … Application.CreateForm(…); … Application.Run; end; end.Файл TPrevInstUnit unit TPrevInstUnit; interface uses Forms, Windows, Dialogs, SysUtils; function InitInstance : Boolean; implementation const UniqueAppStr : PChar = #0; // Различное для каждого приложения // Но одинаковое для каждой копии программы var MessageId : Integer; OldWProc : TFNWndProc = Nil; MutHandle : THandle = 0; SecondExecution : Boolean = False; function NewWndProc(Handle: HWND; Msg: Integer; wParam, lParam: Longint): Longint; StdCall; begin //- Если это - сообщение о регистрации... } if (Msg = MessageID) then begin //- если основная форма минимизирована if IsIconic(Application.Handle) then begin //- восстанавливаем ееApplication.Restore; end else begin //- вытаскиваем на перед ShowWindow(Application.Handle, SW_SHOW); SetForegroundWindow(Application.Handle); Application.BringToFront; end; Result := 0; end else { В противном случае посылаем сообщение предыдущему окну } Result := CallWindowProc(OldWProc, Handle, Msg, wParam, lParam); end; function InitInstance : Boolean; var BSMRecipients: DWORD; begin Result := False; //- пробуем открыть MUTEX созданный предыдущей копией программы MutHandle := CreateMutex(Nil, True, UniqueAppStr); //- Мутекс уже был создан ? SecondExecution := (GetLastError = ERROR_ALREADY_EXISTS); if (MutHandle = 0) then begin ShowMessage('Ошибка создания Mutex.'); Exit; end; if Not (SecondExecution) then begin //- назначаем новый обработчик сообщений приложения, а старый сохраняем OldWProc := TFNWndProc(SetWindowLong(Application.Handle, GWL_WNDPROC, Longint(@NewWndProc))); //- если обработчик не найден устанавливаем ошибку if (OldWProc = Nil) then begin ShowMessage('Ошибка поиска стандартного обработчика сообщений приложения.'); Exit; end; //- Установить "нормальный" статус основного окна приложения ShowWindow(Application.Handle, SW_ShowNormal); //- покажем основную форму приложения Application.ShowMainForm := True; //- все нормально мама трын тин тин тин тири тын тын Result := True; end else begin //- установить статус окна приложения "невидимый" ShowWindow(Application.Handle, SW_Hide); //- Не покажем основную форму приложения Application.ShowMainForm := False; //- Посылаем другому приложению сообщение и информируем о необходимости // перевести фокус на себя BSMRecipients := BSM_APPLICATIONS; BroadCastSystemMessage(BSF_IGNORECURRENTTASK or BSF_POSTMESSAGE, @BSMRecipients, MessageID, 0, 0); end; end; initialization begin //- Создать ункальную строку для опознания приложения UniqueAppStr := PChar('YoungHackerNetworkDataBaseProgramm'); //- Зарегистрировать в системе уникальное сообщение MessageID := RegisterWindowMessage(UniqueAppStr); end; finalization begin if (OldWProc <> Nil) then { Приводим приложение в исходное состояние } SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(OldWProc)); end; end. Как не допустить запуск второй копии программы XNomadic рекомендует следующий код: FindWindow является неполным решением (если меняется заголовок окна или если есть другая программа с таким же заголовком или типом окна). Вторично: Это работает медленно. Правильно — использовать обьекты синхронизации Win32 API. Лениво пользовать семафоры, покажу на именованных мутексах (семафоры с двумя состояниями). Unit OneInstance32; interface implementation uses Forms; var g_hAppMutex: THandle; function OneInstance: boolean; var g_hAppCritSecMutex: THandle; dw: Longint; begin g_hAppCritSecMutex := CreateMutex(nil, true, PChar(Application.Title + '.OneInstance32.CriticalSection')); // if GetLastError - лениво писать g_hAppMutex := CreateMutex(nil, false, PChar(Application.Title + 'OneInstance32.Default')); dw := WaitForSingleObject(g_hAppMutex, 0); Result := (dw <> WAIT_TIMEOUT); ReleaseMutex(g_hAppCritSecMutex); // необязательно вследствие последующего закрытия CloseHandle(g_hAppCritSecMutex); end; initialization g_hAppMutex := 0; finalization if LongBool(g_hAppMutex) then begin ReleaseMutex(g_hAppMutex); // необязательно CloseHandle(g_hAppMutex); end; end. Как не допустить запуск второй копии программы XIМихаил Чумак рекомендует следующий код: Есть такая штука Atom (см. Help). program SelfCheck; uses Windows,Forms,Unit1 in 'Unit1.pas' {Form1}; const AtStr='MyProgram'; function CheckThis : boolean; var Atom: THandle; begin Atom:= GlobalFindAtom(AtStr); Result:= Atom <> 0; if not result then GlobalAddAtom(AtStr); end; begin if not CheckThis then begin // Запуск программмы Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; GlobalDeleteAtom(GlobalFindAtom(AtStr)); // !!! end else begin MessageBox(0,'Нельзя запустить две копии','Моя программа',0); end; end. Элегантно и работает однозначно. Спасибо Славе Шубину. Как не допустить запуск второй копии программы XIINomadic рекомендует следующее: A: Воспользуйтесь функцией ActivatePrevInstance из библиотеки rxLib. Для завершения второго экземпляра используйте Application.Terminate. (AS): Другой вариант: X:\DELPHI2\DEMOS\IPCDEMOS\ipcthrd.pas, функция IsMonitorRunning(). Как правильно завершить некое приложение?Nomadic рекомендует следующий код: Если не принудительно, то можно послать на его Instance сообщение WM_QUIT. Если же необходимо принудительно терминировать приложение, то смотрите ниже — под Windows NT процесс можно терминировать через специально предназначенный для этого хэндл. Иначе гарантии нет. Предположим, что процесс создаем мы, ожидая его завершения в течение maxworktime. Тогда — var dwResult: Longint; // This example was converted from C source. begin // Not tested. Some 'nil' assignments must be applied // as zero assignments in Pascal. Some vars need to // be declared (maxworktime, si, pi). AA. if CreateProcess(nil, CmdStr, nil, nil, FALSE,CREATE_NEW_CONSOLE, nil, nil, si, pi) then begin CloseHandle(pi.hThread); dwResult := WaitForSingleObject(pi.hProcess, maxworktime*1000*60); CloseHandle(pi.hProcess); if dwResult <> WAIT_OBJECT_0 then begin pi.hProcess := OpenProcess(PROCESS_TERMINATE, FALSE, pi.dwProcessId); if pi.hProcess <> nil then begin TerminateProcess(pi.hProcess, 0); CloseHandle(pi.hProcess); end; end; end; end; Как отчитывать промежутки времени с точностью, большей чем 60 мсек?Nomadic рекомендует следующий код: Для начала описываешь процедуру, которая будет вызываться по сообщению от таймера : procedure FNTimeCallBack(uTimerID, uMessage: UINT;dwUser, dw1, dw2: DWORD); stdcall; begin //// Тело процедуры. end; а дальше в программе (например по нажатию кнопки) создаешь Таймер и вешаешь на него созданную процедуру uTimerID:=timeSetEvent(10, 500, @FNTimeCallBack, 100, TIME_PERIODIC); Подробности смотри в Help. Hу и в конце убиваешь таймер timeKillEvent(uTimerID); И все. Точность этого способа до 1 мсек. минимальный интервал времени можно задавать 1 мсек. Обратите внимание на то, что все CALLBACK-функции, вызываемые Windows, должны использовать соглашение о вызовах stdcall. Как сделать чтобы при событиях моя программа отпpавляла кому-либо сообщение на мой компьютеp?Nomadic рекомендует следующий код: Если только послать, то проще всего, пожалуй… W32: F1 «NetMessageBufferSend»; Win16: Почему-то не описан, но руками наковырял… function NetMessageBufferSend(Zero1, Zero2: Word; WhoTo: PChar; Buffer: PChar; BufSize: Word): Integer; external 'netapi' index 525; «Кому» может быть '*' == всем. Что нужно давать WSAAsyncSelect в качестве параметра handle, если тот запускается и используется в dll (init), и никакой формы (у которой можно было бы взять этот handle) в этой dll не создается?Nomadic рекомендует следующий код: const WM_ASYNCSELECT = WM_USER+0; type TNetConnectionsManager = class(tobject) protected FWndHandle : HWND; procedure WndProc(var MsgRec : TMessage); … end; constructor TNetConnectionsManager.Create begin inherited Create; FWndHandle := AllocateHWnd(WndProc); … end; destructor TNetConnectionsManager.Destroy; begin … if FWndHandle<>0 then DeallocateHWnd(FWndHandle); inherited Destroy; end; procedure TNetConnectionsManeger.WndProc(var MsgRec : TMessage); begin with MsgRec do if Msg = WM_ASYNCSELECT then WMAsyncSelect(MsgRec) else DefWindowProc(FWndHandle, Msg, wParam, lParam); end; Hо pекомендую посмотpеть WinSock2, в котоpом можно: WSAEventSelect(FSocket, FEventHandle, FD_READ or fd_close); WSAWaitForMultipleEvents(…); WSAEnumNetworkEvents(FSocket, FEventHandle, lpNetWorkEvents); То есть, обойтись без окон и без очеpеди сообщений windows, а заодно иметь возможность pаботать и с IPX/SPX, и с netbios. Вызов других программVRSLazy@mail.ru пишет: Доброго времени суток, Вот посмотрел Ваше произведение Советы по делфи, мне очень понравилось :-) Правда в вопросе/решении запустить другую программу просто обалдел :-( Я как то долго мучился с этим самым ShellExecute пока не пришёл к следующему: uses …ToolWin, Windows … procedure Run(App: String); var ErrStr : String; PMSI: TStartupInfo; PMPI: TProcessInformation; begin try CreateProcess(nil, @App[1] , nil, nil, False, NORMAL_PRIORITY_CLASS, nil, nil, PMSI, PMPI); except ErrStr := 'Fault run process: '''+App+''''; Application.MessageBox(@ErrStr[1],'Failure process', MB_OK+MB_ICONERROR); end; разумеется это одно из самых корявых решений, но всё же работает, как вариант сойдет? Получение списка запущеных приложенийIgor Nikolaev aKa The Sprite предлагает следующий код: procedure TForm1.Button1Click(Sender: TObject); VAR Wnd : hWnd; buff: ARRAY [0..127] OF Char; begin ListBox1.Clear; Wnd := GetWindow(Handle, gw_HWndFirst); WHILE Wnd <> 0 DO BEGIN {Hе показываем:} IF (Wnd <> Application.Handle) AND {-Собственное окно} IsWindowVisible(Wnd) AND {-Hевидимые окна} (GetWindow(Wnd, gw_Owner) = 0) AND {-Дочернии окна} (GetWindowText(Wnd, buff, sizeof(buff)) <> 0) THEN BEGIN GetWindowText(Wnd, buff, sizeof(buff)); ListBox1.Items.Add(StrPas(buff)); END; Wnd := GetWindow(Wnd, gw_hWndNext); END; ListBox1.ItemIndex := 0; end; Как мне запустить какую-нибудь программу? А как подождать, пока эта программа не отработает? Как выяснить, работает ли программа или уже завершилась? Как принудительно закрыть выполняющуюся программу?Nomadic рекомендует следующее: A: WinExec() или ShellExecute. У второй больше возможностей. (SO): CreateProcess() в параметре process info возвращает handle запущенного процесса. Вот и делаешь WaitForSingleObject(pi.hProcess, INFINITE); (AA): (Win16) Delay можно взять из rxLib. handle := WinExec(…); if handle >= 32 then while GetModuleUsage(handle) > 0 do Delay(nn); else raise … (AM): Чтобы выяснить, работает ли программа, используйте GetProcessTimes(), параметр lpExitTime. (Win32) Для принудительного завершения процесса — TerminateProcess. (Win16) (RR): Надо послать программе сообщение WM_QUIT: Handle := Winexec(App, 0); PostMessage(Handle, WM_QUIT, 0, 0); Открытие выбранного файла в работающем приложенииПангин Дмитрий Викторович прислал письмо следующего содержания: При программировании MDI-приложений возникает следующая задача: Если пользователь кликнул на файле, тип которого поддерживается создаваемым приложением, то, если приложение уже запущено, не нужно запускать новую копию приложения, а нужно открыть выбранный файл в уже работающем приложении. Я сделал это так (возможно есть более красивое решение): \\ В файле проекта: var i: integer; hMainForm:hwnd; copyDataStruct:TCopyDataStruct; ParamString:string; WParam,LParam:integer; begin \\ ищем главное окно приложения, вместо Caption - nil, \\ поскольку к заголовку главного окна может добавиться заголовок MDIChild \\ (нужно позаботиться об уникальности имени класса главной формы) hMainForm:= FindWindow('TMainForm', nil); if hMainForm = 0 then begin Application.Initialize; Application.CreateForm(TFrmMain, frmMain); for i:=1 to ParamCount do TMainForm(Application.MainForm).OpenFile(ParamStr(i)); Application.Run; end else begin ParamString:=''; for i:=1 to ParamCount do begin \\ запихиваем все параметры в одну строку с разделителями ?13 ParamString:=ParamString+ParamStr(i)+ #13; end; \\ создаем запись типа TCopyDataStruct CopyDataStruct.lpData:=PChar(ParamString); CopyDataStruct.cbData:=Length(ParamString); CopyDataStruct.dwData:=0; WParam:=Application.Handle; LParam:=Integer(@CopyDataStruct); \\ отсылаем сообщение WM_COPYDATA главному окну открытого приложения SendMessage(hMainForm,WM_CopyData,WParam,LParam); Application.Terminate; end; end. \\ Обработчик сообщения WM_COPYDATA procedure TMainForm.CopyData(var Msg: TWMCopyData); var ParamStr:string; CopyDataStructure:TCopyDataStruct; i:integer; len:integer; begin CopyDataStructure:= Msg.CopyDataStruct^; ParamStr:=''; len:= CopyDataStructure.cbData; for i:=0 to len-1 do begin ParamStr:=ParamStr+(PChar(CopyDataStructure.lpData)+i)^; end; i:=0; while not(Length(ParamStr)=0) do begin if isDelimiter(#13,ParamStr,i) then begin OpenFile(Copy(ParamStr,0,i-1)); ParamStr:=Copy(ParamStr,i+1,Length(ParamStr)-i-1); end; inc(i); end; inherited; end; Убиваем активное приложениеThe_Sprite прислал письмо следующего содержания: Данная функция позволяет завершить выполнение любой активной программы по её classname или заголовку окна. Совместимость: Все версии Delphi Исходный код функции procedure KillProgram(Classname : string; WindowTitle : string); const PROCESS_TERMINATE = $0001; var ProcessHandle : THandle; ProcessID: Integer; TheWindow : HWND; begin TheWindow := FindWindow(Classname, WindowTitle); GetWindowThreadProcessID(TheWindow, @ProcessID); ProcessHandle := OpenProcess(PROCESS_TERMINATE, FALSE, ProcessId); TerminateProcess(ProcessHandle, 4); end;Комментарии Xianguang Li=(22 Октября 2000) В Delphi 5, при компиляции получается следующая ошибка: Incompatible types: 'String' and 'PChar'. После изменения выражения TheWindow := FindWindow(ClassName, WindowTitle) на TheWindow := FindWindow(PChar(ClassName), PChar(WindowTitle)) Нормально откомпилировалось. И ещё: если мы не знаем ClassName или WindowTitle программы, которую мы хотим убить, то мы не сможем её завершить. Причина в том, что нельзя вызвать функцию в виде: KillProgram(nil, WindowTitle) или KillProgram(ClassName, nil) Компилятор не позволяет передать nil в переменную типа String. Итак, я изменил объявление KillProgram(ClassName: string; WindowTitle: string) на KillProgram(ClassName: PChar; WindowTitle: PChar), вот теперь функция действительно может завершить любое приложение, если вы не знаете ClassName или WindowTitle этого приложения. |
|
||
Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх |
||||
|