Archive for the ‘MFC’ Category

Unexpected behavior of Win32 when checking serial ports.

I recently noticed very interesting behavior in ClearCommError() function of Win32.
This code returns how many bytes there are in incoming buffer for a serial port.

DWORD CSerialPort::BytesWaiting()
{
  //Validate our parameters
  ASSERT(IsOpen());

  //Check to see how many characters are unread
  COMSTAT stat;
  GetStatus(stat);
  return stat.cbInQue;
}

void CSerialPort::GetStatus(COMSTAT& stat)
{
  //Validate our parameters
  ASSERT(IsOpen());

  DWORD dwErrors;
  if (!ClearCommError(m_hComm, &dwErrors, &stat))
  {
    DWORD dwLastError = GetLastError();
    TRACE(_T("CSerialPort::GetStatus, Failed in call to ClearCommError, Error:%d\n"), dwLastError);
    ThrowSerialException(dwLastError);
  }
}

However, after actually debugging it I found out that there are cases where

  • The number of bytes in its incoming buffer doesn’t increase while it is calling BytesWaiting() to see if it received enough bytes to read. Then, it should read its incoming buffer for the given COM port to have BytesWaiting() starts to return accumulated number of bytes after reading.
  • When BytesWaiting() returns 0, you still need to read the buffer not to fail in detecting any further data arrived from the other side of communication. reading 0 bytes in this case, will cause the BytesWaiting() keeps returning the number of bytes read after that point.

Normally, whether you read or not, callying BytesWaiting() correctly reports the number of bytes in an incoming buffer.
However, if any of those listed above happens, it stops reporting correct number of bytes.

So, this code fails.

int CLensControllerDlg::ReadIncomingPacket(BYTE incomingBuffer[], int numOfBytesToRead, bool *pShouldGiveUp)
{
     DWORD bytesInReceptionQueue;
     int readNumOfBytes, extraReadNumOfBytes;

     readNumOfBytes = extraReadNumOfBytes = 0;

     // First read
     readNumOfBytes = m_pSerialPort->Read( incomingBuffer, numOfBytesToRead );
     int lengthToReadFurther;
     int secondTryReadLength = 0;

     static int trialCount = 0;

     // Check if it needs to read once more
     if( (incomingBuffer[ 0] == (BYTE) 0x50) &&
           (incomingBuffer[ 1] == (BYTE) 0xAF ))
     {
           lengthToReadFurther = static_cast<int >(incomingBuffer[4]) + 1; // payload length + check sum
            // Read length + 1 more ( 1 is for checksum )

            //!!!!! [1] !!!!!
            // There are cases where bytesInReceptionQueue doesn't have accumulated number of
            // bytes in a reception bufffer.
            while( (bytesInReceptionQueue = m_pSerialPort->BytesWaiting()) < lengthToReadFurther )
           {
                Sleep( 3 ); // 10 msec
                trialCount++;

                //!!!!! [2] !!!!!
                // So, if the bytesInReceptionQueue doesn't contain the accumulated number of bytes
                // through the iteration, try to read some.
                // Then it will start to work again.
                 if( trialCount > 10 )
                {
                      if( bytesInReceptionQueue > 0 )
                     {
                            // Give another chance
                           secondTryReadLength = m_pSerialPort->Read( &incomingBuffer[readNumOfBytes], bytesInReceptionQueue );
                           readNumOfBytes += secondTryReadLength;

                           lengthToReadFurther -= secondTryReadLength;

                           trialCount = 0;

                     }
                      else
                     {
                            // let's see what it happens if try reading for 0 bytes and read further
                           BYTE temp[ 16];
                            int tempReadLength;

                            // bytesInReceptionQueue == 0
                           tempReadLength = m_pSerialPort->Read( &incomingBuffer[readNumOfBytes], bytesInReceptionQueue );
                           readNumOfBytes += tempReadLength;
                           lengthToReadFurther -= tempReadLength;

                            //// Once read, somehow, bytesInReceptionQueue starts to be accumulated again.
                            //bytesInReceptionQueue = m_pSerialPort->BytesWaiting();

                            //tempReadLength = m_pSerialPort->Read( &incomingBuffer[readNumOfBytes], bytesInReceptionQueue );
                            //readNumOfBytes += tempReadLength;
                            //lengthToReadFurther -= tempReadLength;

                           CString msgString;
                           msgString.Format( _T( ">> Will give up this incoming data : %x %x %x %x %x\r\n"),
                                                  incomingBuffer[0], incomingBuffer[1 ],
                                                  incomingBuffer[2], incomingBuffer[3 ],
                                                  incomingBuffer[4] );

                           WriteLogMessage( msgString );
                           TRACE( msgString );

                            if( pShouldGiveUp != NULL )
                           {
                                *pShouldGiveUp = true;
                           }

                           trialCount = 0;
                            return 0;
                     }
                }
           }

            // In the queue, now there are lengthToReadFurther or more to read.
           extraReadNumOfBytes = m_pSerialPort->Read( &incomingBuffer[readNumOfBytes], lengthToReadFurther );
     }

     return (readNumOfBytes + extraReadNumOfBytes);
}

The code above was written while I was debugging. So, it’s not clean nor compact.
So, here is better version if you want to see.

int CLensControllerDlg::ReadIncomingPacket(BYTE incomingBuffer[], int numOfBytesToRead, bool *pShouldGiveUp)
{
     DWORD bytesInReceptionQueue;
     int readNumOfBytes, extraReadNumOfBytes;

     readNumOfBytes = extraReadNumOfBytes = 0;

     // First read
     readNumOfBytes = m_pSerialPort->Read( incomingBuffer, numOfBytesToRead );
     int lengthToReadFurther;
     int secondTryReadNumOfBytes = 0;

     // Check if it needs to read once more
     if( (incomingBuffer[0] == (BYTE)0x50) &&
          (incomingBuffer[1] == (BYTE)0xAF ))
     {
          lengthToReadFurther = static_cast<int>(incomingBuffer[4]) + 1; // payload length + check sum
          // Read length + 1 more ( 1 is for checksum )
          while( lengthToReadFurther > 0 )
          {
               Sleep( 1 ); // 10 msec

               bytesInReceptionQueue = m_pSerialPort->BytesWaiting();

               // If it's normal, just waiting until bytesInReceptionQueue >= lengthToReadFurther,
               // and read the whole "lengthToReadFurther" amount of data all at once.
               //
               // However, sometimes the inbound buffer should be read to have further data accumulated.
               // If not,
               secondTryReadNumOfBytes = m_pSerialPort->Read( &incomingBuffer[readNumOfBytes], lengthToReadFurther );
               readNumOfBytes += secondTryReadNumOfBytes;

               lengthToReadFurther -= secondTryReadNumOfBytes;
          }
     }

     return readNumOfBytes;
}
Advertisements

CCheckListbox and item height

Well, MFC shows its age.

Although many fairly new programmers, who started to write code after the year 2000, think MFC is old, but in 90’s it was not.
It’s not as elegant as Cocoa or even new .NET ( with C#. .NET for C++ looks like too much overloaded truck with redundant stuffs on it. ).
However, it solves problems for its generation, and there was good reason for MS people to take such design.

However, they had to renovate it. There are many issues like control vs. view for similar components, inconsistent way to deal with widgets, etc.
Today I found a problem with item height for CCheckListBox. With VS resource editor, you can change “font size” of a dialog box or a window. What it actually does is not to change font size for widgets on them.

font size( I think it’s also a problem. What it actually does is to change “scale” of the coordinate system on that dialog box or window. Because the scale is changed, everything like button size is also changed as well as font size. So, it’s not right to name the property “Font(Size)”. )

Problem is that when an instance of CCheckListbox is instantiated and items are added to that, it checks whether each item height is bigger than its minimum size like this. ( Please focus on the last line. )

void CCheckListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
  // You must override DrawItem and MeasureItem for LBS_OWNERDRAWVARIABLE
  ASSERT((GetStyle() & (LBS_OWNERDRAWFIXED | LBS_HASSTRINGS)) ==
    (LBS_OWNERDRAWFIXED | LBS_HASSTRINGS));

  CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
  ENSURE(pDC);

  if (((LONG)(lpDrawItemStruct->itemID) >= 0) &&
    (lpDrawItemStruct->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)))
  {
    int cyItem = GetItemHeight(lpDrawItemStruct->itemID);
    BOOL fDisabled = !IsWindowEnabled() || !IsEnabled(lpDrawItemStruct->itemID);

    COLORREF newTextColor = fDisabled ?
      RGB(0x80, 0x80, 0x80) : GetSysColor(COLOR_WINDOWTEXT);  // light gray
    COLORREF oldTextColor = pDC->SetTextColor(newTextColor);

    COLORREF newBkColor = GetSysColor(COLOR_WINDOW);
    COLORREF oldBkColor = pDC->SetBkColor(newBkColor);

    if (newTextColor == newBkColor)
      newTextColor = RGB(0xC0, 0xC0, 0xC0);   // dark gray

    if (!fDisabled && ((lpDrawItemStruct->itemState & ODS_SELECTED) != 0))
    {
      pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT));
      pDC->SetBkColor(GetSysColor(COLOR_HIGHLIGHT));
    }

    if (m_cyText == 0)
      VERIFY(cyItem >= CalcMinimumItemHeight());

It turned out that cyItem, which is height of an interested item at that moment, is less than the minimum height if the font size is set to 12.
So, MFC is not updated to work with its visual resource editor or vice versa.
At least there should be some property to choose item height based on chosen “Font size” on the visual editor. If the code for MFC presents relevant error message when debugging, it can be even better.

Anyway, if you set the item height using SetItemHeight() sufficiently, its debugger will not complain again. This is not documented in the section for CCheckListBox at all.

We complain that Apple let Mac OS X and its Cocoa framework behind due to its focus on iOS. ( kernel is the same on the both and Cocoa is almost same to CocoaTouch, but still.. ) However, Apple people keep updating their tool set to work properly with their framework. MS doesn’t seem to be considerate on this long-lived-but-dependable MFC framework. Yes, model-wise, .NET is better. But I often notice that .NET with C++ is too bloated and .NET with C# is too weak. For native environment, MS doesn’t have new framework to replace MFC for C++.
Nowadays, it doesn’t seem to me that there are many S/W engineers working with MFC/C++. So, probably they don’t ask much to MS. Well, in the world of Web app, Java and C#, where people usually justify “computers are getting faster. So, bytecode/VM language like C#/Java are OK.”. I think it’s understandable argument. But still it’s not sufficient to me. It’s like to use 486 computers with the speed of 386 computers. For convenience of using and learning a language, MS gave up many things in C#/.NET, which can be done more easily with C++/MFC/Win32.

How much complete a framework can be : MS vs. Apple

As a person who major in CS, I have interest in fundamental technology or techniques like compilers, OS, 3D graphics, computer vision, parallel systems & distributed systems. So, one of the reason I started to work with 3 major OSes, i.e. Windows, Unix and Mac, and have had lots of interest in their architectural design. Therefore I like to work with different frameworks and compare their design or philosophy behind them.

I think OS architecture is like a whole picture, while frameworks are each part of the OSes. What architectural design an OS has is reflected to its frameworks. When overall architecture design affect how they work, how their components are related one another and so on, frameworks for them should be designed as such. This is one of the reason I think Apple should have filed with OS architectures than their visual UI design against MS and Google.

Here, I would like to show the very difference between .NET and Cocoa. If you have followed ToolBox API and Win32/MFC, you will see similarity between Apple’s and MS’s. Also, there are similarity between Cocoa and .NET. I think it is because Objective-C/Cocoa is in the SmallTalk camp which test OOP concept in academia and geared toward SE, while C++ is in Simula camp which is more for real industry field. Because C++ is for more real field, it should have been compromised with many restriction like CPU speed etc. Also the C++ has been evolved for everything for all people. So, although C++ was introduced as THE OOP language, it has many different concept like meta programming, ad-hoc polymorphism. Also, because of that it also contains its syntax which became somewhat bad and some concept got to have many semantics. (e.g. extern vs. static ) C# is not too different from C++ in that. Although MS streamlined C# from C++. (actually C# was influenced more by Java at first. But as its name suggests it has the face of C++. # in its name is somewhat amusing. When they call it, don’t forget that it is called “sharp” rather than “pound”. # in musical notation is half higher than a note it is attached too. So, C# is C++ in that sense. ) I can feel those philosophy to be all for all from C#.
However, Objective-C and Cocoa was evolved to be focused to increase productivity. So, when using Objective-C and Cocoa, you naturally become to focus on core logic rather than “how to achieve that”. Objective-C is more simple and Cocoa is designed to be very intuitive and powerful.

OK. Let’s see how Objective-C/Cocoa achieves “GUI code on main thread”.

- (IBAction)doInThread:(id)sender
{
    [NSThread detachNewThreadSelector:@selector(doSomethingMethod:)
                             toTarget:self withObject:nil];
}

// Thread method
- (void)doSomethingMethod:(id)object
{
    // Calls appendLogMessage: on main thread
    [self performSelector:@selector(appendLogMessage:)
                 onThread:[NSThread mainThread]
               withObject:@"Do something..."
            waitUntilDone:YES];
}

- (void)appendLogMessage:(NSString *)messageString
{
    NSTextStorage *textStorage = [m_logBoxView textStorage];
    NSUInteger theLastPos = [textStorage length];

    [m_logBoxView setSelectedRange:NSMakeRange( theLastPos, 0 )];

    [m_logBoxView insertText:[NSString stringWithFormat:@"%@\n",
                              messageString]];
}

It’s very intuitive. (Don’t confuse with that you don’t understand Obj-C code. Once you get used to it, it is very easy and straightforward. ) The appendLogMessage: is written just like a normal message/method.

Now, let’s check how it looks in C#/.NET.

#region MSDN way
private void m_doSomethingButton_Click(object sender,EventArgs e)
{
    m_choreThread = new Thread(new ThreadStart(this.StartThreadLoop));

    m_choreThread.Name = "MSDN Thread";
    m_choreThread.Start();
}

// Thread method
private void StartThreadLoop()
{
    // This is a thread loop
    int i = 0;

    while (i < 10)
    {
        // 1. Call WriteLogMessage()
        this.WriteLogMessage(String.Format("Do chores hard!!! {0} from {1}", i, m_choreThread.Name));

        i++;
    }

}

// 2. Visit this in the non-main thread space
// 5. Visit this in the main thread space
private void WriteLogMessage(string msgString)
{
    // 3. If it is still in the context of a non-main thread
    if (m_logTextBox.InvokeRequired)
    {
        WriteLogMessageCallback writeLogMessageDelegate = new WriteLogMessageCallback(WriteLogMessage);

        // 4. Invoke in its main thread space
        this.Invoke(writeLogMessageDelegate, new object[] { msgString });
    }
    else
    {
        // 6. Call this in the main thread space.
        m_logTextBox.AppendText(msgString + Environment.NewLine);
    }
}
#endregion

I commented how things are visited in the C#/.NET case to help your understanding. So, to you, who use the framework, .NET case prevent you from concentrating on the main logic. In other words, you should know how things work in C#/.NET.

One of the virtue of OOP is data hiding and encapsulation. Although it says “Data Hiding”, it’s not only about “Data” but also “how it works internally.” C#/.NET fails in this area seriously as you can see. In other words, you should be aware of in which thread context the WriteLogMessage() is called and the method should be implemented with that knowledge.
Compared to that, messages in Objective-C/Cocoa can be written just like usual messages.

Then, are they really different? Couldn’t MS make their .NET framework as elegant as Cocoa? No. I don’t think so. It looks to me that Cocoa’s underlying code could be similar to that of .NET. The difference is that how much Apple people refined their framework by considering people who actually use their framework and design the framework to let people to focus on their own logic not on how Apple implemented this and that.

Then, let’s try to make it look similar to that of Objective-C/Cocoa.

#region Cocoa Way
private void m_doSomethinginCocoaWayButton_Click(object sender, EventArgs e)
{
    WriteLogMessage2(Environment.NewLine + "----------------------------" + Environment.NewLine);
    m_choreThread = new Thread(new ThreadStart(this.StartThreadLoop2));
    m_choreThread.Name = "Cocoa Thread";
    m_choreThread.Start();
}

private void StartThreadLoop2()
{
    // This is a thread loop
    int i = 0;

    WriteLogMessageCallback writeLogMessageDelegate = new WriteLogMessageCallback(WriteLogMessage2);

    while (i < 10)
    {
        this.performOnMainThread(writeLogMessageDelegate,
                                 new object[] { String.Format("Do chores hard!!! {0} from {1}", i, m_choreThread.Name) } );

        i++;
    }

}

private void performOnMainThread( WriteLogMessageCallback methodToInvoke, object[] parameter )
{
    if (m_logTextBox.InvokeRequired)
    {
        this.Invoke(methodToInvoke, parameter);
    }
}

private void WriteLogMessage2(string msgString)
{
    m_logTextBox.AppendText(msgString + Environment.NewLine);

}
#endregion

Don’t bother with the method name “performOnMainThread“. It’s not our interest. Whatever the name is, it doesn’t matter. What we need to focus on here is the pattern of how WriteLogMessage2() is called.
Also, additionally assume that Object class in .NET framework, which all classes are inherited from, contains the performOnMainThread(). Then like Cocoa, you can ask an object to invoke a given method on main thread. Then you, as a application program developer, can write the method to be invoked without worrying about knowing how those invoke system works.

Similar things happens with timer invocation. If you want to invoke a message at certain intervals in Cocoa, the code is very straightforward and very clean. However, in Win32/MFC case, the OnTimer() handler should check what timer triggers this timer event. In this case, it looks to me that Apple people utilizes signals and events. (Signal is so-called H/W events while event is called S/W event, if you want to be specific about the terms. ) I will not show sample code for that here.

Point here is that the level of refinement is higher on Apple’s. Cocoa framework is designed for SE more in mind, while MFC/.NET are more about “Let’s put this on the market as quickly as possible.” Good news is that.NET is more similar to Cocoa than their previous frameworks, because NeXT showed a future a while ago and MS looked to adopted that.

I always wonder why MS simply adopted Objective-C/Cocoa for their platform development. Well, C#.NET was a kind of a gun toward Java and MS languages can share the same .NET framework. So, there is slight difference, but even with Objective-C/Cocoa, there is a language bind. Well.. it’s true that people should make their own Cocoa equivalent.. yeah.. that can be the major issue to MS.
( Well.. I know that not choosing Obj-C/Cocoa or OpenStep by MS was also due to market situation, marketing, etc, but here I would like to focus on technical side. )

I wonder how F-Script people and Ruby Cocoa people bridged their language to Cocoa. (Script Bridging tech.. )

Silverlight와 WPF의 미래

이번 글은, 요새 글들이 그렇긴 하지만, 소스 코드 하나 없고, 이쪽 S/W 산업의 동향을 생각해 보는 것이어서, HotPotato 블로그로 옮겼다.

Silverlight와 WPF의 미래

 

 

CreateEx() and Create() for CDockablePane

Historically CreateEx() has been thought as Create() with additional functionality like more styles.
So, if you take a look at many MFC classes which provide CreateEx() and Create(), you can see that CreateEx() calls Create() and do more chores to provide added functionality.

However, I found out that CDockablePane’s doesn’t.
Although MSDN document shows that there is CreateEx() for CDockablePane, it actually doesn’t exist.
So, you shouldn’t be fooled by the MSDN document and create CDockablePane with CreateEx() and wonder why it doesn’t work.

How to put CFormView on CDockableView

At StackOverflow, there is an inquiry,
How can I place a MFC CFormView inside a CDockablePane?

It is very popular question on the net. Try searching “CFormView on CDockablePane”, “How to put CFormView in CDockableView”, “CView on CDockableView” and so on. ( If CView can be on CDockablePane, CFormView can be too, because CFormView is a child class of CView. )
However I didn’t find anyone who was successful in doing so.

CDockablePane is part of MFC Feature Pack. Although MS distributes it as a part of Visual Studio Service Pack 1, but it was not made by MS. It was made by BCGsoft. I think the author did quite amazing job. However, as he said in the forum, it doesn’t support hosting of CView and its derivatives. One of the reason is said to be routing of window message. If you check all the CDockablePane related classes and all the panes on Visual Studio carefully, you will notice that all the widget on CDockablePane are controls not views.

(This is significant difference between Cocoa and MFC. With Cocoa NSView derivatives can be on any NSWindow/NSView. Actually NSView is derivative of NSWindow on Mac and vice versa on iPhone.

However on Windows, CView and its derivatives are not designed to be on CDialog and so on. So, that is why there are separate CView derivatives like CEditView, CListView, CRichEditView, CFormView, CHtmlEditView, CHtmlView, CTreeView and controls like CEdit, CListCtrl, CTreeCtrl. On dialog boxes, CView derivatives are not supposed to be put.

On Mac, on the other hand, any NSView derivatives can be put on any window and any NSView derivatives. Although NSOutlineView ( or whatever ) which contains a tree controller inside can be seen like the relationship between CListView and CListCtrl, but they are totally different. You use CListCtrl to put it on a dialog box, while you can make a new document window or some window as CListView with its own frame window. Also C*View utilizes the document template architecture, while *Ctrl don’t. However, on a Mac, to use tree control, COutlineView is used instead of its tree control. You can make your own view using the tree control, but if you want to use them as they are, you don’t need to handle the controls directly.

This makes GUI programming very easy on Mac. Very flexible, very powerful and very clean source codes are what you get.

In Cocoa Head LA, I was asked by others why I thought Mac was better than Windows. I would like to explain all this kind of things, but I couldn’t. First, I’m not good at casual talking in English. Second, they are subtle, so I couldn’t think of good example on MFC and Cocoa to compare. Once I run into this kind of issue, I can say “Ah.. this is it!”.

There is another good thing on Cocoa. With Interface Builder, you can see the containment of views visually. You will know how stressful to check someone else’s code which deals with GUI element connected as parent-child relationship a lot and to figure out what causes malfunctioning of the code. )

Anyway, lets go back to original topic.
However, it doesn’t mean that you CANNOT put CView derivatives on CDockablePane.

Here is how to.

For CMainFrame

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CMDIFrameWndEx::OnCreate(lpCreateStruct) == -1)
		return -1;

	// enable Visual Studio 2005 style docking window behavior
	CDockingManager::SetDockingMode(DT_SMART);
	// enable Visual Studio 2005 style docking window auto-hide behavior
	EnableAutoHidePanes(CBRS_ALIGN_ANY);

	// create docking windows
	if (!CreateDockingWindows())
	{
		TRACE0("Failed to create docking windows\n");
		return -1;
	}

	// JongAm
	m_myFormViewPane.EnableDocking( CBRS_ALIGN_ANY );
	DockPane( &m_myFormViewPane );
	////////

	return 0;
}

BOOL CMainFrame::CreateDockingWindows()
{
	BOOL bNameValid;

	// JongAm
	// Create MyFormViewPane

	CString strMyFormViewPane;
	bNameValid = strMyFormViewPane.LoadStringW( IDS_MYFORMVIEW_PANE );
	ASSERT( strMyFormViewPane );
	if( !m_myFormViewPane.Create( strMyFormViewPane, this, CRect( 0, 0, 205, 157 ), TRUE,
								  ID_FILE_NEWFORMVIEW,
								  WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
								  CBRS_BOTTOM | CBRS_FLOAT_MULTI ) )
	{
		TRACE0( "Failed to create My Form View Pane" );
		return -1;
	}

	////////////

	SetDockingWindowIcons(theApp.m_bHiColorIcons);
	return TRUE;
}

Now for the Dockable pane

CDockableFormViewPane::CDockableFormViewPane()
: m_pMyFormView(NULL)
{
}

int CDockableFormViewPane::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CDockablePane::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  Add your specialized creation code here

	m_pMyFormView = CMyFormView::CreateOne(this);

	return 0;
}

void CDockableFormViewPane::OnSize(UINT nType, int cx, int cy)
{
	CDockablePane::OnSize(nType, cx, cy);

	// TODO: Add your message handler code here

	if( m_pMyFormView )
		m_pMyFormView-&gt;SetWindowPos( NULL, 0, 0, cx, cy, SWP_NOZORDER );
}

And finally for CFormView

void CMyFormView::OnInitialUpdate()
{
	CFormView::OnInitialUpdate();

	// TODO: Add your specialized code here and/or call the base class
	GetParentFrame()->RecalcLayout();
	SetScrollSizes( MM_TEXT, CSize( 205, 157 ) );

	ResizeParentToFit(FALSE);
}

CMyFormView *CMyFormView::CreateOne( CWnd *pParent )
{
	CMyFormView *pFormView = new CMyFormView;
	//CMyFormView *pFormView = NULL;
	//CRuntimeClass *pRuntimeClass = RUNTIME_CLASS(CMyFormView);
	//pFormView = (CMyFormView *)pRuntimeClass->CreateObject();

	CDockableFormViewAppDoc *pDoc = CDockableFormViewAppDoc::CreateOne();
	pFormView->m_pDocument = pDoc;

	CCreateContext *pContext = NULL;

#if 0
	if( !pFormView->CreateEx(0, NULL, NULL, WS_CHILD | WS_VISIBLE, CRect(0,0,205,157),
		pParent, -1, pContext ) )
#else
	if (!pFormView->Create(NULL, NULL, WS_CHILD | WS_VISIBLE, CRect(0, 0, 205, 157), pParent, 0, pContext))
#endif
	//if( !pFormView->CreateEx( 0, AfxRegisterWndClass(0, 0, 0, 0), NULL,
	//	WS_CHILD | WS_VISIBLE, CRect( 0, 0, 205, 157), pParent, -1, pContext) )
	{
		AfxMessageBox( _T("Failed in creating CMyFormView") );
	}

	pFormView->OnInitialUpdate();

	return pFormView;
}

int CMyFormView::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
	// TODO: Add your message handler code here and/or call default

	int nResult = 0;

	CFrameWnd* pParentFrame = GetParentFrame();

	if( pParentFrame == pDesktopWnd )
	{
		// When this is docked
		nResult = CFormView::OnMouseActivate(pDesktopWnd, nHitTest, message);
	}
	else
	{
		// When this is not docked

		BOOL isMiniFrameWnd = pDesktopWnd->IsKindOf( RUNTIME_CLASS( CMiniFrameWnd ) );
		BOOL isPaneFrameWnd = pDesktopWnd->IsKindOf( RUNTIME_CLASS( CPaneFrameWnd ) );
		BOOL isMultiPaneFrameWnd = pDesktopWnd->IsKindOf( RUNTIME_CLASS( CMultiPaneFrameWnd ) );

		// pDesktopWnd is the frame window for CDockablePane

		nResult = CWnd::OnMouseActivate( pDesktopWnd, nHitTest, message );

		//nResult = CWnd::OnMouseActivate( pDesktopWnd, nHitTest, message );
		//if( nResult == MA_NOACTIVATE || nResult == MA_NOACTIVATEANDEAT )
		//	return nResult;

		//if (pDesktopWnd != NULL)
		//{
		//	// either re-activate the current view, or set this view to be active
		//	//CView* pView = pDesktopWnd->GetActiveView();
		//	//HWND hWndFocus = ::GetFocus();
		//	//if (pView == this &&
		//	//	m_hWnd != hWndFocus && !::IsChild(m_hWnd, hWndFocus))
		//	//{
		//	//	// re-activate this view
		//	//	OnActivateView(TRUE, this, this);
		//	//}
		//	//else
		//	//{
		//	//	// activate this view
		//	//	pDesktopWnd->SetActiveView(this);
		//	//}
		//}
	}

	return nResult;
}

However, as the author of MFC Feature Pack mentioned, there are many issues about windows message routing. For example, OnMouseActivate() should be customized to prevent crash when you click a mouse button when the dockable pane on which CFormView resides is floating. Also, if the View on the dockable pane has more complicated structure like CSplitView, which contains multiple GUI widget which are needed to be updated to refresh their content, it becomes very complicated.

Currently I try to solve such problems.
However, here I at least showed how to put CFormView/View on CDockablePane. I hope this can save many people’s time to investigate how to solve this issue.

ADDED : Because one guy asked me how to implement the CreateOne() member function, I added it above and its header file here.

#pragma once

// CMyFormView form view

class CDockableFormViewAppDoc;

class CMyFormView : public CFormView
{
	DECLARE_DYNCREATE(CMyFormView)

public:
	CMyFormView();           // protected constructor used by dynamic creation
	virtual ~CMyFormView();

	static CMyFormView *CreateOne( CWnd *pParent );

	// return associated document
	CDockableFormViewAppDoc * GetDocument(void);

public:
	enum { IDD = IDD_CONTENT_PANE };
#ifdef _DEBUG
	virtual void AssertValid() const;
#ifndef _WIN32_WCE
	virtual void Dump(CDumpContext& dc) const;
#endif
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

	DECLARE_MESSAGE_MAP()
	// points to linked document
	CDockableFormViewAppDoc *m_pDocument;

public:
	virtual void OnInitialUpdate();
	afx_msg int OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message);
	afx_msg void OnSizing(UINT fwSide, LPRECT pRect);
};

Sources : DockableFormViewApp-Modified.zip

Storing a pointer value in a non-pointer variable vs. Storing general value in a pointer variable : INT_PTR, LONG_PTR etc vs. MAKEINTRESOURCE

If you are a programmer who studied C/C++ after Windows 95, you may not know the existence of windows data types like INT_PTR, LONG_PTR, because you don’t really need to use it.

So, when you have a chance to port legacy codes to x64 platform, you may  fail in considering proper porting of variables declared with those or variables which don’t use them but semantically means the same.

At StackOverFlow, there is good explanation on this subject.

DWORD_PTR, INT_PTR, LONG_PTR, UINT_PTR, ULONG_PTR When, How and Why?

However, I would like to add more comment.

During MS DOS era, many programmers wrote codes in a way that the longest data type available on his/her machine to store addresses or pointer values. There were a little reason for that. For example, you want to do somewhat mathematical calculation with addresses without bordering with the width of the data type.
To me, it has been always better to calculate on addresses with the width of data type in mind, but there are some other cases you should have disregard the width of data type and just calculate something. It was kind of high technique for doing something weird. ( Some can be, while others are not. )

Some Windows API functions and member functions still require to use such convention. They ask you to pass address value to a variable which is not pointer variables. For that purpose, they use *_PTR.

So, I noticed that not a few programmers, who don’t know history, tended to fail in porting legacy code to x64.

Also, there is opposite example.
This time, it is to store non-pointer value to a pointer variable. Such an example is MAKEINTRESOURCE() macro.

#define MAKEINTRESOURCE(i)(LPTSTR)((DWORD)((WORD)(i)))

So, it takes an integer number for a resource like resource ID for a bitmap or an icon, and converts it to pointer to string (LPTSTR).

So, if you are one of new guys who don’t know its history, just get familiar with this and don’t be puzzled!

%d bloggers like this: