| « Поставить закладку » « Сделать стартовой » | |||
|
|||
|
Как создать MFC-приложение с поддержкой нескольких расширений файлов
Вступление Я уже довольно много времени пишу приложения с использованием классов MFC. Это очень хорошая и удобная библиотека классов, плюс к ней добавлен MFC AppWizard, автоматически генерирующий шаблон приложения, куда остаётся вписать только ваш собственный код. И всё же у библиотеки MFC есть один очень существенный минус: если вам нужно сделать что-то, заранее в этой библиотеке не предусмотренное, то приходится тратить очень много времени и сил, изворачиваться, пытаясь вылезти из тех рамок, в которые нас запихнула MFC. Данная статья посвящена созданию приложений, которые работают с несколькими типами файлов. C чего начнём? А начнём мы, как и следует, с точной и чёткой формулировки задачи. В этой статье мы будем создавать однооконное (SDI) приложение, с одним типом документа, с одним типом представления, но с поддержкой файлов нескольких расширений. Простым примером может служить текстовый редактор: скажем, файлы txt и log - они оба текстовые и должны обрабатываться и отображаться одинаково. Может быть и другой вариант: файлы содержат одни и те же данные, но в разных форматах... В общем, вариантов довольно много. Понятно, что у каждого могут быть свои требования, поэтому я постараюсь сделать задачу как можно более общей. Итак, мы будем создавать программу, которая:
Приступаем к выполнению Как несложно догадаться, первым шагом будет создание нового приложения. Здесь надо сделать небольшое лирическое отступление. Дело в том, что решение поставленных задач будет немного отличаться в зависимости от того, в какой среде происходит создание проекта. Поэтому я буду вести параллельно две линии для систем Visual Studio 6.0 и Visual Studio .NET 2003, указывая различающиеся места. Итак, запускаем AppWizard, называем наше приложение SDI. На первом шаге выбираем тип - Single document, ставим галочку Document/View architecture support, английский язык. Второй шаг: базы данных не нужны, ставим None. Третий: контейнеры не нужны, None. Четвёртый: Здесь ставим галочки по желанию, я оставил всё как есть по умолчанию. Нажимаем кнопку Advanced и вводим параметры нового приложения:
Далее можно сразу жать Finish, более-менее важных настроек дальше нет. Компилируем наш чистый проект, и запускаем его. Можно ничего в программе не делать, регистрация файлов происходит автоматически при запуске. Сейчас я хочу сделать ещё одно отступление, посвящённое более подробному разбору того, что мы только что вписывали. Здесь будет рассказано, как файлы регистрируются в системе, и что при этом пишется в реестр. Если кому-то уже всё это известно, следующий параграф можно пропустить. Регистрация файлов в системе Я предполагаю здесь, что читатель уже знаком с тем, что такое реестр и как с ним работать. Если нет - этому посвящено довольно много литературы, поэтому рассказывать здесь основы я не буду. Для того, чтобы Windows умела работать с файлом, задавать ему иконку, менять его контекстное мемню и т.д., необходимо завести в реестре идентификатор файла. Именно его мы указали в поле File type ID. Это по сути название нашего типа файла с точки зрения системы. Все типы файлов хранятся в реестре в ветви [HKEY_CLASSES_ROOT] под своим же названием. Давайте посмотрим в ветвь нашего нового типа файла: [HKEY_CLASSES_ROOTSDI.Document]. В этой ветви параметр (По умолчанию) (в английских версиях Windows - (Default)) содержит название типа файла каким он предстаёт пользователю: откройте любую папку на диске, вызовите контекстное меню, и в нём подменю Создать: вы увидите среди прочих некий "Unnamed file" - ничего не напоминает? Именно это значение мы вписали в поле File new name (long name), и оно же находится в параметре (По умолчанию) ветви [HKEY_CLASSES_ROOTSDI.Document]. Хорошо, идём дальше. Разворачиваем эту веточку и видим два подраздела: DefaultIcon и shell.Второй нам не нужен, а вот первый в том же параметре (По умолчанию) по умолчанию содержит иконку, сопоставленную данному типу файла. Что ж, как описывается тип файла, мы разобрали. Осталось узнать ещё немного: как же тип файла связывается с расширением? А очень просто. В том же разделе реестра [HKEY_CLASSES_ROOT] перечислены все расширения - это разделы, начинающиеся с точки. Найдём там и наше расширение - .ex1 (точка важна!). В параметре (По умолчанию) этого раздела мы видим как раз идентификатор типа, к которому прикреплено данное расширение, а именно - наш SDI.Document. И осталось указать на последнюю деталь, которая нам понадобится в будущем: это наличие подраздела ShellNew в разделе [HKEY_CLASSES_ROOT.ex1]. Именно из-за наличия этого подраздела у нас и появляется новый тип файла в меню Создать. Попробуйте удалить или переименовать ShellNew, и вы увидите, что наш "Unnamed file" исчезнет из этого меню. Продолжим Итак теперь мы знаем смысл трёх из семи заполненных полей. Остальные поля не имеют отношения к реестру, а касаются только самой программы. А именно: Main frame caption - это просто заголовок основного окна приложения; Filter name - фильтр, появляющийся в диалогах открытия и сохранения файлов; Doc type name - имя нашего типа с точки зрения программы (если бы у нас программа поддерживала несколько разных типов документов, то при создании нового выводился бы диалог с предложением выбрать тип создаваемого документа. Doc type name - это название, появляющееся в том списке. В нашем приложении его не будет); File new name (short name) - имя нового докумемнта по умолчанию (в многооконных приложениях при создании нового пустого документа его имя создаётся из этого параметра плюс порядковый номер). Теперь мы полностью знаем смысл всех тех таинственных манипуляций, что совершали раньше. Можно приступать к процессу модификации программы в соответствии с решаемой задачей. Добавление нескольких расширений На данный момент программа поддерживает только одно расширение - EX1. Для того, чтобы добавить нужные нам EX2 и EX3, откроем строковые ресурсы нашей программы и посмотрим на строку с идентификатором IDR_MAINFRAME. Я здесь не буду досконально описывать формат этой строки - большая часть её - это те строки, о которых я уже рассказал, если что-то неясно - вперёд в MSDN! :) Нам сейчас важна следующая часть этой строки: nSDI Files (*.ex1)n.ex1n. Изменим её следующим образом: nSDI Files (*.ex1; *.ex2; *.ex3)n.ex1;.ex2;.ex3n, откомпилируем и запустим наше приложение. В окнах открытия и сохранения мы видим эти самые фильтры и они работают! Неужели так просто? Увы, не совсем... Посмотрите в реестр: наш тип файла связан с весьма любопытным расширением .ex1;.ex2;.ex3. Не слишком-то это похоже на правильную регистрацию нужных нам типов файлов... И вот теперь начинаются танцы с бубном, которые должны привести нас к тому, что мы хотим получить. Итак, у нас проблемы с регистрацией расширений. За регистрацию отвечает метод CWinApp::RegisterShellFileTypes(). Однако зайдя внутрь этого метода, мы видим, что единственное осмысленное действие, которое он выполняет, это вызов
А вот уже метод CDocManager::RegisterShellFileTypes(BOOL bCompat) как раз и выполняет всю работу по регистрации форматов. Значит, именно его нам надо переопределять. Создаём новый класс CDocManagerEx, отнаследованный от CDocManager. Теперь в файле SDI.cpp добавляем строчку а в методе InitInstance() сразу после строчки дописываем строчку
Теперь нам нужно переопределить метод CDocManagerEx::RegisterShellFileTypes(BOOL bCompat). Самый простой способ сделать нормальную реализацию - это скопировать её из исходников MFC. Внимание: в MFC 4.2 в этом методе есть небольшой глючок. В приаттаченном проекте он исправлен (ничего серьёзного, просто регистрация файлов происходила не совсем при тех условиях, что должна бы происходить, судя по логике кода). В MFC 7.1 эта ошибка исправлена. В этом методе используются несколько констант и функций, которые в нашем файле не определены, поэтому их надо перенести из того же файла, откуда мы взяли вышеприведённый код (это файл docmgr.cpp). Эти определения находятся в самом начале этого файла. Для нашего кода нужны все константы и функция _AfxSetRegKey(...). Кроме того, нужно подключить файл afxpriv.h (я сделал это в файле stdafx.h), иначе код не будет компилироваться. Начнём теперь разбираться с нашей RegisterShellFileTypes. Этот метод для каждого зарегестрированного в приложении шаблона документов (он у нас всего один) создаёт в реестре запись с соответствующим идентификатором, регистрирует открытие файлов на свой EXE-файл и устанавливает связь между расширением файла и типом. Если bCompat == TRUE, то попутно ещё регистрируются на тот же EXE-файл команды печати, добавляется запись об иконке для данного типа файлов и прописывается ключик ShellNew для появления нового типа файла в подменю Создать. Для наших целей самым удобным вариантом будет создать отдельный метод для регистрации одного конкретного расширения. Объявим его как Здесь я ввёл следующие параметры:
В этот метод можно перенести чуть ли не всё содержимое метода RegisterShellFileTypes, нужно только убрать цикл, перебирающий все шаблоны документа, и добавить условие проверки флага bShellNew. Далее, вставляем там код, модифицирующий имя типа файла (это нужно, чтобы сделать разные иконки), ещё пара косметических добавлений, и метод готов. Теперь осталось подправить RegisterShellFileTypes. Оставляем там цикл по шаблонам документов (на будущее, вдруг понадобится...), вставляем инкрементирующийся счётчик номера иконки и в этом цикле вызываем RegisterSingleFileType с нужными параметрами. Осталось только добавить ещё две иконки для нужных типов файлов, да решить для себя - нужно ли добавлять этот тип файлов в меню Создать. Я решил эту запись не добавлять. Если кому-то она очень нужна, я думаю, читатель с этой архисложной задачей справится самостоятельно ;-) Диалоги открытия/сохранения На данный момент практически все задачи уже выполнены. Осталась самая малость - подправить диалоги открытия и сохранения файлов так, чтобы они позволяли выбирать нужные нам файловые фильтры. Сначала мы подправим ещё один метод - CSingleDocTemplate::MatchDocType(). В нашем варианте задачи это, в общем-то, необязательно. Метод MatchDocType() отвечает за определение типа открываемого файла. У нас тип один (не путать с расширением!), поэтому особой разницы не будет. Но если кто-то захочет модифицировать программу так, чтобы она поддерживала несколько типов документов, то этот метод будет полезен. Для этого нам нужно создать свой класс, отнаследованный от CSingleDocTemplate. Назовём его CMyDocTemplate, пропишем использование конструктора базового класса и добавим метод
Реализацию берём из исходников MFC и подправляем так, чтобы расширение файла сравнивалось не с шаблонной строкой вида ".ex1;.ex2;.ex3", а по отдельности с каждым из расширений. Для того, чтобы проект компилился, необходимо объявить в файле MyDocTemplate.cpp функцию
Далее, нужно добавить заголовочный файл shlwapi.h (лучше всего это сделать в файле stdafx.h) и подключить библиотеку shlwapi.lib. После всего этого идём в файлик SDI.cpp, пишем там в начале а в методе InitInstance() ищем строчки и заменяем CSingleDocTemplate на CMyDocTemplate .
Так, отлично. Немножко передохнём, и снова в путь. Уже совсем чуть-чуть осталось. За работу с диалогом выбора файла отвечает метод CDocManager::DoPromptFileName(). Класс CDocManagerEx у нас уже есть, поэтому прямо в нём переопределяем метод
В реализации, как обычно, взятой из исходного кода MFC, ищем то место, где добавляется фильтр *.* (это место помечено соответствующим комментарием), и дописываем перед этим наш код:
Для того, чтобы этот метод работал, нам ещё необходимо добавить функцию _AfxAppendFilterSuffix(). Её определение находится в том же файле, что и реализация методов класса CDocManager. Копируем её оттуда и подправляем в соответствии с тем, что у нас сейчас в основном шаблоне несколько расширений. Кстати говоря, эту функцию нужно править только в MFC 4.2. MFC версии 7.1 уже содержит код, который правильно обрабатывает шаблоны с несколькими расширениями. Ну и последний штрих - добавление кода, который сделает нам правильное расширение у безымянного файла:
Этот код добавляется почти в самом конце метода DoPromptFileName(), перед тем, как переменная fileName будет использована. Результаты Ну вот и всё! Наконец-то наша программа готова. Самые нетерпеливые, я думаю, уже откомпилировали и запустили полученный проект, убедившись, что всё теперь работает именно так, как и намечалось (или не убедившись, что маловероятно, но, конечно, полностью не исключено). К этой статье присоединены два проекта - один для Visual Studio 6.0, второй - для Visual Studio .NET 2003. Различия там незначительны, но они есть. Надеюсь, эта статья оказалась вам полезной. Если остались какие-то вопросы или найдены ошибки, то прошу - в комментарии или же лично, по электронной почте. Ссылки на проекты: Рубрика: MFC
HTML 5: пять вещей вызывающих особый интер....
HTML 5 — это грядущее обновление гипертекстового языка разметки, основного способа создания контента для размещения его во всемирной паутине. Разработка HTML остановилась в 1999 году, на версии HTML 4.01 и с тех пор web-содержимое изменилось так, что текущие спецификации HTML перестали соответствовать сегодняшним требованиям. HTML 5 нацелен на то, чтобы увеличить функциональную совместимость HTML и соответствовать растущим требованиям разнообразного и смешанного web-контента. HTML 5 так же нацелен на устранение недостатков четвертой версии. В этой статье мы взглянем на 5 новых интересных вещей в HTML 5.
Подробнее... |
Рубрика: Html
| Добавлено: 22.12.2008
asp.net: ListView с разных сторон.
Элемент управления ListView был представлен в .Net Framework 3.5 как замена устаревшему GridView. Новый элемент имеет более расширенный функционал, чем его предшественник, но в тоже время лишен некоторых внутренних механизмов, что впрочем целиком следствие из расширенной универсальности ListView. Среди отличий ListView и GridView можно назвать и гибкую настройку разметки, что позволяет выводить данные не только в табличном виде, но и вообще в любом каком пожелает программист. Благодаря шаблонам ItemTemplate, EditItemTemplate, InsertItemTeplate можно настроить внешний вид при любом из состояний ListView: редактировании или выборе элемента.
Подробнее... |
Рубрика: .NET компоненты
| Добавлено: 22.12.2008
Создание кросс-таб отчета в Stimulsoft Rep....
Компания Стимулсофт предоставляет для разработчиков мощный набор инструментов для создания отчетов для Microsoft Visual Studio .Net 2005 и 2008; эти инструменты доступны как для Windows Forms, так и для Web Forms. Это генератор отчетов Stimulsoft Reports.Net. Генератор отчетов Stimulsoft Reports.Net имеет ряд особенностей: простая работа с дизайнером отчетов, полная поддержка экспорта в PDF, Word, Excel и многие другие форматы. Crystal Report и Microsoft Reporting Service – очень хорошие программные продукты для повседневной работы, но, если Вам необходимо создать отчеты с поддержкой кросс-табов, drill down, Ajax, штрих-кодов и возможностью подключения одновременно более одного источника данных, то Stimulsoft Reports.Net поможет Вам сэкономить массу времени. Также, данный генератор отчетов позволяет пользователям создавать свои собственные отчеты любой сложности. И все эти особенности делают Stimulsoft Reports.Net хорошим выбором в сфере программных продуктов для Business Intelligence.
Подробнее... |
Рубрика: .NET компоненты
| Добавлено: 22.12.2008
Остальные статьи: |
Цитата дня (все,добавить):
|
Realcoding.NET
© 2003-2008 |
Контакты |
Реклама на сайте
|