MFC 프로젝트 버전 일괄 변경 툴

MFC 프로젝트가 여러개 일때, 버전 변경을 일일이 수정하는 것이 번거롭습니다.

이럴때, 버전일 일괄적으로 변경해 주는 툴입니다.


원리는, 설정 파일에 리소스파일 경로를 넣어둔 목록을 만들고

툴에서 파일 목록을 읽어서 아규먼트로 받은 버전으로 일괄 치환 합니다.

Visual Studio 2015 C# ( .NET Framework 4.5.2 ) 기반에서 코딩해 봤습니다.


자세한 내용은 github에 올린 내용으로 대체 합니다.

( https://github.com/lmk/changeVer )


changeVer

All change FileVersion/ProductVersion on MFC resource files

Enveroment

  • Virsual Studio 2015
  • C#

Help

Usage>
$ changeVer.exe -f FileVersion -p ProductVersion -c ConfigFilename
ex) $ changeVer.exe -f 2.1.6.33 -p 2.1.0.0 -c resources.txt
ex) $ changeVer.exe -s -c resources.txt

Options>
        -f[F]: After File version
        -p[P]: After Product version
        -c[C]: Config filename
        -s[S]: Show Current version

How to use

  1. Make list file
$ type resources.txt
D:\Source\server\server.rc
D:\Source\client\client.rc
D:\Source\client2\client2.rc
D:\Source\client3\client3.rc
  1. Execute tool
$ changeVer -f 2.1.6.34 -p 2.1.0.0 -c resources.txt
OK:     D:\Source\server\server.rc
OK:     D:\Source\client\client.rc
OK:     D:\Source\client2\client2.rc
OK:     D:\Source\client3\client3.rc

$ changeVer -s -c resources.txt
D:\Source\server\server.rc:
         FILEVERSION 2,1,6,34
         PRODUCTVERSION 2,1,0,0
                    VALUE "FileVersion", "2, 1, 6, 34"
                    VALUE "ProductVersion", "2, 1, 0, 0"
D:\Source\client\client.rc:
         FILEVERSION 2,1,6,34
         PRODUCTVERSION 2,1,0,0
                    VALUE "FileVersion", "2, 1, 6, 34"
                    VALUE "ProductVersion", "2, 1, 0, 0"
D:\Source\client2\client2.rc:
         FILEVERSION 2,1,6,34
         PRODUCTVERSION 2,1,0,0
                    VALUE "FileVersion", "2, 1, 6, 34"
                    VALUE "ProductVersion", "2, 1, 0, 0"
D:\Source\client3\client3.rc:
         FILEVERSION 2,1,6,34
         PRODUCTVERSION 2,1,0,0
                    VALUE "FileVersion", "2, 1, 6, 34"
                    VALUE "ProductVersion", "2, 1, 0, 0"


Visual Leak Detector 를 사용하여 Leak을 감지하는 방법.

Visual Leak Detector 를 사용하여 Leak을 감지하는 방법.

사용조건

  • Visual Studio 2008/2010/2012/2013/2015

사용방법 (VS2010 기준으로 설명합니다.)

  • Visual Sutdio 설정을 합니다.
    • VS를 실행하고, 적용할 솔루션을 엽니다.
    • VS를 실행해서 보기 > 다른 창 > Property Manager 창을 엽니다.
    • Property Manager 창에서 아무 프로젝트에서든 선택하고 적용하고자 하는 구성에서 "Microsfot.Cpp.x64.user"를 더블클릭 합니다.
    • 속성 페이지가 열리면, 왼쪽 메뉴 트리에서 VC++ Directorys > Include Directories에 VLD의 include 경로를 추가합니다.
      • 저는 "C:\Program Files (x86)\Visual Leak Detector\include" 여기에 설치되어 있네요.
    • 같은 창의 Libaray Directories에 VLD의 lib 경로를 추가합니다.
      • 저는 64 비트라서 "C:\Program Files (x86)\Visual Leak Detector\lib\Win64"를 추가 했네요.
      • 한번만 하면 솔루션 아래 여러개의 프로젝트가 있는경우 모두 반영됩니다.
  • stdafx.h 파일에 아래 코드를 추가합니다. 
    #include <vld.h>
    
    • 공식 Document에는 stdafx.h 호출후에 넣으라고 하는데, stdafx.h 파일에 넣어도 잘 돌아갑니다.
    • 공통되는 헤더 파일이 있으면 거기에 추가하면 됩니다.
  • 이제 VS에서 F7로 컴파일하고, F5로 실행합니다.
  • 프로그램이 정상 종료되면, 출력 > 디버그 창에 Leak 정보가 출력 됩니다.
    • VS의 디버그 중지로 하면 안됩니다.

참고

  • 직접 실행하는 경우.
    • 컴파일된 실행 파일을 VS에서 실행하지 않고 탐색기(또는 CMD)에서 직접 실행 하는 경우 아래 파일이 실행 파일과 같은 경로에 있어야 합니다.
      • dbghelp.dll
      • Microsoft.DTfW.DHL.manifest
      • vld_x64.dll
    • 해당 파일은 64비트의 경우 C:\Program Files (x86)\Visual Leak Detector\bin\Win64 에 있습니다.


MFC용 ADO클래스 (버전업)

미루고 미루던 작업을 디드어 했습니다. ㅎㅎ
하나의 커넥션을 끊지 않고, 계속 활용하도록 수정했습니다.

원본보기

[CAdoDB.h] =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

[CODE type=c++]
#ifndef __ADODB_HEADER_B694B3CA_2D77_4AC8_95E3_DBA44C3DB329__

#define __ADODB_HEADER_B694B3CA_2D77_4AC8_95E3_DBA44C3DB329__
/*******************************************************************
*
* MDB를 OLE로 연결한다.
* 하나의 connection로 모든 처리를 한다.
*
* ** 반드시 클래스 사용전 CoInitialize()함수를 호출해
* COM Object사용을 초기화 해야 한다.
*
*
* Usage:
* CAdoDB db;
* db.Init( "test.mdb" );
* db.Open( sql );
* while( db.IsEOF() == FALSE )
* {
* value = db.GetValueString( "field" );
* db.MoveNext();
* }
* db.Close();
*
* db.Excute( sql );
*
*
*
* 2006-07-21
*
* By newtype / newtype@newtype.pe.kr / http://newtype.pe.kr/
*
/****************************************************************/

#pragma once

#import "msado26.tlb" rename ("EOF","adoEOF") no_namespace
/*
#import "C:\program files\common files\system\ado\msado15.dll" \
no_namespace \
rename("EOF", "adoEOF")
*/


class CAdoDB
{
public:
CAdoDB(void);
~CAdoDB(void);
BOOL Init(CString Filename);
BOOL Connection(CString Filename = _T(""));
BOOL CloseConneciton();
BOOL Open(CString sql, CString Filename = _T(""));
BOOL Close();
BOOL IsEOF();
CString GetValueString(CString Field);
int GetValueInt(CString Field);
BOOL MoveNext();
BOOL Excute(CString sql, CString Filename = _T(""));

_ConnectionPtr GetConnection();
void AttachConnection( _ConnectionPtr conn );
void DetachConnection();


private:
_ConnectionPtr m_pConn;
_RecordsetPtr m_pRs;
CString m_strConnection;
BOOL m_IsOpen;
BOOL m_IsConn;

BOOL FileCmp( CString Filename );
};


#endif // __ADODB_HEADER_B694B3CA_2D77_4AC8_95E3_DBA44C3DB329__

[/HTML][/CODE]
[CAdoDB.cpp] =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
[CODE type=c++]
/* AdoDB.cpp */
#include "adodb.h"

/*******************************************************************
* OLE DB connection string
*******************************************************************/
const TCHAR *oleString = _T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=");


/********************************************************************
* 기본 생성자
*
* connection객체와 recordset객체를 생성한다.
********************************************************************/
CAdoDB::CAdoDB(void)
{
if ( FAILED(m_pConn.CreateInstance(__uuidof(::Connection))) )
AfxMessageBox(_T("Connection COM 객체 생성에 실패 하였습니다."), MB_OK, NULL);

if ( FAILED(m_pRs.CreateInstance(__uuidof(Recordset))) )
AfxMessageBox(_T("Recordset COM 객체 생성에 실패 하였습니다."), MB_OK, NULL);

m_strConnection = _T("");
m_IsOpen = FALSE;
}



/********************************************************************
* 기본 소멸자
*
* connection객체와 recordset객체를 close하고 반환 한다.
********************************************************************/
CAdoDB::~CAdoDB(void)
{
Close();
CloseConneciton();

m_pRs.Release();
m_pConn.Release();
}



/********************************************************************
* 연결 문자열을 만든다.
* 기존 파일명과 틀릴때에만 connection을 닫고, 연결문자열을 만든다.
*
* return : BOOL : TRUE | FALSE
*
* parameter :
* [in] CString Filename : MDB 파일명
********************************************************************/
BOOL CAdoDB::Init(CString Filename)
{
if ( m_pRs==NULL || m_pConn==NULL )
return FALSE;

if ( Filename.IsEmpty() )
return FALSE;

if ( FileCmp( Filename ) == TRUE )
return TRUE;

Close();

m_strConnection = oleString + Filename;

return FALSE;
}



/********************************************************************
* DB에 연결한다.
* 기존 connection이 있으면 유지 한다.
*
* return : BOOL : TRUE | FALSE
*
* parameter :
* [in] CString Filename : MDB 파일명
********************************************************************/
BOOL CAdoDB::Connection(CString Filename)
{
if ( Filename.IsEmpty() && m_strConnection.IsEmpty() )
return FALSE;

if ( Filename.IsEmpty() == FALSE )
{
Init( Filename );
}

if ( m_pConn == NULL )
return FALSE;

if ( m_IsConn == TRUE )
return TRUE;

try
{
m_pConn->Open( (_bstr_t)m_strConnection, "","" ,adConnectUnspecified );

m_IsConn = TRUE;
}
catch(_com_error &err)
{

m_IsConn = FALSE;

TRACE("Error : %s : DB CONNECTION FAIL\n", err.Description() );
}

return m_IsConn;
}



/********************************************************************
* 레코드 셋을 오픈 한다.
* 기존 recordset이 있으면 close 한다.
*
* return : BOOL : TRUE | FALSE
*
* parameter :
* [in] CString sql : SQL Query SELECT 구문
* [in] CString Filename : MDB 파일명
********************************************************************/
BOOL CAdoDB::Open(CString sql, CString Filename)
{
HRESULT hr;
VARIANT conn;

if ( m_pConn == NULL || m_pRs == NULL )
return FALSE;

if ( Filename.IsEmpty() == FALSE )
{
Connection(Filename);
}
else
{
Connection();
}

if ( m_IsConn == FALSE )
return FALSE;

if ( m_IsOpen == TRUE )
Close();

try
{
//TRACE( sql );
TRACE( "\n" );

VariantInit(&conn);
conn.pdispVal=m_pConn;
conn.vt=VT_DISPATCH;

hr = m_pRs->Open( (LPTSTR)(LPCTSTR)sql, conn,
adOpenForwardOnly, adLockOptimistic, adCmdUnknown);
}
catch(_com_error &e)
{
TRACE("\tDescription: %s\n", (LPCTSTR) e.Description());
}
catch(...)
{
TRACE("*** Unhandled Exception ***\n");
}

if ( SUCCEEDED( hr ) )
m_IsOpen = TRUE;

if ( sql.TrimLeft().Left(7).CollateNoCase( "SELECT " ) )
{
m_IsOpen = FALSE;
}

return SUCCEEDED( hr );
}



/********************************************************************
* 더 이상 튜플이 없으면 TRUE를 반환 한다.
*
* return : BOOL : TRUE | FALSE
*
* parameter : None
********************************************************************/
BOOL CAdoDB::IsEOF()
{
if ( !m_IsOpen )
return TRUE;

return (BOOL)m_pRs->GetadoEOF();
}



/********************************************************************
* 현재 튜플의 Field에 해당하는 Instance를 문자열 형태로 얻는다.
*
* return : CString : Value
*
* parameter :
* [in] CString Field : 값을 가져올 Attribute Name
********************************************************************/
CString CAdoDB::GetValueString(CString Field)
{
if ( !m_IsOpen || IsEOF() )
return "";

_variant_t vt = m_pRs->GetCollect( (LPTSTR)(LPCTSTR)Field );

return ( (vt.vt == VT_NULL)? ("") : vt );
}


/********************************************************************
* 현재 튜플의 Field에 해당하는 Instance를 정수 형태로 얻는다.
*
* return : int : Value
*
* parameter :
* [in] CString Field : 값을 가져올 Attribute Name
********************************************************************/
int CAdoDB::GetValueInt(CString Field)
{
return atoi( GetValueString(Field) );
}


/********************************************************************
* 다음 튜플로 이동한다.
*
* return : BOOL : TRUE | FALSE
*
* parameter : None
********************************************************************/
BOOL CAdoDB::MoveNext()
{
if ( !m_IsOpen )
return FALSE;

return SUCCEEDED( m_pRs->MoveNext() );
}


/********************************************************************
* recordset을 닫는다.
*
* return : BOOL : TRUE | FALSE
*
* parameter : None
********************************************************************/
BOOL CAdoDB::Close()
{
if ( m_IsOpen == FALSE )
return TRUE;

m_IsOpen = FALSE;

return SUCCEEDED( m_pRs->Close() );
}


/********************************************************************
* connection을 끊는다.
*
* return : BOOL : TRUE | FALSE
*
* parameter : None
********************************************************************/
BOOL CAdoDB::CloseConneciton()
{
if ( m_IsConn == FALSE )
return TRUE;

m_IsConn = FALSE;

return SUCCEEDED( m_pConn->Close() );
}


/********************************************************************
* DML 쿼리를 실행한다.
* connection이 없으면 연결한다.
*
* return : BOOL : TRUE | FALSE
*
* parameter :
* [in] CString sql : SQL Query DML 구문
* [in] CString Filename : MDB 파일명
********************************************************************/
BOOL CAdoDB::Excute(CString sql, CString Filename /*= _T("")*/)
{
if ( m_pConn == NULL )
return FALSE;

if ( Filename.IsEmpty() == FALSE )
{
Connection(Filename);
}
else
{
Connection();
}

if ( m_IsConn == FALSE )
return FALSE;

try
{
TRACE( sql );
TRACE( "\n" );

m_pConn->Execute( (_bstr_t)sql, NULL, adCmdText );
}
catch(_com_error &err)
{
TRACE("Error : %s : DB Execute FAIL\n", err.Description() );
return FALSE;

}

return TRUE;
}



/********************************************************************
* 기존에 연결 파일명과 비교해 틀리면 FALSE를 반환한다.
* (개선의 여지가 있음.
* 파일명에 경로가 포함될 경우 정확한 비교가 되지 않음 )
*
* return : BOOL : TRUE | FALSE
*
* parameter :
* [in] CString Filename : MDB 파일명
********************************************************************/
BOOL CAdoDB::FileCmp( CString Filename )
{
if ( m_IsConn == FALSE )
return FALSE;

return m_strConnection.CompareNoCase( oleString + Filename ) == 0;
}


/********************************************************************
* 현재 connection을 얻는다.
*
* return : _ConnectionPtr
*
* parameter : None
* [in] CString Filename : MDB 파일명
********************************************************************/
_ConnectionPtr CAdoDB::GetConnection()
{
return m_pConn;
}



/********************************************************************
* connection을 새로 Attach한다.
* 이미 사용중인 connection 및 Recordset는 닫는다.
*
* return : None
*
* parameter :
* [in] _ConnectionPtr conn : 사용할 Connection Object
********************************************************************/
void CAdoDB::AttachConnection( _ConnectionPtr conn )
{
if ( m_IsOpen == TRUE )
Close();

if ( m_IsConn == TRUE )
{
CloseConneciton();
m_pConn.Release();
}

m_pConn = conn;
m_IsConn = TRUE;
}

/********************************************************************
* connection을 새로 Attach한다.
* 이미 사용중인 connection 및 Recordset는 닫는다.
*
* return : None
*
* parameter :
* [in] _ConnectionPtr conn : 사용할 Connection Object
********************************************************************/
void CAdoDB::DetachConnection()
{
if ( m_IsConn == FALSE )
return;

m_pConn = NULL;
m_IsConn = FALSE;
}
[/HTML][/CODE]

항상 위 효과 내기

[CODE type=c++]
// CDialog를 상속받아
// OnCancel()과 DoModal()을 오버라이딩 한다.



// 닫아도 닫히지 않는다 숨길뿐...
void CMyDlg::OnCancel()
{
       ShowWindow( SW_HIDE );
       SetWindowPos( &wndNoTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
       AfxGetMainWnd()->SetFocus();
}

// Dlg를 모달로 연다
INT_PTR CMyDlg::DoModal()
{
       ShowWindow( SW_SHOW );
       SetWindowPos( &wndTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);

       return 0;
}



// 생성한 후
CMyDlg *pMyDlg = new CMyDlg();
pMyDlg->Create( IDD_DIALOG_MY, CWnd::FromHandle(::GetDesktopWindow()) );
pMyDlg->DoModal();



// 숨겨져 있으면 보여준다.

if ( m_pMyDlg->ShowWindow( SW_SHOWNA ) == SW_HIDE )
        pMyDlg->DoModal();

[/CODE]

문자열 분할시, 특정 번째 문자열 얻기

제목이 어렵네요.. ㅡㅡ;
소스 보시면 어려운것 없으니 금방 이해 하실겁니다.
좋은 시간 되세요..^^


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

//--------------------------------------------------------------------------//
//Fuction        : Splite
//Parameter : LPTSTR lpszDest, LPCTSTR lpcszSorc, TCHAR cSep, int nIndex
//Return        : LPCTSTR
//Note                : lpcszSorc를 cSep로 나누어서 nIndex번째 문자열을 lpszDest에 넣는다.
//--------------------------------------------------------------------------//
LPCTSTR Splite( LPTSTR lpszDest, LPCTSTR lpcszSorc, TCHAR cSep, int nIndex)
{
       int i, j, k, l;
       int count;

       count = (int)strlen(lpcszSorc);
       for (i=j=k=l=0; i<count && l<=nIndex; i++)
       {
               if ( *(lpcszSorc+i) == cSep )
               {
                       k=i;
                       strncpy( lpszDest, lpcszSorc+j, k-j );
                       lpszDest[k-j] = 0;
                       j=k+1;
                       l++;
               }
       }

       return lpszDest;
}

CListCtrl에서 깜박임이 덜한 SetItemText

//--------------------------------------------------------------------------//
//Fuction        : UpdateListItem
//Parameter : CListCtrl* pList, int nIndex, int nSubitem, LPCTSTR lpszText
//Return        : BOOL
//Note                : pList의 nIndex, nSubitem의 텍스트를 lpszText로 바꾼다.
//            같은 값이 벌써 들어가 있으면 FALSE, 새로운 값으로 대체 하면 TRUE
//--------------------------------------------------------------------------//
BOOL UpdateListItem(CListCtrl* pList, int nIndex, int nSubitem, LPCTSTR lpszText)
{

   CString strText = pList->GetItemText( nIndex, nSubitem );
       if ( strText.CompareNoCase( lpszText ) )
       {
       pList->SetItemText( nIndex, nSubitem, lpszText );
               return TRUE;
       }

       return FALSE;
}

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

원리는 간단합니다.
쓰려는 Text와 쓰여져있는 Text를 비교해 틀릴때만 써주는거죠.
각 Item별로 해줘도 되지만..
함수하나로 만들어 봤습니다.

좋은 시간 되세요.

CComboBox에서 ItemData와 ItemText찾기

//--------------------------------------------------------------------------//
//Fuction        : FindComboString
//Parameter : CComboBox* pCombo, DWORD dwSearch, CString *pstrDest
//Return        : int
//Note                : pCombo에서 Callback Item중 dwSearch를 찾아
//            Index를 리턴하고, 해당 Index의 List Text를 pstrDest로 넘긴다.
//--------------------------------------------------------------------------//
int FindComboString(CComboBox* pCombo, DWORD dwSearch, CString *pstrDest)
{
       for(int i=pCombo->GetCount()-1; i>=0; i--)
       {
               if ( pCombo->GetItemData( i ) == dwSearch )
               {
                       pCombo->GetLBText( i, *pstrDest );
           return i;
               }
       }

       return -1;
}



//--------------------------------------------------------------------------//
//Fuction        : FindComboString
//Parameter : CComboBox* pCombo, CString strSearch, DWORD* pdwDest
//Return        : int
//Note                : pCombo에서 List Text중 strSearch를 찾아
//            Index를 리턴하고, 해당 Index의 Callback Item을 pdwDest로 넘긴다
//--------------------------------------------------------------------------//
int FindComboDword(CComboBox* pCombo, CString strSearch, DWORD* pdwDest)
{
       CString str="";
       for(int i=pCombo->GetCount()-1; i>=0; i--)
       {
               pCombo->GetLBText( i, str );
               if ( !str.Compare( strSearch ) )
               {
           *pdwDest = (DWORD)pCombo->GetItemData( i );
                       return i;
               }
       }
       return -1;
}

전체경로를 받아 파일이름을 제외한 경로만 구하기

//-------------------------------------------------------------------------------------//
// Function                        : OnlyDirecory
// Parameter                : CString strMoney
// Return                        : LPTSTR strFull                        
// Note                                : 인자로 받은 전체파일이름의 파일이 있는 경로를 리턴한다.
//-------------------------------------------------------------------------------------//
LPTSTR OnlyDirecory(LPTSTR strFull)
{
       TCHAR* pPos = strFull + strlen( strFull );

       while ( strncmp( pPos, "\\", 1 ) && pPos != strFull )
               pPos--;
       *pPos = '\0';

       return strFull;
}

CTreeCtrl에서 ItemData 찾기 (자식노드 포함)

//-------------------------------------------------------------------------------------//
// Function         : FindTreeData
// Parameter        : HTREEITEM hItem, DWORD dwData
// Return           : HTREEITEM
// Note             : hItem이하 모든 모든 노드에서 dwData를 찾는다.
//-------------------------------------------------------------------------------------//
HTREEITEM FindTreeData(CTreeCtrl* pTree, HTREEITEM hItem, DWORD dwData)
{
       HTREEITEM hitemFind, hItemChile, hItemSibling;
       hitemFind = hItemChile = hItemSibling = NULL;


       if ( pTree->GetItemData( hItem ) == dwData )
       {
               hitemFind = hItem;
       }
       else
       {
               // 자식 노드를 찾는다.
               hItemChile = pTree->GetChildItem( hItem );
               if ( hItemChile )
               {
                       hitemFind = FindTreeData( pTree, hItemChile, dwData );

               }

               // 형제노드를 찾는다.
               hItemSibling = pTree->GetNextSiblingItem( hItem );
               if ( hitemFind==NULL && hItemSibling )
               {
                       hitemFind = FindTreeData( pTree, hItemSibling, dwData );
               }
       }
   
       return hitemFind;
}

MFC에서 .NET스타일의 메뉴를 사용해 보자


NewMenu


The NewMenu is published on
CodeProject
. There you will find the discussion board.






This is a prerelease of NewMenu 1.13 (will be published shortly)





Released NewMenu 1.11 (Published on codeproject)
















STYLE_XPSTYLE_ICYSTYLE_ORIGINAL


Introduction


This class, CNewMenu, implements owner drawn menus derived
from the CMenu class. The coal of this class is to have menus with
the same look as the new menu from the Microsoft products. In the other hand an
easy use of a new menu class with Icons and Title support.
I was inspired from the great class BCMenu from Brent Corkum. I took some ideas
and almost every function, but reimplemented almost the whole code and expanded
it with some other nice functions. It was a hard work to change the look of the
whole menu that belongs to an application, changing border and adding shading.
Furthermore, it was tricky changing all menus under windows 2000 or system menu
under Windows XP when you are using themes. Over all, I hope it's now easy
enough for you to use this class in new applications.


This new menu works under Windows 95/98/Me and Windows NT4.0/2000/XP. When you
like to use Chinese or some other special characters, you have to compile your
project with Unicode. But keep in mind you must have installed the Unicode
libraries with the Visual studio otherwise you get a linking error.


Additional when you want that your program works on all platforms, you have to
compile it with the Visual Studio 6.0. I did not find out why the drawing don't
work fine when it is compiled with the Visual Studio 7.0 under Windows NT 4.0.
Furthermore when you do not like the flat border you can use the
STYLE_XXX_NOBORDER and then menus will have the standard menu border from the
system.


Nice Title in Menus











Oh yes, its also possible to add a title to menu. You can add a title on the
top or on the left side of a menu. You can have a gradient or a solid color as
background. With the function SetMenuTitle you can add or remove a
title from a menu. The gradient drawing works also on Windows 95.
// Set a title to the system menu 
m_SystemNewMenu.SetMenuTitle(_T("New Menu Sample"),
MFT_GRADIENT|MFT_LINE|MFT_CENTER);

New icons effects brightening, glooming and graying








Under menu style XP you have the possibility to enable or
disable the additional drawing style to icons. Disabled icons become a grayed
look and non selected icons become a gloom look. You can see the
different between those drawing styles in the sample.

How to use the CNewMenu in a project




  1. The easiest way is to replace all CDialog, CFrameWnd, CMDIFrameWnd,
    CMDIChildWnd, CMiniFrameWnd
    and CMultiDocTemplate through
    the new classes CNewDialog, CNewFrameWnd,
    CNewMDIFrameWnd, CNewMDIChildWnd, CNewMiniFrameWnd

    and CNewMultiDocTemplate
    in your project.

  2. Set in the application, derived from CWinApp, the menustyle just
    before you create the frame or a dialog window in the function InitInstance.
    // Set drawing style of all menus to XP-Mode or STYLE_ORIGINAL
    CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);


  3. When you have a multi document template, add the loading code for menu icons
    just after creating the template.
    CNewMultiDocTemplate* pDocTemplate; 
    pDocTemplate = new CNewMultiDocTemplate(IDR_MDINEWTYPE,
    RUNTIME_CLASS(CMDINewMenuDoc),
    // custom MDI child frame
    RUNTIME_CLASS(CChildFrame),
    RUNTIME_CLASS(CMDINewMenuView));

    AddDocTemplate(pDocTemplate);


    // Loading toolbar icons into the menu
    pDocTemplate->m_NewMenuShared.LoadToolBar(IDR_DRAWBAR);



  4. Insert at the end of OnCreate function of the MainFrame the loading code for
    the icons. For example:
    m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);


  5. Finally don't forget to include the definition from the "NewMenu.h" in stdafx.h
    and add NewMenu.cpp to the project.


  6. Now you are ready to use the class.

How to use the CNewMenu in a dialog-Application




  1. Set in the application the menustyle before you create the dialog in the
    function InitInstance.
    // Set drawing style of all menus to XP-Mode
    CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);


  2. Replace all CDialog to CNewDialog in your Project.
    For example base class from CAboutDlg.


  3. For loading bitmaps in to the menu the best place is in OnInitDialog
    function after calling the base class function.
    // CDialogDlg could be your Dialog.
    BOOL CDialogDlg::OnInitDialog()
    {
    // Call the baseclass
    CNewDialog::OnInitDialog();


    // Load icons to the menu


    m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);
    }


  4. Don't forget to include the the definition from the "NewMenu.h" in stdafx.h and
    add NewMenu.cpp to the project.



How to use the CNewMenu in a special frame window




  1. Normally you should overwrite measureitem, menuchar and
    initmenupopup. For this purpose I use the CNewFrame template
    class. So you can replace 'CYourSpecialBase' with 'CNewFrame'
    in your project.


  2. But don't change it in IMPLEMENT_DYNAMIC, IMPLEMENT_SERIAL
    or IMPLEMENT_DYNACREATE the base class to the new one.
    IMPLEMENT_DYNAMIC(CYourNewSpecialFrame, CYourSpecialBase)


  3. Don't forget to change the baseclass in BEGIN_MESSAGE_MAP.
    BEGIN_MESSAGE_MAP(CYourNewSpecialFrame, CNewFrame) 
    //{{AFX_MSG_MAP(CYourNewSpecialFrame)
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()


  4. For loading bitmaps in to the menu, you can add the code in OnCreate
    function.


  5. Finally don't forget to include the the definition from the "NewMenu.h" in
    stdafx.h and add NewMenu.cpp to the project.



How to use the CNewMenu in a MDI-Application




  1. Change in the the application class in the BOOL InitInstance() function
    the CMultiDocTemplate to CNewMultiDocTemplate
    CNewMultiDocTemplate* pDocTemplate; 
    pDocTemplate = new CNewMultiDocTemplate(IDR_MDINEWTYPE,
    RUNTIME_CLASS(CMDINewMenuDoc),
    // custom MDI child frame
    RUNTIME_CLASS(CChildFrame),
    RUNTIME_CLASS(CMDINewMenuView));
    AddDocTemplate(pDocTemplate);


  2. Now insert the following code for loading additional bitmaps from a toolbar
    into the menus.
    // Loading toolbar icons into the menu 
    pDocTemplate->m_NewMenuShared.LoadToolBar(IDR_DRAWBAR);


    // Set drawing style of all menus to XP-Mode
    CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);


  3. Change the base class from the main frame class (CMainFrame) from
    CMDIFrameWnd
    to CNewMDIFrame in definition and
    implementation file.
      // Change the base class in the following makro
    IMPLEMENT_DYNAMIC(CMainFrame, CNewMDIFrameWnd)

    // don't forget to change here the baseclass
    BEGIN_MESSAGE_MAP(CMainFrame, CNewMDIFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
    // NOTE - the ClassWizard will add and remove mapping macros here.
    // DO NOT EDIT what you see in these blocks of generated code !
    ON_WM_CREATE()
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()


  4. Add at the end of int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    for loading bitmap in the default menu.
    m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);


  5. Finally change the baseclass of the child frame from CMDIChildWnd to
    CNewMDIChildWnd. Do the same for all derived class CDialog
    to the base class of CNewDialog.


  6. Don't forget to include the the definition from the "NewMenu.h" in stdafx.h and
    add NewMenu.cpp to the project.



How to use the CNewMenu in a SDI-Application




  1. Set in the application the menustyle before you create the dialog in the
    function InitInstance.
    // Set drawing style of all menus to XP-Mode
    CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);


  2. Replace all CDialog to CNewDialog in your Project.


  3. Change the base class from the main frame class (CMainFrame) from
    CFrameWnd
    to CNewFrameWnd
    in definition and implementation file.

  4. Add at the end of int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    for loading bitmap in the default menu.
    m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);


  5. Don't forget to include the the definition from the "NewMenu.h" in stdafx.h and
    add NewMenu.cpp to the project.



How to replace system menu in system dialogs


Sometimes it would be pretty to change the menu in MessageBox or in
CFileDialog too. But unfortunately a MessageBox has no base class for
owerwriting. An other problem has the file dialog, there are at least two
dialog and the CFileDialog does only subclass the child dialog of the shown
FileDialog. For those dialog, and some more, I added a special subclassing
mechanism for our work.


// Subclass dialog with system menu 
CNewMenuHelper myHelper(NEW_MENU_DIALOG_SUBCLASS|NEW_MENU_DIALOG_SYSTEM_MENU);
AfxMessageBox(_T("You must restart the application"),MB_OK);

Even thought what can I do when I do not want to have a menu border
replacing in the next shown dialog? It is very simple! Just place the
CNewMenuHelper before your call like in the following sample.


// Subclass dialog with normal border painting 
CNewMenuHelper myHelper(NEW_MENU_DEFAULT_BORDER);
AfxMessageBox(_T("You must restart the application"),MB_OK);

Color replacing of bitmaps with 16 colors


The MFC replace some colors depended of the chosen system colors. So when you
use a black line in the bitmap, the color black will be replaced with the
system color of COLOR_BTNTEXT. Following the color mapping map. By
the way, it is the same color mapping of the toolbars.


The color RGB(0x00,0x00,0x00) will be changed to COLOR_BTNTEXT       // black
The color RGB(0x80,0x80,0x80) will be changed to COLOR_BTNSHADOW // dark gray
The color RGB(0xC0,0xC0,0xC0) will be changed to COLOR_BTNFACE // bright gray
The color RGB(0xFF,0xFF,0xFF) will be changed to COLOR_BTNHIGHLIGHT // white

Adding Icons to Menus


There are several possibilities adding icons to menu. An easy way is with a
toolbar resource. But the developer studio support only 16 color for
toolbar-bitmaps. What you can do is: first define your toolbar; then replace
the saved toolbar-bitmap with a hicolor bitmap by hand. Additional you can
define every icon in a different bitmap and assign it to a menuitem. My way was
define a bitmap like in an image list, make a helper table with the resource id
of the bitmap, size of an icon and followed by the ids of the icons. The end of
the table must be marked with an id number NULL.


// Define the table
static WORD ToolId[] =
{ IDR_NEWMENU256, // Resource id of the bitmap-icons
16, 15, // Dimension of each icon


ID_FILE_NEW, // First icon's ID
ID_FILE_OPEN, // Second icon's ID
ID_FILE_SAVE, // and so on...

NULL}; // Marker for end of table

//Load the definition into menu with transparent color
m_DefaultNewMenu.LoadToolBar(ToolId,RGB(192,192,192));

How to add an icon to a submenu


It is very simple to add or change the title of a submenu entry when you know
the menu text. The helper function ModifyODMenu can be used.


// set the icon IDB_MORE_C to the submenu "More A"
m_NewMenuShared.ModifyODMenu(0,_T("More A"),IDB_MORE_C);

Menu under Windows 2000


It was a strange thing to detect, that under Windows 2000, you have a window
menu with handle 0x10012, which is shared to all application. (I think under
Windows 98 is the menu 0x0090). I don't know why, but this is also a reason of
the specialty for some effects belonging to subclassing menu. First when you
have disabled menu effects, the menu windows are alternating created or showed.
That means, first showed menu is mostly the special menu, never created nor
destroyed only showed and disabled, and the second is normal created and
destroyed after closing. The third time, sometime, the special window is showed
again and then the next menu is created normal. For this reason, when you
subclass the special menu, you are responsible to restore all values before
unsubclassing it. Be careful when you changing some flags or styles, because
the other applications like having the standard menu. You can also receive
bluescreen when you forget to unsubclass or when changing painting areas
belongs to that menu. Really ugly!!! Second, when you enabled menu effects
every shown menu is created new from scratch. For this reason it doesn't matter
when you don't restore all flags or styles!


But almost I forgot to explain how I got the goal to subclass all menus. Well,
for first, you can't subclass the system menu class #32768 from an application;
also I was looking for other possibility. I decide to create a windows hook for
all windows messages and catching all messages for creating a window. After
checking for the window class, when I found the menu class, I subclassed it.
But, how I can subclass the special menu, which is never created? I found, that
one of the first messages, which were sending to that menu, was 0x01E2 and one
of the latest WM_SHOWWINDOW. So I caught these messages for sub-
and unsubclassing the special menu. Only between those two messages you have
access to the special window!! Why? I don't know, but be careful.


How to paint the border


Well after I got it to subclass the menu window I can draw the border by
myself by processing WM_NCPAINT message. But how is the shade
made? Just before the menu is showed on the screen I save the menu region in a
bitmap, this is a reason why it does not work when the underground has been
changed, and combine it with a shade drawing. An other way would be using
layered windows, but this will not work on all systems like windows 95/98.


System menu under Windows XP


I thought owner drawing a menu is all time easy. But on Windows XP, with
themes enabled, it is a curious thing. Especially the main frame when the
system menu were displayed. Instead of owner drawing, the menu items were drawn
by the frame. The frame had painted the windows button of the width of the menu
at the place of the menu item :-) instead of drawing it. Strange! So I found a
workaround. I increased the menu items identifier before showing the menu and
restored after closing it. So, it works under Windows 2000 and XP.


Important info for debugging the new menu under Windows 2000


You should never stop debugging when you are in middle painting of the menus,
because the special menu wouldn't be restored nor unsubclassed. Furthermore
there were a lot of strange effects. It is not forbidden, but be careful and
have a lot time for rebooting the system when it doesn't work correctly after
stopping the debugger.


History


23 January 2003 - Version 1.13




  • SetMenuText fixed.

  • Menubar color with theme fixed.

  • Added some helper for borderhandling of non CNewMenu

10 November 2002 - Version 1.11




  • Border drawing with menu animation under Win 98 corrected.

  • Border drawing / repainting when submenu closed fixed.

  • New menu style ICY.

  • Helperclasses for changing system menu for CommonDialog.

30 July 2002 - Version 1.10




  • Sharing Icons between menu for saving GDI-Resources.

  • New effects for drawing icons under STYLE_XPxx.

  • Recoloring of menu icons by changing system colors.

  • Drawing of the scroll button by extreme big menus a little bit better.

  • Mixing different icon sizes in a menu is now supported but they are not zoomed
    to the same size.

Known bugs




  • Menu border with shade is not updated when underground has been changed.

  • Menu border is drawn in a wrong size after changing the menu border style under
    windows settings. But restart the application and then it will work again fine.

  • The High Contrast Mode is not supported with all right colors.






<출처>
http://www.podetti.com/NewMenu/