SFTP 구축 및 연결.pdf
0.19MB

 

 

FTP (File Transfer Protocol)  :   파일 폴더 공유

SFTP (Secure File Transfer Protocol) :  암호화로 통신되는 파일 폴더 공유

 

SFTP 사용을 위해 간단히 정리해보았습니다.

 

서버는 freeFTPd(SFTP무료, FTP무료) 또는 Filezilla(SFTP 유료, FTP무료)

로 사용 되고,

 

클라이언트 (접속) 은 WinSCP / FileZilla 가능합니다.

c++ 랜덤 코드!

 

https://learn.microsoft.com/ko-kr/cpp/standard-library/random?view=msvc-170 

 

<random>

자세한 정보:

learn.microsoft.com

 

#include <iostream>
#include <random>

using namespace std;

int main()
{
	random_device rd;
	mt19937 gen(rd());
	uniform_int_distribution<int> distribution(0, 10); // 0 ~ 10
	
	for (int i = 0; i < 10; ++i)
	{
		cout << distribution(gen) << endl;
	}
}

업무 중 필요해서 만들어 본 금액을 한글로 표시해주는 코드입니다.

만들 때 마다 코드가 달라지는데 기본적인 방식은 비슷하기에 저장..

 

using System;

namespace ConsoleApp2
{
	class Program
	{
		private static readonly string[] num = { "", "일", "이", "삼", "사", "오", "육", "칠", "팔", "구" };
		private static readonly string[] digit = { "", "십", "백", "천" };
		private static readonly string[] word = { "", "만 ", "억 ", "조 ", "경 " }; //띄어쓰기 포함시켰습니다.

		//숫자 -> 한글 값
		public static void Main(string[] args)
		{
			string result = string.Empty;
			string sign = string.Empty;
			string readLine = Console.ReadLine();

			int wordIndex = 0;
			long input = 0;

			if (readLine[0] == '-') //음수 양수 확인
			{
				sign = "-";
			}

			//숫자만 추출 (그냥 만들어본거 ㅋ)
			for (int i = 0; i < readLine.Length; ++i)
			{
				if ('0' <= readLine[i] && readLine[i] <= '9')
				{
					input = input * 10 + readLine[i] - '0';
				}
			}

			while (input > 0)
			{
				string subResult = "";

				for (int i = 0; i < 4; ++i) // 1, 10, 100, 1000 단위별로 확인합니다.
				{
					long val = input % 10;
					input /= 10;

					if (val == 0) //0은 출력하지 않습니다.
					{
						continue;
					}

					if (val == 1 && i != 0) //일천, 일백, 일십에서는 일을 제거해줍니다.
					{
						subResult = digit[i] + subResult;
					}
					else //나머지 숫자들에 대해선 정상 출력해줍니다.
					{
						subResult = num[val] + digit[i] + subResult;
					}
				}

				if (subResult != "") //값이 있다면 추가해줍니다.
				{
					result = subResult + word[wordIndex] + result;
				}

				wordIndex++;
			}

			if (string.IsNullOrEmpty(result))
			{
				Console.WriteLine("영원");
			}
			else
			{
				Console.WriteLine("{0}{1}원", sign, result);
			}
		}
	}
}

SendMessage 와 윈도우 콜백 함수 (WndProc) 를 이용하여 다음과 같이 프로그램 간 통신을 할 수 있습니다.

 

프로그램 간 메시지를 송신/수신하여 처리 된 결과

 

 

[보내는 프로그램]

using System;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsForms_SendProcess
{
    public partial class Form1 : Form
    {
        //명령어 모음
        //https://wiki.winehq.org/List_Of_Windows_Messages
        private const int WM_COPYDATA = 0x4A;

        [DllImport("user32.dll")]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, uint wParam, ref COPYDATASTRUCT lParam);

        public struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;

            public string message;
        }

        public Form1()
        {
            InitializeComponent();

            textBox_processName.Text = "WindowsForms ReceiveProcess";
        }

        private void button_sendMessage_Click(object sender, EventArgs e)
        {
            Process[] process = Process.GetProcessesByName(textBox_processName.Text);
            string message = textBox_message.Text;

            for (int i = 0; i < process.Length; ++i)
            {
                COPYDATASTRUCT cds = new COPYDATASTRUCT();

                cds.dwData = IntPtr.Zero;

                //영어 문자 1byte, 한글 2byte
                byte[] buffer = Encoding.Default.GetBytes(message);

                cds.message = message;

                cds.cbData = buffer.Length + 1;  //보내는 string 길이 + 1 (문자의 끝 의미)

                SendMessage(process[i].MainWindowHandle, WM_COPYDATA, 0, ref cds);
            }
        }
    }
}

 

[받는 프로그램]

using System;
using System.Windows.Forms;

namespace WindowsForms_ReceiveProcess
{
    public partial class Form1 : Form
    {
        //명령어 모음
        //https://wiki.winehq.org/List_Of_Windows_Messages
        private const int WM_COPYDATA = 0x4A;

        public struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;

            public string message;
        }

        public Form1()
        {
            InitializeComponent();
        }

        protected override void WndProc(ref Message m)
        {
            try
            {
                switch (m.Msg)
                {
                    case WM_COPYDATA:

                        COPYDATASTRUCT cds = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT));

                        listBox1.Items.Add(cds.message);
                        listBox1.SelectedIndex = listBox1.Items.Count - 1;
                        break;

                    default:

                        base.WndProc(ref m);
                        break;

                }

            }
            catch (Exception ex)
            {
                MessageBox.Show("WinForm Error : " + ex.Message);
            }
        }
    }
}

URI (별칭식 경로 : Uniform Resource Identifier 통합 자원 식별자)
URI는 특정 리소스를 식별하는 경로를 의미한다. 웹 기술에서 사용하는 논리적 또는 물리적 리소스를 식별하는 고유한 문자열 시퀀스다.

URL (절대 경로 : Uniform Resource Locator 또는 통칭 web address)
URL은 흔히 웹 주소라고도 하며, 컴퓨터 네트워크 상에서 리소스가 어디 있는지 알려주기 위한 규약이다. URI의 서브셋이다.

 

POST 방식은 기본 (URL)에 요청할 때 BODY 부분에 데이터를 넘겨주는 방식입니다.

Spring 공부를 하면 해당 API 들을 직접 호출 및 테스트 해볼 수 있게 만들면 좋겠네요.

POST 방식은 코드만 올리겠습니다.

1개 보내는 방식, multipart 로 2개 이상의 다른 데이터 보내는 방식입니다.

using System;
using System.IO;  //Stream
using System.Net; //HttpWebRequest, WebRequest 등 사용
using System.Windows.Forms;

using System.Net.Security; //SSL (Secure Sockets Layer) 관련
using System.Security.Cryptography.X509Certificates; //암호화 된 인증서 관련
using System.Text;

namespace WindowsFormsApp_POST_HttpRequest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            PostRequest();
        }

        private bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            //인증서에 대한 결과를 보려면 여기서 브레이크 걸고 certificate, chain 등을 살펴보면 됩니다.
            //권장하지 않는 방법이지만 인증 기관에 대해 true로 설정해둡니다.
            return true;
        }

        private void PostRequest()
        {
            //****************************** 이 부분은 첫 사용하기 전에만 넣어주면 됩니다. ***********************************
            //웹 통신 시 System.Net.WebException : 기본 연결이 닫혔습니다. 관련 에러 방지용 프로토콜 정의입니다.
            //Secure Sockets Layer, Transport Layer Secure
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
                                                   SecurityProtocolType.Tls11 |
                                                   SecurityProtocolType.Tls12 |
                                                   SecurityProtocolType.Ssl3;

            //https (hyper text transfer protocol Secure : 전송 규약 보안) 보안이 올바른지 확인하는 구문입니다. 
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
            //****************************************************************************************************************

            //[ContentType] : Body에서 전송되는 콘텐츠의 유형을 서버에 알려줍니다.
            //                MIME (Multipurpose Internet Mail Extensions) 표준을 따릅니다.
            //
            //ex)
            //1. multipart/form-data            2개 이상으로 한 번에 보낼 때 boundary와 함께 사용
            //2. application/json               제이슨 형태
            //3. application/x-www-urlencoded   기본 타입
            //4. image/tiff                     이미지, 확장자
            //   등 등

            //한 개의 데이터를 POST 방식으로 보내기
            PostRequestOne();

            //2개 이상의 데이터를 POST 방식으로 보내기
            //multipart/form-data 방식으로 사용되며 줄 구분을 위한 boundary가 필요   
            PostRequestAll();
        }

        private void PostRequestOne ()
        {
            string uri = textBox_uri.Text;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);   //요청 준비
            HttpWebResponse response = null;

            try
            {
                request.Method = "POST"; //기본 값은 GET 입니다.
                request.ContentType = "application/x-www-form-urlencoded"; //url encoded 방식 변수=값&변수1=값..

                string userName = "name";
                string password = "1234"; //서버와 협의하여 해쉬 등으로 암호화
                string data = string.Format("userName={0}&password={1}", userName, password);

                //request body에 데이터 채우기
                byte[] buffer = Encoding.UTF8.GetBytes(data);
                Stream requestStream = request.GetRequestStream(); //request에 보낼 데이터 입력용
                requestStream.Write(buffer, 0, buffer.Length);
                requestStream.Close();

                //응답 결과 받기
                response = (HttpWebResponse)request.GetResponse();
                Stream responseStream = response.GetResponseStream();
                StreamReader responseStreamReader = new StreamReader(responseStream);

                //응답 값 받아서 리턴하거나 사용하면 됩니다.
                string result = responseStreamReader.ReadToEnd();

                responseStreamReader.Close();
                responseStream.Close();
                response.Close();
            }
            catch (Exception ex)
            {
                //로그 남기면 좋을 듯
                MessageBox.Show("Request Error" + Environment.NewLine + ex.ToString());
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
            }
        }

        private void PostRequestAll ()
        {
            string uri = textBox_uri.Text;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);   //요청 준비
            HttpWebResponse response = null;

            try
            {
                request.Method = "POST"; //기본값은 GET
                                         //그 밖에도 timeout, credential 등 다양한 옵션을 줄 수 있습니다.

                //https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
                //2개 이상 multipart로 보내는 방법
                //보낼 값) field1 = value, 파일 = byte file

                //경계를 통해 데이터 타입 명시 및 다음과 같은 형태로 만들어서 보내면 됩니다.
                /*
                    Content-Type: multipart/form-data; boundary=isBoundary
                
                    --isBoundary
                    Content-Disposition: form-data; name="field1"

                    value
                    --isBoundary
                    Content-Disposition: form-data; name="파일"; filename="myImage.tiff" (filename은 부가적인 부분으로 없어도 된다고 봤네요.)
                    Content-Type: image/tiff (타입에 대한 명시 필요)

                    byte file
                    --isBoundary-- (경계의 끝은 -- 붙여주기)
                 */

                string endLine = "\r\n";            //웹에서 개행을 나타내는 문자열입니다. 
                string boundary = "isBoundary";     //구분자입니다. --isBoundary ~ --isBoundary-- 로 끝나게 됩니다.

                request.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);
                byte[] boundaryBytes = Encoding.ASCII.GetBytes(endLine + "--" + boundary + endLine);

                Stream requestStream = request.GetRequestStream();
                requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                //
                //--isBoundary
                //

                string field1 = "field1";
                string value = "value";
                string data = string.Format("Content-Disposition: form-data; name=\"{0}\"{1}{2}{3}", field1, endLine, endLine, value);
                byte[] dataBytes = Encoding.UTF8.GetBytes(data);

                requestStream.Write(dataBytes, 0, dataBytes.Length);
                //[진행 현황]
                //
                //--isBoundary
                //Content-Disposition; form-data; name="field1"
                //
                //value

                requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                //[진행 현황]
                //
                //--isBoundary
                //Content-Disposition; form-data; name="field1"
                //
                //value
                //--isBoundary
                //

                string name = "파일";
                string filename = "myImage.tiff";

                string data2 = string.Format("Content-Disposition: form-data; name=\"{0}\" filename=\"{1}\"{2}Content-Type: image/tiff{3}{4}",
                                                name, filename, endLine, endLine, endLine);
                byte[] data2Bytes = Encoding.UTF8.GetBytes(data2);

                requestStream.Write(data2Bytes, 0, data2Bytes.Length);
                //[진행 현황]
                //
                //--isBoundary
                //Content-Disposition; form-data; name="field1"
                //
                //value
                //--isBoundary
                //Content-Disposition: form-data; name="파일" filename="myImage.tiff"
                //Content-Type: image/tiff
                //
                //

                //예시 경로 파일 바이트로 읽어서 보내기
                string imagePath = @"c:\temp\myImage.tiff";
                FileStream fileStream = new FileStream(imagePath, FileMode.Open, FileAccess.Read);
                byte[] imageBuffer = new byte[4096];
                int bytesRead = 0;
                while (true)
                {
                    bytesRead = fileStream.Read(imageBuffer, 0, imageBuffer.Length);
                    if (bytesRead <= 0) //이미지 바이트 다 읽은 경우
                    {
                        break;
                    }
                    requestStream.Write(imageBuffer, 0, bytesRead);
                }
                fileStream.Close();

                byte[] endBoundaryByte = Encoding.ASCII.GetBytes(endLine + "--" + boundary + "--" + endLine);
                requestStream.Write(endBoundaryByte, 0, endBoundaryByte.Length);
                //[진행 현황]
                //
                //--isBoundary
                //Content-Disposition; form-data; name="field1"
                //
                //value
                //--isBoundary
                //Content-Disposition: form-data; name="파일" filename="myImage.tiff"
                //Content-Type: image/tiff
                //
                //byte file
                //--isBoundary--

                response = (HttpWebResponse)request.GetResponse(); //요청 후 결과 반환
                Stream responseStream = response.GetResponseStream();   //요청에 대한 stream 가져오기
                StreamReader responseStreamReader = new StreamReader(responseStream); //해당 stream 읽기 위한 streamReader

                //응답값 받아서 리턴하거나 사용하면 됩니다.
                string result = responseStreamReader.ReadToEnd();

                responseStreamReader.Close();
                responseStream.Close();
                response.Close();
            }
            catch (Exception ex)
            {
                //로그 남기면 좋을 듯
                MessageBox.Show("Request Error" + Environment.NewLine + ex.ToString());
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
            }
        }
    }
}

 

URI (별칭식 경로 : Uniform Resource Identifier 통합 자원 식별자)
URI는 특정 리소스를 식별하는 경로를 의미한다. 웹 기술에서 사용하는 논리적 또는 물리적 리소스를 식별하는 고유한 문자열 시퀀스다.

URL (절대 경로 : Uniform Resource Locator 또는 통칭 web address)
URL은 흔히 웹 주소라고도 하며, 컴퓨터 네트워크 상에서 리소스가 어디 있는지 알려주기 위한 규약이다. URI의 서브셋이다.

 

GET 방식은 헤더 부분인 주소(URI)에 변수와 값 을 통해 결과를 얻어오는 방법 입니다.

주소 뒷 쪽에 ? 변수1 = 값1 & 변수2 = 값2

 

식으로 변수와 값을 넘겨줄 수 있습니다. 아래는 구글에 간단하게 hi 를 GET으로 요청한 결과 입니다.

https://google.com/search 에 대해해 뒷 편에 ? q = hi 를 붙인 것이죠.

                                                                    (q는 검색어 변수, hi는 값이 되겠죠)

구글에서 hi 검색한 결과

C#에서 호출 한 소스는 아래와 같습니다.

통신을 위해서 json 을 반환시키기도 하며 200(응답 성공), 400(클라이언트 에러), 500(서버쪽 에러) 를 통해

결과를 알 수도 있습니다.

 

.NET 에 따라 SecurityProtocol 값이 달라질 수 있습니다. (현재 .NET 4.7.2 로 작성 되었습니다.)

(ServicePointManager 첫 연결 시만 등록해주면 됩니다.)

using System;
using System.IO;  //Stream
using System.Net; //HttpWebRequest, WebRequest 등 사용
using System.Windows.Forms;

using System.Net.Security; //SSL (Secure Sockets Layer) 관련
using System.Security.Cryptography.X509Certificates; //암호화 된 인증서 관련

namespace WindowsFormsApp10
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button_request_Click(object sender, EventArgs e)
        {
            //****************************** 이 부분은 첫 사용하기 전에만 넣어주면 됩니다. ***********************************
            //웹 통신 시 System.Net.WebException : 기본 연결이 닫혔습니다. 관련 에러 방지용 프로토콜 정의입니다.
            //Secure Sockets Layer, Transport Layer Secure
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
                                                   SecurityProtocolType.Tls11 |
                                                   SecurityProtocolType.Tls12 |
                                                   SecurityProtocolType.Ssl3;

            //https (hyper text transfer protocol Secure : 전송 규약 보안) 보안이 올바른지 확인하는 구문입니다. 
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
            //****************************************************************************************************************

            string uri = textBox_uri.Text;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);   //요청 준비
            HttpWebResponse response = null;

            try
            {
                response = (HttpWebResponse)request.GetResponse(); //요청 후 결과 반환

                Stream stream = response.GetResponseStream();   //요청에 대한 stream 가져오기
                StreamReader reader = new StreamReader(stream); //해당 stream 읽기 위한 streamReader

                string result = reader.ReadToEnd(); //결과 읽어오기

                reader.Close();
                stream.Close();

                textBox_result.Text = result;
            }
            catch (Exception ex)
            {
                textBox_result.Text = "Request Error" + Environment.NewLine + ex.ToString();
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
            }
        }

        private bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            //인증서에 대한 결과를 보려면 여기서 브레이크 걸고 certificate, chain 등을 살펴보면 됩니다.
            //권장하지 않는 방법이지만 인증 기관에 대해 true로 설정해둡니다.
            return true;
        }
    }
}

 

컴파일러서비스를 통해 어디서 누가 호출을 했는지 확인 할 수 있습니다.

로그를 남길 때 별도의 호출 함수 양식을 작성하지 않고도 사용할 수 있는 방법입니다!

 

DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"); 를 통해

년-월-일 시:분:초

를 정의할 수도 있습니다. 아래는 호출 예제입니다.

 

using System;
using System.Windows.Forms;
using System.Runtime.CompilerServices; //[Caller 사용]

namespace WindowsFormsApp
{
    public partial class Form1 : Form
    {
        Test test = new Test();

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            test.ShowCaller();
            Log("로그 내용");
        }
        
        private void Log (string message, [CallerMemberName] string name = "")
        {
            string log = string.Format("{0} [{1}] : {2}{3}", DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"), name, message, Environment.NewLine);
            MessageBox.Show(log);
        }
    }

    class Test
    {
        public void ShowCaller([CallerMemberName] string name = "", [CallerFilePath] string path = "", [CallerLineNumber] int line = 0)
        {
            MessageBox.Show("name : " + name + "\n" +
                            "path : " + path + "\n" +
                            "line : " + line);
        }
    }
}

대충 만든 폼! ㅎㅎ

 

test.ShowCaller() 내부의 메시지박스

 

Log("로그 내용") 내부의 메시지 박스

 

using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApp9
{
    //Microsoft Hook Document
    //https://docs.microsoft.com/en-us/windows/win32/winmsg/hooks

    public partial class Form1 : Form
    {
        private HookManager hookManager = new HookManager();

        public Form1()
        {
            InitializeComponent();

            this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form1_FormClosed);
            hookManager.SetHook();
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            hookManager.UnHook(); //프로그램이 종료될 때 반드시 후킹을 꺼줘야합니다.
        }
    }

    public class HookManager
    {
        #region WindowsAPI

        [DllImport("user32.dll")]
        private static extern int SetWindowsHookEx(int idHook, LowLevelKeyboardProc callback, int hInstance, uint threadId);

        [DllImport("user32.dll")]
        private static extern bool UnhookWindowsHookEx(int hInstance);

        [DllImport("user32.dll")]
        private static extern int CallNextHookEx(int idHook, int nCode, int wParam, ref HookStruct lParam);

        [DllImport("kernel32.dll")]
        private static extern int LoadLibrary(string lpFileName);

        private delegate int LowLevelKeyboardProc(int nCode, int wParam, ref HookStruct lParam);
        private LowLevelKeyboardProc lowLevelKeyboardProc = HookProc;

        #endregion

        //구조체 크기를 맞춰줘야 올바른 값을 받을 수 있습니다.
        public struct HookStruct
        {
            public int vkCode;
            int scanCode;
            public int flags;
            int time;
            int dwExtraInfo;
        }

        //WM_Keydown
        //https://wiki.winehq.org/List_Of_Windows_Messages
        const int WH_KEYBOARD_LL = 13; //저수준 특수키 관련 Alt + .. Ctrl + ..
        const int WM_KEYDOWN = 256;
        const int WM_KEYUP = 257;
        const int WM_SYSKEYDOWN = 260;
        const int WM_SYSKEYUP = 261;

        private static int hookId = 0;

        public void SetHook()
        {
            int hInstance = LoadLibrary("User32");
            hookId = SetWindowsHookEx(WH_KEYBOARD_LL, lowLevelKeyboardProc, hInstance, 0);
        }

        public void UnHook()
        {
            UnhookWindowsHookEx(hookId);
        }

        public static int HookProc(int code, int wParam, ref HookStruct lParam)
        {
            if (code >= 0)
            {
                switch (wParam)
                {
                    case WM_KEYDOWN:
                    case WM_KEYUP:
                    case WM_SYSKEYDOWN:
                    case WM_SYSKEYUP:

                        Keys key = (Keys)lParam.vkCode;

                        if ((key == Keys.Tab    && lParam.flags == 32) ||   // Alt+Tab
                            (key == Keys.Escape && lParam.flags == 32) ||   // Alt+Esc
                            (key == Keys.F4     && lParam.flags == 32) ||   // Alt+F4
                            (key == Keys.Escape && lParam.flags == 0)  ||   // Ctrl+Esc
                            (key == Keys.LWin   && lParam.flags == 1)  ||   // Left Windows Key 
                            (key == Keys.RWin   && lParam.flags == 1))      // Right Windows Key 
                        {
                            return 1; //Do not handle key events
                        }
                        break;
                }
            }

            return CallNextHookEx(hookId, code, (int)wParam, ref lParam);
        }
    }
}

 

using System;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 12345;

            //C, D, N, F, E, X 등 뒤에 숫자는 표현할 자리를 의미합니다.

            //아래식은 모두 ToString() 형태에서 사용 가능합니다!
            //Console.WriteLine("12345 C5 = {0}", a.ToString("C5"));

            Console.WriteLine("12345 C5 = {0:C5}", a); //통화
            Console.WriteLine("12345 D5 = {0:D5}", a); //정수 (0으로 채워집니다.)
            Console.WriteLine("12345 N5 = {0:N5}", a); //3자리마다 콤마
            Console.WriteLine("12345 F5 = {0:F5}", a); //실수형 (자릿수에서 반올림)
            Console.WriteLine("12345 E5 = {0:E5}", a); //지수형
            Console.WriteLine("12345 X5 = {0:X5}", a); //16진수

            Console.WriteLine();
            Console.WriteLine("12345 2진수  = {0}", Convert.ToString(a, 2));
            Console.WriteLine("12345 8진수  = {0}", Convert.ToString(a, 8));
            Console.WriteLine("12345 16진수 = {0}", Convert.ToString(a, 16));

            Console.WriteLine();
            DateTime dateTime = DateTime.Now;
            Console.WriteLine("DateTime.ToString(yyyy-MM-dd hh-mm-ss) = {0}", dateTime.ToString("yyyy-MM-dd hh-mm-ss"));

            Console.WriteLine();
            float b = 1234.5678f;
            Console.WriteLine("1234.5678f F2 = {0:F2}", b);
        }
    }
}

 

결과값

NSIS : Nullsoft Scriptable Install System

 검색 창에 NSIS 를 치면 다운받아서 설치파일을 만들 수 있습니다.

인터넷 검색을 통해 기본적인 설치에 필요한 것들을 모아두었습니다.

절대 경로로 입력하거나 현재 .nsi 파일 위치를 기준으로 파일 등이 인식됩니다.

("License.txt" 같은 경우가 .nsi 파일과 함께 있는 경우입니다.)

#!define 명칭은 ${이름} 식으로 사용할 수 있습니다.
#주석은 " ; " , " # " , " /* */ " 입니다.

!define PRODUCT_FILENAME "MyProject"	# ${PRODUCT_FILENAME} 식으로 "MyProject" 문자열 사용이 가능합니다.
!define PRODUCT_NAME "Product"
!define PRODUCT_VERSION "22.08.10.1"
!define PRODUCT_PUBLISHER "MyHome"
!define PRODUCT_WEB_SITE "http://www.~~~.com"

#제어판에 삭제하기 등록하기 위한 레지스트리 경로입니다.
#!define PRODUCT_UNINST_ROOT_KEY "HKLM"

!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_FILENAME}"

!include "MUI.nsh"  	# Modern User Interface GUI 제공을 위해 사용해줍니다.
!include "LogicLib.nsh" 	# if, for 문 등을 사용하기 위해 선언 됩니다.

#!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"            #해당 경로 아이콘 사용 (기본값)
#!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"      #해당 경로 아이콘 사용 (기본값)

#각각의 순서도 중요합니다. 순서에 따라 인스톨 후 설정, 설정 후 인스톨 등이 진행될 수 있습니다.
!insertmacro MUI_PAGE_WELCOME          	         #환영메시지 (없으면 바로 설치 진행)
!insertmacro MUI_PAGE_LICENSE "License.txt" 	 #라이센스 파일이 있다면 등록하여 보여줍니다.

#!insertmacro MUI_PAGE_COMPONENTS 	#각 섹션마다 설치할지 말지 선택할 수 있게 보여줍니다.

!insertmacro MUI_PAGE_DIRECTORY		#파일 설치 위치 지정      $INSTDIR 이 됩니다.
!insertmacro MUI_PAGE_INSTFILES         #설치과정 페이지 (없으면 에러남)
!insertmacro MUI_PAGE_FINISH            #종료메시지 (없으면 설치 후 바로 종료)
!insertmacro MUI_LANGUAGE "korean"     	#언어 (가장 마지막에 써줘야 에러가 안난다고 합니다.)

#제어판에 등록할 이름 (아직 미사용)
Name "${PRODUCT_FILENAME} ${PRODUCT_VERSION}"

#프로그램 설치 파일 이름
OutFile "${PRODUCT_FILENAME}.exe"

#설치할 위치 $INSTDIR 을 통해 접근이 가능합니다.
InstallDir "C:\MyProject"

#설치 기본은 SilentInstall normal 이고 SilentInstall 모드면 백그라운드에서 진행되며
#트로이 목마로 인식된다고 합니다.
SilentInstall normal

#변수 선언 (변수 사용해보기 위해 선언했습니다.)
Var defaultPath

#Section + [옵션] + [섹션 이름] + [섹션 ID]
#https://wonsx.tistory.com/27?category=539749
Section "Install"

 StrCpy $defaultPath "$INSTDIR" #변수 defaultPath = $INSTDIR 로 설정해줍니다.
 
 #execwait 프로그램 실행 후 종료까지 기다립니다.
 #execwait ".\WindowsViewCapture.exe"

 SetOutPath "$defaultPath"   #Install 영역 설치 시 등록 된 파일들은 해당 폴더에 저장됩니다.
 SetOverwrite on               #덮어쓰기 모드

 #폴더 생성 (SetOutPath에 대해선 폴더가 자동으로 생성 됩니다.)
 # CreateDirectory "$defaultPath\Test1"

 #파일 등록  (.은 현재 위치 기준, 생략 해도 현재 위치 기준)
 File ".\Files\Test.exe"         # == "Files\Test.exe"

 #레지스트리 등록
/*
 HKCR or HKEY_CLASSES_ROOT
 HKLM or HKEY_LOCAL_MACHINE
 HKCU or HKEY_CURRENT_USER
 HKU or HKEY_USERS
 HKCC or HKEY_CURRENT_CONFIG
 HKDD or HKEY_DYN_DATA
 HKPD or HKEY_PERFORMANCE_DATA
 SHCTX or SHELL_CONTEXT
*/
 
 #shell open command 에 인자값 1개 받도록 입력 및 url protocol 생성
 WriteRegStr HKCR "${PRODUCT_FILENAME}\shell\open\command" "" "$\"$INSTDIR\Test.exe$\" $\"%1$\""
 WriteRegStr HKCR "${PRODUCT_FILENAME}" "URL Protocol" ""

 #실행 시 경고 메시지 뜨지 않게 설정해줍니다.
 WriteRegDWORD HKCU "Software\Microsoft\Internet Explorer\ProtocolExecute\${PRODUCT_FILENAME}" "WarnOnOpen" 00000000 

/* 파일이 있는 경우 설치 진행 or 취소 등
 
 iffileexists "경로" YES NO
 YES:
  ...
  goto END
 NO:
  ...
 END:
*/

 #제어판에서 삭제가 가능하도록 HKLM Uninstall 레지스트리를 등록해줍니다.
 WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\AppMainExe.exe"
 WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_FILENAME}"
 WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
 WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
 WriteRegStr HKLM "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\Uninstall.exe"

 #삭제파일을 만들어줍니다.
 WriteUninstaller "$INSTDIR\Uninstall.exe"

 #완료 시 닫기
 #SetAutoClose true

SectionEnd

Section "ShortCut"
 #바로가기 생성 CreateShortCut "바로가기 만들 위치.lnk" "바로가기 만들 파일"
 CreateShortCut "$DESKTOP\TestShortCut.lnk" "$defaultPath\Test.exe" #파일 이름 변수로 주고 하면 될듯
SectionEnd

Function un.onUninstSuccess
  HideWindow
  MessageBox MB_ICONINFORMATION|MB_OK "${PRODUCT_FILENAME}는(은) 완전히 제거되었습니다."
FunctionEnd

Function un.onInit
  MessageBox MB_ICONQUESTION|MB_YESNO "${PRODUCT_FILENAME}를(을) 제거하시겠습니까?" IDYES pass
   Abort
pass:
FunctionEnd

#언인스톨러는 추가 된 모든 파일들을 직접 입력해서 삭제해주면 됩니다.
Section un.Uninstall

 #파일 삭제해주기 (파일이 추가 된 만큼 혹은 폴더 채로 삭제해줍니다)
 Delete "$INSTDIR\Test.exe"
 Delete "$INSTDIR\Uninstall.exe"
 Delete "$DESKTOP\TestShortCut.lnk"

 #레지스트리 내용 삭제
 #커스텀 URI 등록 삭제
 DeleteRegKey HKCR "${PRODUCT_FILENAME}"
 
 #실행 시 경고 메시지 안뜨게 하는거 삭제
 DeleteRegKey HKCU "Software\Microsoft\Internet Explorer\ProtocolExecute\${PRODUCT_FILENAME}"
 
 #제어판에 보여지는 레지스트리 삭제
 DeleteRegKey HKLM "${PRODUCT_UNINST_KEY}"

 #폴더 삭제 RMDir /r "$INSTDIR" 을 통해 폴더의 모든 파일들을 삭제해줄 수도 있습니다.
 RMDir "$INSTDIR"
 
 #자동으로 종료
 SetAutoClose true

SectionEnd

+ Recent posts