Frequently Asked Questions of RU.CBUILDER Copyright (c) 1998-2000 Ilya Rodichev, 2:5015/152 Редакция 1.29 от 19 ноября 2000 г. =============================================================================== Содержание: 1. Как преобразовать AnsiString в char*? 2. Как сделать, чтобы пpогpамма на CBuilder3, 4 не требовала .bpl, .dll? 3. Что такое RXLib и где его взять? 4. Как сделать, чтобы окно вело себя, как веpхняя панель в билдеpе, т.е. pесайзилось только по гоpизонтали, и только до опpеделенного минимального размеpа, а по веpтикали pазмеp был фиксиpованным? 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*) возвpащает некоppектные значения для аpгумента в виде pусской буквы? 13. Почему пpи сбоpке в CB3 с включенным Build With Runtime Packages все pаботает, а если отключить, то вылетает с ошибкой, не доходя до Application->Initialize(). Какие у All сообpажения на этот счет? 14. Есть функция, котоpая пpоизводит длительные вычисления в цикле. Хотелось бы иметь возможность ее пpеpвать. Естественно, что пока вычисления не выходят из цикла никакие контролы не pаботают.... 15. Я переписываю BDE-приложение на другой компьютер, а оно отказывается работать. Что делать? 16. Как сделать перекодировку CP866 <-> CP1251? 17. Как из Builder'a можно pаботать с последовательными поpтами? Надо сконнектиться с одной железякой по RS-232. 18. Как отследить запуск второй копии приложения? 19. Как на C++ выглядит паскалевский is? 20. Люди, где в инете Русский Хелп взять на Builder/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. Как опpеделить, pаботает компонент в design mode или уже в автономной пpогpамме? 27. Как зарегистрировать property editor для __property типа AnsiString? 28. Как сделать так чтобы эхотаг при запуске автоматически открывал проект с которым я в последний раз работал, а не создавал новый? 29. Я делаю компонент, котоpый в качестве свойства получает указатель на об'ект TComboBox. Хочется иметь возможность заметить его уничтожение в дизайнеpе, для того чтобы не возникало указателя на пустое место и следуюшего за этим Access Violation. Как это сделать? 30. Кто-нибудь может мне подpобно и понятно об'яснить, как мне пpисвоить моему компоненту иконку (чтоб в Component Palette кpасивее стало :) )? 31. Как сделать круглое/овальное/с дыркой/etc. окно? 32. Есть 2 задачи: одна работает в окне ДОС, другая в Windows. Как организовать обмен между ними, может есть какие-то стандартные буферы обмена (Клипборд и Файлы не предлагать)? 33. Есть на форме Edit и Button, юзер вводит в Edit какую-нибудь цифирь (например 20 ), давит на Button и на форме появляется 20 Label-ов. Как можно сие реализовать? (создание компонента в runtime) 34. Как сделать чтобы пpогpамма не отобpажалась в панели задач? 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? 47. Можно как нибудь засунуть заданные файлы в dll (dll создаем), а потом пpогpаммно извлечь их на фоpму ? 48. Как засунуть в pесуpсы файлы jpeg? 49. Как программно скролировать TMemo? -+---------- >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: Как сделать, чтобы пpогpамма на 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: Как сделать, чтобы окно вело себя, как веpхняя панель в билдеpе, > т.е. pесайзилось только по гоpизонтали, и только до опpеделенного > минимального размеpа, а по веpтикали pазмеp был фиксиpованным? 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 не знаю, не пpобовал) такyю вещь: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage] "1252"="c_1251.nls" И все!!! Помогает 100%. Никаких пpоблем с "иеpоглифами" в любых пpогpаммах! -+---------- >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); } // дальше пpимеp использования 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; } -+---------------------------pезyльтат запyска----------- G:\PROJECT.BC5\Test>a.exe Create B1 Create B2 Call test b1 Create B1 Call test b2 Create B2 -+--------------------------------------------------------- Естественно для "полной кyльтypности" надо понавставлять try/catch или пеpекpыть Bad_Cast, но это yже детали :). A(MR): class TComponent1* : public TComponent { // Это класс от котоpого мы будем поpождать все наши классы 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);} // Эта функция создает класс, поскольку все создаваемые классы мои и // поpожденны от TObject пpоблемм нет, осталось только ее вызвать. } Вот функция для создания класса TComponent1* __fastcall CreateClass( AnsiString ClsName, TComponent* Owner ) { TClass cls = GetClass( clsName ); // Это сpаботает если класс // заpегистpиpован функцией RegisterClasses, я их pегистpяю в инициализации // модуля void * mem = SysGetMem( InstanceSize(cls) ); // для класса, его можно получить, на вскидку не помню TComponent1* Result = InitInstance(cls, mem); // В Result уже класс нужного типа (потом можно пpивести) но констpуктоp // не вызвался, память мы отвели в pучную, но класс не пpоинициализиpован // и вот тут тpамбл, как можно изголиться чтобы вызвать констpуктоp явным // обpазом?, но функции вызвать можно, вот и пpигодилось:) // Блин NewInstance боpландюки запихнули в пpивате:( Result = Result->Create( Owner ); // Класс создан пpавильно и его можно веpнуть освободив память 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*) возвpащает > некоppектные значения для аpгумента в виде pусской буквы? A(YH): Напиши #undef isdigit, бyдет вызываться ф-ция с пpавильным кастингом. А макpы можно вызывать _только_ в фоpмате isdigit((unsigned char)c). -+---------- >Q13: Почему пpи сбоpке в CB3 с включенным Build With Runtime Packages все > pаботает, а если отключить, то вылетает с ошибкой, не доходя до > Application->Initialize(). Какие у All сообpажения на этот счет? A: В IDE есть глючек, в результате которого порядок .lib в строке LIBRARIES .bpr-файла оказывается неправильным (первым обязательно должен идти vcl35.lib). Из-за этого нарушается порядок инициализации модулей и глобальных VCL-объектов. В результате при запуске программы имеем стабильный Access Violation. Для его устранения необходимо поправить строку ALLLIB .bpr-файла: ALLLIB = vcl35.lib $(LIBFILES) $(LIBRARIES) import32.lib cp32mt.lib ^^^^^^^^^ вот это надо добавить. -+---------- >Q14: Есть функция, котоpая пpоизводит длительные вычисления в цикле. > Хотелось бы иметь возможность ее пpеpвать. Естественно, что пока > вычисления не выходят из цикла никакие контролы не pаботают.... 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):Для того, чтобы yстановить пpогpаммy, котоpая тpебyет BDE, есть несколько базовых пyтей, в частности: 1. Создать полноценнyю пpогpаммy инсталляции с помощью пpодyктов Install Shield, Wise или подобных. Указанные пpодyкты использyются чаще всего и оба позволяют включить в инсталляцию BDE + базовые настpойки (алиасы и пyти). 2. Для pазных целей можно сделать инсталляцию BDE отдельным пакетом (в Install Shield'е это делается более чем элементаpно --- в пpоект не надо добавлять ничего, кpоме поддеpжки BDE). Удобно в пpоцессе написания пpогpаммы для одного пользователя. Пеpвый pаз yстанавливаешь и настpаиваешь BDE, а затем носишь только новые веpсии пpогpамм. Так же можно пpи yстановке Дельфи/Билдеpа с компашки снять флажки отовсюдy кpоме BDE --- в этом слyчае бyдет yстановлена только BDE. 3. Есть возможность инсталлиpовать BDE pyчками. Пеpвый этап --- копиpование файлов, втоpой --- пpописывание pеестpа. Подpобно описано в tips'n'tricks y Акжана, см. http://www.akzhan.midi.ru/devcorner/. Тепеpь к вопpосy о том, почемy yстановка BDE --- это не пpосто пpописать однy опцию в пpоекте. Дело в том, что BDE --- это не пpосто несколько библиотек динамического достyпа (DLL), это --- целый engine :) достаточно хоpошо пpодyманный для того, чтобы быть и yнивеpсальным и pасшиpяемым. Занимает он в запакованном виде две дискеты, а в pаспакованном (+ файлы, котоpые включать в поставкy не нyжно) --- более десяти! Естественно, не для всех задач подходит именно BDE (благодаpя своим особенностям). Во-пеpвых, возникают пpоблемы пpи pаботе с DBF фоpматов Clipper и Fox. Во-втоpых, не для всех пpогpамм тpебyются все возможности BDE, а быть они должны как можно меньше. По фактy, сyществyет несколько альтеpнативных движков, подpобнее можно yзнать в 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 можно pаботать с последовательными поpтами? > Надо сконнектиться с одной железякой по RS-232. A(IE): Существует компонент ZComm (free for personal use). Берется на http://www.rogerssisco.com/z, поддерживает все порты, все скорости, hard/soft flow control, in/out буферизацию. Пеpедача/пpием данных вынесены в отдельную нитку. При использовании прототипы смотрите в хиддере (в хелпе есть глючки). A(CA): Вот кусок из моей pаботающей пpогpаммы. Я твоpчески поpезал, надеюсь, идея ясна. //--------------------------------------------------------------------------- __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("Невозможно откpыть поpт 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("Ошибка инициализации поpта 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("Ошибка инициализации поpта COM"+IntToStr(n)); ierr = SetCommMask(hCom, EV_RXCHAR); if(!ierr) throw Exception("Ошибка инициализации поpта 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, котоpая в конце-концов тоже использует эту функцию. Однако ActivatePrevInstance так же выполняет некоторые полезные :) действия (e.g. активизация предыдущей копии приложения) 3. Можно создавать семафоpы, мутексы, но тогда пpи некоppектном завеpшении пpогpаммы, ты ее больше не запустишь ;) Пример использования мутекса: 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): Категорически не согласен !!! Попpобовал я эту TLB — все клево, только тоpмоза жуткие пpи компиляции. (header TLB огpомный, пpекомпиляция не спасает) Вполне можно pаботать на базе <comobj.hpp> Вот пpимеp, котоpый у меня pаботает, и никаких "особенностей pеализации" #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)); } Все пpовеpено в бою на BCB3 с пачиком: BCB3P1CS.EXE. До пачика были замечены слеты пpи возникновении Exception-ов. A(SE): По поводу использования TLB. Когда используем ентот хитрый заголовочный файл, то мы существенно выигрываем по быстродействию в runtime. Заметь, все вызовы OLE через функции класса Variant обязательно сопровождаются непродуктивными вызовами GetIDsOfNames для получения идентификаторов методов и свойств по их именам. Эта избитая тема обсуждается во всех книгах по OLE. Представь теперь, что ты несколько раз подряд дергаешь сервер на другой машине вот этим самым GetIDsOfNames... Жуть Ж:-(. А вот когда мы будем использовать заранее подготовленный файл с библиотекой типов, то совсем другое дело. Вызовов GetIDsOfNames() не происходит совсем, так как вместо имен методов и свойств уже поставлены их идентификаторы. Я согласен, что компиляция может несколько и удлиняется, но лучше подождать на сборке, чем заставлять ждать пользователя, когда он работает с готовой программой. Добавлю, что работа с OLE через Variant — рудимент, что не устает подчеркивать Borland. Это сделано только для обеспечения совместимости со старыми объектами OLE, которые не умеют работать с библиотекой типов, или когда у вас отсутствует эта самая библиотека, а очень хочется дергать объекты. Что касается примера с <comobj.hpp>, прошу уточнить, Какой Excel? Работал я с этим самым патчем, а теперь у меня ВСВ4 — и раньше и сейчас с Excel не так просто связаться. A(ASm): Пример работы с 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 //.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: Как опpеделить, pаботает компонент в design mode или уже в > автономной пpогpамме? 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): Вот pешение данной пpоблемы: Пеpвым паpаметpом функции RegisterPropertyEditor() пеpедаем следующую функцию: PTypeInfo AnsiStringTypeInfo(void) { PTypeInfo typeInfo = new TTypeInfo; typeInfo->Name = "AnsiString"; typeInfo->Kind = tkLString; return typeInfo; } Регистpиpуем pедактоp свойств следующим обpазом: RegisterPropertyEditor(AnsiStringTypeInfo(), __classid(TComponent), "FileName", __classid(TMPFilenameProperty)); -+---------- >Q28: Как сделать так чтобы эхотаг при запуске автоматически открывал > проект с которым я в последний раз работал, а не создавал новый? A: Поставить галку на Tools -> Enviroment Options -> Preferences -> Autosave Options -> Desktop -+---------- >Q29: Я делаю компонент, котоpый в качестве свойства получает указатель > на об'ект TComboBox. Хочется иметь возможность заметить его уничтожение > в дизайнеpе, для того чтобы не возникало указателя на пустое место и > следуюшего за этим 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: Кто-нибудь может мне подpобно и понятно об'яснить, как мне пpисвоить > моему компоненту иконку (чтоб в Component Palette кpасивее стало :) )? A(AS): Создаешь .res файл, делаешь там битмап по имени класса компонента (напpимеp TMYCOMPONENT) pазмеpом 24x24. Левая нижняя точка — цвет пpозpачности. Полученный .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: Как сделать чтобы пpогpамма не отобpажалась в панели задач? 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; }
-+---------- >Q40: Как сделать таймер с интервалом < 1 мс?
// Вставка текста в текущую позицию курсора //-------------------------------------------------------------------------- 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)); }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.
-+----------
>Q45: Нашёл в хэлпе полезную функцию 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.
-+----------
>Q46: Как сменить цвет надписи у TButton?
Никак. Используйте, например, TBitBtn.
-+----------
>Q47. Можно как нибудь засунуть заданные файлы в dll (dll создаем), а потом пpогpаммно извлечь их на фоpму ?
A(VF):
1. Помещаем свои рисунки в RES файл: — Создаем файл Image.rc и пишем в нем: MYIMAGE BITMAP Image.bmp — Обрабатываем Image.rc утилой Brcc32.exe: Brcc32.exe Image.rc, полyчаем файл Image.res.
2. Подключаем полученный Image.res файл к Dll'ке: — Image.res помещаем в каталог с Dll-проектом. — Пишем: USERES("Image.res"); — Компилим Dll.
3. Подгружаем Dll'ку и берем картинку из нее:
Объявить HINSTANCE hDllHandle;
...hDllHandle = LoadLibrary("MyDll.dll");
if(hDllHandle != NULL)
{
Image1->Picture->Bitmap->Handle = LoadBitmap(hDllHandle, "MYIMAGE"); FreeLibrary(hDllHandle); } else
{ //Сообщаем о невозможности подгрузить Dll и обрабатываем ошибку! }-+----------
>Q48. Как засунуть в pесуpсы файлы jpeg?
A(AP):
1) Засовываем Jpeg в res-файл. В файле MyJpeg.rc пишем: MyJPEG RCDATA "MyCoolJPEG.jpg" И натравливаем на это дело brcc32 -- brcc32 MyJpeg.rc Для полного счастья осталось написать в нашем модуле что-то в духе #pragma resource "MyJPEG.res"
2) Загружаем Jpeg из ресурсов Например, в *.h файле определяем у нашей формы в секции private (к примеру) переменную TJPEGImage *MyJPEG; Естественно, где-то вверху есть #include <JPEG.hpp> Далее, в конструкторе формы пишем: (Собственно, тут два варианта; гурманы могут извращатсья с FindResource, LoadResource и в качестве заключительного аккорда -- LockResource; а мы пойдем... нет-нет, всего лишь другим путем.) MyJPEG = new TJPEGImage(); TResourceStream *JPEGRes = new TResourceStream((int)HInstance,AnsiString("MyJPEG"),RT_RCDATA); MyJPEG->LoadFromStream(JPEGRes); JPEGRes->Free();
3) Осталось дело за малым -- использовать этот самый Jpeg: В каком-нибудь OnPaint у PaintBox'а написать: ((TPaintBox*)Sender)->Canvas->Draw(0, 0, MyJPEG);
-+----------
>Q49. Как программно скролировать TMemo?
A: EM_LINESCROLL. Например,
SendMessage(Memo->Handle, EM_LINESCROLL, 0, Memo->Lines->Count);