Program

Как определить директорию, из которой запущена программа

Вопрос
Как определить директорию, из которой запущена готовая программа (файл EXE)

Ответ

В общем случае, этого сделать невозможно! Есть масса способов "сбить с толку" любые команды и функции.

Однако все-таки есть ряд функций, которые могут решить подобную проблему при определенных условиях. Надо всегда помнить, что эти функции не абсолютны. Имеют ограничения.

  
 * Путь доступа к первой запущенной программе (главному файлу проекта)  
  JustPath(SYS(16,0))  
    
 * Путь доступа к текущей директории  
  FullPath("") - пустая строка (две кавычки подряд) - обязательны  
  SYS(5)+SYS(2003)  
  SET("DEFAULT")+CURDIR()  
  _VFP.DefaultFilePath
Как проверить факт существования переменной памяти, поля таблицы, метода формы

Вопрос
Как проверить факт существования переменной памяти, поля таблицы, метода формы и т.п. ?

Ответ

В большинстве случаев такая проверка может быть осуществлена с помощью функции TYPE(). В качестве вычисляемого значения в эту функцию передается тот объект, факт существования которого проверяем. Если в результате получаем значение отличное от "U", то это означает, что такой объект существует.

Как проверить факт существования переменной памяти

IF TYPE("m.MyVar") = "U"  
 	* Переменной памяти с именем MyVar - не существует  
  ENDIF

Как проверить факт существования поля таблицы (курсора, View)

Если таблица (курсор, View) уже открыта в текущей DataSession, то

IF TYPE("MyTableAlias.MyField") = "U"  
 	* Для таблицы, открытой с алиасом MyTableAlias   
 	* не существует поля с именем MyField  
  ENDIF

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

IF InDBC("MyTable.MyField","FIELD") = .F.  
 	* Для таблицы, включенной в контейнер базы данных под именем MyTable   
 	* не существует поля с именем MyField  
  ENDIF

Как проверить факт существования (использования, открытия) формы

IF TYPE("_VFP.Forms('MyForm')")="U"  
 	* Формы со свойством name = MyForm не существует (не открыта)  
  ENDIF

Обратите внимание, что здесь используется системная переменная _VFP. Использовать аналогичный синтаксис с системной переменной _SCREEN - не получиться. Коллекция _SCREEN.Forms() не принимает в качестве параметра символьные значения. Только числовые.

Кроме того, данный способ не сработает для форм, имеющих значение совйства ShowWindow = 2 - As Top-Level Form. В этом случае придется просто перебирать весь массив _SCREEN.Forms() или _VFP.Forms()

LOCAL lnI, loForm  
  loForm = NULL  
  FOR lnI=1 TO _SCREEN.FormCount  
  	loForm = _SCREEN.Forms(m.lnI)  
  	IF m.loForm.Name = "MyForm"  
 		* Нашли форму со свойством Name = "MyForm"  
  		EXIT  
  	ENDIF  
  	loForm = NULL  
  ENDFOR  
  IF IsNull(m.loForm) = .T.  
 	* Формы со свойством name = MyForm не существует (не открыта)  
  ENDIF

Как проверить факт существования метода, события или свойства в используемом объекте

IF TYPE("_VFP.Forms('MyForm')")="O" AND PEMStatus(_VFP.Forms('MyForm'),'MyMethod',5) = .T.  
 	* Открыта форма со свойством name = MyForm   
 	* и в этой форме существует метод, событие или свойство с именем MyMethod  
  ENDIF

Для форм, со свойством ShowWindow = 2 - As Top-Level Form данный способ не годится. Придется также перебирать все элементы массива _SCREEN.Forms() как и в предыдущем примере.

Замечание

Кроме приведенных способов есть и другие варианты проверки факта существования того или иного объекта. Причем от версии к версии количество способов растет, поскольку появляются все новые команды и функции.

Как работать с ini-файлами

Вопрос
Как работать (читать и записывать) с ini-файлами?

Ответ

ini-файл или файл с расширением из 3 букв "INI" (от слов initialization file - файл инициализации) - это обычный текстовый файл, который имеет специфическое содержание. Выглядит его содержимое примерно так:

// Комментарий
[Имя_раздела_1]
Имя_реквизита_1 = значение реквизита
Имя_реквизита_2 = значение реквизита

// Комментарий
[Имя_раздела_2]
Имя_реквизита_1 = значение реквизита
Имя_реквизита_2 = значение реквизита

Т.е. в квадратных скобках указывается имя раздела (другое название - "секция"), затем идет список реквизитов (другое название - "ключ") этого раздела и их значение. Количество разделов и количество реквизитов внутри раздела не ограничено.

Желательно давать имена разделам и реквизитам в одно слово из латинских букв без пробелов и спец.символов. Но это скорее перестраховка. Если для Вас удобнее воспринимать имя раздела или реквизита с пробелом (в два слова) - пишите с пробелом!

В качестве значения реквизита принимается вся строка от первого отличного от пробела символа после символа равенства до окончания строки, включая пробелы. Т.е. здесь для задания текстовой строки использовать кавычки не надо. Предполагается, что все значения имеют только и исключительно символьный тип.

Символом комментария обычно служит две наклонные черты подряд. Хотя, в принципе, можно использовать абсолютно любой символ (вот только пробел нельзя  ). Для используемых ниже API-функций важен факт совпадения искомого имени начиная с первого символа. Достаточно того, чтобы первый символ не совпадал с искомым.

В принципе, можно прочитать такой файл из FoxPro функциями чтения текстовых файлов (FOPEN(), FREAD(), FileToStr() и т.п.) и сделать его разбор. "Но есть способ лучше" (с). Существуют специальные API-функции предназначенные для чтения и записи INI-файлов.

GetPrivateProfileString - считывает значение указанного реквизита из указанного раздела.
WritePrivateProfileString - записывает значение указанного реквизита в указанный раздел указанного ini-файла. Если указанного раздела или реквизита не существует, то он будет создан. Если указанного ini-файла не существует, то он также будет создан.

Выглядит это примерно так:

Запись значения в ini-файл

  
  Declare Integer WritePrivateProfileString In Win32API As WritePrivStr ;    
  	String cSection, ;	&& имя раздела  
  	String cKey, ;	&& имя реквизита  
  	String cValue, ;	&& значение реквизита  
  	String posfile	&& имя ini-файла с полным путем доступа  
    
  LOCAL lnError  
  lnError = WritePrivStr("TestSection", ;  
  			"TestKey", ;  
  			"TestValue", ;  
  			Fullpath("test.ini"))  
  IF lnError<>1  
 	* Запись в ini-файл не удалась. Произошла ошибка  
  ELSE  
 	* Смотрим, что получилось во вновь созданном файле  
  	MODIFY FILE test.ini  
  ENDIF

Чтение значения из ini-файл

  
  Declare Integer GetPrivateProfileString In Win32API  As GetPrivStr ;    
  	String cSection, ;		&& Имя раздела  
  	String cKey, ;		&& Имя реквизита  
  	String cDefault, ;		&& Значение по умолчанию, если нет указанного раздела или реквизита  
  	String @cBuffer, ;		&& Собственно считанное значение реквизита  
  	Integer nBufferSize, ;	&& Максимальное количество символов в считанном реквизите  
  	String posfile		&& имя ini-файла с полным путем доступа  
    
  LOCAL lcBuffer, lnBuffer  
  lcBuffer = SPACE(2000)  
    
  lnBuffer = GetPrivStr("TestSection", ;  
  			"TestKey", ;  
  			"Нет значения", ;  
  			@lcBuffer, ;  
  			LEN(m.lcBuffer), ;  
  			Fullpath("test.ini"))  
    
  IF m.lnBuffer = 0  
 	* Ничего не прочитали  
  ELSE  
 	* Прочитанное значение храниться в первых m.lnBuffer символах переменной m.lcBuffer  
  	?LEFT(m.lcBuffer,m.lnBuffer)  
 	* Общая длина переменной m.lcBuffer по прежнему 2000 символов,   
 	* но символ m.lnBuffer+1 имеет ASCII-код равный 0, а все прочие - пробелы  
  ENDIF

Удаление раздела или реквизита
Удаление - это запись не определенного значения

  
 * Удаление только одного реквизита TestKey в разделе TestSection  
  LOCAL lnError  
  lnError = WritePrivStr("TestSection", ;  
  			"TestKey", ;  
  			NULL, ;  
  			Fullpath("test.ini"))  
  IF lnError<>1  
 	* Запись в ini-файл не удалась. Произошла ошибка  
  ELSE  
 	* Смотрим, что получилось   
  	MODIFY FILE test.ini  
  ENDIF  
    
 * Удаление всего раздела TestSection со всеми реквизитами  
  LOCAL lnError  
  lnError = WritePrivStr("TestSection", ;  
  			NULL, ;  
  			NULL, ;  
  			Fullpath("test.ini"))  
  IF lnError<>1  
 	* Запись в ini-файл не удалась. Произошла ошибка  
  ELSE  
 	* Смотрим, что получилось   
  	MODIFY FILE test.ini  
  ENDIF

Определение структуры ini-файла

Если структура ini-файла, т.е. имена секций и ключей, заранее не известны, то получить структуру ini-файла можно также используя функцию GetPrivateProfileString указав вместо имени секции или ключа число 0.

В этом случае в качестве возвращаемого значения Вы получите либо список имен секций (если указать 0 первым параметром), либо список имен ключей указанной секции (если указать 0 вторым параметром). Имена будут разделены символом Chr(0)

  
 * Список секций ini-файла  
  LOCAL lcBuffer, lnBuffer  
  lcBuffer = SPACE(2000)  
    
  lnBuffer = GetPrivStr(0, ;  
  			0, ;  
  			"", ;  
  			@lcBuffer, ;  
  			LEN(m.lcBuffer), ;  
  			Fullpath("test.ini"))  
    
  IF m.lnBuffer > 0  
  	m.lcBuffer = CHR(0) + LEFT(m.lcBuffer,m.lnBuffer)  
  	LOCAL lnI, lnFromPos, lnToPos  
  	FOR m.lnI = 1 TO OCCURS(CHR(0),m.lcBuffer)-1  
  		lnFromPos = AT(CHR(0),m.lcBuffer,m.lnI)  
  		lnToPos = AT(CHR(0),m.lcBuffer,m.lnI+1)  
  		?SubStr(m.lcBuffer,m.lnFromPos+1,m.lnToPos-m.lnFromPos-1)  
  	ENDFOR  
  ENDIF  
    
 * Аналогично получаем список ключей секции TestSection   
  LOCAL lcBuffer, lnBuffer  
  lcBuffer = SPACE(2000)  
    
  lnBuffer = GetPrivStr("TestSection", ;  
  			0, ;  
  			"", ;  
  			@lcBuffer, ;  
  			LEN(m.lcBuffer), ;  
  			Fullpath("test.ini"))  
    
  IF m.lnBuffer > 0  
  	m.lcBuffer = CHR(0) + LEFT(m.lcBuffer,m.lnBuffer)  
  	LOCAL lnI, lnFromPos, lnToPos  
  	FOR m.lnI = 1 TO OCCURS(CHR(0),m.lcBuffer)-1  
  		lnFromPos = AT(CHR(0),m.lcBuffer,m.lnI)  
  		lnToPos = AT(CHR(0),m.lcBuffer,m.lnI+1)  
  		?SubStr(m.lcBuffer,m.lnFromPos+1,m.lnToPos-m.lnFromPos-1)  
  	ENDFOR  
  ENDIF

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

Также пример работы с INI-файлами можно посмотреть в классе, поставляемом вместе с FoxPro

MODIFY CLASS oldinireg OF HOME()+'FFC\registry.vcx'

Хотя в новейшей идеологии Microsoft предполагается, что вместо ini-файлов следует использовать запись/чтение в системный реестр Windows.

Применительно к FoxPro следует рассмотреть возможность хранения дополнительных настроек в обычном файле DBF, поскольку FoxPro предназначен, прежде всего, именно для работы с файлами DBF.

Символьные поля, переменные памяти и константы

Вопрос

Я формирую запрос через SQLExec() или макроподстановку. Почему не получается сформировать запрос более чем на 255 символов?

Ответ

Символьные поля, переменные памяти и константы

В FoxPro существует отличия в предельно допустимой длине символьных данных в зависимости от того, о чем идет речь: поле таблицы, переменной памяти или константе

Ну, с полями таблицы все ясно. Их предельная длина определяется собственно размерностью поля. Однако размерность поля не может превышать 254 символов. Для memo-полей объем не может превышать 2ГБ (2 гигабайта - это 2 и 9 нулей символов).

Длина символьной константы не может превышать 255 символов

Длина символьной переменной теоретически не может превышать 16,777,184 символов. "Теоретически", потому, что при определенных условиях все-таки может. Просто за последствия такого превышения никто не отвечает.

Отдельно следует остановиться на отличии символьной константы от символьной переменной. Начинающие программисты очень часто получают сообщение об ошибке "Command contains unrecognized phrase/keyword (Error 36)" именно в силу непонимания этой разницы.

В FoxPro для написания символьных констант можно использовать 3 символа: одинарные кавычки, двойные кавычки, квадратные скобки. В чем отличие символьной константы от символьной переменной проще объяснить на примере. Словами получится долго и невнятно. Например:

  
  LOCAL lcString  
  lcString = 'Начало строки' + " середина строки " + [окончание строки]

Так вот, здесь то, что записано в кавычках и квадратных скобках - это 3 символьные константы, а lcString - это одна символьная переменная

Вообще-то, лично мне трудно представить, как можно написать одну символьную константу длиной больше 255 символов. Точнее написать-то такое можно, но как потом это можно редактировать! Получится совершенно не "читабельный" текст. Я рекомендовал бы разбивать длинные символьные константы на небольшие фрагменты с тем, чтобы каждый отдельный фрагмент целиком помещался в окне редактирования. Примерно так:

  
  LOCAL lcString  
  lcString = "Начало строки"+;  
        " середина строки "+;  
        "окончание строки"

В FoxPro нет специальных управляющих символов, как, например, в C используется обратный слеш. Поэтому, если Вам надо использовать кавычки не как границу символьной константы, а как собственно символ, то просто обрамляйте их кавычками другого типа:

?[ЗАО "Рога и копыта"]

Следует всегда помнить, что FoхPro отличается определенной "самостоятельностью" в преобразовании данных. Применительно к символьным данным это проявляется в том, что если Вы попытаетесь записать в символьное поле больше символов, чем позволяет его размерность, то ошибки это не вызовет, просто "лишние" символы будут отброшены без каких-либо дополнительных сообщений об ошибках.

  
  CREATE CURSOR test (test C(5))  
  INSERT INTO test (test) VALUES ("1234567890")  
  ?test.test

Если Вам надо записать в переменную памяти несколько строк текста "как есть", то, начиная с версии Visual FoxPro 7 для этой цели лучше использовать скобки TEXT...ENDTEXT

  
  LOCAL lcText  
    
  TEXT TO m.lcText NOSHOW  
  Первая строка текста  
  Вторая строка текста  
  "Название"  
  ENDTEXT  
    
  ?m.lcText

Как видите, все что находится между TEXT и ENDTEXT воспринимается не как набор команд, а просто как текст. Содержимое переменой m.lcText.

Как работать с путями доступа и именами файлов, содержащих пробелы

Вопрос
При использовании путей доступа или имен файлов, содержащих пробелы, я получаю сообщения об ошибках. Как работать с путями доступа и именами файлов, содержащих пробелы?

Ответ

В общем случае следует обрамлять такие имена кавычками или использовать выражения имени.

Например, на Вашем компьютере есть файл по такому пути доступа:

Цитата:

C:\Мои документы\Мой файл.txt

Если Вы хотите обратиться к такому файлу напрямую, по имени, то это следует делать так:

MODIFY FILE "C:\Мои документы\Мой файл.txt"

Если Вы считали это имя в переменную памяти, например, при помощи функции GetFile() или имя директории при помощи функции GetDir(), то следует использовать выражение имени. Скобки.

  
  LOCAL lcFileName  
  lcFileName = GetFile()  
  MODIFY FILE (m.lcFileName)

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

  
  LOCAL lcFileName  
  lcFileName = "C:\Мои документы\Мой файл.txt"  
  MODIFY FILE &lcFileName

фактически преобразуется в такой код

MODIFY FILE C:\Мои документы\Мой файл.txt

Далее вступает в действие синтаксический анализатор кода. Для него имя файла - это параметр команды. А параметры, в данном случае, отделяются друг от друга пробелами. Следовательно, FoxPro прочитает данную команду следующим образом:

MODIFY FILE C:\Мои

Разумеется, ни такой директории, ни имени файла не существует. И Вы получаете сообщение об ошибке.

Чтобы предотвратить подобное развитие событий, необходимо добавить дополнительные кавычки

  
  LOCAL lcFileName  
  lcFileName = ["C:\Мои документы\Мой файл.txt"]  
  MODIFY FILE &lcFileName

Этот код будет прочитан следующим образом

MODIFY FILE "C:\Мои документы\Мой файл.txt"

Вот здесь уже все в полном порядке. Параметр - это то, что ограничено кавычками.

Хотя, разумеется, использование дополнительных кавычек не слишком удобно. Поэтому проще при работе с именами файлов использовать выражения имени. Т.е. скобки. В этом случае Вы автоматически страхуетесь от возможных пробелов в именах файлов и путях доступа.

  
  LOCAL lcFileName  
  lcFileName = "C:\Мои документы\Мой файл.txt"  
  MODIFY FILE (m.lcFileName)

 

Как вывести приложение на передний план

Вопрос

Как вывести на передний план и активизировать окно нужного приложения. Это может быть окно Word, или ранее запущенное приложение FoxPro, или форма для ввода логина и пароля в приложении FoxPro (иногда она не выводится на передний план)

Ответ

Идея решения принадлежит Вадиму Пирожкову (piva)

Для вывода окна любого приложения на передний план, необходимо знать текст заголовка нужного окна.

  
  LOCAL loShell, lcCaption  
  loShell=CreateObject("WScript.Shell")      
  lcCaption = "Заголовок_нужного_Окна"  
  IF loShell.AppActivate(m.lcCaption) = .T.  
  	loShell.SendKeys("% ~")  
  ELSE  
  	MessageBox("Приложение "+m.lcCaption+" не найдено")  
  ENDIF

Метод loShell.AppActivate() выполняет поиск приложения по первым символам заголовка нужного окна и активизирует его, что сопровождается выводом окна на передний план. Если указанного окна обнаружить не удалось, то данный метод вернет .F., но никакого сообщения об ошибке не будет.

Проблема в том, что метод loShell.AppActivate() выводит приложение на передний план "как есть". Например, если приложение было свернуто (минимизировано), то оно, конечно, будет активизировано. Но активизировано в том же, свернутом, состоянии.

Чтобы приложение развернуть на весь экран необходимо выполнить некоторые манипуляции в самом приложении. Нажать некую последовательность клавиш. Вот именно этим и занимается метод loShell.SendKeys() - посылает в активное приложение некоторый набор кодов клавиш.

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

Данное меню содержит пункты по управлению размером и положением окна. Причем самый первый пункт этого меню "Восстановить". Этот пункт (поскольку он первый) автоматически становится активным при открытии меню. Остается только нажать клавишу Enter для его выбора.

Вот именно эта последовательность клавиш - ALT+пробел+ENTER - и зашифрована в параметре метода

loShell.SendKeys("% ~")

Если Вы уверены, что активизируемое Вами приложение находится в "нормальном", развернутом состоянии, то вызывать метод loShell.SendKeys() нет необходимости.

Также вызывать именно эту последовательность нажатия клавиш не имеет смысла, если приложение не имеет стандартного системного меню. В FoxPro подавить активизацию системного меню можно, например, установив свойство

_SCREEN.ControlBox = .F.

Условное обозначение кодов клавиш, используемых в методе SendKeys(), приведено в следующей таблице

  
  Key 		Code   
  BACKSPACE 	{BACKSPACE}, {BS}, or {BKSP}   
  BREAK 		{BREAK}   
  CAPS LOCK 	{CAPSLOCK}   
  DEL or DELETE 	{DELETE} or {DEL}   
  DOWN ARROW 	{DOWN}   
  END 		{END}   
  ENTER 		{ENTER}or ~   
  ESC 		{ESC}   
  HELP 		{HELP}   
  HOME 		{HOME}   
  INS or INSERT 	{INSERT} or {INS}   
  LEFT ARROW 	{LEFT}   
  NUM LOCK 	{NUMLOCK}   
  PAGE DOWN 	{PGDN}   
  PAGE UP 	{PGUP}   
  PRINT SCREEN 	{PRTSC}   
  RIGHT ARROW 	{RIGHT}   
  SCROLL LOCK 	{SCROLLLOCK}   
  TAB 		{TAB}   
  UP ARROW 	{UP}   
  F1 		{F1}   
  F2 		{F2}   
  F3 		{F3}   
  F4 		{F4}   
  F5 		{F5}   
  F6 		{F6}   
  F7 		{F7}   
  F8 		{F8}   
  F9 		{F9}   
  F10 		{F10}   
  F11 		{F11}   
  F12 		{F12}   
    
  Key 		Code   
  SHIFT 		+   
  CTRL 		^   
  ALT 		%

Все прочие клавиши в методе SendKeys() выводятся как буквы соответствующих клавишь или их ASCII-коды в функции Chr(). Например, вместо символа "~" можно указать код chr(13) или {ENTER}

  
  loShell.SendKeys("% "+chr(13))  
 * или  
  loShell.SendKeys("% {ENTER}")

Как вывести на передний план текущее приложение FoxPro

В этом случае, заголовком приложения является значение свойства

_SCREEN.Caption

  
  LOCAL loShell  
  loShell=CreateObject("WScript.Shell")      
  loShell.AppActivate(_SCREEN.Caption)

В данном случае, как раз нет смысла использовать метод SendKeys() в силу того, что нужное Вам окно заведомо находится в нужном состоянии.

Как вывести на передний форму со свойством Form.ShowWindow = 2 - As Top-Level

Как правило, такие формы используются для ввода логина и пароля пользователя при открытии приложения. Однако, по умолчанию, на передний план выводится окно собственно среды FoxPro (_SCREEN), если до вызова формы была предпринята попытка активизировать основное окно FoxPro или сделать в него вывод какой-либо информации. Поэтому, зачастую, форма для ввода логина и пароля открывается, но на передний план не выводится.

В этом случае, в качестве заголовка окна используется заголовок формы.

  
 * Вызываю форму для ввода логина и пароля  
  DO FORM MyTopForm.scx    
    
 * Вывожу ее на передний план  
  LOCAL loShell  
  loShell=CreateObject("WScript.Shell")      
  loShell.AppActivate(MyTopForm.Caption)  
    
 * Организую точку останова приложения  
  READ EVENTS

В данном случае, как раз нет смысла использовать метод SendKeys() в силу того, что нужное Вам окно заведомо находится в нужном состоянии.

Как вывести на передний план приложение Word или Excel в котором сформировали отчет

Довольно распространенный способ формирования отчетов - это прямое заполнение документов в Word или Excel. Естественно, после формирования отчета хотелось бы вывести его на передний план.

Приложение Word

  
 * Создание экземпляра приложения Word  
  LOCAL loWord as Word.Application  
  loWord = CreateObject("Word.Application")  
 * Далее идет создание заполнение документа   
  ...  
 * Сформированный документ делается видимым  
  loWord.Visible = .T.  
    
 * Выводится экземпляр приложения Word на передний план  
  LOCAL loShell  
  loShell=CreateObject("WScript.Shell")      
  IF loShell.AppActivate(loWord.Caption) = .T.    
  	loShell.SendKeys("% ~")    
  ELSE    
  	MessageBox("Приложение "+loWord.Caption+" не найдено")    
  ENDIF

Приложение Excel

  
 * Создание экземпляра приложения Excel  
  LOCAL loExcel as Excel.Application  
  loExcel = CreateObject("Excel.Application")  
 * Далее идет создание и заполнение документа  
  ...  
 * Сформированный документ делается видимым  
  loExcel.Visible = .T.  
    
 * Выводится экземпляр приложения Excel на передний план  
  LOCAL loShell  
  loShell=CreateObject("WScript.Shell")      
  IF loShell.AppActivate(loExcel.Caption) = .T.    
  	loShell.SendKeys("% ~")    
  ELSE    
  	MessageBox("Приложение "+loExcel.Caption+" не найдено")    
  ENDIF

Однако в отношении Word или Excel есть специфический способ активизации, основанный на том, что разворачивание этого окна на максимум из свернутого (минимизированного) состояния автоматически приводит к его активизации.

Приложение Word

  
 * Выводится экземпляр приложения Word на передний план  
  #DEFINE wdWindowStateMinimize 2  
  #DEFINE wdWindowStateMaximize 1  
    
  loWord.WindowState = wdWindowStateMinimize	&& Сворачиваем окно  
  loWord.WindowState = wdWindowStateMaximize	&& Разворачиваем окно, что приводит к его активизации  

Приложение Excel

  
 * Выводится экземпляр приложения Excel на передний план  
  #DEFINE xlMinimized -4140  
  #DEFINE xlMaximized -4137  
    
  loExcel.WindowState = xlMinimized	&& Сворачиваем окно  
  loExcel.WindowState = xlMaximized	&& Разворачиваем окно, что приводит к его активизации  

В качестве констант для сворачивания и разворачивания Excel можно также использовать значения 1 и 2 из набора констант Word.

Как получить разницу двух дат в формате: лет, месяцев, дней

Вопрос
Как получить разницу двух дат в формате: лет, месяцев, дней

Ответ

В общем случае, данный вопрос не имеет решения.

Пожалуйста, не надо сразу бросаться писать опровержения, что вот у меня есть готовый код, который это делает или вот есть ссылка на решение. Сначала дочитайте до конца, что именно подразумевается в данном случае под фразой "решения не имеет".

Проблема заключается в том, что нет однозначного определения того, что есть "месяц" и "год". Как правило, под этим подразумевают календарные месяцы. Но сложность в том, что календарный месяц - это понятие не абсолютное, а относительное. Календарный месяц - это не просто некоторое количество дней. Это количество дней, прошедших с определенной даты.

Для лучшего понимания проблемы рассмотрим простой пример.

Надо определить разницу дат между 30 января и 2 апреля одного 2008 года.

Если бы задача заключалась в определении количества дней, то все решалось бы простым вычитанием

?{^2008-04-02} - {^2008-01-30} && 63 дня

А как определить количество месяцев? Результат будет существенно зависеть от того, как именно будет определяться один месяц. Возможно несколько идеологий расчета:

Вариант 1

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

Под термином "месяц" в процессе прибавления подразумевается такая операция, в результате которой номер дня остается тот же самый, а номер месяца увеличивается на 1. Если у нового месяца нет такой даты, то устанавливается дата равная последнему дню нового месяца. Так работает функция GoMonth()

Это значит, что если прибавить один месяц к 30 января, то получим 29 февраля (2008 год - високосный), поскольку в феврале нет 30 числа.

Затем прибавляем к 29 февраля еще один месяц, получаем 29 марта. Прибавлять еще один месяц уже не надо, поскольку результат будет заведомо больше 2 апреля.

Между 29 марта и 2 апреля остается 4 дня. Значит, результат будет 2 месяца и 4 дня.

Заметьте, что в не високосном году результат был бы 2 месяца и 5 дней

Обратите внимание на тот факт, что если начальная дата равна 29, 30 или 31 января результат был бы один и тот же! Поскольку прибавление одного месяца давало бы все то же 29 февраля. Получается парадоксальная ситуация - количество дней разное, а результат одинаковый.

Вариант 2

Алгоритм похож на "Вариант 1", но прибавлять будем не по одному месяцу за раз, а сразу прибавим столько месяцев, чтобы оказаться как можно ближе к конечной дате. Т.е. прибавим к 30 января сразу 2 месяца и получим 30 марта.

Между 30 марта и 2 апреля остается 3 дня. Значит, результат будет 2 месяца и 3 дня.

Заметьте, что если бы конечным месяцем был бы не апрель, а март, то данный вариант расчета ничем не отличался бы от "Варианта 1".

Вариант 3

Под термином "месяц" будем понимать именно календарный месяц. Т.е. февраль - это интервал от 01 февраля до 29 февраля, март - это интервал от 01 марта до 31 марта.

В этом случае имеем 2 полных месяца - февраль и март. И "остатки" от граничных месяцев: 1 день до конца января и 2 дня апреля. Значит, результат будет 2 месяца и 3 дня.

Но в данном случае повезло. Оставшихся дней явно не хватает для полного месяца. А если бы оставшихся дней было бы, например, 40 (с 10 января по 20 апреля)? Встал бы вопрос, сколько дней надо выделить из этого количества дней, чтобы получить еще 1 месяц? Берем количество дней в январе (месяце начала периода) или в апреле (месяце конца периода)?

Вариант 4

Принимаем, что все месяцы имеют одинаковое количество дней, которое вычисляется по следующей формуле:

(365+365+365+366)/4/12 = 30.4375

Тогда количество месяцев - это просто отношение количества дней к данной константе, округленное до целого. Значит, результат будет 2 месяца и 2 дня.

Могут быть и другие варианты расчета.

Как видите, результат существенно зависит от того, что именно понимается под термином "месяц". Точнее, как именно этот самый месяц выделяется в заданном диапазоне. Причем, даже нельзя сказать, что один способ является "правильным", а другие "не правильные". Они все "правильные". Но в рамках определенной идеологии.

Другими словами, вопрос расчета интервала в формате "лет, месяцев, дней" заключается не столько в коде, сколько в идеологии (алгоритме) расчета. Как только будет принята определенная идеология расчета, запрограммировать ее не составит труда.

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

Следует еще заметить, что те же проблемы касаются и определения любых других календарных интервалов. Таких как "неделя", "квартал", "год". Т.е. прежде чем писать код, необходимо описать по каким правилам будут определяться указанные интервалы.

Вы можете поискать уже готовые примеры расчета на данном сайте или в интернете. Но всегда следует иметь в виду, что все эти примеры кодируют определенную идеологию определения месяца и года. К сожалению, как именно (по какой идеологии) происходит это определение из кода далеко не всегда видно. Поэтому, результат работы найденных примеров может оказаться далек от ожидаемых Вами. Каждый пример следует тщательно протестировать для определения его пригодности под Вашу конкретную задачу.

//////////////// ///////////////
Авторизация
*
*
Генерация пароля