Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)
닷넷 System.ComponentModel.LicenseManager를 이용한 라이선스 적용

찾아보니, 다음과 같은 글이 눈에 띄는군요.

Licensed Applications using the .NET Framework
; http://www.codeguru.com/columns/experts/article.php/c5469

구현 방식은 예상외로 무척 간단했습니다. 라이선스를 걸어 보호하려는 클래스가 있다면 아래와 같이 LicenseProvider 특성을 걸어주고,

[LicenseProvider(typeof(MyLicenseProvider))]
public class Class1
{
    public Class1()
    {
        LicenseManager.Validate(typeof(Class1), this);
    }
}

다음으로, LicenseProvider를 우리가 구현해 주면 됩니다.

public class MyLicenseProvider : System.ComponentModel.LicenseProvider
{
    public override License GetLicense(LicenseContext context, 
        Type type, object instance, bool allowExceptions) 
    {
        return null;
    }
}

GetLicense 내의 코드 구현은 그야말로 천차만별이라고 보면 되겠습니다. 중요한 것은, 라이선스가 올바르다고 판단이 되면 GetLicense 메서드에서 License 개체(추상 클래스이므로 그것을 상속받은 개체)를 반환해주면 됩니다.




위의 글에서 보면 닷넷 BCL에서 기본 제공되는 LicFileLicenseProvider를 설명해 주고 있는데요. 이건 매우 간단한 거라서, 해당 클래스가 라이선스가 걸린 것임을 나타내는 정도로만 쓰일 뿐 어떤 강제적인 수단을 동원하는 것은 아닙니다. 이 때문에, LicFileLicenseProvider로 보호된 클래스가 있다면 실행 파일과 같은 위치에 "{네임스페이스를 포함한 클래스 이름}.lic"라는 파일명을 두는 것으로 '라이선스 검사'에 통과할 수 있습니다.

제가 테스트 한 예제를 기준으로 한번 살펴볼까요?

우선, 다음과 같은 예제 클래스를 두고,

===== CommercialLibrary 라이브러리 프로젝트의 Class1.cs 파일 내용 =====

namespace CommercialLibrary
{
    [LicenseProvider(typeof(LicFileLicenseProvider))]
    public class LicFileLicensedClass1
    {
        public LicFileLicensedClass1()
        {
            License lic = LicenseManager.Validate(typeof(LicFileLicensedClass1), this);
            if (lic == null)
            {
                Console.WriteLine("lic == null");
            }
            else
            {
                Console.WriteLine(lic);
            }
        }

        public void Do()
        {
            Console.WriteLine("licensed library is activated!");
        }
    }
}

이런 경우, CommercialLibrary.dll을 사용하는 ConsoleApplication1.exe 측에서는 다음과 같은 이름의 lic 파일을 exe 파일과 같은 폴더에 두어야 합니다.

CommercialLibrary.LicFileLicensedClass1.lic

그리고, 그 파일의 내용에는 "{네임스페이스를 포함한 클래스 이름} is a licensed component." 같은 형식의 문자열이 포함되어 있어야 합니다. 예제에서는 다음과 같습니다.

CommercialLibrary.LicFileLicensedClass1 is a licensed component.

보시는 것처럼, 다소 허무하긴 하지만 LicFileLicenseProvider 파일은 상속이 가능하고 IsKeyValid와 GetKey 메서드를 재정의할 수 있기 때문에 약간의 부가적인 코드만 구현해 준다면 상용 라이브러리를 보호하기 위한 어느 정도의 기준은 만족시킬 수 있습니다.




상용 컴포넌트를 사용해 보신 분들은 lc.exe 컴파일러와 친숙하실 텐데요.

System.ComponentModel.LicenseException (MSBuild의 lc.exe 빌드 과정 생략)
; https://www.sysnet.pe.kr/2/0/518

64비트 OS에서의 ChartFX 라이선스 문제
; https://www.sysnet.pe.kr/2/0/537

기왕 여기까지 온 김에, 그동안 무심코 써왔던 lc.exe에 대해 "Licensed Applications using the .NET Framework" 글을 통해서 이해를 해보는 것도 좋겠지요.

사실, lc.exe를 굳이 사용하지 않아도 상관없습니다. 위에서 설명한 것처럼 lic 파일을 실행 파일과 같이 두는 것은 그리 어려운 일이 아니기 때문입니다. 문제는, lic 파일에 2개 이상의 구성요소 정의가 불가능하다는 데 있습니다. 즉, 라이선스를 걸어놓은 구성 요소 10개를 사용중이라면 10개의 lic 파일들이 exe 파일과 동일한 폴더에 배포되어야 한다는 약간의 불편함 정도가 있는 것입니다.

바로 이런 문제를 해결하기 위해 lc.exe를 사용합니다. lc.exe를 이용하면 라이선스 정보를 '하나의 파일에 여러개 정의하는 것'이 가능하고, 게다가 그렇게 정의된 파일을 어셈블리 내의 리소스로 포함하는 것이 가능합니다. 즉, 배포할 때 lic 파일들이 전혀 필요없고 오로지 exe 파일 하나만을 복사하는 것으로 끝낼 수 있다는 장점이 있습니다.

lc.exe 실행 방법을 알아보기 위해 MSDN 도움말을 보면,

License Compiler (Lc.exe)
; https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-2.0/ha0k3c9f(v=vs.80)

/complist 옵션에 주는 파일(ConsoleApplication1Lic.txt)의 내용을 다음과 같이 구성하도록 설명하고 있는데요. (바로 이 파일 내에 여러 개의 구성 요소를 지정해 주는 것이 가능합니다.)

CommercialLibrary.LicFileLicensedClass1, CommercialLibrary.dll

그리고, 위의 파일에 들어가는 구성요소에 대한 lic 파일(이 예제에서는 CommercialLibrary.LicFileLicensedClass1.lic)이 exe 파일과 같은 폴더에 있어야 합니다.

애석하게도 그렇게 모든 준비를 마치고, 실제로 실행해 보면 다음과 같은 식으로 LC0003 오류가 발생합니다.

D:\...[생략]...\bin\Debug>lc /target:ConsoleApplication1.exe /complist:ConsoleApplication1Lic.txt /i:CommercialLibrary.DLL
Microsoft (R) .NET License Compiler
[Microsoft .Net Framework, Version 4.0.30319.225]
Copyright (c) Microsoft Corporation.  All rights reserved.


Processing complist 'ConsoleApplication1Lic.txt'...
ConsoleApplication1Lic.txt (1) : error LC0003 : Unable to resolve type 'CommercialLibrary.LicFileLicensedClass1, Commerc
ialLibrary.DLL'

이거 원인 찾느라고 엄청난 시간을 소비했는데요. ^^; 아래의 예제에 소개된 것을 보고서야 오류를 바로 잡을 수 있었습니다.

.NET Licensing
; http://windowsforms.net/articles/Licensing.aspx

즉, ConsoleApplication1Lic.txt 파일의 내용에서 DLL 확장자를 지정해서는 안되는 것이었습니다.

CommercialLibrary.LicFileLicensedClass1, CommercialLibrary

아래는 실행 결과입니다.

D:\...[생략]...\bin\Debug>lc /target:ConsoleApplication1.exe /complist:ConsoleApplication1Lic.txt /i:CommercialLibrary.DLL
Microsoft (R) .NET License Compiler
[Microsoft .Net Framework, Version 4.0.30319.225]
Copyright (c) Microsoft Corporation.  All rights reserved.


Processing complist 'ConsoleApplication1Lic.txt'...
System.ComponentModel.LicFileLicenseProvider+LicFileLicense
Creating Licenses file consoleapplication1.exe.licenses...

이렇게 생성된 "consoleapplication1.exe.licenses" 파일은 닷넷 어셈블리에 '리소스' 자원으로 포함될 수 있습니다. 하지만, 이렇게 되면 빌드 절차가 복잡해지기 때문에 사실 이런 유형으로는 거의 사용되질 않고 있습니다.

MSbuild에서는 이런 작업을 간단하게 할 수 있도록 프로젝트에 licx라는 특별한 확장자를 가진 파일이 있다면 그것을 '/complist'에 지정되는 파일이라고 여기고 자동으로 lc.exe 컴파일러를 호출하여 .licenses 파일을 생성하고 어셈블리의 리소스로 포함까지 시켜줍니다.

실제로 LicFileLicenseProvider를 기준으로 테스트를 원한다면, 다음과 같은 식으로 솔루션을 구성해 주면 됩니다.

license_manager_compiler_1.png

즉, 사용하려는 응용 프로그램(ConsoleApplication1.exe)측에서 licx 파일을 추가하고 내용은 다음과 같이 구성합니다.

CommercialLibrary.LicFileLicensedClass1, CommercialLibrary

위에서 lc.exe 컴파일을 명령행으로 사용하는 방법을 설명하면서 언급했지만, lc.exe는 /complist 옵션에 지정될 파일 뿐만 아니라, 원래의 구성요소에 대한 lic 파일들이 필요하므로, 라이브러리(CommercialLibrary.dll) 측에서는 CommercialLibrary.LicFileLicensedClass1.lic 파일의 "Copy to output Directory" 속성을 "Copy if newer" 설정으로 해두시면 '편리합니다.' (물론, 배포시에는 lic 파일들은 없어도 됩니다.)

결국, 빌드된 ConsoleApplication1.exe 파일을 .NET Reflector로 확인해 보면 다음과 같이 명령행에서 lc.exe를 실행했을 때 처럼 "consoleapplication1.exe.licenses 리소스 파일이 들어있는 것을 확인할 수 있습니다.

license_manager_compiler_2.png

LicenseProvider와 lc.exe에 대해서 이 정도 알아봤으니, 이제 자신만의 라이선스 체계를 만들어서 LicFileLicenseProvider를 확장하고 싶어질 텐데요. 소스 코드를 구현하기 전에 아래의 글들 중에서 자신이 생각하고 있던 것이 있다면,... ^^ 그대로 가져다 쓰는 것도 좋겠지요.

C#: LicenseManager (and Other uses): Get a Mobile Devices Unique ID 
; http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/1962/C-LicenseManager-and-Other-uses-Get-a-Mobile-Devices-Unique-ID.aspx

C#: Developing a LicenseProvider/License: Getting the Motherboard ID. 
; http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/1963/C-Developing-a-LicenseProvider-License-Getting-the-Motherboard-ID.aspx

C#: Developing a LicenseProvider/License: Getting the Manufacturer’s UID.
; http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/1964/C-Developing-a-LicenseProvider-License-Getting-the-Manufacturer-rsquo-s-UID.aspx

C#: Developing a LicenseProvider/License: Getting the CPU’s ID. 
; http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/1965/C-Developing-a-LicenseProvider-License-Getting-the-CPU-rsquo-s-ID.aspx

참고로, 첨부한 파일은 제가 테스트한 프로젝트 소스입니다.



[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/9/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2012-08-08 02시26분
[전호진] 유용한 정보 도움 많이 되었습니다 ^^
[guest]

... 46  47  48  49  [50]  51  52  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12688정성태6/24/202117660.NET Framework: 1073. C# - JSON 역/직렬화 시 리플렉션 손실을 없애는 JsonSrcGen [2]파일 다운로드1
12687정성태6/22/202114983오류 유형: 729. Invalid data: Invalid artifact, java se app service only supports .jar artifact
12686정성태6/21/202116944Java: 22. Azure - 자바(Java)로 만드는 Web App Service - Java SE (Embedded Web Server) 호스팅
12685정성태6/21/202118160Java: 21. Azure Web App Service에 배포된 Java 프로세스의 메모리 및 힙(Heap) 덤프 뜨는 방법
12684정성태6/19/202116507오류 유형: 728. Visual Studio 2022부터 DTE.get_Properties 속성 접근 시 System.MissingMethodException 예외 발생
12683정성태6/18/202117831VS.NET IDE: 166. Visual Studio 2022 - Windows Forms 프로젝트의 x86 DLL 컨트롤이 Designer에서 오류가 발생하는 문제 [1]파일 다운로드1
12682정성태6/18/202114373VS.NET IDE: 165. Visual Studio 2022를 위한 Extension 마이그레이션
12681정성태6/18/202114658오류 유형: 727. .NET 2.0 ~ 3.5 + x64 환경에서 System.EnterpriseServices 참조 시 CS8012 경고
12680정성태6/18/202116714오류 유형: 726. python2.7.exe 실행 시 0xc000007b 오류
12679정성태6/18/202116822COM 개체 관련: 23. CoInitializeSecurity의 전역 설정을 재정의하는 CoSetProxyBlanket 함수 사용법파일 다운로드1
12678정성태6/17/202115322.NET Framework: 1072. C# - CoCreateInstance 관련 Inteop 오류 정리파일 다운로드1
12677정성태6/17/202118105VC++: 144. 역공학을 통한 lxssmanager.dll의 ILxssSession 사용법 분석파일 다운로드1
12676정성태6/16/202117279VC++: 143. ionescu007/lxss github repo에 공개된 lxssmanager.dll의 CLSID_LxssUserSession/IID_ILxssSession 사용법파일 다운로드1
12675정성태6/16/202115169Java: 20. maven package 명령어 결과물로 (war가 아닌) jar 생성 방법
12674정성태6/15/202116421VC++: 142. DEFINE_GUID 사용법
12673정성태6/15/202117038Java: 19. IntelliJ - 자바(Java)로 만드는 Web App을 Tomcat에서 실행하는 방법
12672정성태6/15/202118659오류 유형: 725. IntelliJ에서 Java webapp 실행 시 "Address localhost:1099 is already in use" 오류
12671정성태6/15/202127326오류 유형: 724. Tomcat 실행 시 Failed to initialize connector [Connector[HTTP/1.1-8080]] 오류
12670정성태6/13/202117303.NET Framework: 1071. DLL Surrogate를 이용한 Out-of-process COM 개체에서의 CoInitializeSecurity 문제파일 다운로드1
12669정성태6/11/202117499.NET Framework: 1070. 사용자 정의 GetHashCode 메서드 구현은 C# 9.0의 record 또는 리팩터링에 맡기세요.
12668정성태6/11/202119999.NET Framework: 1069. C# - DLL Surrogate를 이용한 Out-of-process COM 개체 제작파일 다운로드2
12667정성태6/10/202117795.NET Framework: 1068. COM+ 서버 응용 프로그램을 이용해 CoInitializeSecurity 제약 해결파일 다운로드1
12666정성태6/10/202115445.NET Framework: 1067. 별도 DLL에 포함된 타입을 STAThread Main 메서드에서 사용하는 경우 CoInitializeSecurity 자동 호출파일 다운로드1
12665정성태6/9/202117496.NET Framework: 1066. Wslhub.Sdk 사용으로 알아보는 CoInitializeSecurity 사용 제약파일 다운로드1
12664정성태6/9/202115294오류 유형: 723. COM+ PIA 참조 시 "This operation failed because the QueryInterface call on the COM component" 오류
12663정성태6/9/202117673.NET Framework: 1065. Windows Forms - 속성 창의 디자인 설정 지원: 문자열 목록 내에서 항목을 선택하는 TypeConverter 제작파일 다운로드1
... 46  47  48  49  [50]  51  52  53  54  55  56  57  58  59  60  ...