Showing posts with label MFC. Show all posts
Showing posts with label MFC. Show all posts

Wednesday, May 07, 2008

Convert VC++6 to VC++7 -- (2)

Below is a laundry list of the problems and solutions.

Compiler Error: fatal error C1083: Cannot open include file: 'fstream.h': No such file or directory
Solution: change include from fsream.h to fstream, and add using namespace std;
Related problems: ios::nocreate and ios::noreplace are deprecated. ios::nocreate is replace by ios::in

CFile::ReadHuge() and CFile::WriteHuge() are obsolete. Use CFile::Read() and CFile::Write() instead.

Replace &afxChNil by empty string "".

Compiler Error:
  • error C2059: syntax error : '<'
  • error C2143: syntax error : missing ';' before '<'
  • error C2182: 'ConstructElements' : illegal use of type 'void'
  • error C2988: unrecognizable template declaration/definition
Solution: ConstructElements and DestructElements are deprecated. Remove definition of these functions. Ref: Microsoft KB 318734

WINVER default changed to 0x0501 (Windows XP). If your program still want to support Windows 2000, the the following line must be included in the project:
#define WINVER 0x0400

CPropetySheetEx, CPropertyPageEx are deprecated, they are included back to CPropertySheet and CPropertyPage.


CString s(45) no longer compiles. Because CString is changed to template based function with more constructors. This definition has ambiguous overloaded constructors. Change to CString s((TCHAR)45).



VC++7.1 has stricter type requirements. For example, conversion from HANDLE to int and uint is not allowed.

Compiler Error:
error C2668: 'sqrt' : ambiguous call to overloaded function
Solution:
sqrt, fabs, log and other CRT math functions support both double and float type now. An integer input to these functions will cause the error above.
Example:
int i = 10;
float f = sqrt(i); //error C2688
float f = sqrt((float)i); //compiles ok


Compiler Error:
error C2440: 'static_cast' : cannot convert from 'void (__thiscall XXXXXXXX::* )(void)' to 'void (__thiscall XXXXXX::* )(NMHDR *,LRESULT *)'
Solution:
Function signature of event handler OnKillfocus was changed from void Func(void) to void Func(NMHDR*, LRESULT*).

Thursday, May 01, 2008

Convert VC++6 to VC++7 -- (1)

VC++6 is a pretty solid product, which can still produce decent software in Windows XP. But Microsoft has decided to stop support for VC6 for about 2 years now. With the arrival of VS2008 and Windows Vista, it might finally be the time to convert those old projects that you still want to keep alive into a newer platform.

To start the conversion is really easy, just open the .dsp file in Visual Studio 2003. It will convert the project for you automatically, and generate the .vcproj and .sln files. If you are lucky, the next step is press F7 to build the solution and press F5 to run it, and you are done. Unfortunately, 90% of us will need to change the code to make it even compile. VC++6 is less standard compliant than VC++7 and there are some other breaking changes in VC++7.

This series will report problems I've seen when I convert a VC++6 project to VC++7 and some tips to help smooth the process.

Tip 1: for a project that is still actively maintained, during the conversion care must be taken so that changes do not break the software under VC++6. Use this to allow the code changes exists peacefully in VC++6:

#if _MSC_VER >=1500
//this is VC++9.0 or above
#elif _MSC_VER >= 1400
// this is VC++8.0
#elif _MSC_VER >= 1310
// this is VC++7.1
#elif _MSC_VER > 1300
// this is VC++7.0
#else
//assume VC++6
#endif

Thursday, November 08, 2007

Error Installing ActiveX in IE

If an ActiveX component fails to install in IE, to find out the source of the problem, you just need to select IE menu: Tools > Internet Options, click "Settings" button in the "Browsing history" group, then click the "View files" button. There should be a file named like this: "?CodeDownloadErrorLog!name={XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}". This is the error log that contains a bit more information to help find the source of installation failure. The above description is based on IE 7, you should be able to find similar options in other IE versions.

In my specific case, the error log looks like this:

*** Code Download Log entry (07 Nov 2007 @ 10:01:26) ***
Code Download Error: (hr = 800c0300) Unknown Error!!
Operation failed. Detailed Information:
...

...
LOG: Item mfc42.dll being processed.
ERR: INF Processing: Failed (800c0300) processing: mfc42.dll
. Cannot get primary/default language!LOG: URL Download Complete: hrStatus:0, hrOSB:800c0300, hrResponseHdr:0, URL:(*****.CAB)
LOG: Reporting Code Download Completion: (hr:800c0300 (FAILED), CLASSID: e45975a1..., szCODE:(*****.cab), MainType:(null), MainExt:(null))


Below is the related section in the .inf file:

[mfc42.dll]
FileVersion=6,2,4131,0
file=http://[Web Server]/libraries.cab
RegisterServer=no
DestDir=11


After I commented out the "FileVersion" parameter, the ActiveX component is installed successfully.

[mfc42.dll]
;FileVersion=6,2,4131,0
file=http://[Web Server]/libraries.cab
RegisterServer=no
DestDir=11

Since the problem only happens on Windows 2000 computers, and my development environment in Windows XP Pro. I suspect that the MFC42 library provided by me may not be compatible with Win2000. Or the MFC42 is already in use by Win2000, and Win2000 have difficulty upgrading to the newer version provided by me.

Friday, March 24, 2006

srand() and threads in MFC

This is an old problem I only run into recently: You should call srand() for each and every thread that calls rand().

In its multi-thread version, the CRT implemenation in VC++6.0 will save seed to thread local storage. So, it is necessary to seed the rand() function in every new thread your program created. Otherwise, the random number sequence is same as if seeded by 1. This poses 2 serious questions:
  1. How can I write a class that is thread safe, and caller won't have to worry about calling srand() whenever they create a new thread? I do not have a solution yet. The best I can think of is to ask callers, when not sure, always call srand() whenever a new thread is created;
  2. A second question is what if user creates multiple threads in a batch, this will make the recommended intialization code: srand( (unsigned) time(NULL)); fail, because the threads are created within 1 second, and you will get the same sequence in all your threads. After some searching, I found this discussion thread to be quite helpful: It proposed to multiply time by the thread ID to seed rand().

Thursday, January 12, 2006

Dialog Data Validation for MFC

Problem: Data validation in MFC is a bit difficult. We only get
a couple of DDV functions that left a lot to be desired. For example, there is
no easy way to make sure a “Dropdown Combo Box” is not empty.


Solution: After some searching in MSDN documents and
googling, I cannot find a good clean solution. But, I am inspired by the MFC
source code and found the following solution.


In the "DoDataExchange" function of your dialog, you only need to add a few
lines of code (highlighted below) to validate an empty combo box.
Following this logic, you can easily add more sophiscate validation logic, and
make sure the focus will be set to the control that you want user to change.


void CMyDlg::DoDataExchange(CDataExchange* pDX)
{
CMyDlg::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CTaskMTTPage)
DDX_CBString(pDX, IDC_COMBOFILENAME, m_strFilename);
//}}AFX_DATA_MAP

//customized data verification
if(pDX->m_bSaveAndValidate)
{
//we will only check data during validation
if(m_strFilename.IsEmpty())
{
CString errorMessage = "The file name field cannot be empty.";
AfxMessageBox(errorMessage, MB_ICONEXCLAMATION);
errorMessage.Empty();

pDX->PrepareCtrl(IDC_COMBOFILENAME); //make sure pDX will focus to this control
pDX->Fail();
}
}

}

Extra: In the process of searching for the solution mentioned
above, I found this interesting problem: VC7 and VC98 have slightly different
definition of public members of CDataExchange class (notice the highlighted
code segment below). Why should Microsoft change definition of public member of
their library? Isn't public member supposed to be some sort of binding
contract?


Another interesting discovery is that Visual Studio intell-sense won't show "m_hWndLastControl"
although it is public member of the class. Did I miss something here?


Why do I care about this obscure member of CDataExchange? Well, I
am trying to find a way to make sure the dialog will set focus on the correct
control after an error condition occurs. My first intent was to manipulate the
m_hWndLastControl
or m_idLastControl member, thus lead
to the discovery. Eventually, I found out calling function PrepareCtrl
is a much better solution.


Definition of class CDataExchange in VC98 include:

// CDataExchange - for data exchange and validation
class CDataExchange
{
// Attributes
public:
BOOL m_bSaveAndValidate; // TRUE => save and validate data
CWnd* m_pDlgWnd; // container usually a dialog

// Operations (for implementors of DDX and DDV procs)
HWND PrepareCtrl(int nIDC); // return HWND of control
HWND PrepareEditCtrl(int nIDC); // return HWND of control
void Fail(); // will throw exception

#ifndef _AFX_NO_OCC_SUPPORT
CWnd* PrepareOleCtrl(int nIDC); // for OLE controls in dialog
#endif

// Implementation
CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate);

HWND m_hWndLastControl; // last control used (for validation)
BOOL m_bEditLastControl; // last control was an edit item
};


Definition of class CDataExchange in VC7 include:

// CDataExchange - for data exchange and validation
class CDataExchange
{
// Attributes
public:
BOOL m_bSaveAndValidate; // TRUE => save and validate data
CWnd* m_pDlgWnd; // container usually a dialog

// Operations (for implementors of DDX and DDV procs)
HWND PrepareCtrl(int nIDC);
HWND PrepareEditCtrl(int nIDC);
void Fail(); // will throw exception

CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate);

#ifndef _AFX_NO_OCC_SUPPORT
COleControlSite* PrepareOleCtrl(int nIDC); // for OLE controls in dialog
#endif

// Implementation
UINT m_idLastControl; // last control used (for validation)
BOOL m_bEditLastControl; // last control was an edit item
};