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

Материал из Весельчак У
Перейти к: навигация, поиск
м
(Перенаправление на FAQ:WinAPI VCPP)
 
(не показано 5 промежуточных версий этого же участника)
Строка 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
+
 
+
class 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>
+
 
+
После предопределения класса компилятор позволяет объявлять указатель на класс, так как переменная указателя на любой тип имеет всегда один и тот же размер (4 байта).
+
 
+
===Как переключить раскладку клавиатуры в другом (активном) процессе?===
+
 
+
Для этого можно использовать функции:
+
* 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>
+
 
+
Примечание: WM_CLOSE аналогично нажатию на крестик окна. То есть, это "мягкое" закрытие окна, так как это сообщение приложение может обработать по своему.
+
Если же точно так же отправить сообщение WM_QUIT (при этом в wParam указывается значение, возвращаемое процессом после жавершения), то оконная процедура, получив это сообщение, просто прекращает работу и приложение закрывается сразу.
+
 
+
===Как корректно перевести тип BSTR в CString и наоборот?===
+
 
+
# Для конвертирования BSTR в CString нужно просто присвоить переменной типа CString переменную типа BSTR. Оператор "=" класса CString сам выполнит всю работу.
+
# Для конвертирования BSTR в CString нужно выполнить код:
+
 
+
<syntaxhighlight lang="cpp">
+
// указатель на будущий буфер с BSTR.
+
// По сути, BSTR определён как typedef WCHAR* BSTR;
+
// то есть - указатель на 16-битный символ юникод.
+
BSTR bstrHE = 0;
+
 
+
// строка для конвертации
+
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.
+
 
+
===Как программно установить переменные окружения?===
+
 
+
Чтобы добавить или изменить их программно, необходимо воспользоваться ключом реестра
+
 
+
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment
+
 
+
Затем нужно отправить широковещательное сообщение 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">
+
// этот файл надо включить, там определёна WM_KICKIDLE
+
#include <afxpriv.h>
+
 
+
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. Например, вместо ''KERNEL32! 0x77E8B184()'' увидим ''KERNEL32!CreateThread''.
+
 
+
===Как обработать сообщения, которые приходят к элементу управления?===
+
 
+
Скажем, нужно обработать сообщения, приходящие к элементу класса 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

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