MS Visual C++ / MFC FAQ
Paul Kalyakin <Paul.Kalyakin@p20.f32.n5029.z2.fidonet.org>
http://www.rusdev.newmail.ru
- Q1. Как показать ProgressBar на StatusBar'е ?
- Q2. Как использовать CTreeCtrl для построения дерева каталогов диска, как в
Проводнике ?
- Q3. Есть класс - потомок CListView. Как изменить стиль у объекта CListCtrl,
принадлежащего к этому *view (например установить стиль Report)?
- Q4. Как CString привести к char *?
- Q5. Какие библиотеки (Freeware/Commercial) существуют для Visual C++ ?
- Q6. А можно пример консольной программы ?
- Q7. В созданном мастером (VC 6.0) win32 Console Application попытка вывести
русский текст дает кракозяблики.Что делать ?
- Q8. Пытаюсь из своей программы вызвать Word97, для это делаю несколько
импортов и в результате имею кучу ошибок. Как правильно ?
- Q9. А как отредактировать ресурсы .exe файла ?
- Q10. Как программно получить номер билда своего приложения в VC++?
- Q11. Какой функцией можно переключить видеорежим ?
- Q12. Как вызвать окно выбора папки ?
A1.
Предположим, что вы хотите показать CProgressCtrl на весь StatusBar.
Для этого необходимо проделать следующее:
- Выберите пункт меню View - Resource Symbols. Нажмите кнопку New и
добавьте новое имя, в нашем примере это будет ID_PROGRBAR.
- В файле MainFrm.cpp найдите объявление массива indicators (он
находиться сразу после END_MESSAGE_MAP) и отредактируйте его к
следующиему виду
static UINT indicators[] =
{
ID_PROGRBAR
};
- В файле _MainFrm.h создайте protected переменную m_bCreated типа
BOOL и public переменную m_progress типа CProgressCtl.
- В файле MainFrm.cpp отредактируйте конец функции
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) таким образом:
к участку кода:
if (!m_wndStatusBar.Create(this ) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof (UINT)))
{
TRACE0("Failed to create status bar\n" );
return -1; // fail to create
}
добавьте следующую строку:
else {
m_wndStatusBar.SetPaneInfo(0,ID_PROGRBAR,SBPS_STRETCH,10);
}
Кроме того, добавьте инициализацию нашей переменной m_bCreated
.........
m_bCreated=FALSE;
..........
- Теперь мы можем использовать ProgressBar в строке статуса, естественно не
забыв создать этот объект. Предположим, у нас есть функция
CMainFrame::OnWork(). Она будет выглядеть примерно так:
void CMainFrame::OnWork()
{
RECT rc;
m_wndStatusBar.GetItemRect(0,&rc);
if (m_bCreated==FALSE)
{
// создаем m_progress
m_progress.Create(WS_VISIBLE|WS_CHILD, rc,&m_wndStatusBar, 1);
// Устанавливаем размер от 0 до 100
m_progress.SetRange(0,100);
m_progress.SetStep(1);
m_bCreated=TRUE;
}
for (int I = 0; I < 100; I++)
{
Sleep(20);
m_progress.StepIt();
}
}
-Если откомпилировать проект на этой фазе, то все будет работать, но при
изменении размера окна линейка ProgressBar'а размеры менять не будет, поэтому
необходимо перекрыть событие OnSize:
void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
CFrameWnd::OnSize(nType, cx, cy);
if (m_bCreated)
{
RECT rc;
m_wndStatusBar.GetItemRect(0,&rc);
m_progress.SetWindowPos(&wndTop, rc.left, rc.top,
rc.right - rc.left,rc.bottom - rc.top, 0);
}
}
- Вот теперь все /-))))) Откомпилируйте проект и убедитесь, что все
работает.
A2. (A. Лисеев Дмитрий. dimik@infopro.spb.su)
Это тормозно и глючно. На больших дисках это займет несколько минут. Если
каталоги добавляются или удалются другими приложениями во время работы твоего
контрола, то будешь весь в проблемах. Все гораздо проще. Никаких рекурсий.
Просматриваем корневой каталог на предмет наличия подкаталогов и создаем итемы
первого уровня, в которых создаем по одному фиктивному итему (чтобы крестик
был и итем можно было раскрыть).
+ Каталог 1
+ Каталог 2
+ Каталог 3
Как только юзер пытается раскрыть итем, соответствующий некому каталогу, мы
удаляем из него фиктивный итем, просматриваем этот подкаталог и добавляем
соответствующие итемы со своими фиктивными внутри.
-Каталог 1
+ Каталог 4
+ Каталог 5
+ Каталог 6
+ Каталог 2
+ Каталог 3
Как только юзер закрывает итем, мы удаляем из него все дочерние итемы и
обратно добавляем фиктивный. Если структура каталогов изменилась, для
обновления юзеру достаточно просто закрыть и открыть соответствующую ветку.
Именно так и работает "Проводник".
A3.
Для этого пишите в OnInitialUpdate вашего вида
void CMyListView::OnInitialUpdate()
{
......
CListView::OnInitialUpdate();
CListCtrl& theCtrl = GetListCtrl();
DWORD dwStyle=GetWindowLong(theCtrl.m_hWnd,GWL_STYLE);
SetWindowLong(theCtrl.m_hWnd,GWL_STYLE,dwStyle|LVS_REPORT);
....
A3. (by Pavel Nazin 2:5020/1053.21)
Гораздо проще перекрыть PreCreateWindow (лучше всего воспользоваться
ClassWizard-ом) и поковырять переданный по ссылке CREATESTRUCT типа такого:
BOOL CMyListView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style|=LVS_REPORT;//так мы добавляем стиль
cs.style&=LVS_REPORT;//а вот так снимаем
return CMyListView::PreCreateWindow(cs);
}
A4. (by Yuri Khodin 2:5020/1200.20)
#include <atlbase.h>
USES_CONVERSION;
CString strData(_T("Some Data"));
char* lpszString = T2A((LPTSTR)(LPCTSTR)strData);
A4. (by Paul Kalyakin 2:5029/3.29 hjobyf@mail.ru)
CString tmp_str;
char* st;
st=tmp_str.GetBuffer(tmp_str.GetLength())
важно то, что если с tmp_str что-либо сделать, то необходимо опять получить
указатель на внутренний буфер CString.
A5.
1- BCG Control Library (freeware)
http://msnhomepages.talkcity.com/WindowsWay/stasl/index.html
2- CJLibrary (freeware)
http://www.codejock.com
Stringray Software (commercial) www.stingray.com
Фирма Stringray Software производит библиотеки для Visual C++ (MFC, ATL):
1. Stingray Objective Toolkit (PRO) - набор различных компонентов для
MFC и ATL
2. Stingray Objective Grid (PRO) - мощная сетка данных с возможностями,
близкими к Excel. Дружит с базами данных (через DAO,ADO,ODBC). Можно
использовать для ввода данных в таблицы БД и для вывода/печати простых
отчётов.
3. Stingray Objective Chart - средство для построения диаграмм
4. Stingray Objective Views - средство для создания Visio-подобных
интерфейсов (при помощи векторной графики)
5. Stingray Objective Edit - текстовый редактор с подсветкой синтаксиса
кроме этих, есть и другие продукты
- Dundas Software (commercial) http://www.dundas.com
Фирма Dundas Software производит библиотеки для Visual C++ (MFC):
1. Dundas Ultimate Toolbox - набор компонентов для MFC, по составу
несколько отличающийся от Stingray Objective Toolkit.
2. Dundas Ultimate Grid - сетка данных, конкурент Stingray Objective Grid.
3. Dundas TCP/IP - реализация протоколов POP3,NEWS и т.п.
4. Dundas Chart - диаграммы
и другие продукты
A6. by Alexander Fedorov (2:5030/437.74)
#include <windows.h>
#include <stdlib.h>
void main()
{
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
SMALL_RECT srct;
CHAR_INFO chiBuffer[160];
COORD coord1, coord2;
char ddd[666];
CharToOem("2:5095/38 - злобный ламерюга", ddd);
DWORD cWritten;
coord1.Y = 0; coord1.X = 0;
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleOutputCharacter(hStdout, ddd, lstrlen(ddd), coord1, cWritten);
for (int i = 0; i {
WORD wColors = 1 + i * 3;
coord1.X = i;
WriteConsoleOutputAttribute(hStdout, , 1, coord1, cWritten);
}
srct.Top = 0; srct.Left = 0; srct.Bottom = 1; srct.Right = 79;
coord1.Y = 0; coord1.X = 0;
coord2.Y = 1; coord2.X = 80;
ReadConsoleOutput(hStdout, chiBuffer, coord2, coord1, );
for (i = 0; i {
srct.Left = (SHORT)((double)(79 - lstrlen(ddd)) * rand() / RAND_MAX);
srct.Top = (SHORT)((double)25 * rand() / RAND_MAX);
srct.Bottom = srct.Top + 1;
WriteConsoleOutput(hStdout, chiBuffer, coord2, coord1, );
}
Sleep(10000);
A7. by Dmitriy Reznitskiy (2:5020/1452.112)
CharToOem, OemToChar - оно?
A8. by Igor Tkachoff (2:5037/9.37)
// Office.h
#define Uses_MSO2000_
#ifdef Uses_MSO2000
// for Office 2000
#import <mso9.dll>
#import <vbe6ext.olb>
#import <msword9.olb> rename("ExitWindows","_ExitWindows")
#import <excel9.olb> rename("DialogBox","_DialogBox") \
rename("RGB","_RGB") \
exclude("IFont","IPicture")
#import <dao360.dll> rename("EOF","EndOfFile") rename("BOF","BegOfFile")
#import <msacc9.olb>
#else
// for Office 97
#import <mso97.dll>
#import <vbeext1.olb>
#import <msword8.olb> rename("ExitWindows","_ExitWindows")
#import <excel8.olb> rename("DialogBox","_DialogBox") \
rename("RGB","_RGB") \
exclude("IFont","IPicture")
#import <DAO350.DLL> \
rename("EOF","EndOfFile") rename("BOF","BegOfFile")
#import <msacc8.olb>
#endif
<>pКаталоги проставь сам, если надо. Просто я предпочитаю сваливать все
библиотеки в одну кучу. А еще лучше сделать #import один раз, а затем
подключать #include "тыры-пыры.tlh".
P.S. С 2000'ным аккуратнее. Некоторые методы (типа Run, Open, Add) имеют новую
версию. И если хочешь совместимость с 97 то следует вызывать старые версии,
которые называются типа RunOld и т.п.
A9.
Это возможно лишь под NT.
A10. by Pavel Zolotuhin (2:5025/60.15)
Штатной возможности нет, поскольку не все одинаково трактуют понятие "номер
билда" и не все одинаково его используют. Однако большинство людей используют
для хранения номера билда конкретного файла ресурсы типа VERSIONINFO, откуда
эту информацию можно потом получить (для отображения в диалоге "О программе"
:-) с помощью функций из version.dll.
Упрощенно говоря, информация о версии файла хранится в VERSIONINFO в виде
четырех чисел, значимость которых убывает слева направо. Например, для
mfc42.dll из поставки Win2k версия файла выглядит как 6.0.8665.0. Здесь первая
цифра, как я понимаю, совпадает с версией продукта (MSVC 6), вторая означает
подверсию (MSVC 6.0), третья - номер билда, а четвертая - я не знаю. В своих
dll-ках и exe-шниках Microsoft постоянно использует эту схему, я - тоже.
Обычно для автоматического увеличения номера версии используются макросы
Visual Studio (== скрипты на VBScript), ковыряющие файл ресурсов проекта. Эти
макросы либо связываются с кнопкой на тулбаре MSDev, либо вызываются из
обработчика события Application_BeforeBuildStart в файле макросов. Примеры
подобных макросов горой лежат на девелоперских сайтах, наподобие
www.codeguru.com. Для себя я сделал собственный, который реализует номер билда
в указанном выше смысле. Вот его исходник (должен работать на MSVC6SP3).
Sub IncVersion()
'DESCRIPTION: Increments file version
Dim oDoc
Dim iVer
Set oDoc = Documents.Open(Application.ActiveProject &".rc", "Text")
if oDoc Is Nothing Then
Exit Sub
End If
oDoc.Selection.FindText "FILEVERSION", dsMatchCase
if Len(oDoc.Selection) = 0 Then
oDoc.Close dsSaveChangesNo
Set oDoc = Nothing
Exit Sub
End If
oDoc.Selection.EndOfLine
oDoc.Selection.FindText ",", dsMatchBackward
oDoc.Selection.CharLeft
oDoc.Selection.WordLeft dsExtend
iVer = oDoc.Selection
iVer = iVer + 1
oDoc.Selection = iVer
oDoc.Selection.FindText """FileVersion""", dsMatchCase
if Len(oDoc.Selection) = 0 Then
oDoc.Close dsSaveChangesNo
Set oDoc = Nothing
Exit Sub
End If
oDoc.Selection.EndOfLine
oDoc.Selection.FindText ",", dsMatchBackward
oDoc.Selection.CharLeft
oDoc.Selection.WordLeft dsExtend
iVer = oDoc.Selection
iVer = iVer + 1
oDoc.Selection = iVer
oDoc.Close dsSaveChangesYes
Set oDoc = Nothing
End Sub
A11. by Alexander Shargin (2:5030/852.22)
Этим занимается ChangeDisplaySettings(...);
Вот тебе пример, который устанавливает разрешение 640x480 (24 bit):
=== Cut ===
DEVMODE md;
ZeroMemory(&md, sizeof(md));
md.dmSize = sizeof(md);
md.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
md.dmBitsPerPel = 24;
md.dmPelsWidth = 640;
md.dmPelsHeight = 480;
ChangeDisplaySettings(&md, 0);
=== Cut ===
Только не повторяй ошибку, которую допустил я, когда писал этот пример:
восстанови исходное разрешение, когда твоя программа будет заканчивать
выполнение.
A12.
Воспользуйтесь следующей функцией:
BOOL FGetDirectory(LPTSTR szDir)
{ BOOL fRet;
TCHAR szPath[MAX_PATH];
LPITEMIDLIST pidl;
LPITEMIDLIST pidlRoot;
LPMALLOC lpMalloc;
BROWSEINFO bi =
{
NULL,
NULL,
szPath,
"Выберите папку",
BIF_RETURNONLYFSDIRS,
NULL,
0L,
0
};
if (0 != SHGetSpecialFolderLocation(HWND_DESKTOP, CSIDL_DRIVES, &pidlRoot))
return FALSE;
if (NULL == pidlRoot)
return FALSE;
bi.pidlRoot = pidlRoot;
pidl = SHBrowseForFolder(&bi);
if (NULL != pidl)
fRet = SHGetPathFromIDList(pidl, szDir);
else
fRet = FALSE; // Get the shell's allocator to free PIDLs
if (!SHGetMalloc(&lpMalloc) && (NULL != lpMalloc))
{
if (NULL != pidlRoot)
{
lpMalloc->Free(pidlRoot);
}
if (NULL != pidl)
{
lpMalloc->Free(pidl);
}
lpMalloc->Release();
}
return fRet;
}
LPTSTR PszAlloc(int cch)
{
return (LPTSTR) LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (cch+1));
}
bool PszDeAlloc(HLOCAL mem_ptr)
{
return (LocalFree(mem_ptr)==NULL) ? true : false;
}
Затем, при необходимости предложить пользователю выбрать папку
используйте примерно такой код:
....
LPTSTR fname;
fname=PszAlloc(250);
FGetDirectory(fname);
......
PszDeAlloc((HLOCAL)fname);
HomePage для этого FAQ - http://www.rusdev.newmail.ru
|