module.obj : error LNK2019: unresolved external symbol __imp__mciSendCommandA@16 referenced in function "long __stdcall WndProc(struct HWND__ *, unsigned int, unsigned int, long)" (?WndProc@@YGJPAUHWND__@@IIJ@Z)
Устранил ошибку следующим образом: - Нашёл (через Help\Index...) подпрограмму mciSendCommand. В конце справки там была указана используемая библиотека с этой функцией - Winmm.lib. - Через Project\ИмяПроекта Properties...\Configuration Properties\Linker\Input\Additional Dependencies добавил в список библиотек Winmm.lib.
Полную информацию по процессу линковки даёт опция линкера /VERBOSE, устанавливается через Project\ИмяПроекта Properties...\Configuration Properties\Linker\General\ShowProgress\Display All Progress Messages (/VERBOSE). Иногда Help\Index... даёт неверное имя библиотеки. Тогда может помочь поиск по содержимому всех файлов c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Lib\*.lib, на появление строки с искомой функцией (для нашего примера mciSendCommand). Один из найденных файлов надо добавить в список библиотек (через Project\ИмяПроекта Properties...\Configuration Properties\Linker\Input\Additional Dependencies).
Для получения помощи по функции в коде иногда лучше не просто нажать F1, когда курсор стоит на имени функции, а скопировать имя функции в поле LookFor окна поиска Help\Index... - тогда будут видны все варианты описаний прототипов функции.
Изменение поведения (кода) методов класса - открыть исходный файл *.cpp нашего нового класса, и выбрать в меню View\Properties Window (или нажать Alt+Enter). В появившемся окне нажать кнопку Overrides (на ней значок в виде зелёного кубика). Появится список методов класса. Выбираем нужный метод, выбираем из выпадающего списка имя_метода. В модуль класса вставляется пустышка, которую предстоит изменить для доработки поведения метода класса.
Что происходит, когда мы добавляем (привязываем) переменную к ресурсу (например, полю ввода)? Когда мы добавляем переменную, связанную с ресурсом окна ввода, то на самом деле в *.h-файл, описывающий класс главного окна (например, диалога, в котором есть окно ввода), добавляется в раздел public класса главного окна добавляется переменная. В моём примере *.h-файл ciconfigDlg.h описывал класс главного диалогового окна CciconfigDlg (тип CDialog), и в этом файле в раздел public описания класса CciconfigDlg добавлялась переменная CEdit IPaddress.
Два метода получить текст из переменной типа CEdit (переменной, связанной с окном ввода). В нашем случае переменная, куда считываем текст, это CString ip, а переменная CEdit IPaddress связана с ресурсом окна ввода IDC_EDIT1.
Когда меняем свойства ресурса, то меняется его текстовое описание в файле *.rc. Например, имеем IDC_LIST1 (окно - список вывода) и связанную с ним переменную CListBox LogList. Если у IDC_LIST1 свойство Sort == False, то описание у него в *.rc-файле будет таким:
Указатель на экземпляр класса диалогового окна можно получить с помощью ключевого слова this
void CciconfigDlg::OnBnClickedButton1(){// TODO: Add your control notification handler code here
...
pSocket= new CClientSocket(this);
...}
В этом примере вызов конструктора CClientSocket требует параметра, который содержит указатель на главное окно диалога (им инициализируется внутренняя переменная).
Ошибка Compiler Error C2582 'operator =' function is unavailable in 'class' возникает, когда в правой части стоит указатель на экземпляр класса, в котором нет определения оператора "=". Например, наш класс, не имеющий этого оператора CciconfigDlg. На выражение присваивания
Здесь bbb произвольное имя. Кроме того, эта ошибка может возникнуть тогда, когда в левой части выражения с "=" стоит не указатель на экземпляр класса CciconfigDlg, а сам экземпляр класса, например, переменная m_Dlg определена ошибочно вот так:
class CClientSocket : public CSocket{
public:
..
protected:
CciconfigDlg m_Dlg;};
а нужно вот так, тогда ошибки C2582 не будет даже без определения оператора "=" в классе CciconfigDlg:
class CClientSocket : public CSocket{
public:
..
protected:
CciconfigDlg* m_Dlg;};
При работе с CSocket нужно быть осторожным с ->Close() и delete - например, эти функции вызывать в обработчике кнопки, в котором создался экземпляр CSocket, иначе не получает управления обработчик приёма ::OnReceive.
Нельзя никакие операции, вызывающие блокировку, выполнять в обработчиках событий кнопок, иначе приложение "виснет", пока не закончится операция. Такие операции нужно запускать в отдельном потоке.
Частенько бывает что нужно воспользоваться некоторыми функциями WinAPI, но если проект создается с поддержкой MFC, то ничего не выходит. Для вызова функций WinApi из MFC используй оператор :: перед именем функции, например ::GetWindowText(hWnd, ....
В Visual Studio есть очень удобный менеджер конфигураций проекта. Конфигурация - это набор установок для компилируемой и линкуемой программы. По умолчанию существует 2 конфигурации - Debug и Release, назначение которых очевидно из названия. Когда я научился их применять (достаточно в тулбаре выбрать одну из них в выпадающем списке) - одну для отладки, другую для получения готового приложения, то мне захотелось автоматически упаковывать exe-шник релиза упаковщиком AsPack. Оказалось, для этого можно воспользоваться Build Evens:
- Project\имя_приложения Properties..., выбираем тип конфигурации Release - Build Events\Post Build Event\Command Line, вставляем туда "c:\Program Files\AsPack\ASPack.exe" "$(ProjectDir)$(OutDir)\$(TargetFileName)" /Q /B-
Здесь "$(ProjectDir)$(OutDir)\$(TargetFileName)" означает полный путь к упаковываемому экзешнику (путь этот составлен из специальных макроопределений Visual Studio), а /Q /B- просто опции для AsPack, которые указывают без вопросов всё сделать и не делать бэкапа.
- для отладки макроопределений удобно в командную строку Build Event подставить команду echo макроопределение(я) >testfile.txt. В результате в файле testfile.txt можно прочитать, что подставляется вместо макроопределения.
Глобальные переменные в C++ можно определять как внутри основного тела программы _tmain, так и до нее - сразу после одной или нескольких (если есть) строк include. Инициализировать переменные, объявленные до _tmain, можно только вместе с объявлением.
Уже существующие (написанные ранее) процедуры из файлов *.cpp удобно использовать следующим образом:
- наш *.cpp файл с нужными функциями переписываем в корневую папку проекта; - добавляем *.cpp файл в проект - в Solution Explorer щёлкаем правой кнопкой на папке Source Files, выбираем Add\Add Existing Item..., выбираем наш файл *.cpp; - в тело модуля, где вызывается процедура любая процедура из *.cpp (в этом примере процедура WLog), вставляем в глобальный блок объявления extern:
В процессе написания программы целесообразно изредка переключаться в режим компилирования Release, потому что при этом могут вылезать неожиданные ошибки (не проявляющие себя в режиме компилирования Debug).
Оказывается, любые константы, у которых не указана разрядность (или тип), по умолчанию считаются компилятором за 32-х разрядные, то есть, например, если мы укажем 0xE0, то это на самом деле будет означать 0x000000E0. На первый взгляд, ну и что тут такого? Однако из-за приведения типов это может порождать непредсказуемый результат. Например, вариант 1 нерабочий, а вариант 2 работает нормально.
В варианте 1 по метке case 0xE0 управление НИКОГДА не передаётся, независимо от значения переменной ch0. Дизассемблирование показывает, что в начале оператора switch происходит приведение переменной к типу int с помощью инструкции movsx, которая заполняет все старшие биты слова битом 7 значения 0xE0, то есть получается при этом 0xFFFFFFE0. Далее это значение по метке case 0xE0 уже сравнивается с 0x0000000E0. Пример дизассемблированного кода варианта 1:
switch (ch0) 0049436Amovsxeax,byteptr[ch0];тут 0xE0 превращается в 0xFFFFFFE0 0049436Emovdwordptr[ebp-190h],eax 00494374cmpdwordptr[ebp-190h],0 0049437Bje 0049438B 0049437Dcmpdwordptr[ebp-190h],0E0h; а тут сравн. 0xFFFFFFE0 с 0x000000E0 00494387je00494391; по метке 00494391 управление НИКОГДА не передается 00494389jmp00494397{
case 0x00:... 0049438B...break; 0049438Fjmp 004943A3
case 0xE0:... 00494391...break; 00494395jmp 004943A3 default:... 00494397... 0049439A... 0049439B... 004943A0... } 004943A3jmp ...
Само собой, 0xFFFFFFE0 никогда не равняется 0x0000000E0, и код не работает.
Перехват Ctrl-Break в консольном приложении можно установить с помощью функции SetConsoleCtrlHandler - позволяет, кроме того, реагировать на Ctrl-C и события закрытия консоли. Пример:
1. Описываем обработчик консоли:
BOOL CtrlHandler( DWORD fdwCtrlType )/* Обработчик исключительных ситуаций консоли, типа нажатия на Ctrl-Break или щелчка на кнопке с крестиком */{
CString ctrl_message_descr;switch( fdwCtrlType ){// Handle the CTRL-C signal. case CTRL_C_EVENT://поскольку мы запустили поток, обрабатывающий клавиатурные// нажатия, сюда управление никогда не попадает
ctrl_message_descr ="Ctrl-C event";
WLog (ctrl_message_descr);return( TRUE );// CTRL-CLOSE: confirm that the user wants to exit. case CTRL_CLOSE_EVENT:
ctrl_message_descr ="Ctrl-Close event";
WLog (ctrl_message_descr);
CleanUp();
bStop =true;
Beep(600, 200);return( FALSE );// Pass other signals to the next handler. case CTRL_BREAK_EVENT:
ctrl_message_descr ="Ctrl-Break event";
WLog (ctrl_message_descr);printf(ctrl_message_descr);
CleanUp();
bStop =true;
Beep(900, 200);return FALSE;case CTRL_LOGOFF_EVENT:
ctrl_message_descr ="Ctrl-Logoff event";
WLog (ctrl_message_descr);printf(ctrl_message_descr);
CleanUp();
bStop =true;
Beep(1000, 200);return FALSE;case CTRL_SHUTDOWN_EVENT:
ctrl_message_descr ="Ctrl-Shutdown event";
WLog (ctrl_message_descr);printf(ctrl_message_descr);
CleanUp();
bStop =true;
Beep(750, 500);return FALSE;default:
ctrl_message_descr ="Unknown event!!!";
WLog (ctrl_message_descr);printf(ctrl_message_descr);
CleanUp();
bStop =true;return FALSE;}}
2. Инициализация в начале main:
...if( SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE )){if(iDebugLevel ==1)
WLog("Control Handler (Ctrl-Break interception) is installed.");}else{
messtr ="ERROR: Could not set control handler";
WLog(messtr);printf(messtr);
nRetCode =2;
Error =true;}
...
Многострочное окно текста можно выводить, воспользовавшись простым Edit Control (обычно он используется для редактирования однострочного текста). По шагам:
- перетаскиваем его на конструируемое окно. - меняем размеры перетаскиванием границ. Для того, чтобы выбрать наш Edit Control, можно пользоваться выпадающим списком панели Properties (иногда иначе не получается, например, если Edit Control лежит поверх какого-нибудь Tab Control). - устанавливаем у Edit Control свойство Multiline = True - при необходимости включаем полосы прокрутки (Horizontal Scroll и/или Vertical Scroll присваиваем True) - привязываем к нашему Edit Control переменную - правой кнопкой Add Variable..., из выпадающего списка Control ID выбираем наш Edit Control (например IDC_EDIT1), указываем Variable name (например, edVar). - для вывода текста используем метод SetWindowText (смотри также другие методы класса CEdit в справке Visual Studio), например:
edVar.SetWindowText("Hello World!");
Чтобы вывести несколько строк, используйте разделитель \r\n, например:
edVar.SetWindowText("Line 1rnLine 2");
Чтобы очистить окно текста, используем:
edVar.SetWindowText("");
Чтобы добавить текст в конец, используем:
CString csTempText;
edVar.GetWindowText(csTempText);
csTempText +="этот текст добавим ";
edVar.SetWindowText(csTempText);
- создаем новый проект, тип проекта Visual C++ Projects\MFC\MFC Application, даем название проекту (пусть это будет test003). - открывается MFC Application Wizard. Выбираем Application Type\Dialog based, Use MFC in a static library, жмем Finish. - открываем Resource View, выбираем test003\test003.rc\Dialog\IDD_TEST003_DIALOG, двойным щелчком открываем редактор диалога. - удаляем с формы диалога "TODO: Place dialog controls here.". Находим на Toolbox элемент Tab Control, перетаскиваем его на форму диалога, меняем размеры по вкусу. - привязываем к нашему Tab Control переменную - правая кнопка на нём, выбираем Add Variable..., Variable name указываем m_Tabs, жмем Finish. - Теперь если запустить программу, то у Tab Control закладок не будет. Нужен код, который изначально создает 2 закладки. Этот код добавляем в место, где инициализируется наш диалог - в метод Ctest003Dlg::OnInitDialog(), после подсказки "TODO: Add extra initialization here":
TC_ITEM tci;// эта структура нужна для вставки закладкиmemset(&tci,0,sizeof(tci)); // очистка структуры
tci.mask= TCIF_TEXT;// у закладки будет только текст
tci.pszText="Закладка 1";
m_Tabs.InsertItem(0, &tci);// первая закладка имеет индекс 0
tci.pszText="Закладка 2";
m_Tabs.InsertItem(1, &tci);// вставляем вторую закладку
- если запустить программу, то увидим закладки. Для того, чтобы поместить на закладки содержимое, нужно предварительно для каждой закладки подготовить форму диалога и нарисовать на каждой форме свои элементы управления. Для этого в Resource View на папке Dialog щелкаем правую кнопку, выбираем Add Resource..., в появившемся окошке выбираем Dialog и нажимаем кнопку New. Переименуем наш ресурс - Resource View, выбираем test003\test003.rc\Dialog\, правый щелчок на IDD_DIALOG1, выбираем Properties. Свойство ID содержит имя IDD_DIALOG1, меняем его на IDD_TABPAGE1. - меняем свойства и содержимое нашей закладки - двойной щелчок на Resource View\test003\test003.rc\Dialog\IDD_TABPAGE1, в панели Properties откроются свойства нашей закладки. Меняем свойство Style на Child, свойство Border на None, удаляем кнопки Ok и Cancel, чтобы осталась чистая форма. - делаем копию созданной закладки - правая кнопка на Resource View\test003\test003.rc\Dialog\IDD_TABPAGE1, выбираем Copy, правая кнопка на Resource View\test003\test003.rc\Dialog, выбираем Paste. Появляется IDD_TABPAGE2. - добавляем элементы управления, которые нам нужны - предположим, на первой страничке будет строка редактирования Edit Control, а на второй страничке будет IP Address Control. Перетаскиваем с Toolbox соответствующие элементы на каждую страничку IDD_TABPAGE1 и IDD_TABPAGE2. - добавляем к каждой страничке свой класс - правый щелчок на страничке в визуальном редакторе, выбираем Add Class..., указываем Class name CTabPage1, Base Class выбираем CDialog, жмем Finish. Для второй странички делаем тоже самое, имя класса указываем CTabPage2. Эти классы нужны, чтобы в обработчике смены закладок запускать создание соответствующей закладки вызовом конструктора класса (например, new CTabPage1). - чтобы классы были CTabPage1 и CTabPage2 были видны в реализации основного класса диалога программы, в начало файла test003Dlg.cpp добавим строчки:
#include "TabPage1.h"#include "TabPage2.h"
- добавляем в описание класса Ctest003Dlg (файл test003Dlg.h) переменную m_pTabDialog - указатель на память, в которой будет храниться отображаемая страничка Tab Control (этот указатель присваивается вызовом конструктора классов CTabPage1 и CTabPage2):
- Нужно добавить обработчик события смены закладки. Двойным щелчком открываем Resource View\test003\test003.rc\Dialog\IDD_TEST003_DIALOG (нашу главную форму диалога), правая кнопка на Tab Control, выбираем Add Event Handler..., проследим, чтобы в окне Message type было выбрано TCN_SELCHANGE, жмем кнопку Add and Edit. - Теперь добавим код в обработчик события смены закладки:
void Ctest003Dlg::OnTcnSelchangeTab1(NMHDR *pNMHDR, LRESULT *pResult){// TODO: Add your control notification handler code hereint id;// ID диалога// надо сначала удалить предыдущий диалог в Tab Control'е:if(m_pTabDialog){
m_pTabDialog->DestroyWindow();delete m_pTabDialog;}// теперь в зависимости от того, какая закладка выбрана, // выбираем соотв. диалогswitch( m_Tab.GetCurSel()+1)// +1 для того, чтобы порядковые номера закладок// и диалогов совпадали с номерами в case{// первая закладкаcase1:
id = IDD_TABPAGE1;
m_pTabDialog =new CTabPage1;//вызываем конструктор класса// тип указателя соответствует нужному диалогу,// иначе добавленный в диалог код не будет функционироватьbreak;// вторая закладкаcase2:
id = IDD_TABPAGE2;
m_pTabDialog =new CTabPage2;//вызываем конструктор классаbreak;// все остальные закладки, если они есть,// будут здесь тоже представлены, каждая - отдельным case// а если обработка такого номера не предусмотренаdefault:
m_pTabDialog =new CDialog;// новый пустой диалогreturn;}// end switch// создаем диалог
m_pTabDialog->Create (id, (CWnd*)&m_Tabs);//параметры: ресурс диалога и родитель
CRect rc;
m_Tab.GetWindowRect(&rc);// получаем "рабочую область"
m_Tab.ScreenToClient(&rc);// преобразуем в относительные координаты// исключаем область, где отображаются названия закладок:
m_Tab.AdjustRect(FALSE, &rc);// помещаем диалог на место..
m_pTabDialog->MoveWindow (&rc);// и показываем:
m_pTabDialog->ShowWindow ( SW_SHOWNORMAL );
m_pTabDialog->UpdateWindow();*pResult =0;}
- все, программа работоспособна, за исключением того, что при запуске не отображается содержимое первой закладки. Для этого в BOOL Ctest003Dlg::OnInitDialog() нужно добавить код:
Это можно сделать, удалив из дерева Solution Explorer\имя_проекта\Source Files\имя_класа.cpp и Solution Explorer\имя_проекта\Header Files\имя_класа.h. Соответствующий класс автоматически пропадет из списка Class View. При этом файлы имя_класа.cpp и имя_класа.h с диска не удаляются, это нужно сделать вручную. В противном случае при попытке создать класс с тем же именем появится предупреждающее сообщение.
- чтобы поменять высоту выпадающего списка, нужно щелкнуть в визуальном редакторе ресурсов на стрелочку вниз в CComboBox. Появится возможность поменять высоту выпадающего списка перетаскиванием нижнего квадратика. При этом в файле *.rc, в разделе "Dialog" меняется значение высоты выпадающего списка (здесь число 134):
- свойство Type меняет поведение CComboBox. Если выбран тип Dropdown, то в окне редактирования можно вводить текст, если выбрано Drop List, то вводить ничего нельзя, можно только выбирать из списка. - в свойство Data можно добавлять свои строки в режиме редактирования ресурса, каждая строка отделяется от другой точкой с запятой. - в режиме выполнения очищается список функцией ResetContent(), добавляются строки в конец списка AddString(), вставляются InsertString(). В InsertString() указывается индекс строки, перед которой будет вставка, если указать -1, то вставка будет в конец, по аналогии с AddString(). - SetCurSel() выбирает строку, параметром является индекс выбираемой строки. 0 означает 1-ю строку, 1 - вторую и т. д. (если указать -1, то не будет выбрана никакая строка).
Здесь показано, как выполнить действия по завершению программы. Для этого в момент инициализации (для приложения - диалогового окна это будет, например, тело BOOL имя_класса_диалога::OnInitDialog()) запускается подпрограмма atexit(). Пример:
...void OnExit (void){
Beep(4000, 200);}
...BOOL Cping5bDlg::OnInitDialog(){
... // TODO: Add extra initialization here
... atexit(OnExit); return TRUE;// return TRUE unless you set the focus to a control}
Можно также использовать функцию _onexit(), пример:
int OnExit (){
Beep(4000, 200);
CString param_list;
param_list ="";for(int i=0; i<Include.GetSize();i++)
param_list += Include.GetAt(i)+";";
csSaveIni("common_settings", "Include", param_list);return0;}
...BOOL CsyslogvDlg::OnInitDialog(){
...
// TODO: Add extra initialization here
...
_onexit(OnExit);return TRUE;// return TRUE unless you set the focus to a control}
Ограничения - в теле OnExit нельзя использовать переменные, связанные с окнами, поскольку они уже уничтожены; нужно использовать заранее заполненные глобальные переменные (в этом примере массив Include.). Кроме того, у меня не получилось объявить метод OnExit как экземпляр класса окна CsyslogvDlg (в целях доступа к переменным класса), пришлось сделать простую глобальную процедуру. См. также пример, как перехватывать момент выхода из консоли (функция SetConsoleCtrlHandler()).
Compiling... имя_файла.cpp c:\VisualStudioProjects\ping5b\имя_файла.cpp(33) : fatal error C1010: unexpected end of file while looking for precompiled header directive
Build log was saved at "file://c:\VisualStudioProjects\ping5b\Debug\BuildLog.htm" ping5b - 1 error(s), 0 warning(s)
Нужно добраться до свойств объекта CWinApp, а там обилие возможностей - например, у него есть переменная m_pszExeName, которая как раз и содержит имя исполняемого файла без расширения. Если, например, у вас приложение типа MFC-диалог, то у него обязательно есть класс приложения, обычно называемый Cимя_программыApp (реализация класса в имя_программы.cpp, заголовочный файл имя_программы.h). Этот класс Cимя_программыApp является производным от класса CWinApp (задается в имя_программы.h). Экземпляр класса объявлен в имя_программы.cpp, и обычно носит имя theApp. Что нужно сделать по шагам:
- узнаем название класса приложения (виден в корне дерева Class View, название класса заканчивается на App). Например, название класса Cping5bApp. - ищем название экземпляра класса (переменной) - либо в помощью поиска по файлам на название класса Cping5bApp, либо открыв файл определения класса, в нашем случае это будет ping5b.cpp. В этом файле будет строка, в которой и будет задаваться переменная класса:
Cping5bApp theApp;
- в файле, где нужно обратиться к theApp.m_pszExeName, нужно добавить в начало строку для нашего примера:
#include "ping5b.h"
Теперь в этом файле можно обращаться к переменным и методам объекта theApp:
CWnd->EnableWindow(false);//запрещает окно (кнопка, список, любой Control)
CWnd->EnableWindow(true);//разрешает окно (кнопка, список, любой Control)
CWnd->IsWindowEnabled();//получить состояние запрещенности окна
Поскольку все Control-ы являются наследниками класса CWnd, то в вышеуказанных выражениях нужно просто подставить указатель на переменную нужного Control.
У этого метода куча недостатков. Текст выводится некультяпистым шрифтом на белом фоне, так что на серой форме это будет смотреться некрасиво - происходит из-за того, что по умолчанию используется для вывода непрозрачный фон (OPAQUE). Тот же эффект дает вызов:
dc->SetBkMode(OPAQUE);
Чтобы это поправить, можно использовать
dc->SetBkMode(TRANSPARENT);
Теперь фон текста будет прозрачный.
Текст рисуется поверх всех элементов управления на окне, как будто их и не было. Если текст попадает при выводе за пределы окна, он обрезается или не прорисовывается. При передвижении окна или перерисовке нарисованный текст будет пропадать. Это легко исправить, если код поместить в OnPaint.
Разница в работе GetWindowDC() и GetDC() в том, что GetDC() выводит тест относительно верхнего левого угла серой формы, сразу под голубой плашкой окна, а GetWindowDC() выводит тест относительно верхнего левого угла всего окна целиком, вместе с плашкой.
int MessageBox(
HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType);
Вторую разновидность нужно вызывать как ::MessageBox, иначе она в зависимости от контекста окажется принадлежащей какому-нибудь оконному классу. Если вместо hWnd указать NULL, то отображаемое окно не будет иметь родительского окна. Это означает, что он становится "независимым" от основной программы - и к окну программы, и к окошку MessageBox можно одновременно получить доступ через интерфейс GUI. Если же в качестве параметра hWnd указать m_hWnd, то появляющееся окно MessageBox станет модальным - не закрыв его, нельзя получить доступ в главное окно. Остальные параметры у обоих функций MessageBox эквивалентны, как и возвращаемые значения, и подробно описаны в MSDN.
Класс CListCtrl предоставляет возможность создать список а-ля списка окна Explorer - объектами списка могут быть маленькие иконки, большие иконки (туда можно загрузить картинку), список может быть простой и расширенный, в виде таблицы. Отображение зависит от стиля, который можно назначить при разработке программы (в окне свойств Properties меняем значение свойства View, выбрав из выпадающего списка один из вариантов Icon, Small Icon, List или Report). Манипулируем экземпляром CListCtrl, привязав, как обычно, к нему переменную, и потом в коде обращаемся к методам этой переменной. Вот примеры действий над экземпляром CListCtrl:
1. Получить размеры
CRect rect;
m_cListCtrl.GetClientRect(&rect);
2. Добавить столбцы (только если стиль выбран в Report)
При работе со строками важно учитывать следующее отличие AnsiString и CString - нумерация символов в AnsiString начинается с 1, а у CString с 0. Например, так нужно получить последний символ и удалить его в AnsiString:
2. Устанавливаем компонент Regular Expression Component Library Vc71 в любую папку, я поставил в C:\Program Files\Microsoft Visual Studio .NET 2003\Regular Expression Component Library Vc71\
3. В Visual Studio C++ 7.1 делаем File\New Project...\Project Types:Win32\Win32 Console Project, указываем имя проекта, например, regextest, Жмем OK, в Application Settings должно быть выбрано Console application, и можно поставить галочку на Add support for:MFC, жмем Finish.
4. Добавляем
#include < RegularExpressionClass.h >
В Project\Properties\Configuration Properties\C/C++\General\Additional Include Directories добавляем "$(VSInstallDir)\Regular Expression Component Library Vc71\Include"
8. Все это мы проделали в конфигурации Debug. Для конечного релиза нужно сделать то же самое, только еще целесообразно в Project\Properties\Configuration Properties\General\Use of MFC\ выбрать Use MFC in a Static Library, а также для предотвращения ошибки Linker Tools Error LNK2005 в Configuration Properties\Linker\Command Line\Additional Options:\ добавляем /FORCE:MULTIPLE (см. совет Ошибка линкера LNK2005).
11. Разбиение строки на части (по разделителю - пробелу). В этом примере на входе строка csLine, на выходе части помещаются в вектор out. Части извлекаются с помощью итератора it, первая часть помещается в csPart.
Ошибка линкера LNK2005: Linker Tools Error LNK2005 "symbol already defined in object" быстро устраняется добавлением опции /FORCE:MULTIPLE в командную строку линкера: Configuration Properties\Linker\Command Line\Additional Options:\ добавляем /FORCE:MULTIPLE.
После компиляции получаем кучу страшных предупреждений, но программа работает:
Compiling resources... Linking... nafxcw.lib(afxmem.obj) : warning LNK4006: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) already defined in libcpmt.lib(newop.obj); second definition ignored nafxcw.lib(afxmem.obj) : warning LNK4006: "void * __cdecl operator new[](unsigned int)" (??_U@YAPAXI@Z) already defined in libcpmt.lib(newaop.obj); second definition ignored Release/autoname.exe : warning LNK4088: image being generated due to /FORCE option; image may not run LINK : warning LNK4089: all references to 'SHELL32.dll' discarded by /OPT:REF LINK : warning LNK4089: all references to 'comdlg32.dll' discarded by /OPT:REF LINK : warning LNK4089: all references to 'ole32.dll' discarded by /OPT:REF
Если Вы создали приложение с помощью визарда как диалоговое окно, и добавили обработчики сообщений для диалогового окна WM_KEYDOWN или WM_KEYUP (напомню, что это делается через Class View\правая кнопка на классе диалогового окна, Properties\кнопка Messages, далее выбираем сообщение и добавляем для него обработчик), то этот обработчик все равно срабатывать не будет. Проблема (и её решение) описана тут - http://www.winterdom.com/dev/mfc/pretrans.html . Проблема решается так:
1. В заголовочном файле класса (имя_класса.h), в секции public класса диалогового окна добавляется строка с переназначением процедуры PreTranslateMessage:
2. В файле класса диалогового окна (имя_класса.cpp) вставляется код процедуры PreTranslateMessage (в этом примере отслеживается нажатие кнопок Ctrl+C):
1. Добавить private или protected переменную типа CToolTipCtrl в класс вашего диалога. 2. Добавить в класс управляющую переменную (control member variable), для каждого элемента, у которого будет подсказка. Это можно сделать с помощью ClassWizard (на закладке Member Variable). 3. Переопределить CDialog::OnInitDialog и вызвать в нем CToolTipCtrl::Create. Затем вызвать CToolTipCtrl::AddTool для каждого элемента с подсказкой, передавая адрес управляющей переменной и текст подсказки в качестве параметров. 4. Переопределить CDialog::PreTranslateMessage и вызвать в ней CToolTipCtrl::RelayEvent для каждого сообщения, передаваемого в функцию. Это нужно для того, чтобы элемент ToolTip получал все необходимые сообщения мыши.
Практическая реализация по шагам (на примере простейшего приложения на основе стандартного диалога):
1. Открываем файл имя_класса_диалога.h (у меня был файл unusedportsDlg.h), в определение класса (у меня был класс class CunusedportsDlg : public CDialog), в раздел public, в конец вставляем переменную класса типа CToolTipCtrl:
Добавляем в имя_класса_диалога.cpp (у меня был файл unusedportsDlg.cpp), добавляем код процедуры PreTranslateMessage:
BOOL CunusedportsDlg::PreTranslateMessage(MSG* pMsg){// TODO: Add your specialized code here and/or call the base classif(pMsg->message == WM_KEYDOWN){
...
}if(pMsg->message == WM_KEYUP){
...
}
hints.RelayEvent(pMsg);return CDialog::PreTranslateMessage(pMsg);}
2. Идем на закладку Resource View, далее выбираем в дереве проекта папку Dialog, двойным щелчком открываем редактор диалога, правой кнопкой на каждом элементе диалога добавляем переменную (в контекстном меню выбираем Add Variable..., далее запускается Wizard, указываем в поле Variable name: что-то типа varOkBtn, жмем Finish). Так надо поступить с каждом элементом управления, для которого будет всплывающая подсказка (для кнопки, строки редактирования, пикера даты и т. п.).
3. В файле имя_класса_диалога.cpp (у меня был файл unusedportsDlg.cpp), в подпрограмме имя_класса::OnInitDialog() (у меня была подпрограмма BOOL CunusedportsDlg::OnInitDialog()) добавляем инициализацию хинтов, код должен быть наподобие такого:
...//включаем систему подсказок
hints.Create(pDlg);
hints.AddTool(&varDateBeg, "select START data to analize free ports");
hints.AddTool(&varDateEnd, "select END data to analize free ports");
hints.AddTool(&varOutputList, "analize result will be here");
hints.AddTool(&varOkBtn, "click to start analize free ports");
...
Теперь при запуске приложения у нас нужные элементы управления получат нужные надписи подсказок.
4. В процедуре имя_класса::PreTranslateMessage (у меня была процедура BOOL CunusedportsDlg::PreTranslateMessage(MSG* pMsg)) добавляем в конец код:
Все, теперь при наведении курсора на элемент управления будут всплывать желтенькие подсказки. Если нужно переопределить эти подсказки runtime, то нужно пользоваться методом DelTool:
...
hints.DelTool(&varOkBtn);
hints.AddTool(&varOkBtn, "новый текст хинта");
...
Для работы с файлами есть два основных метода - _open/_filelength/_read/_close, и fopen/fread/feof/fclose. Опишем оба метода, их достоинства и недостатки.
_open/_filelength/_read/_close
Работа с файлами через хендл. Самая большая неприятность этого метода - файл всегда открывается в текстовом режиме. Это означает, что часть информации будет при чтении потеряна - например, последовательность \r\n преобразуется в \n, и это не всегда удобно. Удобство в том, что есть подпрограмма определения размера файла (_filelength). Пример использования:
int fhSour;int iFileLength, iBytesReaded;char*buf;
fhSour = _open(catalog +"\\"+ csFN, _O_RDONLY);if(-1== fhSour){
WLog("The file "+ catalog +"\\"+ csFN +" was not opened");exit(1);}
iFileLength = _filelength(fhSour);
buf =(char*)malloc(iFileLength);//iButesReaded может быть меньше iFileLength
iBytesReaded = _read(fhSour, buf, iFileLength);
_close(fhSour);if(-1== iBytesReaded){
WLog("Error read file "+ csFN);exit(1);}//делаем ASCIIZ-строку
buf[iBytesReaded]=0;
CString csFileContent =(CString)buf;free(buf);
fopen/fread/feof/fclose
Работа с файлами через потоки, хороший выбор. Удобство в том, что можно открывать файлы в двоичном виде. Неудобство в том, что нельзя сразу определить размер файла, не прочитав его до конца (конец файла тестируется feof). Пример использования:
FILE*stream;size_t count, total =0;char buffer[100];
CString csOutput ="";if((stream =fopen( csFileName, "rb"))==NULL)
WLog("Error open (not exist?): "+ csFileName );else{while(!feof( stream )){/* Attempt to read in 99 bytes: */
count =fread( buffer, sizeof(char), 99, stream );if(ferror( stream )){
WLog("Error read file "+ csFileName);break;}
buffer[count]=0;
total += count;
csOutput +=(CString)buffer;}fclose( stream );
varOutputList.SetWindowText(csOutput);}
Постоянно забываю эти простые специальные символы. CR, Carriage Return, код 0x0D, использование в C как \r LF, Line Feed, код 0x0A, использование в C как \n CRLF, \r\n, используется в файлах DOS и Windows как последовательность, завершающая строку (0x0D 0x0A).
- Resource View\папка Bitmap\правая кнопка Add Resource...\выбираем Bitmap\нажимаем кнопку New - в папке ресурсов Bitmap появляется новая запись IDB_BITMAP1 (это ID картинки) и одновременно открывается окно редактора картинки. IDB_BITMAP1 лучше поменять на что-то более подходящее по смыслу, например IDB_MYBUTTON (делается через свойства картинки, параметр ID). - меняем у нашей кнопки свойство Bitmap на True, а свойство Caption очищаем (свойство Caption очищать не обязательно, все равно картинка закроет надпись на кнопке). - рисуем картинку. Проще всего открыть какой-нибудь ресурс в Интернете, посвященный иконкам, например http://www.iconsfree.org/free-icons/language/eng/c/categoriesRating/icons-categories-rating.html, выбрать нужную иконку и прямо с экрана скопировать в буфер обмена растр картинки (для этого очень рекомендую программу Kleptomania - копирует с экрана и графику, и текст), а затем вставить содержимое буфера прямо в редакторе ресурсов Visual Studio. - к кнопке, в которую будем вставлять картинку, привязываем переменную. Для этого на кнопке в контекстном меню выбираем Add Variable..., в окно Variable name: подставляем что-то типа varMyBtn - в процедуре инициализации формы диалога OnInitDialog() добавляем следующий код (процедура WLog пишет ошибки в текстовый файл):
- скачиваем ZipArchive Library с сайта http://www.artpol-software.com/Download.aspx, это 2 файла - ziparchive.zip (Sources, Documentation and Samples) и setup.zip (Installation Setup).
- запускаем файл setup.exe (из архива setup.zip), указываем директорию для установки C:\Program Files\Microsoft Visual Studio .NET 2003\ZipArchive library. Если у Вас не MSDN версии 6, то галочку "интегрировать help" не ставим.
- теперь нам надо получить откомпилированную библиотеку ZipArchive.lib той версии, которая подходит к нашей версии Visual Studio 7.0. Для этого из архива ziparchive.zip, который мы скачали, распаковываем 2 файла ZipArchive.vcproj и ZipArchive.sln в папку C:\Program Files\Microsoft Visual Studio .NET 2003\ZipArchive library\ZipArchive\. Те файлы, что там есть (версии 8.0), можно спокойно стереть, поскольку в ziparchive.zip есть их копии в папке _projects\Visual Studio 2005\ZipArchive\. Двойным щелчком открываем файл C:\Program Files\Microsoft Visual Studio .NET 2003\ZipArchive library\ZipArchive\ZipArchive.vcproj, выбираем конфигурацию Release, компилируем. В результате получаем файл C:\Program Files\Microsoft Visual Studio .NET 2003\ZipArchive library\ZipArchive\Release\ZipArchive.lib.
- создаем новый проект в Visual Studio - Visual C++ Projects\Win32\Win32 Console Project, поддержку ATL и MFC не добавляем.
- в свойствах проекта, раздел C/C++\Additional Include Directories добавляем путь к папке, где лежат h-файлы:
"C:\Program Files\Microsoft Visual Studio .NET 2003\ZipArchive library\ZipArchive"
- в свойствах проекта, раздел Linker\Additional Library Directories добавляем путь к папке, где лежит откомпилированная библиотека ZipArchive.lib: "C:\Program Files\Microsoft Visual Studio .NET 2003\ZipArchive library\ZipArchive\Release"
- в свойствах проекта, в раздел Linker\Input\Additional Dependencies добавляем ZipArchive.lib.
Ругань началась при попытке компиляции c-кода в cpp-проекте на строку *HidDevices = calloc (*NumberDevices, sizeof (HID_DEVICE)): путь_до_проекта\usbhid.cpp(70): error C2440: '=' : cannot convert from 'void *' to 'PHID_DEVICE'
Причина - переменная *HidDevices имела тип PHID_DEVICE, а calloc возвращает тип void *. Дело в том, что в проекте CPP компилятор более строго проверяет соответствие типов, чем в проекте на чистом C. Вариантов решения 2 - либо поменять тип проекта (с CPP на чистый C), либо применить явное преобразование типа, например так: *HidDevices = (PHID_DEVICE) calloc (*NumberDevices, sizeof (HID_DEVICE));
ExitProcess() полностью, немедленно завершает программу, даже если ExitProcess() вызвать из потока, запущенного в программе. Причем функция, настроенная на обработку выхода с помощью atexit(), не срабатывает.
- внутри Visual Studio идем Tools\Options...\Projects\VC++ Directories, Show directories for настраиваем по таблице (здесь $(VCInstallDir) равно c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7): Include files .../Prof-UIS/Include или $(VCInstallDir)\..\Prof-UIS\Include .../Prof-UIS/Src или $(VCInstallDir)\..\Prof-UIS\Src Source files .../Prof-UIS/Include или $(VCInstallDir)\..\Prof-UIS\Include .../Prof-UIS/Src или $(VCInstallDir)\..\Prof-UIS\Src Library files .../Prof-UIS/Bin_710 или $(VCInstallDir)\..\Prof-UIS\Bin_710
- распаковать в папку c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\VCWizards\ папку ProfUISAppWizard из архива ProfUisAppWizard_2003.ZIP
- файлы ProfUISAppWizard.ico, ProfUISAppWizard.vsdir и ProfUISAppWizard.vsz из архива ProfUisAppWizard_2003.ZIP положить в папку c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\vcprojects\
- теперь сразу, без перегрузки Visual Studio будет доступен новый визард по адресу File\New\Project...\Visual C++ Projects\ProfUISAppWizard
- нужно откомпилировать все версии библиотеки ProfUIS264*.lib, для этого откройте c:\Program Files\Microsoft Visual Studio .NET 2003\Prof-UIS\Workspace\ProfUIS_710.sln, выберите проект ProfUISLIB и откомпилируйте его со всеми возможными конфигурациями:
ANSI Debug, Static ANSI Debug ANSI Debug RDE, Static ANSI Debug RDE ANSI Debug RDE with MFC DLL, Static ANSI Debug RDE with MFC DLL ANSI Debug with MFC DLL, Static ANSI Debug with MFC DLL ANSI Release, Static ANSI Release ANSI Release RDE, Static ANSI Release RDE ANSI Release RDE with MFC DLL, Static ANSI Release RDE with MFC DLL ANSI Release with MFC DLL, Static ANSI Release with MFC DLL MBCS Debug, Static MBCS Debug MBCS Debug RDE, Static MBCS Debug RDE MBCS Debug RDE with MFC DLL, Static MBCS Debug RDE with MFC DLL MBCS Debug with MFC DLL, Static MBCS Debug with MFC DLL MBCS Release, Static MBCS Release MBCS Release RDE, Static MBCS Release RDE MBCS Release RDE with MFC DLL, Static MBCS Release RDE with MFC DLL MBCS Release with MFC DLL, Static MBCS Release with MFC DLL Unicode Debug, Static Unicode Debug Unicode Debug RDE, Static Unicode Debug RDE Unicode Debug RDE with MFC DLL, Static Unicode Debug RDE with MFC DLL Unicode Debug with MFC DLL, Static Unicode Debug with MFC DLL Unicode Release, Static Unicode Release Unicode Release RDE, Static Unicode Release RDE Unicode Release RDE with MFC DLL, Static Unicode Release RDE with MFC DLL Unicode Release with MFC DLL, Static Unicode Release with MFC DLL
Для этого идем в Build\Batch Build..., ставим галки на конфигурациях ProfUISLIB, жмем Build. После того, как все выбранные конфигурации откомпилируются (это надолго, можно пойти попить кофейку), в каталоге c:\Program Files\Microsoft Visual Studio .NET 2003\Prof-UIS\Bin_710\ появится куча файлов ProfUIS264*.lib.
- для того, чтобы отлаживаемые программы запускались, нужно добавить в переменную Path системы путь до библиотек c:\Program Files\Microsoft Visual Studio .NET 2003\Prof-UIS\Bin_710\, для этого открываем Control Panel\System\Advanced\Environment Variables...\System variables, выбираем переменную Path и нажимаем кнопку Edit..., в строке Variable Value: в конец добавляем ;c:\Program Files\Microsoft Visual Studio .NET 2003\Prof-UIS\Bin_710\
Перелогиниваться при этом не нужно, просто надо перезапустить приложение (Visual Studio), из которого запускаем программу. Другой способ - скопировать нужную dll в каталог отлаживаемой программы, или задать в свойствах проекта линковать библиотеки статически - в свойствах проекта программы, в разделе Configuration Properties\General указать Use of MFC в Use MFC in a Static Library.
Такая ошибка у меня часто возникает, когда я беру модуль *.cpp (из другого старого проекта) без хедера и пытаюсь вставить в новый проект. На переменные вставляемого модуля, объявленные с директивой extern (в этом примере так была объявлена переменная LogFileName) будет в этом случае будет происходить ошибка LNK2001 и следующая за ней общая фатальная ошибка LNK1120. Проблема устраняется объявлением CString LogFileName в любом другом модуле.
Иногда это нужно сделать, если файл кода используется совместно с embedded-проектами для микроконтроллеров. Правой кнопкой щелкаем на нужном C-файле в дереве проекта, выбираем Properties -> Configuration Properties -> C/C++ -> Advanced -> Compile As -> Compile as C++ Code (/TP).
Получить путь до запускаемого файла приложения вместе с именем *.exe файла можно вызовом ExecutablePath, а полный путь до запускаемого файла приложения без самого имени *.exe можно вызовом StartupPath. Пример:
[STAThreadAttribute]int main(array<System::String^>^args){// Включение визуальных эффектов Windows XP до создания// каких-либо элементов управления
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);// создание файла лога
applog = new Log("myapp.log");// Создание главного окна и его запуск
applog->Write("[START]");
applog->Write(Application::ExecutablePath);//полный путь до *.exe вместе с именем *.exe
applog->Write(Application::StartupPath);//полный путь до *.exe без с имени *.exe
Application::Run(gcnew Form1());
applog->Write("[EXIT]");
delete applog;return0;}
Обратное преобразование из System::String в char* может быть выполнено либо с помощью функции sprintf_s, либо с помощью объекта Marshal. Метод с помощью sprintf_s:
Иногда нужно переименовать папку проекта, чтобы сделать его работоспособную копию. Однако, если после этого открыть проект в переименованной папке, то не отображается в конструкторе форма проекта (конструктор выдает ошибку). Очистка и компиляция работают, но это не решает проблему. Чтобы полностью починить переименованный проект, нужно выйти из Visual Studio, удалить файл *.sdf в корневой папке проекта, и снова открыть проект в Visual Studio. Файл *.sdf создастся заново, конструктор форм нормально заработает и покажет форму, доступную для редактирования.
Ранее проект нормально собирался под Windows XP SP3 32-бита в среде разработки Microsoft Visial C++ 2010 со встроенной библиотекой VS 2010 NET 4.0. После перехода на Windows 7 в той же среде Microsoft Visial C++ 2010, но со встроенной библиотекой VS 2010 NET 4.5 начала выскакивать ошибка:
LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt.
(LINK : fatal error LNK1123: сбой при преобразовании в COFF: файл недопустим или поврежден). Ошибка исчезла после дополнительной установки пакета обновления SP1 для Microsoft Visual Studio 2010 (KB983509).
error C2664: CreateFileW: cannot convert parameter 1 from 'char[]' to 'LPCWSTR' error C2664: CreateFileW: невозможно преобразовать параметр 1 из "char []" в "LPCWSTR"
Строковый литерал: это обычная строка текста, которая напрямую указывается в коде программы:
char myText [] ="Hello, World!";
Здесь был приведен пример обычной ASCIIZ строки, где каждый символ представлен 1 байтом. Некоторые функции Win API требуют на входе UNICODE-строк типа wchar_t. Для представления строковых литералов как строк из символов wchar_t используют макросы _T(), TEXT() и модификатор L. Примеры:
После загрузки данных (методами Row -> Add()) таблица DataGridView начинает себя вести непредсказуемо. Во-первых, полоса прокрутки справа от таблицы не соответствует количеству данных в таблице. Во-вторых, при прокрутке таблицы вниз клавишей со стрелкой "вниз" выскакивает исключение ArgumentOutOfRangeException. Пример полного сообщения об ошибке:
Необработанное исключение типа "System.ArgumentOutOfRangeException" произошло
в System.Windows.Forms.dll". Дополнительные сведения: Значение '154' недопустимо
для 'Value'. 'Value' должно лежать в диапазоне от 'minimum' до 'maximum'.
Решение проблемы: после заполнения таблицы данными нужно вызвать метод PerformLayout(). Пример:
В файле sdf сохраняется информация для работы подсистемы Visual Studio Intellisense (подсветка синтаксиса, рекомендации об устранении ошибок, предупреждения и т. п.). Этот файл занимает много места на диске (от 20 мегабайт и больше). Можно ли как-то безопасно удалить sdf-файл, чтобы при повторном открытии решения он создавался заново (или можно ли как-то вообще избавиться от этого файла)?
Опытным путем выяснил, что удаление файла на работоспособность проекта и Visual Studio не влияет. Файл *.sdf будет заново создан, когда Вы откроете проект.
[Каталог для размещения SDF-файла]
Можно также настроить место для сохранения этого файла (например в папке C:\TEMP), что может быть полезным, когда Вы не хотите замусоривать Ваши каталоги с проектами лишними временными файлами. Перейдите в меню Tools -> Options -> Text Editor -> C/C++ -> Advanced, в разделе Fallback Location установите "Always Use Fallback Location" в True и "Do Not Warn If Fallback Location Used" в True. В разделе Fallback Location Вы можете ввести путь наподобие C:\Temp, либо оставить путь пустым, тогда Visual Studio в качестве каталога будет использовать директорию временных файлов в папке AppData.
[Как запретить создание SDF-файла]
Можно совсем отключить базу данных Intellisense: меню Tools -> Options -> Text Editor -> C/C++ -> Advanced -> Disable Database выставьте в true.
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\atlmfc\include\afx.h(24): fatal error C1189:
#error : Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version.
Please #define _AFXDLL or do not use /MD[d]
Как исправить: открыть свойства конфигурации, опцию Общие -> Использование MFC -> задать в "Использовать MFC в общей DLL".
По умолчанию все поля в структурах обычно имеют выравнивание на 4 байта (32-битное слово). Но как быть, если надо получить упакованную структуру, без пустот?
voidWLog (CString S)
{
CStdioFile file;
WORD Year, Month, Day, HH, MM, SS;
CString asYear, asMonth, asDay, asHH, asMM, asSS;
//Получить текущее время.
CTime t = CTime::GetCurrentTime();
Year = t.GetYear();
Month = t.GetMonth();
Day = t.GetDay();
HH = t.GetHour();
MM = t.GetMinute();
SS = t.GetSecond();
try
{
CStdioFile file( filename,
CFile::modeRead|CFile::typeBinary );
result = file.Read(data, size);
file.Close();
}
catch(CFileException* pe)
{
TRACE(_T("File could not be opened, cause = %d\n"),
pe->m_cause);
pe->Delete();
}
return result;
}
[Запись файла в двоичном режиме]
Если файл filename не существует, то он будет создан, если существует, то он будет уничтожен и перезаписан.
staticboolfilewrite (u8* data, CString filename, UINT size)
{
bool result =true;
try
{
CStdioFile file( filename,
CFile::modeCreate|CFile::modeWrite|CFile::typeBinary );
file.Write(data, size);
file.Close();
}
catch(CFileException* pe)
{
TRACE(_T("File could not be opened, cause = %d\n"),
pe->m_cause);
pe->Delete();
result =false;
}
return result;
}
[Запись файла в текстовом режиме на добавление]
В этом примере файл записывается как текстовый лог. Если файл csExeName.log не существует, то он будет создан, если существует, то содержимое строки S будет дописано в конец файла csExeName.log с добавлением метки текущего времени.
voidWLog (CString S)
{
CStdioFile file;
WORD Year, Month, Day, HH, MM, SS;
CString asYear, asMonth, asDay, asHH, asMM, asSS;
//Получить текущее время.
CTime t = CTime::GetCurrentTime();
Year = t.GetYear();
Month = t.GetMonth();
Day = t.GetDay();
HH = t.GetHour();
MM = t.GetMinute();
SS = t.GetSecond();
Обе директивы подключают заголовочные файлы (с расширением *.h), где находятся определения типов, констант, макросов, внешних переменных. Но заголовочные файлы бывают разные - некоторые поставляются вместе с системой программирования (такой как Visual Studio или Qt) и предназначены для подключения кода "стандартных" библиотек, а некоторые создает сам пользователь.
Готовые загловочные файлы называются predefined, т. е. "уже определенные", и они указываются в угловых скобках (иногда без расширения). Например:
#include < iostream>
Заголовочные файлы, созданные пользователем, указываются в двойных кавычках:
Чтобы вызвать функцию, написанную на языке C, из кода на языке C++, нужно использовать ключевое слово extern "C", когда декларируется функция C. Тогда можно вызывать эту функцию точно так же, как вызов любой другой функции. Пример:
/* Это код C++ для декларации функции foo,
которая где-то определена в коде C: */
extern"C"void foo( );
После этой декларации функция foo может быть вызвана в коде C++, примерно так:
extern"C"void foo();
voidmain()
{
// Вызов функции:
foo( );
}
А что если нам нужно декларировать сразу несколько функций в коде C++? Для этого лучше всего использовать для них группу, вот так:
/* Это код C++, где декларируется доступ к нескольким
Когда событие исключения выброшено, но не обработано, то это означает, что не определен подходящий обработчик для вида выброшенного исключения. Это означает также, что вообще не найден никакой замещающий обработчик исключения (ellipsis catch handler, так называемый обработчик последнего шанса).
Итак, что произойдет в таком сценариии? На C++ есть заранее определенная функция terminate, которая будет вызвана, когда для исключение нет подходящего обработчика. Функция terminate затем вызовет другую функцию с именем abort. Функция abort остановит выполнение программы.
Однако также стоит отметить, что функция terminate может быть переопределена для вызова некоторой другой функции – которая не вызовет abort – перед выходом из приложения. Для этого просто используйте функцию set_terminate, и передайте ей имя функции, которую хотите вызвать вместо terminate. Для того, чтобы переданная функция работала стандартным образом, лучше всего в ней вызвать функцию exit для завершения работы приложения.
[Что такое ellipsis catch handler]
На C++ ellipsis catch handler очень прост. Все, что он делает - перехватывает любые не обработанные исключения. Поэтому ellipsis catch handler работает как обработчик последнего шанса, когда нет никакого другого подходящего обработчика. Вот пример того, как может выглядеть ellipsis catch handler:
try
{
throw"Что-то там произошло";
}
// Это ellipsis catch handler:
catch(...)
{
cout <<"Для этого сценария нет подходящего обработчика";
}
Откуда пошло название ellipsis catch handler для такого обработчика? Причина тривиальна - многоточие "...", которое также называется ellipsis, и обычно оно используется в смысле "et cetera" (и так далее).
error C2146: синтаксическая ошибка: отсутствие ";" перед идентификатором "ulong64"
В данном случае неправильно задан оператор typedef для типа ulong64. Само собой, такая ошибка может возникнуть и при неправильном определени любого типа. Пример ошибочного определения типа:
typedefunsigned _uint64 ulong64;
В этом примере ошибка C2146 произошла из-за отсутствия определения типа _uint64. Для Visual Studio правильным определением 64-битного типа без знака может быть замена _uint64 на __int64, ошибка C2146 пропадет:
Причина в том, что Visual Studio (в зависимости от версии) может не полно поддерживать современные стандарты C++. Языки C++ и C оба определяют суффикся "ULL" и "LLU" как допустимые для целочисленных литералов. Однако VS2010 эти суффиксы не поддерживает, а VS2012 поддерживает только суффикс ULL.
Устранить ошибку C2059 для данного случая можно заменой суффикса LLU на LU:
В системе программирования MSVC используйте ключевое слово __inline или _inline вместо inline. Ключевое слово inline определено стандартом c99, и этот стандарт (пока) не полностью поддерживается в MSVC.
Цитата из источника [1]: "Ключевое слово inline доступно только в C++. Ключевые слова __inline и __forceinline доступны в обоих языках C и C++. Для совместимости с предыдущими версиями _inline является синонимом __inline".
На платформе MSVC (Visual Studio C++) замените __func__ на __FUNCTION__:
#if defined(_WIN32) || defined(_WIN64)
#define __func__ __FUNCTION__
#endif
Здесь __FUNCTION__ это так называемый предопределенный макрос, который в Visual Studio C++ разворачивается в строковый литерал, который содержит имя функции, где встретился макрос __FUNCTION__ (подробнее см. [2]).
IDE Visual Studio не может найти файл заголовка, который находится в корневой папке проекта. Откройте свойства проекта, зайдите в раздел Свойства конфигурации -> Каталоги VC++ и в раздел "Каталоги включения" добавьте $(ProjectDir). Иногда $(ProjectDir) надо также добавить в раздел "Справочные каталоги".
1 совет - тот-же эффект: #pragma comment(lib, "winmm.lib") 17 совет - если в диалоговом окне несколько итемов с одинаковым ID=IDC_STATIC (а в VS это идентификатор по умолчанию для StaticText и пр.), то результат непредсказуем
Да, для работы с файлами можно пользоваться стандартными API. Функцией CreateFile открываем файл на чтение/запись, потом читаем/пишем с помощью ReadFile/WriteFile или маппим.
Комментарии
#pragma comment(lib, "winmm.lib")
17 совет - если в диалоговом окне несколько итемов с одинаковым ID=IDC_STATIC (а в VS это идентификатор по умолчанию для StaticText и пр.), то результат непредсказуем
RSS лента комментариев этой записи