Форум » GUI » Вылет из программы... » Ответить

Вылет из программы...

Andrey: Всем привет ! Как можно в МиниГуи программе сделать вызов своих функций при аварийном завершении программы (т.е. при вылете) ? Т.е. хочу сделать сразу после появления MsgBox() вызов 2-3 функций и потом сделать DbCloseAll(). Как это реализовать ? Для чего это нужно, поясню: при входе в свою программу я пишу в базу кто и когда вошел в программу (логин пользователя). И при выходе из программы, стираю этого пользователя из базы. Если программа "вылетает", то юзер числится как работающий. Древнее наследие с клипера, хотел переделать, да так и осталось.

Ответов - 173, стр: 1 2 3 4 5 6 7 8 9 All

SergKis: Pasha пишет чтобы хоть понять, какой наследник TCnlData генерирует ошибку Это мало что даст, т.к. родителем будет TWndData, а наследник, в основном, TCnlData, там вариантов мало. Для понимания ситуации надо полный текст программы Андрея и трассировать его ситуацию по списку контролов и окон.

Andrey: Опять прога вылетает на простом месте у юзеров. У меня не вылетает. Почему так ? Вот такая ошибка: [pre2]DbInfo: Alias - ZAIVKA, Ord - ZA_KVIPZA, Recno - 2001/2036 Time from start: 0 days 4 hours 47 mins 22 secs Error MGERROR/0 Control: Label_Tit1 Of Form_YRep1 Not defined. Program terminated. --------------------------------- Stack Trace --------------------------------- Called from MSGMINIGUIERROR(0) Called from VERIFYCONTROLDEFINED(0) Called from SETPROPERTY(0) Called from RPRT18_ZAIVKI(300) in module: Source\form_report18.prg Called from (b)HB_MACROBLOCK(0) Called from REPORTCALC(680) in module: Source\form_report00.prg Called from (b)HB_MACROBLOCK(0) Called from RUNBUTTONRESUME(544) in module: Source\form_report00.prg Called from (b)REPORTFORMCALC(259) in module: Source\form_report00.prg Called from _DOCONTROLEVENTPROCEDURE(0) Called from EVENTS(0) Called from MYEVENTSHANDLER(1647) in module: Source\main.prg Called from DOMESSAGELOOP(0) Called from _ACTIVATEWINDOW(0) Called from REPORTFORMCALC(399) in module: Source\form_report00.prg[/pre2] Вот код: [pre2] SELECT ZAIVKA DbSetOrder(1) nKolvo := ORDKEYCOUNT() FOR nG := 1 TO LEN(aXdim) ..... cVal := "Расчет по " + cTitle SetProperty( aLabel[1], aLabel[2], "Value" , cVal ) // выводим надпись FOR nI := 1 TO nKolvo SELECT ZAIVKA ORDKEYGOTO( nI ) IF nI % nPokaz == 0 // выводим через XXX записей cMsg := SPACE( 10 ) + HB_NtoS( nI ) + "/" + HB_NtoS( nKolvo ) SetProperty( aLabel[1], aLabel[2], "Value" , cVal + cMsg ) // выводим надпись // строка 300 // рисуем бегунок Label_Progress_Bar( aBeg[1], aBeg[2], aBeg[3], aBeg[4], aBeg[5], aBeg[6],aBeg[7] , nI * nProc, 100 ) ENDIF .... ProcessMessages() NEXT .... NEXT[/pre2] Вывожу форму, а в другом модуле делаю расчёт. Бегунок дергаю из модуля расчёта. Штук 20 таких отчётов и на одном сегодня появился вылет. Потом правда не вылетало... Как юзера этого добиваются ? Одни загадки.... Писать везде проверку на существование такого объекта на форме ? Блин, утомительно... Как можно решить проблему вылета ?

Dima: Andrey пишет: Писать везде проверку на существование такого объекта на форме ? Видимо да


SergKis: Andrey пишет Штук 20 таких отчётов и на одном сегодня появился вылет. Потом правда не вылетало... По мне, это говорит о том, что идет работа с памятью, мусоросборщик или windows damp\restore\swap и ты попадаешь, когда адрес еще в подвешенном состоянии для программы. Дима прав, делать надо свою ф-ю mySet\GetProperty с циклом и begin sequence, если recover делать wApiSleep(100) и повторять, операцию доступа к контролу, несколько раз такого цикла и дальше идешь без вывода, можешь лог вести на такие ситуации. Еще мусоросборщик надо самому запускать хотя бы перед созданием новых окон. Программы у тебя, кажется, не маленькие и в ресурсах до фига всего, если не ошибаюсь - вот память и колбасит PS/ И на мой взгляд поменьше всяких "бегунков-прыгунков-ползунков" - это все потребляет ресурсы

Andrey: SergKis пишет: Еще мусоросборщик надо самому запускать хотя бы перед созданием новых окон. Программы у тебя, кажется, не маленькие и в ресурсах до фига всего, если не ошибаюсь - вот память и колбасит Как это сделать ? Ни разу не делал.... Судя по скрину экрана (а я при ошибке всегда теперь делаю скрин) был запущенн только модуль отчетов, где вылетела и модуль чтения базы постгресса по таймеру. Может там что-то пришло по таймеру ? Хотя при входе в модуль отчётов я таймер отключаю. [pre2]Called from MSGMINIGUIERROR(0) Called from VERIFYCONTROLDEFINED(0) Called from SETPROPERTY(0) Called from RPRT18_ZAIVKI(300) in module: Source\form_report18.prg Called from (b)HB_MACROBLOCK(0) Called from REPORTCALC(680) in module: Source\form_report00.prg Called from (b)HB_MACROBLOCK(0) Called from RUNBUTTONRESUME(544) in module: Source\form_report00.prg Called from (b)REPORTFORMCALC(259) in module: Source\form_report00.prg Called from _DOCONTROLEVENTPROCEDURE(0) Called from EVENTS(0) Called from MYEVENTSHANDLER(1647) in module: Source\main.prg Called from DOMESSAGELOOP(0) Called from _ACTIVATEWINDOW(0) [/pre2]

SergKis: Andrey пишет Как это сделать ? Ни разу не делал.... HB_GCALL() Поищи "сборщик мусора" на форуме , были темы и ты в них участвовал был запущенн только модуль отчетов, где вылетела и модуль чтения базы постгресса по таймеру. На время работы постгреса, твоя программа, могла быть swapoвана на диск виндой и потом восстанавливатся, для продолжения работы, х.з. в реале что было у клиента. Пример с letoDbf, клиент соединен с сервером работает, потом пошел пить кофе, PC "заснул", клиент пришел, PC "проснулся" и внешне все ok! с прогой hmg, но любое обращение к серверу валит программу, т.к. разрушились буфера\инф. коннекта с сервером. Точно такое же снятие происходит у клиента, если Клиент ушел домой, оставив программу в памяти с открытыми таблицами. С 7-8 утра происходит пополнение базы сервера, локальной программой на сервере (режим shared = 1 letodb.ini), но в монопольном режиме (так версия, пока, устроена). Эта операция проходит 1 раз в день и обязательно должна пройти. В letoDbf есть ф-я Leto_Kil(...), в таком случае она применяется программой пополнения ко всем клиентам с коннектом к серверу. Она срабатывает отлично, разъединяя, закрывая таблицы. Клиенты придя на работу на экране видят прекрасное окно (это я к тому снимку, который ты делаешь с экрана в error.log - скорее вводит в заблуждение), но любая попытка работать валит клиента, т.к. разрушено соединение. Это я к тому, что ты можешь догадываться, предполагать, понимать что могло произойти на клиенте, но знать точно ... Потому твоя программа должна быть готова ко всему, в разумных пределах.

Andrey: Почитал. Да это я пропустил в своё время, посчитав что не нужно. Прога тогда была у меня меньше на порядок. Поставил HB_GCALL() , прога даже не замирает. При входе в отчёты заставляю юзера закрывать ВСЕ другие окна, остаётся только одно окно с отчётами. Буду наблюдать дальше за своими юзерами. Спасибо БОЛЬШОЕ !

Andrey: Делюсь своим опытом понятия и работы с МиниГуи. Корифеям можно не читать, ну или хотя бы добавить. Многое мне Сергей помог и объяснил, но думаю что у других НЕТ такого учителя ! У меня БОЛЬШАЯ проблема была с пониманием почему маленькие проги на МиниГуи отлично работают, а моя большая программа нет. У меня один модуль обслуживает одновремеено 5 похожих таблиц. Объявлена одна переменная [pre2] PRIVATE oBrw[/pre2] В ней и крутяться 5 таблиц. У себя на компе прога не вылетает, а у юзеров были постоянные вылеты. Юзер запускает мою прогу, 1С, мозилу, эксель и т.д. То есть память компа кушается отлично, в отличие от моих тепличных условий. Переделал, как мне Сергей рекомендовал уйти от привязки объектов, например: [pre2] @ 20, 0 LABEL CargoIniSave WIDTH 10 HEIGHT 20 VALUE "" INVISIBLE SetProperty(cFormName, "CargoIniSave", "Cargo", aIniSave ) @ 20, 0 LABEL CargoFilter WIDTH 10 HEIGHT 20 VALUE "" INVISIBLE SetProperty(cFormName, "CargoFilter", "Cargo", {} ) @ 20, 0 LABEL CargoSort WIDTH 10 HEIGHT 20 VALUE "" INVISIBLE SetProperty(cFormName, "CargoSort", "Cargo", {} ) ............[/pre2] На контейнер, который железно привязанный к окну, примерно так: [pre2] (This.Object):Cargo := oKeyData() // создать объект (контейнер) для окна &cFormName oCrg := (This.Object):Cargo oCrg:aRunFunc := {cGlobSection,cFormName,nTable} oCrg:cAlias := ALIAS() oCrg:aMemIndexOpen := aCargoMemIndexOpen oCrg:aIniSave := aIniSave oCrg:aTsbFilter := {} oCrg:aTsbSort := {} oCrg:aSaveFilter := {} oCrg:aColor2Uslov := {} oCrg:aColor2Info := {}[/pre2] Подробно я это уже описывал - Пост N: 6941 Кроме того везде, где есть работа с кнопками (для больших списков), вместо такого кода: [pre2] @ .... BUTTONEX Button_City ... ACTION {|cw,cn| cw := ThisWindow.Name, cn := This.Name ,; SetProperty(cw, cn, "Enabled", .F.) ,; aRet1 := SelectCity() ,; // справочник городов IF( LEN(aRet1) == 0, nCity := 0 , nCity := aRet1[1] ) ,; IF( LEN(aRet1) == 0, cCity := "", cCity := aRet1[2] ) ,; nStreet := 0 , cStreet := "" ,; nDom := nStro := nKorp := nPdzd := nKvar := 0 ,; cDom := cStro := cKorp := cPdzd := cKvar := "" ,; This.Label_Street.Value := "" ,; This.GetBox_NDom.Value := 0 ,; This.GetBox_CDom.Value := "" ,; This.GetBox_NStro.Value := 0 ,; This.GetBox_CStro.Value := "" ,; This.GetBox_NKorp.Value := 0 ,; This.GetBox_CKorp.Value := "" ,; This.GetBox_NPdzd.Value := 0 ,; This.GetBox_CPdzd.Value := "" ,; This.GetBox_NKvar.Value := 0 ,; This.GetBox_CKvar.Value := "" ,; Form_SeekAdr.Label_City.Value := " " + cCity ,; SayLabelAdres(1,cCity) ,; SetProperty(cw, cn, "Enabled", .T.) ,; MyFocus() } [/pre2] Нужно ставить событие на кнопку, вот так: [pre2] @ .... BUTTONEX Button_Street ... ACTION {|| _wSend(20), _wPost(22) } [/pre2]А ниже описание/назначение событий, до END WINDOW примерно так: [pre2]WITH OBJECT This.Object :Event( 0, {| | InkeyGui(200) } ) // для кнопки справочника улиц :Event(20, {| | This.Button_Street.Enabled := .F. , DoEvents() } ) :Event(22, {|ow| LOCAL aRet3 LOCAL aGet //, cGet, cN myLogErr("=== кнопка справочника Улиц = " + ProcNL()) myLogErr(1, valtype(ow:Name), ow:Name) myLogErr(_IsWindowDefined(ow:Name), This.Name) aRet3 := SelectStreet(nCity) // вызов нового окна улиц IF LEN(aRet3) == 0 nStreet := 0 cStreet := "" ELSE nStreet := aRet3[1] cStreet := aRet3[2] ENDIF nDom := nStro := nKorp := nPdzd := nKvar := 0 cDom := cStro := cKorp := cPdzd := cKvar := "" myLogErr(2, valtype(ow:Name), ow:Name) IF !Empty(ow:Name) .and. ISCHAR(ow:Name) aGet := HMG_GetFormControls(ow:Name, "GETBOX") ? " === кнопка справочника Улиц = " + ProcNL() ? " GETBOX =", aGet ; ?a aGet SET WINDOW THIS TO ow:Name /* можно такой вариант делать FOR EACH cGet IN aGet cN := subs(cGet, 7, 2) IF cN == "_C" This.&(cGet).Value := "" ELSEIF cN == "_N" This.&(cGet).Value := 0 ENDIF NEXT */ // мне не нравится запись Form_SeekAdr.GetBox_NDom.Value // т.к. препроцесору нужно знание\объявление об окне // DECLARE Form_SeekAdr // тогда лучше исп. ф-ии DoMethod(), Set\GetProperty() Form_SeekAdr.GetBox_NDom.Value := 0 This.GetBox_CDom.Value := "" This.GetBox_NStro.Value := 0 This.GetBox_CStro.Value := "" This.GetBox_NKorp.Value := 0 This.GetBox_CKorp.Value := "" This.GetBox_NPdzd.Value := 0 This.GetBox_CPdzd.Value := "" This.GetBox_NKvar.Value := 0 This.GetBox_CKvar.Value := "" This.Label_Street.Value := " "+cStreet This.Button_Street.Enabled := .T. // разблок.кнопку SET WINDOW THIS TO SayLabelAdres(2,cCity,cStreet) MyFocus() DoEvents() ENDIF ? " === ", ow:Name, _IsWindowDefined(ow:Name), This.Name, "exit" RETURN NIL } ) :Event(99, {|ow| ow:Release() } ) END WITH END WINDOW[/pre2] Прога стала стабильно работать, все довольны и я в том числе !!! Бывает и так, что у юзера 2 часа прога проработала и свалилась, вроде на пустом месте. Сделал запись всего экрана в папку ошибок при "вылете" из программы. Смотрю на экране только моя программа с ОДНИМ окном и мозила. Вот пример такой ошибки: [pre2]Error BASE/1003 Переменная не существует: OBRW --------------------------------- Stack Trace --------------------------------- Called from (b)FORM_MYTABLE(683) in module: Source\Tbrw_table.prg Called from DO_WINDOWEVENTPROCEDURE(0) Called from TWNDDATA:DOEVENT(0) Called from DO_ONWNDLAUNCH(0) Called from (b)INIT(0) Called from EVENTS(0) Called from MYEVENTSHANDLER(1650) in module: Source\main.prg Called from DOMESSAGELOOP(0) Called from _ACTIVATEWINDOW(0) Called from MAIN(194) in module: Source\main.prg [/pre2] По коду выглядит так: [pre2] :Event(70, {|ow | // F7 Поиск LOCAL hHandle, cSect_F6F7, aGlSect, aFilter LOCAL cFormName := ow:Name hHandle := ow:Handle // или так ThisWindow.Handle This.oBut_Filtr.Enabled := .F. Darken2Open(hHandle) aGlSect := ow:Cargo:aGlobalSection cSect_F6F7 := MyGetMainSection("Секция_F6F7", aGlSect ) aFilter := FormTbrFiltr(nTable,cSect_F6F7,aIniSave,aFilter) MsgLog("--- VK_F7 кнопка: F7 Поиск" + ProcNL() ) MsgLog(" фильтр=" + HB_ValToExp(aFilter) ) _wSend(7, cFormName, aFilter[1]) myLogErr("--- VK_F7 кнопка: F7 Поиск --- " + ProcNL()) myLogErr(1, valtype(ow:Name), ow:Name) Darken2Close(hHandle) IF LEN(aFilter[4]) > 0 TsbNewSeek(cFormName,nTable,aFilter[4]) ELSE TsbNewIndex(cFormName,nTable,aFilter[2],aFilter[1],aFilter[3]) ENDIF myLogErr(2, "hHandle=", hHandle) myLogErr(_IsWindowDefined(ow:Name), ow:Name) IF GetControlIndex("oBut_Filtr", cFormName ) > 0 SetProperty(cFormName, "oBut_Filtr", "Enabled", .T.) ENDIF строка 683 !!! ToUpButtonHideShow( cFormName, nTable, oBrw ) Tsb4Focus(nTable) Return Nil } )[/pre2] Вот и вылет на пустом месте у меня !!! А я всё не понимал, почему падает прога, вроде всё в коде правильно написано. Оказывается всё не так просто. Вот ответ Сергея, я думаю многим будет это интересно тоже почитать: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Мозила много памяти жрет, твоя прога может дергаться на перекомпоновку памяти Как можно меньше используй внешние переменные в блоках кода, особенно, когда они сложные (много вызовов разных), как тут. Много раз говорил, что система и сборщик мусора первыми перелопачивает в памяти PUBLIC и PRIVATE переменные, что бы собрать непрерывный кусок памяти. Т.е. При создании блока кода в oBrw был один адрес, если в процессе работы блока кода, произошла прекомпоновка памяти то ссылка будет на новый адрес, а в блоке кода останется старый и будет сваливаться прога (что у тебя и происходит). Если прога небольшая, то памяти непрерывным куском достаточно для работы (перекомпоновка редко происходит), если прога большая (или исп. еще прога, мозила, например), то это происходит часто и не зависимо от тебя. Использование вместо PUBLIC, PRIVATE переменных команд App.Cargo := oKeyData() ThisWindow.Cargo := oKeyData() или для контрола This.Cargo := oKeyData() это не прихоть, а борьба за правильные адреса. При перекомпоновке памяти Григорий, внедряя класс THmgData и переводя на него внутренние переменные, тоже идет по этому пути. Еще надо было бы перевести на него и внутренние PUBLIC переменные окон и контролов ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Вот так на пустом месте в проге юзера у меня и "падают" .... Здесь есть несколько вариантов решения, остановился пока на простом: [pre2] :Event(70, {|ow,ky,ob| // F7 Поиск ..... ToUpButtonHideShow( cFormName, nTable, ob ) [/pre2] То есть я избавился от oBrw Надеюсь что это пригодиться другим в понимание, как писать проги на МиниГуи. Сергей, СПАСИБО тебе ОГРОМНОЕ !

Andrey: Опять непонятка по вылету из проги. Вот ошибка: [pre2]Time from start: 0 days 6 hours 42 mins 2 secs Error MGERROR/0 Control: Text_1 Of Form_Dim Not defined. Program terminated. --------------------------------- Stack Trace --------------------------------- Called from MSGMINIGUIERROR(0) Called from VERIFYCONTROLDEFINED(0) Called from GETPROPERTY(0) Called from REFRESHBROWSE(1090) in module: Source\Sel_Dim_Hmg.prg Called from MYKEYACTIONDIM(1025) in module: Source\Sel_Dim_Hmg.prg Called from (b)DIMCREATEBROWSE(954) in module: Source\Sel_Dim_Hmg.prg Called from TSBROWSE:SUPERKEYDOWN(0) Called from TSBROWSE:KEYDOWN(0) Called from TSBROWSE:HANDLEEVENT(0) Called from EVENTS(0) Called from DOMESSAGELOOP(0) Called from _ACTIVATEWINDOW(0) Called from DOMETHOD(0) Called from FORM_SEL_DIM(708) in module: Source\Sel_Dim_Hmg.prg Called from SEL_DIM(568) in module: Source\Sel_Dim_Hmg.prg [/pre2] По коду у меня так: [pre2]STATIC FUNCTION RefreshBrowse() .... cSeek := Alltrim( Form_Dim.Text_1.Value ) // строка 1090 .... oBrw_5:Reset() oBrw_5:Setfocus() DO EVENTS RETURN Nil[/pre2] А вызов из [pre2] CASE nKey > 31 .AND. nKey < 254 Search_value := сSearch_value + KeyToChar(nKey) Form_Dim.Text_1.Value := сSearch_value RefreshBrowse() // строка 1025[/pre2] Как так может быть до строки 1025 объект есть - Form_Dim.Text_1.Value := сSearch_value а когда вызвал функцию, то объекта Alltrim( Form_Dim.Text_1.Value ) уже нет ? У юзера прога слетела через 6 часов, до этого уверен, что в это место юзер лазил сотню раз. Там поиск по адресу - выбор из справочника улиц. Как такое может быть и как исправить ?

SergKis: Andrey пишет Как так может быть до строки 1025 объект есть - Form_Dim.Text_1.Value := сSearch_value а когда вызвал функцию, то объекта Alltrim( Form_Dim.Text_1.Value ) уже нет ? Уже объяснял про PUBLIC, PRIVATE, повторю[pre2] // создание контрола mVar := '_' + ParentForm + '_' + ControlName ... k := _GetControlFree() Public &mVar. := k // !!! тут создает ... ... *-----------------------------------------------------------------------------* STATIC PROCEDURE VerifyControlDefined ( cParentName , cControlName ) *-----------------------------------------------------------------------------* // !!! тут проверяет IF ! Empty ( cControlName ) .AND. ! _IsControlDefined ( cControlName , cParentName ) // !!! ^^^^^^^^^^^^^^^^^ MsgMiniGuiError ( "Control: " + cControlName + " Of " + cParentName + " Not defined." ) ENDIF RETURN ... *-----------------------------------------------------------------------------* FUNCTION _IsControlDefined ( ControlName, FormName ) *-----------------------------------------------------------------------------* LOCAL mVar LOCAL i mVar := '_' + NoQuote ( FormName ) + '_' + NoQuote ( ControlName ) i := __mvGetDef ( mVar , 0 ) // !!! тут не находит, хотя было IF i == 0 RETURN .F. ENDIF RETURN ( .NOT. _HMG_aControlDeleted [ i ] ) [/pre2] Т.е. потерялся адрес Public переменной. Как такое может быть и как исправить ? Судя по названиям DIM... у тебя массивы, по Br_5:Reset() они пересоздаются, т.е. память выделяется и возможно, у тебя сразу 2а массива могут занимать место в памяти. Может перед созданием др. массива, пробежаться по старому и сделать NIL или самому массиву NIL. Потом запускать сборщик мусора (может быть почаще это делать, определив места). Может переключиться на dbf работу. Обвесь получение данных с контрола begin sequnce с ожиданием и повторным чтением при не удаче Можно сделать для 5 таблиц отдельный запуск (exe 1, а main входов 5), т.е. независимо работают с таблицами в разных разделах

Andrey: SergKis пишет: Уже объяснял про PUBLIC, PRIVATE, повторю Там переменная сSearch_value - STATIC PUBLIC - oBrw_5, ну и сам Form_Dim.Text_1.Value Понял теперь. Я что-то считал что если объект положил на форму, то всё ОТЛИЧНО ! Т.е. существует всегда. Всегда забываю что это просто PUBLIC переменная. Наверное тогда эту переменную сSearch_value нужно отвязать от Form_Dim.Text_1.Value Так как она STATIC, то просто тупо к ней обращаться без перекладывания на форму. Спасибо ! Т.е. юзер открыл справочник улиц, пошёл покурил или чай попил (или в мозиле почту проверил, в игрушку поиграл), потом пришёл и решил выбрать наконец то улицу... Вот и вылет ... Да уж ...

Andrey: В чем разница использования переменных. Допустим я создам вместо всех PUBLIC переменных в программе такой контейнер: [pre2]WITH OBJECT ( App.Cargo := oKeyData() ) :cFileIni := ChangeFileExt( App.ExeName, '.ini' ) // настройки в программе :cFileLng := ChangeFileExt( App.ExeName, '.lng' ) // смена языка в программе :nLang := 1 // текущий язык в программе // .... и так далее END WITH // обращение в программе: LOCAL cFile := (App.Cargo):cFileIni LOCAL nLang := (App.Cargo):nLang[/pre2] Или сделаю контейнер через PUBLIC переменную: [pre2] PUBLIC oPubApp // назвал покороче, удобнее писать oPubApp := oKeyData() oPubApp:cCurDir := GetStartUpFolder() + "\" oPubApp:cDbfDir := oPubApp:cCurDir + "DBASE" + "\" oPubApp:cLogFile := oPubApp:cCurDir + "_Msg.log" oPubApp:aBColor := SILVER oPubApp:cFileIni := ChangeFileExt( App.ExeName, '.ini' ) oPubApp:cFileLng := ChangeFileExt( App.ExeName, '.lng' ) oPubApp:nLang := 1 [/pre2] Что лучше использовать или что будет надёжней в программе ? С учётом того, что адреса PUBLIC переменных теряются... А если будет программа небольшая, можно ли остановиться на oPubApp := oKeyData() ?

SergKis: Andrey пишет С учётом того, что адреса PUBLIC переменных теряются... Думаю, ты не понял про потерю. Могут теряться, если делать PUBLIC oBrw ... ON ACTION {|| ..., oBrw:SetFocus() } Или сделаю контейнер через PUBLIC переменную: По мне лучше делать LOCAL oPubl ... App.Cargo := oHmgData() App.Cargo:oPubl := oHmgData() oPubl := App:Cargo:oPubl oPubl:cCurDir := GetStartUpFolder() + "\" oPubl:cDbfDir := oPubl:cCurDir + "DBASE" + "\" oPubl:cLogFile := oPubl:cCurDir + "_Msg.log" oPubl:aBColor := SILVER oPubl:cFileIni := ChangeFileExt( App.ExeName, '.ini' ) oPubl:cFileLng := ChangeFileExt( App.ExeName, '.lng' ) oPubl:nLang := 1 в блоке кода будет [pre2] ... := {|ow| Local oPubl := App.Cargo:oPubl ... Return Nil } [/pre2]

Haz: SergKis пишет: в блоке кода будет Сергей, а как такой вариант? PUBLIC oBrw в блоке ... { | ow | local oBr := oBrw ... return nil }

SergKis: Игорь Имеет шанс свалится. Не свалится, скорее такой вариант {|ow| myFunc(ow) } FUNCT myFunc(oWnd) LOCAL oBr := oBrw ... но возникает, условно, лишняя ф-я, т.к. в блоке, после манипуляций с Enabled контролов, как правило вызов ф-ии общего назначения. И после нее снова какие то манипуляций с Enabled контролов PS Свалится может и такой вариант FUNCT myFunc(oWnd) LOCAL oBr := oBrw ... тут вызываем, что то, что жрет память, не делаем сами сборку мусора (т.е. может сама сработать) ... и делаем oBr:SetFocus() Если делали сборку мусора сами, то лучше сделать повторно oBr := oBrw

Haz: SergKis пишет: Свалится может и такой вариант FUNCT myFunc(oWnd) LOCAL oBr := oBrw Сколько же переделывать

SergKis: Haz пишет Сколько же переделывать Не спеши сразу переделывать. Сделай для начала сборку мусора перед функцией, т.е. {|ow| DoEvents(), hb_gcAll(), DoEvents(), myFunc(ow) } возможно памяти будет хватать

Andrey: SergKis пишет: По мне лучше делать LOCAL oPubl ... App.Cargo := oHmgData() App.Cargo:oPubl := oHmgData() oPubl := App:Cargo:oPubl oPubl:cCurDir := GetStartUpFolder() + "\" oPubl:cDbfDir := oPubl:cCurDir + "DBASE" + "\" oPubl:cLogFile := oPubl:cCurDir + "_Msg.log" Не совсем понял. В главном модуле определяю [pre2]App.Cargo := oHmgData() Далее присваиваю нужные мне переменные App.Cargo:cFileIni := ChangeFileExt( App.ExeName, '.ini' ) App.Cargo:cFileLng := ChangeFileExt( App.ExeName, '.lng' ) App.Cargo:nLang := 1 // .... и так далее [/pre2] А уже в нужных функциях обращаюсь к этим переменным так: [pre2]LOCAL oApp // LOCAL oPubl - не пойдет, буду путаться с уже объявленными cPubPathDbf, cPubPathTemp и т.д. LOCAL cFileIni, cFileLng, nLang ..... oApp := App.Cargo cFileIni := oApp:cFileIni // или App.Cargo:cFileIni cFileLng := oApp:cFileLng // или App.Cargo:cFileLng nLang := oApp:nLang // или App.Cargo:nLang или так LOCAL cFileIni, cFileLng, nLang WITH OBJECT App.Cargo cFileIni := :cFileIni cFileLng := :cFileLng nLang := :nLang // .... и так далее END WITH [/pre2] Вот так правильно будет ? И в чем разница между oKeyData() и oHmgData() ?

SergKis: Andrey пишет И в чем разница между oKeyData() и oHmgData() ? В oHmgData() ничего лишнего для работы с контейнером, как с переменными PUBLIC, в oKeyDat() есть методы, редко используемые :Do(...) :Json(...) :Sum(...) ... В oHmgData() строковые переменные сразу переводит в upper, в oKeyData() нет. А уже в нужных функциях обращаюсь к этим переменным так: LOCAL oApp // LOCAL oPubl - не пойдет, буду путаться с уже объявленными cPubPathDbf, cPubPathTemp и т.д. LOCAL cFileIni, cFileLng, nLang ..... 1. oApp := App.Cargo cFileIni := oApp:cFileIni // или App.Cargo:cFileIni cFileLng := oApp:cFileLng // или App.Cargo:cFileLng nLang := oApp:nLang // или App.Cargo:nLang или так LOCAL cFileIni, cFileLng, nLang 2. WITH OBJECT App.Cargo cFileIni := :cFileIni cFileLng := :cFileLng nLang := :nLang // .... и так далее END WITH В 1. и 2. правильно - это как одна секция из ini, т.е. один уровень, а если тебе надо организовать данные по группам (секциям), тогда делаем Local o ... App.Cargo := oHmgData() oApp := App.Cargo oApp:oMain := oHmgData() // группа\секция Main oApp:oText := oHmgData() // группа\секция Text можно писать oApp:oMain:Host := "127.0.0.1" oApp:oMain:Port := 2814 oApp:oMain:Lang := "RU" ... но можно писать короче, беря адрес объекта в переменную o := oApp:oMain // взяли для работы группу Main или o := App.Cargo:oMain o:Host := "127.0.0.1" o:Port := 2814 o:Lang := "EN" ... так же можем поступать и группой Text o := oApp:oText // взяли для работы группу Text или o := App.Cargo:oText o:NewCopy := "New as ..." o:New := "New" o:Del := "Delete" o:Edit := "Edit" ...

Петр: SergKis пишет: В oHmgData() ничего лишнего для работы с контейнером, как с переменными PUBLIC, в oKeyDat() есть методы, редко используемые oHmgData() это псевдофункция класса THmgData, возвращающая объект класса. oKeyDat() - функция, возвращающая экземпляр объекта TKeyData или TWmEData, в зависимости от принятых аргументов. Класс THmgData доступен всегда, доступность других классов зависит от состояния константы _OBJECT_ в момент компиляции библиотеки.



полная версия страницы