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

Материал из Весельчак У
Перейти к: навигация, поиск
(Перенаправление на FAQ:WinAPI VCPP)
 
(не показана 21 промежуточная версия 2 участников)
Строка 1: Строка 1:
−
=== Как узнать, что пользователь меняет текст в приложении на основе CRichEditView?===
+
#REDIRECT [[FAQ:WinAPI_VCPP]]
−
Нужно добавить обработчик сообщения EN_CHANGE. Сообщение генерируется CRichEditView каждый раз, когда пользователь изменяет текст в окне CRichEditView
+
−
 
+
−
===Чем отличается метод CArray::GetSize от метода CArray::GetCount?===
+
−
Эти методы абсолютно идентичны. Скорее всего, в более старых версиях библиотек функции, одинаковые по смыслу, имели разные имена. Поэтому в целях совместимости разработчики оставили обе функции)
+
−
 
+
−
===Как загрузить текстовую строку из ресурса?===
+
−
Пример:
+
−
<syntaxhighlight lang="cpp">
+
−
CString m_Temp;
+
−
m_Temp.LoadString(ID_MYSTRING);
+
−
</syntaxhighlight>
+
−
 
+
−
===Как поменять иконку у элемента item в CListCtrl?===
+
−
Пример:
+
−
<syntaxhighlight lang="cpp">
+
−
//заполняем структуру LVITEM
+
−
LVITEM lvItem;
+
−
memset(&lvItem,0,sizeof(lvItem));
+
−
+
−
lvItem.mask =LVIF_IMAGE;//меняться будет картинка
+
−
lvItem.iItem =...;// индекс элемента списка (Zero-based)
+
−
lvItem.iSubItem =0;
+
−
lvItem.iImage =...; //индекс иконки (из списка элемента)
+
−
+
−
pList->SetItem(&lvItem);
+
−
</syntaxhighlight>
+
−
 
+
−
===Имеется класс Class1, содержащий член типа "указатель на Class2". В Class2 также имеется указатель на Class1. Компилятор выдаёт ошибку. Что надо делать?===
+
−
Перекрёстное определение обходится следующим образом: код обоих классов разносится в пары файлов *.h и *.cpp, а перед каждым классом вписывается предопределение другого класса:
+
−
 
+
−
 
+
−
файл "class1.h"
+
−
<syntaxhighlight lang="cpp">
+
−
class Class2;//предопределение Class2
+
−
 
+
−
Class1
+
−
{
+
−
Class2* m_p2;//компилятор разрешит объявление указателя Class2*
+
−
 
+
−
void F1(Class2* p);//компилятор разрешит объявление указателя Class2*
+
−
void F2(Class1* p);
+
−
};
+
−
</syntaxhighlight>
+
−
 
+
−
файл "class1.cpp"
+
−
<syntaxhighlight lang="cpp">
+
−
#include "class1.h"
+
−
#include "class2.h"
+
−
 
+
−
void Class1::F1(Class2* p)
+
−
{
+
−
...
+
−
}
+
−
 
+
−
void Class1::F2(Class1* p)
+
−
{
+
−
...
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
 
+
−
файл "class2.h"
+
−
<syntaxhighlight lang="cpp">
+
−
class Class1;//предопределение Class1
+
−
 
+
−
Class2
+
−
{
+
−
Class1* m_p1;//компилятор разрешит объявление указателя Class1*
+
−
 
+
−
void F3(Class1* p);//компилятор разрешит объявление указателя Class1*
+
−
};
+
−
</syntaxhighlight>
+
−
 
+
−
 
+
−
файл "class2.cpp"
+
−
<syntaxhighlight lang="cpp">
+
−
#include "class2.h"
+
−
#include "class1.h"
+
−
 
+
−
void Class2::F3(Class1* p)
+
−
{
+
−
...
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
 
+
−
После предопределения класса компилятор позволяет объявлять указатель на класс, так как переменная указателя на любой тип имеет всегда один и тот же размер.
+
−
 
+
−
===Как переключить раскладку в другом (то есть в активном) процессе?===
+
−
Для этого можно использовать функции
+
−
 
+
−
* GetKeyboardLayout(...); // определить текущую раскладку
+
−
* LoadKeyboardLayout(...); // загрузить новую раскладку
+
−
* VerLanguageName(...); // получить строку с описанием языка
+
−
 
+
−
Подробности - в MSDN
+
−
 
+
−
===Как получить хендл элемента управления, зная его идентификатор?===
+
−
Пример:
+
−
Пусть элемент Edit лежит на окне CMyWnd.
+
−
Тогда:
+
−
<syntaxhighlight lang="cpp">
+
−
void CMyWnd::некая_процедура()
+
−
{
+
−
//для MFC
+
−
CEdit* ed=(CEdit* ) GetDlgItem(ID_EDIT1);
+
−
HWND hwnd=ed->GetSafeHwnd();
+
−
if(hwnd)
+
−
{
+
−
...
+
−
}
+
−
+
−
//для Win32 API
+
−
HWND hwnd=GetDlgItem(m_hWnd,ID_EDIT1);
+
−
if(hwnd)
+
−
{
+
−
...
+
−
}
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
===Как из дочернего окна закрыть приложение?===
+
−
Для этого нужно послать родительскому окну сообщение WM_CLOSE:
+
−
 
+
−
через указатель на родительское окно:
+
−
<syntaxhighlight lang="cpp">
+
−
pParent->PostMessage(WM_CLOSE);
+
−
</syntaxhighlight>
+
−
 
+
−
при помощи хендла родительского окна:
+
−
<syntaxhighlight lang="cpp">
+
−
::PostMessage(pParent->m_hWnd,WM_CLOSE,(WPARAM)0,(LPARAM)0);
+
−
</syntaxhighlight>
+
−
 
+
−
===Как корректно перевести тип BSTR в CString и наоборот?===
+
−
 
+
−
# для конвертирования BSTR в CString нужно просто присвоить переменной типа CString переменную типа BSTR.
+
−
Оператор "=" класса CString сам выполнит всю работу.
+
−
 
+
−
# для конвертирования BSTR в CString нужно выполнить код:
+
−
 
+
−
<syntaxhighlight lang="cpp">
+
−
//указатель на будущий буфер с BSTR.
+
−
BSTR bstrHE=0;
+
−
// (По сути, BSTR определён как
+
−
// typedef WCHAR* BSTR;
+
−
// то есть указатель на 16-битный символ юникод)
+
−
 
+
−
//строка для конвертации
+
−
CString Str="мой текст";
+
−
 
+
−
//Выделяем память и загружаем адрес блока в bstrHE
+
−
bstrHE=Str.AllocSysString();
+
−
 
+
−
//...
+
−
//тут работаем с bstrHE[]
+
−
//...
+
−
 
+
−
//освобождаем память
+
−
SysFreeString(bstrHE);
+
−
</syntaxhighlight>
+
−
 
+
−
===Как получить доступ к графическим ресурсам элементов текущей темы оформления Windows?===
+
−
 
+
−
Для этого нужно использовать функции
+
−
 
+
−
* OpenThemeData(...);
+
−
* DrawThemeBackground(...);
+
−
* DrawFrameControl(...);
+
−
* CloseThemeData(...);
+
−
 
+
−
(подробности - в MSDN)
+
−
 
+
−
===Имеется класс, производный от CDialog. Когда диалог в фокусе, нажатие на Enter или Esc приводит к закрытию диалога. Как это запретить?===
+
−
При нажатии Enter происходит выполнение команды IDOK,при нажатии Esc - IDCANCEL.
+
−
 
+
−
Нужно переопределить в классе виртуальные функции OnOk() и OnCancel(), которые соответственно вызываются для этих команд. При помощи визарда эти функции добавить можно так:
+
−
 
+
−
Положить на диалог две кнопки с идентификаторами IDOK и IDCANCEL (при создании нового диалога они там есть сразу), затем двойной щелчок по кнопке добавит соответствующий обработчик. В обработчиках надо удалить вызовы СDialog::OnOK() и CDialog::OnCancel(), тогда диалог закрываться не будет.
+
−
 
+
−
Однако тогда диалог станет невозможно закрыть кнопкой с крестиком. Это обходится так: добавляем обработчик сообщения WM_CLOSE - OnClose(), и в нём делаем вызов OnOk() или OnCancel():
+
−
<syntaxhighlight lang="cpp">
+
−
void CPlayersPropsDialog::OnClose()
+
−
{
+
−
CDialog::OnClose();
+
−
CDialog::OnCancel();//добавлено
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
===Я написал в VC++ простейший макрос, где невозможно ошибиться, а компилятор всё же выдаёт ошибку. Не пойму, в чём дело?===
+
−
 
+
−
Скорее всего после одного из символов соединения строк "\" имеется пробел или табуляция. Их надо удалить. Можно найти их "на ощупь", а можно включить показ непечатных символов в студии. Найти эту команду можно так:
+
−
 
+
−
Tools->Customize...->вкладка Commands. В окошке Category выбрать "Edit", и среди кнопок справа найти кнопку, на которой написано "a.b"
+
−
 
+
−
Эту кнопку надо перетащить на одну из панелей инструментов студии.
+
−
 
+
−
===Как получить список всех процессов, включая idle?===
+
−
 
+
−
Использовать функции
+
−
* Process32First(...);
+
−
* Precess32Next(...);
+
−
 
+
−
(подробности - в MSDN)
+
−
 
+
−
===Как программно установить переменные окружения?===
+
−
Чтобы добавить или изменить их программно, необходимо воспользоваться ключом реестра
+
−
<syntaxhighlight lang="cpp">
+
−
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment
+
−
</syntaxhighlight>
+
−
 
+
−
Затем нужно отправить широковещательное сообщение WM_SETTINGCHANGE (это позволит приложениям узнать об изменениях):
+
−
<syntaxhighlight lang="cpp">
+
−
::SendMessage(HWND_BROADCAST,WM_SETTINGCHANGE,0,0);
+
−
</syntaxhighlight>
+
−
 
+
−
 
+
−
===Как сделать, чтобы у окна был черный фон?===
+
−
Нужно переопределить обработчик сообщения WM_CTLCOLOR - OnCtlColor():
+
−
 
+
−
<syntaxhighlight lang="cpp">
+
−
//глобальная переменная или член класса CMyDlg,
+
−
// инициализированный в OnInitDialog()
+
−
CBrush br(RGB(0,0,0));
+
−
 
+
−
 
+
−
HBRUSH CMyDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+
−
{
+
−
HBRUSH hbr = CMyDlg::OnCtlColor(pDC, pWnd, nCtlColor);
+
−
+
−
//фон диалога (или View)
+
−
if(nCtlColor==CTLCOLOR_DLG)
+
−
{
+
−
return (HBRUSH)br;
+
−
}
+
−
+
−
//фон элементов SCtatic, лежащих на форме (если нужно)
+
−
if(nCtlColor==CTLCOLOR_STATIC)
+
−
{
+
−
//делаем фон текста статика прозрачным
+
−
pDC->SetBkMode(TRANSPARENT);
+
−
return (HBRUSH)br;
+
−
}
+
−
+
−
return hbr;
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
===Почему на моем компьютере экзешник, созданный в MFC запускается, а на других компьютерах - нет? Требует какую-то  dll-ку.===
+
−
Нужно выбирать режим Release:
+
−
 
+
−
Menu->Build->Configurations...->Release,
+
−
 
+
−
и экзешник, соответственно, брать из папки Release проекта.
+
−
 
+
−
===Как в диалог добавить меню?===
+
−
Нужно создать в редакторе ресурсов новое меню, затем вставить его в диалог:
+
−
 
+
−
# открыть в редакторе диалог,
+
−
# в свойствах (во вкладке General, окошко Menu) указать идентификатор ресурса меню.
+
−
# при помощи визарда добавить для меню обработчики OnCommand() и OnUpdateСommand().
+
−
 
+
−
===Почему не вызывается OnUpdate для пунктов меню (не получается ни затенить, ни отметку поставить)?===
+
−
В диалоге (CDialog) у меню апдейт не происходит сам, в отличие от мейнфрейма (CFrameWnd). Для этого нужно сделать самим апдейт в обработчике сообщения WM_KICKIDLE.
+
−
 
+
−
Визардом этот обработчик не добавляется, поэтому можно переопределить виртуальную DefWindowProc() и там обработать:
+
−
<syntaxhighlight lang="cpp">
+
−
#include <afxpriv.h> //этот файл надо включить, там определёна WM_KICKIDLE
+
−
 
+
−
LRESULT CMyDialog::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
+
−
{
+
−
if(message==WM_KICKIDLE)
+
−
{
+
−
//делаем апдейт для всех пунктов меню
+
−
CMenu* pMainMenu=GetMenu();
+
−
if(pMainMenu)
+
−
{
+
−
CCmdUI cmdUI;
+
−
for(UINT n=0; n < pMainMenu->GetMenuItemCount(); n++)
+
−
{
+
−
CMenu* pSubMenu = pMainMenu->GetSubMenu(n);
+
−
if(!pSubMenu)continue;
+
−
+
−
cmdUI.m_nIndexMax = pSubMenu->GetMenuItemCount();
+
−
for(UINT i=0; i<cmdUI.m_nIndexMax; i++)
+
−
{
+
−
cmdUI.m_nIndex = i;
+
−
cmdUI.m_nID = pSubMenu->GetMenuItemID(i);
+
−
cmdUI.m_pMenu = pSubMenu;
+
−
cmdUI.DoUpdate(this, FALSE);
+
−
}
+
−
}
+
−
}
+
−
}
+
−
 
+
−
return CDialog::DefWindowProc(message, wParam, lParam);
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
 
+
−
===Как в главном окне отключить системное меню (в левом верхнем углу) и кнопки(в правом верхнем углу)?===
+
−
При создании окна нужно убрать стиль WS_SYSMENU (теперь пользователь корректно может закрыть окно только Alt+F4, ну или если вы предоставите ему дополнительную возможность сделать это)
+
−
 
+
−
Пример 1. (для SDI, MDI)
+
−
<syntaxhighlight lang="cpp">
+
−
//этот обработчик уже добавлен визардом
+
−
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
+
−
{
+
−
if( !CFrameWnd::PreCreateWindow(cs) )
+
−
return FALSE;
+
−
// TODO: Modify the Window class or styles here by modifying
+
−
//  the CREATESTRUCT cs
+
−
 
+
−
///////////////////////////////
+
−
//добавленная часть
+
−
{
+
−
//убираем стиль
+
−
cs.style&=~WS_SYSMENU;
+
−
}
+
−
///////////////////////////////
+
−
 
+
−
return TRUE;
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
Пример 2. (для диалога)
+
−
 
+
−
Если диалог описан в ресурсах, то в свойствах диалога убрать галочки
+
−
 
+
−
* "System menu"
+
−
* "Minimize box"
+
−
* "Maximize box"
+
−
 
+
−
===Как запретить пользователю нажать на кнопку?===
+
−
Нужно применить метод класса CWnd::EnableWindow(...) для кнопки. Значение параметра 0 делает кнопку неактивной, 1 - делает активной (кстати, не только для кнопки можно, а для любого класса, производного от класса CWnd).
+
−
 
+
−
 
+
−
Пусть имеется некий диалог, на нём лежит кнопка c ID == IDC_1. В любом месте кода диалога (кроме конструктора и деструктора, хотя, если проверить наличие валидного хендла диалога, как в примере, то ничего страшного не будет и там) выполняем код
+
−
<syntaxhighlight lang="cpp">
+
−
//делаем кнопку неактивной
+
−
if(m_hWnd)
+
−
{
+
−
CWnd* pw=0;
+
−
pw=GetDlgItem(IDC_1);
+
−
if(pw)
+
−
{
+
−
pw->EnableWindow(0);
+
−
}
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
===Что означают сообщения, которые студия выводит при компиляции, вроде таких: Loaded 'C:\WINDOWS\SYSTEM\WSOCK32.DLL', no matching symbolic information found. ===
+
−
Это строка говорит о том, что студия не смогла найти отладочные символы для WSOCK32.DLL. В этом нет ничего страшного, просто при наличии отладочных символов в процессе отладки можно получить доступ к именам функции из системных DLL. Например, вместо
+
−
<syntaxhighlight lang="cpp">
+
−
KERNEL32! 0x77E8B184()
+
−
</syntaxhighlight>
+
−
увидим
+
−
 
+
−
<syntaxhighlight lang="cpp">
+
−
KERNEL32!CreateThread.
+
−
</syntaxhighlight>
+
−
 
+
−
===Как обработать сообщения, которое приходит к некому элементу управления?===
+
−
Скажем, нужно обработать сообщения, приходящие к элементу класса CEdit. Нужно произвести от CEdit свой класс, и в виртуальной процедуре класса WindowProc перехватить нужные сообщения.
+
−
<syntaxhighlight lang="cpp">
+
−
virtual LRESULT WindowProc(
+
−
UINT message,
+
−
WPARAM wParam,
+
−
LPARAM lParam
+
−
);
+
−
</syntaxhighlight>
+
−
 
+
−
Пример. Полностью выключаем, скажем, обработку сообщения WM_CHAR
+
−
<syntaxhighlight lang="cpp">
+
−
LRESULT CMyEdit::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
+
−
{
+
−
switch(message)
+
−
{
+
−
case WM_CHAR:
+
−
{
+
−
return 0;
+
−
}
+
−
break;
+
−
}
+
−
+
−
return CEdit::WindowProc(message, wParam, lParam);
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
Чтобы связать экземпляр класса с элементом управления на форме, добавьте визардом для
+
−
элемента управления переменную класса (выбрав не Value а Control).
+
−
 
+
−
===Как настроить количество пробелов в табуляции?===
+
−
Есть пара способов:
+
−
 
+
−
# Tools->Options->Tabs->Insert Spaces
+
−
# правой кнопкой мыши щёлкнуть по тексту, в контекстном меню выбрать properties
+
−
 
+
−
===Как автоматически расставить отступы?===
+
−
Нужно в студии выделить текст для форматирования, нажать Alt+F8.
+
−
 
+
−
Ещё есть программа Artistic Style ( http://sourceforge.net/projects/astyle/ ), которая занимается переформатированием кода.
+
−
 
+
−
===Поиск границ блока===
+
−
Сочетание клавиш CTRL+ "}" ищет парную фигурную скобку в тексте и переходит к ней.
+
−
 
+
−
===Вертикальное выделение текста===
+
−
Если удерживать клавишу Alt, а затем начать выделять текст мышкой, то область выделения принимает форму прямоугольника, что позволяет выделять вертикальные фрагменты текста.
+
−
 
+
−
===Как узнать количество установленных в CListCtrl столбцов?===
+
−
Пример
+
−
<syntaxhighlight lang="cpp">
+
−
//указатель на переменную-элемент управления
+
−
CListCtrl* pL=...;
+
−
 
+
−
//получаем количество столбцов
+
−
int count=pL->GetHeaderCtrl()->GetItemCount();
+
−
</syntaxhighlight>
+
−
 
+
−
===Как получить иконку приложения?===
+
−
Если приложение запущено, то нужно найти его главное окно и послать ему сообщение WM_GETICON.
+
−
<syntaxhighlight lang="cpp">
+
−
//функция возвращант хендл иконки
+
−
LRESULT SendMessage(hWnd, WM_GETICON, wParam, 0);
+
−
 
+
−
//hWnd == хендл окна приложения
+
−
//wParam ==
+
−
// 1)==ICON_BIG - получить большую иконку
+
−
// 2)==ICON_SMALL - получить маленькую иконку
+
−
// 3)==ICON_SMALL2 - получить маленькую иконку, если она определена
+
−
// в приложении. Если её нет, то маленькую иконку,
+
−
// сгенерированную системой из большой иконки
+
−
</syntaxhighlight>
+
−
 
+
−
===Как в CString можно найти или вырезать часть строки?===
+
−
Это можно сделать при помощи методов класса CString:
+
−
<syntaxhighlight lang="cpp">
+
−
//вырезает кусок строки
+
−
CString Mid(int nFirst) const;
+
−
CString Mid(int nFirst,  int nCount) const;
+
−
 
+
−
//возвращает кусок строки (сначала или с конца)
+
−
CString Left(int nCount ) const;
+
−
CString Right(int nCount ) const;
+
−
 
+
−
//возвращает начальный кусок строки, в котором есть только
+
−
//символы из набора, представленного в lpszCharSet
+
−
CString SpanIncluding(LPCTSTR lpszCharSet ) const;
+
−
 
+
−
//возвращает начальный кусок строки, в котором нет
+
−
//символов из набора, представленного в lpszCharSet
+
−
CString SpanExcluding(LPCTSTR lpszCharSet ) const;
+
−
 
+
−
//убирает "пробелоподобные" символы из самого начала строки
+
−
//(то есть - пробел, табуляцию (\t), возврат каретки, перевод строки (/r/n))
+
−
void TrimLeft();
+
−
//убирает все повторы символа из самого начала строки
+
−
void TrimLeft(TCHAR chTarget );
+
−
//убирает из самого начала строки все символа из набора lpszTargets
+
−
void TrimLeft(LPCTSTR lpszTargets );
+
−
 
+
−
void TrimRight();
+
−
void TrimRight(TCHAR chTarget );
+
−
void TrimRight(LPCTSTR lpszTargets );
+
−
 
+
−
//поиск в строке
+
−
int Find( TCHAR ch ) const;
+
−
int Find( LPCTSTR lpszSub ) const;
+
−
int Find( TCHAR ch, int nStart ) const;
+
−
int Find( LPCTSTR pstr, int nStart ) const;
+
−
 
+
−
//поиск, начиная с конца
+
−
int ReverseFind( TCHAR ch ) const;
+
−
 
+
−
//поиск позиции первого символа, одного из набора,
+
−
// представленного в lpszCharSet
+
−
int FindOneOf( LPCTSTR lpszCharSet ) const;
+
−
</syntaxhighlight>
+
−
 
+
−
===Как отобразить на элементах управления промежуточные результаты длительных вычислений?===
+
−
В общем случае это делается принудительной перерисовкой нужного окна путём объявления этого окна невалидным
+
−
<syntaxhighlight lang="cpp">
+
−
pWnd->Invalidate(0);
+
−
</syntaxhighlight>
+
−
а затем непосредственной отсылки в оконную процедуру сообщения WM_PAINT
+
−
<syntaxhighlight lang="cpp">
+
−
pWnd->UpdateWindow();
+
−
</syntaxhighlight>
+
−
 
+
−
Пример:
+
−
<syntaxhighlight lang="cpp">
+
−
CWnd* pWnd=...;//окно, которое надо перерисовывать
+
−
 
+
−
for(int i=0; i<10000; i++)
+
−
{
+
−
//меняется содержимое окна
+
−
//...
+
−
 
+
−
//немедленная перерисовка
+
−
pWnd->Invalidate(0);
+
−
pWnd->UpdateWindow();
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
Также можно также просто вызвать метод RedrawWindow() с параметрами по умолчанию.
+
−
 
+
−
===Я написал DLL, которую используют несколько приложений. Всё вроде работает, но когда происходит очередной вызов функции из DLL, почему то данные в функции обнуляются. С чем это связано?===
+
−
Переменные и массивы в DLL, содержимое которых должно использоваться несколькими процессами, должны объявляться статическими. Статическая переменная или массив инициализируются только один раз в момент загрузки DLL.
+
−
Пример:
+
−
<syntaxhighlight lang="cpp">
+
−
DWORD calltest()
+
−
{
+
−
//будет выполнено только при первом вызове
+
−
static DWORD callcount=0;
+
−
static DWORD str[1000]={0};
+
−
 
+
−
//будет выполняться каждый раз
+
−
callcount++;
+
−
return callcount;
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
===Как открыть проекцию файла в память и как с ней работать?===
+
−
 
+
−
Создание проекции:
+
−
<syntaxhighlight lang="cpp">
+
−
HANDLE CreateFileMapping(
+
−
HANDLE hFile, //хендл уже открытого файла
+
−
LPSECURITY_ATTRIBUTES lpAttributes,
+
−
DWORD flProtect, //способ открытия проекции
+
−
DWORD dwMaximumSizeHigh, //размер файла (старшие 4 байта, обычно==0)
+
−
DWORD dwMaximumSizeLow, //размер файла (младшие 4 байта)
+
−
LPCTSTR lpName
+
−
);
+
−
</syntaxhighlight>
+
−
 
+
−
Доступ к созданной проекции производится процедурой:
+
−
<syntaxhighlight lang="cpp">
+
−
LPVOID MapViewOfFile(
+
−
HANDLE hFileMappingObject, //хендл проекции
+
−
DWORD dwDesiredAccess, //способ работы с проекцией
+
−
DWORD dwFileOffsetHigh,
+
−
DWORD dwFileOffsetLow, //смещение от начала файла (младшие 4 байта)
+
−
SIZE_T dwNumberOfBytesToMap //длина в байтах (если ==0 - то весь файл)
+
−
);
+
−
</syntaxhighlight>
+
−
 
+
−
далее с файлом можно работать как с обычным массивом в памяти.
+
−
 
+
−
После работы с проекцией, её надо освободить процедурой
+
−
<syntaxhighlight lang="cpp">
+
−
BOOL UnmapViewOfFile(
+
−
LPCVOID lpBaseAddress //адрес, который вернула процедура MapViewOfFile
+
−
);
+
−
</syntaxhighlight>
+
−
 
+
−
Пример:
+
−
<syntaxhighlight lang="cpp">
+
−
HANDLE hFile=...;//хендл уже открытого файла
+
−
DWORD dwdFileLen=...;//размер открытого файла
+
−
 
+
−
HANDLE hMapFile=0;//хендл для проекции
+
−
 
+
−
//создаём проекцию "только для чтения"
+
−
hMapFile=::CreateFileMapping(hFile,0,PAGE_READONLY,0,dwdFileLen,0);
+
−
 
+
−
//хендл hFile можно закрыть уже здесь, в принципе,
+
−
//но мы сделаем это позже
+
−
 
+
−
//получаем доступ к проекции (тоже только для чтения)
+
−
BYTE* pbyFile=(BYTE*)::MapViewOfFile(hMapFile,FILE_MAP_READ,0,0,0);
+
−
 
+
−
///////////////////
+
−
//тут работаем с массивом pbyFile[dwdFileLen] как с обычным,
+
−
//только не забываем, что он открыт только для чтения
+
−
//...
+
−
//...
+
−
//...
+
−
///////////////////
+
−
 
+
−
//отключаем файл данных от адресного пространства
+
−
UnmapViewOfFile(pbyFile);
+
−
 
+
−
//освобождаем хендл проекции
+
−
CloseHandle(hMapFile)
+
−
 
+
−
//освобождаем хендл открытого файла
+
−
CloseHandle(hFile);
+
−
</syntaxhighlight>
+
−
 
+
−
===Как в MFC Grid control отобразить картинку в ячейке?===
+
−
Нужно создать список изображений (CImageList), затем передать указатель на список в таблицу. Поскольку элемент управления лишь копирует указатель, список должен создаваться динамически, либо быть членом класса, экземпляр которого существует всё время работы таблицы.
+
−
<syntaxhighlight lang="cpp">
+
−
CImageList m_ImageList;
+
−
CGridCtrL m_Grid1;
+
−
...
+
−
...
+
−
//неким образом создаётся список
+
−
m_ImageList.Create(...);
+
−
//...
+
−
 
+
−
//вставляем в элемент управления
+
−
m_Grid1.SetImageList(&m_ImageList);
+
−
 
+
−
//
+
−
</syntaxhighlight>
+
−
 
+
−
===Как при помощи IPicture отобразить картинку из файла? ===
+
−
Пример - несложная функция, отображающая картинку из файла. Поддерживаются форматы BMP, GIF, JPEG, PNG, TIFF, EMF
+
−
<syntaxhighlight lang="cpp">
+
−
#include "atlconv.h"
+
−
#define HandleIsValid(H) (H!=(HANDLE)-1 && H!=(HANDLE)0)
+
−
 
+
−
//*pDestDC - CDC, на который предполагается вывод картинки
+
−
//pchzFilePath - путь к файлу
+
−
//pWid - (возвращает) ширина картинки
+
−
//pHig - (возвращает) высота картинки
+
−
bool DrawBitmapFromFile(CDC* pDestDC,const char* pchzFilePath,int *pWid=0,int *pHig=0)
+
−
{
+
−
bool result=false;
+
−
 
+
−
if(pWid)*pWid=0;
+
−
if(pHig)*pHig=0;
+
−
 
+
−
int Wid=0;
+
−
int Hig=0;
+
−
 
+
−
IPicture* pPic=0;
+
−
try
+
−
{
+
−
IPicture* ptmpPic=0;
+
−
USES_CONVERSION;
+
−
HRESULT hr;
+
−
 
+
−
CString txt=pchzFilePath;
+
−
hr=::OleLoadPicturePath(
+
−
const_cast<LPOLESTR>(T2COLE(txt)),
+
−
0,0,0,IID_IPicture,
+
−
reinterpret_cast<void **>(&ptmpPic)
+
−
);
+
−
 
+
−
if(hr==S_OK && ptmpPic)
+
−
{
+
−
pPic=ptmpPic;
+
−
}
+
−
else
+
−
{
+
−
throw 0;
+
−
}
+
−
 
+
−
OLE_XPOS_HIMETRIC cxSrc;
+
−
OLE_YPOS_HIMETRIC cySrc;
+
−
 
+
−
if(S_OK!=pPic->get_Width(&cxSrc))
+
−
{
+
−
throw 0;
+
−
}
+
−
 
+
−
if(S_OK!=pPic->get_Height(&cySrc))
+
−
{
+
−
throw 0;
+
−
}
+
−
 
+
−
Wid=cxSrc/26;
+
−
Hig=cySrc/26;
+
−
 
+
−
//рисуем
+
−
if(S_OK !=pPic->Render(pDestDC->GetSafeHdc(),
+
−
0,Hig,Wid,-Hig,0,0,cxSrc,cySrc,0))
+
−
{
+
−
throw 0;
+
−
}
+
−
}
+
−
catch(...)
+
−
{
+
−
result=false;
+
−
}
+
−
 
+
−
if(pPic)
+
−
{
+
−
pPic->Release();
+
−
pPic=0;
+
−
}
+
−
 
+
−
 
+
−
if(pWid)
+
−
{
+
−
*pWid=Wid;
+
−
}
+
−
 
+
−
if(pHig)
+
−
{
+
−
*pHig=Hig;
+
−
}
+
−
 
+
−
return result;
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
===Как позволить пользователю начать ввод новой строки в многострочном поле редактирования?===
+
−
Нужно в редакторе ресурсов поставить для свойств окошка CEdit галочку "WantReturn" или добавить стиль ES_WANTRETURN при создании CEdit элемента управления динамически. Тогда при нажатии Enter в поле редактирования будет вводиться новая строка.
+
−
 
+
−
===Как выводят картинку-логотип(splash screen) при запуске программы?===
+
−
Для этого используется класс, производный от CDialog. Этот диалог после создания показывает себя на экран, выводя на себя рисунок. Также запускает таймер, по срабатывани которого диалог гасится. Создаётся диалог в процедуре InitInstance приложения.
+
−
 
+
−
В VC6++ также есть уже готовый компонент
+
−
 
+
−
Project->Add To Project -> Components and Controls->(в папке "Visual C++ Components" есть SplashScreen.)
+
−
 
+
−
===Как сделать, чтобы при выпадении списка у ComboBox была не одна строка, а больше, вроде все свойства покрутил, не помогает Вместо выпадающего списка одна строка и скролл...===
+
−
В редакторе ресурсов надо щелкнуть в комбобоксе на треугольнике - появляется рамка для растягивания вниз - размер выпадающего списка.
+
−
 
+
−
===В VC++.net, что делать после того, как на форму установил ActiveX ListView, как объявить класс и переменную для этого элемента?===
+
−
Щёлкнуть правой кнопкой мыши на элементе, в меню - add variable или add class
+
−
 
+
−
===Как работать с буфером обмена (Clipboard)?===
+
−
 
+
−
====КОПИРОВАНИЕ В БУФЕР ОБМЕНА====
+
−
 
+
−
Алгоритм:
+
−
# Готовим данные
+
−
#* Выделяем память из кучи,  вызывая GlobalAlloc()
+
−
#* Получаем указатель на выделенную память, вызывая GlobalLock()
+
−
#* Заполняем данные
+
−
#* Освобождаем указатель, вызывая GlobalUnlock();
+
−
# Открываем буфер обмена, вызывая OpenClipboard();
+
−
# Очищаем буфер, вызывая EmptyClipboard();
+
−
# Вызываем SetClipboardData() один раз для каждого формата вставляемых данных (имеется в виду - если одни и те же данные представлены в разных форматах, и приложение может эти форматы создать)
+
−
# Закрываем буфер, вызывая CloseClipboard();
+
−
# ВЫЗЫВАТЬ GlobalFree() НЕ НУЖНО, в этом случае это предоставлено системе
+
−
 
+
−
Скажем, хотим поместить в буфер обмена текст.
+
−
<syntaxhighlight lang="cpp">
+
−
//готовим данные
+
−
//выделяем из кучи память для данных
+
−
HANDLE hglbCopy = GlobalAlloc(GMEM_MOVEABLE,10);
+
−
if(!hglbCopy)return;
+
−
 
+
−
//получаем указатель
+
−
void* lpStr = GlobalLock(hglbCopy);
+
−
if(!lpStr)return;
+
−
 
+
−
//заполняем данные
+
−
strcpy((char*)lpStr,"my text");
+
−
 
+
−
//освобождаем указатель
+
−
GlobalUnlock(hglbCopy);
+
−
lpStr=0;
+
−
 
+
−
//открываем буфер обмена
+
−
OpenClipboard();
+
−
//очищаем
+
−
EmptyClipboard();
+
−
 
+
−
//текстовый формат
+
−
SetClipboardData(CF_TEXT,hglbCopy);
+
−
 
+
−
//закрываем буфер
+
−
CloseClipboard();
+
−
</syntaxhighlight>
+
−
 
+
−
 
+
−
 
+
−
====ИЗВЛЕЧЕНИЕ ИЗ БУФЕРА ОБМЕНА====
+
−
 
+
−
Алгоритм:
+
−
# Проверяем, что поддерживается нужный формат данных, вызывая IsClipboardFormatAvailable()
+
−
# Открываем буфер обмена, вызывая OpenClipboard();
+
−
# Достаём данные
+
−
#* Получаем из буфера хендл требуемого формата, вызывая GetClipboardData()
+
−
#* Получаем указатель на выделенную память, вызывая GlobalLock()
+
−
#* Работаем с данными
+
−
#* Освобождаем указатель, вызывая GlobalUnlock();
+
−
# Закрываем буфер, вызывая CloseClipboard();
+
−
 
+
−
Скажем, хотим извлечь из буфера обмена текст.
+
−
<syntaxhighlight lang="cpp">
+
−
//Проверяем, что поддерживается нужный формат данных,
+
−
if(!IsClipboardFormatAvailable(CF_TEXT))return;
+
−
 
+
−
//открываем буфер обмена
+
−
OpenClipboard();
+
−
 
+
−
//достаём данные
+
−
 
+
−
//текстовый формат
+
−
HANDLE hglbCopy = GetClipboardData(CF_TEXT);
+
−
if(!hglbCopy)return;
+
−
 
+
−
//получаем указатель
+
−
void* lpStr = GlobalLock(hglbCopy);
+
−
if(!lpStr)return;
+
−
 
+
−
//читаем данные
+
−
char data[10];
+
−
memmove(data,lpStr,sizeof(data));
+
−
 
+
−
//освобождаем указатель
+
−
GlobalUnlock(hglbCopy);
+
−
lpStr=0;
+
−
 
+
−
//закрываем буфер
+
−
CloseClipboard();
+
−
</syntaxhighlight>
+
−
 
+
−
 
+
−
===Как получить полный путь к экзешнику из самой программы?===
+
−
Можно прочитать параметры командной строки - в командную строку первым параметром система всегда передаёт заключённый в кавычки полный путь к файлу запущенной программы. Достаём путь следующим образом:
+
−
<syntaxhighlight lang="cpp">
+
−
BOOL CMyApp::InitInstance()
+
−
{
+
−
//добыча полного имени экзешника
+
−
CString csFullExeName;
+
−
{
+
−
CString csAppName=GetCommandLine();
+
−
csAppName.Delete(0,1);
+
−
csAppName.Replace('\"','\0');
+
−
csFullExeName=(const char*)csAppName;
+
−
}
+
−
//теперь csFullExeName содержит искомый путь
+
−
 
+
−
//...
+
−
//...
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
===Каким способом exe файл может заменить самого себя? ===
+
−
 
+
−
К примеру, программа должна обновляется через Internet и хочет обновить свой exe-файл. Но так как он в данный момент запущен, это будет, естественно, невозможно сделать напрямую.
+
−
Один из способов - сделать копию экзешника и из него обновиться
+
−
 
+
−
<syntaxhighlight lang="cpp">
+
−
#define def_exeNAMEup "c:\\myprog_toupdate.exe" //путь к старому файлу
+
−
#define def_exeNAMEdnld "c:\\myprog_downloaded.exe" //путь к новому файлу (уже "скачали":) )
+
−
#define def_keyUdate "/update"
+
−
 
+
−
void CTESTFAQApp::UpdateItself()
+
−
{
+
−
//добыча полного имени экзешника
+
−
CString csFullExeName;
+
−
{
+
−
CString csAppName=GetCommandLine();
+
−
csAppName.Delete(0,1);
+
−
csAppName.Replace('\"','\0');
+
−
csFullExeName=(const char*)csAppName;
+
−
}
+
−
//теперь csFullExeName - содержит искомый путь
+
−
 
+
−
//делаем копию экзешника с другим именем
+
−
CopyFile(csFullExeName,def_exeNAMEup,0);
+
−
 
+
−
//передаём в параметры нового процесса ключ и путь к текущему файлу
+
−
CString csParams=def_keyUdate;
+
−
csParams+=" ";
+
−
csParams+=csFullExeName;
+
−
 
+
−
//запускаем копию
+
−
STARTUPINFO si;
+
−
PROCESS_INFORMATION pi;
+
−
ZeroMemory(&si,sizeof(si));
+
−
si.cb = sizeof(si);
+
−
ZeroMemory(&pi,sizeof(pi));
+
−
if(CreateProcess(def_exeNAMEup,def_keyUdate,0,0,0,0,0,0,&si,&pi))
+
−
{
+
−
//завершаем текущий процесс
+
−
ExitFromMyApp();
+
−
}
+
−
}
+
−
 
+
−
void CTESTFAQApp::ExitFromMyApp()
+
−
{
+
−
exit(0);
+
−
}
+
−
 
+
−
BOOL CTESTFAQApp::InitInstance()
+
−
{
+
−
//смотрим, не запущено ли для апдейта?
+
−
CString txt;
+
−
txt=m_lpCmdLine;
+
−
if(txt.Find(def_keyUdate)==0)
+
−
{
+
−
//делаем айдейт
+
−
txt.Replace(def_keyUdate,"");
+
−
txt.TrimLeft();
+
−
 
+
−
CString csFullExeName=txt;
+
−
 
+
−
//берём откуда то уже скачанный файл обновления
+
−
//...
+
−
CString csFull_Downloaded_ExeName=def_exeNAMEdnld;
+
−
 
+
−
//копируем файл (обновляем старый то есть)
+
−
CopyFile(csFull_Downloaded_ExeName,csFullExeName,0);
+
−
 
+
−
//запускаем обновлённого мученника
+
−
STARTUPINFO si;
+
−
PROCESS_INFORMATION pi;
+
−
ZeroMemory(&si,sizeof(si));
+
−
si.cb = sizeof(si);
+
−
ZeroMemory(&pi,sizeof(pi));
+
−
if(CreateProcess(csFullExeName,0,0,0,0,0,0,0,&si,&pi))
+
−
 
+
−
ExitFromMyApp();
+
−
 
+
−
}
+
−
//...
+
−
//...
+
−
}
+
−
</syntaxhighlight>
+
−
 
+
−
=== Как производится конвертация из кодировки UTF8 в 1251 и наоборот?===
+
−
 
+
−
Можно перевести строку из UTF8 в Unicode, затем из Unicode в 1251
+
−
 
+
−
Ниже приведена структура, содержащая процедуры перекодирования,
+
−
а также процедуру с примером использования
+
−
<syntaxhighlight lang="cpp">
+
−
//описание структуры
+
−
struct coder
+
−
{
+
−
//utf8->unicode
+
−
static wchar_t* utf8_to_unicode__dontForgetDeleteArr(const char *utf8_string)
+
−
{
+
−
wchar_t* pRes=0;
+
−
int res_len=0;
+
−
 
+
−
//тест на возможность преобразования
+
−
res_len=MultiByteToWideChar(CP_UTF8,0,utf8_string,-1,0,0);
+
−
if(!res_len)
+
−
{
+
−
return 0;
+
−
}
+
−
 
+
−
//выделяем память
+
−
pRes = new wchar_t[res_len];
+
−
if(!pRes)
+
−
{
+
−
return 0;
+
−
}
+
−
 
+
−
//преобразование
+
−
if(!MultiByteToWideChar(CP_UTF8,0,utf8_string,-1,pRes,res_len))
+
−
{
+
−
delete [] pRes;
+
−
return 0;
+
−
}
+
−
 
+
−
return pRes;
+
−
}
+
−
 
+
−
//unicode->1251
+
−
static char * unicode_to_1251__dontForgetDeleteArr(const wchar_t *unicode_string)
+
−
{
+
−
char* pRes=0;
+
−
int res_len=0;
+
−
 
+
−
//тест на возможность преобразования
+
−
res_len = WideCharToMultiByte(1251,0,unicode_string,-1,0,0,0,0);
+
−
if(!res_len)
+
−
{
+
−
return 0;
+
−
}
+
−
 
+
−
//выделяем память
+
−
pRes=new char[res_len];
+
−
if(!pRes)
+
−
{
+
−
return 0;
+
−
}
+
−
 
+
−
//преобразование
+
−
if(!WideCharToMultiByte(1251,0,unicode_string,-1,pRes,res_len,0,0))
+
−
{
+
−
delete [] pRes;
+
−
return 0;
+
−
}
+
−
 
+
−
return pRes;
+
−
}
+
−
 
+
−
//процедура с примером
+
−
static void Example()
+
−
{
+
−
wchar_t* unicode_string=0;
+
−
char* cp1251_string=0;
+
−
 
+
−
//исходный текст
+
−
char utf8_string[] = "UTF-8 + русский текст";
+
−
 
+
−
for(;;)
+
−
{
+
−
 
+
−
unicode_string=utf8_to_unicode__dontForgetDeleteArr(utf8_string);
+
−
if(!unicode_string)
+
−
{
+
−
AfxMessageBox("Не удалось конвертировать в unicode!");
+
−
break;
+
−
}
+
−
 
+
−
cp1251_string = unicode_to_1251__dontForgetDeleteArr(unicode_string);
+
−
 
+
−
if(!cp1251_string)
+
−
{
+
−
AfxMessageBox("Не удалось конвертировать из unicode!");
+
−
break;
+
−
}
+
−
 
+
−
break;
+
−
}
+
−
 
+
−
//cp1251_string - результат
+
−
AfxMessageBox(cp1251_string);
+
−
 
+
−
//не забываем удалить массивы
+
−
if(unicode_string)
+
−
{
+
−
delete [] unicode_string;
+
−
unicode_string=0;
+
−
}
+
−
 
+
−
if(cp1251_string)
+
−
{
+
−
delete [] cp1251_string;
+
−
cp1251_string=0;
+
−
}
+
−
}
+
−
};
+
−
 
+
−
//вызов примера
+
−
coder::Example();
+
−
</syntaxhighlight>
+
−
 
+
−
===Как работать с базой данных Access из программы на VC.net?===
+
−
Проще всего подключить ADO компоненты и сделать все на них.
+
−
 
+
−
Компоненты  ADO (ADODC,DATAGRID и т.п.) это ActiveX.
+
−
 
+
−
В редакторе ресурсов, во всплывающем меню, есть Insert ActiveX Control -> открывается список всех компонент, зарегистрированных на машине. Нужно найти компоненты (Это компоненты от MS и они помечены (OLEDB)) и вставить их в диалог. Для того что бы иметь возможность управлять ими, нужно импортировать обертки для них, это делается при помощи ClassWizard, там есть импорт->класс->указать файл с компонентой (OSX). Появляется список всех классов, объявленных внутри.
+
−
 
+
−
Расставляем нужные галочки, нажимаем ОК.
+
−
 
+
−
[[Category:FAQ]]
+

Текущая версия на 19:43, 3 октября 2009

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