BUG: UNICODE Build of Default ATL Project with Performance Monitor Support Fails (320485)



The information in this article applies to:

  • Microsoft Visual C++ .NET (2002)
  • Microsoft Visual C++ .NET (2003)

This article was previously published under Q320485

SYMPTOMS

When you add an ATL Performance Monitor object to a default ATL project that uses attributes, and the character set of the project is changed to Unicode, the build fails and you receive error C2440 as follows:
'return' : cannot convert from 'char [20]' to 'LPCTSTR'

CAUSE

The attribute provider does not accept a Unicode string, either as _T("xxx") or _L("xxx"), and the Performance Monitor macros do not always convert a string to Unicode.

RESOLUTION


To work around this problem, do not to use attributes for a Unicode build of this kind of project. You must redefine three macros if you want to use these attributes. To avoid changing the atlperf.h header, redefine these macros in the header file for the Performance Monitor object of the project, as follows:

The BEGIN_PERF_MAP macro defines a function that is named GetAppName() that directly returns the AppName parameter that is passed to the macro. To work around this problem, return a created CString object instead, so that the following line:
return AppName;
				
is returned as follows:
return CString(AppName);
				
The following is the full redefinition for BEGIN_PERF_MAP, together with the #undef for the original code for your reference.
NOTE: However, if you copy and paste from this article, formatting problems may cause compile errors. Microsoft recommends that you copy these macros from atlperf.h, paste them into your source file, and then make the changes as suggested.

#undef BEGIN_PERF_MAP

#define BEGIN_PERF_MAP(AppName) \ 
	private: \ 
		LPCTSTR GetAppName() const throw() { return CString(AppName); } \ 
		HRESULT CreateMap(WORD wLanguage, HINSTANCE hResInstance, UINT* pSampleRes = NULL) throw() \ 
		{ \ 
			CPerfMon* pPerf = this; \ 
			(void)pPerf; \ 
			wLanguage; \ 
			hResInstance; \ 
			if (pSampleRes) \ 
				*pSampleRes = 0; \ 
			CString strName; \ 
			CString strHelp; \ 
			HRESULT hr; \ 
			(void)hr; \ 
			ClearMap();
				
The DECLARE_PERF_OBJECT_EX macro uses the raw namestring and helpstring objects that are passed to the macro. Convert these to Unicode by wrapping them in CString objects. The lines that read as follows:
strName = (LPCTSTR) namestring; \ 
strHelp = (LPCTSTR) helpstring; \ 
				
must be changed to the following:
strName = CString(namestring); \ 
strHelp = CString(helpstring); \ 
				
The following is the complete DECLARE_PERF_OBJECT_EX macro:
#undef DECLARE_PERF_OBJECT_EX

#define DECLARE_PERF_OBJECT_EX(dwObjectId, namestring, helpstring, detail, instanceless, structsize, maxinstnamelen, defcounter) \ 
		static HRESULT RegisterObject(CPerfMon* pPerf, WORD wLanguage, HINSTANCE hResInstance, UINT* pSampleRes) throw() \ 
		{ \ 
			CString strName; \ 
			CString strHelp; \ 
			HRESULT hr; \ 
			_ATLTRY \ 
			{ \ 
				__pragma(warning(push)); \ 
				__pragma(warning(disable: 4127)); \ 
				if (IS_INTRESOURCE(namestring)) \ 
				{ \ 
					ATLASSERT(IS_INTRESOURCE(helpstring)); \ 
					if (pSampleRes) \ 
						*pSampleRes = (UINT) (UINT_PTR) namestring; \ 
					if (hResInstance && !strName.LoadString(hResInstance, (UINT) (UINT_PTR) namestring, wLanguage)) \ 
						return E_FAIL; \ 
					if (hResInstance && !strHelp.LoadString(hResInstance, (UINT) (UINT_PTR) helpstring, wLanguage)) \ 
						return E_FAIL; \ 
				} \ 
				else \ 
				{ \ 
					ATLASSERT(!IS_INTRESOURCE(helpstring)); \ 
					strName = CString(namestring); \ 
					strHelp = CString(helpstring); \ 
				} \ 
				__pragma(warning(pop)); \ 
			} \ 
			_ATLCATCHALL() \ 
			{ \ 
				return E_FAIL; \ 
			} \ 
			hr = pPerf->AddObjectDefinition(dwObjectId, strName, strHelp, detail, defcounter, instanceless, (ULONG) structsize, maxinstnamelen); \ 
			if (FAILED(hr)) \ 
				return hr; \ 
			return S_OK; \ 
		} \ 
		/* NOTE: put a semicolon after your call to DECLARE_PERF_OBJECT*(...) */ \ 
		/* You must have this for the code wizards to parse things properly */ \ 
		static const DWORD kObjectId = dwObjectId
				
The DEFINE_COUNTER_EX macro is similar to DECLARE_PERF_OBJECT_EX, and the workaround is the same. The following is the complete macro:
#undef DEFINE_COUNTER_EX

#define DEFINE_COUNTER_EX(member, dwCounterId, namestring, helpstring, detail, countertype, maxcountersize, defscale) \ 
			CAssertValidField< (countertype) & ATLPERF_SIZE_MASK >::AssertValidFieldType( &_PerfCounterClass::member ); \ 
			_ATLTRY \ 
			{ \ 
				__pragma(warning(push)); \ 
				__pragma(warning(disable: 4127)); \ 
				if (IS_INTRESOURCE(namestring)) \ 
				{ \ 
					ATLASSERT(IS_INTRESOURCE(helpstring)); \ 
					if (hResInstance && !strName.LoadString(hResInstance, (UINT) (UINT_PTR) namestring, wLanguage)) \ 
						return E_FAIL; \ 
					if (hResInstance && !strHelp.LoadString(hResInstance, (UINT) (UINT_PTR) helpstring, wLanguage)) \ 
						return E_FAIL; \ 
				} \ 
				else \ 
				{ \ 
					ATLASSERT(!IS_INTRESOURCE(helpstring)); \ 
					strName = CString(namestring); \ 
					strHelp = CString(helpstring); \ 
				} \ 
				__pragma(warning(pop)); \ 
			} \ 
			_ATLCATCHALL() \ 
			{ \ 
				return E_FAIL; \ 
			} \ 
			hr = pPerf->AddCounterDefinition(dwCounterId, strName, strHelp, detail, countertype, maxcountersize, (ULONG) offsetof(_PerfCounterClass, member), defscale); \ 
			if (FAILED(hr)) \ 
				return hr;
				

STATUS

Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.

MORE INFORMATION

Steps to Reproduce the Behavior

  1. Create a default ATL application. By default, this uses attributed code and the MBCS character set.
  2. Add an instance of the ATL Performance Monitor Object.
  3. Build the project. Notice that it builds correctly.
  4. In Solution Explorer, right-click the project, and then click Properties .
  5. Under Configuration Properties, click General, and then in the right pane under Project Defaults, change the Character Set from Use Multi-Byte Character Set to Use Unicode Character Set.
  6. Rebuild the project. Note that it fails with error C2440.

Modification Type:MinorLast Reviewed:4/29/2003
Keywords:kbbug kbpending kbUnicode KB320485