Does CDockablePane::Serialize() method is calling from MFC Feature Pack core?
I have dockable window class that inherited from CDockablePane class. My class override virtual Serialize() method and declared as serial DECLARE_SERIAL/IMPLEMENT_SERIAL. But MFC does not call my Serialize() method! Why ?
MSDN say that CDockablePane class have serialization methods: SaveState(), LoadState() and Serialize(). First two (SaveState(), LoadState()) are used internally and Serialize() used for "serializes the pane". But it is not calling!
No, the Serialize() method is not called by MFC framework automatically. You have to call it manually usually from CDocument::Serialize(). LoadState() and SaveState() methods are used to save/restore pane position and state. They do use Registry as storage.
As for DECLARE_SERIAL and IMPLEMENT_SERIAL they are used to support operator>> and other CArchive-specific things plus runtime class information. So you'll be able to use BOOL IsKindOf(RUNTIME_CLASS(...)) automagically.
My answer proposition. My class CSerializableDockablePane performs call for Serialize method when this docked pane is created or stored by framework (framework calls methods LoadState() and SaveState()).
You can use CSerializableDockablePane as base class for your dockable pane and overwrite virtual Serialize() method for your need.
Code:
class CSerializableDockablePane
: public CDockablePane
{
DECLARE_SERIAL(CSerializableDockablePane)
public:
typedef CDockablePane TBase;
public:
CSerializableDockablePane();
virtual ~CSerializableDockablePane();
virtual BOOL LoadState(LPCTSTR lpszProfileName = NULL, int nIndex = -1, UINT uiID = (UINT)-1);
virtual BOOL SaveState(LPCTSTR lpszProfileName = NULL, int nIndex = -1, UINT uiID = (UINT)-1);
virtual void Serialize(CArchive& ar);
};
/////////////////////////////////////////////////////////////////////////////
// CSerializableDockablePane
#define _MFC_DOCVIEW_PROFILE _T("DockableViews")
#define _REG_UI_DOCVIEWSECTION_FMT _T("%TsSerializableDockablePane-%d")
#define _REG_UI_DOCVIEWSECTION_FMT_EX _T("%TsSerializableDockablePane-%d%x")
#define _REG_UI_SETTINGS _T("Settings")
IMPLEMENT_SERIAL(CSerializableDockablePane, CSerializableDockablePane::TBase, VERSIONABLE_SCHEMA | 2)
CSerializableDockablePane::CSerializableDockablePane()
{
}
CSerializableDockablePane::~CSerializableDockablePane()
{
}
BOOL CSerializableDockablePane::LoadState(LPCTSTR lpszProfileName /*= NULL*/, int nIndex /*= -1*/, UINT uiID /*= (UINT)-1*/)
{
BOOL bRes = TBase::LoadState(lpszProfileName, nIndex, uiID);
if (bRes) {
const CString strProfileName = ::AFXGetRegPath(_MFC_DOCVIEW_PROFILE, lpszProfileName);
if (nIndex == -1) {
nIndex = GetDlgCtrlID();
}
CString strSection;
if (uiID == (UINT)-1) {
strSection.Format(_REG_UI_DOCVIEWSECTION_FMT, (LPCTSTR)strProfileName, nIndex);
}
else {
strSection.Format(_REG_UI_DOCVIEWSECTION_FMT_EX, (LPCTSTR)strProfileName, nIndex, uiID);
}
LPBYTE lpbData = nullptr;
UINT uiDataSize = 0;
CSettingsStoreSP regSP;
CSettingsStore& reg = regSP.Create(FALSE, TRUE);
if (!reg.Open(strSection)) {
return FALSE;
}
if (!reg.Read(_REG_UI_SETTINGS, &lpbData, &uiDataSize)) {
return FALSE;
}
try
{
CMemFile file(lpbData, uiDataSize);
CArchive ar(&file, CArchive::load);
Serialize(ar);
bRes = TRUE;
}
catch (CMemoryException* pEx)
{
pEx->Delete();
TRACE(_T("Memory exception in CSerializableDockablePane::LoadState()!\n"));
}
catch (CArchiveException* pEx)
{
pEx->Delete();
TRACE(_T("CArchiveException exception in CSerializableDockablePane::LoadState()!\n"));
}
if (lpbData != nullptr) {
delete[] lpbData;
}
}
return bRes;
}
BOOL CSerializableDockablePane::SaveState(LPCTSTR lpszProfileName /*= NULL*/, int nIndex /*= -1*/, UINT uiID /*= (UINT)-1*/)
{
BOOL bRes = TBase::SaveState(lpszProfileName, nIndex, uiID);
if (bRes) {
const CString strProfileName = ::AFXGetRegPath(_MFC_DOCVIEW_PROFILE, lpszProfileName);
if (nIndex == -1) {
nIndex = GetDlgCtrlID();
}
CString strSection;
if (uiID == (UINT)-1) {
strSection.Format(_REG_UI_DOCVIEWSECTION_FMT, (LPCTSTR)strProfileName, nIndex);
}
else {
strSection.Format(_REG_UI_DOCVIEWSECTION_FMT_EX, (LPCTSTR)strProfileName, nIndex, uiID);
}
try
{
CMemFile file;
{
CArchive ar(&file, CArchive::store);
Serialize(ar);
ar.Flush();
}
UINT uiDataSize = (UINT)file.GetLength();
LPBYTE lpbData = file.Detach();
if (lpbData != NULL)
{
CSettingsStoreSP regSP;
CSettingsStore& reg = regSP.Create(FALSE, FALSE);
if (reg.CreateKey(strSection)) {
bRes = reg.Write(_REG_UI_SETTINGS, lpbData, uiDataSize);
}
free(lpbData);
}
}
catch (CMemoryException* pEx)
{
pEx->Delete();
TRACE(_T("Memory exception in CSerializableDockablePane::SaveState()!\n"));
}
}
return bRes;
}
void CSerializableDockablePane::Serialize(CArchive& ar)
{
TBase::Serialize(ar);
}
// CSerializableDockablePane
/////////////////////////////////////////////////////////////////////////////
Related
I've building a windows app for browsing web pages using cefSharp.
I need to implement some short cut keys into that application, can any one tell me how can i achieve this functionality.
Ex.
ctrl + tab = move to next tab
I'm able to track if user presses any single key, but unable to track multi key pressing.
IKeyboardHandler
public class KeyboardHandler : IKeyboardHandler
{
public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey)
{
bool result = false;
Debug.WriteLine(String.Format("OnKeyEvent: KeyType: {0} 0x{1:X} Modifiers: {2}", type, windowsKeyCode, modifiers));
// TODO: Handle MessageNeeded cases here somehow.
return result;
}
public bool OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut)
{
const int WM_SYSKEYDOWN = 0x104;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_SYSKEYUP = 0x105;
const int WM_CHAR = 0x102;
const int WM_SYSCHAR = 0x106;
const int VK_TAB = 0x9;
bool result = false;
isKeyboardShortcut = false;
// Don't deal with TABs by default:
// TODO: Are there any additional ones we need to be careful of?
// i.e. Escape, Return, etc...?
if (windowsKeyCode == VK_TAB)
{
return result;
}
Control control = browserControl as Control;
int msgType = 0;
switch (type)
{
case KeyType.RawKeyDown:
if (isSystemKey)
{
msgType = WM_SYSKEYDOWN;
}
else
{
msgType = WM_KEYDOWN;
}
break;
case KeyType.KeyUp:
if (isSystemKey)
{
msgType = WM_SYSKEYUP;
}
else
{
msgType = WM_KEYUP;
}
break;
case KeyType.Char:
if (isSystemKey)
{
msgType = WM_SYSCHAR;
}
else
{
msgType = WM_CHAR;
}
break;
default:
Trace.Assert(false);
break;
}
// We have to adapt from CEF's UI thread message loop to our fronting WinForm control here.
// So, we have to make some calls that Application.Run usually ends up handling for us:
PreProcessControlState state = PreProcessControlState.MessageNotNeeded;
// We can't use BeginInvoke here, because we need the results for the return value
// and isKeyboardShortcut. In theory this shouldn't deadlock, because
// atm this is the only synchronous operation between the two threads.
control.Invoke(new Action(() =>
{
Message msg = new Message() { HWnd = control.Handle, Msg = msgType, WParam = new IntPtr(windowsKeyCode), LParam = new IntPtr(nativeKeyCode) };
// First comes Application.AddMessageFilter related processing:
// 99.9% of the time in WinForms this doesn't do anything interesting.
bool processed = Application.FilterMessage(ref msg);
if (processed)
{
state = PreProcessControlState.MessageProcessed;
}
else
{
// Next we see if our control (or one of its parents)
// wants first crack at the message via several possible Control methods.
// This includes things like Mnemonics/Accelerators/Menu Shortcuts/etc...
state = control.PreProcessControlMessage(ref msg);
}
}));
if (state == PreProcessControlState.MessageNeeded)
{
// TODO: Determine how to track MessageNeeded for OnKeyEvent.
isKeyboardShortcut = true;
}
else if (state == PreProcessControlState.MessageProcessed)
{
// Most of the interesting cases get processed by PreProcessControlMessage.
result = true;
}
Debug.WriteLine(String.Format("OnPreKeyEvent: KeyType: {0} 0x{1:X} Modifiers: {2}", type, windowsKeyCode, modifiers));
Debug.WriteLine(String.Format("OnPreKeyEvent PreProcessControlState: {0}", state));
return result;
}
Finally got an alternative to implement shortcut functionality without implementing IKeyboardHandler.
Here i used Global Keyboard Hook to implement this.
GlobalKeyboardHook
public class GlobalKeyboardHook
{
#region Variables and dll import
#region For key capturing
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int code, int wParam, ref keyBoardHookStruct lParam);
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, LLKeyboardHook callback, IntPtr hInstance, uint theardID);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
public delegate int LLKeyboardHook(int Code, int wParam, ref keyBoardHookStruct lParam);
public struct keyBoardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x0100;
const int WM_KEYUP = 0x0101;
const int WM_SYSKEYDOWN = 0x0104;
const int WM_SYSKEYUP = 0x0105;
LLKeyboardHook llkh;
public List<Keys> HookedKeys = new List<Keys>();
IntPtr Hook = IntPtr.Zero;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
#endregion
#region For modifier capturing
/// <summary>
/// Gets the state of modifier keys for a given keycode.
/// </summary>
/// <param name="keyCode">The keyCode</param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public static extern short GetKeyState(int keyCode);
//Modifier key vkCode constants
private const int VK_SHIFT = 0x10;
private const int VK_CONTROL = 0x11;
private const int VK_MENU = 0x12;
private const int VK_CAPITAL = 0x14;
#endregion
#endregion
#region Constructor
// This is the Constructor. This is the code that runs every time you create a new GlobalKeyboardHook object
public GlobalKeyboardHook()
{
llkh = new LLKeyboardHook(HookProc);
// This starts the hook. You can leave this as comment and you have to start it manually
// Or delete the comment mark and your hook will start automatically when your program starts (because a new GlobalKeyboardHook object is created)
// That's why there are duplicates, because you start it twice! I'm sorry, I haven't noticed this...
// hook(); <-- Choose!
}
~GlobalKeyboardHook()
{ unhook(); }
#endregion
#region Functions and implementation
/// <summary>
/// Hook (Start listening keybord events)
/// </summary>
public void hook()
{
IntPtr hInstance = LoadLibrary("User32");
Hook = SetWindowsHookEx(WH_KEYBOARD_LL, llkh, hInstance, 0);
}
/// <summary>
/// Unhook (Stop listening keybord events)
/// </summary>
public void unhook()
{
UnhookWindowsHookEx(Hook);
}
/// <summary>
/// Pass key into event
/// </summary>
/// <param name="Code">Key code</param>
/// <param name="wParam">int event type (keydown/keyup)</param>
/// <param name="lParam">keyBoardHookStruct enum for detecting key</param>
/// <returns>next hook call</returns>
public int HookProc(int Code, int wParam, ref keyBoardHookStruct lParam)
{
if (Code >= 0)
{
Keys key = (Keys)lParam.vkCode;
if (HookedKeys.Contains(key))
{
//Get modifiers
key = AddModifiers(key);
KeyEventArgs kArg = new KeyEventArgs(key);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
{
KeyDown(this, kArg);
}
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
{
KeyUp(this, kArg);
}
if (kArg.Handled)
return 1;
}
}
return CallNextHookEx(Hook, Code, wParam, ref lParam);
}
/// <summary>
/// Checks whether Alt, Shift, Control or CapsLock
/// is pressed at the same time as the hooked key.
/// Modifies the keyCode to include the pressed keys.
/// </summary>
private Keys AddModifiers(Keys key)
{
//CapsLock
if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) key = key | Keys.CapsLock;
//Shift
if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) key = key | Keys.Shift;
//Ctrl
if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) key = key | Keys.Control;
//Alt
if ((GetKeyState(VK_MENU) & 0x8000) != 0) key = key | Keys.Alt;
return key;
}
#endregion
}
Start hook
To start hook add below code to form load or application start
//Start listening keybord events
gHook = new GlobalKeyboardHook();
gHook.KeyDown += new KeyEventHandler(gHook_KeyDown);
foreach (Keys key in Enum.GetValues(typeof(Keys)))
{
gHook.HookedKeys.Add(key);
}
gHook.hook();
Stop hook
Add this code on application exit(form closing event)
//Stop listening keybord events
gHook.unhook();
Handel key down event for shortcut implementation
public void gHook_KeyDown(object sender, KeyEventArgs e)
{
if (this.ContainsFocus)
{
if (e.KeyData.ToString().ToUpper().IndexOf("ALT".ToUpper()) >= 0
&& e.KeyValue == 66)//B = 66
{
//ALT + B
new Thread(() =>
{
// execute this on the gui thread. (winforms)
this.Invoke(new Action(delegate
{
tosBrowserBtnBack_Click(this, new EventArgs());
}));
}).Start();
}
else if (e.KeyData.ToString().ToUpper().IndexOf("ALT".ToUpper()) >= 0
&& e.KeyValue == 70)//F = 70
{
//ALT + F
new Thread(() =>
{
// execute this on the gui thread. (winforms)
this.Invoke(new Action(delegate
{
tosBrowserBtnFor_Click(this, new EventArgs());
}));
}).Start();
}
}
Global hook works for every keystroke whether your application has focus or not, so here I've used this.ContainsFocus to check current form has focus or not, if has then handle shortcut events. If you need to implementation shortcut on hole application then you need to check if application has focus or not.
For that you need to create a new class
ApplicationFocus
public class ApplicationFocus
{
/// <summary>Returns true if the current application has focus, false otherwise</summary>
public static bool ApplicationIsActivated()
{
var activatedHandle = GetForegroundWindow();
if (activatedHandle == IntPtr.Zero)
{
return false; // No window is currently activated
}
var procId = Process.GetCurrentProcess().Id;
int activeProcId;
GetWindowThreadProcessId(activatedHandle, out activeProcId);
return activeProcId == procId;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
}
Then replace if (this.ContainsFocus) with ApplicationFocus.ApplicationIsActivated() in gHook_KeyDown event.
This is worked for me.
The MFC Feature Pack toolbar combo-button (class CMFCToolBarComboBoxButton) works perfectly in horizontal toolbar mode. But in vertical layout mode it is the simple press button without combobox feature.
How to make CMFCToolBarComboBoxButton able to works in vertical mode?
This my solution. I overwrote the behavior of the buttons in vertical mode. And now the button shows combobox drop down window when it is pressed in vertical mode.
You should use class CVerticalableToolBarComboBoxButton instead of CMFCToolBarComboBoxButton in ReplaceButton() method when you add combobox button to your toolbar.
Example: In the images below you can see the behavior of the buttons in the horizontal and vertical modes.
Horizontal mode
Vertical mode
Class code:
class CVerticalableToolBarComboBoxButton
: public CMFCToolBarComboBoxButton
{
DECLARE_SERIAL(CVerticalableToolBarComboBoxButton)
public:
typedef CMFCToolBarComboBoxButton TBase;
protected:
bool m_bDoVerticalMode;
public:
CVerticalableToolBarComboBoxButton(bool bDoVerticalMode = true);
CVerticalableToolBarComboBoxButton(UINT uiID, int iImage, DWORD dwStyle = CBS_DROPDOWNLIST, int iWidth = 0, bool bDoVerticalMode = true);
virtual ~CVerticalableToolBarComboBoxButton();
virtual void Serialize(CArchive& ar);
virtual BOOL OnClick(CWnd* pWnd, BOOL bDelay = TRUE);
virtual void OnChangeParentWnd(CWnd* pWndParent);
virtual void OnMove();
virtual void OnSize(int iSize);
protected:
void AdjustVerticalRect();
};
/////////////////////////////////////////////////////////////////////////////
// CVerticalableToolBarComboBoxButton
IMPLEMENT_SERIAL(CVerticalableToolBarComboBoxButton, CVerticalableToolBarComboBoxButton::TBase, VERSIONABLE_SCHEMA | 1)
CVerticalableToolBarComboBoxButton::CVerticalableToolBarComboBoxButton(bool bDoVerticalMode /*= true*/)
: m_bDoVerticalMode(bDoVerticalMode)
{}
CVerticalableToolBarComboBoxButton::CVerticalableToolBarComboBoxButton(UINT uiID, int iImage, DWORD dwStyle /*= CBS_DROPDOWNLIST*/, int iWidth /*= 0*/, bool bDoVerticalMode /*= true*/)
: TBase(uiID, iImage, dwStyle, iWidth)
, m_bDoVerticalMode(bDoVerticalMode)
{}
CVerticalableToolBarComboBoxButton::~CVerticalableToolBarComboBoxButton()
{}
void CVerticalableToolBarComboBoxButton::Serialize(CArchive& ar)
{
TBase::Serialize(ar);
if (ar.IsLoading()) {
ar >> m_bDoVerticalMode;
}
else {
ar << m_bDoVerticalMode;
}
}
BOOL CVerticalableToolBarComboBoxButton::OnClick(CWnd* pWnd, BOOL bDelay /*= TRUE*/)
{
BOOL bRes = FALSE;
bool bDefault = m_bHorz || !m_bDoVerticalMode;
if (!bDefault) {
if (IsFlatMode()) {
if (m_pWndEdit == NULL) {
m_pWndCombo->SetFocus();
}
else {
m_pWndEdit->SetFocus();
}
m_pWndCombo->ShowDropDown();
if (pWnd != NULL) {
pWnd->InvalidateRect(m_rectCombo);
}
bRes = TRUE;
}
}
if (bDefault) {
bRes = TBase::OnClick(pWnd, bDelay);
}
return bRes;
}
void CVerticalableToolBarComboBoxButton::OnChangeParentWnd(CWnd* pWndParent)
{
TBase::OnChangeParentWnd(pWndParent);
if (!m_bHorz & m_bDoVerticalMode) {
AdjustVerticalRect();
}
}
void CVerticalableToolBarComboBoxButton::OnMove()
{
TBase::OnMove();
if (!m_bHorz & m_bDoVerticalMode) {
AdjustVerticalRect();
}
}
void CVerticalableToolBarComboBoxButton::OnSize(int iSize)
{
TBase::OnSize(iSize);
if (!m_bHorz & m_bDoVerticalMode) {
AdjustVerticalRect();
}
}
void CVerticalableToolBarComboBoxButton::AdjustVerticalRect()
{
ASSERT(m_bDoVerticalMode);
ASSERT(!m_bHorz);
if (m_pWndCombo->GetSafeHwnd() == NULL || m_rect.IsRectEmpty()) {
m_rectCombo.SetRectEmpty();
m_rectButton.SetRectEmpty();
return;
}
CMFCToolBar* pParentBar = nullptr;
{
CWnd* pNextBar = m_pWndCombo->GetParent();
while (pParentBar == nullptr && pNextBar != nullptr) {
pParentBar = DYNAMIC_DOWNCAST(CMFCToolBar, pNextBar);
pNextBar = pNextBar->GetParent();
}
}
if (IsCenterVert() && (!m_bTextBelow || m_strText.IsEmpty()) && (pParentBar != nullptr))
{
const int nRowHeight = pParentBar->GetRowHeight();
const int yOffset = std::max<>(0, (nRowHeight - m_rect.Height()) / 2);
m_rect.OffsetRect(0, yOffset);
}
{
CRect rect;
m_pWndCombo->GetWindowRect(&rect);
const int nWidth = std::max<>(rect.Width(), m_iWidth);
rect.left = m_rect.left;
rect.top = m_rect.top;
rect.right = m_rect.left + nWidth;
rect.bottom = m_rect.top + m_nDropDownHeight;
if ((pParentBar != nullptr) && pParentBar->IsDocked()) {
const UINT nID = pParentBar->GetParentDockSite()->GetDockSiteID();
if (nID == AFX_IDW_DOCKBAR_RIGHT) {
rect.left = m_rect.right - nWidth;
rect.right = m_rect.right;
}
}
m_pWndCombo->SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOZORDER | SWP_NOACTIVATE);
m_pWndCombo->SetEditSel(-1, 0);
}
{
m_pWndCombo->GetWindowRect(&m_rectCombo);
m_pWndCombo->ScreenToClient(&m_rectCombo);
m_pWndCombo->MapWindowPoints(m_pWndCombo->GetParent(), &m_rectCombo);
}
if (m_bFlat) {
m_rectButton = m_rectCombo;
}
else {
m_rectButton.SetRectEmpty();
}
}
// CVerticalableToolBarComboBoxButton
/////////////////////////////////////////////////////////////////////////////
I'm trying to handle thread pool in a class.
below is my code.
#include <Windows.h>
class ClassA
{
public : // user API
ClassA()
{
}
~ClassA()
{
}
public : //my muiti-thread func
void init()
{
//*************************************
// multithread Initialization
//*************************************
pool = NULL;
cleanupgroup = NULL;
rollback = 0;
bool bRet = false;
pool = CreateThreadpool(NULL);
if(NULL == pool)
{
goto cleanPool;
}
rollback = 1;
SetThreadpoolThreadMaximum(pool, 5);
bRet = SetThreadpoolThreadMinimum(pool, 10);
if (FALSE == bRet) {
goto cleanPool;
}
cleanupgroup = CreateThreadpoolCleanupGroup();
if (NULL == cleanupgroup) {
goto cleanPool;
}
rollback = 2;
SetThreadpoolCallbackPool(&CallBackEnviron, pool);
SetThreadpoolCallbackCleanupGroup(&CallBackEnviron,
cleanupgroup,
NULL);
return ;
cleanPool:
switch (rollback)
{
case 2:
// Clean up the cleanup group.
CloseThreadpoolCleanupGroup(cleanupgroup);
case 1:
// Clean up the pool.
CloseThreadpool(pool);
default:
break;
}
return ;
}
void foo()
{
PTP_WORK work = NULL;
work = CreateThreadpoolWork(ClassA::_delegate,
NULL,
&CallBackEnviron);
}
static void __stdcall _delegate(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WORK Work)
{
//some code
}
PTP_POOL pool;
UINT rollback;
TP_CALLBACK_ENVIRON CallBackEnviron;
PTP_CLEANUP_GROUP cleanupgroup;
};
int main()
{
ClassA a;
a.init();
a.foo();
}
If you make a project and execute this code, it gets unhandled execption...
I have no clue why...
I think the exception is caused by an uninitialized structure CallBackEnviron. The documentation states that this structure must be initialized by InitializeThreadpoolEnvironment
So, I've been playing with trying to create an AppBar program I'm making. Now, the program itself is actually quite simple but I had to borrow some code from a CodeProject project to make it an AppBar.
So, my code is the following:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
enum ABMsg : int
{
ABM_NEW = 0,
ABM_REMOVE,
ABM_QUERYPOS,
ABM_SETPOS,
ABM_GETSTATE,
ABM_GETTASKBARPOS,
ABM_ACTIVATE,
ABM_GETAUTOHIDEBAR,
ABM_SETAUTOHIDEBAR,
ABM_WINDOWPOSCHANGED,
ABM_SETSTATE
}
enum ABNotify : int
{
ABN_STATECHANGE = 0,
ABN_POSCHANGED,
ABN_FULLSCREENAPP,
ABN_WINDOWARRANGE
}
enum ABEdge : int
{
ABE_LEFT = 0,
ABE_TOP,
ABE_RIGHT,
ABE_BOTTOM
}
private bool fBarRegistered = false;
private int uCallBack;
private int whereToPin;
[DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("USER32")]
static extern int GetSystemMetrics(int Index);
[DllImport("User32.dll", ExactSpelling = true,
CharSet = System.Runtime.InteropServices.CharSet.Auto)]
private static extern bool MoveWindow
(IntPtr hWnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);
private void RegisterBar()
{
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = this.Handle;
if (!fBarRegistered)
{
uCallBack = RegisterWindowMessage("AppBarMessage");
abd.uCallbackMessage = uCallBack;
uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd);
fBarRegistered = true;
ABSetPos();
}
else
{
SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd);
fBarRegistered = false;
}
}
private void ABSetPos()
{
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = this.Handle;
abd.uEdge = whereToPin;
if (abd.uEdge == (int)ABEdge.ABE_LEFT || abd.uEdge == (int)ABEdge.ABE_RIGHT)
{
abd.rc.top = 0;
abd.rc.bottom = SystemInformation.PrimaryMonitorSize.Height;
if (abd.uEdge == (int)ABEdge.ABE_LEFT)
{
abd.rc.left = 0;
abd.rc.right = Size.Width;
}
else
{
abd.rc.right = SystemInformation.PrimaryMonitorSize.Width;
abd.rc.left = abd.rc.right - Size.Width;
}
}
else
{
abd.rc.left = 0;
abd.rc.right = SystemInformation.PrimaryMonitorSize.Width;
if (abd.uEdge == (int)ABEdge.ABE_TOP)
{
abd.rc.top = 0;
abd.rc.bottom = Size.Height;
}
else
{
abd.rc.bottom = SystemInformation.PrimaryMonitorSize.Height;
abd.rc.top = abd.rc.bottom - Size.Height;
}
}
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref abd);
switch (abd.uEdge)
{
case (int)ABEdge.ABE_LEFT:
abd.rc.right = abd.rc.left + Size.Width;
break;
case (int)ABEdge.ABE_RIGHT:
abd.rc.left = abd.rc.right - Size.Width;
break;
case (int)ABEdge.ABE_TOP:
abd.rc.bottom = abd.rc.top + 100;
break;
case (int)ABEdge.ABE_BOTTOM:
abd.rc.top = abd.rc.bottom - Size.Height;
break;
}
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref abd);
MoveWindow(abd.hWnd, abd.rc.left, abd.rc.top,
abd.rc.right - abd.rc.left, abd.rc.bottom - abd.rc.top, true);
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == uCallBack)
{
switch (m.WParam.ToInt32())
{
case (int)ABNotify.ABN_POSCHANGED:
ABSetPos();
break;
}
}
base.WndProc(ref m);
}
protected override System.Windows.Forms.CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Style &= (~0x00C00000); // WS_CAPTION
cp.Style &= (~0x00800000); // WS_BORDER
cp.ExStyle = 0x00000080 | 0x00000008; // WS_EX_TOOLWINDOW | WS_EX_TOPMOST
return cp;
}
}
private void button1_Click(object sender, EventArgs e)
{
whereToPin = (int)ABEdge.ABE_LEFT;
RegisterBar();
}
private void button2_Click(object sender, EventArgs e)
{
whereToPin = (int)ABEdge.ABE_RIGHT;
RegisterBar();
}
}
My two questions are:
What are the possible values cp.Style and how do those values effect the display of the AppBar? (The code to which I refer to is located in the System.Windows.Forms.CreateParams override)
I see they are values such as (~0x00C00000) but I have no idea how they work beyond those specific values and can't seem to find any enumeration of different values.
I'm a rather new, self teaching, programmer who does well by taking examples and molding them to my own uses. Thanks in advance for any help you can provide.
Here you go...
Window Styles
As per the OP's original query, there's also:
Window Class Styles
I'm missing something fundamental, and probably both simple and obvious.
My issue:
I have a view (CPlaybackView, derived from CView).
The view displays a bunch of objects derived from CRectTracker (CMpRectTracker).
These objects each contain a floating point member.
I want to display that floating point member when the mouse hovers over the CMpRectTracker.
The handler method is never executed, although I can trace through OnIntitialUpdate, PreTranslateMessage,
and OnMouseMove.
This is in Visual C++ v. 6.0.
Here's what I've done to try to accomplish this:
1. In the view's header file:
public:
BOOL OnToolTipNeedText(UINT id, NMHDR * pNMHDR, LRESULT * pResult);
private:
CToolTipCtrl m_ToolTip;
CMpRectTracker *m_pCurrentRectTracker;//Derived from CRectTracker
2. In the view's implementation file:
a. In Message Map:
ON_NOTIFY_EX(TTN_NEEDTEXT,0,OnToolTipNeedText)
b. In CPlaybackView::OnInitialUpdate:
if (m_ToolTip.Create(this, TTS_ALWAYSTIP) && m_ToolTip.AddTool(this))
{
m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_AUTOPOP, SHRT_MAX);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_INITIAL, 200);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_RESHOW, 200);
}
else
{
TRACE("Error in creating ToolTip");
}
this->EnableToolTips();
c. In CPlaybackView::OnMouseMove:
if (::IsWindow(m_ToolTip.m_hWnd))
{
m_pCurrentRectTracker = NULL;
m_ToolTip.Activate(FALSE);
if(m_rtMilepostRect.HitTest(point) >= 0)
{
POSITION pos = pDoc->m_rtMilepostList.GetHeadPosition();
while(pos)
{
CMpRectTracker tracker = pDoc->m_rtMilepostList.GetNext(pos);
if(tracker.HitTest(point) >= 0)
{
m_pCurrentRectTracker = &tracker;
m_ToolTip.Activate(TRUE);
break;
}
}
}
}
d. In CPlaybackView::PreTranslateMessage:
if (::IsWindow(m_ToolTip.m_hWnd) && pMsg->hwnd == m_hWnd)
{
switch(pMsg->message)
{
case WM_LBUTTONDOWN:
case WM_MOUSEMOVE:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
m_ToolTip.RelayEvent(pMsg);
break;
}
}
e. Finally, the handler method:
BOOL CPlaybackView::OnToolTipNeedText(UINT id, NMHDR * pNMHDR, LRESULT * pResult)
{
BOOL bHandledNotify = FALSE;
CPoint CursorPos;
VERIFY(::GetCursorPos(&CursorPos));
ScreenToClient(&CursorPos);
CRect ClientRect;
GetClientRect(ClientRect);
// Make certain that the cursor is in the client rect, because the
// mainframe also wants these messages to provide tooltips for the
// toolbar.
if (ClientRect.PtInRect(CursorPos))
{
TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
CString str;
str.Format("%f", m_pCurrentRectTracker->GetMilepost());
ASSERT(str.GetLength() < sizeof(pTTT->szText));
::strcpy(pTTT->szText, str);
bHandledNotify = TRUE;
}
return bHandledNotify;
}
Got it:
Just to put it to bed, here's the result:
BEGIN_MESSAGE_MAP(CPlaybackView, CThreadView)
ON_NOTIFY_EX(TTN_NEEDTEXT,0,OnToolTipNeedText)
END_MESSAGE_MAP()
void CPlaybackView::OnInitialUpdate()
{
if (m_ToolTip.Create(this, TTS_ALWAYSTIP) && m_ToolTip.AddTool(this))
{
m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_AUTOPOP, 2000);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_INITIAL, 1);
m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_RESHOW, 1);
m_ToolTip.Activate(FALSE);
BOOL ena = EnableToolTips(TRUE);
}
else
{
TRACE("Error in creating ToolTip");
}
}
void CPlaybackView::OnMouseMove(UINT nFlags, CPoint point)
{
CPlaybackDoc* pDoc = (CPlaybackDoc*) GetDocument();
if (::IsWindow(m_ToolTip.m_hWnd))
{
CMpRectTracker *pRectTracker = HitTest(point);
if(pRectTracker)
{
m_ToolTip.Activate(TRUE);
}
else
{
m_ToolTip.Activate(FALSE);
}
}
}
BOOL CPlaybackView::OnToolTipNeedText(UINT id, NMHDR * pNMHDR, LRESULT * pResult)
{
BOOL bHandledNotify = FALSE;
CPoint CursorPos;
VERIFY(::GetCursorPos(&CursorPos));
ScreenToClient(&CursorPos);
CRect MilepostRect;
m_rtMilepostRect.GetTrueRect(MilepostRect);
// Make certain that the cursor is in the client rect, because the
// mainframe also wants these messages to provide tooltips for the
// toolbar.
if(MilepostRect.PtInRect(CursorPos))
{
TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
CMpRectTracker *pRectTracker = HitTest(CursorPos);
if(pRectTracker)
{
CString str;
str.Format("%f", pRectTracker->GetMilepost());
ASSERT(str.GetLength() < sizeof(pTTT->szText));
::strcpy(pTTT->szText, str);
bHandledNotify = TRUE;
}
}
return bHandledNotify;
}
BOOL CPlaybackView::PreTranslateMessage(MSG* pMsg)
{
if (::IsWindow(m_ToolTip.m_hWnd) && pMsg->hwnd == m_hWnd)
{
switch(pMsg->message)
{
case WM_LBUTTONDOWN:
case WM_MOUSEMOVE:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
m_ToolTip.RelayEvent(pMsg);
break;
}
}
return CView::PreTranslateMessage(pMsg);
}
CMpRectTracker* CPlaybackView::HitTest(CPoint &point)
{
CPlaybackDoc* pDoc = (CPlaybackDoc*) GetDocument();
ASSERT_VALID(pDoc);
CMpRectTracker *pTracker = NULL;
POSITION pos = pDoc->m_rtMilepostList.GetHeadPosition();
while(pos)
{
CMpRectTracker tracker = pDoc->m_rtMilepostList.GetNext(pos);
if(tracker.HitTest(point) >= 0)
{
m_CurrentRectTracker = tracker;
pTracker = &m_CurrentRectTracker;
break;
}
}
return pTracker;
}