[Оглавление]   [1]  [2]  [3]  [4[5]  [6]  [7]


Windows API & Delphi VCL FAQ

Часть 4


Q: Как правильно завершить некое приложение?
A: Если не принудительно, то можно послать на его 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;


Serge Nozhenko
(2:5020/175).

Q: [Win32] Как удалить файл в корзину (Recycle Bin)?
A:
program del;

uses
 ShellApi;

//function SHFileOperation(const lpFileOp: TSHFileOpStruct): Integer; stdcall;

Var T:TSHFileOpStruct;
    P:String;
begin
  P:='C:\Windows\System\EL_CONTROL.CPL';
  With T do
  Begin
    Wnd:=0;
    wFunc:=FO_DELETE;
    pFrom:=Pchar(P);
    fFlags:=FOF_ALLOWUNDO
  End;
  SHFileOperation(T);
End.


Ed Lagerburg
lagerbrg@euronet.nl.

Q: Как отобразить некоторые окна своей программы в панели задач Windows (помимо главного окна)
A: Hапример, так:
  procedure TMyForm.CreateParams(var Params :TCreateParams); {override;}
  begin
    inherited CreateParams(Params); {CreateWindowEx}
    Params.ExStyle := Params.ExStyle or WS_Ex_AppWindow;
  end;


Max Rusov
(2:5030/456.1).

Q: Как изменить цвет отмеченных записей в DBGrid?
A: Hапример, так:
 DefaultDrawing:=False;
 ....
procedure TfrmCard.GridDrawColumnCell(Sender: TObject; const Rect: TRect;
                                          DataCol: Integer; Column: TColumn;
                                          State: TGridDrawState);
var
  Index   : Integer;
  Marked,
  Selected: Boolean;
begin

  Marked := False;
  if (dgMultiSelect in Grid.Options) and THackDBGrid(Grid).Datalink.Active then
    Marked
:=Grid.SelectedRows.Find(THackDBGrid(Grid).Datalink.Datasource.Dataset.Bookmark
, Index);

  Selected := THackDBGrid(Grid).Datalink.Active and (Grid.Row-1 =
                                THackDBGrid(Grid).Datalink.ActiveRecord);

  if Marked then begin
    Grid.Canvas.Brush.Color:=$DFEFDF;;
    Grid.Canvas.Font.Color :=clBlack;
  end;

  if Selected then begin
    Grid.Canvas.Brush.Color:=$FFFBF0;
    Grid.Canvas.Font.Color :=clBlack;
    if Marked then
      Grid.Canvas.Brush.Color:=$EFE3DF; { $8F8A30 }
  end;

  Grid.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;
где
  THackDBGrid = class(TDBGrid)
    property DataLink;
    property UpdateLock;
  end;


Vadim Puzanov
vadim@mimex.krasnoyarsk.su
(2:5090/20).

Q: [Win32] Как проверить, имеем ли мы административные привилегии в системе?
A:
// Routine: check if the user has administrator provileges
// Was converted from C source by Akzhan Abdulin. Not properly tested.
type
 PTOKEN_GROUPS = TOKEN_GROUPS^;

function RunningAsAdministrator (): Boolean;
var
 SystemSidAuthority: SID_IDENTIFIER_AUTHORITY = SECURITY_NT_AUTHORITY;
 psidAdmin: PSID;
 ptg: PTOKEN_GROUPS = nil;
 htkThread: Integer; { HANDLE }
 cbTokenGroups: Longint; { DWORD }
 iGroup: Longint; { DWORD }
 bAdmin: Boolean;

begin
    Result := false;
    if not OpenThreadToken(GetCurrentThread(),      // get security token
                        TOKEN_QUERY,
                        FALSE,
                        htkThread) then
       if GetLastError() = ERROR_NO_TOKEN then
       begin
          if not OpenProcessToken(GetCurrentProcess(),
                               TOKEN_QUERY,
                               htkThread) then
          Exit;
       end
       else
          Exit;
    if GetTokenInformation(htkThread,            // get #of groups
                           TokenGroups,
                           nil,
                           0,
                           cbTokenGroups) then
       Exit;
    if GetLastError() <> ERROR_INSUFFICIENT_BUFFER then
       Exit;
    ptg := PTOKEN_GROUPS( getmem( cbTokenGroups ) );
    if not Assigned(ptg) then
       Exit;
    if not GetTokenInformation(htkThread,           // get groups
                            TokenGroups,
                            ptg,
                            cbTokenGroups,
                            cbTokenGroups) then
       Exit;
    if not AllocateAndInitializeSid(SystemSidAuthority,
                                 2,
                                 SECURITY_BUILTIN_DOMAIN_RID,
                                 DOMAIN_ALIAS_RID_ADMINS,
                                 0, 0, 0, 0, 0, 0,
                                 psidAdmin) then
       Exit;
    iGroup := 0;
    while iGroup < ptg^.GroupCount do // check administrator group
    begin
       if EqualSid(ptg^.Groups[iGroup].Sid, psidAdmin) then
       begin
          Result := TRUE;
          break;
       end;
       Inc( iGroup );
    end;
    FreeSid(psidAdmin);
end;


Andy Nikolayev
an@megatel.msk.su
(2:5020/56).

Q: Как вставить в StatusPanel свои компоненты, например ProgressBar?
A: pgProgress положить на форму как Visible := false;
StatusPanel надо OwnerDraw сделать и pефpешить, если Position меняется.
procedure TMainForm.stStatusBarDrawPanel(StatusBar: TStatusBar;
  Panel: TStatusPanel; const Rect: TRect);
begin
  if Panel.Index = pnProgress then
  begin
    pgProgress.BoundsRect := Rect;
    pgProgress.PaintTo(stStatusBar.Canvas.Handle, Rect.Left, Rect.Top);
  end;
end;


Vladimir Gaitanoff
vg@divo.ru
(2:50/430.2).

Q: Как отчитывать промежутки времени с точностью, большей чем 60 мсек?
A: Для начала описываешь процедуру, которая будет вызываться по сообщению от таймера :
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 мсек.

Leonid Tserling
tlv@f3334.dd.vaz.tlt.ru.

Q: Как вставить в нужное место Rich Text в Rich Text Control?
A: Вы можете послать сообщение EM_STREAMIN с параметром SFF_SELECTION методом Perform для замены текущего Selection. Выдержка из Help:

EM_STREAMIN
wParam = (WPARAM) (UINT) uFormat; // Integer
lParam = (LPARAM) (EDITSTREAM FAR *) lpStream; // EDITSTREAM^

The EM_STREAMIN message replaces the contents of a rich edit control with the specified data stream.

Parameters

uFormat

One of the following data formats, optionally combined with the SFF_SELECTION flag:

Value Meaning
SF_TEXT Text
SF_RTF Rich-text format
If the SFF_SELECTION flag is specified, the stream replaces the contents of the current selection. Otherwise, the stream replaces the entire contents of the control.

lpStream

Pointer to an EDITSTREAM structure. The control reads (streams in) the data by repeatedly calling the function specified by the structure's pfnCallback member.

Return Value

Returns the number of characters read.


Mikhail Chernyshev
Mikhail-Chernyshev@usa.net
(2:4615/26).

Q: Как указать максимальный размер текста для RichEdit Control?
A: У этого компонента есть свойство MaxLength, которое работает некорректно. Поэтому лучше пользоваться RichEdit.Perform(EM_LIMITTEXT, нужный размер, 0); Причем перед каждом открытии файла это действие необходимо повторять.

Maxim Liverovskiy
(2:5030/254.38)

Если Вы передаете в качестве размера 0, то ОС ограничивает размер OS Specific Default Value. Реально, по результатам моих экспериментов, поставить можно размер, чуть меньший доступной виртуальной памяти. Я ограничился 90% от свободной виртуалки.

Для того, чтобы не повторять этот вызов (EM_LIMITTEXT), можно воспользоваться сообщением EM_EXLIMITTEXT.

Stas Mehanoshin
(2:5030/143.23).

Q: Как инсталлировать на время работы программы свои шрифты?
A: Добавить фонт (.fon, .fot, .fnt, .ttf) в систему можно след. образом:
{$IFDEF WIN32}
    AddFontResource( PChar( my_font_PathName { AnsiString } ) );
{$ELSE}
var
    ss  : array [ 0..255 ] of Char;

    AddFontResource ( StrPCopy ( ss, my_font_PathName ));
{$ENDIF}
    SendMessage ( HWND_BROADCAST, WM_FONTCHANGE, 0, 0 );
Убрать его по окончании работы:
{$IFDEF WIN32}
    RemoveFontResource ( PChar(my_font_PathName) );
{$ELSE}
    RemoveFontResource ( StrPCopy ( ss, my_font_PathName ));
{$ENDIF}
    SendMessage ( HWND_BROADCAST, WM_FONTCHANGE, 0, 0 );
При этом не надо никаких перезагрузок и прочего, после добавления фонт сразу можно использовать. my_font_PathName : string ( не string[nn] для D2+) - содержит полный путь с именем и расширением необходимого фонта. После удаления фонта форточки о нем забывают. Если его не удалить, он (кажется) так и останется проинсталенным, во всяком случае, я это не проверял.

Andry Trushin
(2:5020/474.7).

Q: Как научить Delphi делать правильное округление дробных чисел?
A: Для решения этой проблемы мною написана функция, которую можно модифицировать для всех случаев. Смысл заключается в том, что рассматривается строка. После этого все проблемы с округлением снялись.
Function RoundStr(Zn:Real;kol_zn:Integer):Real;
{Zn-значение;  Kol_Zn-_ол-во знаков после запятой}
Var snl,s,s0,s1,s2:String; n,n1:Real; nn,i:Integer;
begin
 s:=FloatToStr(Zn);
 if (Pos(',',s)>0) and (Zn>0) and
    (Length(Copy(s,Pos(',',s)+1,length(s)))>kol_zn)
 then begin
   s0:=Copy(s,1,Pos(',',s)+kol_zn-1);
   s1:=Copy(s,1,Pos(',',s)+kol_zn+2);
   s2:=Copy(s1,Pos(',',s1)+kol_zn,Length(s1));
   n:=StrToInt(s2)/100; nn:=Round(n);
   if nn>=10 then begin
     snl:='0,';  For i:=1 to kol_zn-1 do snl:=snl+'0'; snl:=snl+'1';
     n1:=StrToFloat(Copy(s,1,Pos(',',s)+kol_zn))+StrToFloat(snl);
     s:=FloatToStr(n1); if Pos(',',s)>0 then s1:=Copy(s,1,Pos(',',s)+kol_zn);
   end else s1:=s0+IntToStr(nn);
   if s1[Length(s1)]=',' then s1:=s1+'0';
   Result:=StrToFloat(s1);
 end else Result:=Zn;
end;


Nadya Kutareva
(2:5021/13.11)

Все-таки работа со строками здесь излишество -

function RoundEx( X: Double; Precision : Integer ): Double;
{Precision :
1   - до целых
10  - до десятых
100 - до сотых
...
}
var  ScaledFractPart, Temp : Double;
begin
  ScaledFractPart := Frac(X)*Precision;
  Temp := Frac(ScaledFractPart);
  ScaledFractPart := Int(ScaledFractPart);
  if Temp >=  0.5 then ScaledFractPart := ScaledFractPart + 1;
  if Temp <= -0.5 then ScaledFractPart := ScaledFractPart - 1;
  RoundEx := Int(X) + ScaledFractPart/Precision;
end;


Ilya Golovko
(2:5010/101.19).

Q: Мне нужно откpыть из моей фоpмы модальное окно, т.е. пpиостановить pаботу в моей фоpме до обpаботки этого модального окна. Hо пpи этом я теpяю возможность убpать (минимизиpовать) мою фоpму.
A:
function TMyForm.Execute: TModalResult;
begin
  Show;
  try
    SendMessage(Handle, CM_ACTIVATE, 0, 0);
    ModalResult := 0;
    repeat
      Application.HandleMessage;
      if Application.Terminated then ModalResult := mrCancel;
      if ModalResult = mrCancel then CloseModal;
    until ModalResult <> 0;
    Hide;
    Result := ModalResult;
    SendMessage(Handle, CM_DEACTIVATE, 0, 0);
  finally
    Hide;
  end;
end;
Конечно, в TMyForm должно быть FormStyle := fsStayOnTop;

Eugeny D.Shtefanov
shtefanov@usa.net.

Q: Интересная вещь: как консольное приложение может узнать что Винды завершаются?
A: Все процессы получают сигналы 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 -- там всё есть.



Alexander V. Naumochkin
(2:5020/59).

Q: Как работать с поименованными каналами под W'95/NT в сети?
A: сервер :
  StrPCopy(buff,Edit1.Text);
  fPipeHandle:=CreateNamedPipe(buff,
    Pipe_Access_Duplex or File_Flag_Overlapped,
    Pipe_Type_Message or Pipe_ReadMode_Byte or Pipe_Wait,
    5, $400, $400, 235, nil);
клиент :
  StrPCopy(buff,Edit1.Text);
  fFileHandle:=CreateFile(buff,
    Generic_Read or Generic_Write,
    File_Share_Read or File_Share_Write,
    nil,
    Open_Existing,
    File_Attribute_Normal or File_Flag_Overlapped or Security_Anonymous,
    0);
  if fFileHandle <> Invalid_Handle_Value then begin ...


Jack Sinelnikov
(2:5054/9.13).

Q: Как запретить переключение на другие задачи или хотя-бы контролировать этот процесс?
A: Выключить Ctl-alt-del:
bool old;
SystemParametersInfo (SPI_SCREENSAVERRUNNING,1,&old,0)
Включить обратно
SystemparametersInfo (SPI_ScreenSaverrunning,0,&old,0)

Мне помогло. Хоть и пpишлось повозиться: в хэлпе нет пpо паpаметp SPI_SCRENSAVERRUNNING...



Konstantin Okolelyh
(2:5025/77.23).

Q: Как рисовать картинки в пунктах меню (через OwnerDraw)?
A:
unit DN_Win;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, Menus, StdCtrls,

type
  TDNForm = class(TForm)
    MainMenu1: TMainMenu;
    cm_MainExit: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure cm_MainExitClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    BM:TBitmap;
    Procedure WMDrawItem(var Msg:TWMDrawItem);      message wm_DrawItem;
    Procedure WMMeasureItem(var Msg:TWMMeasureItem); message
wm_MeasureItem;

  end;

var
  DNForm : TDNForm;

implementation

{$R *.DFM}

var
  Comm,yMenu : word;

procedure TDNForm.FormCreate(Sender: TObject);
begin
  {картинку в меню}
  yMenu:=GetSystemMetrics(SM_CYMENU);
  comm:=cm_MainExit.Command;
  ModifyMenu(MainMenu1.Handle,0,mf_ByPosition or mf_OwnerDraw,comm,'Go');
end;{TDNForm.FormCreate}

procedure TDNForm.cm_MainExitClick(Sender: TObject);
begin
  DNForm.Close;
end;{TDNForm.cmExitClick}

{для прорисовки меню}
Procedure TDNForm.WMMeasureItem(var Msg:TWMMeasureItem);
Begin
with Msg.MeasureItemStruct^ do
  if ItemID=comm then  begin ItemWidth:=yMenu; Itemheight:=yMenu; end;
End;{WMMeasureItem}
{}
Procedure TDNForm.WMDrawItem(var Msg:TWMDrawItem);
var
  MemDC:hDC;
  BM:hBitMap;
  mtd:longint;
Begin
with Msg.DrawItemStruct^ do
  begin
  if ItemID=comm then
    begin
      BM:=LoadBitMap(hInstance,'dver');
      MemDC:=CreateCompatibleDC(hDC);{hDC входит в структуру
TDrawItemStruct}
      SelectObject(MemDC,BM);
      {rcItem входит в структуру TDrawItemStruct}
      if ItemState=ods_Selected then mtd:=NotSrcCopy  else mtd:=SrcCopy;

StretchBlt(hDC,rcItem.left,rcItem.top,yMenu,yMenu,MemDC,0,0,24,23,mtd);
      DeleteDC(MemDC);
      DeleteObject(BM);
    end;
  end{with}
End;{TDNForm.WMDrawItem}

end.


Eugeny Sverchkov
es906@kolnpp.elektra.ru
(2:5031/12.23).
Hosted by uCoz