퇴근5분전

# WCF 간단예제
 - ABC ( Address<where>, Binding<how>, Contract<what> )
 인터넷 찾아보면 많이 나오니까 ... 건너뛰고 코드로 정리해본다. 

 

# WCF 라이브러리 프로젝트

    . IASvc
    . IBSvc
    . WebSvc : IASvc, IBSvc

 

# Console 프로젝트
      . CustomIDPWDValidator : System.IdentityModel.Selectors.UserNamePasswordValidator 
          - 사용자 정의 로그인 인증 처리 클래스  ( mmc 에서 인증서 복사 ( 개인용 -> 신뢰할수 있는 사용자 ) )
      . Main()에 구현
      {
            서비스 구현
      }

 

# Window 서비스 : 추후에 윈도우 서비스에 올려 사용 

      . 서비스 protected override void OnStart(string[] args)
      {

            ServiceHost = new ( typeof(WCF라이브러리.클래스타입) );

            ServiceHost.Open();

      }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
## windows 인증
 
[[ 서버 ]]
WSHttpBinding bind = new WSHttpBinding(SecurityMode.Message);
bind.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
Uri baseUri = new Uri("http://localhost:9633/Package");
 
using (ServiceHost host = new ServiceHost(typeof(WebSvc), baseUri))
{
    ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
    smb.HttpGetEnabled = true;
    host.Description.Behaviors.Add(smb);
 
    // http://localhost:9633/Package/SVC_A/
    host.AddServiceEndpoint(typeof(IASvc), bind, "SVC_A"); // SVC_A 적용.
    // http://localhost:9633/Package/SVC_B/
    host.AddServiceEndpoint(typeof(IBSvc), bind, "SVC_B"); // SVC_B 적용.
 
    Console.WriteLine("host Starting... Stop is press Any key!");
    host.Open();
 
    Console.ReadKey();
    host?.Close();
}
 
 
[[ 클라이언트 ]]
string url = "http://localhost:9633/Package/SVC_A/";
System.ServiceModel.WSHttpBinding binding = 
new System.ServiceModel.WSHttpBinding(System.ServiceModel.SecurityMode.Message);
binding.Security.Message.ClientCredentialType = 
System.ServiceModel.MessageCredentialType.Windows;
System.ServiceModel.EndpointAddress endPoint = 
new System.ServiceModel.EndpointAddress(url);
 
using (PackageService.ASvcClient clnt = new PackageService.ASvcClient(binding, endPoint))
{
    //같은 pc일때는 주석해도 동작! 다른 pc에서 접근시 대상pc의 계정, 비밀번호가 필요함.
    //clnt.ClientCredentials.Windows.ClientCredential = 
new System.Net.NetworkCredential("계정""비밀번호");
    MessageBox.Show(clnt.Hello_ServiceA());
}
// 서비스 B도 위와 동일함. 
 
 
 
 
 
## 사용자정의 인증처리. ( 운용시 X.509 인증서 필요 )
 
[[ 서버 ]]
WSHttpBinding bind = new WSHttpBinding(SecurityMode.Message);
bind.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
Uri baseUri = new Uri("http://localhost:9633/Package");
 
using (ServiceHost host = new ServiceHost(typeof(WebSvc), baseUri))
{
    ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
    smb.HttpGetEnabled = true;
    host.Description.Behaviors.Add(smb);
 
    host.Credentials.ServiceCertificate.SetCertificate(
            System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
            System.Security.Cryptography.X509Certificates.StoreName.My,
            System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectName,
"{등록한 인증서명}");
 
   host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = 
System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
   host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = 
new CustomIDPWDValidator(); // 사용자정의 id,pwd 인증
 
    // http://localhost:9633/Package/SVC_A/
    host.AddServiceEndpoint(typeof(IASvc), bind, "SVC_A"); // SVC_A 적용.
    // http://localhost:9633/Package/SVC_B/
    host.AddServiceEndpoint(typeof(IBSvc), bind, "SVC_B"); // SVC_B 적용.
 
    Console.WriteLine("host Staring... Stop is press Any key!");
    host.Open();
 
    Console.ReadKey();
    host?.Close();
}
 
 ** 테스트 인증서 발급
 (( 비주얼스튜디오에서 솔루션 트리 > 
프로젝트에서 우측버튼 > Open In Terminal 하면 파워쉘 뜨는데 아래 명령어 실행. ))
 
makecert.exe -sr LocalMachine -ss My -a sha1 -n CN={등록한 인증서명} -sky exchange -pe
 
makecert.exe -sr CurrentUser -ss My -a sha1 -n CN=CLNT -sky exchange -pe 
//클라이언트용인데... 실제 테스트 해보면 이것까지 할 필요는 없다.
 
참고 :
https://www.codeproject.com/Articles/36683/9-simple-steps-to-enable-X-509-certificates-on-WCF
 
 
[[ 클라이언트 ]]
string url = "http://localhost:9633/Package/SVC_A/";
System.ServiceModel.WSHttpBinding binding = 
new System.ServiceModel.WSHttpBinding(System.ServiceModel.SecurityMode.Message);
binding.Security.Message.ClientCredentialType = 
System.ServiceModel.MessageCredentialType.UserName;
System.ServiceModel.EndpointAddress endPoint = 
new System.ServiceModel.EndpointAddress(
    new Uri(url),
System.ServiceModel.EndpointIdentity.CreateDnsIdentity("{등록한 인증서명}")); 
//서버 쪽에 설정된 인증서명
 
using (System.ServiceModel.ChannelFactory<PackageService.IASvcChannel> proxy =
    new System.ServiceModel.ChannelFactory<PackageService.IASvcChannel>(binding, endPoint))
{
    /*MESSAGE, USERNAME 일때 로그인 인증*/
    proxy.Credentials.UserName.UserName = "로그인ID";  
//pc계정아님 - wcf통신 인증을 위한 로그인ID - 시스템 사용자id( 예:erp로그인 아이디 )
    proxy.Credentials.UserName.Password = "로그인PWD"
//pc계정아님 - wcf통신 인증을 위한 로그인PWD - 시스템 사용자id pwd( 예:erp로그인 아이디 pwd )
    proxy.Credentials.ServiceCertificate.Authentication.CertificateValidationMode =
        System.ServiceModel.Security.X509CertificateValidationMode.None;
    /*MESSAGE, USERNAME 일때 로그인 인증*/
 
    PackageService.IASvcChannel ch = proxy.CreateChannel();
    MessageBox.Show(ch?.Hello_ServiceA());
}
 
 
 
cs

 

사용자정의 로그인처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CustomIDPWDValidator : System.IdentityModel.Selectors.UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (userName == "로그인ID" && password == "로그인PWD")
            {                
                Console.WriteLine($"[{userName}] 로그인");
                return;
            }
            else
            {
                throw new System.IdentityModel.Tokens.SecurityTokenException("땡!");
            }
        }
    }
cs

 

서버 서비스쪽에서 클라이언트 ip 정보를 얻을때 아래 remote 객체에 .Address에 있음

1
2
3
4
5
OperationContext oc = OperationContext.Current;
System.ServiceModel.Channels.RemoteEndpointMessageProperty remote =
     oc.IncomingMessageProperties[System.ServiceModel.Channels.RemoteEndpointMessageProperty.Name]
         as System.ServiceModel.Channels.RemoteEndpointMessageProperty;
 
cs

 

서버에서 등록한 인증서명 = {등록한 인증서명} 을

클라이언트에서 EndpointAddress 설정시 url과 EndpointIdentity.CreateDnsIdentity("{등록한 인증서명}")으로

등록하여야 함.

만약 등록하지 않고 다른 코드처럼 하면 아래와 같은 에러가 뜸.

 System.ServiceModel.Security.MessageSecurityException

  HResult=0x80131501

  메시지=보내는 메시지의 ID를 검사하지 못했습니다. 원격 끝점의 필요한 DNS ID가 'localhost'이지만 원격 끝점이 DNS 클래임 '{등록한 인증서명}'을(를) 제공했습니다.

   이 끝점이 올바른 원격 끝점인 경우 채널 프록시를 만들 때 DNS ID '{등록한 인증서명}'을(를) EndpointAddress의 Identity 속성으로 명시적으로 지정하여 이 문제를 해결할 수 있습니다. 

 

서버에서 인증서 등록!! 확인

   1. 커맨드창에 mmc <- 실행.

   2. 실행후 파일 > 스냅인 추가/제거 클릭하면 아래처럼 팝업.

   3. 인증서 추가 > 인증서 스냅인 ( 내 사용자 계정 ) 으로 선택 후 마침.

   4. 테스트 인증서 복사 : 개인 인증서에서 >> 신뢰할수 있는 사용자 인증서로 복사!! 

      : 운영 일때는 조금 다르겠지?

mmc 실행창이 뜬 후 인증서 스냅인 추가.

mmc 스냅인 사용하여 등록... 

docs.microsoft.com/ko-kr/dotnet/framework/wcf/feature-details/how-to-view-certificates-with-the-mmc-snap-in

 

 

방법: MMC 스냅인을 사용 하 여 인증서 보기 - WCF

보안 WCF 클라이언트 또는 서비스는 인증서를 자격 증명으로 사용할 수 있습니다. MMC 플러그 인을 사용 하 여 검사할 수 있는 인증서 저장소의 유형에 대해 알아봅니다.

docs.microsoft.com

 

# 테스트 했을때

 

 

'# 4) .Net ( Vs 2010 ) > 차세대 WPF, WCF' 카테고리의 다른 글

Progress ] 로딩표시 창!  (0) 2015.10.15
WPF] Numeric TextBox  (0) 2015.09.15

 

 WPF 로딩창... 이틀 힘들었넹..

 

 

 

BTN을 누르면 로딩창이 떠서 돌다가 프로세스가 끝나면 창이 닫힌다!

 

이 기능을 구현하는데 ... WPF로 하려니 대박.. ㅡ.,ㅡ;; 뭐가 이렇게 안되는게 많지? ( 모르는 걸꺼야... )

 

조건 :

  1. GIF 의 로딩 애니메이션과 관련해서 배경이 투명임.

  2. 오래 걸리는 프로레스처리 기간동안 화면에 떠서 땡글 땡글 돌고 있어야 한다.

  3. 처리가 완료되면 자동으로 닫혀야 한다.

 

여기서 WPF로 쉽게 접근해본다고...

 

처음 WebBrowser로 시작 Resource에 넣었는데... WebBrowser에 전달을 어떻게 하지?...

Html문서로 만들면... 이미지를 어딘가 올려놔야 될텐데.. 로컬경로라 해도... 배포문제도 있공...

좀 해보다가 Pass

 

다음 구글링 하니 MediaElement 로 하는게 있는데 이것도 리소스랑 연결을 못해서 ... ㅡ.ㅡ;;

 

또 winform의 PictureBox 이건 간단하게 되었다.

 

단... 여기서 완전 삽질 시작...

WPF.Window창에 설정을 마치고

     WindowsFormsHost 에 PictureBox를 넣고 리소스를 연결했다.

          GIF의 바탕이 원래 투명인데!!!

          윈도우 창의 AllowsTransparency="True" 를 설정하고 폼을 띄우니 배경이 흰색이 나온다!

          흰색을 없애보려고 별짓다하다가 두손두발 들었다.

     

그래서 Winform을 프로젝트에 추가해서 PictureBox에 이미지를 넣었다.

 

로딩폼을 띄웠다가 접었다 해야되는데!!!

 

구글링 하면 WPF 관련해서 여러가지 뜨는데...

 음...  처음부터 그냥 윈폼처럼 구현할것을... WPF라고 죄다 찾다가 삽질만...

덕분에 좀더 많이 뒤져봤지만...

 

 

 # WPF - Window 와 Form 부모창 셋팅할수 있는 방법

 http://stackoverflow.com/questions/1095763/how-to-set-a-wpf-window-as-the-owner-of-a-winforms-form

 

 # 단순 비동기 대리자 호출만으로 뺑글뺑글이 안되서 쓰레드!

   WPF ... 쓰레드... 허허

   

 -- 그나마 코드를 많이 손본 소스!

 

            trd = new System.Threading.Thread(() =>
            {
                w = new ProgressBarForm();
                w.FormClosed += delegate {
                      System.Windows.Threading.Dispatcher.CurrentDispatcher.InvokeShutdown();  // Step2
                };
                w.Show();
                System.Windows.Threading.Dispatcher.Run();  // Step1

                int a = 0; // Step3

            });
            trd.SetApartmentState(System.Threading.ApartmentState.STA);
            trd.IsBackground = true;
            trd.Start();

 

 이 쓰레드가 돌면 w.Show 한 후 Run에서 블럭이 걸린다.

 와... 이걸 몰라서 계속 a = 0으로 안빠져서 이 쓰레드가 미쳤나 했다.

폼이 닫힐때 Step2에서 ShutDown 되면 그때서야 빠진다.

 

이건 또 다른 방법 어차피 똑같음!

 

            trd = new System.Threading.Thread(() =>
            {
                System.Windows.Threading.DispatcherFrame df = new System.Windows.Threading.DispatcherFrame();
                w = new ProgressBarForm();
                w.FormClosed += delegate {
                    df.Dispatcher.InvokeShutdown(); // Step2
                };
                w.Show();
                System.Windows.Threading.Dispatcher.PushFrame(df);  // Step1

                int a = 0;  // Step3

            });
            trd.SetApartmentState(System.Threading.ApartmentState.STA);
            trd.IsBackground = true;
            trd.Start();

 

음 WPF 프로젝트니까 처음 Xaml로 시작했다가 결국엔 윈폼이네...

 

GIF 배경색이 투명으로만 나왔어도 쉽게 가는거였는뎅...

 

구글링에 삽질에... 암튼..

 

정비 하고 또 테스트 하고 하다보니 ...

 

 결국엔 윈폼 코드가 되버렸넹.. 크크..

생각보다 WPF 어려운데... 낼부터 좀더 파고들어봐야징.

  -- 최종 소스 ㅡ.ㅡ;? 

            trd = new System.Threading.Thread(() =>
            {
                ProgressBarForm w = new ProgressBarForm();
                Func<System.Windows.Forms.DialogResult> showDialogDelegate = w.ShowDialog;
                showDialogDelegate.BeginInvoke(ir => showDialogDelegate.EndInvoke(ir), null);

                IsRunning = true;

                while (IsRunning) System.Threading.Thread.Sleep(234);

                w.Invoke(new Action(() =>
                {
                    using (w)
                    {
                        w.Close();
                    }
                    w = null;
                }));
            });
            trd.SetApartmentState(System.Threading.ApartmentState.STA);
            trd.IsBackground = true;
            trd.Start();

 

-- 소스는 ( 숨겨둠! )

 

 

오늘 ( 2015-10-02 ) CodeProject 갔더니.

http://www.codeproject.com/Tips/1035207/Number-Only-Behaviour-for-WPF

 

내가 만든 프로그램 버그 : Ctrl + C눌렀는데 복사가 안되고 붙여넣기가 된다?

>> 집에서 수정해놓았던 소스는 C , V 코드가 이미 수정되어 있네...

 

>> 숫자 이외에 붙여넣기 할때 붙여지는 현상

  SetValue 에서 valueString의 데이타 타입확인 후 변환 후 콤마 찍고 바인딩 하도록 수정함!

 

#########################################################################

 

WPF Numeric 컨트롤이 없을줄이야 ㅡ.,ㅡ^

 

구글링 해도 없네..?

 

거기다가 ... ErrorProvidor가 없어 ㅡㅡ;?

 

예제들이 모두 바이딩을 해야 되다뉘???

 

 

 

 

쨔안!~~~ 만들었다.

 

컨트롤을 만들었다기 보다.. Textbox에 Template 을 이용해서 만들었다.

 

위 예로는 5, 2 <-- Numeric( 5, 2 ) 로 만들어진 컬럼을 대상으로 컨트롤 입력/출력을 함!!

 

입력할때마다 콤마 콤마가 자동으로 위치잡고 소수점 찍으면 뒤로는 한자리씩 교체되고...

 

유효성 체크도 한방에 주르륵... 처리 해주고..

 

이게 컨트롤로 만든게 아니라서 ... 뭐... 필요할때쯤 되면 만들어 보든가~

 

### 디버깅..

 

 

 

ReadOnly 스타일을 넣었고,

정수만 처리할때 - 자리를 처리 하지 않도록 뺌.

Control + c, v, x 처리.

 

디버깅을 하다가 예상하지 못한 일을 격었다.

 

SetValue( NumberText1, 99999999.99999999999 )   로 지정하였을때!!

 

double 인데 문자열로 변환 했더니 반올림이 되어서 나온다?

 

음... 일단 컨트롤에 자리수가 8, 2 이기 때문에 소수 2자리 뒤에 문자들은 반올림 없이 잘라내려고

 

문자열로 만드는 과정에서 당황했었다.

 

찾은 방법은 r !! n, d, x등등 봤는데... r... 이건 반올림 없이 문자열로 만들어준다.

뒤에 자르고 콤마를 넣어주며 마무리!!!

 

### 수정본 ### 소수점처리가 잘못되어있었넹... , 파라미터 부분에서 포맷도 제거 함.

 

private string GetNumberFormat(object dm, NumericTextBoxInfo info)
        {
            string number = "" + dm;

            if (info.Scale > 0)
            {
                // 소수점뒷자리를 잘라냄.
                if (number.IndexOf('.') >= 0)
                {
                    string[] nums = number.Split('.');
                    number = nums[0] + ".";
                    if (nums[1].Length > info.Scale)
                    {
                        number += nums[1].Substring(0, info.Scale);
                    }
                    else
                    {
                        number += nums[1].PadRight(info.Scale, '0');
                    }
                }
                else
                { 
                    number = number + "." + "".PadRight(info.Scale, '0');
                }
            }
            else
            {
                if (number.IndexOf('.') >= 0)
                {
                    number = number.Remove(number.IndexOf('.'));
                }
            }
            /*뒤에서부터 콤마 찍고 온다..*/
            int start = number.Length;
            if (number.IndexOf('.') > 0) {
                start = number.IndexOf('.');
            }

            for (int end = start - 3; end >= 0; end -= 3 )
            {
                number = number.Insert(end, ",");
            }

            return number.TrimStart(',');
        }

 

 

하핫.. 재미있당.

 

소스 hidden~ ( 갱신 20150915 )