Программирование на Visual C++. Архив рассылки - Алекс Jenter
Шрифт:
Интервал:
Закладка:
В MFC существует специальный класс, под названием CCmdTarget. Наследуя свои классы от cCmdtarget, вы можете обеспечить для них необходимую функциональность в dispatch виде – как раз как ее понимают скрипты. При созднании нового класса в ClassWizard (View>ClassWizard>Add Class>New), наследуемого от cСmdtarget, просто щелкните на кнопке Automation или Creatable by ID, чтобы обеспечить возможность создания экземпляра объекта по его ProgID. Замечу, что для программ, реализующих внутреннюю автоматизацию, это не нужно. Для приложений, реализующих внешнуюю и смешанную автоматизацию, это необходимо для "корневых" объектов.
После создания такого объекта, ClassWizard создает интерфейс ITestAutomatedClass (это dispatch интерфейс, т.е. наследуется от IDispatch), который реализуется моим CTestAutomatedClass. Теперь к этому интерфейсу я могу добавить методы или свойства, которые автоматически будут реализованы в CTestAutomatedClass. Я добавил свойство Age.
COM-объекты, коим и является наш CTestAutomatedClass, можно создавать только динамически. Это связано с тем, что объект может использоваться несколькими приложениями одновременно, а значит, удаление объекта из памяти не может выполнить ни одно из них. Разумно предположить, что объект сам должен отвечать за свое удаление. Такой механизм реализован при помощи механизма ссылок (reference count). Когда приложение получает указатель на объект, он увеличивает свой внутренний счетчик ссылок, а когда приложение освобождает объект – счетчик ссылок уменьшается. При достижении счетчиком нуля, объект удаляет сам себя. Если наш объект был создан по ProgID другим приложением, то программа CTestApp (другими словами, Automation-Server) не завершится до тех пор, пока счетчик ссылок CTestAutomatedClass не станет равным нулю.
Создаваемые через ProgID COM-объекты, обычно являются Proxy-компонентами. Реально они не содержат никакой функциональности, но имеют доступ к приложению и его внутренним, не доступным извне, функциям. Хотя можно организовать все таким образом, чтобы всегда создавался только один COM-объект, а все остальные вызовы на создание возвращали указатели на него.
Метод интерфейса CCmdTarget GetIDispatch(), позволяет получить указатель на реализованный интерфейс IDispatch. В параметрах можно указать, нужно ли увеличивать счетчик ссылок или нет.
В следующей статье, посвященной использованию функциональности WebBrowser Control, я обращусь к практическому применению dispatch-объектов программы в скриптах. А в дальнейшем, мы поговорим о внедрении процессора скриптов в собственные приложения.
ВОПРОС-ОТВЕТQ. Как сделать так, чтобы программа сама себя могла стереть, т.е. свой *.exe файл?
LowFeaRA1 Удалить программу в тот момент, когда она запущена, не представляется возможным (во всяком случае такая возможность мне не знакома), остается удаление после завершения ее выполнения. Идея следующая: при выходе из программы создать BAT-файл, который ждет до тех пор, пока файл можно будет удалить (программа завершит работу), удаляет файл программы и себя, и запустить его:
void MyDlg::OnDestroy() {
CDialog::OnDestroy();
const char *AppName=AfxGetApp()->m_pszExeName;
FILE *f=fopen("selfdel.bat","w+");
fprintf(f, ":dcn"
"del %s.exen"
"if exist %s.exe goto dcn"
"del selfdel.bat", AppName, AppName);
fclose(f);
WinExec("selfdel.bat",FALSE);
}
Преимущества:
-файл удаляется сразу в тот момент, когда это становится возможно
Недостатки:
-если запустить два экземпляра приложения, то после завершения работы первого мы получаем цикл активного ожидания до тех пор пока не завершится второй экземпляр (это незаметно в W95/98, но в NT в окне Task Manager можно заметить полную загрузку процессора). Также пользователь все это время будет удивляться наличию невесть откуда взявшегося файла sefdel.bat.
Майкрософт же предлагает свой способ решения проблемы, причем его реализация отличается для WinNT и Win95/98. Удаление (переименование, замещение, и т.д.) файла происходит во время следующей перезагрузки системы.
Win95/98: В процессе перезагрузки системы запускается утилита wininit.exe, которая осуществляет заданные действия над файлами, указанные в секции [rename] файла wininit.ini. При этом т.к. wininit.exe запускается еще до того как запущена система поддержки длинных имен файлов, все имена должны быть указаны в формате DOS (8.3).
Последовательность действий для удаления или переименования файла:
1. Проверить наличие файла WININIT.INI в директории Windows
2. Если WININIT.INI существует, открываем его и добавляем новые строки в секцию [rename]. Если файла нет, создаем его и секцию [rename] в нем. 3.Добавляем строки следующего формата в секцию [rename]:
DestinationFileName=SourceFileName
Оба пути DestinationFileName и SourceFileName не должны содержать длинных имен. Приемник и источник должны находится на одном диске. Для удаления файла вместо DestinationFileName использовать NUL.
WinNT:
Здесь все сделано по-человечески. Для удаления файла следует использовать функцию MoveFileEx():
MoveFileEx(szSrcFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
где szSrcFile – имя файла или директории
Преимущества:
-"Лицензированный" метод Майкрософт
Недостатки:
-Чрезмерно утяжеленная процедура редактирования wininit.ini, проблемы при работе с длинными именами Win95/98,
-Удаление происходит только в момент перезагрузки.
Bad SectorA2 Программа не может удалить свой exe-файл, пока она работает. Это фундаментальное правило при работе под Windows. Поэтому всё, что остаётся – это поручить удаление другому процессу перед тем как завершить свой.
Самый простой вариант – создать на лету и запустить bat-файл, который дождётся завершения нашего процесса, а затем удалит его exe-файл. Более сложные варианты подразумевают создание в чужом процессе (например, в Task Manager) рабочего потока, который опять же дождётся завершения нашего процесса и убьёт файл.
Вот пример функции, которая создаёт bat-файл и запускает его, чтобы убить наш exe-файл. Лучше всего вызывать её непосредственно перед завершением нашего процесса.
void DelSelf() {
// Получаем свой путь
char szExePath[MAX_PATH];
GetModuleFileName(NULL, szExePath, MAX_PATH);
// Создаём bat-файл
static char szBat[] = ":Looprn"
"del %1rn"
"if exist %1 goto Looprn"
"del %0";
HANDLE hFile = CreateFile("__delself.bat", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
DWORD temp;
WriteFile(hFile, (LPVOID)szBat, strlen(szBat), &temp, NULL);
CloseHandle(hFile);
// Запускаем его
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW;
PROCESS_INFORMATION pi;
char szCommand[MAX_PATH+15] = "__delself.bat ";
strcat(szCommand, szExePath);
CreateProcess(NULL, szCommand, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi);
return;
}
Замечу, что это только пример, который можно улучшать в различных направлениях. Можно, скажем, получать имя bat-файла через GetTempFileName, чтобы гарантировать его уникальность. Или понизить приоритет создаваемого из bat-файла процесса до минимума, чтобы он кушал поменьше ресурсов в процессе циклической проверки существования exe-файла.
Александр Шаргин ([email protected]) В ПОИСКАХ ИСТИНЫQ. Есть у меня файлы с расширением .pdb (Microsoft C/C++ program database 2.00) (их MS VC++ делает, в папке Debug проекта создаются), можно ли с их помощью восстановить исходники программы (размер у них такой, что туда не только прога влезет, но и комментарии к ней в HTML (FrontPage Style) формате :)
Andrey ShtukaturovПока все. До скорого!
Алекс Jenter [email protected] Красноярск, 2001.Программирование на Visual C++
Выпуск №33 от 18 февраля 2001 г.
Приветствую!
Сегодня хочу предложить вашему вниманию заключительную часть статьи про ODBC нашего постоянного автора Александра Шаргина, которая, если вы помните, открывает цикл статей о технологиях доступа к данным.
СТАТЬЯ
Доступ к БД с использованием ODBC
Часть 2
Автор: Александр Шаргин
В предыдущей части статьи мы с вами рассмотрели основы использования ODBC для доступа к базам данных. Но в процессе знакомства с этой технологией у каждого естественным образом возникает целый ряд вопросов. Ответам на самые распространённые из них и будет посвящена вторая часть статьи.
Перемещение на другие компьютерыПри переносе программы, использующей ODBC, на другой компьютер возникает целый ряд проблем. Во-первых, на нём может не оказаться нужных компонентов ODBC, а значит, их необходимо распространять вместе с программой. Во-вторых, на другом компьютере придётся заново регистрировать источник данных, причём желательно, чтобы пользователь об этом не знал. В этом разделе я расскажу, как всё это делается.