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

Материал из Весельчак У
Перейти к: навигация, поиск
м (1 версия)
(не показаны 2 промежуточные версии 2 участников)
Строка 523: Строка 523:
 
}
 
}
 
</pre>
 
</pre>
===Как создать на диалоге группу RadioButton-ов и как задать порядок их обхода клавишей Tab?===
 
Кладём на форму N контролов RadioButton. Затем у первого из группы ставим свойство Group , у остальных в группе - убираем это свойство. Порядок обхода (Tab Order) задаётся так: нажимаем Ctrl+D (загораются номера таб-порядка). Затем щёлкаем элементы в группе в таком порядке, который требуется.
 
===Где лучше устанавливать начальные значения CComboBox?===
 
1) В визарде (новая строка данных - Ctrl+Enter)
 
2) В функции OnInitDialog (для диалога) или OnInitialUpdate (для CView)
 
===Как перевести RichEdit в режим замены символов?===
 
1) Программно, зная хендл контрола (hWnd) :
 
 
::PostMessage(hWnd,WM_KEYDOWN,VK_INSERT,1);
 
 
2) Пользователь может нажать Insert.
 
===Как вызвать метод класса CMainFrame (главное окно) из любого места программы?===
 
AfxGetApp()->m_pMainWnd-> ... ;
 
===Как запретить появление полос прокруток на CFormView, когда пользователь делает размер главного окна меньше размера окна CFormView ?===
 
методом SetScaleToFitSize()
 
<pre>
 
void CMyView::OnInitialUpdate()
 
{
 
CFormView::OnInitialUpdate();
 
/////////
 
GetParentFrame()->RecalcLayout();
 
ResizeParentToFit();
 
SIZE s={0,0};
 
SetScaleToFitSize(s);
 
/////////
 
...
 
...
 
}
 
</pre>
 
===Как нарисовать прямоугольник с вертикальным цветовым градиентом ?===
 
 
pdc - указатель на контекст устройства
 
pSize - указатель на структуру SIZE с размером прямоугольника
 
dwdColor1, dwdColor2 - начальный и конечный цвет
 
bySteps - количество шагов градиента (1...255)
 
<pre>
 
//вертикальный градиент
 
void sFillVertGradientRect(CDC* pdc,SIZE *pSize, COLORREF dwdColor1, COLORREF dwdColor2,BYTE bySteps)
 
{
 
if(!bySteps)bySteps=1;
 
WORD i;
 
 
long W,H,x1,x2;
 
BYTE R1,G1,B1,R2,G2,B2;
 
float dh,dR,dG,dB,y1,y2,Rc,Gc,Bc;
 
 
//ширина и высота
 
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)-((float)R1))/((float)bySteps);
 
dG=(((float)G2)-((float)G1))/((float)bySteps);
 
dB=(((float)B2)-((float)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;
 
}
 
}
 
</pre>
 
пример вызова:
 
<pre>
 
SIZE Size={100,100};
 
sFillGradientRect(&dc,&Size, RGB(200,0,0), RGB(0,200,0),10);
 
</pre>
 
===Как под Windows отслеживать изменение файла?===
 
Использовать функции
 
  FindFirstChangeNotification
 
  FindNextChangeNotification
 
  FindCloseChangeNotification
 
===Есть массив char[] , как сконвертировать его в CString ?===
 
<pre>
 
char buf[]="text";
 
 
//строка должна быть обязательно заканчиваться нулём.
 
 
//конвертируем так
 
CString txt(buf);
 
 
//или так
 
CString txt;
 
txt=buf;
 
</pre>
 
===Как зарезервировать в CString буфер нужной длины?===
 
 
при помощи метоодов класса:
 
  CString::GetBuffer
 
  и
 
  CString::GetBufferSetLength
 
 
Разница между CString::GetBuffer(nLen) и CString::GetBufferSetLength(nLen) в том, что первый возвращает строку не меньше заданной длины, а второй возвращает строку точно равную заданной длине. Обе могут перераспределять память если надо. В обычных случаях лучше GetBuffer.
 
 
Если содержимое буфера менялось, то после этого нужно вызвать CString::ReleaseBuffer с указанием новой длины. Значение -1 в вызове CString::ReleaseBuffer означает, что длину строки будет вычислена автоматом (функцией strlen) в методе."-1" удобно использовать, если известно, что строка заканчивается нулем.
 
===Как передать больше одного параметр в процедуру потока?===
 
 
Определить структуру с указателями на всё любое,
 
например:
 
<pre>
 
struct mystr
 
{
 
CEdit* pEd;
 
CDialog* pDlg;
 
DWORD* pdwd;
 
int *pn;
 
};
 
</pre>
 
запуск потока:
 
<pre>
 
mystr *pparam=new mystr;//экземпляр не должен быть временным!!!
 
memset(pparam,0,sizeof(*pparam));
 
pparam->pEd=...;
 
pparam->pdwd=...;
 
 
::AfxBeginThread(thread,pparam);
 
//тут экземпляр *(pparam) уже нельзя использовать!!!
 
//он удалиться в потоке
 
</pre>
 
в потоке:
 
<pre>
 
//поток:
 
UINT threadLoader(LPVOID pParam)
 
{
 
mystr data=*((mystr*)pParam);
 
delete ((mystr*)pParam);//подчищаем память
 
 
...
 
data.pDlg->...;
 
(*data.pdwd)=...;
 
...
 
}
 
</pre>
 
===Как убрать главное меню из CMainFrame ?===
 
<pre>
 
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
 
{
 
//обнуляем хендл меню до вызова CFrameWnd::PreCreateWindow
 
cs.hMenu = 0;
 
 
if( !CFrameWnd::PreCreateWindow(cs) )
 
return FALSE;
 
...
 
...
 
}
 
</pre>
 
===Как работающая программа может определить, что юзер завершает работу Windows?===
 
В этот момент всем процессам посылается сообщение WM_QUERYENDSESSION - его и нужно отловить в обработчике OnQueryEndSession(). Если вернуть из обработчика значение 0 , то Windows продолжит работу.
 
===Как сделать всплывающую подсказки для класса CWnd и классов, от него производных?===
 
Допустим, имеется диалог класса CMyDlg. Делаем подсказки для контролов. (для CStatic контролов не забудьте поставить свойство Notify)
 
<pre>
 
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();
 
//
 
//создаём
 
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);
 
}
 
</pre>
 
===Как в отладчике VС просмотреть содержимое std::vector<string> V===
 
В режиме отладки открываем окно "Watch" (ALT+3) и вставляем выражения:
 
 
 
V._Myfirst      - будет показан первый элемент
 
V._Myfirst+1  - второй
 
V._Myfirst+2  - и т.д.
 
===Я вывожу на контекст устройства текст. Как определить в пикселах ширину и высоту выведенных символов текста?===
 
использовать процедуру API
 
<pre>
 
BOOL GetTextExtentPoint32(
 
HDC hdc,// хендл контекста
 
LPCTSTR lpString,// выводимая строка
 
int cbString,// длина строки в символах
 
LPSIZE lpSize// указатель на структуру SIZE, куда
 
//будут помещены размеры
 
);
 
</pre>
 
 
[[Category:FAQ]]
 

Версия 17:26, 15 мая 2007

Содержание

У меня в программе имеется процедура, которая производит очень много вычислений. Как сделать так, чтобы пользователь мог прервать процесс вычислений?

1) В общем случае решение такое: вычисления производятся в одном потоке (T1) процесса, а обработка команд пользователя - в другом (T2). Одним из потоков может являться и основной поток. Поток T2, получив команду пользователя, выставляет определённый флаг (переменную,событие), доступный также и потоку T1. Поток T1, проверив флаг в определённый момент, прекращает/приостанавливает/продолжает свою работу. Естественно, скорость реакции T1 зависит от характера вычислений. Самый приятный вариант - вычисления, выполняемые в цикле, который часто повторяется. Тогда в начале или в конце цикла поток T1 просто проверяет флаг и принимает решение.

2) Есть ещё "ленивое решение" (подойдёт для простеньких приложений): вычисления выполняются в основном потоке, и также в начале цикла проверяется флаг. А кроме того, перед проверкой флага выполняется такой код:

	//выполнить N сообщений из очереди сообщений
	int imsg;
	MSG m;
	for(imsg=0;imsg<N;imsg++)
	{
		//выборка,выполнение и удаление одного сообщения
		//из очереди сообщений всех окон данного потока
		if(::PeekMessage(&m,0,0,0,PM_REMOVE))
		{
			//транслирование виртуальных клавиш
			::TranslateMessage(&m);
			//обработка сообщения
			::DispatchMessage(&m);
		}
	}

то есть будут обрабатываться все сообщения, в частности нажатия кнопок. Правда с тормозами. Тормоза можно попробовать регулировать числом N.

Примечание В любом случае необходимо тщательно продумать вариант, когда пользователь попытается закрыть программу во время вычислений :) Самое простое: в главном окне программы перехватить WM_CLOSE и разрешить закрытие программы, либо вывести предупреждение и продолжить работу.

не могу обратиться к COM из дополнительного потока, мне пишут- "CoInitialize() не вызвано"

нужно вызывать CoInitialize() в каждом новом потоке

Как при открытии CFileDialog можно было выбирать только папки?

Никак, он этого не делает. Для того, чтобы выбирать директории применяют функцию SHBrowseForFolder.

простой пример

	char pchSelectedF[MAX_PATH]="";
	
	BROWSEINFO bi=
	{
		m_hWnd,
		0,
		pchSelectedF,
		"Выбираем папку",
		0,0,0,0

	};
	
	SHBrowseForFolder(&bi);
	//bi.pszDisplayName - выбранная папка

как в CDialog-based классе получить коды клавиш которые нажимает пользователь? Пробовал добавлять обработчики OnChar() и OnKeyDown(), эти обработчики вообще не вызываются

Диалоги действительно не обрабатывают OnChar() и OnKeyDown(), Поэтому нужно ловить сообщения WM_CHAR и WM_KEYDOWN в виртуальной PreTranslateMessage()

BOOL CMyDialog::PreTranslateMessage(MSG* pMsg) 
{
	if(pMsg->message==WM_KEYDOWN)
	{
		//	pMsg->wParam	- код клавиши
		//	pMsg->hwnd		- хендл контрола окна, который получил сообщение
		//	pMsg->lParam	- дополнительная информация:
	
			//биты 0...15	- количество повторов
			//биты 16...23	- скан-код
			//бит 24		-установлен, если нажат правый Ctrl
	}
	
	if(pMsg->message==WM_CHAR)
	{
		//pMsg - всё аналогично WM_KEYDOWN
	}
	
	return CDialog::PreTranslateMessage(pMsg);
}

===имеется непрерывный участок памяти M. 1) как заполнить M определённым значением? 2) как сравнить два участка памяти M1 и M2 на равенство значений? 3) как скопировать участок M1 в M2 ?=== 1) заполнить участок памяти длиной N байтов можно функцией

  void* memset(void* pM, int val, size_t N);

или при помощи макроса

  ZeroMemory(Destination,Length);//использует memset
  FillMemory(Destination,Length,Fill);//использует memset

2) сравнить два участка памяти длиной N байтов можно функцией

  int memcmp(const void* pM2, const void* pM1, size_t N);

memcmp возвращает 0, если участки памяти идентичны.

3) как скопировать участок M1 длиной N байтов в M2 можно функцией

  void* memcpy(void* pM2, const void* pM1, size_t N);
  void* memmove(void* pM2, const void* pM1, size_t N);

или при помощи макросов

  CopyMemory(Destination,Source,Length);//использует
  MoveMemory(Destination,Source,Length);

Если участки памяти перекрываются, то результат работы memcpy не определён. Функция memmove гарантирует правильное копирование даже при перекрывающихся участках

Примечание: Правильность переданных параметров в функциях, естественно, не проверяется.

Мне нужно удалить контрол (CComboBox) с диалога, причём это надо сделать из обработчика сообщения этого контрола.

Вот так вылетает ошибка:

void CMyDialog::OnSelchange()
{
	CComboBox* m_pControl=(CComboBox*)GetDlgItem(IDC_MYCOMBO);
	m_pControl->DestroyWindow();
}

Удалять объект из принадлежащего ему обработчика нельзя. Нужно удалить объект после завершения обработчика. Например можно послать в диалого-родитель некое сообщение, в обработчике которого и удалить контрол.

Например:

enum
{
	e_command_delete	=	WM_USER+1,
};

void CMyDialog::OnSelchange()
{
	//CComboBox* m_pControl=(CComboBox*)GetDlgItem(IDC_MYCOMBO);
	//m_pControl->DestroyWindow();
	
	//кладём в очередь сообщений своё сообщение
	PostMessage(WM_COMMAND,(e_command_delete<<16) |IDC_MYCOMBO ,0x5555);
}

BOOL CMyDialog::OnCommand(WPARAM wParam, LPARAM lParam) 
{
	if((wParam>>16)==e_command_delete)
	{
		if(((WORD)wParam)==IDC_MYCOMBO)
		{
			CComboBox* m_pControl=(CComboBox*)GetDlgItem(IDC_MYCOMBO);
			m_pControl->DestroyWindow();
			return 1;
		}
	}
	
	return CDialog::OnCommand(wParam, lParam);
}

Я создал простой проект DLL на VC++ 6.0. Все скомпилилось нормально. Но в другой программе не могу вызвать функцию из DLL - программа не может найти функцию по имени.

В папке проекта нужно создать обычный файл txt и переименовать в "имя_проекта_dll.DEF"

В файле перечислить весь экспорт:

EXPORTS
	MyFunctionName1
	MyFunctionName2
	MyFunctionName3

И затем включить файл этот в дерево проекта.

Есть ли какой-нибудь макрос в VC7, возвращающий строку вида ClassName::FunctionName внутри соответствующей функции?

__FUNCSIG__

Как динамически подгрузить ресурсы к ATL проекту?

имеется класс Код:

class CComModule : public _ATL_MODULE

в котором определена переменная m_hInstResource. Нужно присвоить переменной значение хэндла Dll.

Как динамически подгрузить ресурсы к MFC проекту?

InitInstance() загружается Dll с ресурсами, затем вызывается функция AfxSetResourceHandle c параметром - хэндлом этой dll.

BOOL CMyApp::InitInstance()
{
	HINSTANCE hRes = NULL;
	hRes= LoadLibrary("ResourceD.dll");
	if(hRes)
	{
		AfxSetResourceHandle(hRes);
	}
	
	
	return CWinApp::InitInstance();
}

возможно ли использование файлов чистого С и С++ в одном проекте? Если да, то каковы должны быть настройки компилятора? А то ругается на Unexpected end of file while looking for precompiled header directive.

Компилятор распознает язык по расширению файла и устанавливает на весь файл. Нельзя часть файла компилировать как С, а другую как С++. Для того, чтобы Сишные функции вызывать в С++, а С++ в С, их надо декларировать для С++ как

   extern "C"
	#ifdef __cplusplus
		extern "C"
		{
	#endif
	
		int func1(int );
		int func2(int );
		int func3(int );
	
	#ifdef __cplusplus
		}
	#endif

Возможно, надо стереть *.pch файл и собрать снова.

Также в настройках компилятора можно попробовать отключить прекомпиленые хедеры для конкретного файла-

  "C/С++"->"Precompiled Header"

(правда перекомпиляция будет длится немного дольше)

Или можно подключить в файлах stdafx.h (тогда не придется отключать use precompiled header )

где найти описание nmake для VC++6?

В MSDN http://msdn.microsoft.com/library/en-us/vcug98/html/_asug_overview.3a_.nmake_reference.asp

Каким образом можно установить значение переменной для вызывающего процесса/потока из своей собственной DLL ? Проблема заключается в том, что несмотря на то, что все линкуется нормально, значение переменной процесса не изменяется, когда я его устанавливаю вручную в библиотеке.

Проблема вызвана тем, что каждая влинкованная libc (одна линкуется в исполняемый файл, ещё одна в dll) содержит свою копию переменной.

1) Можно воспользоваться динамически подгружаемой библиотекой С runtime. Для этого приложение и dll надо компилировать с ключом /MD (или MDd для отладки). В таком случае переменная будет общей для приложения и dll.

2) Можно в основном модуле программы (тот, который станет .exe) сделать "дырку" (backdoor), через которую присваивать переменной (int errno) значение:

__declspec(dllexport) void set_errno(int code)
{
	errno = code;
}

В dll при подключении к процессу надо извлечь из модуля программы указатель на эту функцию.

	typedef void (*SETINT)(int);
	SETINT g_set_errno=0;
	void set_errno_in_exe(int code)
	{
		if(g_set_errno) g_set_errno(code);
	}
	
	BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
	{
		HANDLE hModule; // Handle to the main module
		if(fdwReason == DLL_PROCESS_ATTACH )
		{
			hModule = GetModuleHandle(NULL);
			if (hModule == NULL) return FALSE;
			g_set_errno = (SETINT) GetProcAddress(hModule, "set_errno");
		}
		return TRUE;
	}

В нужном месте в dll вместо присваивания errno нужно вызывать внешнюю функцию

void inside_dll_func()
{
	...
	...
	set_errno_in_exe(ERROR_CODE);
	...
}

Как закрасить фон окна CWnd ?

Нужно добавить обработчик сообщения WM_CTLCOLOR

class CMyDialog : public CDialog
{
	//
	CBrush m_back_brush;
	...
	...
};

//конструктор
CMyDialog::CMyDialog(): CDialog(CMyDialog::IDD)
{
	//создаём кисть фона
	m_back_brush.CreateSolidBrush(RGB(192,186,207));
}
HBRUSH CMyDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 
{
	switch(nCtlColor)
	{
		//CStatic-контрол
		case WM_CTLCOLORSTATIC:
		{
			//у статиков делаем прозрачный фон
			pDC->SetBkMode(TRANSPARENT);
			//и красный цвет текста
			pDC->SetTextColor(RGB(255,0,0));
	
			//надо же что-то вернуть :)
			return (HBRUSH) (m_back_brush.m_hObject);
		}
		break;
	
		//диалог
		case WM_CTLCOLORDLG:
		{
			//возвращаем хендл кисти нужного фона
			return (HBRUSH) (m_back_brush.m_hObject);
		}
		break;
	}
	
	//фон по умолчанию
	return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}

Как определить , что курсор мыши вышел за границу окна ?

1) способ первый Использовать функцию _TrackMouseEvent. Например, есть класс MyST

class MyST : public CStatic
{
	bool m_bTrackingNow;
	...
	...
};
	
//в конструкторе
MyST:MyST()
{
	 m_bTrackingNow=false;
}
	
//в обработчике OnMouseMove запускаем отслеживание
void MyST::OnMouseMove(UINT nFlags, CPoint point)
{
	if(!m_bTrackingNow)
	{
		m_bTrackingNow=true;
		TRACKMOUSEEVENT tme;
		tme.cbSize=sizeof(tme);
		tme.dwFlags=TME_LEAVE;//отслеживаем выход курсора
		tme.hwndTrack = m_hWnd;//из этого окна
	
		::_TrackMouseEvent(&tme);//"запуск" отслеживания
		//при выходе курсора за границу окна будет
		//будет сгенерировано сообщение WM_MOUSELEAVE
	}
	...
	...
	CStatic::OnMouseMove(nFlags,point);
}
	
//ловим сообщение WM_MOUSELEAVE, для этого
//переопределяем виртуальную PreTranslateMessage()
BOOL MyST::PreTranslateMessage(MSG* pMsg)
{
	if(pMsg->message==WM_MOUSELEAVE)
	{
		//тут обрабатываем
		...
		...
		m_bTrackingNow=false;
	}
	...
	...
	return CStatic::PreTranslateMessage(pMsg);
}

2) способ дыва, для случая, когда надо регулировать время "реакции" на выход за границу

enum
{
	def_TrackTimer_ID		=	1000, //ID таймера
	def_TrackTimer_value	=	50, //миллисекунд
};
	
MyST::MyST()
{
	m_bTrackingNow=false;
}
	
//в обработчике OnMouseMove запускаем таймер
void MyST::OnMouseMove(UINT nFlags, CPoint point)
{
	//перезапуск таймера
	SetTimer(def_TrackTimer_ID,def_TrackTimer_value,0);
	m_bTrackingNow=true;
	...
	...
	CStatic::OnMouseMove(nFlags,point);
}
	
void MyST::OnTimer(UINT nIDEvent)
{
	if(nIDEvent==def_TrackTimer_ID)
	{
		KillTimer(def_TrackTimer_ID);//таймер гасит сам себя
		//смотрим, где курсор
		POINT pnt;
		if(GetCursorPos(&pnt))
		{
			if(WindowFromPoint(pnt)!=this)
			{
				m_bTrackingNow=false;//вышли за окно
			}
		}
	}
	CStatic::OnTimer(nIDEvent);
}

Когда размещаю компонент RichEdit на форму, программа запускается и тут же закрывается. Что здесь не так?

Необходимо до начала использования контрола инициализировать работу с классом:

для RichEdit необходимо вызвать функцию AfxInitRichEdit(), для RichEdit2 - AfxInitRichEdit2().

Вызывать надо в InitInstance() приложения (в случае для MFC).

Как сделать обработчик сообщения для нескольких контролов сразу?

Если без помощи визарда, то переопределить виртуальную OnCommand()

BOOL CMyDialog::OnCommand(WPARAM wParam, LPARAM lParam) 
{
	WORD wMess=(wParam>>16);//командное сообщение
	int nID=(int)(wParam &0x0000ffff));//ID контрола
	HWND hW=(HWND)lParam;//хендл контрола
	
	//смотрим, какой контрол
	switch(nID)
	{
		//кнопки
		case ID_BN1:
		case ID_BN2:
		case ID_BN3:
		{
			//смотрим, какое сообщение
			switch(wMess)
			{
				case BN_CLICKED:{	...	}break;
			}
		}
		break;
	
		//едиты
		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);
}

Когда запускаю программу, то все надписи на русском языке теряются - показываются вопросики. Что делать?

Лечится так: непосредственно после создания проекта открываем дерево ресурсов и в свойствах каждого элемента дерева ставим язык Russian. Если этого не сделать сразу, то все русские при компиляции ресурсов потеряются.

Как проекте VC6 MFC получить путь, откуда запущен ЕХЕ ?

Использовать GetModuleFileName() :

	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 - содержит путь

Как получить доступ к контролам на панели CReBar, принадлежащей классу MainFrame?

#include "MainFrm.h"
	
//CMyApp - класс вашего приложения. theApp - глобальная переменная,
//поэтому для доступа к ней используем extern
extern CMyApp theApp;

void CMyView::F()
{
	//Получаем главное окно приложения в любом месте программы
	CMainFrame* pMainFrame=(CMainFrame*)(theApp.m_pMainWnd);
	pMainFrame->m_wndDlgBar ....//Делаем что хотим
	...
}

Как загрузить и показать один из стандартных курсоров?

HCURSOR hCursor;
hCursor=AfxGetApp()->LoadStandardCursor(IDC_UPARROW);
if(hCursor)SetCursor(hCursor);

идентификаторы стандартных курсоров: IDC_ARROW IDC_IBEAM IDC_WAIT IDC_CROSS IDC_UPARROW IDC_SIZENWSE IDC_SIZENESW IDC_SIZEWE IDC_SIZENS IDC_SIZEALL

Как запретить пользователю закрыть программу нажатием на крестик?

Нужно добавить обработчик сообщеник WM_CLOSE - OnClose() - в главное окно программы. Для диалоговых приложений - это главный диалог, для одно- и много-документных - это CMainFrame.

void CMainFrame::OnClose()
{
	if(......)
	{
		//не разрешаем закрыть
		return;
	}
	
	CFrameWnd::OnClose();
}