최근 Winform을 이용하여 네이버 카페에 자동으로 글을 등록하는 프로그램을 개발하게 되어 기술과 소스를 공유하고자 작성한다.

 

로그인 / 글쓰기 테스트

 

1. 네이버 자동 로그인

네아로(네이버 아이디로 로그인)을 활용하여 자동 로그인을 구현하려 했다.

Winform의 WebBrowser 혹은 ASP.NET MVC or ASP.NET Webform을 활용하여 네이버 로그인을 구현한 경험은 있지만, WebBrowser없이 로그인을 구현하는 경우는 처음이라 많이 삽질을 했다. 구글에 WebBrowser를 띄우지 않고 로그인하는 방법은 많지 않았고, 있다고 하더라고 POST 패킷 분석 로그인 방법이였다.

 

패킷 분석 로그인 방법은 두 곳을 참조 하였다.

[참조]

- http://non-sponsor.tistory.com/6 (네이버 bvsd 로그인)

- https://it-raccoon.tistory.com/15 (위 코드 기반으로 최신업데이트)

 

 

* bvsd : 네이버 로그인시 전송하는 파라미터의 key이다. 위 [참조] 사이트 들에서는 bvsd를 생성하여 로그인하는 방법을 안내하고 있다. 로그인 성공 이후 Cookie를 저장하여 로그인을 유지한다.

 

위 두 코드를 활용하여 bvsd 로그인 시 OAuth 로그인이 되도록 수정하였다.

할땐 골치 아프더니 다 하고 나니 생각보다 간단

 

1-1. 네이버 로그인 OAuth 구현

보통 네이버 로그인 OAuth 가이드에 따라

[네이버 OAuth 로그인 Url] https://nid.naver.com/oauth2.0/authorize?client_id={client_id}&response_type=code&redirect_uri={callback_url}&state={state} 

경로를 브라우저에 띄워 로그인을 유도한다. 하지만, 원하는건 브라우저 혹은 WebBrowser없이 코드로 로그인을 하는것이다.

 

##

추가적으로 response_type=token으로 할 경우와 code로 할 경우 응답 값이 다르다.

둘 다 로그인 구현은 가능하지만 응답값이 다르다.

token의 경우 응답값으로 Access Token을 받을 수 있으며, Refresh Token을 받을 수 없다.

code의 경우 응답값으로 code와 state를 받아 Access Token과 Refresh Token을 발급 받아야 한다.

##

 

HttpWebRequest로 해당 Url에 bvsd와 OAuth 파라미터를 추가하여 로그인 시도를 한다.

* logintp=oauth2, url=https://nid.naver.com/oauth2.0/authorize?client_id={client_id}&response_type=code&redirect_uri={callback_url}&state={state}을 넣어주면된다.

추가적으로, svctype, smart_LEVEL은 PC마다 다르게 나올수도 있다. 해당값을 바꿔줘야 하는지는 잘 모르겠다.

위 이미지의 파란박스가 bvsd이다.

 

소스에서는 위와 같이 작성되어 있다.

 

이렇게 해주면 bvsd를 활용하여 OAuth 로그인을 성공할 수 있다.

이후 작업은 OAuth 로직에 맞춰 진행되며, 위에서 언급한 code와 state로 Access Token과 Refresh Token을 발급 받아 네이버 API 이용할 수 있다.

 

Refesh Token을 저장하여 추후 ID, PW입력없이 API 사용이 가능하다.

 

1-2. 카페 API 사용

소스는 네이버 개발자 카페 글쓰기 명세에서 그대로 가져와서 썼다.

아주 친절하게 잘 작성되어있다.

 

카페 글쓰기 API 사용시 ClubID와 MenuID를 알아야 글을 작성 할 수 있다.

ClubID와 MenuID는 카페 메뉴에서 오른쪽 클릭 후 "새 창에서 열기" or "새 탭에서 열기"를 하면 확인 가능하다.

https://cafe.naver.com/블라블라?iframe_url=/ArticleList.nhn%3Fsearch.clubid=카페아이디%26search.menuid=메뉴아이디%26search.boardtype=L

 

 

textbox에 미리 값을 채워서 사용해주세요. (안그럼 먹통 인건 안비밀)

 

 

// 급하게 해서 소스가 더러워도 이해해주세여ㅠㅠ

NaverCafeTest.zip
0.02MB

'Programming > C#' 카테고리의 다른 글

[C#]ini file 사용법 & 소스 공유  (540) 2017.01.03
[C#]Log4Net 사용법  (587) 2017.01.03
[C#]Snappy 사용법  (143) 2016.12.30
[C#]DataTable Sorting 간단 사용법  (971) 2016.12.30

C# 응용 프로그램을 만들다보면 설정 파일이 필요한 경우가 있다.

이때 ini file을 사용하면 간편하다. 물론 xml로 된 config file도 있지만 ini file이 직관적이고 수정하기 편하다.

구글링을 해보면 C#에서 ini파일을 다루는 여러 내용들이 있지만, 효율적으로 ini file을 관리하고 수정이 용이하게 설명된 내용이 없어 직접 만든 내용을 공유하려 한다.

그렇다고 내가 만든게 짱짱맨 이런 의미는 아니다.


ini file의 구성은

크게 section과 key로 나뉜다.

예를 들면

[Database]

Address="localhost"

Id="root"

[User]

Name="홍길동"

Age="29"


이런 ini file이 있을때 [Database]와 [User]가 section이 되고 Address, id, Name, Age가 key가 된다.


아래 소스를 보면서 하나씩 짚어가보자

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
/// System.ComponentModel.DataAnnotations 는 참조에서 어셈블리를 추가해야 한다.
using System.ComponentModel.DataAnnotations;
using System.Runtime.InteropServices;
using System.Text;

namespace ConfigManager
{
	/// <summary>
	/// ini file의 정보를 담을 그릇(클래스)
	/// <typeparam name="K1"></typeparam>
	/// <typeparam name="K2"></typeparam>
	/// <typeparam name="V"></typeparam>
	[Serializable]
	public class MultiKeyDictionary<k1, k2, v> : Dictionary<k1, dictionary<k2, v>>
	{
		public V this[K1 key1, K2 key2]
		{
			get
			{
				if (!ContainsKey(key1) || !this[key1].ContainsKey(key2))
				{
					return default(V);
					//throw new ArgumentOutOfRangeException();
				}

				return base[key1][key2];
			}
			set
			{
				if (!ContainsKey(key1))
					this[key1] = new Dictionary<k2, v>();
				this[key1][key2] = value;
			}
		}

		public void Add(K1 key1, K2 key2, V value)
		{
			if (!ContainsKey(key1))
				this[key1] = new Dictionary<k2, v>();
			this[key1][key2] = value;
		}

		public bool ContainsKey(K1 key1, K2 key2)
		{
			return base.ContainsKey(key1) && this[key1].ContainsKey(key2);
		}

		public new IEnumerable<v> Values
		{
			get
			{
				return from baseDict in base.Values
					   from baseKey in baseDict.Keys
					   select baseDict[baseKey];
			}
		}
	}

	/// <summary>
	/// Section 구조
	/// 추가할 Section이 있을 경우 enum에 추가하고 section name만 정의해주면 된다.
	/// </summary>
	public enum SectionNames
	{
		/// [Display(Name = "Database")] 에서 Name에 들어가는 문자열이 ini file에 실질적으로 쓰여지는 Section의 이름이다.

		/// <summary>
		///  Section Name "Database"
		/// </summary>
		[Display(Name = "Database")]
		//해당 이름과 아래 선언된 enum의 이름이 일치해야한다.
		Database_Section,

		/// <summary>
		/// Section Name "User"
		/// </summary>
		[Display(Name = "User")]
		User_Section,
	}

	/// <summary>
	/// Database Section의 Key 나열
	/// 추가할 key가 있을 경우 enum에 추가하고 디폴트 값만 정의해주면 된다.
	/// </summary>
	public enum Database_Section
	{
		/// [Display(Description = "localhost")]에서 Description에 들어가는 문자열은 ini에 key가 없는 경우 디폴트로 들어가는 값이다.

		/// <summary>
		/// Default Value : localhost
		/// </summary>
		[Display(Description = "localhost")]
		// Address 그대로 ini file의 key값으로 쓰여진다. 
		Address,

		/// <summary>
		/// Default Value : root
		/// </summary>
		[Display(Description = "root")]
		// Id 그대로 ini file의 key값으로 쓰여진다.
		Id,
	}

	/// <summary>
	/// User Section의 Key 나열
	/// </summary>
	public enum User_Section
	{
		/// <summary>
		/// Default Value : 홍길동
		/// </summary>
		[Display(Description = "홍길동")]
		Name,

		/// <summary>
		/// Default Value : 29
		/// </summary>
		[Display(Description = "29")]
		Age,
	}

	public class ConfigData
	{
		//ini file의 경로 설정
		//원하는 경로와 이름으로 바꿔주면 된다.
		private string inifilepath = @"C:\Config.ini";

		//ini file의 정보를 담을 변수 선언
		private MultiKeyDictionary<sectionnames, string, object> _ConfigData = null;

		//ini file을 읽을때 사용하는 함수
		[DllImport("kernel32.dll")]
		private static extern int GetPrivateProfileString(
			String section, String key, String def, StringBuilder retVal, int Size, String filePath);

		//ini file을 쓸때 사용하는 함수
		[DllImport("kernel32.dll")]
		private static extern long WritePrivateProfileString(
			String Section, String key, String val, String filePath);

		public ConfigData()
		{
			_ConfigData = new MultiKeyDictionary<sectionnames, string, object>();
		}

		/// <summary>
		/// ConfigData Class내에서 ini file을 쓰기 위한 함수
		/// </summary>
		/// <param name="Section">
		/// <param name="Key">
		/// <param name="Value">
		private void IniWriteValue(String Section, String Key, String Value)
		{
			WritePrivateProfileString(Section, Key, Value, inifilepath);
		}

		/// <summary>
		/// ConfigData Class내에서 ini file을 읽기 위한 함수
		/// </summary>
		/// <param name="Section">
		/// <param name="Key">
		/// <returns></returns>
		private String IniReadValue(String Section, String Key)
		{
			StringBuilder temp = new StringBuilder(255);
			int i = GetPrivateProfileString(Section, Key, "", temp, 255, inifilepath);

			return temp.ToString();
		}

		/// <summary>
		/// 외부에서 ConfigData Class 객체를 이용해 Ini file의 값을 셋팅하기 위한 함수
		/// </summary>
		/// <typeparam name="KeyEnum"></typeparam>
		/// <param name="key">
		/// <param name="value">
		public void SetConfigData<keyenum>(KeyEnum key, string value)
		{
			DisplayAttribute keyAttr = typeof(KeyEnum).GetMember(key.ToString())
											.First()
											.GetCustomAttribute<displayattribute>();

			SectionNames sectionName = (SectionNames)Enum.Parse(typeof(SectionNames), typeof(KeyEnum).Name);

			_ConfigData[sectionName][key.ToString()] = value;
		}

		/// <summary>
		/// 외부에서 ConfigData Class 객체를 이용해 Ini file의 값을 가져오기 위한 함수
		/// </summary>
		/// <typeparam name="KeyEnum"></typeparam>
		/// <param name="key">
		/// <returns></returns>
		public string getConfigData<keyenum>(KeyEnum key)
		{
			DisplayAttribute keyAttr = typeof(KeyEnum).GetMember(key.ToString())
											.First()
											.GetCustomAttribute<displayattribute>();

			SectionNames sectionName = (SectionNames)Enum.Parse(typeof(SectionNames), typeof(KeyEnum).Name);

			return _ConfigData[sectionName][key.ToString()].ToString();
		}

		/// <summary>
		/// Ini file을 저장하기 위한 함수
		/// </summary>
		public void SaveConfigData()
		{
			for (int i = 0; i < _ConfigData.Count; i++)
			{
				for (int j = 0; j < _ConfigData.ElementAt(i).Value.Count; j++)
				{
					string sectionname = _ConfigData.ElementAt(i).Key.ToString();


					DisplayAttribute keyAttr = typeof(SectionNames).GetMember(sectionname)
														.First()
														.GetCustomAttribute<displayattribute>();

					string section = keyAttr.Name;
					string key = _ConfigData.ElementAt(i).Value.ElementAt(j).Key.ToString();
					string value = _ConfigData.ElementAt(i).Value.ElementAt(j).Value.ToString();
					IniWriteValue(
						section,    //section
						key,        //key
						value       //value
						);
				}
			}
		}

		/// <summary>
		/// Ini file을 읽기 위한 함수
		/// </summary>
		public void ReadConfigData()
		{
			foreach (string strSectionName in Enum.GetNames(typeof(SectionNames)))
			{
				SectionNames sectionName = (SectionNames)Enum.Parse(typeof(SectionNames), strSectionName);

				DisplayAttribute sectionAttr = typeof(SectionNames).GetMember(strSectionName)
													  .First()
													  .GetCustomAttribute<displayattribute>();

				if (!_ConfigData.ContainsKey(sectionName))
					_ConfigData.Add(sectionName, new Dictionary<string, object="">());

				Type keyType = Type.GetType(string.Format("ConfigManager.{0}", sectionName));

				foreach (string strKeyName in Enum.GetNames(keyType))
				{
					DisplayAttribute keyAttr = keyType.GetMember(strKeyName)
													  .First()
													  .GetCustomAttribute<displayattribute>();

					string value = IniReadValue(sectionAttr.Name, strKeyName);

					if (!_ConfigData[sectionName].ContainsKey(strKeyName)) // 키가 없는 경우 추가
					{
						if (!string.IsNullOrEmpty(value)) // ini File에 값이 있다면 ini File 값 입력
							_ConfigData[sectionName].Add(strKeyName, value);
						else // ini File에 값이 없다면 Default 값 입력
							_ConfigData[sectionName].Add(strKeyName, keyAttr.Description);
					}
					else // 키가 있는 경우 값 입력
					{
						if (!string.IsNullOrEmpty(value)) // ini File에 값이 있다면 ini File 값 입력
							_ConfigData[sectionName][strKeyName] = value;
						else // ini File에 값이 없다면 Default 값 입력
							_ConfigData[sectionName][strKeyName] = keyAttr.Description;
					}
				}
			}
		}
	}
}

//이렇게 class를 만들었다. 실질적으로 사용하는 방법은 이렇다
using ConfigManager;

ConfigData _config = new ConfigData();
// ini file read
_config.ReadConfigData();

// 각 key에서 값 가져오기
string address = _config.getConfigData<database_section>(Database_Section.Address);
string id = _config.getConfigData<database_section>(Database_Section.Id);
string name = _config.getConfigData<user_section>(User_Section.Age);
string age = _config.getConfigData<user_section>(User_Section.Name);

// 각 key에 값 할당
// 값을 셋팅만 하지 실질적으로 ini file에 쓰기 위해선 save를 해줘야한다.
string address_change = "127.0.0.1";
string id_change = "server";
string name_change = "시나공공";
string age_change = "28"; // 늙었다...

_config.SetConfigData<database_section>(Database_Section.Address, address_change);
_config.SetConfigData<database_section>(Database_Section.Id, id_change);
_config.SetConfigData<User_Section>(User_Section.Name, name_change);
_config.SetConfigData<User_Section>(User_Section.Age, age_change);

// ini file save
_config.SaveConfigData();


구조적으로, 프로그래밍 적으로 좋은 방법인지 아닌지는 객관적으로 잘 모르겠으나, 내 나름대로 쓰기에 나쁘지 않다고 생각하여 공유해본다.

조언 및 충고 해주시면 너무너무 감사하겠습니다.

'Programming > C#' 카테고리의 다른 글

[C#] 네이버 자동 로그인 / 카페 글쓰기 API  (899) 2019.11.01
[C#]Log4Net 사용법  (587) 2017.01.03
[C#]Snappy 사용법  (143) 2016.12.30
[C#]DataTable Sorting 간단 사용법  (971) 2016.12.30

log4net 사용 관련 참고 사이트 : http://hind.pe.kr/1199

log4net 설정 관련 참고 사이트 : http://egloos.zum.com/empty79/v/2956254


C#에서 logging을 할때 손쉽고 편하게 사용할 수 있는 라이브러리를 소개하려한다.

Log4Net이라는 라이브러리 이다.

이 라이브러리는 log4j라는 java라이브러리에서 따온것이라고 알고있다.


log4net에 대한 자세한 내용은 홈페이지를 참조 바랍니다.

https://logging.apache.org/log4net/


1. 사용 방법

 - Nuget으로 Log4Net을 검색하면 아파치 log4net이 제일 상위에 나타난다


 "설치"버튼을 클릭하면 사용준비 끝.


2. 설정 파일

 - 사실 설정 파일에 대해 정확하게 알지 못한다. 일단 구글링을 통해 알아본 기본적인 틀을 확인해보자.

   응용 프로그램의 경우 app.config 파일에 설정 내용을 입력 하지만 나는 asp.net에서 로깅을 하려하기 때문에

   별도의 LogConfig.xml 파일을 생성하였다. (web.config에 하면 되려나)

<log4net>
  <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
    <file value="C:\Log\"/>
    <appendtofile value="true"/>
    <datepattern value="yyyy-MM-dd'_Log.log'"/>
    <staticlogfilename value="false"/>
    <rollingstyle value="Date"/>
    <layout type="log4net.Layout.PatternLayout">
      <conversionpattern value="%d [%t] %-5p - %m%n"/>
    </layout>
  </appender>
  <root>
    <level value="ALL"/>
    <appender-ref ref="RollingFile"/>
  </root>
</log4net>

<appender> 태그는 로그를 어떤식으로 남길 것인지에 대한 큰 틀이라고 보면된다.

appender 태그 안에서 파일을 어느곳에 만들것인지 파일이름을 어떻게 할것인지 로그 패턴을 어떻게 할것인지를 정할 수 있다.

<root> 태그는 기본 logger 설정을 하며, level 태그는 ALL, Warn, 등등 으로 나뉘며 해당 레벨 이상의 로그만 로그파일에 쓰길 원할경우 설정한다.

appender-ref 태그에는 appender의 name을 입력하여 연결을 해준다.


3. 소스

//using 해준다.
using log4net;
using log4net.Config;

//로깅할 곳에 로그 변수 추가
private ILog _logger = null;
public readonly ILog Logger
{
  get { return _Logger; }
}

string strDirPath;
strDirPath = @"C:\Log";
DirectoryInfo dirInfo = new DirectoryInfo(strDirPath);
//log4net에서 폴더를 만들어주는지 안만들어주는지 몰라서.. 일단 혹시나해서 만듦

if (dirInfo.Exists == false) { if (dirInfo != null) dirInfo.Create(); } //LogConfig.xml 경로를 설정해준다. string AppPath = AppDomain.CurrentDomain.BaseDirectory; //log4net.config 하위의 xmlconfigurator에 설정 파일을 등록한다. XmlConfigurator.Configure(new System.IO.FileInfo(AppPath + @"bin\LogConfig.xml")); //설정파일의 appender의 name에 아까 입력한 RollingFile을 입력하여 logger를 추가한다. _logger = log4net.LogManager.GetLogger("RollingFile"); // ----------- 이렇게 해서 기본적인 변수 선언과 사용준비는 끝. // 사용 //error if(_logger.IsErrorEnabled) _logger.Error("Error!!!!!!!!!!!"); if(_logger.IsDebugEnabled) _logger.Debug("Debug!!!!!!!!!!!"); if(_logger.IsInfoEnabled) _logger.Info("Info!!!!!!!!!!!");

로그를 남길때 수준을 정할 수 있다.

Error, Fatal, Warn, Info, Debug


이렇게 해서 로그 파일을 확인해 보면 아래와 같이 로그가 남는 것을 확인 할 수 있다.



*log4net을 사용하다 보면 남겨야 하는 로그의 성격이 다른 것들이 있다. 이럴경우 로그파일을 나눠서 로그를 남기고 싶을 경우가 있다.

이럴 경우에는 설정파일을 살짝 바꿔서 사용하면된다.


아래와 같이 appender를 추가하고 logger를 추가해주면 끝. 알기쉽게 이름을 잘 지어주면 끝.

눈치가 빠른 사람들은 소스에서 어떻게 사용하는지도 대충은 파악했을 것이다.


<log4net> <appender name="Common" type="log4net.Appender.RollingFileAppender"> <file value="C:\Log\" /> <appendToFile value="true" /> <datePattern value="yyyy-MM-dd'_Common.log'" /> <staticLogFileName value="false" /> <rollingStyle value="Date" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%d [%t] %-5p - %m%n" /> </layout> </appender> <appender name="Socket" type="log4net.Appender.RollingFileAppender"> <file value="C:\Log\" /> <appendToFile value="true" /> <datePattern value="yyyy-MM-dd'_Socket.log'" /> <staticLogFileName value="false" /> <rollingStyle value="Date" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%d [%t] %-5p - %m%n" /> </layout> </appender> <appender name="Database" type="log4net.Appender.RollingFileAppender"> <file value="C:\Log\" /> <appendToFile value="true" /> <datePattern value="yyyy-MM-dd'_Database.log'" /> <staticLogFileName value="false" /> <rollingStyle value="Date" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%d [%t] %-5p - %m%n" /> </layout> </appender> <root> <level value="ALL" /> </root> <logger name="Common"> <level value="ALL" /> <appender-ref ref="Common" /> </logger> <logger name="Socket"> <level value="ALL" /> <appender-ref ref="Socket" /> </logger> <logger name="Database"> <level value="ALL" /> <appender-ref ref="Database" /> </logger> </log4net>

여러 파일에 로그 남기기 소스는 첨부파일을 열어보자

LogManager.cs



'Programming > C#' 카테고리의 다른 글

[C#] 네이버 자동 로그인 / 카페 글쓰기 API  (899) 2019.11.01
[C#]ini file 사용법 & 소스 공유  (540) 2017.01.03
[C#]Snappy 사용법  (143) 2016.12.30
[C#]DataTable Sorting 간단 사용법  (971) 2016.12.30

압축 알고리즘 선택 기준 : http://bspfp.pe.kr/192 참조


Snappy 개요 : http://www.mimul.com/pebble/default/2012/04/10/1334044735206.html 참조


Snappy 사용법


1. Nuget에서 Snappy를 검색하여 Snappy for .NET을 설치한다.

2. using Snappy;


 
//압축된 내용
byte[] byteCompressed = null;
//압축 해제 된 내용
byte[] byteDeCompressed = null;

Stream stream = null;

ulong ulJsonLength = 0;

byteCompressed = File.ReadAllBytes("FilePath");

byteDeCompressed = SnappyCodec.Uncompress(byteCompressed);

stream = new MemoryStream(byteDeCompressed);

// stream에 담아 지지고볶고 사용 하면 된다.
// ** 해당 내용은 압축 해제를 다룬 내용.
// ** 압축은 위의 소스를 조금만 수정하여 사용하면 될듯 하다.


* Snappy 함수들


함수가 많지 않다. 압축과 압축 해제는 Compress, Uncompress 함수를 사용하면 된다. 단순히 byte[] 만 넣어주면 알아서 압축과 해제를 해주니 사용하기는 간단하다.

'Programming > C#' 카테고리의 다른 글

[C#] 네이버 자동 로그인 / 카페 글쓰기 API  (899) 2019.11.01
[C#]ini file 사용법 & 소스 공유  (540) 2017.01.03
[C#]Log4Net 사용법  (587) 2017.01.03
[C#]DataTable Sorting 간단 사용법  (971) 2016.12.30

Index라는 컬럼을 가진 DataTable이 있을때 Index로 Sorting을 하고싶다면 다음과 같이 사용하면 된다.

DataTable dt;
//Columns add ... 
//Rows add ...
dt.DefaultView.Sort = "index desc";

desc : 내림차순

asc : 올림차순


* Sotring 후 그냥 dt를 사용 할경우 Sorting된 값을 사용할 수 없다. dt.DefaultView를 사용 해야 Sorting된 값을 확인 할 수 있다.


tip

Sorting 후 key값의 index를 알고싶을때는 find 함수를 사용하면 된다.

예를 들어 Index라는 컬럼이 Datatable의 key값일 경우

dt.DefaultView.Sort = "index desc";
//20이라는 index의 Row를 알고싶을 경우
int nRow = dt.DefaultView.find(20);
//nRow를 사용하면 된다.
//find에서 찾는 값이 없을 경우 -1을 리턴하니 참고하길
//Row를 찾았으니 이제 해당 row의 값을 가져오는 방법을 알아보자
//Datatable에 Name이라는 컬럼이 있다고 한다면 아래와 같은 방법으로 사용가능하다.
string Student_Name = dt.DefaultView[nRow]["Name"].ToString();
//....


'Programming > C#' 카테고리의 다른 글

[C#] 네이버 자동 로그인 / 카페 글쓰기 API  (899) 2019.11.01
[C#]ini file 사용법 & 소스 공유  (540) 2017.01.03
[C#]Log4Net 사용법  (587) 2017.01.03
[C#]Snappy 사용법  (143) 2016.12.30

+ Recent posts