FAQ:WinAPI, VCPP Part 2
Содержание
- 1 У меня в программе имеется процедура, которая производит очень много вычислений. Как сделать так, чтобы пользователь мог прервать процесс вычислений?
- 2 не могу обратиться к COM из дополнительного потока, мне пишут- "CoInitialize() не вызвано"
- 3 Как при открытии CFileDialog можно было выбирать только папки?
- 4 как в CDialog-based классе получить коды клавиш которые нажимает пользователь? Пробовал добавлять обработчики OnChar() и OnKeyDown(), эти обработчики вообще не вызываются
- 5 Мне нужно удалить контрол (CComboBox) с диалога, причём это надо сделать из обработчика сообщения этого контрола.
- 6 Я создал простой проект DLL на VC++ 6.0. Все скомпилилось нормально. Но в другой программе не могу вызвать функцию из DLL - программа не может найти функцию по имени.
- 7 Есть ли какой-нибудь макрос в VC7, возвращающий строку вида ClassName::FunctionName внутри соответствующей функции?
- 8 Как динамически подгрузить ресурсы к ATL проекту?
- 9 Как динамически подгрузить ресурсы к MFC проекту?
- 10 возможно ли использование файлов чистого С и С++ в одном проекте? Если да, то каковы должны быть настройки компилятора? А то ругается на Unexpected end of file while looking for precompiled header directive.
- 11 где найти описание nmake для VC++6?
- 12 Каким образом можно установить значение переменной для вызывающего процесса/потока из своей собственной DLL ? Проблема заключается в том, что несмотря на то, что все линкуется нормально, значение переменной процесса не изменяется, когда я его устанавливаю вручную в библиотеке.
- 13 Как закрасить фон окна CWnd ?
- 14 Как определить , что курсор мыши вышел за границу окна ?
- 15 Когда размещаю компонент RichEdit на форму, программа запускается и тут же закрывается. Что здесь не так?
- 16 Как сделать обработчик сообщения для нескольких контролов сразу?
- 17 Когда запускаю программу, то все надписи на русском языке теряются - показываются вопросики. Что делать?
- 18 Как проекте VC6 MFC получить путь, откуда запущен ЕХЕ ?
- 19 Как получить доступ к контролам на панели CReBar, принадлежащей классу MainFrame?
- 20 Как загрузить и показать один из стандартных курсоров?
- 21 Как запретить пользователю закрыть программу нажатием на крестик?
У меня в программе имеется процедура, которая производит очень много вычислений. Как сделать так, чтобы пользователь мог прервать процесс вычислений?
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();
}