프로그램에서 엑셀(Excel)을 다루어 보자.(5)

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (3/22/2011 4:19:15 PM) Viewing : 3830

한동안 문제 없이 잘 사용하던 컴포넌트가 문제가 생겼습니다.

저는 아직 VS2008을 사용하고 있어서 몰랐었는데 오늘 팀원(연X정 씨)이 문제 제기를 해와서 살펴 보았습니다.

이전 설명에서도 언급했듯이 apach.poi 라는 어셈블리는 자바 기반입니다.

.net 에서는 j# 으로 컴파일되어 있어서 이를 구동하기 위해서는 j# 재배포 패키지가 필요합니다.

그런데.....

VS 2010에서는 CLR 구조가 바뀌어서...아님..ms에서 j#은 버린건지도 모르겠습니다만...문제가 생깁니다.

vjsnativ.dll 라는 어셈블리를 찾을 수 없다는 메시지를 던집니다.

넵..이런 오류가 되겠습니다.

 

열심히 구글링 해보았습니다.

저와 같은 문제를 가지고 있는 많은 개발자들의 글을 볼 수 있었고 trick 이지만..이에 대한 해결책을 제시한 글을 보았습니다.

http://blogs.windwardreports.com/davidt/2011/02/calling-j-code-from-net-40.html

별거 없습니다.

동적으로 해당 경로의 dll을 읽어 오는게 되겠습니다.

/// 
/// 어셈블리를 가져옵니다.
/// 
/// Name of the lp file.
/// 
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr LoadLibrary(string lpFileName);

이는 WIN32 API를 PInvoke로 설정한 메서드입니다.

이제 이 메서드를 이용하여 호출합니다.

/// <summary>
/// <para><see cref="ExcelHandler"/>의 정석 생성자입니다.</para>
/// <para>.NET Framework 4.0 이상에서는 J# 불러 올 수 없습니다.</para>
/// <para>아래 코드는 win32 API를 이용하여 직접 어셈블리를 읽어 오는 코드입니다.</para>
/// <para>반드시 J# 재배포 패키지 2.0 이 설치되어 있어야 합니다..</para>
/// </summary>
/// <remarks></remarks>
static ExcelHandler()
{
    if (Environment.Version.Major >= 4)
    {
        string folder = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), @"..\Microsoft.NET\Framework\v2.0.50727");
        folder = System.IO.Path.GetFullPath(folder);
        LoadLibrary(System.IO.Path.Combine(folder, "vjsnativ.dll"));
    }
}

위에서 보듯이 정적 생성자에 정의 하였습니다..

한번만 불러 오면 되기 때문이겠죠..

76 행을 보게 되면 버전 체크를 합니다.Major 버전이 4 이상이면 즉 닷넷 프레임워크의 버전이 4 이상이면 아래 루틴을 타겠지요..

^^;;

 

저도 이제 슬슬 VS2010으로 가봐야 할 때가 온거 같기도 하네요..ㅡ.ㅡ;;;


마지막 업데이트 : (3/22/2011 4:26:13 PM)




첨부 파일 보기 (1)
Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기

RPNetworks.Resources - 03

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (1/4/2011 11:59:35 AM) Viewing : 2040

마지막글이 되겠네요.

리소스 작업을 하다 보니까 자주 쓰이는 것들이 눈에 띄게 되었습니다.

첫 글에도 언급했듯이 대부분 인증관련 내용이나 게시판 관련 내용이 되겠습니다.

자주쓰는 TEXT 를 미리 리소스로 만들어 놓으면 사용하기에 참 용이할 것 같다는 생각이 들었습니다.

그러나 이 구조는 단위 네임스페이스당 하나의 리소스 형식이라 공용으로 뽑기에는 코드를 좀 수정해야 했죠.

어쨋든 그래서 생성한 클래스가 CommonLocalizedEnumConverter 입니다.

namespace RPNetworks.Resources 
{ 
    public class CommonLocalizedEnumConverter : ResourceEnumConverter 
    { 
        public CommonLocalizedEnumConverter(Type type) 
            : base(type) 
        { 
            base.ResourceManager = new CustomResourceManager(ResourceType.String).ResourceManager; 
        } 
    } 
}

네임스페이스를 보면 알겠지만 이 클래스는 ResoruceEnumConverter를 상속 받았지만 네임스페이스는 현재 위치 그대로 입니다.

그리고 자주 쓸만한 열거형을 모아서 한번 만들어 보았습니다.(근데 이게...참 까탈스럽더군요..ㅡ.ㅡ;;;;)

AuthMessage 나 BoardMessage 같은 열거형이 바로 그것입니다.

이것을 사용하기 위한 방법은 아래 코드와 같습니다.

this.lblCommon.Text = CommonLocalizedEnumConverter.ConvertToString(BoardMessage.BoardCreated);

뭐 별거 없네요..ㅋ~

샘플을 한번 만들어 보았습니다..

아주..귀찮네요..ㅡ.ㅡ;;;;; 그래도 글빨이 안서는 지라.....


마지막 업데이트 : (1/5/2011 5:09:08 PM)




첨부 파일 보기 (1)
Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기

RPNetworks.Resources - 02

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (1/4/2011 11:48:41 AM) Viewing : 2204

이전 글에 이어서 이번에는 열거형을 이용한 리소스관리를 해 보도록 하겠습니다.

이 내용은

http://www.codeproject.com/csharp/LocalizingEnums.asp

게시물의 코드를 참조했습니다.

열거형의 값을 리소스화 시켜 다국어로 보여 주는 게 되겠습니다. 참 기가막히도록 참신한 아이디어네요..

메인 클래스는 CustomResourceConverter 라는 클래스입니다.

코드 설명은 제가 만든게 아니기 때문에 생략 (ㅡ.ㅡ;;;) 합니다.

어쨋든 이 클래스를 이용하여 작업을 해보도록 하겠습니다.

이전 글에서 살펴 보았던 IShop.Resources 를 계속 사용해 보도록 하겠습니다.

아래와 같은 열거형이 있다고 가정합니다.

public enum Message
{
    Ok,
    Cancel,
    Yes,
    No
}

사용자 화면에는 Ok 의 경우 "확인" 으로 보여 주고 싶다고 합니다. 그럼 작업을 시작해 보지요.

먼저 IShop.Resources 에 아래와 같은 클래스를 생성합니다.

namespace IShop.Web
{
    public class LocalizedEnumConverter : ResourceEnumConverter
    {
        public LocalizedEnumConverter(Type type) : base(type)
        {
            base.ResourceManager = new CustomResourceManager(ResourceType.String
                              , typeof(LocalizedEnumConverter)).ResourceManager;
        }
    }
}

앞서 방법과 마찬가지로 네임스페이스 이름에 주의 하시기 바랍니다.

이제 리소스 파일을 생성해 보도록 하겠습니다.

이전에 글에서 생성하였던 리소스파일을 계속 사용합니다.

IShop.Web.String.Resources 라는 파일이 생성되었지요?

이 파일을 계속 사용합니다. 참..깜박 한 것이 있는데 이 파일의 경로는 반드시 Resources 라는 폴더에 위치해야 합니다.

즉 IShop.Resources 프로젝트에서 /Resources/IShop.Web.String.Resources 가 되겠습니다.

리소스 파일을 열고 Key 와 Value 를 입력합니다.

key:Message_Ok, value:확인
key:Message_Cancel, value:취소
key:Message_Yes, value:예
key:Message_No, value:아니오

"열거형의 이름_열겨형의 값" 형식이 됩니다.

그리고 마지막으로 열거형에 TypeConverter 특성을 붙여 줍니다.

[TypeConverter(typeof(LocalizedEnumConverter))]
public enum Message
{
    Ok,
    Cancel,
    Yes,
    No
}

이제 다 되었습니다.

이 값을 사용하기 위한 코드는 아래와 같습니다.

string ok = LocalizedEnumConverter.ConvertToString(Message.Ok);
//ok = "확인"

간단하지욤???


마지막 업데이트 : (1/5/2011 5:10:34 PM)




Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기

RPNetworks.Resources - 01

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (1/3/2011 9:34:48 PM) Viewing : 2048

이번에 알아 볼 것은 리소스 관리 입니다.

이미 예전에 리소스 관련된 글을 쓴적이 있습니다.

http://bangsil.net/MyPosts/DotNet/1a654797-8893-4c7c-9e93-c979ad67b2fa

기본적으로 리소스는 하나의 프로젝트의 Properties 라는 폴더의 하위에 위치하게 됩니다.

이 리소스는 이미지나 문자열 아이콘 파일등등 어느 것이든 가능합니다.

여러개의 프로젝트가 있는 솔루션의 경우에는 각 프로젝트마다 그 프로젝트에 해당되는 리소스가 존재하게 될테고 이를 관리 하는 것은 참 까탈스럽습니다.

그래서 이렇게 뿔뿔이 흩어져 있는 리소스를 하나의 프로젝트로 모아서 관리 할 수 있는 방법은 없을까 하고 고민하게 되었습니다.

기본적인 샘플은 위 링크에서 다운 받아서 테스트 해 보시기 바랍니다.

그리고 위 링크에서 사용되었던 버전과 달라진 점이라면 자주쓰는 데이터는 CommonMessage 형태로 따로 뽑아 두었습니다. 대부분 인증이나 게시판 관려 메시지가 되겠네요..

우선 하나씩 살펴 보도록 하겠습니다.

public enum ResourceType
{
    /// 
    /// 아이콘 타입입니다.
    /// 
    Icon = 0,
    /// 
    /// 문자열 타입입니다.
    /// 
    String,
    /// 
    /// 이미지 타입입니다.
    /// 
    Image,
    /// 
    /// Object 타입입니다.
    /// 
    Object
}

이 어셈블리를 이용하여 사용할 수 있는 리소스 형식은 위와 같습니다.

 이 어셈블리에는 중요한 클래스가 2개가 있습니다.

그중 하나는 CommonLocalizedEnumConvertoer 이며 또 하나는 CustomResourceManager 클래스입니다.

전자의 경우에는 열거형을 지역화 하는 경우에 사용하는 클래스입니다. 후자의 경우는 열거형이 아닌 경우에 사용하는 클래스입니다.

후자의 경우에는 별도로 Proxy 클래스가 필요로 합니다. 이 글의 첫 링크에 있는 예제는 바로 후자의 것이 되겠네요.

우선 CumstomResourceManager 클래스를 이용하는 법을 알아 봅시다.

public CustomResourceManager(ResourceType resourceType, Type type)
{
    bool hasResource = false;
    Assembly asm = Assembly.GetCallingAssembly();
    string resourceName = asm.GetName().Name + ".Resources." 
                        + type.Namespace + "." + resourceType.ToString();
    foreach (string reName in asm.GetManifestResourceNames())
    {
        if (reName.Substring(0, reName.LastIndexOf('.')) == resourceName)
        {
            hasResource = true;
            break;
        }
    }
    if (!hasResource)
    {
        throw new MissingManifestResourceException("해당 타입의 리소스를 찾을 수 없습니다.");
    }
    else
    {
        rm = new ResourceManager(resourceName, asm);
    }
}

ResourceType은 앞서 보았던 열거형입니다.
즉 매개변수로 해당 리소스 파일이 있는지 찾고 찾게 되면 ResourceManger 의 인스턴스를 생성하는 코드가 되겠습니다.

이 룰에 적합하게 하기 위해서는 리소스파일의 경우 {네임스페이스}.String.{CultureCode(생략가능)}.resources  형식이 되어야 합니다.

또 다른 생성자는 각 네임스페이스의 리소스가 아니라 공용리소스를 생성하기위한 생성자입니다.

이 클래스에는 리소스를 가져오는 여러 메서드가 있습니다만 대동소이합니다.

예를 들어 리소스를 문자열로 가져오기 위해서는 GetString 메서드를 호출합니다 코드는 아래와 같습니다.

public string GetString(string key)
{
    if (key.Length == 0)
    {
        return null;
    }
    try
    {
        return rm.GetString(key);
    }
    catch
    {
        return null;
    }
}

이제 실제로 사용해보도록 하겠습니다.

앞서 잠깐 언급했듯이 이 클래스를 이용하기 위해서는 Proxy 클래스가 필요합니다.

예를 들어 I-Shop  이라는 쇼핑몰 프로젝트를 한다고 칩니다.

그러면 이 솔루션에 해당되는 리소스를 담당하는 하나의 프로젝트를 생성합니다.

예를 들면 IShop.Resources 라는 프로젝트가 되겠습니다.

이 프로젝트는 RPNetworks.Resources 어셈블리를 참조해야 합니다.

프로젝트를 생성했으면 우선 리소스 파일을 생성합니다. 리소스 파일은 Resourcer.net 을 이용하면 편리합니다.

예를 들어 IShop.Web 이라는 프로젝트의 리소스 파일을 생성한다고 칩시다.

리소스 파일의 이름은 IShop.Web.String.resources 가 되겠습니다. 만약 영문이라면 IShop.Web.String.en.resources 가 될테고 문자열리소스가 아니라 이미지 리소스 라면 ISHop.Web.Image.resources 가 될것입니다.

이제 이 프로젝트에 proxy 클래스를 만들어 보겠습니다.

namespace IShop.Web
{
    public class StringProxy
    {
        private static CustomResourceManager rm;

        /// 
        ///  클래스의 정적 생성자입니다.
        /// 
        static StringProxy()
        {
            rm = new CustomResourceManager(ResourceType.String, typeof(StringProxy));
        }
        public static string Download_Migration_01
        {
            get { return rm.GetString("Download_Migration_01"); }
        }
    }
}

1행을 보면 IShop.Web 입니다.

네임스페이스는 현재 네임스페이스가 아니라 이 리소스를 사용할 곳의 네임스페이스를 지정해 주어야 합니다.
IShop.Web 이라는 프로젝트의 문자열 리소스 Proxy 클래스라는 의미가 되겠습니다.

이렇게 proxy 클래스를 생성하게 되면 코드에서는 StringProxy.Download_Migration_01 처럼 사용할 수 있게 됩니다.

간단하지욤?

다음에는 열거형을 사용하는 법을 알아 보도록 하겠습니다.


마지막 업데이트 : (1/5/2011 5:12:26 PM)




첨부 파일 보기 (1)
Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기

RPNetworks.Configuration - 03

현재 주소 복사
트랙백 주소 복사
방실이님의 글 (12/4/2010 3:39:38 PM) Viewing : 1639

이번에는 dbConnection 에 대해서 알아 보도록 하겠습니다.

config 파일 안에는 아래와 같이 구성되어 있습니다.


 
  
   
   
  
 

dbConnection 섹션에는 connectionStrings 컬렉션 밖에 없습니다.

이 컬렉션은 db연결 문자열을 가지고 있습니다.

이 섹션 값을 코드 상에서 직접 볼일은 아마도 웹폼에서는 없을 듯 합니다.윈폼에서는 있겠죠..

RPNetworks.Core - SqlDB 클래스 에서 아래와 같은 코드가 있었습니다.

public SqlDB(string connectionString)
{
    this.connection = new SqlConnection(ConfigurationHelper.GetConnectionString(connectionString));
}

 이 코드는  config에 정의된 연결문자열의 이름 (name 속성) 을 인풋으로 받으면 이를 복호화 하여 SqlConnection 개체를 만드는 코드 입니다.

여기서 사용되는 ConfigurationHelper.GetConnectionString() 메서드가 오늘 살펴볼 가장 중요한 메서드입니다.

그 전에 connectionStrings 의 요소의 속성들에 대해서 잠깐 살펴 보겠습니다.

name 은 키값입니다. 당연히 유일해야 겠죠.

connectionString은 암호화된 문자열입니다.

securityType 는 암호화할 타입입니다.

이는 RPNetworks.Security.SecurityType 열거형의 Int32값입니다. 그 내용은 아래와 같습니다.

public enum SecurityType
{
    /// 
    /// 아리아 알고리즘입니다.
    /// 
    AriaAlgorithm = 0,
    /// 
    /// 시드 알고리즘입니다.
    /// 
    SeedAlgorithm,
    /// 
    /// 128비트 MD5 알고리즘입니다.
    /// 
    MD5,
    /// 
    /// 128비트 SHA1 알고리즘입니다.
    /// 
    SHA1,
    /// 
    /// 256비트 SHA256 알고리즘입니다.
    /// 
    SHA256
}

연결 문자열은 복호화가 가능하여야 하므로 securityType 은 항상 0 아니면 1이어야 겠자만. SEED 보다는 ARIA가 더 빠릅니다.그래서 전 0을 선호합니다.

마지막으로 keySize는 암호화할 키 사이즈입니다. 암호화 형식에 따라 128, 256 등등이 있습니다.
이 부분은 각 암호화 클래스를 참고 하십시오.

이제 GetConnectionString 메서드를 살펴 보도록 하겠습니다.

public static string GetConnectionString(string connectionStringName)
{
    if (ConfigurationHelper.ConnectionHash != null)
    {
        if (ConfigurationHelper.ConnectionHash[connectionStringName] != null)
        {
            return ConfigurationHelper.ConnectionHash[connectionStringName].ToString();
        }
    }
    else
    {
        ConfigurationHelper.ConnectionHash = new Hashtable();
    }
    string returnValue = string.Empty;

    DBConnectionSection section = ConfigurationHelper.Ancestor.DbConnectionSection;
    if (section == null)
    {
        return String.Empty;
    }
    if (!String.IsNullOrEmpty(section.ConnectionStrings[connectionStringName].Parameters["securityType"]))
    {
        if (String.IsNullOrEmpty(section.ConnectionStrings[connectionStringName].Parameters["keySize"]))
        {
            section.ConnectionStrings[connectionStringName].Parameters["keySize"] = "128";
        }

        try
        {
            object secuityProviderBase = null;
            Assembly asm = ConfigurationHelper.IsWebForm 
               ? Assembly.LoadFrom(HttpContext.Current.Server.MapPath("/bin/RPNetworks.Security.dll")) 
               : Assembly.LoadFrom("RPNetworks.Security.dll");
            MethodInfo[] methodInfos = asm.GetType("RPNetworks.Security.SecurityProviderBase")
                                       .GetMethods(BindingFlags.Static | BindingFlags.Public);
            for (int i = 0; i < methodInfos.Length; i++)
            {
                if (methodInfos[i].GetParameters().Length == 2)
                {
                    secuityProviderBase = methodInfos[i].Invoke(null, new object[] { 
                         Convert.ToInt32(section.ConnectionStrings[connectionStringName]
                                                             .Parameters["securityType"]), 
                         Convert.ToInt32(section.ConnectionStrings[connectionStringName]
                                                             .Parameters["keySize"]) 
                    });
                    break;
                }
            }

            if (secuityProviderBase != null)
            {
                MethodInfo info1 = secuityProviderBase.GetType().BaseType.GetMethod("CreateMasterKey");
                info1.Invoke(secuityProviderBase, new object[] { connectionStringName });
                info1 = secuityProviderBase.GetType().BaseType.GetMethod("DecryptFromString");
                returnValue = info1.Invoke(secuityProviderBase, new object[] {
                    section.ConnectionStrings[connectionStringName].Parameters["connectionString"] 
                }).ToString();
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
    else
    {
        returnValue = section.ConnectionStrings[connectionStringName].Parameters["connectionString"];
    }
    ConfigurationHelper.ConnectionHash[connectionStringName] = returnValue;
    return returnValue;
}

ConfigurationHelper.ConnectionHash 라는 해쉬테이블은 한번 읽어온 연결 문자열을 저장해 놓기 위해 사용합니다.

30행 부터는 config에 정의된 값을 기반으로 리플렉션을 이용하여 윈폼과 웹폼일때 각 해당 폴더에 Security.dll 파일을 읽어와서 DecryptFromString() 메서드를 호출하여 복호화 하는 일련의 과정입니다.

복호화가 되면 이를 해쉬 테이블에 저장합니다.

해쉬테이블에 저장하는 이유는 연결 문자열은 아주 빈번하게 사용되지만 이를 호출시 마다 리플렉션을 이용하여 복호화 하는 것은 힘겹기 때문입니다. 이때문에 최초에 한번 복호화 하여 이를 저장한다면 이후 부터는 바로 이 값을 가져다 쓰게 되니 복호화 과정이 생략되게 되는 겁니다.

이리 하여 복호화 된 연결 문자열을 얻게 되었습니다.


마지막 업데이트 : (1/5/2011 5:22:11 PM)




Trackback 보기 (0)
댓글 보기 (0)
댓글 쓰기



<< < 1 2 3 > >>