출처 : 데브피아 김경진님의 글([VC++] 윈도우 버전 정확하게 얻어오는 새로운 방법)

 
기존 방식의 구현

  

지금까지 윈도우 버전을 얻어오기 위하여 주로 사용했던 방법은 바로 GetVersionEx 함수를 사용하는 것이었습니다. 하지만 Visual Studio 2013 버전에서 GetVersionEx 함수

(혹은 GetVersion 함수)를 사용하려고 하니 아래와 같은 오류를 내뱉으며 컴파일이 되질 않습니다.

컴파일 오류

  

물론 #pragma warning(disable:4996) 으로 에러가 발생하지 않게 막아놓고 사용할수야 있겠지만 이렇게 사용하기에는 찝찝한 기분을 떨쳐버릴 수가 없습니다. MS가 GetVersionEx 함수를 deprecated 시키고 컴파일 오류까지 발생시키며 사용하지 말 것을 권장한다면, 거기에는 분명히 그럴만한 이유가 있겠죠. 그렇다면 왜 지금까지 잘 사용해오던 GetVersionEx 함수를 이제와서 사용하지 말라고 하는걸까요?

 

 

호환 모드 문제

  

윈도우에서는 다음과 같이 어플리케이션을 호환 모드로 실행할 수 있게 되어있습니다.

 

 

 

 

어플리케이션을 호환 모드로 실행하였을 경우에 GetVersionEx 함수를 사용하면 현재 윈도우 버전을 얻어오는 것이 아니라 호환 모드에서 선택한 윈도우 버전을 얻어오게 됩니다. 실제로 윈도우 8에서 호환 모드를 Windows XP SP3로 설정하고 실행하니 다음과 같이 GetVersionEx 함수가 Windows XP SP3 버전(5.1.3)을 리턴하는 것을 볼 수 있습니다.

 

<그림: 호환 모드에서 GetVersionEx 함수의 오동작>

 

  

GetVersionEx 함수의 호환 모드 문제는 사실 어제 오늘의 문제가 아니라 지금까지 쭈욱 발생되어왔던 문제입니다. 일반적인 상황에서는 호환 모드를 사용할 일이 거의 없기 때문에 신경쓰지 않았을 뿐이죠. 하지만 만에 하나라도 사용자가 호환 모드를 사용한다면 GetVersionEx 함수의 결과값이 달라져서 예기치 못한 문제가 발생할 수 있습니다.

 

 

Windows 8.1 문제

  

GetVersionEx 함수는 앞서 살펴본 호환 모드 문제 뿐만 아니라 또 하나의 중대한 문제점을 가지고 있습니다. 바로 Windows 8.1 이상의 OS에서 제대로 동작하지 않는다는 것인데요, GetVersionEx 함수가 deprecated 된 이유도 이 때문이라 할 수 있습니다. 실제로 Windows 8.1 환경에서 테스트해보니 GetVersionEx 함수가 6.3.0을 리턴하는 대신 Windows 8의 버전인 6.2.0을 리턴하는 것을 볼 수 있습니다.

 

 

<그림: Windows 8.1에서 GetVersionEx 함수의 오동작>

 

 

이러한 결과가 발생하는 이유는 Windows 8.1 버전부터 GetVersionEx 함수가 매니페스트에 의존적으로 동작하도록 변경되었기 때문입니다. 이 문제를 해결하려면 어플리케이션이 Windows 8.1을 타겟팅하도록 매니페스트를 작성해주어야 합니다.

 

 

 

하지만 어플리케이션을 만들 때 마다 위와 같은 매니페스트를 작성해줘야 하고, 새 버전의 OS가 나오면 또 다시 매니페스트에 supportedOS 를 추가해줘야 하니 여간 번거로운게 아닙니다. MS가 GetVersionEx 를 deprecated 시킨 이유가 이제 이해가 되는군요.

 

 

VerifyVersionInfo / 

Version Helper API

  

MS에서 GetVersionEx 함수 대신 사용하도록 권장하는 방법은 VerifyVersionInfo 함수 또는 Version Helper API를 사용하는 것입니다. 두 가지 방식 모두 호환 모드 설정에 영향을 받지 않으며, Windows 8.1 이상에서도 문제없이 동작합니다. 

 

먼저 VerifyVersionInfo 함수는 현재 윈도우 버전이 지정한 버전과 같은지, 같거나 큰지, 작은지 등등을 판단하여 그에 대한 결과를 BOOL 타입으로 리턴하는 함수입니다. VerifyVersionInfo 함수를 이용하여 현재 윈도우 버전이 Windows 8 이상인지 검사하려면 다음과 같이 구현하면 됩니다. 

 

OSVERSIONINFOEX osvi;
DWORDLONG dwlConditionMask = 0;

ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
osvi.dwMajorVersion = 6;
osvi.dwMinorVersion = 2;

VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);

if (!VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask))
{
    ::MessageBox(NULL, _T("You need at least Windows 8"), _T("Version Not Supported"), MB_OK);
}

 

 

다음으로 Version Helper API는 Windows 8.1 SDK에 포함되어 있으며(Visual Studio 2012 이하 버전에서는 별도로 설치 필요), 현재까지 지원되는 Version Helper API의 목록은 다음과 같습니다.

 

  • IsWindowsXPOrGreater, IsWindowsXPSP1OrGreater, IsWindowsXPSP2OrGreater, IsWindowsXPSP3OrGreater
 
  • IsWindowsVistaOrGreater, IsWindowsVistaSP1OrGreater, IsWindowsVistaSP2OrGreater
 
  • IsWindows7OrGreater, IsWindows7SP1OrGreater
 
  • IsWindows8OrGreater, IsWindows8Point1OrGreater
 
  • IsWindowsServer
 
  • IsWindowsVersionOrGreater

 

 

Version Helper API를 이용하여 현재 윈도우 버전이 Windows 8 이상인지 검사하려면 다음과 같이 구현하면 됩니다.

 

#include <VersionHelpers.h>

if (!IsWindows8OrGreater())
{
    MessageBox(NULL, _T("You need at least Windows 8"), _T("Version Not Supported"), MB_OK);
}

 

 

위에서 설명한 두 가지 방식은 모두 현재 윈도우의 버전을 얻어오는 방식이 아니라, 현재 윈도우 버전이 해당 버전과 같은지(혹은 그 이상인지) 검증하는 방식입니다. 그러므로 GetVersionEx 함수처럼 현재 윈도우의 버전을 얻어오고 싶은 경우에는 또 다른 방법을 사용해야 합니다.

 

 

GetWindowsVersion 함수 구현

  

현재 윈도우의 정확한 버전을 얻기위해 사용할 수 있는 방법은 두 가지 방법이 있습니다. 첫 번째 방법은 VerifyVersionInfo 함수 또는 IsWindowsVersionOrGreater 함수를 반복적으로 호출하면서 현재 윈도우 버전을 구해나가는 것이고, 두 번째 방법은 Network Management Function 중에 하나인 NetWkstaGetInfo 함수를 이용하는 것입니다(

NetWkstaGetInfo 함수는 원래 네트워크상의 워크스테이션 정보를 얻어오는 함수지만 윈도우 버전 정보을 얻어오는 데에도 활용할 수 있습니다)개인적으로는 두 번째 방법이 더 깔끔한 것 같아서 두 번째 방법으로 GetWindowsVersion 함수를 구현해봤습니다.

 

#include <Lm.h>
#pragma comment(lib, "netapi32.lib")

BOOL GetWindowsVersion(DWORD& dwMajor, DWORD& dwMinor)
{
    static DWORD dwMajorCache = 0, dwMinorCache = 0;
    if (0 != dwMajorCache)
    {
        dwMajor = dwMajorCache;
        dwMinor = dwMinorCache;
        return TRUE;
    }

    LPWKSTA_INFO_100 pBuf = NULL;
    if (NERR_Success != NetWkstaGetInfo(NULL, 100, (LPBYTE*)&pBuf))
        return FALSE;

    dwMajor = dwMajorCache = pBuf->wki100_ver_major;
    dwMinor = dwMinorCache = pBuf->wki100_ver_minor;
    NetApiBufferFree(pBuf);

    return TRUE;
}

 

 

제가 참고한 자료에서는 NetWkstaGetInfo 함수가 다소 느리다는 얘기가 있어서 최초 한 번만 

NetWkstaGetInfo 함수를 호출하고, 그 다음부터는 캐시 값을 리턴하도록 구현했습니다. 그리고 NetWkstaGetInfo 함수로는 서비스팩 버전을 구할 수 없으므로 기존 GetWindowsVersion 함수와 

VerifyVersionInfo 함수를 조합하여 서비스팩 버전까지 얻어오는 오버로드 함수를 하나 더 만들어봤습니다.

 

 

BOOL GetWindowsVersion(DWORD& dwMajor, DWORD& dwMinor, DWORD& dwServicePack)
{
    if (!GetWindowsVersion(dwMajor, dwMinor))
        return FALSE;

    static DWORD dwServicePackCache = ULONG_MAX;
    if (ULONG_MAX != dwServicePackCache)
    {
        dwServicePack = dwServicePackCache;
        return TRUE;
    }

    const int nServicePackMax = 10;
    OSVERSIONINFOEX osvi;
    DWORDLONG dwlConditionMask = 0;

    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_EQUAL);

    for (int i = 0; i < nServicePackMax; ++i)
    {
        osvi.wServicePackMajor = i;
        if (VerifyVersionInfo(&osvi, VER_SERVICEPACKMAJOR, dwlConditionMask))
        {
            dwServicePack = dwServicePackCache = i;
            return TRUE;
        }
    }
    return FALSE;
}

 

 

위에서 구현한 GetWindowsVersion 함수는 호환 모드에 영향을 받지않으며, Windows 8.1 이상에서도 정확한 윈도우 버전을 얻어올 수 있습니다.

 

<그림: 호환 모드에서  GetWindowsVersion  함수 호출>

 

<그림: Windows 8.1에서  GetWindowsVersion  함수 호출>

 

 

Reference

  

MSDN - GetVersionEx functionMSDN - Version Helper functions

Part1: Overcoming Windows 8.1's deprecation of GetVersionEx and GetVersion APIs

 

 

원문 링크

 



출처 : 데브피아 김경진님의 글([VC++] 윈도우 버전 정확하게 얻어오는 새로운 방법)

 

출처 : http://blog.naver.com/techshare/220138680455


Visual Studio 2013에서 가장 아쉬운 부분이 바로 매크로인데요. 다행히 "Visual Commander" 확장 도구를 이용하면 그와 같은 제약에서 벗어날 수는 있습니다.

Visual Commander
; http://vlasovstudio.com/visual-commander/


현재 Free, Professional 제품으로 나누어서 판매하고 있습니다.

Free 버전 다운로드 (Visual Commander v1.5 - December 23, 2013)
; http://vlasovstudio.com/visual-commander/VisualCommander_15.vsix

Professional Edition 구매 ($39)
; http://vlasovstudio.com/visual-commander/professional_edition.html


사용법도 간단합니다. vsix 설치 후 Visual Studio를 실행하면 "VCMD" 메뉴가 생기는데 거기서 "Commands"를 선택한 후 "Add" 버튼을 누르면 다음과 같이 기본 매크로 함수를 위한 뼈대가 생성됩니다.




예를 들어, 제가 Visual Studio 2010에서 사용했던 매크로 함수는 다음과 같은데요.

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports EnvDTE100
Imports System.Diagnostics

Public Module InsertTag

    Sub InsertCenterHr()
        Dim selection As TextSelection

        If (ActiveDocument() Is Nothing) Then
            Exit Sub
        End If

        selection = DTE.ActiveDocument.Selection()

        Dim str As String
        str = "<hr style='width: 50%' />" & Environment.NewLine
        SetText(selection, str)
    End Sub

    Sub SetText(ByVal selection As TextSelection, ByVal txt As String)
        Dim prop As EnvDTE.Property
        prop = DTE.Properties("TextEditor", "PlainText").Item("IndentStyle")
        Dim previousIndent = prop.Value
        prop.Value = 0

        selection.Text = txt

        prop.Value = previousIndent

    End Sub

End Module


이를 Visual Commander에서는 다음과 같이 동일하게 코드를 복사해서 사용할 수 있습니다.

Imports EnvDTE
Imports EnvDTE80
Imports System

Public Class C
    Implements VisualCommanderExt.ICommand

    Sub Run(DTE As EnvDTE80.DTE2, package As Microsoft.VisualStudio.Shell.Package) Implements VisualCommanderExt.ICommand.Run

            Dim selection As TextSelection
            Dim activeDocument As EnvDTE.Document

            activeDocument = DTE.ActiveDocument

            If (activeDocument Is Nothing) Then
                    Exit Sub
            End If

            selection = activeDocument.Selection()

            Dim str As String
            str = "<hr style='width: 50%' />" & Environment.NewLine
            SetText(DTE, selection, str)        
    End Sub


        Sub SetText(DTE As EnvDTE80.DTE2, ByVal selection As TextSelection, ByVal txt As String)
            Dim prop As EnvDTE.Property
            prop = DTE.Properties("TextEditor", "PlainText").Item("IndentStyle")
            Dim previousIndent = prop.Value
            prop.Value = 0

            selection.Text = txt

            prop.Value = previousIndent

        End Sub
End Class


보시는 바와 같이 DTE 변수를 넘겨준다는 점을 제외하고는 거의 차이가 없습니다.

불편한 점은 사용법에 있습니다. 이전에는 매크로 탐색기에서 해당 매크로를 더블-클릭하면 실행이 가능했지만, "Visual Commander"의 경우 "Commands" 창에서 더블-클릭해 실행하는 것을 지원하지 않고 있습니다. 매번 "VCMD" 메뉴를 펼쳐서 새롭게 등록된 명령어를 선택해 줘야 하는데요. 물론 이 부분은 기본적으로 단축키 등록을 통해 우회적으로 해결할 수는 있지만 어쨌든 불편한 것은 사실입니다. 
(이 부분을 제가 요청했는데 최신 버전에서 개발자가 그 의견을 받아들여 수정했습니다. 이제 예전의 Macro Explorer처럼 편리하게 사용할 수 있습니다.) 

매크로가 그리웠던 분들... ^^ 지금 설치해 보세요.




참고로 아래 코드는 현재 제가 사용하고 있는 _T("") 자동으로 해 주는 매크로 소스입니다.

Imports EnvDTE
Imports EnvDTE80
Imports Microsoft.VisualBasic

Public Class C
	Implements VisualCommanderExt.ICommand

	Sub Run(DTE As EnvDTE80.DTE2, package As Microsoft.VisualStudio.Shell.Package) Implements VisualCommanderExt.ICommand.Run

		'DESCRIPTION: This macro will automatically put "_T( )" around 
		'             your strings, Author: Orin Walker, 1999, Version 1.1
		'             Last change - Acidpop(http://acidpop.tistory.com) (2012.09.07)
		'             Supported Visual Studio 2010 Macro
		Dim iCount As Integer
		Dim bFoundAQuote As Boolean
		Dim strTemp As String
		Dim strStuffAtEnd As String
		Dim bDone As Boolean
		Dim str As String
		Dim strBuildString As String
		Dim Selection As TextSelection
		Dim win as Window

		win = DTE.ActiveWindow
		If (win.type <> EnvDTE.vsWindowType.vsWindowTypeDocument) And (win.type <> EnvDTE.vsWindowType.vsWindowTypeCodeWindow) Then
'			MsgBox( "type=" & win.type & "  Doc=" & EnvDTE.vsWindowType.vsWindowTypeDocument & "  CodeWin=" &  EnvDTE.vsWindowType.vsWindowTypeCodeWindow )
			Exit Sub
		End If

		iCount = 0
		bFoundAQuote = False
		DTE.ActiveDocument.Selection.SelectLine()
		strTemp = DTE.ActiveDocument.Selection.Text

		Selection = DTE.ActiveDocument.Selection

		strStuffAtEnd = ""
		While bDone <> True
			str = ParseString(strTemp, bFoundAQuote, strStuffAtEnd)
			strBuildString = strBuildString + str

			If bFoundAQuote = True Then
				strTemp = strStuffAtEnd
			Else
				bDone = True
				'DTE.ActiveDocument.Selection.Delete()
				'DTE.ActiveDocument.Selection = strBuildString
				Selection.Text = strBuildString
			End If
			iCount = iCount + 1
			If iCount > 100 Then    ' safety valve
				bDone = True
			End If
		End While
		'End If
	End Sub
    Function ParseString(ByVal strTemp, ByRef bFoundAQuote, _
                         ByRef strStuffAtEnd)
		Dim strSpace As String
		Dim iLen As Integer
		Dim iPos As Integer
		Dim x As Integer
		Dim strCheck As String
		Dim iUnderscoreTPos As Integer
		Dim strBeforeFirstQuote As String
		Dim strNewTempStr As String
		Dim strRemaining As String
		Dim strStuffInQuotes As String


		'DESCRIPTION: This is a helper function for the UnderscoreT macro,
		'             Author: Orin Walker, 1999, Version 1.1
		' Comment in/out whatever style you prefer       
		strSpace = ""   ' NO space before or after "_T("
		'strSpace = " " ' Add a space before and after "_T("
		iLen = Len(strTemp)
		bFoundAQuote = False
		' Get the position of the first quote on the line
		iPos = InStr(strTemp, Chr(34))
		If iPos > 0 Then    'a quote was found
			' Go back and see if we have an existing 
			' _T( defined for this quote
			x = iPos - 5          ' Go back up to 5 characters
			If x <= 0 Then      ' If we have reached the 
				' beginning of our string
				x = 1           ' Set x to start at the first character
			End If
			strCheck = Mid(strTemp, x, iPos)

			iUnderscoreTPos = InStr(strCheck, "_T(")

			' If we found one grab everything before the first quote
			strBeforeFirstQuote = Mid(strTemp, 1, iPos - 1)
			If iUnderscoreTPos > 0 Then     ' we found an "_T("
				' Do NOT add the "_T(" to our temporary string
				strNewTempStr = strBeforeFirstQuote
			Else
				' Now create our new temporary string and append "_T("
				strNewTempStr = strBeforeFirstQuote + "_T(" + strSpace
			End If
			' Get the remaining string
			strRemaining = Mid(strTemp, iPos + 1, iLen)

			iLen = Len(strRemaining)
			' Now find the second quote
			iPos = InStr(strRemaining, Chr(34))

			If iPos > 0 Then
				' If we found one save the stuff in quotes
				strStuffInQuotes = Chr(34) + Mid(strRemaining, 1, iPos)

				' And grab the stuff after the quotes
				strStuffAtEnd = Mid(strRemaining, iPos + 1, iLen)

				If iUnderscoreTPos > 0 Then     ' we found an _T(
					' Do NOT add the final ")" to our parsed string, 
					' because it alreasy exists
					ParseString = strNewTempStr + strStuffInQuotes
				Else
					' Create our parsed string
					ParseString = strNewTempStr + strStuffInQuotes + _
						strSpace + ")"
				End If
				bFoundAQuote = True
			Else
				' No SECOND quote was found so just return 
				' what was passed in
				ParseString = strTemp
			End If
		Else
			' No quote was found so just return what was passed in
			ParseString = strTemp
		End If
    End Function
End Class

 

 

 

 

다음은 CPP 파일과 .H 파일을 서로 왔다 갔다 해 주는 소스입니다.

Imports EnvDTE
Imports EnvDTE80
Imports Microsoft.VisualBasic
 
Public Class C
    Implements VisualCommanderExt.ICommand
 
    Function GetFilenameFromPath(ByVal strPath As String) As String
        ' Returns the rightmost characters of a string upto but not including the rightmost '\'
        ' e.g. 'c:\winnt\win.ini' returns 'win.ini'

        If Right$(strPath, 1) <> "\" And Len(strPath) > 0 Then
            GetFilenameFromPath = GetFilenameFromPath(Left$(strPath, Len(strPath) - 1)) + Right$(strPath, 1)
        End If
    End Function

    Sub Run(DTE As EnvDTE80.DTE2, package As Microsoft.VisualStudio.Shell.Package) Implements VisualCommanderExt.ICommand.Run
        '////////////////////////////////////////////
        'Nooruddin Kapasi 1998.
        'Pavel Sokolov , CEZEO software , http://www.cezeo.com , Adaptation for .NET
        'DESCRIPTION: Switch Between Header and cpp
        '////////////////////////////////////////////

        Dim a As String
        Dim b As String
        Dim Flag As Integer
        Dim tmp As String
        Flag = 0
        a = DTE.ActiveDocument.FullName()
        tmp = InStr(a, ".cpp")
        If tmp Then
            b = Left(a, Len(a) - 3) + "h"
            Flag = 1
        Else
            tmp = InStr(a, ".h")
            If tmp Then
                b = Left(a, Len(a) - 1) + "cpp"
                Flag = 1
            End If
        End If

        If Flag Then
            Try
                DTE.Documents.Open(b, "Text")
            Catch
                a = GetFilenameFromPath(DTE.ActiveDocument.FullName())
                tmp = InStr(a, ".cpp")
                If tmp Then
                    b = Left(a, Len(a) - 3) + "h"
                    Flag = 1
                Else
                    tmp = InStr(a, ".h")
                    If tmp Then
                        b = Left(a, Len(a) - 1) + "cpp"
                        Flag = 1
                    End If
                End If

                a = DTE.Solution.FindProjectItem(b).FileNames(0)
                DTE.Documents.Open(a, "Text")

            End Try

        End If
    End Sub
End Class
 

 

 


다음은 선택 영역을 //로 주석 처리 해 주는 소스입니다.
Imports EnvDTE
Imports EnvDTE80
Imports Microsoft.VisualBasic

Public Class C
	Implements VisualCommanderExt.ICommand

	Sub Run(DTE As EnvDTE80.DTE2, package As Microsoft.VisualStudio.Shell.Package) Implements VisualCommanderExt.ICommand.Run
            Dim win
            win = DTE.ActiveWindow
 
	    If (win.type <> EnvDTE.vsWindowType.vsWindowTypeDocument) And (win.type <> EnvDTE.vsWindowType.vsWindowTypeCodeWindow) Then
           	  MsgBox( "This macro can only be run when a text editor window is active." )
		  Exit Sub
            else
                if InStr( DTE.ActiveDocument.Selection.Text, vbCr ) > 0 then
                    DTE.ActiveDocument.Selection.ReplaceText( "^", "//", EnvDTE.DsTextSearchOptions.dsMatchRegExp )
                End If
            End If
	End Sub

End Class

 

 

 

 

다음은 선택 영역의 //로 된 주석을 해제 해 주는 소스입니다.

Imports EnvDTE
Imports EnvDTE80
Imports Microsoft.VisualBasic

Public Class C
	Implements VisualCommanderExt.ICommand

	Sub Run(DTE As EnvDTE80.DTE2, package As Microsoft.VisualStudio.Shell.Package) Implements VisualCommanderExt.ICommand.Run
		Dim win
		win = DTE.ActiveWindow
	       If (win.type <> EnvDTE.vsWindowType.vsWindowTypeDocument) And (win.type <> EnvDTE.vsWindowType.vsWindowTypeCodeWindow) Then
           	       MsgBox( "This macro can only be run when a text editor window is active." )
		       Exit Sub
              else
			DTE.ActiveDocument.Selection.ReplaceText( "^//", "", EnvDTE.DsTextSearchOptions.dsMatchRegExp )
		End If
		
	End Sub

End Class



출처 : http://blog.naver.com/techshare/220138680455


tstring 클래스는 std::basic_string<> 탬플릿 클리스를 상속받은 클래스이다.

지금까지 std::string이든 std::wstring이든 사용하면서 불편한 점이 바로 CString에는 있는 format()함수가 없다는 것이다.

이를 위해 wsprintf를 사용한다든지 했을텐데 필자 역시 불편하여 이를 위한 유틸리티 함수를 몇 개 만들어본다. 기본 개념은 _snprintf() 라는 함수의 첫번째와 두번째 인자에 NULL과 0을 넣으면 실제 완성된 문자열의 길이를 리턴해 준다.

이 길이를 이용해서 버퍼를 할당하고 완전한 _snprintf() 함수를 호출하여 마무리 한다.

#include "tstring.h"
...
...
void CMFCApplication1Dlg::OnBnClickedButton1()
{
	tstring a;
	std::format(a, _T("aaa : %.3f"), 0.2232487);
	std::append_format(a, _T("==> abc : %s"), _T("mjp") );
	MessageBox(a.c_str());
}

결과는 다음과 같다.



소스파일은 첨부파일에서 다운받으세요.

tstring.7z



현재 웹페이지에 있는 이메일 주소를 모두 가져오는 소스(함수)




function extractEmails (text)
{
    return text.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/gi);
}



function t()
{
	var col = document.getElementsByTagName( 'SPAN' );
	var count = col.length;
	var i = 0, idx_at, idx_start, idx_end;
	var e, str, ret;

	ret = "";

	for( i =0; i < count; i ++ )
	{
		e = col.item(i);
		str = e.innerText;

		idx_at = str.indexOf( '@' );
		if( idx_at > 0 )
		{
			ret = ret + extractEmails(str) + "\n";
		}
	}

	return ret;
}


CScrollView의 윈도우의 ScrollBar는 윈도 개발 초기에 SHORT형 자료형을 기반으로 개발되었기 때문에 scrollbar의 thumb을 잡고 이동할때 32767을 넘는 순간 오동작을 하게 된다.

이 경우 아래와 같이 WM_HSCROLL과 WM_VSCROLL을 핸들링해서 아래 코드를 집어 넣으면 간단히 해결된다.


void CDemoView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	SCROLLINFO si = { 0, }; 
    si.cbSize = sizeof(SCROLLINFO); 
    si.fMask = SIF_ALL; 
    GetScrollInfo(SB_HORZ, &si); 
    nPos = si.nTrackPos; 

	CScrollView::OnHScroll(nSBCode, nPos, pScrollBar);
}

void CDemoView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	SCROLLINFO si = { 0, }; 
    si.cbSize = sizeof(SCROLLINFO); 
    si.fMask = SIF_ALL; 
    GetScrollInfo(SB_VERT, &si); 
    nPos = si.nTrackPos; 

	CScrollView::OnVScroll(nSBCode, nPos, pScrollBar);
}


위 방법은 CScrollview를 사용할 때의 이야기고, CScrollBarCtrl 클래스르 직접 사용하거나

혹은, CScrollView가 아닌 다른 View에서 문제를 봉착하였을 경우 GetScrollInfo()함수를 이용하여 적당히 처리하면 해결할 수 있다.

CPoint/POINT 의 double형 변형체 CdblPoint / dblPOINT

CSize / SIZE 의 double 형 변형체 CdblSize / dblSIZE

CRect / RECT의 double형 변형체 CdblRect / dblRECT



첨부파일 참조




dblPoint.cpp


dblPoint.h


dblRect.cpp


dblRect.h


dblSize.cpp


dblSize.h


출처 : http://xarfox.tistory.com/120


Visual Studio 2008 Professional Trial 버전 날짜제한 우회방법

| 

1. 프로그램 추가/삭제 에 가서...
2. Visual Studio 2008 Professional 에서 제거/변경 클릭하면,
3. CD-KEY 넣는곳이 나온다. 거기에 아래 키중 하나 집어넣고 업그레이드...

Windows7 사용자의 경우,
CD-KEY 넣는 곳을 찾을 수 없다.
이건 버그인데, MS에서 제공하는 아래 패치를 설치한 후에 다시 시도하면 CD-KEY를 넣을 수 있다.

https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=27638

KB979576.zip





CD-KEYs

1.Visual Studio 2008 Professional Edition:
XMQ2Y-4T3V6-XJ48Y-D3K2V-6C4WT 

2.Visual Studio 2008 Team Test Load Agent:
WPX3J-BXC3W-BPYWP-PJ8CM-F7M8T 

3.Visual Studio 2008 Team System:
PYHYP-WXB3B-B2CCM-V9DX9-VDY8T

4.Visual Studio 2008 Team Foundation Server:
WPDW8-M962C-VJX9M-HQB4Q-JVTDM



출처 : http://xarfox.tistory.com/120


Windows Vista이후 프로그래밍으로 시스템 볼륨을 조절하는 방법이 달라졌다.

이 소리 조절을 쉽게 할 수 있도록 만들어진 클래스가 있어 공유한다.

프로그래밍 언어는 Delphi이지만, 소스코드를 이해할 줄 안다면 다른 언어로 포팅이 가능하다.


이 클래스를 Delphi7에서 테스트를 했지만 아마 이후 버전의 Delphi에서도 무리없이 동작할 거라 생각된다.

이 클래스는 Win78Mixer unit에 있는 TBasicMixer 클래스의 Instance만 있으면 된다.


CreateVis78Mixer() 클래스를 이용해서 TBasicMixer 객체를 가져온 다음

MasterVolume 값을 조절하면 된다.



Delphi Volume Control Mixer.zip


Visual Studio 2010의 가장 큰 특징중의 하나는 바로 C+0 Lamda  표현식을 사용할 수 있는 거다.

이 Lamda 표현식은 다른 언어 특히 루비나 파이썬과 같은 프로그래밍 언어에서 많이 사용하는 문법이었는데 지금까지 C++에서는 이 편리한 Lamda 표현식을 지원하지 않다가 이제서야 지원하게 되었다.

Lamda 표현식에 대해서는 인터넷에 자료가 널려 있기 때문에 더이상 설명하지 않는다. 구글링을 하기 바란다.


Lamda 표현식을 내가 만든 프로그램에서 사용하기 위해서는 다음과 같은 과정을 따르면 된다.


1. #include <functional>

2. typedef 을 이용해서 람다 함수 원형을 정의 한다.
3. 함수의 인자로 람다 Functor 를 입력받고 이를 활용하면 된다.


여기서 설명하는 C+0 Lamda 표현식은 MS 개발툴의 경우 VS2010 이상에서만 가능하다.




====================== 헤더 파일 선언 내용 ======================
#include <functional>

template<class _Fn1> inline
BOOL CALLBACK EnumControlsProcLamda(HWND hWnd, _Fn1 * func)
{
	(*func)(hWnd);
	return true;
}
template<class _Fn1> inline
void EnumAllControls(CWnd * pWnd, _Fn1 _Func)
{
	EnumChildWindows(pWnd->GetSafeHwnd(), (WNDENUMPROC)EnumControlsProcLamda<_Fn1>, (LPARAM)&_Func);
}



====================== 사용 방법 ======================
.
.
.
// 모든 자식 컨트롤 true로 만들기
// 람다 함수 내에서 지역변수 Capture가 필요하면 [=]혹은 [&]을 이용한다.
EnumAllControls( [](HWND h){
	if( h )
	{
		if( !IsWindowEnabled(h) )
		{
			EnableWindow(h);
		}
	}
});




====================== 함수 실행 시간 구하는 Lamda함수  ======================


template <class Function>
__int64 time_call(Function&& f)
{
   __int64 begin = GetTickCount();
   f();
   return GetTickCount() - begin;
}





C++의 RTTI는 정말 편리하다.

프로그램 실행중에 변수의 자료형을 확인할 수 있으니 말이다.

RTTI는 C++ 표준서 및 MFC RTTI 지원 문서를 참고하자.


C++ RTTI : http://sweeper.egloos.com/2826160

MFC RTTI : http://anster.egloos.com/2173004


그런데 써본 사람들은 알겠지만, 이 MFC의 RUNTIME_CLASS() 매크로는 속도도 빠르고 좋은데

문제는 내포 클래스(혹은 중첩 클래스라고 말하기도 한다)에는 사용할 수 없다.

내포 클래스란...? http://initial4-blog.blogspot.kr/2012/06/c-study-512-part.html


내포 클래스는 용도에 따라서 매우 유용한 클래스인데, 이런 내포(중첩)클래스에서도 RTTI를 사용할 수 있도록 MFC의 RUNTIME_CLASS를 약간 수정해 보았다.



IMPLEMENT_DYNAMIC_NESTED() 의 사용방법은 다음과 같다.


IMPLEMENT_RUNTIME_GETRUNTHISCLASS(nesting_class, class_name, base_class_name)

=> nesting_class는 내부 클래스를 감싸는 외부 클래스

=> class_name은 외부 클래스 안에 있는 내포 클래스 즉, 중첩 클래스

=> base_class_name은 내포 클래스 즉, 중첩 클래스가 상속하고 있는 부모 클래스.


아래는 사용 예를 보여준다.





----------------  헤더파일에 추가할 내용 ----------------

#define RUNTIME_CLASS_NESTED(nesting_class, class_name) ((CRuntimeClass*)(&nesting_class::class_name::class##class_name))


#if _MSC_VER >= 1600
#define IMPLEMENT_RUNTIME_GETRUNTHISCLASS(nesting_class, class_name, base_class_name)\
					CRuntimeClass* nesting_class::class_name::GetRuntimeClass() const \
						{ return RUNTIME_CLASS_NESTED(nesting_class, class_name); } \
					CRuntimeClass* PASCAL nesting_class::class_name::GetThisClass() \
						{ return RUNTIME_CLASS_NESTED(nesting_class, class_name); }
#else
#define IMPLEMENT_RUNTIME_GETRUNTHISCLASS(nesting_class, class_name, base_class_name)\
					CRuntimeClass* nesting_class::class_name::GetRuntimeClass() const \
						{ return RUNTIME_CLASS_NESTED(nesting_class, class_name); } 
#endif




#ifdef _AFXDLL
#define IMPLEMENT_RUNTIMECLASS_NESTED(nesting_class, class_name, base_class_name, wSchema, pfnNew) \
	CRuntimeClass* PASCAL nesting_class::class_name::_GetBaseClass() \
		{ return RUNTIME_CLASS(base_class_name); } \
	AFX_COMDAT const AFX_DATADEF CRuntimeClass nesting_class::class_name::class##class_name = { \
		#class_name, sizeof(class nesting_class::class_name), wSchema, pfnNew, \
			&nesting_class::class_name::_GetBaseClass, NULL }; \
	IMPLEMENT_RUNTIME_GETRUNTHISCLASS(nesting_class, class_name, base_class_name)



#else
#define IMPLEMENT_RUNTIMECLASS_NESTED(nesting_class, class_name, base_class_name, wSchema, pfnNew) \
	AFX_COMDAT const AFX_DATADEF CRuntimeClass nesting_class::class_name::class##class_name = { \
		#class_name, sizeof(class nesting_class::class_name), wSchema, pfnNew, \
			RUNTIME_CLASS(base_class_name), NULL }; \
	IMPLEMENT_RUNTIME_GETRUNTHISCLASS(nesting_class, class_name, base_class_name)



#endif

#define IMPLEMENT_DYNAMIC_NESTED(nesting_class, class_name, base_class_name) \
	IMPLEMENT_RUNTIMECLASS_NESTED(nesting_class, class_name, base_class_name, 0xFFFF, NULL)




----------------  사용하는 방법 예제 ----------------



A.h

class CA : public CObject
{
public:
	DECLARE_DYNAMIC(CA)

	CA(){}
	virtual ~CA(){}
	
	
	
private:
	class CANested : public CObject
	{
	public:
		DECLARE_DYNAMIC(CANested)
		CANested() {}
		virtual ~CANested() {}
		
		
	};

};






A.cpp

IMPLEMENT_DYNAMIC(CA, CObject)
IMPLEMENT_DYNAMIC_NESTED(CA, CANested, CObject)




+ Recent posts