FAQ по C++Builder
Вопросы
Q1. Как показать ProgressBar на StatusBar'е ?
Q2. Как использовать CTreeCtrl для построения дерева каталогов диска, как в
Проводнике ?
Q3. Есть класс - потомок CListView. Как изменить стиль у объекта CListCtrl,
принадлежащего к этому *view (например установить стиль Report)?
Q4. Как CString привести к char *?
Q5. Какие библиотеки (Freeware/Commercial) существуют для Visual C++ ?
Q6. А можно пример консольной программы ?
Q7. В созданном мастером (VC 6.0) win32 Console Application попытка вывести
русский текст дает кракозяблики.Что делать ?
Q8. Пытаюсь из своей программы вызвать Word97, для это делаю несколько
импортов и в результате имею кучу ошибок. Как правильно ?
Q9. А как отредактировать ресурсы .exe файла ?
Q10. Как программно получить номер билда своего приложения в VC++?
Q11. Какой функцией можно переключить видеорежим ?
Q12. Как вызвать окно выбора папки ?
Ответы
==============================================================================
Q1. Как показать ProgressBar на StatusBar'е ?
A1.
Предположим, что вы хотите показать CProgressCtrl на весь StatusBar.
Для этого необходимо проделать следующее:
- Выберите пункт меню View - Resource Symbols. Нажмите кнопку New и
добавьте новое имя, в нашем примере это будет ID_PROGRBAR.
- В файле MainFrm.cpp найдите объявление массива indicators (он
находиться сразу после END_MESSAGE_MAP) и отредактируйте его к
следующиему виду
static UINT indicators[] =
{
ID_PROGRBAR
};
- В файле _MainFrm.h создайте protected переменную m_bCreated типа
BOOL и public переменную m_progress типа CProgressCtl.
- В файле MainFrm.cpp отредактируйте конец функции
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) таким образом:
к участку кода:
if (!m_wndStatusBar.Create(this ) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof (UINT)))
{
TRACE0("Failed to create status bar\n" );
return -1; // fail to create
}
добавьте следующую строку:
else {
m_wndStatusBar.SetPaneInfo(0,ID_PROGRBAR,SBPS_STRETCH,10);
}
Кроме того, добавьте инициализацию нашей переменной m_bCreated
.........
m_bCreated=FALSE;
..........
- Теперь мы можем использовать ProgressBar в строке статуса, естественно не
забыв создать этот объект. Предположим, у нас есть функция
CMainFrame::OnWork(). Она будет выглядеть примерно так:
void CMainFrame::OnWork()
{
RECT rc;
m_wndStatusBar.GetItemRect(0,&rc);
if (m_bCreated==FALSE)
{
// создаем m_progress
m_progress.Create(WS_VISIBLE|WS_CHILD, rc,&m_wndStatusBar, 1);
// Устанавливаем размер от 0 до 100
m_progress.SetRange(0,100);
m_progress.SetStep(1);
m_bCreated=TRUE;
}
for (int I = 0; I < 100; I++)
{
Sleep(20);
m_progress.StepIt();
}
}
-Если откомпилировать проект на этой фазе, то все будет работать, но при
изменении размера окна линейка ProgressBar'а размеры менять не будет, поэтому
необходимо перекрыть событие OnSize:
void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
CFrameWnd::OnSize(nType, cx, cy);
if (m_bCreated)
{
RECT rc;
m_wndStatusBar.GetItemRect(0,&rc);
m_progress.SetWindowPos(&wndTop, rc.left, rc.top,
rc.right - rc.left,rc.bottom - rc.top, 0);
}
}
- Вот теперь все /-))))) Откомпилируйте проект и убедитесь, что все
работает.
==============================================================================
Q2. Как использовать CTreeCtrl для построения дерева каталогов диска, как в
Проводнике ? Неужели необходимо рекурсивно просмотреть диск, а потом прописать
ручками все Итемы данного контрола ??
A2. (A. Лисеев Дмитрий.
dimik@infopro.spb.su
)
Это тормозно и глючно. На больших дисках это займет несколько минут. Если
каталоги добавляются или удалются другими приложениями во время работы твоего
контрола, то будешь весь в проблемах. Все гораздо проще. Никаких рекурсий.
Просматриваем корневой каталог на предмет наличия подкаталогов и создаем итемы
первого уровня, в которых создаем по одному фиктивному итему (чтобы крестик
был и итем можно было раскрыть).
+ Каталог 1
+ Каталог 2
+ Каталог 3
Как только юзер пытается раскрыть итем, соответствующий некому каталогу, мы
удаляем из него фиктивный итем, просматриваем этот подкаталог и добавляем
соответствующие итемы со своими фиктивными внутри.
-Каталог 1
+ Каталог 4
+ Каталог 5
+ Каталог 6
+ Каталог 2
+ Каталог 3
Как только юзер закрывает итем, мы удаляем из него все дочерние итемы и
обратно добавляем фиктивный. Если структура каталогов изменилась, для
обновления юзеру достаточно просто закрыть и открыть соответствующую ветку.
Именно так и работает "Проводник".
==============================================================================
Q3. Есть класс - потомок CListView. Как изменить стиль у объекта CListCtrl,
принадлежащего к этому *view (например установить стиль Report) ?
A3.
Для этого пишите в OnInitialUpdate вашего вида
void CMyListView::OnInitialUpdate()
{
......
CListView::OnInitialUpdate();
CListCtrl& theCtrl = GetListCtrl();
DWORD dwStyle=GetWindowLong(theCtrl.m_hWnd,GWL_STYLE);
SetWindowLong(theCtrl.m_hWnd,GWL_STYLE,dwStyle|LVS_REPORT);
....
A3. (by Pavel Nazin 2:5020/1053.21)
Гораздо проще перекрыть PreCreateWindow (лучше всего воспользоваться
ClassWizard-ом) и поковырять переданный по ссылке CREATESTRUCT типа такого:
BOOL CMyListView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style|=LVS_REPORT;//так мы добавляем стиль
cs.style&=LVS_REPORT;//а вот так снимаем
return CMyListView::PreCreateWindow(cs);
}
==============================================================================
Q4. Как CString привести к char * ? _
A4. (by Yuri Khodin 2:5020/1200.20)
#include <atlbase.h>
USES_CONVERSION;
CString strData(_T("Some Data"));
char* lpszString = T2A((LPTSTR)(LPCTSTR)strData);
A2. (by Paul Kalyakin 2:5029/3.29
hjobyf@mail.ru
)
CString tmp_str;
char* st;
st=tmp_str.GetBuffer(tmp_str.GetLength())
важно то, что если с tmp_str что-либо сделать, то необходимо опять получить
указатель на внутренний буфер CString.
==============================================================================
Q5. Какие библиотеки Freeware/Commercial существуют для Visual C++ ? _
A5.
1- BCG Control Library (freeware)
http://msnhomepages.talkcity.com/WindowsWay/stasl/index.html
2- CJLibrary (freeware)
http://www.codejock.com/
Stringray Software (commercial) www.stingray.com
Фирма Stringray Software производит библиотеки для Visual C++ (MFC, ATL):
1. Stingray Objective Toolkit (PRO) - набор различных компонентов для
MFC и ATL
2. Stingray Objective Grid (PRO) - мощная сетка данных с возможностями,
близкими к Excel. Дружит с базами данных (через DAO,ADO,ODBC). Можно
использовать для ввода данных в таблицы БД и для вывода/печати простых
отчётов.
3. Stingray Objective Chart - средство для построения диаграмм
4. Stingray Objective Views - средство для создания Visio-подобных
интерфейсов (при помощи векторной графики)
5. Stingray Objective Edit - текстовый редактор с подсветкой синтаксиса
кроме этих, есть и другие продукты
-
Dundas Software (commercial)
http://www.dundas.com/
Фирма Dundas Software производит библиотеки для Visual C++ (MFC):
1. Dundas Ultimate Toolbox - набор компонентов для MFC, по составу
несколько отличающийся от Stingray Objective Toolkit.
2. Dundas Ultimate Grid - сетка данных, конкурент Stingray Objective Grid.
3. Dundas TCP/IP - реализация протоколов POP3,NEWS и т.п.
4. Dundas Chart - диаграммы
и другие продукты
==============================================================================
Q6.А можно пример консольной программы ? _
A6. by Alexander Fedorov (2:5030/437.74)
#include <windows.h>
#include <stdlib.h>
void main()
{
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
SMALL_RECT srct;
CHAR_INFO chiBuffer[160];
COORD coord1, coord2;
char ddd[666];
CharToOem("2:5095/38 - злобный ламерюга", ddd);
DWORD cWritten;
coord1.Y = 0; coord1.X = 0;
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleOutputCharacter(hStdout, ddd, lstrlen(ddd), coord1, cWritten);
for (int i = 0; i {
WORD wColors = 1 + i * 3;
coord1.X = i;
WriteConsoleOutputAttribute(hStdout, , 1, coord1, cWritten);
}
srct.Top = 0; srct.Left = 0; srct.Bottom = 1; srct.Right = 79;
coord1.Y = 0; coord1.X = 0;
coord2.Y = 1; coord2.X = 80;
ReadConsoleOutput(hStdout, chiBuffer, coord2, coord1, );
for (i = 0; i {
srct.Left = (SHORT)((double)(79 - lstrlen(ddd)) * rand() / RAND_MAX);
srct.Top = (SHORT)((double)25 * rand() / RAND_MAX);
srct.Bottom = srct.Top + 1;
WriteConsoleOutput(hStdout, chiBuffer, coord2, coord1, );
}
Sleep(10000);
==============================================================================
Q7. В созданном мастером (VC 6.0 ) win32 Console Application попытка вывести
русский текст дает кракозяблики.Что делать ? _
A7. by Dmitriy Reznitskiy (2:5020/1452.112)
CharToOem, OemToChar - оно?
==============================================================================
Q8. Пытаюсь из своей программы вызвать Word97, для это делаю несколько импортов
и
в результате имею кучу ошибок. Как правильно ? _
A8. by Igor Tkachoff (2:5037/9.37)
// Office.h
#define Uses_MSO2000_
#ifdef Uses_MSO2000
// for Office 2000
#import <mso9.dll>
#import <vbe6ext.olb>
#import <msword9.olb> rename("ExitWindows","_ExitWindows")
#import <excel9.olb> rename("DialogBox","_DialogBox") \
rename("RGB","_RGB") \
exclude("IFont","IPicture")
#import <dao360.dll> rename("EOF","EndOfFile") rename("BOF","BegOfFile")
#import <msacc9.olb>
#else
// for Office 97
#import <mso97.dll>
#import <vbeext1.olb>
#import <msword8.olb> rename("ExitWindows","_ExitWindows")
#import <excel8.olb> rename("DialogBox","_DialogBox") \
rename("RGB","_RGB") \
exclude("IFont","IPicture")
#import <DAO350.DLL> \
rename("EOF","EndOfFile") rename("BOF","BegOfFile")
#import <msacc8.olb>
#endif
Каталоги проставь сам, если надо. Просто я предпочитаю сваливать все
библиотеки в одну кучу. А еще лучше сделать #import один раз, а затем
подключать #include "тыры-пыры.tlh".
P.S. С 2000'ным аккуратнее. Некоторые методы (типа Run, Open, Add) имеют новую
версию. И если хочешь совместимость с 97 то следует вызывать старые версии,
которые называются типа RunOld и т.п.
==============================================================================
Q9. А как отредактировать ресурсы .exe файла ? _
A9.
Это возможно лишь под NT.
==============================================================================
Q10. Как программно получить номер билда своего приложения в VC++? _
A10. by Pavel Zolotuhin (2:5025/60.15)
Штатной возможности нет, поскольку не все одинаково трактуют понятие "номер
билда" и не все одинаково его используют. Однако большинство людей используют
для хранения номера билда конкретного файла ресурсы типа VERSIONINFO, откуда
эту информацию можно потом получить (для отображения в диалоге "О программе"
:-) с помощью функций из version.dll.
Упрощенно говоря, информация о версии файла хранится в VERSIONINFO в виде
четырех чисел, значимость которых убывает слева направо. Например, для
mfc42.dll из поставки Win2k версия файла выглядит как 6.0.8665.0. Здесь первая
цифра, как я понимаю, совпадает с версией продукта (MSVC 6), вторая означает
подверсию (MSVC 6.0), третья - номер билда, а четвертая - я не знаю. В своих
dll-ках и exe-шниках Microsoft постоянно использует эту схему, я - тоже.
Обычно для автоматического увеличения номера версии используются макросы
Visual Studio (== скрипты на VBScript), ковыряющие файл ресурсов проекта. Эти
макросы либо связываются с кнопкой на тулбаре MSDev, либо вызываются из
обработчика события Application_BeforeBuildStart в файле макросов. Примеры
подобных макросов горой лежат на девелоперских сайтах, наподобие
www.codeguru.com. Для себя я сделал собственный, который реализует номер билда
в указанном выше смысле. Вот его исходник (должен работать на MSVC6SP3).
Sub IncVersion()
'DESCRIPTION: Increments file version
Dim oDoc
Dim iVer
Set oDoc = Documents.Open(Application.ActiveProject &".rc", "Text")
if oDoc Is Nothing Then
Exit Sub
End If
oDoc.Selection.FindText "FILEVERSION", dsMatchCase
if Len(oDoc.Selection) = 0 Then
oDoc.Close dsSaveChangesNo
Set oDoc = Nothing
Exit Sub
End If
oDoc.Selection.EndOfLine
oDoc.Selection.FindText ",", dsMatchBackward
oDoc.Selection.CharLeft
oDoc.Selection.WordLeft dsExtend
iVer = oDoc.Selection
iVer = iVer + 1
oDoc.Selection = iVer
oDoc.Selection.FindText """FileVersion""", dsMatchCase
if Len(oDoc.Selection) = 0 Then
oDoc.Close dsSaveChangesNo
Set oDoc = Nothing
Exit Sub
End If
oDoc.Selection.EndOfLine
oDoc.Selection.FindText ",", dsMatchBackward
oDoc.Selection.CharLeft
oDoc.Selection.WordLeft dsExtend
iVer = oDoc.Selection
iVer = iVer + 1
oDoc.Selection = iVer
oDoc.Close dsSaveChangesYes
Set oDoc = Nothing
End Sub
=============================================================================
Q11. Какой функцией можно переключить видеорежим ?
A11. by Alexander Shargin (2:5030/852.22)
Этим занимается ChangeDisplaySettings(...);
Вот тебе пример, который устанавливает разрешение 640x480 (24 bit):
=== Cut ===
DEVMODE md;
ZeroMemory(&md, sizeof(md));
md.dmSize = sizeof(md);
md.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
md.dmBitsPerPel = 24;
md.dmPelsWidth = 640;
md.dmPelsHeight = 480;
ChangeDisplaySettings(&md, 0);
=== Cut ===
Только не повторяй ошибку, которую допустил я, когда писал этот пример:
восстанови исходное разрешение, когда твоя программа будет заканчивать
выполнение.
=============================================================================
Q12. Как вызвать окно выбора папки ?
A12.
Воспользуйтесь следующей функцией:
BOOL FGetDirectory(LPTSTR szDir)
{ BOOL fRet;
TCHAR szPath[MAX_PATH];
LPITEMIDLIST pidl;
LPITEMIDLIST pidlRoot;
LPMALLOC lpMalloc;
BROWSEINFO bi =
{
NULL,
NULL,
szPath,
"Выберите папку",
BIF_RETURNONLYFSDIRS,
NULL,
0L,
0
};
if (0 != SHGetSpecialFolderLocation(HWND_DESKTOP, CSIDL_DRIVES, &pidlRoot))
return FALSE;
if (NULL == pidlRoot)
return FALSE;
bi.pidlRoot = pidlRoot;
pidl = SHBrowseForFolder(&bi);
if (NULL != pidl)
fRet = SHGetPathFromIDList(pidl, szDir);
else
fRet = FALSE; // Get the shell's allocator to free PIDLs
if (!SHGetMalloc(&lpMalloc) && (NULL != lpMalloc))
{
if (NULL != pidlRoot)
{
lpMalloc->Free(pidlRoot);
}
if (NULL != pidl)
{
lpMalloc->Free(pidl);
}
lpMalloc->Release();
}
return fRet;
}
LPTSTR PszAlloc(int cch)
{
return (LPTSTR) LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (cch+1));
}
bool PszDeAlloc(HLOCAL mem_ptr)
{
return (LocalFree(mem_ptr)==NULL) ? true : false;
}
Затем, при необходимости предложить пользователю выбрать папку
используйте примерно такой код:
....
LPTSTR fname;
fname=PszAlloc(250);
FGetDirectory(fname);
......
PszDeAlloc((HLOCAL)fname);
FAQ RU.CBUILDER
Содержание:
1. Как преобразовать AnsiString в char*?
2. Как сделать, чтобы программа на CBuilder3, 4 не требовала .bpl, .dll?
3. Что такое RXLib и где его взять?
4. Как сделать, чтобы окно вело себя, как верхняя панель в билдере,
т.е. ресайзилось только по горизонтали, и только до определенного
минимального размера, а по вертикали размер был фиксированным?
5. Как организовать SplashScreen?
6. Как засунуть иконку в system tray ("туда, где часы" (c))?
7. Как руссифицировать Database Desktop 7?
8. Из-за чего может виснуть С++Builder 3 под Windows 98 (при запуске)?
Он запускался в Windows 95 при 16 цветах, а в Windows 98 никак не хочет.
9. Почему в билдере размер структуры всегда растягивается до кратного 4-ем?
10. Какой-нибудь из CBuilder'ов умеет делать win16 Exe?
11. Как создать компонент по его имени?
12. Почему функция isdigit (да и остальные is*) возвращает некорректные
значения для аргумента в виде русской буквы?
13. Почему при сборке в CB3 с включенным Build With Runtime Packages все
работает, а если отключить, то вылетает с ошибкой, не доходя до
Application->Initialize(). Какие у All соображения на этот счет?
14. Есть функция, которая производит длительные вычисления в цикле.
Хотелось бы иметь возможность ее прервать. Естественно, что пока
вычисления не выходят из цикла никакие контролы не работают....
15. Я переписываю BDE-приложение на другой компьютер, а оно отказывается
работать. Что делать?
16. Как сделать перекодировку CP866 <-> CP1251?
17. Как из Builder'a можно работать с последовательными портами?
Надо сконнектиться с одной железякой по RS-232.
18. Как отследить запуск второй копии приложения?
19. Как на C++ выглядит паскалевский is?
*20. Люди, где в инете Русский Хелп взять на Builder 3.0/WinAPI?
21. Как сделать окно как у WinAMP?
22. Почему не работает код:
Variant v = Variant::CreateObject("Excel.Application");
v.OlePropertySet("Visible",true);
*23. Как работать с OLE-сервером Excel с помощью библиотеки типов?
24. Почему не удается получить интерфейс Workbooks с помощью метода
Workbooks() интерфейса Application_?
25. Кто подскажет, каким образом определяется номер версии программы, с
тем чтобы в "About..." автоматически его вытаскивать (как показать
содер-
жимое ресурса VERSIONINFO).
26. Как определить, работает компонент в design mode или уже в автономной
программе?
27. Как зарегистрировать property editor для __property типа AnsiString?
28. Как сделать так чтобы эхотаг при запуске автоматически открывал
проект с которым я в последний раз работал, а не создавал новый?
29. Я делаю компонент, который в качестве свойства получает указатель
на об'ект TComboBox. Хочется иметь возможность заметить его уничтожение
в дизайнере, для того чтобы не возникало указателя на пустое место и
следуюшего за этим Access Violation. Как это сделать?
30. Кто-нибудь может мне подробно и понятно об'яснить, как мне присвоить
моему компоненту иконку (чтоб в Component Palette красивее стало :) )?
31. Как сделать круглое/овальное/с дыркой/etc. окно?
32. Есть 2 задачи: одна работает в окне ДОС, другая в Windows. Как
организовать обмен между ними, может есть какие-то стандартные буферы
обмена (Клипборд и Файлы не предлагать)?
33. Есть на форме Edit и Button, юзер вводит в Edit какую-нибудь цифирь
(например 20 ), давит на Button и на форме появляется 20 Label-ов.
Как можно сие реализовать? (создание компонента в runtime)
34. Как сделать чтобы программа не отображалась в панели задач?
*35. Как запустить процесс, дождаться окончания его инициализации,
дождаться завершения, получить код возврата?
36. Где можно взять хелп по Win32 API?
37. Столкнулся с проблемой, что TImageList не раборает корректно на
некоторых машинах. К примеру не отрисовываются картинки на ToolBar в
кнопках. Причем на моей машине, где проект создавался - все Ок а вот при
переносе на другую машину начинаются проблемы.
38. Была у меня програмка на BCB3 и там некоторые функции разделялись:
одни в конструкторе формы, другие - в событии формкреэйт. Переполз на
BCB4 и что же конструктор вызывается после события создания формы -
это как? (другой вариант этого вопроса: имеем форму с добавленными мною
полями типа AnsiString. В OnCreate я эти поля заполняю некоторыми
значениями, ставлю breakpoint в OnShow и смотрю эти переменные - они
пустые!)
39. Как грамотно связаться с MS Word (OLE)?
40. Как сделать таймер с интервалом < 1 мс?
41. Как определить количество памяти, доступной Windows и её свободный объём?
42. Как получить список запущенных задач?
43. Как получить список исполняемых процессов?
44. Как узнать загрузку процессора?
45. Нашёл в хэлпе полезную функцию ROUND, а программа не компилируется.
Пишет "Call to undefined function" :( Как же округлять?
*46. Как сменить цвет надписи у TButton?
>Q1: Как преобразовать
AnsiString в char*?
A: У класса AnsiString есть
метод, декларация которого выглядит так:
char* __fastcall c_str() const;
E.g.: char a[10];
AnsiString b="CBuilder";
strcpy(a, b.c_str());
А вообще, все методы AnsiString
достаточно подробно описаны в хелпе. Так что
RTFM :)
>Q2: Как сделать, чтобы
программа на CBuilder3,4 не требовала .bpl, .dll?
A: В Project|Options|Packages
снять галку с Build with runtime packages,
Project|Options|Linker снять галку с Use dynamic RTL.
>Q3: Что такое RXLib
и где его взять?
A(AM): (ответ с разрешения
автора взят из RU.DELPHI.F.A.Q.)
Одна из самых, если не
самая лучшая библиотека общего назначения для
Delphi. Огромное количество компонентов и полезных функций. Полные исходные
тексты. Совместима со всеми Delphi, а также с C++Builder. Великолепные
примеры использования. Исчерпывающие файлы помощи на русском языке.
IMHO -- a must have для любого дельфиста. Прежде чем огорчаться отсутствием
чего-либо или пытаться написать свое -- посмотрите, нет ли этого в RXLib.
Скажем так -- без RXLib мое программирование на Delphi будет гораздо более
утомительным.
Взять можно на http://www.rxlib.com
>Q4: Как сделать, чтобы
окно вело себя, как верхняя панель в билдере,
> т.е. ресайзилось только по горизонтали, и только до определенного
> минимального размера, а по вертикали размер был фиксированным?
A: Надо написать обработчик
сообщения WM_GETMINMAXINFO.
Например, так:
class TForm1 : public TForm
{
//...........
private:
void __fastcall WMGetMinMaxInfo(TMessage& Msg);
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WM_GETMINMAXINFO, TMessage, WMGetMinMaxInfo)
END_MESSAGE_MAP(TForm)
};
void __fastcall TForm1::WMGetMinMaxInfo(TMessage&Mmsg)
{
(LPMINMAXINFO(Msg.LParam))->ptMinTrackSize.x=200;
(LPMINMAXINFO(Msg.LParam))->ptMinTrackSize.y=Height;
(LPMINMAXINFO(Msg.LParam))->ptMaxTrackSize.y=Height;
Msg.Result=0;
}
A: В CB4 можно воспользоваться
свойством Constraints.
>Q5: Как организовать
SplashScreen?
A: 1. Посмотреть на $(BCB)\Examples\DBTasks\MastApp
2. Воспользоваться функцией ShowSplashWindow(...) из RXLib.
3. Написать руками :)
а) Делаешь форму, которая будет изображать SplashScreen;
б) Делаешь WinMain вида:
WINAPI WinMain(HINSTANCE,
HINSTANCE, LPSTR, int)
{
try
{
SplashF=new TSplashF(Application);
SplashF->Show();
SplashF->Update();
Application->Initialize();
//...
SplashF->Close();
delete SplashF;
Application->Run();
//...
>Q6: Как засунуть иконку
в system tray ("туда, где часы" (c))?
A: 1. Воспользоваться компонентом
TRxTrayIcon из RXLib.
2. Посмотреть в хелпе описание на Shell_NotifyIcon(...).
3. Посмотреть на $(BCB)\Examples\Apps\TrayIcon (есть только в CB3,4).
4. Посмотреть на $(BCB)\Examples\Controls\Tray (CB4).
>Q7: Как руссифицировать
Database Desktop 7?
A: [HKEY_CURRENT_USER\Software\Borland\DBD\7.0\Preferences\Properties]
"SystemFont"="MS Sans Serif"
A(IU): Ребят, я давно делаю
под НТ (под 95 не знаю, не пробовал) такую вещь:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage]
"1252"="c_1251.nls"
И все!!! Помогает 100%.
Никаких проблем с "иероглифами" в любых
программах!
>Q8: Из-за чего может
виснуть С++Builder 3 под Windows 98 (при запуске)?
> Он запускался в Windows 95 при 16 цветах, а в Windows 98 никак не
> хочет.
A: Из-за видюхи (особенно
этим страдают S3 VirgeDX). Надо либо убавлять
Hardware Acceleration, либо менять драйверы.
A(AS): [HKEY_CURRENT_CONFIG\Display\Settings]
"BusThrottle"="on"
>Q9: Почему в билдере
размер структуры всегда растягивается до кратного
> 4-ем?
A: Из-за выравнивания (RTFM
Data Alignment).
Чтобы поля структуры выравнивались на 8-ми битную границу, необходимо
использовать следующую конструкцию:
#pragma pack(push, 1)
<structure definition>
#pragma pack(pop)
Менять выравнивание для
всего проекта (Project Options\Advanced Compiler\
Data Alignment) не рекомендуется.
>Q10: Какой-нибудь из
CBuilder'ов умеет делать win16 Exe?
A: Нет.
>Q11: Как создать компонент
по его имени?
A(YH):
#include <typeinfo.h>
#include <stdio.h>
class A {
public:
virtual A *Create(void) = 0;
};
class B1 : A {
public:
B1();
A *Create(void) { return(new B1); }
};
class B2 : A {
public:
B2();
A *Create(void) { return(new B2); }
};
B1::B1() { printf("Create
B1\n"); }
B2::B2() { printf("Create B2\n"); }
// Собственно "создатель"
A *CopyCreate(A *a)
{
if(a && typeid(A).before(typeid(a))) return(a->Create());
else printf("Illegal call\n");
return(NULL);
}
// дальше пример использования
void main( void )
{
B1 *b1 = new B1;
B2 *b2 = new B2;
printf("Call test
b1\n");
B1 *bb1 = dynamic_cast<B1*>(CopyCreate(reinterpret_cast<A*>(b1)));
printf("Call test b2\n");
B2 *bb2 = dynamic_cast<B2*>(CopyCreate(reinterpret_cast<A*>(b2)));
delete b;
delete bb2;
delete b1;
delete b2;
}
-----------------результат запуска-----------
G:\PROJECT.BC5\Test>a.exe
Create B1
Create B2
Call test b1
Create B1
Call test b2
Create B2
Естественно для "полной
культурности" надо понавставлять try/catch или
перекрыть Bad_Cast, но это уже детали :).
A(MR):
class TComponent1* : public
TComponent
{ // Это класс от которого мы будем порождать все наши классы
public:
__fastcall TComponent1( TComponent* Owner ):TComponent(Owner){}
virtual TComponent1* __fastcall Create(TComponent* Owner)=0;
}
class TMyClass1 : public
TComponent1
{
public:
__fastcall TMyClass1(TComponent* Owner):TComponent1(Owner){}
virtual TMyClass1* __fastcall Create(TComponent* Owner)
{return new TMyClass1(Owner);}
// Эта функция создает класс, поскольку все создаваемые классы мои и //
порожденны от TObject проблемм нет, осталось только ее вызвать.
}
Вот функция для создания
класса
TComponent1* __fastcall CreateClass( AnsiString ClsName, TComponent* Owner )
{
TClass cls = GetClass( clsName ); // Это сработает если класс //
зарегистрирован функцией RegisterClasses, я их регистряю в инициализации
// модуля
void * mem = SysGetMem( InstanceSize(cls) );
// для класса, его можно получить, на вскидку не помню
TComponent1* Result = InitInstance(cls, mem);
// В Result уже класс нужного типа (потом можно привести) но конструктор
// не вызвался, память мы отвели в ручную, но класс не проинициализирован
// и вот тут трамбл, как можно изголиться чтобы вызвать конструктор явным
// образом?, но функции вызвать можно, вот и пригодилось:)
// Блин NewInstance борландюки запихнули в привате:(
Result = Result->Create( Owner );
// Класс создан правильно и его можно вернуть освободив память
SysFreeMem( mem );
return Result;
}
A: Если список классов, которые надо создавать по имени, не очень велик,
то можно так:
TControl* CreateControlByName(AnsiString
ClassName, TComponent *Owner)
{
TMetaClass *c=GetClass(ClassName);
if(c==NULL)
throw Exception("Unregistered class.");
if(c==__classid(TButton))
return new TButton(Owner);
if(c==__classid(TEdit))
return new TEdit(Owner);
return NULL;
}
>Q12: Почему функция
isdigit (да и остальные is*) возвращает
> некорректные значения для аргумента в виде русской буквы?
A(YH): Напиши #undef isdigit,
будет вызываться ф-ция с правильным кастингом.
А макры можно вызывать _только_ в формате isdigit((unsigned char)c).
>Q13: Почему при сборке
в CB3 с включенным Build With Runtime Packages все
> работает, а если отключить, то вылетает с ошибкой, не доходя до
> Application->Initialize(). Какие у All соображения на этот счет?
A: В IDE есть глючек, в
результате которого порядок .lib в строке LIBRARIES
.bpr-файла оказывается неправильным (первым обязательно должен идти
vcl35.lib). Из-за этого нарушается порядок инициализации модулей и
глобальных VCL-объектов. В результате при запуске программы имеем
стабильный Access Violation. Для его устранения необходимо поправить строку
ALLLIB .bpr-файла:
ALLLIB = vcl35.lib $(LIBFILES) $(LIBRARIES) import32.lib cp32mt.lib
^^^^^^^^^ вот это надо добавить.
>Q14: Есть функция, которая производит длительные вычисления в цикле.
> Хотелось бы иметь возможность ее прервать. Естественно, что пока
> вычисления не выходят из цикла никакие контролы не работают....
A: Вставить в цикл, в котором
происходят вычисления, вызов
Application->ProcessMessages(); Т.е.:
for(.....
{
// здесь выполняются вычисления
Application->ProcessMessages();
}
A: Вынести вычисления в
отдельный thread.
>Q15: Я переписываю BDE-приложение
на другой компьютер, а оно
> отказывается работать. Что делать?
A(VS): 1. Использовать инсталляционный
пакет, например InstallShield или Wise.
2. Не использовать его. В этом случае нет универсального решения.
Оно будет варьироваться в зависимости от использования BDE в локальном или
серверном режиме, для доступа к Paradox- или DBF-таблицам, использования
локального SQL, версии BDE, и так далее... Здесь приведен пример для наиболее
общего варианта - пятая версия BDE, локальные таблицы, без использования
локального SQL, стандартная кодировка ANSI:
Нужно добавить следующие
файлы из папки BDE к вашему исполняемому модулю:
blw32.dll, idapi32.dll,
idr20009.dll, idpdx32.dll для Paradox-таблиц или
iddbas32.dll для DBF-таблиц, bantam.dll, charset.cvb, usa.btl
Доступ к таблицам надо настроить
не через псевдонимы (alias'ы), а через пути в
файловой системе. В идеале все таблицы храните в папке программы, тогда нужно
только указать имя таблицы без пути.
Приготовленный таким образом
дистрибутив запускается на любой машине без
необходимости инсталляции BDE, максимально устойчив и нечувствителен к смене
имен папок/переинсталляции системы/порчи реестра/влиянии на другие
BDE-приложения. Добавка к основному модулю составляет для этих семи
dll-библиотек ~1030 КБ, после упаковки ~470 КБ.
A(MS):Для того, чтобы установить программу, которая требует BDE, есть несколько
базовых путей, в частности:
1. Создать полноценную программу
инсталляции с помощью продуктов Install
Shield, Wise или подобных. Указанные продукты используются чаще всего и оба
позволяют включить в инсталляцию BDE + базовые настройки (алиасы и пути).
2. Для разных целей можно
сделать инсталляцию BDE отдельным пакетом (в Install
Shield'е это делается более чем элементарно --- в проект не надо добавлять
ничего, кроме поддержки BDE). Удобно в процессе написания программы для одного
пользователя. Первый раз устанавливаешь и настраиваешь BDE, а затем носишь
только новые версии программ. Так же можно при установке Дельфи/Билдера с
компашки снять флажки отовсюду кроме BDE --- в этом случае будет установлена
только BDE.
3. Есть возможность инсталлировать
BDE ручками. Первый этап --- копирование
файлов, второй --- прописывание реестра. Подробно описано в tips'n'tricks y
Акжана, см. http://www.akzhan.midi.ru/devcorner/.
Теперь к вопросу о том,
почему установка BDE --- это не просто прописать одну
опцию в проекте.
Дело в том, что BDE ---
это не просто несколько библиотек динамического доступа
(DLL), это --- целый engine :) достаточно хорошо продуманный для того, чтобы
быть и универсальным и расширяемым. Занимает он в запакованном виде две
дискеты, а в распакованном (+ файлы, которые включать в поставку не нужно) ---
более десяти!
Естественно, не для всех
задач подходит именно BDE (благодаря своим
особенностям). Во-первых, возникают проблемы при работе с DBF форматов Clipper
и Fox. Во-вторых, не для всех программ требуются все возможности BDE, а быть
они должны как можно меньше.
По факту, существует несколько
альтернативных движков, подробнее можно узнать в
ru.delphi.db...
(AA):
...и на сайтах
http://market.kaluga.ru/yra/
Домашняя страница Юрия Бескоровайного. Посвящена работе с базами данных с
помощью сторонних библиотек. На ней Вы найдёте множество полезной информации
о работе с базами данных, компонентах и библиотеках, их ошибках и исправлениях
к ним, а также об адаптации к русскому языку. На особом месте - пакеты от
Advantage.
http://www.kylecordes.com/bag
BDE and MIDAS Alternatives Guide. Информация о различных библиотеках,
позволяющих работать с базами данных без BDE и MIDAS.
Alex Plas (Саша Пляс) - alexplas@chat.ru, plas@yurteh.net
>Q16: Как сделать перекодировку
CP866 <-> CP1251?
A: RTFM CharToOem, CharToOemBuff,
OemToChar, OemToCharBuff.
>Q17: Как из Builder'a
можно работать с последовательными портами?
> Надо сконнектиться с одной железякой по RS-232.
A(IE): Существует компонент
ZComm (free for personal use). Берется на
http://www.rogerssisco.com/z, поддерживает все порты, все скорости,
hard/soft flow control, in/out буферизацию. Передача/прием данных вынесены в
отдельную нитку. При использовании прототипы смотрите в хиддере (в хелпе
есть глючки).
A(CA): Вот кусок из моей работающей программы. Я творчески порезал, надеюсь,
идея ясна.
//---------------------------------------------------------------------------
__fastcall TComPort::TComPort(TComponent* Owner) : TComponent(Owner)
{
OverlappedStructure.Offset = 0;
OverlappedStructure.OffsetHigh = 0;
OverlappedStructure.hEvent = 0;
iComNumber = 2;
iBaudRate = 9600;
hCom = INVALID_HANDLE_VALUE;
}
//---------------------------------------------------------------------------
int __fastcall TComPort::Open(int n)
{
bool ierr;
AnsiString ComName;
ComName = "\\\\.\\COM"+IntToStr(n);
if(hCom != INVALID_HANDLE_VALUE)
Close();
hCom = CreateFile(ComName.c_str(),
GENERIC_READ|GENERIC_WRITE, 0, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, 0);
if (hCom == INVALID_HANDLE_VALUE)
throw Exception("Невозможно открыть порт COM"+IntToStr(n));
SetupComm(hCom, 2048, 2048);
GetCommTimeouts(hCom, &Timeouts);
Timeouts.ReadIntervalTimeout = MAXDWORD;
Timeouts.ReadTotalTimeoutMultiplier = 0;
Timeouts.ReadTotalTimeoutConstant = 0;
Timeouts.WriteTotalTimeoutMultiplier = 0;
Timeouts.WriteTotalTimeoutConstant = 0;
ierr = SetCommTimeouts(hCom, &Timeouts);
if(!ierr) throw Exception("Ошибка
инициализации порта COM"+IntToStr(n));
GetCommState(hCom, &dcbBuf);
dcbBuf.BaudRate = iBaudRate;
dcbBuf.fBinary = true;
dcbBuf.fParity = false;
dcbBuf.ByteSize = 8;
dcbBuf.Parity = 0;
dcbBuf.StopBits = 0;
ierr = SetCommState(hCom, &dcbBuf);
if(!ierr) throw Exception("Ошибка
инициализации порта COM"+IntToStr(n));
ierr = SetCommMask(hCom,
EV_RXCHAR);
if(!ierr) throw Exception("Ошибка
инициализации порта COM"+IntToStr(n));
return iComNumber = n;
}
//---------------------------------------------------------------------------
int __fastcall TComPort::Open(void)
{
return Open(iComNumber);
}
//---------------------------------------------------------------------------
void __fastcall TComPort::Close(void)
{
CloseHandle(hCom);
hCom = INVALID_HANDLE_VALUE;
}
//---------------------------------------------------------------------------
void __fastcall TComPort::FlushBuffers(void)
{
PurgeComm(hCom, PURGE_TXABORT|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_RXCLEAR);
}
//---------------------------------------------------------------------------
DWORD __fastcall TComPort::WriteBlock(void *buf, int count)
{
DWORD realCount;
WriteFile(hCom, buf, count, &realCount, &OverlappedStructure);
return realCount;
}
//---------------------------------------------------------------------------
DWORD __fastcall TComPort::ReadBlock(void *buf, int count)
{
DWORD realCount;
bool bResult = ReadFile(hCom, buf, count, &realCount, &OverlappedStructure);
// if there was a problem,
or the async. operation's still pending ...
if(!bResult)
{
// deal with the error code
switch(GetLastError())
{
case ERROR_HANDLE_EOF:
{
// we're reached the end of the file
// during the call to ReadFile
// code to handle that
throw Exception("1");
}
case ERROR_IO_PENDING:
{
// asynchronous i/o is still in progress
// do something else for a while
Sleep(100);
// check on the results
of the asynchronous read
bResult = GetOverlappedResult(hCom, &OverlappedStructure, &realCount,
false);
// if there was a problem
...
if(!bResult)
{
// deal with the error code
switch(GetLastError())
{
case ERROR_HANDLE_EOF:
{
// we're reached the end of the file
//during asynchronous operation
throw Exception("2");
}
// deal with other error cases
default:
{
throw Exception("3");
}
}
}
} // end case
// deal with other error
cases
default:
{
throw Exception("4");
}
} // end switch
} // end if
return realCount;
}
//---------------------------------------------------------------------------
void __fastcall TComPort::SetBaudRate(int b)
{
GetCommState(hCom, &dcbBuf);
dcbBuf.BaudRate = b;
SetCommState(hCom, &dcbBuf);
}
//---------------------------------------------------------------------------
DWORD __fastcall TComPort::ClearError(void)
{
COMSTAT stCom;
DWORD ierr;
ClearCommError(hCom,&ierr,&stCom);
return ierr;
}
>Q18: Как отследить запуск
второй копии приложения?
A(CA, IR, DGr):
1. Воспользоваться функцией FindWindow(). Ее использование затруднительно если
меняется заголовок окна или есть другое окно с таким же заголовком и
классом окна.
2. Воспользоваться RxLib-овской функцией ActivatePrevInstance, которая
в конце-концов тоже использует эту функцию. Однако ActivatePrevInstance
так же выполняет некоторые полезные :) действия (e.g. активизация предыдущей
копии приложения)
3. Можно создавать семафоры, мутексы, но тогда при некорректном завершении
программы, ты ее больше не запустишь ;)
Пример использования мутекса:
HANDLE hMutex=CreateMutex(NULL,
FALSE, "YourMutexName");
if(GetlastError()==ERROR_ALREADY_EXISTS )
{
// здесь надо бы активизировать предыдущую копию приложения.
// как это сделать, см. ActivatePrevInstance().
}
else
{
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
CloseHandle(hMutex);
}
4. Можно получить имя исполняемого файла для каждого из запущенных процессов,
после чего сравнить его с именем .exe вашего процесса... Недостатки способа:
a) Две копии приложения могут быть запущены из разных мест.
б) Различные методы получения списков запущенных процессов для '9x и NT.
Пример для '9x:
#include <tlhelp32.h>
#include <dos.h>
USERES("Project1.res");
USEFORM("Unit1.cpp", Form1);
//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
HANDLE hSnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe;
pe.dwSize=sizeof(pe);
bool Running=false;
DWORD CurrentProc=GetCurrentProcessId();
if(Process32First(hSnapshot, &pe))
do
{
if(CurrentProc!=pe.th32ProcessID && strcmpi(pe.szExeFile, _argv[0])==0)
{
Running=true;
break;
}
}while(Process32Next(hSnapshot, &pe));
CloseHandle(hSnapshot);
if(Running)
return 1;
try
{
Application->Initialize();
//......
5. Использовать временный
файл:
WINAPI WinMain(HINSTANCE,
HINSTANCE, LPSTR, int)
{
HANDLE hFile = CreateFile("c:\\tempfile.tmp", GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
NULL);
if(hFile == INVALID_HANDLE_VALUE)
return 1;
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
CloseHandle(hFile);
return 0;
}
Это, в принципе, универсальный
способ, устойчивый к некорректному завершению
программы, основным недостатком которого является появление "лишнего"
файла
на диске.
>Q19: Как на C++ выглядит
паскалевский is?
A: dynamic_cast<...>(...);
Пример:
Паскаль: if Screen.Forms[I] is FormClass then begin
C++: if (dynamic_cast<FormClass*>(Screen->Forms[I])){
>Q20: Люди, где в инете
Русский Хелп взять на Builder/WinAPI?
A: На http://www.cbuilder.com.ru
есть следующая информация:
http://www.cbuilder.com.ru/comp/rus_help.zip
- Справка по C++ и
С++Builder 4 на русском языке является первой эскизной версией, содержащей
около 2000 входов, описывающей свыше 500 функций C++, C++Builder и API Windows,
около 200 свойств, методов и событий компонентов, типы данных, исключения и
многое другое. В настоящий момент она, конечно, не дает исчерпывающую
информацию по всем вопросам, которые могут интересовать пользователя. Тем не
менее, авторы справки (Архангельский и К) надеются, что она может помочь в
текущей работе по разработке приложений (во всяком случае, сами авторы активно
используют ее). Ведется работа по созданию более полной и более удобной версии
справки, которая будет распространяться отдельно.
>Q21: Как сделать окно
как у WinAMP?
A(AT): установки формы
= Object Inspector =
BorderIcons=[]
BorderStyle=bsNone
если таскаем за TLabel то
поместить на форму один Label и 3 кнопки SpeedButton
(свернуть, развернуть, закрыть),
в процедуре на событие onMouseDown поместить следующие строчки
// таскаем форму за Label1
void __fastcall TForm1::Label1MouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
const int SC_DRAGMOVE = 0xF012;
if(WindowState!=wsMaximized) // что-бы не таскать развернутое окно
{
ReleaseCapture();
Perform(WM_SYSCOMMAND, SC_DRAGMOVE, 0);
}
}
// на кнопки в событии onClick
// свертывание формы
void __fastcall TForm1::SpeedButton1Click(TObject
*Sender)
{
Perform(WM_SYSCOMMAND,SC_MINIMIZE,0);
}
// развертывание/восстановление
формы
void __fastcall TForm1::SpeedButton2Click(TObject
*Sender)
{
if(WindowState==wsMaximized) //тут не плохо-бы сменить рисунок на кнопке
Perform(WM_SYSCOMMAND,SC_RESTORE,0);
else
Perform(WM_SYSCOMMAND,SC_MAXIMIZE,0);
}
// закрытие формы
void __fastcall TForm1::SpeedButton3Click(TObject
*Sender)
{
Perform(WM_SYSCOMMAND,SC_CLOSE,0);
}
Все объекты могут находиться
на панели (TPanel) - но проще поместить
Bevel на форму.
>Q22: Почему не работает
код:
> Variant v = Variant::CreateObject("Excel.Application");
> v.OlePropertySet("Visible",true);
A(SE): Из-за особенностей
реализации OLE-сервера Excel русской локализации.
В Borland`s examples сказано, что примеры с OLE работают, только если
у вас стоит английская версия Word или Excel.
Необходимо использовать библиотеку типов Excel.
>Q23: Как работать с
OLE-сервером Excel с помощью библиотеки типов?
A(SE): Достаточно выполнить
два шага:
Шаг 1.
*******
Подключаем библиотеку типов Excel к своему проекту.
Выбираем Project|Import Type Library. Нажимаем кнопку Add и ищем в каталоге
с офисом файл xl5en32.olb или excel8.olb для офиса-97. Открываем библиотеку
типов и жмем Ok. ВСВ создает файлы Excel_TLB.cpp и Excel_TLB.h и
подсоединяет их к проекту.
Шаг 2.
*******
Пишем код для запуска Excel:
...
Application_Disp app; // дисп-интерфейс для работы с объектом Application
try {
// пытаемся присоединится к запущенному Excel (а вдруг?)
HRESULT result = app.BindToActive(DIID_Application_);
if(!SUCCEEDED(result)) // в системе нет запущенного Excel
result = app.Bind(DIID_Application_); // запускаем...
if(SUCCEEDED(result)) // если все ок
app.Visible = true; // показываем Excel
}
catch (Exception& e) {
// здесь должна быть обработка ошибки
}
... // работаем с Excel, очень долго и плодотворно
app.Quit(); // ну а здесь принудительно завершаем работу с Excel
A(DG):
Категорически не согласен
!!!
Попробовал я эту TLB - все
клево, только тормоза жуткие при компиляции.
(header TLB огромный, прекомпиляция не спасает) Вполне можно работать на
базе <comobj.hpp>
Вот пример, который у меня
работает, и никаких "особенностей реализации"
#include <comobj.hpp>
Variant app ;
Variant books ;
Variant book ;
Variant sheet ;
//...
app = CreateOleObject("Excel.Application");
books = app.OlePropertyGet("Workbooks");
books.Exec(Procedure("Open")<<"d:\\work\\finder\\files\\22222.xls");
book = books.OlePropertyGet("item",1);
sheet= book.OlePropertyGet("WorkSheets",1);
app.OlePropertySet("Visible", 1);
//...
для чтения/записи ячеек я использую две функции:
Variant __fastcall getValue(int
row,int col)
{
return sheet.OlePropertyGet("Range", toText(row,col) );
}
char* __fastcall toText(int
row,int col)
{
static char cellText[256] ;
cellText[0] = 'A' + col
;
sprintf(&cellText[1],"%d",row+1);
return cellText;
}
void __fastcall setValue(int
row,int col,AnsiString as)
{
Variant r = sheet.OlePropertyGet("Range", toText(row,col) );
r.OlePropertySet("Value", String(as));
}
Все проверено в бою на BCB3
с пачиком: BCB3P1CS.EXE. До пачика были
замечены слеты при возникновении Exception-ов.
A(SE):
По поводу использования
TLB. Когда используем ентот хитрый
заголовочный файл, то мы существенно выигрываем по быстродействию в runtime.
Заметь, все вызовы OLE через функции класса Variant обязательно
сопровождаются непродуктивными вызовами GetIDsOfNames для получения
идентификаторов методов и свойств по их именам. Эта избитая тема обсуждается
во всех книгах по OLE. Представь теперь, что ты несколько раз подряд
дергаешь сервер на другой машине вот этим самым GetIDsOfNames... Жуть Ж:-(.
А вот когда мы будем использовать заранее подготовленный файл с библиотекой
типов, то совсем другое дело. Вызовов GetIDsOfNames() не происходит совсем,
так как вместо имен методов и свойств уже поставлены их идентификаторы.
Я согласен, что компиляция
может несколько и удлиняется, но лучше подождать
на сборке, чем заставлять ждать пользователя, когда он работает с готовой
программой.
Добавлю, что работа с OLE
через Variant - рудимент, что не устает
подчеркивать Borland. Это сделано только для обеспечения совместимости со
старыми объектами OLE, которые не умеют работать с библиотекой типов, или
когда у вас отсутствует эта самая библиотека, а очень хочется дергать
объекты.
Что касается примера с <comobj.hpp>,
прошу уточнить, Какой Excel? Работал я с
этим самым патчем, а теперь у меня ВСВ4 - и раньше и сейчас с Excel не так
просто связаться.
A(AS): Пример работы с Excel.
Пробовалось все на связке builder 3 и
Excel разных версий.
Для успешной работы с русским
excel надо подправить файлы comobj.pas и
oleauto.pas (они лежат в \source\vcl), после чего подключить их к проекту.
У меня по какой-то причине затребовался ffmt, посему ffmt.asm также был
подключен к проекту. Внесенные в исходники VCL изменения действуют, когда в
опциях проекта убрана галка build with runtime packages (или что-то в этом
роде).
comobj.pas:
в районе строки (1326) GetThreadLocale
заменяем на выражение в скобках, в
результате сей фрагмент выглядит так:
Temp := Dispatch.GetIDsOfNames(GUID_NULL,
NameRefs, NameCount,
{ GetThreadLocale,}
((LANG_ENGLISH+SUBLANG_DEFAULT*1024)+SORT_DEFAULT*
65536 ),
DispIDs);
oleauto.pas:
в районе строки (809):
вместо
if Dispatch.GetIDsOfNames(GUID_NULL,
@NameRefs, NameCount,
LOCALE_SYSTEM_DEFAULT, DispIDs) <> 0 then
ставим
if Dispatch.GetIDsOfNames(GUID_NULL,
@NameRefs, NameCount,
((LANG_ENGLISH+SUBLANG_DEFAULT*1024)+SORT_DEFAULT* 65536 ),
DispIDs) <> 0 then
вроде бы все, но мог что-то
подзабыть. если будут вопросы - пишите на
andre538@odusz.elektra.ru
following up a message from
Andrew Smirnov to all:
//.h-файл для работы с excel
//(C) Дмитрий Артемьев.
//dimm@odusz.elektra.ru
//отрисовка рамок у разных
версий excel работает неоднозначно
//---------------------------------------------------------------------------
#ifndef ExServerH
#define ExServerH
//---------------------------------------------------------------------------
//код наличия рамки ячейки
#define BLeft 2 //слева
#define BRight 4 //справа
#define BTop 8 //сверху
#define BBottom 16 //снизу
//линии рамки
#define LNone 0 //рамка отсутствует
#define LSingle 1 //одинарная тонкая
#define LDouble 9 //двойная тонкая
#define LBold 7 //жирная
class ExServer
{
public:
ExServer();
~ExServer();
bool ExcelOpen(); //открывает
Excel
bool ExcelClose(); //закрывает Excel
bool BookOpen( AnsiString &BookName ); //открывает рабочую книгу по имени
bool BookClose( AnsiString &BookName ); //закрывает рабочую книгу по имени
bool BookSave( AnsiString &BookName ); //сохраняет рабочую книгу по имени
bool SheetOpen( AnsiString &SheetName );//открывает лист рабочей книги
//по названию
bool CellSet( AnsiString &CellName, float CellValue );//заносит числовое
//значение в ячейку
//с указанным именем
bool CellSet( AnsiString &CellName, AnsiString &CellText );
//заносит строку
//в ячейку
//с указанным именем
float CellGet( AnsiString &CellName ); //возвращает числовое значение
//ячейки с указанным именем
bool CellRename( AnsiString &CellName, AnsiString &CellName_Old );
//изменяет имя ячейки
bool CellBorderSet( AnsiString &CellName, int Border_Code, int Line_Style
);
//рисует рамку ячейки
bool CellFontSet( AnsiString &CellName, int Font_Size );
//изменяет размер шрифта
private:
Variant var_Excel,
var_Book,
var_Sheet,
var_Cell;
};
#endif
// (C) Дмитрий Артемьев
// dimm@odusz.elektra.ru
// .cpp-файл для работы с excel
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "ExServer.h"
#include <ComObj.hpp>
#include <stdio.h>
#include <math.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
//----------------------------
ExServer::ExServer()
{
}
//Запуск Excel
bool ExServer::ExcelOpen()
{
try
{
var_Excel=CreateOleObject("Excel.Application");
//сделаем Excel видимым
var_Excel.OlePropertySet("Visible",true);
return true;
}
catch(...)
{
MessageBox( 0, "Ошибка при открытии Excel", "Ошибка", MB_OK
);
return false;
}
}
//Закрытие Excel
bool ExServer::ExcelClose()
{
try
{
var_Excel.OleProcedure("Quit");
return true;
}
catch(...)
{
MessageBox( 0, "Ошибка при закрытии Excel", "Ошибка", MB_OK
);
return false;
}
}
//Открытие книги( по имени
)
bool ExServer::BookOpen(AnsiString &BookName)
{
try
{
var_Book=var_Excel.OlePropertyGet("Workbooks").
OlePropertyGet("Open", BookName);
return true;
}
catch(...)
{
MessageBox( 0, "Ошибка при открытии книги", "Ошибка", MB_OK
);
return false;
}
}
//Закрытие книги( по имени )
bool ExServer::BookClose(AnsiString &BookName)
{
try
{
var_Book.OleProcedure("Close");
return true;
}
catch(...)
{
MessageBox( 0, "Ошибка при закрытии книги", "Ошибка", MB_OK
);
return false;
}
}
//Сохранение книги( по имени )
bool ExServer::BookSave(AnsiString &BookName)
{
try
{
var_Book.OleProcedure("Save");
return true;
}
catch(...)
{
MessageBox( 0, "Ошибка при сохранении книги", "Ошибка",
MB_OK );
return false;
}
}
//Окрытие листа( по названию )
//Примечание: до вызова SheetOpen() должна быть открыта
// соответствующая книга вызовом BookOpen();
bool ExServer::SheetOpen(AnsiString &SheetName)
{
try
{
//откроем нужный лист
var_Sheet = var_Book.OlePropertyGet("Worksheets", SheetName);
//сделаем его активным
var_Sheet.OleProcedure("Activate");
return true;
}
catch(...)
{
MessageBox( 0, "Ошибка при открытии листа", "Ошибка", MB_OK
);
return false;
}
}
//Запись в ячейку числа( по имени ячейки )
//Примечание: до вызова CellSet() должен быть открыт
// соответствующий лист вызовом SheetOpen();
bool ExServer::CellSet(AnsiString
&CellName, float CellValue)
{
try
{
var_Sheet.OlePropertyGet("Range", CellName).
OlePropertySet("Value",CellValue);
return true;
}
catch(...)
{
MessageBox( 0, "Ошибка при записи в ячейку", "Ошибка", MB_OK
);
return false;
}
}
//Запись в ячейку строки( по имени ячейки )
//Примечание: до вызова CellSet() должен быть открыт
// соответствующий лист вызовом SheetOpen();
bool ExServer::CellSet(AnsiString
&CellName, AnsiString &CellText)
{
try
{
var_Sheet.OlePropertyGet("Range", CellName).
OlePropertySet("Value",CellText);
return true;
}
catch(...)
{
MessageBox( 0, "Ошибка при записи в ячейку", "Ошибка", MB_OK
);
return false;
}
}
//Чтение из ячейки( по имени ячейки )
//Примечание: до вызова CellGet() должен быть открыт
// соответствующий лист вызовом SheetOpen();
float ExServer::CellGet(AnsiString
&CellName)
{
float CellValue;
try
{
CellValue = var_Sheet.OlePropertyGet("Range", CellName).
OlePropertyGet("Value");
return CellValue;
}
catch(...)
{
MessageBox( 0, "Ошибка при чтении из ячейки", "Ошибка",
MB_OK );
return -1;
}
}
//Переименование ячейки
//Примечание: до вызова CellRename() должен быть открыт
// соответствующий лист вызовом SheetOpen();
bool ExServer::CellRename(AnsiString
&CellName,AnsiString &CellName_Old)
{
//char buffer[50];
try
{
var_Sheet.OlePropertyGet("Range", CellName_Old).
OlePropertySet("Name", CellName);
return true;
}
catch(...)
{
MessageBox( 0, "Ошибка при переименовании ячейки", "Ошибка",
MB_OK );
return false;
}
}
//Рисование рамки( по имени ячейки )
//Примечание: до вызова CellBorderSet() должен быть открыт
// соответствующий лист вызовом SheetOpen();
bool ExServer::CellBorderSet(
AnsiString &CellName, int Border_Code,
int Line_Style )
{
try
{
switch ( Line_Style )
{
//одинарная тонкая линия
case LSingle:
{
//проверим в цикле необходимость рисования
//рамки с каждой стороны ячейки
for( int i=1; i <= 4; i++ )
if( Border_Code & (2<<(i-1)))
var_Sheet.OlePropertyGet("Range", CellName).
OlePropertyGet("Borders", i).
OlePropertySet("LineStyle", LSingle);
return true;
}
//жирная линия
case LBold:
{
for( int i=1; i <= 4; i++ )
if( Border_Code & (2<<(i-1)))
{
var_Cell = var_Sheet.OlePropertyGet("Range", CellName);
var_Cell.OlePropertyGet("Borders", i).
OlePropertySet("LineStyle", LSingle);
var_Cell.OlePropertyGet("Borders",
i).
OlePropertySet("LineStyle", LBold);
}
return true;
}
//двойная тонкая
case LDouble:
{
for( int i=1; i <= 4; i++ )
if( Border_Code & (2<<(i-1)))
var_Sheet.OlePropertyGet("Range", CellName).
OlePropertyGet("Borders", i).
OlePropertySet("LineStyle", LDouble);
return true;
}
//если задан другой стиль
линии рамки
default:
{
for( int i=1; i <= 4; i++ )
if( Border_Code & (2<<(i-1)))
var_Sheet.OlePropertyGet("Range", CellName).
OlePropertyGet("Borders", i).
OlePropertySet("LineStyle", Line_Style);
return true;
}
}
}
catch(...)
{
MessageBox( 0, "Ошибка при рисовании рамки ячейки", "Ошибка",
MB_OK );
return false;
}
}
//Изменение размера шрифта( по имени ячейки )
//Примечание: до вызова CellFontSet() должен быть открыт
// соответствующий лист вызовом SheetOpen();
bool ExServer::CellFontSet(
AnsiString &CellName, int Font_Size )
{
try
{
//обратимся к ячейке по ее имени
var_Cell = var_Sheet.OlePropertyGet("Range", CellName);
//выделим ячейку
var_Cell.OleProcedure("Select");
//изменим размер шрифта
var_Cell.OlePropertyGet("Font").OlePropertySet("Size", Font_Size);
return true;
}
catch(...)
{
MessageBox( 0, "Ошибка при изменении размера шрифта", "Ошибка",
MB_OK );
return false;
}
}
ExServer::~ExServer()
{
}
небольшой пример работы
с Excel на основании вышепреведенного кода
// (C) Дмитрий Артемьев
// dimm@odusz.elektra.ru
екоторые примеры использования
класса ExServer.
1. Открытие Excel:
ExServer *MyExServer;
MyExServer = new ExServer();
MyExServer->ExcelOpen();
2. Открытие книги:
AnsiString BookName = "Test.xls";
MyExServer->BookOpen(BookName);
3. Открытие листа:
AnsiString SheetName =
"Лист1";
MyExServer->SheetOpen(SheetName);
4. Запись числа в ячейку:
AnsiString CellName = "A1";
float CellValue = 1;
MyExServer->CellSet(CellName, CellValue);
5. Запись строки в ячейку:
AnsiString CellName = "A1";
AnsiString CellText = "Test";
MyExServer->CellSet(CellName, CellText);
6. Чтение числа из ячейки:
AnsiString CellName = "A1";
float CellValue = MyExServer->CellGet(CellName);
7. Переименование ячейки:
AnsiString CellName_Old
= "A1";
AnsiString CellName = "test_cell";
MyExServer->CellRename(CellName, CellName_Old);
8. Рисование рамки:
AnsiString CellName = "A1";
//пусть требуется нарисовать
жирную рамку вокруг ячейки "A1":
Border_Code = BLeft|BRight|BTop|BBottom;
Line_Style = LBold;
MyExServer->CellBorderSet( CellName, Border_Code, Line_Style );
9. Изменение размера шрифта
в ячейке:
AnsiString CellName = "A1";
Font_Size = 14;
MyExServer->CellFontSet( CellName, Font_Size );
>Q24: Почему не удается
получить интерфейс Workbooks с помощью метода
>Workbooks() интерфейса Application_?
A(SE): В VBA при вызове
Application.Workbooks() мы получаем собственно
коллекцию книг. А вот если указать аргумент (индекс), то получим элемент
Workbook коллекции Workbooks.
К сожалению, библиотека
Microsoft Excel .OLB не учитывает этих нюансов. А
сервер автоматизации Excel требует четкого указания числа элементов. Т.е.
если вы хотите получить коллекцию Workbooks, вы не ДОЛЖНЫ ПЕРЕДАВАТЬ НА
СЕРВЕР НИКАКИХ АРГУМЕНТОВ! Если мы посмотрим Excel_TLB.H, то увидим
следующий код обращения к серверу в дисп-интерфейсе класса Workbooks:
_GlobalDispT<T>::Workbooks_(TVariant
Index)
{
static _TDispID _dispid(*this, OLETEXT("Workbooks"), DISPID(572));
TAutoArgs<1> _args;
_args[1] = Index /*[VT_VARIANT:0]*/;
OleFunction(_dispid, _args); // передаем аргумент - индекс книги!!!
return _args.GetRetVariant();
}
Т.е. используя этот код,
вы ВСЕГДА ТРЕБУЕТЕ ОТ СЕРВЕРА ЭЛЕМЕНТ
КОЛЛЕКЦИИ -Workbook!!!
Если вы только запустили
Excel, но ничего еще не открыли и не создали, то
откуда же взяться элементам в коллекции Workbooks? Вот сервер и ругается ;-)
Правильнее будет переписать этот метод вот так:
_GlobalDispT<T>::Workbooks_(TVariant
Index)
{
static _TDispID _dispid(*this, OLETEXT("Workbooks"), DISPID(572));
TAutoArgs<0> _args;
OleFunction(_dispid, _args);
return _args.GetRetVariant();
}
Теперь вы получите обратно
объект Workbooks и можете делать с ним все, что
захотите.
>Q25: Кто подскажет,
каким образом определяется номер версии
>программы, с тем чтобы в "About..." автоматически его вытаскивать
(как
>показать содержимое ресурса VERSIONINFO).
A: 1. Воспользоваться классом
TVersionInfo из RxLib.
2. Воспользоваться функцией API GetFileVersionInfo(...). Пример:
void __fastcall TAboutF::FormCreate(TObject
*Sender)
{
DWORD h;
DWORD Size=GetFileVersionInfoSize(Application->ExeName.c_str(), &h);
if(Size==0) return;
char *buf;
buf=(char*)GlobalAlloc(GMEM_FIXED, Size);
if(GetFileVersionInfo(Application->ExeName.c_str(),
h,
Size,
buf)!=0)
{
char *ValueBuf;
UINT Len;
VerQueryValue(buf, "\\VarFileInfo\\Translation", &(void*)ValueBuf,
&Len);
if(Len>=4)
{
AnsiString CharSet=IntToHex((int)MAKELONG(*(int*)(ValueBuf+2), *(int*)ValueBuf),
8);
if(VerQueryValue(buf,
AnsiString("\\StringFileInfo\\"+CharSet+"\\ProductName").c_str(),
&(void*)ValueBuf,
&Len)!=0)
AppName->Caption=ValueBuf;
if(VerQueryValue(buf,
AnsiString("\\StringFileInfo\\"+CharSet+"\\FileVersion").c_str(),
&(void*)ValueBuf,
&Len)!=0)
Version->Caption=ValueBuf;
if(VerQueryValue(buf,
AnsiString("\\StringFileInfo\\"+CharSet+"\\LegalCopyright").c_str(),
&(void*)ValueBuf,
&Len)!=0)
Copyright->Caption=ValueBuf;
if(VerQueryValue(buf,
AnsiString("\\StringFileInfo\\"+CharSet+"\\CompanyName").c_str(),
&(void*)ValueBuf,
&Len)!=0)
Company->Caption=ValueBuf;
}
}
GlobalFree(buf);
}
>Q26: Как определить,
работает компонент в design mode или уже в
> автономной программе?
A: if(ComponentState.Contains(csDesigning))
{
// design time
}
>Q27: Как зарегистрировать
property editor для __property типа AnsiString?
A(ИТ):
#include <dsgnintf.hpp>
#include <typinfo.hpp>
namespace Mycomponent
{
void __fastcall PACKAGE Register()
{
RegisterPropertyEditor(*(GetPropInfo((PTypeInfo)(TObject::ClassInfo(
__classid(TMyComponent))), "AnsiStringProperty")->PropType),
__classid(TMyComponent), "AnsiStringProperty",
__classid(TMyPropEditor));
}
}
Проблема в том, что дельфийский
typeinfo(string) не имеет аналога в CB. Так
искомый typeinfo берется из RTTI самого компонента (там есть входы для всех
published пропертей). Если надо зарегистрировать PropertyEditor не для
конкретного компонента, то подойдет любой с пропертью типа AnsiString
(какой-нибудь TLabel->Caption).
A(NS):
Вот решение данной проблемы:
Первым параметром функции
RegisterPropertyEditor() передаем
следующую функцию:
PTypeInfo AnsiStringTypeInfo(void)
{
PTypeInfo typeInfo = new TTypeInfo;
typeInfo->Name = "AnsiString";
typeInfo->Kind = tkLString;
return typeInfo;
}
Регистрируем редактор свойств
следующим образом:
RegisterPropertyEditor(AnsiStringTypeInfo(),
__classid(TComponent),
"FileName", __classid(TMPFilenameProperty));
>Q28: Как сделать так
чтобы эхотаг при запуске автоматически открывал
> проект с которым я в последний раз работал, а не создавал новый?
A: Поставить галку на Tools
-> Enviroment Options -> Preferences -> Autosave
Options -> Desktop
>Q29: Я делаю компонент,
который в качестве свойства получает указатель
> на об'ект TComboBox. Хочется иметь возможность заметить его уничтожение
> в дизайнере, для того чтобы не возникало указателя на пустое место и
> следуюшего за этим Access Violation. Как это сделать?
См. метод TComponent::Notification(...).
Например:
void __fastcall TMyComponent::Notification(TComponent*
AComponent,
TOperation Operation)
{
// TComboBox *FComboBox; - private член TMyComponent, содержит указатель на
// нужный TComboBox
if(AComponent==FComboBox && Operation==opRemove)
SetComboBox(NULL);
// Вызов метода предка
TParentClass::Notification(AComponent, Operation);
}
>Q30: Кто-нибудь может
мне подробно и понятно об'яснить, как мне присвоить
> моему компоненту иконку (чтоб в Component Palette красивее стало :) )?
A(AS): Создаешь .res файл,
делаешь там битмап по имени класса компонента
(например TMYCOMPONENT) размером 24x24. Левая нижняя точка - цвет
прозрачности. Полученный .res файл подключаешь к проекту.
Q31: Как сделать круглое/овальное/с
дыркой/etc. окно?
A(AB): Используя API функцию
SetWindowRgn(...). Пример:
int __fastcall Sin(int a,
int R)
{
double W=36*3.14159265/180.0; return R*sin(W*a);
}
int __fastcall Cos(int a,
int R)
{
double W=36*3.14159265/180.0; return R*cos(W*a);
}
HRGN __fastcall GetStarReg(int
X, int Y, int R)
{
TPoint P[5];
P[0]=Point(X, Y-R);
P[1]=Point(X-Sin(4, R), Y-Cos(4, R));
P[2]=Point(X-Sin(8, R), Y-Cos(8, R));
P[3]=Point(X-Sin(2, R), Y-Cos(2, R));
P[4]=Point(X-Sin(6, R), Y-Cos(6, R));
return CreatePolygonRgn(P, 5, WINDING);
}
void __fastcall TForm1::FormCreate(TObject
*Sender)
{
int X=Width/2, Y=Height/2;
HRGN R1, R2, R;
R=GetStarReg(X, Y, 100);
for(int i=1;i<10;i+=2)
{
R1=GetStarReg(X-Sin(i, 120), Y-Cos(i, 110), 40);
CombineRgn(R, R, R1, RGN_OR);
}
R1=GetStarReg(X, Y, 30);
CombineRgn(R, R, R1, RGN_DIFF);
R1=CreateEllipticRgn(3,
3, Width-6, Height-6);
R2=CreateEllipticRgn(20, 10, Width-20, Height-10);
CombineRgn(R1, R1,R2, RGN_DIFF);
CombineRgn(R, R, R1, RGN_OR);
SetWindowRgn(Handle, R,
TRUE);
}
Перевод с Delphi в C++Builder
мой (IR).
>Q32: Есть 2 задачи:
одна работает в окне ДОС, другая в Windows. Как
> организовать обмен между ними, может есть какие-то стандартные
> буферы обмена (Клипборд и Файлы не предлагать)?
A(IEr): Попробуй через "трубу"
(pipe). Принцип такой - запускаешь досовскую
программу через CreateProcess, в STARTUPINFO:
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = GetStdHandler(STD_ERROR_HANDLE);
потом
CreatePipe(&hIn, &si.hStdOutput, 0, 0);
CreatePipe(&si.hStdInput, &hOut, 0, 0);
дальше все просто:
for (;;) {
DWORD code;
if (!GetExitCodeThread(pi.hThread, &code)) // pi это структура
return -1; // PROCESS_INFORMATION
if (code == STILL_ACTIVE) { // переданная в CreateProcess
// Читаем с помощью hIn
// Пишем с помощью hOut
}
else
break;
}
CloseHandle(si.hStdInput);
CloseHandle(hOut);
CloseHandle(si.hStdOutput);
CloseHandle(hIn);
Досовская программа читает
из stdin, пишет в stdout.
>Q33: Есть на форме Edit
и Button, юзер вводит в Edit какую-нибудь цифирь
> (например 20 ), давит на Button и на форме появляется 20 Label-ов.
> Как можно сие реализовать? (создание компонента в runtime)
void __fastcall TForm1::Button1Click(TObject
*Sender)
{
int count=Edit1->Text.ToInt();
TLabel *lbl;
for(int i=0;i<count;i++)
{
lbl=new TLabel(this);
lbl->Parent=this;
lbl->Caption=AnsiString("Label")+AnsiString(i);
lbl->Top=i*20;
lbl->Left=10;
}
}
>Q34: Как сделать чтобы
программа не отображалась в панели задач?
A1. ShowWindow(Application->Handle,
SW_HIDE); // прячем
ShowWindow(Application->Handle, SW_SHOW); // показываем
A2. Установить окну Application
стиль WS_EX_TOOLWINDOW:
WINAPI WinMain(HINSTANCE,
HINSTANCE, LPSTR, int)
{
DWORD Style=GetWindowLong(Application->Handle, GWL_EXSTYLE);
Style|=WS_EX_TOOLWINDOW;
SetWindowLong(Application->Handle, GWL_EXSTYLE, Style);
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
>Q35: Как запустить процесс,
дождаться окончания его инициализации,
> дождаться завершения, получить код возврата?
void __fastcall TForm1::Button1Click(TObject
*Sender)
{
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb=sizeof(STARTUPINFO);
si.wShowWindow=SW_SHOWNORMAL;
PROCESS_INFORMATION pi;
DWORD ExitCode;
if(CreateProcess(NULL,
"c:\\windows\\notepad.exe c:\\autoexec.bat",
NULL,
NULL,
FALSE,
CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi)==TRUE)
{
CloseHandle(pi.hThread); // освобождаем ресурсы
WaitForInputIdle(pi.hProcess, INFINITE); // ждем окончания инициализации
// запущенного процесса.*
WaitForSingleObject(pi.hProcess, INFINITE); // ждем завершения процесса
GetExitCodeProcess(pi.hProcess, &ExitCode); // получаем код возврата
CloseHandle(pi.hProcess); // освобождаем ресурсы
}
}
*) Под окончанием инициализации
понимается момент, когда процесс начинает
ожидать команд от пользователя.
>Q36: Где можно взять
хелп по Win32 API?
В поставку CBuilder'а входит
Win32 SDK Reference, содержащий описание
функций API. Для его установки при инсталляции необходимо поставить (или не
снимать :) галочку на MS SDK Help Files. Для вызова справки по F1 необходимо
подключить эти справочные файлы с помощью программы OpenHelp, входящей в
поставку CBuilder'а. Или вызывать руками через
Programs\Borland C++ Builder\Help\MS SDK Help Files\Win32 SDK Reference.
"Родной" хелп берется на msdn.microsoft.com или на дисках 4-5 из
поставки MS Visual Studio.
>Q37: Столкнулся с проблемой,
что TImageList не раборает корректно на
> некоторых машинах. К примеру не отрисовываются картинки на ToolBar в
> кнопках. Причем на моей машине, где проект создавался - все Ок а вот
> при переносе на другую машину начинаются проблемы.
Необходимо обновить comctl32.dll
на машинах, где наблюдаются проблемы.
>Q38: Была у меня програмка
на BCB3 и там некоторые функции разделялись:
> одни в конструкторе формы, другие - в событии формкреэйт. Переполз на
> BCB4 и что же конструктор вызывается после события создания формы -
> это как? (другой вариант этого вопроса: имеем форму с добавленными мною
> полями типа AnsiString. В OnCreate я эти поля заполняю некоторыми
> значениями, ставлю breakpoint в OnShow и смотрю эти переменные - они
> пустые!)
Необходимо у всех форм/datamodule'ей
поставить OldCreateOrder = false;
>Q39: Как грамотно связаться
с MS Word (OLE)?
A(SE): С Вордом никаких
проблем для связи через OLE нет. Вот давно обещал
многим законченный класс для вызова Ворда из СВ - пожалуйста, пользуйтесь...
Цеплял я его уже к проектам 10, и все пашет и на 95, и на 98 (на NT не
пробовал).
(MessageBox - моя функция,
поменяйте на похожую из ВС, облом искать
исходник)
H:
//--------------------------------------------------------------------------
#ifndef MSWordH
#define MSWordH
//--------------------------------------------------------------------------
void __fastcall CopyRTFToClipboard(AnsiString buf);
//--------------------------------------------------------------------------
enum WinwordColor { mswColAuto, mswColBlack, mswColBlue, mswColCyan,
mswColGreen, mswColMagenta,
mswColRed, mswColYellow, mswColWhite, mswColDarkBlue,
mswColDarkCyan,
mswColDarkGreen, mswColDarkMagenta, mswColDarkRed,
mswColDarkYellow,
mswColDarkGray, mswColLightGray };
enum WinwordTabType { mswTabLeft, mswTabCenter, mswTabRight, mswTabDecimal,
mswTabBar };
enum WinwordAlign { mswAlgLeft, mswAlgCentered, mswAlgRight,
mswAlgJustified };
enum WinwordSpacing { mswSpSingle, mswSpLines, mswSpDouble };
//--------------------------------------------------------------------------
class TMSWord
{
private:
Variant msWord;
AnsiString msWordTitle;
bool __fastcall Run();
public:
__fastcall ~TMSWord();
bool __fastcall Create(AnsiString
fileName);
bool __fastcall GotoBookmark(AnsiString mark);
void __fastcall InsertText(AnsiString str);
void __fastcall InsertTextEOL(AnsiString str);
void __fastcall Paste();
void __fastcall Restore();
bool __fastcall RunApplication(AnsiString dir);
void __fastcall SetFontFormat(TFont *f,WinwordColor color = mswColAuto);
void __fastcall SetFontFormat(char *fontName,int fontSize,TFontStyles
style,
WinwordColor fontColor = mswColAuto);
void __fastcall SetBookmark(AnsiString mark);
void __fastcall SetParagraphFormat(int leftIndent = 0,int rightIndent =
0,
int before = 0,int after = 0,
WinwordSpacing lineSpacing =
mswSpSingle,
WinwordAlign align = mswAlgLeft);
void __fastcall SetTabs(int *tabs,int num,WinwordTabType type =
mswTabLeft);
void __fastcall Exit();
};
//--------------------------------------------------------------------------
#endif
CPP:
//---------------------------
#include <vcl.h>
#pragma hdrstop
#include <comobj.hpp>
#include <clipbrd.hpp>
#include <utilcls.h>
#include <vclutils.hpp>
#include "RxShell.hpp"
#include "MSWord.h"
#include "MSWordLoc.h"
#include "VMessage.h"
#pragma package(smart_init)
#define START "Start"
#define WORD_EXE "WinWord.exe"
//**************************************************************************
// Копирование текста в буфер
//--------------------------------------------------------------------------
void __fastcall CopyRTFToClipboard(AnsiString buf)
{
// регистрируем формат RichText
Word cfRTF = (Word)RegisterClipboardFormat(TEXT("Rich Text Format"));
Clipboard()->Open();
int nTextLen = (buf.Length() + 1) * sizeof(TCHAR);
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE,nTextLen);
if(hGlobal != NULL) {
void *lpText = GlobalLock(hGlobal);
memcpy(lpText,buf.c_str(),nTextLen);
Clipboard()->Clear();
GlobalUnlock(hGlobal);
Clipboard()->SetAsHandle(cfRTF,(int)hGlobal);
}
Clipboard()->Close();
}
// Класс TMSWord
//**************************************************************************
// Деструктор
//--------------------------------------------------------------------------
__fastcall TMSWord::~TMSWord()
{
msWord.Clear();
}
// Создание объекта и открытие файла
//--------------------------------------------------------------------------
bool __fastcall TMSWord::Create(AnsiString fileName)
{
// если объект уже создан
if(!msWord.IsEmpty()) {
try {
// пытаемся активизировать Word
if((short)msWord.Exec(PropertyGet("AppIsRunning") << msWordTitle)
== -1)
msWord.Exec(Procedure("AppRestore") << msWordTitle);
}
catch (EOleSysError& e) {
// если окно с документом (не Word) было закрыто пользователем, то
ErrorCode = 0
// выходим из catch сразу на загрузку документа (без запуска
Word-a)
if(e.ErrorCode) {
// перехватываем событие, когда Word уже был запущен программой
// а потом закрыт пользователем
if(e.ErrorCode != 0x800706BA) {
MessageBox(e.Message + LoadStr(MSWORD_ERROR1),msError);
return false;
}
if(!Run()) // запускаем Word повторно
return false;
}
}
}
// объект еще не создан
else if(!Run()) // пытаемся запустить
return false;
// объект успешно создан, открываем файл
try {
msWord.Exec(Procedure("AppShow"));
msWord.Exec(Procedure("FileNew") << fileName);
// получаем заголовок созданного окна
msWordTitle = AnsiString("Microsoft Word - ") +
msWord.Exec(PropertyGet("WindowName"));
return true;
}
catch (Exception& e) {
MessageBox(e.Message + LoadStr(MSWORD_ERROR2),msError);
}
return false;
}
// Закрытие Word
//--------------------------------------------------------------------------
void __fastcall TMSWord::Exit()
{
try {
if(!msWord.IsEmpty() &&
MessageBox(LoadStr(MSWORD_CONFIRM),msConfirm_YesNo) == IDYES)
msWord.Exec(Procedure("AppClose"));
}
catch (...) {
}
}
// Переход на закладку
//--------------------------------------------------------------------------
bool __fastcall TMSWord::GotoBookmark(AnsiString mark)
{
try {
msWord.Exec(Procedure("EditGoto") <<
NamedParm("Destination",mark.c_str()));
return true;
}
catch (Exception& e) {
MessageBox(e.Message + "\nBookmark: " + mark,msError);
}
return false;
}
// Вставка текста в текущую позицию курсора
//--------------------------------------------------------------------------
void __fastcall TMSWord::InsertText(AnsiString str)
{
msWord.Exec(Procedure("Insert") << str);
}
// Вставка текста в текущую позицию курсора с переходом на новую строку
//--------------------------------------------------------------------------
void __fastcall TMSWord::InsertTextEOL(AnsiString str)
{
msWord.Exec(Procedure("Insert") << (str + '\n'));
}
// Вставка текста в текущую позицию курсора
//--------------------------------------------------------------------------
void __fastcall TMSWord::Paste()
{
msWord.Exec(Procedure("EditPaste"));
}
// Восстановление (активация) Word
//--------------------------------------------------------------------------
void __fastcall TMSWord::Restore()
{
try {
msWord.Exec(Procedure("AppRestore") << msWordTitle);
msWord.Exec(Procedure("AppMaximize") << msWordTitle <<
1); //
максимизируем
}
catch (EOleSysError&) {
MessageBox(LoadStr(MSWORD_INFO),msInfo);
}
}
// Собственно запуск Word-a как OLE-сервера
//--------------------------------------------------------------------------
bool __fastcall TMSWord::Run()
{
bool isWordRunning = true;
// сначала пытаемся переключиться на активный Word
try {
msWord = Variant::GetActiveObject("Word.Basic");
}
catch (EOleSysError& e) {
isWordRunning = false;
}
if(!isWordRunning) { // нет запущенного Word
try {
msWord = Variant::CreateObject("Word.Basic");
msWord.Exec(Procedure("AppMaximize") << 1); // максимизируем
}
catch (EOleSysError& e) {
MessageBox(e.Message + LoadStr(MSWORD_ERROR1),msError);
return false;
}
}
return true;
}
// Запуск Word-a как приложения
//--------------------------------------------------------------------------
bool __fastcall TMSWord::RunApplication(AnsiString dir)
{
if(FileExecute(START,WORD_EXE,dir,esNormal) <= 32) {
MessageBox(LoadStr(MSWORD_ERROR1),msError);
return false;
}
Delay(20000);
return true;
}
// Задание характеристик шрифта
//--------------------------------------------------------------------------
void __fastcall TMSWord::SetFontFormat(TFont *f,WinwordColor color)
{
msWord.Exec(Procedure("FormatFont") <<
NamedParm("Points",f->Size) <<
NamedParm("Font",f->Name.c_str()) <<
NamedParm("Color",color));
if(f->Style.Contains(fsBold))
msWord.Exec(Procedure("Bold") << 1);
if(f->Style.Contains(fsItalic))
msWord.Exec(Procedure("Italic") << 1);
if(f->Style.Contains(fsUnderline))
msWord.Exec(Procedure("Underline") << 1);
if(f->Style.Contains(fsStrikeOut))
msWord.Exec(Procedure("Strikethrough") << 1);
}
// Задание характеристик шрифта
//--------------------------------------------------------------------------
void __fastcall TMSWord::SetFontFormat(char *fontName,int
fontSize,TFontStyles style,WinwordColor fontColor)
{
TFont *f = new TFont();
f->Name = fontName;
f->Size = fontSize;
f->Style = style;
SetFontFormat(f,fontColor);
delete f;
}
// Задание имени закладки
//--------------------------------------------------------------------------
void __fastcall TMSWord::SetBookmark(AnsiString mark)
{
msWord.Exec(Procedure("EditBookmark") << NamedParm("Name",mark)
<<
NamedParm("Add",1));
}
// Задание характеристик абзаца
//--------------------------------------------------------------------------
void __fastcall TMSWord::SetParagraphFormat(int leftIndent,int rightIndent,
int before,int after,
WinwordSpacing lineSpacing,
WinwordAlign align)
{
msWord.Exec(Procedure("FormatParagraph") <<
NamedParm("LeftIndent",AnsiString(leftIndent).c_str()) <<
NamedParm("RightIndent",AnsiString(rightIndent).c_str()) <<
NamedParm("Before",before) <<
NamedParm("After",after) <<
NamedParm("LineSpacing",lineSpacing) <<
NamedParm("Alignment",align));
}
// Установка позиций табуляции
//--------------------------------------------------------------------------
void __fastcall TMSWord::SetTabs(int *tabs,int num,WinwordTabType type)
{
for(int i = 0;i < num;i++)
msWord.Exec(Procedure("FormatTabs") <<
NamedParm("Position",AnsiString(tabs[i]).c_str()) <<
NamedParm("Align",type) <<
NamedParm("Set",1));
}
>Q40: Как сделать таймер
с интервалом < 1 мс?
A(VK): Использовать Performance
Counter.
Функцией QueryPerformanceFrequency
получаем частоту счётчика,
которая, как правило, выше 1Мгц, создаём отдельный поток и в цикле
функцией QueryPerformanceCounter считываем его значение.
Практически, на P200 достаточно
точно определяются интервалы в 20 мкс.
>Q41: Как определить
количество памяти, доступной Windows и её свободный объём?
A(VK): Использовать функцию
API GlobalMemoryStatus(...).
>Q42: Как получить список
запущенных задач?
A(VK): Надо составить список
главных окон приложений (top-level windows). Окно
считается главным если:
0. Имеет заголовок
1. Видимо
2. Не имеет родителя
Приходим к следующему коду:
bool __stdcall EnumProc(HWND
hWnd,long) {
char buffer[100];
if(hWnd==NULL)
return false;
GetWindowText(hWnd,buffer,sizeof(buffer));
if (buffer[0]
&& IsWindowVisible(hWnd)
&& GetWindowLong(hWnd,GWL_HWNDPARENT)==0) {
// Ваш код
};
return true;
}
В нужном месте вызываем
его
EnumWindows((WNDENUMPROC)EnumProc,0);
>Q43: Как получить список
исполняемых процессов?
A(VK): Можно посмотреть
в $(BCB)\Examples\Apps\Procview, а можно и по другому.
Под W95/98:
#include <tlhelp32.h>
PROCESSENTRY32 PC32;
hnd=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if ((int)hnd==-1)
return;
PC32.dwSize=sizeof(PC32);
i=Process32First(hnd,&PC32);
while (i) {
// Ваш код
// PID процесса берётся из PC32.th32ProcessID;
// Имя файла через ExtractFileName(PC32.szExeFile);
i=Process32Next(hnd,&PC32);
};
CloseHandle(hnd);
Под NT:
DWORD PIDStack[512];
DWORD modNeeded;
EnumProcesses(PIDStack,sizeof(PIDStack),&modNeeded);
>Q44: Как узнать загрузку
процессора?
A(VK): Под W95/98 :
HKEY CPULoadKey;
int CPULoad;
ULONG Type = REG_DWORD;
DWORD CPULoadSize = sizeof (CPULoad);
Один раз в начале программы:
RegOpenKey(HKEY_DYN_DATA,"PerfStats\\StartStat",&CPULoadKey);
RegQueryValueEx(CPULoadKey,"KERNEL\\CPUUsage",NULL, &Type, (LPBYTE)&CPULoad,
&CPULoadSize);
RegCloseKey(CPULoadKey);
RegOpenKey(HKEY_DYN_DATA,"PerfStats\\StatData",&CPULoadKey);
По мере необходимости
RegQueryValueEx(CPULoadKey,"KERNEL\\CPUUsage",NULL, &Type, (LPBYTE)&CPULoad,
&CPULoadSize);
CPULoad содержит результат в процентах.
Один раз в конце
RegCloseKey(CPULoadKey);
RegOpenKey(HKEY_DYN_DATA,"PerfStats\\StopStat",&CPULoadKey);
RegQueryValueEx(CPULoadKey,"KERNEL\\CPUUsage",NULL, &Type, (LPBYTE)&CPULoad,
&CPULoadSize);
RegCloseKey(CPULoadKey);
Под NT либо использем PDH.dll,
но она мало у кого есть, либо начинаем
шаманские пляски вокруг performance keys в реестре. Пример есть на
www.codepile.com, файл ntcounters.cpp.
>45: Нашёл в хэлпе полезную
функцию ROUND, а программа не компилируется.
Пишет "Call to undefined function" :( Как же округлять?
A(VK): Функция ROUND (как,
впрочем, и TRUNC) принадлежит OCX-контролу
F1Book. Пишем свою:
#include <math.h>
double Round(double Argument,
int Precision)
{
double div = 1.0;
if(Precision >= 0)
while(Precision--)
div *= 10.0;
else
while(Precision++)
div /= 10.0;
return floor(Argument * div + 0.5) / div;
}
Hint: точность может быть
отрицательной. Round(1234,-2)==1200.
>46: Как сменить цвет
надписи у TButton?
Никак. Используйте, например,
TBitBtn.
|