FAQ:WinAPI, VCPP Part 3

Материал из Весельчак У
Версия от 21:25, 2 ноября 2008; RXL (обсуждение | вклад) (Множественные правки: форматирование кода, перефразировка вопросов, редактирование орфографии. (Остановился на "КОПИРОВАНИЕ В БУФЕР ОБ)

Перейти к: навигация, поиск

Содержание

Как узнать, что пользователь меняет текст в приложении на основе CRichEditView?

Нужно добавить обработчик сообщения EN_CHANGE. Сообщение генерируется CRichEditView каждый раз, когда пользователь изменяет текст в окне CRichEditView.

Чем отличается метод CArray::GetSize от метода CArray::GetCount?

Эти методы абсолютно идентичны. Скорее всего, в более старых версиях библиотек функции, одинаковые по смыслу, имели разные имена. Поэтому в целях совместимости разработчики оставили обе функции.

Как загрузить текстовую строку из ресурса?

Пример:

CString m_Temp;
 
m_Temp.LoadString(ID_MYSTRING);

Как поменять иконку у элемента item в CListCtrl?

Пример:

// заполняем структуру LVITEM
LVITEM lvItem;
 
memset(&lvItem, 0, sizeof(lvItem));
 
lvItem.mask = LVIF_IMAGE; // меняться будет картинка
lvItem.iItem = ...; // индекс элемента списка (Zero-based)
lvItem.iSubItem	= 0;
lvItem.iImage = ...; //индекс иконки (из списка элемента)
 
pList->SetItem(&lvItem);

Имеется класс Class1, содержащий член типа "указатель на Class2". В Class2 также имеется указатель на Class1. Компилятор выдаёт ошибку. Что надо делать?

Перекрёстное определение обходится следующим образом: код обоих классов разносится в пары файлов *.h и *.cpp, а перед каждым классом вписывается предопределение другого класса:

Файл "class1.h":

class Class2; // предопределение Class2
 
class Class1
{
	Class2* m_p2; // компилятор разрешит объявление указателя Class2*
 
	void F1(Class2* p); //компилятор разрешит объявление указателя Class2*
	void F2(Class1* p);
};

Файл "class1.cpp":

#include "class1.h"
#include "class2.h"
 
void Class1::F1(Class2* p)
{
	// ...
}
 
void Class1::F2(Class1* p)
{
	// ...
}

Файл "class2.h":

class Class1; // предопределение Class1
 
Class2
{
	Class1* m_p1; // компилятор разрешит объявление указателя Class1*
 
	void F3(Class1* p); // компилятор разрешит объявление указателя Class1*
};

Файл "class2.cpp":

#include "class2.h"
#include "class1.h"
 
void Class2::F3(Class1* p)
{
	// ...
}

После предопределения класса компилятор позволяет объявлять указатель на класс, так как переменная указателя на любой тип имеет всегда один и тот же размер.

Как переключить раскладку в другом (активном) процессе?

Для этого можно использовать функции:

  • GetKeyboardLayout - определить текущую раскладку
  • LoadKeyboardLayout - загрузить новую раскладку
  • VerLanguageName - получить строку с описанием языка

Подробности - в MSDN.

Как получить хендл элемента управления, зная его идентификатор?

Пример: пусть элемент Edit лежит на окне CMyWnd. Тогда:

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)
	{
		// ...
	}
}

Как из дочернего окна закрыть приложение?

Для этого нужно послать родительскому окну сообщение WM_CLOSE.

Через указатель на родительское окно:

pParent->PostMessage(WM_CLOSE);

При помощи хендла родительского окна:

::PostMessage(pParent->m_hWnd, WM_CLOSE, (WPARAM)0, (LPARAM)0);

Как корректно перевести тип BSTR в CString и наоборот?

  1. Для конвертирования BSTR в CString нужно просто присвоить переменной типа CString переменную типа BSTR. Оператор "=" класса CString сам выполнит всю работу.
  2. Для конвертирования BSTR в CString нужно выполнить код:
// указатель на будущий буфер с BSTR.
// По сути, BSTR определён как typedef WCHAR* BSTR;
// то есть - указатель на 16-битный символ юникод.
BSTR bstrHE = 0;
 
// строка для конвертации
CString Str = "мой текст";
 
// Выделяем память и загружаем адрес блока в bstrHE
bstrHE = Str.AllocSysString();
 
// ...	
// тут работаем с bstrHE[]
// ...
 
// освобождаем память
SysFreeString(bstrHE);

Как получить доступ к графическим ресурсам элементов текущей темы оформления 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():

void CPlayersPropsDialog::OnClose() 
{
	CDialog::OnClose();
	CDialog::OnCancel(); // добавлено
}

Я написал в VC++ простейший макрос, где невозможно ошибиться, а компилятор всё же выдаёт ошибку.

Скорее всего после одного из символов соединения строк "\" имеется пробел или табуляция. Их надо удалить. Можно найти их "на ощупь", а можно включить показ непечатных символов в студии. Найти эту команду можно так:

Tools->Customize...->вкладка Commands. В окошке Category выбрать "Edit", и среди кнопок справа найти кнопку, на которой написано "a.b". Эту кнопку надо перенести на одну из панелей инструментов студии.

Как получить список всех процессов, включая idle?

Использовать функции:

  • Process32First
  • Precess32Next

Подробности - в MSDN.

Как программно установить переменные окружения?

Чтобы добавить или изменить их программно, необходимо воспользоваться ключом реестра

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment

Затем нужно отправить широковещательное сообщение WM_SETTINGCHANGE - это позволит приложениям узнать об изменениях:

::SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);

Как сделать, чтобы у окна был черный фон?

Нужно переопределить обработчик сообщения WM_CTLCOLOR - OnCtlColor():

// глобальная переменная или член класса 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;
}

Почему на моем компьютере экзешник, созданный в MFC запускается, а на других компьютерах - нет? Требует какую-то dll.

Нужно выбирать режим Release: Menu->Build->Configurations...->Release. Экзешник, соответственно, брать из папки Release проекта.

Как в диалог добавить меню?

Нужно создать в редакторе ресурсов новое меню, затем вставить его в диалог:

  1. Открыть в редакторе диалог;
  2. В свойствах (во вкладке General, окошко Menu) указать идентификатор ресурса меню;
  3. При помощи визарда добавить для меню обработчики OnCommand() и OnUpdateСommand().

Почему не вызывается OnUpdate для пунктов меню (не получается ни затенить, ни отметку поставить)?

В диалоге (CDialog) у меню апдейт не происходит сам, в отличие от мейнфрейма (CFrameWnd). Для этого нужно сделать апдейт самим, в обработчике сообщения WM_KICKIDLE.

Визардом этот обработчик не добавляется, поэтому можно переопределить виртуальную DefWindowProc() и там обработать:

#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);
}

Как в главном окне отключить системное меню (в левом верхнем углу) и кнопки (в правом верхнем углу)?

При создании окна нужно убрать стиль WS_SYSMENU. Теперь пользователь корректно может закрыть окно только Alt+F4, ну, или если вы предоставите ему дополнительную возможность сделать это.

Пример 1. (для SDI, MDI)

// этот обработчик уже добавлен визардом
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;
}

Пример 2. (для диалога) Если диалог описан в ресурсах, то в свойствах диалога убрать галочки:

  • "System menu"
  • "Minimize box"
  • "Maximize box"

Как запретить пользователю нажать на кнопку?

Нужно применить метод класса CWnd::EnableWindow(...) для кнопки. Значение 0 параметра делает кнопку неактивной, 1 - делает активной (кстати, не только для кнопки можно, а для любого класса, производного от класса CWnd).

Пусть имеется некий диалог, на нём лежит кнопка c ID == IDC_1. В любом месте кода диалога (кроме конструктора и деструктора, хотя, если проверить наличие валидного хендла диалога, как в примере, то ничего страшного не будет и там) выполняем код:

// делаем кнопку неактивной
if (m_hWnd)
{
	CWnd* pw = 0;
	pw = GetDlgItem(IDC_1);
	if (pw)
	{
		pw->EnableWindow(0);
	}
}

Что означают сообщения, которые студия выводит при компиляции, вроде таких: Loaded 'C:\WINDOWS\SYSTEM\WSOCK32.DLL', no matching symbolic information found?

Это строка говорит о том, что студия не смогла найти отладочные символы для WSOCK32.DLL. В этом нет ничего страшного. Просто, при наличии отладочных символов в процессе отладки, можно получить доступ к именам функции из системных DLL. Например, вместо KERNEL32! 0x77E8B184() увидим KERNEL32!CreateThread.

Как обработать сообщения, которые приходят к элементу управления?

Скажем, нужно обработать сообщения, приходящие к элементу класса CEdit. Нужно произвести от CEdit свой класс и в виртуальной процедуре класса WindowProc перехватить нужные сообщения.

virtual LRESULT WindowProc(
	UINT message,
	WPARAM wParam,
	LPARAM lParam 
);

Пример. Полностью выключаем, скажем, обработку сообщения WM_CHAR.

LRESULT CMyEdit::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	switch (message)
	{
		case WM_CHAR:
		{
			return 0;
		}
		break;
	}
 
	return CEdit::WindowProc(message, wParam, lParam);
}

Чтобы связать экземпляр класса с элементом управления на форме, добавьте визардом для элемента управления переменную класса (выбрав не Value, а Control).

Как настроить количество пробелов в табуляции?

Есть пара способов:

  1. Tools->Options->Tabs->Insert Spaces
  2. Правой кнопкой мыши щёлкнуть по тексту, в контекстном меню выбрать properties.

Как автоматически расставить отступы?

Нужно в студии выделить текст для форматирования и нажать Alt+F8.

Ещё есть программа Artistic Style ( http://sourceforge.net/projects/astyle/ ), которая занимается переформатированием кода.

Поиск границ блока.

Сочетание клавиш CTRL+"}" ищет парную фигурную скобку в тексте и переходит к ней.

Вертикальное выделение текста.

Если удерживать клавишу Alt, а затем начать выделять текст мышкой, то область выделения принимает форму прямоугольника, что позволяет выделять вертикальные фрагменты текста.

Как узнать количество установленных в CListCtrl столбцов?

Пример:

// указатель на переменную-элемент управления
CListCtrl* pL = ...;
 
// получаем количество столбцов
int count = pL->GetHeaderCtrl()->GetItemCount();

Как получить иконку приложения?

Если приложение запущено, то нужно найти его главное окно и послать ему сообщение WM_GETICON.

// функция возвращант хендл иконки 
LRESULT SendMessage(hWnd, WM_GETICON, wParam, 0);
 
// hWnd == хендл окна приложения
// wParam == 
//	1)==ICON_BIG - получить большую иконку
//	2)==ICON_SMALL - получить маленькую иконку
//	3)==ICON_SMALL2 - получить маленькую иконку, если она определена
//				в приложении. Если её нет, то маленькую иконку, 
//				сгенерированную системой из большой иконки

Как в CString можно найти или вырезать часть строки?

Это можно сделать при помощи методов класса CString:

// вырезает кусок строки
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;

Как отобразить на элементах управления промежуточные результаты длительных вычислений?

В общем случае это делается принудительной перерисовкой нужного окна путём объявления этого окна невалидным.

pWnd->Invalidate(0);

Затем непосредственной отсылки в оконную процедуру сообщения WM_PAINT.

pWnd->UpdateWindow();

Пример:

CWnd* pWnd = ...; // окно, которое надо перерисовывать
 
for (int i = 0; i < 10000; i++)
{
	// меняется содержимое окна
	// ...
 
	// немедленная перерисовка
	pWnd->Invalidate(0);
	pWnd->UpdateWindow();
}

Также можно также просто вызвать метод RedrawWindow() с параметрами по умолчанию.

Я написал DLL, которую используют несколько приложений. Всё работает, но когда происходит очередной вызов функции из DLL, почему то, данные в функции обнуляются. С чем это связано?

Переменные и массивы в DLL, содержимое которых должно использоваться несколькими процессами, должны объявляться статическими. Статическая переменная или массив инициализируются только один раз в момент загрузки DLL.

Пример:

DWORD calltest()
{
	// будет выполнено только при первом вызове
	static DWORD callcount = 0;
	static DWORD str[1000] = {0}; 
 
	// будет выполняться каждый раз
	callcount++;
	return callcount;
}

Как открыть проекцию файла в память и как с ней работать?

Создание проекции:

HANDLE CreateFileMapping(
	HANDLE hFile, // хендл уже открытого файла
	LPSECURITY_ATTRIBUTES lpAttributes,
	DWORD flProtect, // способ открытия проекции
	DWORD dwMaximumSizeHigh, // размер файла (старшие 4 байта, обычно - 0)
	DWORD dwMaximumSizeLow, // размер файла (младшие 4 байта)
	LPCTSTR lpName
);

Доступ к созданной проекции производится процедурой:

LPVOID MapViewOfFile(
	HANDLE hFileMappingObject, // хендл проекции
	DWORD dwDesiredAccess, // способ работы с проекцией
	DWORD dwFileOffsetHigh,
	DWORD dwFileOffsetLow, // смещение от начала файла (младшие 4 байта)
	SIZE_T dwNumberOfBytesToMap // длина в байтах (если 0 - то весь файл)
);

Далее с файлом можно работать как с обычным массивом в памяти.

После работы с проекцией, её надо освободить.

BOOL UnmapViewOfFile(
	LPCVOID lpBaseAddress // адрес, который вернула процедура MapViewOfFile
);

Пример:

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);

Как в MFC Grid control отобразить картинку в ячейке?

Нужно создать список изображений (CImageList), затем передать указатель на список в таблицу. Поскольку элемент управления лишь копирует указатель, список должен создаваться динамически, либо быть членом класса, экземпляр которого существует всё время работы таблицы.

CImageList m_ImageList;
CGridCtrL m_Grid1;
 
// ...
 
// неким образом создаётся список
m_ImageList.Create(...);
 
// ...
 
//вставляем в элемент управления
m_Grid1.SetImageList(&m_ImageList);

Как при помощи IPicture отобразить картинку из файла?

Пример - несложная функция, отображающая картинку из файла. Поддерживаются форматы BMP, GIF, JPEG, PNG, TIFF, EMF.

#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;
}

Как позволить пользователю начать ввод новой строки в многострочном поле редактирования?

Нужно в редакторе ресурсов поставить для свойств окошка 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)?

КОПИРОВАНИЕ В БУФЕР ОБМЕНА

Алгоритм:

  1. Готовим данные
    • Выделяем память из кучи, вызывая GlobalAlloc()
    • Получаем указатель на выделенную память, вызывая GlobalLock()
    • Заполняем данные
    • Освобождаем указатель, вызывая GlobalUnlock();
  2. Открываем буфер обмена, вызывая OpenClipboard();
  3. Очищаем буфер, вызывая EmptyClipboard();
  4. Вызываем SetClipboardData() один раз для каждого формата вставляемых данных (имеется в виду - если одни и те же данные представлены в разных форматах, и приложение может эти форматы создать)
  5. Закрываем буфер, вызывая CloseClipboard();
  6. ВЫЗЫВАТЬ GlobalFree() НЕ НУЖНО, в этом случае это предоставлено системе

Скажем, хотим поместить в буфер обмена текст.

//готовим данные
//выделяем из кучи память для данных
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();


ИЗВЛЕЧЕНИЕ ИЗ БУФЕРА ОБМЕНА

Алгоритм:

  1. Проверяем, что поддерживается нужный формат данных, вызывая IsClipboardFormatAvailable()
  2. Открываем буфер обмена, вызывая OpenClipboard();
  3. Достаём данные
    • Получаем из буфера хендл требуемого формата, вызывая GetClipboardData()
    • Получаем указатель на выделенную память, вызывая GlobalLock()
    • Работаем с данными
    • Освобождаем указатель, вызывая GlobalUnlock();
  4. Закрываем буфер, вызывая CloseClipboard();

Скажем, хотим извлечь из буфера обмена текст.

//Проверяем, что поддерживается нужный формат данных,
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();


Как получить полный путь к экзешнику из самой программы?

Можно прочитать параметры командной строки - в командную строку первым параметром система всегда передаёт заключённый в кавычки полный путь к файлу запущенной программы. Достаём путь следующим образом:

BOOL CMyApp::InitInstance()
{
	//добыча полного имени экзешника
	CString csFullExeName;
	{
		CString csAppName=GetCommandLine();
		csAppName.Delete(0,1);
		csAppName.Replace('\"','\0');
		csFullExeName=(const char*)csAppName;
	}
	//теперь csFullExeName содержит искомый путь
 
	//...
	//...
}

Каким способом exe файл может заменить самого себя?

К примеру, программа должна обновляется через Internet и хочет обновить свой exe-файл. Но так как он в данный момент запущен, это будет, естественно, невозможно сделать напрямую. Один из способов - сделать копию экзешника и из него обновиться

#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();
 
	}
	//...
	//...
}

Как производится конвертация из кодировки UTF8 в 1251 и наоборот?

Можно перевести строку из UTF8 в Unicode, затем из Unicode в 1251

Ниже приведена структура, содержащая процедуры перекодирования, а также процедуру с примером использования

//описание структуры
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();

Как работать с базой данных Access из программы на VC.net?

Проще всего подключить ADO компоненты и сделать все на них.

Компоненты ADO (ADODC,DATAGRID и т.п.) это ActiveX.

В редакторе ресурсов, во всплывающем меню, есть Insert ActiveX Control -> открывается список всех компонент, зарегистрированных на машине. Нужно найти компоненты (Это компоненты от MS и они помечены (OLEDB)) и вставить их в диалог. Для того что бы иметь возможность управлять ими, нужно импортировать обертки для них, это делается при помощи ClassWizard, там есть импорт->класс->указать файл с компонентой (OSX). Появляется список всех классов, объявленных внутри.

Расставляем нужные галочки, нажимаем ОК.