오늘 도착한 ZDNET 코리아 메일링 리스트를 통해 아주 흥미로운 웹 캐스트가 하나 게시되었습니다. Delphi Prism은 Embacadero의 최신 Rapid Application Development Tool로서 종전의 Delphi .NET에 대한 업그레이드 제품이자, RemObjects사의 Object Pascal 제품의 최신 버전입니다. Visual Studio Shell을 통하여 이전 버전의 Delphi IDE에서 사용헀던 .NET 개발 환경때 보다 더 풍부하고 확장된 기능을 제공하기도 하고, 특히 이번 XE 버전에서는 MonoDevelop 기반의 IDE도 동시에 지원하고 있어서 기능성이 매우 돋보이기도 합니다.

이 동영상에서 소개하는 기술들에 대해 간단히 요약하면, 리눅스, 솔라리스, 맥 OS X에서 데스크 탑 및 서버 닷넷 프레임워크 대체 구현을 제공하는 Mono 프레임워크 (http://mono-project.com/Downloads), 상용 제품군으로 판매되는 MonoTouch (http://monotouch.net/) SDK, 맥 OS X 환경에서 구동 가능한 iPhone 및 iPad SDK (http://developer.apple.com/iphone/), 그리고 Embacadero Delphi Prism XE (http://www.embarcadero.com/products/delphi-prism)를 활용하여 iPhone과 iPad에서 실행 가능한 응용프로그램을 디자인하는 것입니다. 이 중에서 상용 라이선스가 필요한 것은 MonoTouch와 Delphi Prism XE가 되겠습니다.

만약 Delphi가 아닌 C#을 이용하여 응용프로그램을 개발하기 원한다면 Delphi Prism XE 대신 MonoTouch와 함께 제공되는 기본 IDE인 MonoDevelop 만으로도 충분한 개발이 가능합니다.

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

응용프로그램 작성 도중 C#의 소수점 반올림 관련 코드에서 몇몇 자주 사용되는 기능들을 보완하는 코드를 작성하여 올려봅니다. 이 코드는 http://www.latiumsoftware.com/en/delphi/00033.php 의 코드를 바탕으로 작성된 것입니다.

namespace System
{
    // Original Source: http://www.latiumsoftware.com/en/delphi/00033.php
    public static class MathExtension
    {
        public static int Sign(
#if CS3
            this
#endif // CS3
            decimal x)
        {
            return Math.Sign(x);
        }

        public static int Sign(
#if CS3
            this
#endif // CS3
            double x)
        {
            return Math.Sign(x);
        }

        public static int Integer(
#if CS3
            this
#endif // CS3
            decimal x)
        {
            return (int)Math.Truncate(x);
        }

        public static int Integer(
#if CS3
            this
#endif // CS3
            double x)
        {
            return (int)Math.Truncate(x);
        }

        public static decimal Fraction(
#if CS3
            this
#endif // CS3
            decimal x)
        {
            return x - Math.Truncate(x);
        }

        public static double Fraction(
#if CS3
            this
#endif // CS3
            double x)
        {
            return x - Math.Truncate(x);
        }

        public static decimal RoundUp(
#if CS3
            this
#endif // CS3
            decimal x)
        {
            return Integer(x) + Sign(Fraction(x));
        }

        public static double RoundUp(
#if CS3
            this
#endif // CS3
            double x)
        {
            return Integer(x) + Sign(Fraction(x));
        }

        public static decimal RoundDown(
#if CS3
            this
#endif // CS3
            decimal x)
        {
            return Integer(x);
        }

        public static double RoundDown(
#if CS3
            this
#endif // CS3
            double x)
        {
            return Integer(x);
        }

        public static decimal Round(
#if CS3
            this
#endif // CS3
            decimal x)
        {
            return Integer(x) + Integer(Fraction(x) * 2m);
        }

        public static double Round(
#if CS3
            this
#endif // CS3
            double x)
        {
            return Integer(x) + Integer(Fraction(x) * 2d);
        }

        public static decimal Fix(
#if CS3
            this
#endif // CS3
            decimal x)
        {
            if (x >= 0m || Fraction(x) == 0m)
                return Integer(x);
            else
                return Integer(x) - 1;
        }

        public static double Fix(
#if CS3
            this
#endif // CS3
            double x)
        {
            if (x >= 0d || Fraction(x) == 0d)
                return Integer(x);
            else
                return Integer(x) - 1;
        }

        public static decimal RoundDownFix(
#if CS3
            this
#endif // CS3
            decimal x)
        {
            return Fix(x);
        }

        public static double RoundDownFix(
#if CS3
            this
#endif // CS3
            double x)
        {
            return Fix(x);
        }

        public static int Absolute(
#if CS3
            this
#endif // CS3
            int x)
        {
            return Math.Abs(x);
        }

        public static decimal RoundUpFix(
#if CS3
            this
#endif // CS3
            decimal x)
        {
            return Fix(x) + Absolute(Sign(Fraction(x)));
        }

        public static double RoundUpFix(
#if CS3
            this
#endif // CS3
            double x)
        {
            return Fix(x) + Absolute(Sign(Fraction(x)));
        }

        public static decimal RoundFix(
#if CS3
            this
#endif // CS3
            decimal x)
        {
            return Fix(x + 0.5m);
        }

        public static double RoundFix(
#if CS3
            this
#endif // CS3
            double x)
        {
            return Fix(x + 0.5d);
        }

        public static decimal RoundTo(
#if CS3
            this
#endif // CS3
            decimal x, int d)
        {
            decimal n = (decimal)Math.Pow(10, d);
            x *= n;
            return (Integer(x) + Integer(Fraction(x) * 2m)) / n;
        }

        public static double RoundTo(
#if CS3
            this
#endif // CS3
            double x, int d)
        {
            double n = Math.Pow(10, d);
            x *= n;
            return (Integer(x) + Integer(Fraction(x) * 2d)) / n;
        }
    }
}

사용 예시는 다음과 같습니다.

using System;

namespace ConsoleApplication1
{
    public static class Program
    {
        [STAThread]
        public static void Main(string[] args)
        {
#if CS3
            Console.WriteLine("{0}", (3.3m).RoundUp() == 4m);
            Console.WriteLine("{0}", (-3.3m).RoundUp() == -4m);
            Console.WriteLine("{0}", (3.7m).RoundDown() == 3m);
            Console.WriteLine("{0}", (-3.7m).RoundDown() == -3m);
            Console.WriteLine("{0}", (3.5m).Round() == 4m);
            Console.WriteLine("{0}", (-3.5m).Round() == -4m);
            Console.WriteLine("{0}", (3.1m).Round() == 3m);
            Console.WriteLine("{0}", (-3.1m).Round() == -3m);
            Console.WriteLine("{0}", (3.7m).Integer() == 3);
            Console.WriteLine("{0}", (-3.7m).Integer() == -3);
            Console.WriteLine("{0}", (3.7m).Fix() == 3m);
            Console.WriteLine("{0}", (-3.7m).Fix() == -4m);
            Console.WriteLine("{0}", (3.7m).RoundDownFix() == 3m);
            Console.WriteLine("{0}", (-3.7m).RoundDownFix() == -4m);
            Console.WriteLine("{0}", (3.1m).RoundDownFix() == 3m);
            Console.WriteLine("{0}", (-3.1m).RoundDownFix() == -4m);
            Console.WriteLine("{0}", (3.1m).RoundUpFix() == 4m);
            Console.WriteLine("{0}", (-3.7m).RoundUpFix() == -3m);
            Console.WriteLine("{0}", (3.5m).RoundFix() == 4m);
            Console.WriteLine("{0}", (-3.5m).RoundFix() == -3m);
            Console.WriteLine("{0}", (123.456m).RoundTo(0) == 123.00m);
            Console.WriteLine("{0}", (123.456m).RoundTo(2) == 123.46m);
            Console.WriteLine("{0}", (123456m).RoundTo(-3) == 123000m);

            Console.WriteLine("{0}", (3.3d).RoundUp() == 4d);
            Console.WriteLine("{0}", (-3.3d).RoundUp() == -4d);
            Console.WriteLine("{0}", (3.7d).RoundDown() == 3d);
            Console.WriteLine("{0}", (-3.7d).RoundDown() == -3d);
            Console.WriteLine("{0}", (3.5d).Round() == 4d);
            Console.WriteLine("{0}", (-3.5d).Round() == -4d);
            Console.WriteLine("{0}", (3.1d).Round() == 3d);
            Console.WriteLine("{0}", (-3.1d).Round() == -3d);
            Console.WriteLine("{0}", (3.7d).Integer() == 3);
            Console.WriteLine("{0}", (-3.7d).Integer() == -3);
            Console.WriteLine("{0}", (3.7d).Fix() == 3d);
            Console.WriteLine("{0}", (-3.7d).Fix() == -4d);
            Console.WriteLine("{0}", (3.7d).RoundDownFix() == 3d);
            Console.WriteLine("{0}", (-3.7d).RoundDownFix() == -4d);
            Console.WriteLine("{0}", (3.1d).RoundDownFix() == 3d);
            Console.WriteLine("{0}", (-3.1d).RoundDownFix() == -4d);
            Console.WriteLine("{0}", (3.1d).RoundUpFix() == 4d);
            Console.WriteLine("{0}", (-3.7d).RoundUpFix() == -3d);
            Console.WriteLine("{0}", (3.5d).RoundFix() == 4d);
            Console.WriteLine("{0}", (-3.5d).RoundFix() == -3d);
            Console.WriteLine("{0}", (123.456d).RoundTo(0) == 123.00d);
            Console.WriteLine("{0}", (123.456d).RoundTo(2) == 123.46d);
            Console.WriteLine("{0}", (123456d).RoundTo(-3) == 123000d);
#else // !CS3
            Console.WriteLine("{0}", MathExtension.RoundUp(3.3m) == 4m);
            Console.WriteLine("{0}", MathExtension.RoundUp(-3.3m) == -4m);
            Console.WriteLine("{0}", MathExtension.RoundDown(3.7m) == 3m);
            Console.WriteLine("{0}", MathExtension.RoundDown(-3.7m) == -3m);
            Console.WriteLine("{0}", MathExtension.Round(3.5m) == 4m);
            Console.WriteLine("{0}", MathExtension.Round(-3.5m) == -4m);
            Console.WriteLine("{0}", MathExtension.Round(3.1m) == 3m);
            Console.WriteLine("{0}", MathExtension.Round(-3.1m) == -3m);
            Console.WriteLine("{0}", MathExtension.Integer(3.7m) == 3);
            Console.WriteLine("{0}", MathExtension.Integer(-3.7m) == -3);
            Console.WriteLine("{0}", MathExtension.Fix(3.7m) == 3);
            Console.WriteLine("{0}", MathExtension.Fix(-3.7m) == -4);
            Console.WriteLine("{0}", MathExtension.RoundDownFix(3.7m) == 3m);
            Console.WriteLine("{0}", MathExtension.RoundDownFix(-3.7m) == -4m);
            Console.WriteLine("{0}", MathExtension.RoundDownFix(3.1m) == 3m);
            Console.WriteLine("{0}", MathExtension.RoundDownFix(-3.1m) == -4m);
            Console.WriteLine("{0}", MathExtension.RoundUpFix(3.1m) == 4m);
            Console.WriteLine("{0}", MathExtension.RoundUpFix(-3.7m) == -3m);
            Console.WriteLine("{0}", MathExtension.RoundFix(3.5m) == 4m);
            Console.WriteLine("{0}", MathExtension.RoundFix(-3.5m) == -3m);
            Console.WriteLine("{0}", MathExtension.RoundTo(123.456m, 0) == 123.00m);
            Console.WriteLine("{0}", MathExtension.RoundTo(123.456m, 2) == 123.46m);
            Console.WriteLine("{0}", MathExtension.RoundTo(123456m, -3) == 123000m);

            Console.WriteLine("{0}", MathExtension.RoundUp(3.3d) == 4d);
            Console.WriteLine("{0}", MathExtension.RoundUp(-3.3d) == -4d);
            Console.WriteLine("{0}", MathExtension.RoundDown(3.7d) == 3d);
            Console.WriteLine("{0}", MathExtension.RoundDown(-3.7d) == -3d);
            Console.WriteLine("{0}", MathExtension.Round(3.5d) == 4d);
            Console.WriteLine("{0}", MathExtension.Round(-3.5d) == -4d);
            Console.WriteLine("{0}", MathExtension.Round(3.1d) == 3d);
            Console.WriteLine("{0}", MathExtension.Round(-3.1d) == -3d);
            Console.WriteLine("{0}", MathExtension.Integer(3.7d) == 3);
            Console.WriteLine("{0}", MathExtension.Integer(-3.7d) == -3);
            Console.WriteLine("{0}", MathExtension.Fix(3.7d) == 3);
            Console.WriteLine("{0}", MathExtension.Fix(-3.7d) == -4);
            Console.WriteLine("{0}", MathExtension.RoundDownFix(3.7d) == 3d);
            Console.WriteLine("{0}", MathExtension.RoundDownFix(-3.7d) == -4d);
            Console.WriteLine("{0}", MathExtension.RoundDownFix(3.1d) == 3d);
            Console.WriteLine("{0}", MathExtension.RoundDownFix(-3.1d) == -4d);
            Console.WriteLine("{0}", MathExtension.RoundUpFix(3.1d) == 4d);
            Console.WriteLine("{0}", MathExtension.RoundUpFix(-3.7d) == -3d);
            Console.WriteLine("{0}", MathExtension.RoundFix(3.5d) == 4d);
            Console.WriteLine("{0}", MathExtension.RoundFix(-3.5d) == -3d);
            Console.WriteLine("{0}", MathExtension.RoundTo(123.456d, 0) == 123.00d);
            Console.WriteLine("{0}", MathExtension.RoundTo(123.456d, 2) == 123.46d);
            Console.WriteLine("{0}", MathExtension.RoundTo(123456d, -3) == 123000d);
#endif // CS3
        }
    }
}

위의 코드에서 CS3 라는 매크로를 프로젝트 내에 설정하면 Extension Method로 사용할 수 있으며, 이 매크로의 선언을 해제하면 일반 정적 메서드로 변경하여 사용할 수 있습니다. 여러 메서드들이 있지만 그 중에서도 RoundTo 메서드는 사용 빈도가 매우 많은 함수일 듯 합니다. :-)

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

이번에 새로 출시된 RAD Studio 2009에서는 좀 더 구체적이고 혁신적인 델파이 개발 도구의 변화가 있었는데 Delphi .NET Compiler가 RemObjects사의 새 컴파일러 엔진으로 교체됨과 함께 일말의 개발 도구가 Delphi IDE로부터 독립하여 Visual Studio Shell 버전으로 통합되었습니다. 결과적으로 RemObjects사가 줄곧 선보였던 오브젝트 파스칼 기반 개발 환경을 이번에서야 제대로 선을 보이게 되었네요.

사용자 삽입 이미지

출처: RemObjects Homepage

Delphi Prism이라고 불리는 이번 릴리즈는 예전의 Delphi 프로그래밍 환경이 그랬던것처럼 크로스 플랫폼을 강점으로 내세우고 있습니다. Delphi Native의 경우 Delphi와 Kylix의 조합이었었던걸 기억하시는 분들이 많을 줄로 압니다. Delphi Prism의 경우, 별도의 브랜드를 두는 것은 아니며 Delphi Prism .NET을 통하여 빌드 타겟을 MS.NET과 Mono로 놓고 진행할 수 있습니다.

좀 더 자세한 정보는 아래 웹 사이트를 참고하시면 되겠습니다.

http://www.codegear.com/products/delphi/prism
http://www.remobjects.com/prism

스크린 샷

사용자 삽입 이미지


크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

Delphi의 VCL 컨트롤 중에서 TStatusBar 컨트롤이 있습니다. 이 컨트롤은 대부분의 경우 정상적으로 원하는대로 Owner Draw를 구현하여 사용할 수 있지만 뜻하지 않게 일부 운영 체제에서는 Owner Draw가 회피당하거나 (Windows 2000), Owner Draw에 대한 처리가 정확히 마무리되지 않아서 잔상이 남는 버그 (Windows XP, Windows Server 2003)가 발생하곤 합니다. 이 문제를 해결하기 위한 두 가지 방법을 소개하고자 합니다. 참고로 이 버그는 Windows NT 4.0 + Delphi 5.0 환경에서도 발견이 되었으며 이에 그치지 않고 Delphi 2007 + Windows 2000/XP/Server 2003에서도 여전히 나타나는 버그로 보입니다.

Solution 1: 제일 손쉬운 방법은 TStatusBar 컨트롤의 부모를 TForm이 아닌 TPanel로 하여 TPanel을 대신 TForm에 올려놓는 방법입니다. 이렇게 하면 고질적인 Owner Draw 렌더링 버그를 원천적으로 해결하고 멤버 변수 선언에도 영향을 주지 않으므로 리팩터링이 필요하지 않습니다.

Solution 2: 이미 디자인타임에서 컨트롤을 많이 사용하였고 마우스로 조작하기 까다로운 UI를 가진 창에서 문제가 발생한다면 - 또는 - 프로그래밍 방식으로 문제를 해결할 필요가 있다면 다음의 지침에 따라 WndProc 메서드를 추가 구현합니다.

Procedure TForm1.WndProc(var Message: TMessage);
Begin
   Case Message.Msg Of
      { 이런저런 메시지 처리 코드가 포함될 것입니다. 생략합니다. }
      WM_DRAWITEM:
      Begin
         If (Message.WParam) <> 0 And (Message.LParam) <> 0 Then
         Begin
            With PDrawItemStruct(Message.LParam)^ Do
            Begin
               If ctlType = ODT_TYPE Then
                  ctlType := 0;
            End;
         End;
      End;
   End;
   { 상위 윈도 메시지 처리기를 호출하지 않으면 문제가 발생하므로 꼭 확인합니다. }
   Inherited WndProc(Message);
End;

굵게 표시한 코드가 문제를 해결하는데 필요한 코드와 잊지 말아야 할 코드들입니다. 만약 WndProc 메서드를 처음 오버라이드하는 경우 상위 메시지 처리기 호출을 놓치기 쉽습니다. 폼을 생성하지 못하고 에러 메시지만 나온 후 프로그램이 바로 종료되어버린다면 반드시 WndProc 메서드가 상위 메시지 처리기를 올바르게 호출하고 있는지 점검해야 하겠습니다.

도움이 되셨기를 바랍니다. :-)

자료 출처: http://buglist.jrsoftware.org/generated/entry0624.htm

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

종전의 Delphi 7.0 기반 프로젝트를 Delphi 2007으로 마이그레이션하면서 드디어 Delphi 2007에서 개인적으로 가장 쓰고 싶었던 기능인 msbuild 지원을 시험해보았다. msbuild는 .NET Framework 2.0때부터 추가된 기능으로 Unix의 make나 Java의 Ant와 같은 통합 빌드 도구이다.

마이그레이션했던 프로젝트의 규모가 상당히 크고 프로젝트의 수가 많았던 터라 "한번에 빌드하는 것"의 중요성이 참으로 크다. Delphi 2007 이전 버전의 경우에는 이런 일괄 빌드를 위하여 make 유틸리티 등을 도입하여야만 했지만 Delphi 2007은 프로젝트 파일 자체를 msbuild와 호환될 수 있게 생성하므로 바로 이용할 수 있다.

무엇보다도 Delphi 2007의 MSBUILD 지원은 Visual Studio 2005/2008 기반의 프로젝트와 같이 운용될 수 있기 때문에 그 의미가 더 크다. IDE 자체적으로 프로젝트 파일을 공유한다거나 하는 것은 아니지만 MSBUILD라는 단일 도구를 사용할 수 있다는 점이 참으로 즐거운 일이다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)