15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Visual C++ FAQ

Автор: Michael Dunn

Данный материал представляет собой небольшую подборку часто задаваемых вопросов по Visual C++ и MFC, ну и естевственно ответов на них. Так же присутствуют вопросы по ошибкам компилятора и и общие вопросы программирования в Си.

Содержание:

1. Почему я получаю unresolved external error (LNK2001) в main() когда пытаюсь сделать release версию ATL проекта?
2. Как сделать новую строчку в многострочном edit ?
3. Как сделать, чтобы диалог не закрывался при нажатии пользователем Enter или Esc?
4. Я пытаюсь вызвать Windows API, но компилятор выдаёт undeclared identifier error (C2065). Почему?
6. Я добавляю исходные файлы к проекту, а компилятор выдаёт ошибку "C1010: unexpected end of file while looking for precompiled header directive." Почему?
11. Как изменить курсор, когда он находится на моём окне?
12. Как я могу скрыть или показать окно?
13. Как разрешить или запретить элементы управления диалога (кнопки, окна редактирования, и т.д.)?
14. Как заставить окно быть всегда на переднем плане?
17. Как вывести текст в консольном приложении различными цветами?
21. Как сделать глобальную переменную, доступной всем моим файлам .CPP?
22. У меня есть строка, которая является представлением числа, скажем "10235". Как преобразовать её в integer?
23: Как из моего приложения запустить другую программу?

 

 

1. Почему я получаю unresolved external error (LNK2001) в main() когда пытаюсь сделать release версию ATL проекта?

Release версия ATL проектов содержит оптимизацию, по средствам чего проект не связан с C runtime library (CRT) чтобы уменьшить размер Вашего исполняемого модуля. Если Вы используете функции из CRT (например, функции манипуляции со строками) или классы из библиотеки C++, то Вам необходимо линковать проект с CRT.

В опциях проекта, в закладке C/C++ выберите категорию Preprocessor. Удалите директиву _ATL_MIN_CRT из определений препроцессора, тем самым удалив оптимизацию.

Для получения более детальной информации, в MSDN задайте поиск по слову "lnk2001 atl".

 

2. Как сделать новую строчку в многострочном edit ?

Для создания новой строки используется "\r\n". Если Вы будете использовать "\r" или "\n" или даже "\n\r",
то в EDIT-e вместо переноса на следующую строку будут видны квадратики.

 

3. Как сделать, чтобы диалог не закрывался при нажатии пользователем Enter или Esc?

Для начала разберёмся, почему диалог закрывается, даже если убрать кнопки OK и Cancel. CDialog имеет две специальных виртуальных функции, OnOK() и OnCancel(), которые вызываются когда пользователь нажимает клавиши Enter или Esc соответственно. CDialog содержит функцию EndDialog(), которая собственно и закрывает диалог. Так как она относится к функциям специального назначения, то она не присуствует в диалоговой секции BEGIN_MESSAGE_MAP/END_MESSAGE_MAP, и должна быть переопределена по-другому, в отличие от обычных обработчиков нажатия на кнопки.

Если у Вас есть кнопки с идентификаторами IDOK и IDCANCEL, то можно воспользоваться Визардом (ClassWizard) для добавления обработчиков BN_CLICKED для этих кнопок, и они будут делать необходимые действия для OnOK() и OnCancel(). Если у Вас нет кнопок с этими ID, то можно вручную добавить эти виртуальные функции. В заголовочном файле диалогового класса:

class CMyDialog : public CDialog
{
// ...

    // Generated message map functions
    //{{AFX_MSG(CMyDialog)
    virtual void OnOK();
    virtual void OnCancel();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

А затем в соответствующем .CPP файле:

void CMyDialog::OnOK() 
{
}

void CMyDialog::OnCancel() 
{
}

Теперь обработчики не будут вызывать EndDialog() и, соответственно диалог не будет закрываться.

 

4. Я пытаюсь вызвать Windows API, но компилятор выдаёт undeclared identifier error (C2065). Почему?

Заголовочные файлы Windows могут использоваться создания приложения для различных версий Windows начиная с Windows 95 и NT 3.51. Так получилось, что не все функции API присутствуют во всех версиях Windows. Так вот, чтобы компилятор не ругался а, соответственно и не ипытался использовать несуществующие в данной версии Windows функции API, необходимо использовать следующую систему деректив препроцессора.

Директивы позволяют выборочно включать прототипы API:

  • WINVER: версия Windows (в добавление к 9x/Me и NT)
  • _WIN32_WINDOWS: для Windows 9x/Me
  • _WIN32_WINNT: для Windows NT
  • _WIN32_IE: Общие элементы управления

По умолчанию Вы можете использовать только функции присутствующие в Windows 95, NT 3.51, и pre-IE3 common controls. Чтобы использовать API в более поздних версиях Windows, необходимо #define вышеописанные директивы перед включением различных Windows заголовков.

Здесь полный список значений для данных макросов.

 

6. Я добавляю исходные файлы к проекту, а компилятор выдаёт ошибку "C1010: unexpected end of file while looking for precompiled header directive." Почему?

По умолчанию, поекты Visual C++ используют прекомпилированные заголовки. Данная система компилирует большие заголовки только один раз при создании stdafx.cpp. В любом другом файле .CPP Вашего проекта необходимо добавить #include "stdafx.h" в самом начале. Компилятор ищет имя "stdafx.h", чтобы знать, где вставить прекомпилированную заголовочную информацию.

Если возникает такая ошибка, то необходимо отключить прекомпилированные заголовки (precompiled headers). В опциях проекта, в закладке C/C++ выберите категорию Precompiled headers. Кликните радио кнопку Not using precompiled headers, а затем нажмите OK.

 

11. Как изменить курсор, когда он находится на моём окне?

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

 

12. Как я могу скрыть или показать окно?

Чтобы показать окно:

  ShowWindow ( hwndYourWindow, SW_SHOW );

Чтобы скрыть его:

  ShowWindow ( hwndYourWindow, SW_HIDE );

Так же существуют и другие флаги, которые позволяют максимизировать и минимизировать окна. Смотрите страницу ShowWindow() в MSDN.

 

13. Как разрешить или запретить элементы управления диалога (кнопки, окна редактирования, и т.д.)?

Чтобы разрешить элемент управления:

  EnableWindow ( hwndYourControl, TRUE );

Чтобы запретить его:

  EnableWindow ( hwndYourControl, FALSE );

 

14. Как заставить окно быть всегда на переднем плане?

Выводим окно на передний план:

  SetWindowPos ( hwndYourWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE );

Возвращаем в обратное состояние:

  SetWindowPos ( hwndYourWindow, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE );

 

17. Как вывести текст в консольном приложении различными цветами?

Каждый символ сонсольного приложения имеет свои аттрибуты, и Win32 консольные функции могут работать с аттрибутами двумя способами. SetConsoleTextAttribute() работает с символами, записанными в буфере, в то время как FillConsoleOutputAttribute() напрямую изменяет атрибуты символов.

Следующие функции можно использовать для нормального, жирного и обратного текста (предполагается, что класс имеет обработчик консоли через вызов GetStdHandle()):

void CMyConsoleClass::SetTextNormal()
{
    // белым на чёрном - по умолчанию
    SetConsoleTextAttribute ( m_hConsole,
                              FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE );
}

void CMyConsoleClass::SetTextBold()
{
    // ярко-белым на чёрном
    SetConsoleTextAttribute ( m_hConsole,
                              FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
                                FOREGROUND_INTENSITY );
}

void CMyConsoleClass::SetTextReverse()
{
    // чёрным на белом
   SetConsoleTextAttribute ( m_hConsole, 
                             BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE );
}

Обратите внимание, что нет никаких установок для мерцания и подчёркивания, так что Вам прийдётся проявить немного изобратательности, если Вы хотите полностью съэмулировать текстовые режимы ANSI или VT100 данным методом.

 

21. Как сделать глобальную переменную, доступной всем моим файлам .CPP?

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

  int g_volume;

Затем, в заголовочном файле, который будет включён во все файлы .CPP - такой как stdafx.h - добавьте объявление extern:

  extern int g_volume;

Ключевое слово extern указывает компилятору, что g_volume объявлена в других файлах .CPP как int. Если Вы пропустите первый шаг, то компилятор выдаст unresolved external error.

 

22. У меня есть строка, которая является представлением числа, скажем "10235". Как преобразовать её в integer?

Используйте функции atoi() или atol() , либо если число с плавающей точкой, то , atof():

char* szNumber = "10235";
int iNum = atoi ( szNumber );     // integer
long lNum = atol ( szNumber );    // long integer
double dNum = atof ( szNumber );  // floating-point


23: Как из моего приложения запустить другую программу?

Существует несколько функций, позволяющих запускать другие программы. Самая простая - WinExec():

  WinExec ( "C:\\path\\to\\program.exe", SW_SHOWNORMAL );

Так же существует функция ShellExecute(), которая может запускать как экзешники, так и файлы, связанные с приложениями. Например, можно "запустить" текстовый файл, как показано ниже:

  ShellExecute ( hwndYourWindow, "open", "C:\\path\\to\\readme.txt",
                                         NULL, NULL, SW_SHOWNORMAL );

В данном примере, ShellExecute() ищет приложение, связанное с файлами .TXT и запускает его. ShellExecute() также позволяет устанавливать начальную директорию для приложения, а так же дополнительные параметры командной строки. За более подробным описанием функции рекомендую лезть в MSDN.

Если Вам необходимо полностью контролировать запущенное приложение, то необходимо использовать CreateProcess(). CreateProcess() имеет кучу параметров, поэтому детальное описание функции опять же смотрите в MSDN. А здесь приведу только простой пример:

STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi = {0};
BOOL bSuccess;

  bSuccess = CreateProcess ( NULL, "\"C:\\Program Files\\dir\\program.exe\"",
                             NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS,
                             NULL, NULL, &si, &pi );

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

Если CreateProcess() выполнена успешно, то удостоверьтесь, что дескрипторы в структуре PROCESS_INFORMATION закрыты, так как они нам больше не понадобытся.

  CloseHandle ( pi.hThread );
  CloseHandle ( pi.hProcess );

Конечно же, если всё, что Вам нужно, это просто запустить программу, то необходимость в CreateProcess() отпадает, а вот ShellExecute() будет в самый раз.