FAQ:WinAPI, VCPP Part 2 — различия между версиями

Материал из Весельчак У
Перейти к: навигация, поиск
(Создан редирект на FAQ:WinAPI_VCPP. Оптимизация для поисковых систем.)
 
(не показано 9 промежуточных версий этого же участника)
Строка 1: Строка 1:
===Как сделать обработчик сообщения для нескольких элементов управления сразу?===
+
#REDIRECT [[FAQ:WinAPI_VCPP]]
 
+
Без помощи визарда (Wizard) это можно сделать переопределением виртуальной функции OnCommand():
+
 
+
<syntaxhighlight lang="cpp">
+
BOOL CMyDialog::OnCommand(WPARAM wParam, LPARAM lParam)
+
{
+
//командное сообщение
+
WORD wMess=(wParam >> 16);
+
 
+
//ID элемента управления
+
int nID=(int)(wParam &0x0000ffff));
+
 
+
//хендл элемента управления
+
HWND hW=(HWND)lParam;
+
+
// смотрим, какой элемент управления
+
switch (nID)
+
{
+
//CButton (кнопки, переключатели, флажки)
+
case ID_BN1:
+
case ID_BN2:
+
case ID_BN3:
+
{
+
//смотрим, какое сообщение
+
switch (wMess)
+
{
+
case BN_CLICKED:
+
{
+
...
+
}
+
break;
+
}
+
}
+
break;
+
+
//CEdit
+
case ID_ED1:
+
case ID_ED2:
+
case ID_ED3:
+
case ID_ED4:
+
{
+
// смотрим, какое сообщение
+
switch (wMess)
+
{
+
case EN_CHANGE:
+
{
+
...
+
}
+
break;
+
 
+
case EN_KILLFOCUS:
+
{
+
...
+
}
+
break;
+
}
+
}
+
break;
+
}
+
+
return CDialog::OnCommand(wParam, lParam);
+
}
+
</syntaxhighlight>
+
 
+
===Когда я запускаю программу, все надписи на русском языке теряются - показываются вопросики. Что делать?===
+
 
+
Лечится проблема так: непосредственно после создания проекта открываем дерево ресурсов и в свойствах каждого элемента дерева ставим язык Russian. Если этого не сделать сразу, то все русские буквы при компиляции ресурсов потеряются.
+
 
+
===Как в проекте VC6 MFC программно получить путь, откуда был запущен экзешник (исполняемый модуль) самой программы?===
+
 
+
Нужно использовать функцию GetModuleFileName():
+
 
+
<syntaxhighlight lang="cpp">
+
TCHAR pszFileName[MAX_PATH];
+
pszFileName[0]=0;
+
 
+
GetModuleFileName(NULL, pszFileName, MAX_PATH);
+
CString stModulePath=pszFileName;
+
 
+
// ищем первый слеш с конца и удаляем
+
// его вместе с именем файла EXE
+
int nEnd=stModulePath.ReverseFind('\\');//'
+
stModulePath.Delete(nEnd, stModulePath.GetLength() - nEnd);
+
 
+
// stModulePath - содержит путь
+
</syntaxhighlight>
+
 
+
===Как получить доступ к элементам управления панели CReBar, принадлежащей классу MainFrame?===
+
 
+
<syntaxhighlight lang="cpp">
+
#include "MainFrm.h"
+
 
+
// CMyApp - класс вашего приложения. theApp - глобальная переменная,
+
// поэтому для доступа к ней используем extern
+
 
+
extern CMyApp theApp;
+
 
+
void CMyView::F()
+
{
+
// Получаем главное окно приложения в любом месте программы
+
CMainFrame* pMainFrame=(CMainFrame*)(theApp.m_pMainWnd);
+
 
+
pMainFrame->m_wndDlgBar. ...; // Делаем что хотим
+
...
+
}
+
</syntaxhighlight>
+
 
+
===Как загрузить и показать один из стандартных курсоров?===
+
 
+
<syntaxhighlight lang="cpp">
+
HCURSOR hCursor;
+
 
+
hCursor=AfxGetApp()->LoadStandardCursor(IDC_UPARROW);
+
 
+
if(hCursor)
+
{
+
SetCursor(hCursor);
+
}
+
</syntaxhighlight>
+
 
+
Идентификаторы стандартных курсоров:
+
* IDC_ARROW
+
* IDC_IBEAM
+
* IDC_WAIT
+
* IDC_CROSS
+
* IDC_UPARROW
+
* IDC_SIZENWSE
+
* IDC_SIZENESW
+
* IDC_SIZEWE
+
* IDC_SIZENS
+
* IDC_SIZEALL
+
 
+
===Как запретить пользователю закрыть программу нажатием на кнопку с крестиком?===
+
 
+
Для этого нужно добавить обработчик сообщения WM_CLOSE (функция OnClose() в MFC) в главное окно программы. Для диалоговых приложений такое окно - это главный диалог, для одно- и многодокументных приложений - это CMainFrame.
+
 
+
<syntaxhighlight lang="cpp">
+
void CMainFrame::OnClose()
+
{
+
if(...)
+
{
+
// не разрешаем закрыть
+
return;
+
}
+
+
CFrameWnd::OnClose();
+
}
+
</syntaxhighlight>
+
 
+
===Как создать на диалоге группу элементов "RadioButton" и как задать порядок их обхода клавишей Tab?===
+
 
+
Последовательность действий следующая:
+
# Помещаем на форму нужное количество элементов RadioButton;
+
# У первого элемента из группы ставим свойство Group, у остальных в группе - убираем это свойство;
+
# Порядок обхода (Tab Order) задаётся так: нажимаем Ctrl+D (загораются номера таб-порядка). Затем щёлкаем элементы в группе в таком порядке, который требуется.
+
 
+
===Где лучше устанавливать начальные значения элемента CComboBox?===
+
 
+
Это можно сделать парой способов:
+
# В редакторе форм - открыть свойства элемента CComboBox, "данные" (для ввода очередной строки данныхых нажать Ctrl+Enter);
+
# В функции OnInitDialog (в случае диалога) или в функции OnInitialUpdate (для CView).
+
 
+
===Как перевести RichEdit в режим замены символов?===
+
 
+
Имеется несколько способов.
+
 
+
1. Программно, если известен хендл элемента (hWnd), это делается так:
+
 
+
<syntaxhighlight lang="cpp">
+
// помещаем сообщение WM_KEYDOWN с нужными параметрами
+
//в конец очереди сообщений элемента управления
+
::PostMessage(hWnd, WM_KEYDOWN, VK_INSERT, 1);
+
</syntaxhighlight>
+
 
+
2. Пользователь во время работы может нажать клавишу Insert.
+
 
+
===Как вызвать метод главного окна (если используется класс CMainFrame) из любого места программы?===
+
 
+
Это можно сделать так:
+
 
+
<syntaxhighlight lang="cpp">
+
AfxGetApp()->m_pMainWnd->...;
+
</syntaxhighlight>
+
 
+
или так
+
<syntaxhighlight lang="cpp">
+
extern CMyAppXXX theApp; // здесь CMyAppXXX - название класса вашего приложения
+
theApp.m_pMainWnd->...;
+
</syntaxhighlight>
+
 
+
===Как запретить появление полос прокруток на форме класса CFormView, когда пользователь делает размер главного окна меньше размера формы?===
+
 
+
Это можно сделать методом SetScaleToFitSize():
+
 
+
<syntaxhighlight lang="cpp">
+
void CMyView::OnInitialUpdate()
+
{
+
CFormView::OnInitialUpdate();
+
 
+
// скрываем полосы прокрутки
+
GetParentFrame()->RecalcLayout();
+
 
+
// это уберёт полосы прокрутки со вьюхи
+
ResizeParentToFit();
+
 
+
SIZE s={0, 0};
+
SetScaleToFitSize(s);
+
// далее вызовется обработчик OnPaint(), о котором ниже
+
 
+
//...
+
}
+
</syntaxhighlight>
+
 
+
Также обратите внимание на одну особенность: в отладочной версии программы вызов OnPaint после выполнения SetScaleToFitSize() с параметром s={0, 0} происходит с ошибкой (программа при этом завершается).
+
Обходится это так:
+
 
+
<syntaxhighlight lang="cpp">
+
void CMyView::OnPaint()
+
{
+
#ifdef _DEBUG
+
CPaintDC dc(this);
+
#else
+
CFormView::OnPaint();
+
#endif
+
}
+
</syntaxhighlight>
+
 
+
Или так:
+
 
+
<syntaxhighlight lang="cpp">
+
void CMyView::OnPaint()
+
{
+
CPaintDC dc(this);
+
}
+
</syntaxhighlight>
+
 
+
===Как нарисовать прямоугольник с вертикальным цветовым градиентом?===
+
 
+
Нужно задать граничные значения цветов, затем равномерно нарисовать несколько прямоугольников постепенно меняющегося цвета. Количество прямоугольников, а, значит - плавность градиента, задаётся точностью.
+
 
+
Пример:
+
 
+
<syntaxhighlight lang="cpp">
+
// вертикальный градиент
+
// pdc - указатель на контекст устройства
+
// pSize - указатель на структуру
+
// SIZE с размером прямоугольника
+
// dwdColor1, dwdColor2 - начальный и конечный цвет
+
// bySteps - количество шагов градиента (1...255)
+
void sFillVertGradientRect(CDC* pdc, SIZE *pSize, COLORREF dwdColor1,
+
COLORREF dwdColor2, BYTE bySteps)
+
{
+
WORD i=0;
+
long W=0;
+
long H=0;
+
long x1=0;
+
long x2=0;
+
 
+
BYTE R1=0;
+
BYTE G1=0;
+
BYTE B1=0;
+
BYTE R2=0;
+
BYTE G2=0;
+
BYTE B2=0;
+
 
+
float dh=0;
+
float dR=0;
+
float dG=0;
+
float dB=0;
+
float y1=0;
+
float y2=0;
+
float Rc=0;
+
float Gc=0;
+
float Bc=0;
+
+
if(!bySteps)
+
{
+
bySteps=1;
+
}
+
 
+
//ширина и высота
+
W=pSize->cx;
+
H=pSize->cy;
+
+
// раскладываем цвета на их составляющие
+
// для удобства дальнейших вычислений
+
R1=(BYTE)((dwdColor1 & 0x000000ff) );
+
G1=(BYTE)((dwdColor1 & 0x0000ff00) >> 8 );
+
B1=(BYTE)((dwdColor1 & 0x00ff0000) >> 16 );
+
R2=(BYTE)((dwdColor2 & 0x000000ff) );
+
G2=(BYTE)((dwdColor2 & 0x0000ff00) >> 8 );
+
B2=(BYTE)((dwdColor2 & 0x00ff0000) >> 16 );
+
+
// высота разноцветных прямоугольников
+
dh=(float)H / (float)bySteps;
+
 
+
// величина шагов составляющих цветов
+
dR=(float)(R2 - R1) / (float)bySteps;
+
dG=(float)(G2 - G1) / (float)bySteps;
+
dB=(float)(B2 - B1) / (float)bySteps;
+
 
+
// выводим прямоугольники
+
x1=0;
+
x2=W;
+
y1=0;
+
y2=dh;
+
Rc=R1;
+
Gc=G1;
+
Bc=B1;
+
 
+
for(i=0; i<bySteps; i++)
+
{
+
// текущий цвет
+
pdc->FillSolidRect(
+
x1,
+
(int)y1,
+
x2-x1,
+
(int)(y2-y1),
+
RGB((BYTE)Rc, (BYTE)Gc, (BYTE)Bc)
+
);
+
 
+
// следующий цвет и координаты
+
y1+=dh;
+
y2+=dh;
+
Rc+=dR;
+
Gc+=dG;
+
Bc+=dB;
+
}
+
}
+
</syntaxhighlight>
+
 
+
Пример вызова:
+
 
+
<syntaxhighlight lang="cpp">
+
SIZE Size={100, 100};
+
sFillGradientRect(&dc, &Size, RGB(200,0,0), RGB(0,200,0), 10);
+
</syntaxhighlight>
+
 
+
===Как под Windows отслеживать изменение файла?===
+
 
+
Нужно использовать функции:
+
* FindFirstChangeNotification
+
* FindNextChangeNotification
+
* FindCloseChangeNotification
+
 
+
(подробности - в MSDN)
+
 
+
===Как конвертировать массив char[] в CString?===
+
 
+
Это можно сделать методами самого класса CString:
+
 
+
<syntaxhighlight lang="cpp">
+
char buf[]="text";
+
// строка должна обязательно заканчиваться нулём!!!
+
 
+
// конвертируем так
+
CString txt(buf);
+
 
+
// или так
+
CString txt;
+
txt=buf;
+
</syntaxhighlight>
+
 
+
===Как зарезервировать в CString буфер нужной длины?===
+
 
+
Это можно сделать при помощи методов класса:
+
 
+
<syntaxhighlight lang="cpp">
+
  CString::GetBuffer(...);
+
</syntaxhighlight>
+
 
+
и
+
 
+
<syntaxhighlight lang="cpp">
+
  CString::GetBufferSetLength(...);
+
</syntaxhighlight>
+
 
+
Разница между GetBuffer(nLen) и GetBufferSetLength(nLen) в том, что первый возвращает строку ''не меньше'' заданной длины, а второй возвращает строку, точно равную заданной длине. Обе могут перераспределять память, если необходимо. В обычных случаях лучше использовать GetBuffer.
+
 
+
Если содержимое буфера менялось, то после этого нужно вызвать CString::ReleaseBuffer с указанием новой длины. Значение -1 в вызове CString::ReleaseBuffer означает, что длина строки будет вычислена автоматом (функцией strlen) в методе. "-1" удобно использовать, если известно, что строка заканчивается нулем.
+
 
+
===Как передать больше одного параметр в процедуру потока?===
+
 
+
Для этого нужно определить структуру со всеми параметрами (или указателями на них), которые нужно передать. А в процедуру потока передать лишь указатель на заполненную структуру.
+
 
+
Пример:
+
 
+
<syntaxhighlight lang="cpp">
+
struct mystr
+
{
+
CEdit* pEd;
+
CDialog* pDlg;
+
DWORD* pdwd;
+
int *pn;
+
};
+
</syntaxhighlight>
+
 
+
запуск потока:
+
 
+
<syntaxhighlight lang="cpp">
+
mystr *pparam=new mystr; // экземпляр не должен быть временным!!!
+
 
+
memset(pparam, 0, sizeof(*pparam));
+
pparam->pEd=...;
+
pparam->pdwd=...;
+
 
+
::AfxBeginThread(thread, pparam);
+
// тут экземпляр *(pparam) уже нельзя использовать в нашем примере, так как
+
// он у нас удалился в процедуре потока
+
</syntaxhighlight>
+
 
+
Процедура потока:
+
 
+
<syntaxhighlight lang="cpp">
+
// поток:
+
UINT threadLoader(LPVOID pParam)
+
{
+
// копируем данные из структуры в локальную структуру
+
mystr data=*((mystr*)pParam);
+
// освобождаем память временной структуры
+
delete ((mystr*)pParam);
+
pParam=0;
+
 
+
...
+
data.pDlg->...;
+
(*data.pdwd)=...;
+
...
+
}
+
</syntaxhighlight>
+
 
+
===Как убрать главное меню из окна CMainFrame?===
+
 
+
Это можно сделать в виртуальной функции PreCreateWindow:
+
 
+
<syntaxhighlight lang="cpp">
+
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
+
{
+
// обнуляем хендл меню до вызова CFrameWnd::PreCreateWindow
+
cs.hMenu=0;
+
+
if(!CFrameWnd::PreCreateWindow(cs))
+
{
+
return FALSE;
+
}
+
 
+
...
+
}
+
</syntaxhighlight>
+
 
+
===Как работающая программа может определить, что пользователь завершает работу Windows?===
+
 
+
Когда пользователь завершает работу Windows, всем процессам посылается сообщение WM_QUERYENDSESSION. Его нужно отловить в обработчике OnQueryEndSession(). Если вернуть из обработчика значение 0, то Windows продолжит работу.
+
 
+
===Как сделать всплывающую подсказку для класса CWnd и классов, от него производных?===
+
 
+
Допустим, имеется диалог класса CMyDlg. Делаем подсказки для элементов управления (для элементов управления CStatic  не забудьте поставить свойство Notify):
+
 
+
<syntaxhighlight lang="cpp">
+
class CMyDlg:puplic CDialog
+
{
+
CToolTipCtrl m_ToolTip; // мембер класса CMyDlg
+
};
+
 
+
// массив, в котором перечислены идентификаторы элементов управления
+
// и тексты подсказок к ним
+
struct
+
{
+
int ID;
+
const char* pch;
+
};
+
 
+
m_a_Tips[]=
+
{
+
{IDC_BUTTON1, "Кнопка"},
+
{IDC_STATIC1, "Текст"},
+
{0, 0} // признак конца массива
+
};
+
 
+
// в инициализации диалога (хотя, в принципе,
+
// можно и не тут) создаём и привязываем подсказки
+
BOOL CMyDlg::OnInitDialog()
+
{
+
CDialog::OnInitDialog();
+
 
+
// создаём элемент TOOLTIP
+
m_ToolTip.Create(this);
+
 
+
// Привязка подсказок
+
for(int i=0; m_a_Tips[i].ID; i++)
+
{
+
m_ToolTip.AddTool(
+
GetDlgItem(m_a_Tips[i].ID),
+
m_a_Tips[i].pch
+
);
+
}
+
+
// включаем показ подсказок
+
m_ToolTip.Activate(1);
+
 
+
...
+
}
+
 
+
// для того, чтобы подсказки отображались как реакция на движение
+
// курсора мыши, транслируем получаемые окнами сообщения в
+
// виртуальной PreTranslateMessage()
+
BOOL CMyDlg::PreTranslateMessage(MSG* pMsg)
+
{
+
// транслируем
+
if(m_ToolTip.m_hWnd)
+
{
+
m_ToolTip.RelayEvent(pMsg);
+
}
+
 
+
return CDialog::PreTranslateMessage(pMsg);
+
}
+
</syntaxhighlight>
+
 
+
===Как в отладчике Visual С++ просмотреть содержимое std::vector<string>?===
+
 
+
Это можно сделать так: к примеру, имеется:
+
 
+
<syntaxhighlight lang="cpp">
+
std::vector<string> V;
+
</syntaxhighlight>
+
 
+
В режиме отладки открываем окно "Watch" (ALT+3) и вставляем выражения:
+
* "V._Myfirst" - будет показан первый элемент V
+
* "V._Myfirst+1" - второй элемент V
+
* и так далее
+
 
+
===Как при выводе текста на контекст устройства определить ширину и высоту выведенных символов текста в пикселах?===
+
 
+
Для этого нужно использовать процедуру API:
+
 
+
<syntaxhighlight lang="cpp">
+
BOOL GetTextExtentPoint32(
+
HDC hdc, // хендл контекста
+
LPCTSTR lpString, // выводимая строка
+
int cbString, // длина строки в символах
+
LPSIZE lpSize // указатель на структуру SIZE, куда будут помещены размеры
+
);
+
</syntaxhighlight>
+
 
+
Функция вычисляет ширину строки символов, оканчивающейся нулём. Поэтому, чтобы вычислить габариты многострочного текста, текст, в начале, необходимо разбить на строки, разделённые символами '\r' и '\n', и определить ширину и высоту каждой отдельно взятой строки.
+
 
+
[[Category:FAQ]]
+

Текущая версия на 02:30, 9 ноября 2008

Перенаправление на: