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

Материал из Весельчак У
Перейти к: навигация, поиск
(Категория FAQ)
 
(Создан редирект на FAQ:WinAPI_VCPP. Оптимизация для поисковых систем.)
 
(не показаны 54 промежуточные версии 3 участников)
Строка 1: Строка 1:
−
===У меня в программе имеется процедура, которая производит очень много вычислений. Как сделать так, чтобы пользователь мог прервать процесс вычислений?===
+
#REDIRECT [[FAQ:WinAPI_VCPP]]
−
1)  В общем случае решение такое: вычисления производятся в одном потоке (T1) процесса, а обработка команд пользователя - в другом (T2). Одним из потоков может являться и основной поток. Поток T2, получив команду пользователя, выставляет определённый флаг (переменную,событие), доступный также и потоку T1. Поток T1, проверив флаг в определённый момент, прекращает/приостанавливает/продолжает свою работу.
+
−
Естественно, скорость реакции T1 зависит от характера вычислений. Самый приятный вариант - вычисления, выполняемые в цикле, который часто повторяется. Тогда в начале или в конце цикла поток T1 просто проверяет флаг и принимает решение.
+
−
 
+
−
2)  Есть ещё "ленивое решение" (подойдёт для простеньких приложений):
+
−
вычисления выполняются в основном потоке, и также в начале цикла проверяется флаг. А кроме того, перед проверкой флага выполняется такой код:
+
−
<pre>
+
−
//выполнить N сообщений из очереди сообщений
+
−
int imsg;
+
−
MSG m;
+
−
for(imsg=0;imsg<N;imsg++)
+
−
{
+
−
//выборка,выполнение и удаление одного сообщения
+
−
//из очереди сообщений всех окон данного потока
+
−
if(::PeekMessage(&m,0,0,0,PM_REMOVE))
+
−
{
+
−
//транслирование виртуальных клавиш
+
−
::TranslateMessage(&m);
+
−
//обработка сообщения
+
−
::DispatchMessage(&m);
+
−
}
+
−
}
+
−
</pre>
+
−
то есть будут обрабатываться все сообщения, в частности нажатия кнопок. Правда
+
−
с тормозами. Тормоза можно попробовать регулировать числом N.
+
−
 
+
−
Примечание В любом случае необходимо тщательно продумать
+
−
вариант, когда пользователь попытается закрыть программу во время вычислений :)
+
−
Самое простое: в главном окне программы перехватить WM_CLOSE и разрешить
+
−
закрытие программы, либо вывести предупреждение и продолжить работу.
+
−
===не могу обратиться к COM из дополнительного потока, мне пишут- "CoInitialize() не вызвано"===
+
−
нужно вызывать CoInitialize() в каждом новом потоке
+
−
===Как при открытии CFileDialog можно было выбирать только папки?===
+
−
Никак, он этого не делает. Для того, чтобы выбирать директории применяют
+
−
функцию SHBrowseForFolder.
+
−
 
+
−
простой пример
+
−
<pre>
+
−
char pchSelectedF[MAX_PATH]="";
+
−
+
−
BROWSEINFO bi=
+
−
{
+
−
m_hWnd,
+
−
0,
+
−
pchSelectedF,
+
−
"Выбираем папку",
+
−
0,0,0,0
+
−
 
+
−
};
+
−
+
−
SHBrowseForFolder(&bi);
+
−
//bi.pszDisplayName - выбранная папка
+
−
</pre>
+
−
===как в CDialog-based классе получить коды клавиш которые нажимает пользователь? Пробовал добавлять обработчики OnChar() и OnKeyDown(), эти обработчики вообще не вызываются===
+
−
Диалоги действительно не обрабатывают OnChar() и OnKeyDown(), Поэтому нужно
+
−
ловить сообщения WM_CHAR и WM_KEYDOWN в виртуальной PreTranslateMessage()
+
−
<pre>
+
−
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);
+
−
}
+
−
</pre>
+
−
===имеется непрерывный участок памяти 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) с диалога, причём это надо сделать из обработчика сообщения этого контрола.===
+
−
Вот так вылетает ошибка:
+
−
<pre>
+
−
void CMyDialog::OnSelchange()
+
−
{
+
−
CComboBox* m_pControl=(CComboBox*)GetDlgItem(IDC_MYCOMBO);
+
−
m_pControl->DestroyWindow();
+
−
}
+
−
</pre>
+
−
Удалять объект из принадлежащего ему обработчика нельзя.
+
−
Нужно удалить объект после завершения обработчика. Например можно
+
−
послать в диалого-родитель некое сообщение, в обработчике которого
+
−
и удалить контрол.
+
−
 
+
−
Например:
+
−
<pre>
+
−
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);
+
−
}
+
−
</pre>
+
−
===Я создал простой проект DLL на VC++ 6.0. Все скомпилилось нормально. Но в другой программе не могу вызвать функцию из DLL - программа не может найти функцию по имени.===
+
−
В папке проекта нужно создать обычный файл txt и переименовать в
+
−
"имя_проекта_dll.DEF"
+
−
 
+
−
В файле перечислить весь экспорт:
+
−
<pre>
+
−
EXPORTS
+
−
MyFunctionName1
+
−
MyFunctionName2
+
−
MyFunctionName3
+
−
</pre>
+
−
И затем включить файл этот в дерево проекта.
+
−
===Есть ли какой-нибудь макрос в VC7, возвращающий строку вида ClassName::FunctionName внутри соответствующей функции?===
+
−
__FUNCSIG__
+
−
===Как динамически подгрузить ресурсы к ATL проекту?===
+
−
имеется класс
+
−
Код:
+
−
class CComModule : public _ATL_MODULE
+
−
 
+
−
в котором определена переменная m_hInstResource.
+
−
Нужно присвоить переменной значение хэндла Dll.
+
−
===Как динамически подгрузить ресурсы к MFC проекту?===
+
−
InitInstance() загружается Dll с ресурсами, затем вызывается
+
−
функция AfxSetResourceHandle c параметром - хэндлом этой dll.
+
−
<pre>
+
−
BOOL CMyApp::InitInstance()
+
−
{
+
−
HINSTANCE hRes = NULL;
+
−
hRes= LoadLibrary("ResourceD.dll");
+
−
if(hRes)
+
−
{
+
−
AfxSetResourceHandle(hRes);
+
−
}
+
−
+
−
+
−
return CWinApp::InitInstance();
+
−
}
+
−
 
+
−
</pre>
+
−
===возможно ли использование файлов чистого С и С++ в одном проекте? Если да, то каковы должны быть настройки компилятора? А то ругается на Unexpected end of file while looking for precompiled header directive.===
+
−
Компилятор распознает язык по расширению файла и устанавливает на весь файл.
+
−
Нельзя часть файла компилировать как С, а другую как С++. Для того, чтобы Сишные
+
−
функции вызывать в С++, а С++ в С, их надо декларировать для С++ как
+
−
<pre>
+
−
  extern "C"
+
−
#ifdef __cplusplus
+
−
extern "C"
+
−
{
+
−
#endif
+
−
+
−
int func1(int );
+
−
int func2(int );
+
−
int func3(int );
+
−
+
−
#ifdef __cplusplus
+
−
}
+
−
#endif
+
−
</pre>
+
−
Возможно, надо стереть *.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) значение:
+
−
<pre>
+
−
__declspec(dllexport) void set_errno(int code)
+
−
{
+
−
errno = code;
+
−
}
+
−
</pre>
+
−
В dll при подключении к процессу надо извлечь из модуля программы указатель на эту функцию.
+
−
<pre>
+
−
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;
+
−
}
+
−
</pre>
+
−
В нужном месте в dll вместо присваивания errno нужно вызывать внешнюю функцию
+
−
<pre>
+
−
void inside_dll_func()
+
−
{
+
−
...
+
−
...
+
−
set_errno_in_exe(ERROR_CODE);
+
−
...
+
−
}
+
−
</pre>
+
−
===Как закрасить фон окна CWnd ?===
+
−
Нужно добавить обработчик сообщения WM_CTLCOLOR
+
−
<pre>
+
−
class CMyDialog : public CDialog
+
−
{
+
−
//
+
−
CBrush m_back_brush;
+
−
...
+
−
...
+
−
};
+
−
 
+
−
//конструктор
+
−
CMyDialog::CMyDialog(): CDialog(CMyDialog::IDD)
+
−
{
+
−
//создаём кисть фона
+
−
m_back_brush.CreateSolidBrush(RGB(192,186,207));
+
−
}
+
−
</pre>
+
−
<pre>
+
−
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);
+
−
}
+
−
</pre>
+
−
===Как определить , что курсор мыши вышел за границу окна ?===
+
−
1) способ первый
+
−
Использовать функцию _TrackMouseEvent. Например, есть класс MyST
+
−
<pre>
+
−
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);
+
−
}
+
−
</pre>
+
−
2) способ дыва, для случая, когда надо регулировать время "реакции" на выход за границу
+
−
<pre>
+
−
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);
+
−
}
+
−
</pre>
+
−
===Когда размещаю компонент RichEdit на форму, программа запускается и тут же закрывается. Что здесь не так?===
+
−
Необходимо до начала использования контрола инициализировать работу с классом:
+
−
 
+
−
для RichEdit необходимо вызвать функцию AfxInitRichEdit(),
+
−
для RichEdit2 - AfxInitRichEdit2().
+
−
 
+
−
Вызывать надо в InitInstance() приложения (в случае для MFC).
+
−
===Как сделать обработчик сообщения для нескольких контролов сразу?===
+
−
Если без помощи визарда, то переопределить виртуальную OnCommand()
+
−
<pre>
+
−
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);
+
−
}
+
−
</pre>
+
−
===Когда запускаю программу, то все надписи на русском языке теряются - показываются вопросики. Что делать?===
+
−
Лечится так: непосредственно после создания проекта открываем дерево ресурсов и в свойствах каждого элемента дерева ставим язык Russian. Если этого не сделать сразу, то все русские при компиляции ресурсов потеряются.
+
−
===Как проекте VC6 MFC получить путь, откуда запущен ЕХЕ ?===
+
−
Использовать GetModuleFileName() :
+
−
<pre>
+
−
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 - содержит путь
+
−
</pre>
+
−
===Как получить доступ к контролам на панели CReBar, принадлежащей классу MainFrame?===
+
−
<pre>
+
−
#include "MainFrm.h"
+
−
+
−
//CMyApp - класс вашего приложения. theApp - глобальная переменная,
+
−
//поэтому для доступа к ней используем extern
+
−
extern CMyApp theApp;
+
−
 
+
−
void CMyView::F()
+
−
{
+
−
//Получаем главное окно приложения в любом месте программы
+
−
CMainFrame* pMainFrame=(CMainFrame*)(theApp.m_pMainWnd);
+
−
pMainFrame->m_wndDlgBar ....//Делаем что хотим
+
−
...
+
−
}
+
−
</pre>
+
−
===Как загрузить и показать один из стандартных курсоров?===
+
−
<pre>
+
−
HCURSOR hCursor;
+
−
hCursor=AfxGetApp()->LoadStandardCursor(IDC_UPARROW);
+
−
if(hCursor)SetCursor(hCursor);
+
−
</pre>
+
−
идентификаторы стандартных курсоров:
+
−
IDC_ARROW
+
−
IDC_IBEAM
+
−
IDC_WAIT
+
−
IDC_CROSS
+
−
IDC_UPARROW
+
−
IDC_SIZENWSE
+
−
IDC_SIZENESW
+
−
IDC_SIZEWE
+
−
IDC_SIZENS
+
−
IDC_SIZEALL
+
−
===Как запретить пользователю закрыть программу нажатием на крестик?===
+
−
Нужно добавить обработчик сообщеник WM_CLOSE - OnClose() - в главное окно программы. Для диалоговых приложений - это главный диалог, для одно- и много-документных - это CMainFrame.
+
−
<pre>
+
−
void CMainFrame::OnClose()
+
−
{
+
−
if(......)
+
−
{
+
−
//не разрешаем закрыть
+
−
return;
+
−
}
+
−
+
−
CFrameWnd::OnClose();
+
−
}
+
−
</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]]
+

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

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