반응형

안녕하세요

 

C# 컨텐츠로 블로그를 운영중인 코딩 연습생입니다

 

이번 포스팅에서는 C#으로 MSSQL를 접속하고 쿼리문을 전송시켜 연동시키기까지 한번 해보도록 하겠습니다

 

일반적인 방법으로는 

1) 접속정보 생성

2) SqlConnection 생성

3) SqlDataAdapter 생성

4) SqlCommand 실행

5) DataSet 실행

6) DataTable 데이터 받기

 

이런 순서로 직접 날코딩하여 사용을 했었습니다

 

그런데 프로젝트에서 DB 통신을 자주해야 하는경우 매우 귀찮아지게 됩니다

 

그래서 쉽게 MSSQL과 통신하고 내가 원하는 방식으로 FeedBack 받을수 있는 클래스 생성도 같이 설명해보

 

도록 하겠습니다

 

첫번째로 일반 방식입니다

string source = string.Empty;
string SYS_TIME = string.Empty;

//콜렉션 생성
DataRowCollection Rs2 = null;
//접속정보 생성
source = @"User Id=계정ID;Password=패스워드;Server=tjqjIP;Initial Catalog=DB명";

//컨넥션 생성
SqlConnection Con = new SqlConnection(source);
Con.Open();
SqlDataAdapter adapter = new SqlDataAdapter();
string query = "쿼리";
adapter.SelectCommand = new SqlCommand(query, Con);
DataSet ds = new DataSet();
adapter.Fill(ds);
DataTable table = ds.Tables[0];
Rs2 = table.Rows;

if (Rs2 != null && Rs2.Count > 0)
{
	for (int i = 0; i < Rs2.Count; i++)
	{
		//쿼리에서 FeedBack 받은 값 사용
        SYS_TIME = Rs2[0]["시간"].ToString();
	}
}
Con.Close();

 

단순한 SELECT문 하나 사용하더라도 참 많은 구문을 작성해야 합니다

 

두번째 방법으로는 클래스로 DB 명령를 미리 생성해놓고 호출하여 사용하는 방식으로 구현해 볼께요

1) DB에서 사용될 명령를 미리 생성합니다

   - Database.cs라는 구성 요소 클래스 파일 생성

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Data;
using System.Drawing;
using System.Windows.Forms;

namespace db_conn
{
    public partial class Database : Component
    {
        public Database()
        {
            InitializeComponent();
        }

        public Database(IContainer container)
        {
            container.Add(this);

            InitializeComponent();
        }

        private db_conn.BaseDB.DBTypeEnum m_DbType = BaseDB.DBTypeEnum.JDE;

        public db_conn.BaseDB.DBTypeEnum DbType
        {
            get { return m_DbType; }
            set { m_DbType = value; }
        }

        /// <summary>
        /// SELECT처럼 결과가 있는 쿼리를 실행한다
        /// </summary>
        /// <param name="query">쿼리</param>
        /// <returns>데이터테이블</returns>
        public DataTable ExcuteQuery(string query)
        {
            return ExcuteQuery(m_DbType.ToString(), query);
        }
        /// <summary>
        /// SELECT처럼 결과가 있는 쿼리를 실행한다
        /// </summary>
        /// <param name="dbName">DB명</param>
        /// <param name="query">쿼리</param>
        /// <returns>데이터테이블</returns>
        public DataTable ExcuteQuery(string dbName, string query)
        {
            db_conn.BaseDB db = BaseDB.GetDB(dbName.ToUpper());
            return db.ExcuteQuery(query);
        }
        /// <summary>
        /// insert, update처럼 조작하는 쿼리를 실행한다.
        /// </summary>
        /// <param name="query">쿼리</param>
        /// <returns>결과</returns>
        public int ExcuteNonQuery(string query)
        {
            return ExcuteNonQuery(m_DbType.ToString(), query);
        }
        // <summary>
        /// insert, update처럼 조작하는 쿼리를 실행한다.
        /// </summary>
        /// <param name="dbName">DB명</param>
        /// <param name="query">쿼리</param>
        /// <returns>결과</returns>
        public int ExcuteNonQuery(string dbName, string query)
        {
            db_conn.BaseDB db = BaseDB.GetDB(dbName.ToUpper());
            return db.ExcuteNonQuery(query);
        }
        /// <summary>
        /// 대량의 데이터를 인서트한다.
        /// </summary>
        /// <param name="toTbName">인서트할 테이블</param>
        /// <param name="dt">데이터테이블</param>
        /// <returns>오류메세지</returns>
        public string BulkInsert(string toTbName, System.Data.DataTable dt)
        {
            return BulkInsert(m_DbType.ToString(), toTbName, dt);
        }
        /// <summary>
        /// 대량의 데이터를 인서트한다.
        /// </summary>
        /// <param name="dbName">DB명</param>
        /// <param name="toTbName">인서트할 테이블</param>
        /// <param name="dt">데이터테이블</param>
        /// <returns>오류메세지</returns>
        public string BulkInsert(string dbName, string toTbName, System.Data.DataTable dt)
        {
            db_conn.BaseDB db = BaseDB.GetDB(dbName.ToUpper());
            return db.BulkInsert(toTbName, dt);
        }

        /// <summary>
        /// 1:1매칭되는 테이블을 넘겨 Dictionary로 받는다.
        /// </summary>
        /// <param name="dbName">DB명</param>
        /// <param name="query">쿼리(KEY_COL,VAL_COL이 반드시 있어야한다)</param>
        /// <returns>결과</returns>
        public Dictionary<string, string> GetDic1To1(string dbName, string query)
        {
            Dictionary<string, string> ret = new Dictionary<string, string>();
            DataTable dt = ExcuteQuery(dbName, query);
            string key = "";
            string val = "";
            for (int row = 0; row < dt.Rows.Count; row++)
            {
                key = dt.Rows[row]["KEY_COL"].ToString();
                val = dt.Rows[row]["VAL_COL"].ToString();
                if (!ret.ContainsKey(key))
                {
                    ret.Add(key, val);
                }
            }
            return ret;
        }
        /// <summary>
        /// 테이블을 넘겨 Dictionary(row)
        /// </summary>
        /// <param name="dbName">DB명</param>
        /// <param name="query">쿼리(KEY_COL은 반드시 있어야한다)</param>
        /// <returns>결과</returns>
        public Dictionary<string, DataRow> GetDicRow(string dbName, string query)
        {
            Dictionary<string, DataRow> ret = new Dictionary<string, DataRow>();
            DataTable dt = ExcuteQuery(dbName, query);
            string key = "";
            for (int row = 0; row < dt.Rows.Count; row++)
            {
                key = dt.Rows[row]["KEY_COL"].ToString();
                if (!ret.ContainsKey(key))
                {
                    ret.Add(key, dt.Rows[row]);
                }
            }
            return ret;

        }

        /// <summary>
        /// 데이터테이블을 딕셔너리에 넣는다.
        /// </summary>
        /// <param name="keyCols">키값을 가지는 컬럼들(:로 연결한다)</param>
        /// <param name="dt">넘길 테이블</param>
        /// <returns>결과</returns>
        public Dictionary<string, DataRow> GetDicRow(string keyCols, DataTable dt)
        {
            Dictionary<string, DataRow> ret = new Dictionary<string, DataRow>();

            string key = "";
            string[] spKeys = keyCols.Split(':');
            for (int row = 0; row < dt.Rows.Count; row++)
            {
                key = "";
                for (int i = 0; i < spKeys.Length; i++)
                {
                    key += dt.Rows[row][spKeys[i]].ToString();
                }
                if (!string.IsNullOrEmpty(key) && !ret.ContainsKey(key))
                {
                    ret.Add(key, dt.Rows[row]);
                }
            }
            return ret;
        }

        /// <summary>
        /// 데이터테이블을 딕셔너리에 넣는다.
        /// </summary>
        /// <param name="keyCols">키값을 가지는 컬럼들(:로 연결한다)</param>
        /// <param name="sumCols">SUM할 컬럼들(:로 연결한다)</param>
        /// <param name="dt">넘길 테이블</param>
        /// <returns>결과</returns>
        public Dictionary<string, DataRow> GetDicRow(string keyCols, string sumCols, DataTable dt)
        {
            Dictionary<string, DataRow> ret = new Dictionary<string, DataRow>();

            string key = "";
            string[] spKeys = keyCols.Split(':');
            string[] spSums = sumCols.Split(':');
            for (int row = 0; row < dt.Rows.Count; row++)
            {
                key = "";
                for (int i = 0; i < spKeys.Length; i++)
                {
                    key += dt.Rows[row][spKeys[i]].ToString();
                }

                if (!ret.ContainsKey(key))
                {   //INSERT
                    ret.Add(key, dt.Rows[row]);
                }
                else
                {   //UPDATE
                    for (int i = 0; i < spSums.Length; i++)
                    {
                        ret[key][spSums[i]] = Convert.ToDouble(ret[key][spSums[i]]) + Convert.ToDouble(dt.Rows[row][spSums[i]]);
                    }
                }
            }
            return ret;
        }

        /// <summary>
        /// 딕셔너리를 DataTable로 변환
        /// </summary>
        /// <param name="dic">딕셔너리</param>
        /// <returns>테이블</returns>
        public DataTable ConvertDicToDT(Dictionary<string, DataRow> dic)
        {
            DataTable dt = new DataTable();
            int cnt = 0;
            foreach (KeyValuePair<string, DataRow> pair in dic)
            {
                if (cnt == 0)
                {
                    dt = pair.Value.Table.Clone();
                }

                DataRow dr = dt.NewRow();
                for (int col = 0; col < pair.Value.ItemArray.Length; col++)
                {
                    dr[col] = pair.Value[col];
                }
                dt.Rows.Add(dr);

                cnt++;
            }

            return dt;
        }

    }
}

 

2) 접속 DB에 대한 속성 클래스 생성

   - BaseDB 클래스 파일 생성

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Reflection;
using System.Data.Common;

namespace db_conn
{
    
    public abstract class BaseDB 
    {
        #region 열거형
        /// <summary>
        /// 매출타입(OES/OEM)
        /// </summary>
        public enum DBTypeEnum
        {
            TEST            
        }
        #endregion

        private DBTypeEnum m_DBType = DBTypeEnum.TEST;

        public BaseDB(DBTypeEnum dbType)
        {
            m_DBType = dbType;
            
        }

        /// <summary>
        /// 쿼리를 실행한다(결과 있을때)
        /// </summary>
        /// <param name="query">쿼리</param>
        /// <returns>실행결과</returns>
        public abstract DataTable ExcuteQuery(string query);

        /// <summary>
        /// 쿼리를 실행한다(결과 없을때)
        /// </summary>
        /// <param name="query">쿼리</param>
        /// <returns>적용행수</returns>
        public abstract int ExcuteNonQuery(string query);


        /// <summary>
        /// 대량의 데이터를 Insert한다
        /// </summary>
        /// <param name="toTbName">저장할테이블</param>
        /// <param name="dt">데이터</param>
        /// <returns>에러메세지</returns>
        public abstract string BulkInsert(string toTbName, DataTable dt);

        /// <summary>
        /// DB연결자생성
        /// </summary>
        /// <returns>DB연결자</returns>
        protected abstract DbConnection DBConnection();

        

        /// <summary>
        /// SQL파일을 실행한다.
        /// </summary>
        /// <param name="path">SQL파일</param>
        /// <param name="args">인자(:)으로 구분</param>
        /// <returns>결과</returns>
        public DataTable ExcuteFileQuery(string path, string args)
        {
            string query = System.IO.File.ReadAllText(path);
            query = query.Replace("\r\n", " "); //강제개행
            query = query.Replace("\t", " "); //탭키
            query = query.Replace(";", " "); //탭키
            string[] spArgs = args.Split(':');
            for (int i = 0; i < spArgs.Length; i++)
            {
                query = query.Replace("{" + i.ToString() + "}", spArgs[i]);
            }
            return this.ExcuteQuery(query);
        }

        
        /// <summary>
        /// 줄리안데이트
        /// </summary>
        /// <param name="yyyymmdd">년월일</param>
        /// <returns>줄리안</returns>
        public int GetJulianDate(string yyyymmdd)
        {
            int yyyy = Convert.ToInt32(yyyymmdd.Substring(0, 4));
            int mm = Convert.ToInt32(yyyymmdd.Substring(4, 2));
            int dd = Convert.ToInt32(yyyymmdd.Substring(6, 2));
            DateTime lDate = new DateTime(yyyy, mm, dd);
            DateTime rDate = new DateTime(yyyy, 1, 1);

            string date1;
            string date2;
            date1 = "1" + yyyymmdd.Substring(2, 2);
            TimeSpan span = lDate.Subtract(rDate);

            date2 = Convert.ToString(Convert.ToUInt32(span.Days) + 1).PadLeft(3, '0');

            return Convert.ToInt32(date1 + date2);
        }

        public static BaseDB GetDB(string dbType)
        {
            BaseDB db = null;
            switch (dbType)
            {
                case "TEST":
                    db = new TEST();
                    break;
            }
            return db;
        }
    }
}

 

3) DB타입에 따른 세부 명령어 지정

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;

namespace db_conn
{
    public class MssqlDB:BaseDB
    {
        public MssqlDB(string ip, string uid, string pwd, string db)
            : base(DBTypeEnum.MSSQL)
        {
            m_DB_IP = ip;
            m_DB_ID = uid;
            m_DB_PWD = pwd;
            m_DB_Name = db;
        }

        #region 멤버필드

        private string m_DB_IP = "";
        private string m_DB_ID = "";
        private string m_DB_PWD = "";
        private string m_DB_Name = "";

        #endregion

        #region 상수        
        private const string CN_DB_TIME = "600";    //300초까지 타임아웃한다.
        #endregion

        #region 공개메소드

        /// <summary>
        /// 대량의 DataTable을 인서트한다
        /// </summary>        
        /// <param name="toTbName">삽입할 테이블명</param>
        /// <param name="dt">데이터테이블</param>
        public override string BulkInsert(string toTbName, DataTable dt)
        {
            string ret = "";
            try
            {
                SqlBulkCopy bulk = new SqlBulkCopy(DBConnection().ConnectionString);
                bulk.DestinationTableName = toTbName;
                bulk.WriteToServer(dt);
                bulk.Close();
            }
            catch (Exception exLog)
            {
                ret = exLog.Message;
                throw new Exception(exLog.Message);

            }
            return ret;
        }

        /// <summary>
        /// 결과가 있는 쿼리를 실행
        /// </summary>
        /// <param name="query">쿼리</param>
        /// <returns>결과</returns>
        public override DataTable ExcuteQuery(string query)
        {
            DataTable dt = new DataTable();
            try
            {
                SqlDataAdapter adpt = new SqlDataAdapter(query, (SqlConnection)DBConnection());
                adpt.SelectCommand.CommandTimeout = Convert.ToInt32(CN_DB_TIME);
                adpt.Fill(dt);
            }
            catch (Exception eLog)
            {
                throw eLog;
            }
            return dt;
        }

        /// <summary>
        /// 결과가 없는 쿼리를 실행
        /// </summary>
        /// <param name="query">쿼리</param>
        /// <returns>영향받은 Row수</returns>
        public override int ExcuteNonQuery(string query)
        {
            int ret = 0;
            try
            {
                SqlConnection connect = (SqlConnection)DBConnection();
                connect.Open();
                SqlTransaction tran = connect.BeginTransaction();

                try
                {

                    SqlCommand com = new SqlCommand(query, connect, tran);
                    ret = com.ExecuteNonQuery();

                    tran.Commit();
                    connect.Close();
                }
                catch (Exception ineLog)
                {
                    tran.Rollback();
                    throw ineLog;
                }
            }
            catch (Exception eLog)
            {
                throw eLog;
            }
            return ret;
        }

        protected override DbConnection DBConnection()
        {
            string connectStr = "Server=" + m_DB_IP;
            connectStr = connectStr + ";" + "User ID = " + m_DB_ID;
            connectStr = connectStr + ";" + "Password = " + m_DB_PWD;
            connectStr = connectStr + ";" + "Initial Catalog = " + m_DB_Name;
            connectStr = connectStr + ";" + "Connection Timeout= " + CN_DB_TIME;
            SqlConnection ret = new SqlConnection(connectStr);
            return ret;
        }


        public int InsertUpdateImage(string _SQL, System.Drawing.Image _Image, string _ImageFieldName, System.Drawing.Imaging.ImageFormat _ImageFormat)
        {
            int _SqlRetVal = 0;

            try
            {
                SqlConnection con = (SqlConnection)this.DBConnection();
                con.Open();

                System.Data.SqlClient.SqlCommand _SqlCommand
                    = new System.Data.SqlClient.SqlCommand(_SQL, con);


                System.IO.MemoryStream _MemoryStream = new System.IO.MemoryStream();
                _Image.Save(_MemoryStream, _ImageFormat);


                System.Data.SqlClient.SqlParameter _SqlParameter
                    = new System.Data.SqlClient.SqlParameter("@" + _ImageFieldName, SqlDbType.Image);

                _SqlParameter.Value = _MemoryStream.ToArray();
                _SqlCommand.Parameters.Add(_SqlParameter);


                _SqlRetVal = _SqlCommand.ExecuteNonQuery();


                _SqlCommand.Dispose();
                _SqlCommand = null;
            }
            catch (Exception eLog)
            {
                throw eLog;
            }

            return _SqlRetVal;
        }

        #endregion

       
    }
}

 

이렇게 준비가 다 되시면 본문에서 DB 연결과 명령어 사용이 훨씬 간편해 집니다

 

[사용방법]

1. 사용하는 프로젝트에 빌드를 통해 생성된 구성요소클래스 파일을 참조에 넣어준다

 

2. 본문에서 db_conn을 사용하기 위한 선언문

 

3. 다음과 같은 형태로 DB 연동 사용

string SYS_TIME = string.Empty;

string Sql = " SELECT 시간";
       Sql += "  FROM A ";
TEST db = new TEST();
DataTable dt = db.ExcuteQuery(Sql);

if (dt.Rows.Count > 0)
{
	for (int i = 0; i <= dt.Rows.Count - 1; i++)
	{
		SYS_TIME = dt.Rows[i]["시간"].ToString());
	}
}

 

이렇게 하면 첫번째 방식과 동일한 결과 값을 얻을수 있습니다

 

가장 큰 차이점은 첫번째 방식은 쿼리를 종류 SELECT, INSERT, UPDATE, DELETE의 구문에 따라 방식이 조금 바뀝니다

 

그렇기 때문에 사용할때마다 코딩이 구문에 맞게 맞춰야하지요

 

하지만 두번째 방식의 경우 db의 변환 타입만 맞춰 사용하면 구문에 따른 코딩이 단순화가 된다는 점이 있습니다

 

두개 모두 구현하셔서 테스트 해보시기 바랍니다

 

감사합니다~

반응형
반응형

안녕하세요

 

코딩하는남자 코딩연습생입니다

 

오늘 벌써 4번째 글을 쓰고 있는거 같습니다ㅎㅎㅎ

 

요즘 회사 일이 많아 1일1포스팅을 지킬려고 힘들게 노력하다가

 

1일 1포스팅은 무조껀 지키고 시간이 있을때 많이 포스팅 하자 맘 먹고 하루 벌써 4개의 포스팅을 하고 있습니다ㅎㅎㅎ

 

제가 블로그를 운영해서 미비한 수준의 실력이지만 그래도 같은 개발자분들에게 조금이나마 서로 도와주고

 

정보 공유 할 수 있는 계기가 되길 원합니다ㅎㅎ

 

이번 주제는 C#으로 파워포인트 슬라이드를 구현해볼려고 합니다

 

이걸 만들게 된 계기는 공유폴더에 파워포인트 문서를 특정 조건에 의해 자동 슬라이드를 보여 주는 방식의 

 

프로그램을 만들기 위해서 입니다

 

예를 들면 제조회사에서 공정에 작업표준을 띄운다든지 아니면 손님별 가이드 내용을 보여준다던지

 

이러한 특수 조건에 의해 파워포인트 문서를 자동으로 슬라이드로 실행시키는거죠

 

어떻게 보면 간단한 프로그램이지만 어디에 어떻게 사용하는냐에 따라 많이 응용 될수 있을거 같습니다

 

다시 본론으로 돌아와서 기본 빈 C# 프로젝트를 하나 생성합니다

 

저는 Project라는 이름으로 프로젝트를 생성했습니다

 

그다음 비쥬얼베이직에 오피스 프로그램을 불러올수 있다록 참조 설정을 진행 합니다

 

 

 

참조 관리자에서 COM탭에서 형식 라이브러리 항목중에 다음 리스트들을 참조 추가 해줍니다

 

이렇게 추가 해준뒤 이제 소스 코드를 작성해 볼께요

 

[Source Code]

using PowerPoint = Microsoft.Office.Interop.PowerPoint;

파워 포인트 슬라이드 옵션을 사용하기 위해 using문을 통해 파워포인트를 프로젝트에 삽입 시킵니다

 

 

다름 코드분을 통해 해당 위치의 PPT파일을 슬라이드옵션을 부여하여 실행시킵니다

 

 PowerPoint.Application pptapp = new PowerPoint.Application();
 pptapp.Visible = MsoTriState.msoCTrue;
 PowerPoint.Presentation pptPres = pptapp.Presentations.Open(FileName, MsoTriState.msoCTrue, MsoTriState.msoCTrue, MsoTriState.msoCTrue);
 PowerPoint.SlideShowWindow slideWindow = pptPres.SlideShowSettings.Run();

 

이렇게 실행 하게 되면 PPT파일이 실행되고 자동으로 슬라이드 옵션이 시작되면서

 

바로 슬라이드 표현이 가능해 집니다

 

이렇게 간단한 코드 몇줄로 C#으로 파워포인트 슬라이드를 제어 할 수 있습니다

 

해당 Source Code 중에

 

pptapp.Presentations.Open(FileName, MsoTriState.msoCTrue, MsoTriState.msoCTrue, MsoTriState.msoCTrue);

 

해당 구문에서 FileName의 경우 네트워크 공유 폴더의 파일 명을 가져와 자동 입력되도록 구현 했습니다

 

C#으로 네트워크 공유 폴더 접속 하는 방법은 다음번 블로그에 기재 하도록 하겠습니다

 

해당 구문을 복사하여 FileName를 Fix 고정으로 구현하면 바로 테스트가 가능합니다

 

한번 적용해 보시고 궁금한 사항이나 문제점은 댓글로 남겨 주시면 답변 드리도록 하겠습니다

 

감사합니다~

반응형
반응형

안녕하세요.

 

코딩하는남자 코딩연습생입니다

 

현재 개발 정보와 IT 개발 관련 정보를 게시하면서 블러그를 운영하고 있습니다

 

구글 애드센스 발급 이후 "1일 1게시글을 하자!"

 

독하게 맘 먹고 운영 중인데

 

다음 애드핏 심사에서 계속 보류를 맞고 있습니다ㅎㅎㅎ

 

이유는 다음과 같습니다

 

"해당 매체는 최근 콘텐츠 부족으로 보류되었습니다. 지속적인 업데이트 확인이 가능하도록 콘텐츠를 보충 후 재심사를 요청하여 주시기 바랍니다."

 

나름 열정을 가지고 운영하고 있는 중이고 최근 콘텐츠 부족 이라니...ㅋㅋㅋ

 

혹시나 하는 마음에 구글 검색을 해보니

 

첫심사 이후 6개~8개의 최신글을 등록한뒤 재심사를 요청하면 승인이 된다

 

뭐 이런 글이 있더라구요ㅋㅋ

 

그래서 도전!!

 

근데 역시나 결과는 보류입니다ㅋㅋ

 

역시 세상에는 내맘대로 되는게 하나도 없네요ㅋㅋㅋㅋ

 

그래도 뭐 제가 돈 벌자고 블러그 운영하는건 아니고 구글도 사실 애드센스만 받아놨지 수입 아직 0원이고ㅋㅋㅋ

 

블러그 방문자가 일일 한10,000명 정도 되면 수입이 생길라나요?ㅋㅋㅋ

 

IT 관련 정보 글은 아니지만 블러그를 열심히 운영하고자 하는 마음에 찬물을 끼얻은거 같은 기분이 들어

 

한탄 글 한번 써봤습니다ㅋㅋ

 

혹시 다음 애드핏에서 이 글을 보신다면 승인 부탁드립니다ㅋㅋ

 

애드핏 승인되면 결과 보고 하겠습니다~

반응형
반응형

안녕하세요.

 

코딩하는남자의 코딩연습생입니다

 

C#을 통해서 PPT 자동 슬라이드 프로그램을 구현하고 ClickOnce를 사용하여 배포 할려고 하는데

 

응용 프로그램을 시작할 수 없습니다??

 

읭?? 뭐가 잘못됏지?? 

 

처음에는 프로그램상 오류가 있어서 그런건가 의심해서 코드 부분을 열심히 살펴 보았는데

 

아무 문제가 없고 로컬에서 실행을 하니 아주 잘 되더라구요

 

그래서 자세히(D)...를 눌러서 오류 내용을 확인 해봤어요

 

 

"stdole.dll 어셈블리의 강력한 이름 서명이 잘못되었습니다."

 

이런 오류의 종류로 에러가 난것을 확인한 뒤 바로 모든걸 알고 있을거 같은 구글님에게 폭풍 검색 시작...

 

 

와~ 저와 비슷한 분들이 상당히 많은거 같더라구요 검색량이 엄청 났습니다

 

그중에 제일 위의 검색 내용을 확인해 보니

 

 

https://underbar332.tistory.com/m/13

 

Visual Studio 2013 설치후 stdole.dll 서명에러

VS2010으로 작업했던 작업의 업데이트를 위해서 배포를 했더니 (ClickOnce) 다운로드 완료후 실행시 자꾸 " stdole.dll 어셈블리의 강력한 이름 서명이 잘못되었습니다." 라는 메세지로 업데이트가 안되는 현상이..

underbar332.tistory.com

이런 글이 있더라구요

 

결국 결론은 stdole.dll을 교채 했다는 내용은데...

 

제가 만든 프로그램을 배포 댓수가 20대 가량 되는데 그곳의 모든 PC의 stdole.dll을 교체하는건 

 

참 귀찮고 하기 싫은 일이라서 저는 생각을 좀 바꾸엇습니다

 

제 프로젝트의 참고 내용중 stdole.dll을 삭제 한 후에 배포 해보자!

 

근데 생각의외로 좋은 결과가 나왔습니다

 

 

이렇게 참조 내용중 stdole.dll을 삭제시킨 후 재 게시 진행

 

재 설치 이후 정상 설치가 되면서 아주 잘 실행이 되엇습니다

 

저와 같이 ClickOnce 배포후에 "stdole.dll 어셈블리의 강력한 이름 서명이 잘못되었습니다." 같은 에러로

 

당황하신분들은 링크 페이지의 내용처럼 시도 해보시거나 혹은 저처럼 참조 배포를 변경한뒤 재배포 시도를

 

해보시기 바랍니다~

 

감사합니다^^

반응형
반응형

안녕하세요.

 

코딩하는남자에 코딩연습생입니다

 

이번에 회사에서 관리하는 MES 프로그램에 갑작스런 외국인 사용자가 추가 되어

 

기존 한국어/인도네시아어만 사용되고 있었는데 중국어를 추가해야 될 일이 발생했어요~

 

그래서 어떤식으로 다국어 기능을 구현할까 고민하다가 좋은 정보가 있어 블로그에 기재하게 되었습니다

 

원리는 간단합니다

 

기존 프로젝트에 언어별 Resource 파일을 추가해서 

 

컨트롤별 한국어/인도네이아어/중국어 작성해 놓고 이벤트에 따라 해당 리소스 파일의 정보를 보여주는식으로

 

구동 되어 집니다

 

 

[구현 방법]

1. 기존 프로젝트에 폴더를 삽입

   - 저는 Language라고 햇어요

     (솔루션 탐색기에서 폴더 추가하는 방법은 따로 설명하지 않을께요)

   

 

2. Language 폴더에 리소스 파일을 생성합니다

   - 리소스파일명은 개인 취향입니다 저는 Str.resx라고 했어요

     (Language폴더에서 마우스 오른쪽 버튼을 누루고 추가 새 항목 선택 후 리소스파일을 선택하면 됩니다)

   

 

  - 총 3개의 리소스 파일을 추가했습니다

    (Str.resx : 기본 리소스 파일, Str.ko-KR.resx : 한국어 리소스 파일, Str.zh-CN.resx : 중국어 리소스 파일)

 

3. 리소스 파일의 액세스 한정자 확인

   - Str.resx 기본 리소스 파일의 액세스 한정자 타입은 Internal 입니다

   

 

  - Str.ko-KR.resx와 Str.zh-CN.resx 파일의 액세스 한정자 타입은 코드 생성 안됨으로 하시면 됩니다

  (보통 생성 순서에 따르 자동 지정되니 확인만 하시면 될거 같습니다)

 

4. 디자인을 통해 언어 선택을 할 콤보박스를 생성

   - 저는 콤보박스로 했는데 이것도 개인취향이니 편하신데로 해도 되요

 

5. 콤보박스 이벤트 생성

   - comboBox1_SelectedIndexChanged 이벤트 생성하여 콤보박스의 Index가 변경될때 발생하도록 합니다

 

6. comboBox1_SelectedIndexChanged 코드 생성

   - CultureInfo 함수를 사용하기 위해서는 "using System.Globalization;" 선언문을 꼭 해주셔야 합니다

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBox1.SelectedIndex == 0)
            {
                Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("ko-KR");
                SetTextLanguage();
            }
            else if (comboBox1.SelectedIndex == 1)
            {
                Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("zh-CN");
                SetTextLanguage();
            }

        }

  - 언어 선택에 다른 컨트롤 변경 코드 생성

public void SetTextLanguage()
        {
            lblUSERMODE.Text = POPMachine.Language.Str.lblUSERMODE;
            label4.Text = POPMachine.Language.Str.label4;
            NAME_DATE.Text = POPMachine.Language.Str.NAME_DATE;
            button1.Text = POPMachine.Language.Str.button1;
            button3.Text = POPMachine.Language.Str.button3;
            button4.Text = POPMachine.Language.Str.button4;
            label5.Text = POPMachine.Language.Str.label5;
            label14.Text = POPMachine.Language.Str.label14;
            label2.Text = POPMachine.Language.Str.label2;
            label8.Text = POPMachine.Language.Str.label8;
            label10.Text = POPMachine.Language.Str.label10;
            label7.Text = POPMachine.Language.Str.label7;
            label9.Text = POPMachine.Language.Str.label9;
            label6.Text = POPMachine.Language.Str.label6;
            label16.Text = POPMachine.Language.Str.label16;
            label11.Text = POPMachine.Language.Str.label11;
            label13.Text = POPMachine.Language.Str.label13;
            cmdRun_WMIX_NO1.Text = POPMachine.Language.Str.cmdRun_WMIX_NO1;
            cmdRun_WMIX_NO2.Text = POPMachine.Language.Str.cmdRun_WMIX_NO2;
            cmdRun_WMIX_NO3.Text = POPMachine.Language.Str.cmdRun_WMIX_NO3;
            cmdRun_WHLOT_NO.Text = POPMachine.Language.Str.cmdRun_WHLOT_NO;
            cmdRun_WRACK_NO.Text = POPMachine.Language.Str.cmdRun_WRACK_NO;
            cmdRun_NG_SELFCHACK.Text = POPMachine.Language.Str.cmdRun_NG_SELFCHACK;
            cmdRun_WPART_CUST.Text = POPMachine.Language.Str.cmdRun_WPART_CUST;
            cmdRun_WFORM_MCPLAN.Text = POPMachine.Language.Str.cmdRun_WFORM_MCPLAN;
            cmdRun_WLOT_NO.Text = POPMachine.Language.Str.cmdRun_WLOT_NO;
            hoverGradientButton1.Text = POPMachine.Language.Str.hoverGradientButton1;
            cmdRun_WPCardScan.Text = POPMachine.Language.Str.cmdRun_WPCardScan;
            cmdRun_WStart.Text = POPMachine.Language.Str.cmdRun_WStart;
            cmdRun_WOKQty.Text = POPMachine.Language.Str.cmdRun_WOKQty;
            cmdRun_WPrint.Text = POPMachine.Language.Str.cmdRun_WPrint;
            cmdRun_WMCStopHist.Text = POPMachine.Language.Str.cmdRun_WMCStopHist;
            cmdRun_WFinish.Text = POPMachine.Language.Str.cmdRun_WFinish;
            cmdRun_WProcImage.Text = POPMachine.Language.Str.cmdRun_WProcImage;
            cmdRun_WNGQty.Text = POPMachine.Language.Str.cmdRun_WNGQty;
            cmdRun_WLPrint.Text = POPMachine.Language.Str.cmdRun_WLPrint;
            cmdRun_WWaiting.Text = POPMachine.Language.Str.cmdRun_WWaiting;
            button5.Text = POPMachine.Language.Str.button5;
            cmdRun_Workers.Text = POPMachine.Language.Str.cmdRun_Workers;
            cmdRun_WorkHist.Text = POPMachine.Language.Str.cmdRun_WorkHist;
            cmdRun_Mold.Text = POPMachine.Language.Str.cmdRun_Mold;
            cmdRun_Wheel.Text = POPMachine.Language.Str.cmdRun_Wheel;
            cmdRun_SubPartImage.Text = POPMachine.Language.Str.cmdRun_SubPartImage;
            cmdRun_PCardStatus.Text = POPMachine.Language.Str.cmdRun_PCardStatus;
            cmdRun_Video.Text = POPMachine.Language.Str.cmdRun_Video;
            cmdRun_Call.Text = POPMachine.Language.Str.cmdRun_Call;
            cmdRun_Setting.Text = POPMachine.Language.Str.cmdRun_Setting;
            cmdRun_Exit.Text = POPMachine.Language.Str.cmdRun_Exit;

        }

 

저는 해당 화면에 컨트롤이 많아 이렇게 나열했지만 좀 더 응용하면 Resource 파일을 연계하여

 

같은 종류의 컨트롤별 자동 지정되도록 구현하면 Source Code가 좀 더 깔끔해질거 같네요

 

이렇게 구현하게되면 나중에 언어가 추가 될때 당황하지 않고 Resorce파일만 추가하여 적용해주면

 

더 많은 언어들의 쉽게 적용할 수 있습니다

 

한번 도전해 보세요^^

 

감사합니다

반응형
반응형

안녕하세요.

 

코딩 연습생입니다.

 

이번에는 C# 프로그래밍을 통해 어떤 프로세스가 실행되고 있는지 확인을 하여

 

중복실행 방지 또는 강제 프로세스 종료 Kill(PID) 등에 활용할 수 있는 프로세스 검색 하는 Source Code를

 

한번 구현해 봤어요 이미 인터넷에 많이 공개되어 있는 부분이라 특별히 소스에 대한 설명을 생략할께요

 

이 소스를 활용하여 배포시에 좀 더 안정성 있는 프로그램이 되지 않을까 싶습니다~^^

 

 

 

[C# Source Code]

using System.Diagnostics;

 

private void Confirm()

{

   Process[] processList = Process.GetProcessesByName("찾을려는 프로세스 이름");

   if(processList.Length < 1)

   {

      //프로세스가 실행되지 않고 있을때

   }

   else

   {

     //프로세스가 실행되고 있을때

   }

}

 

 

반응형
반응형

안녕하세요

 

코딩하는남자에 코딩연습생입니다~

 

간혹 프로젝트를 진행하다 보면 두개의 프로젝트를 하나의 솔루션에 합쳐서 개발해야 할 때가 발생한다

 

뭐 프로젝트 두개 실행시키는거야 어렵지 않은데...

 

한쪽의 프로젝트에서 나머지 한쪽을 제어하는듯(?)이 보여 주고 싶을 때가 있다

 

예를들면 1번 프로젝트가 실행되고 나면 자연스럽게(?) 두번째 프로젝트가 실행된다던지

 

1번 프로젝트를 종료하면 자연스럽게(?) 두번째 프로젝트가 종료되는 이런 간단한 조작이 필요할 때가 있다

 

저의 경우 ClickOnce를 주로 사용하여 배포를 하는데 이 경우 실행된 프로세스를 통해 다음과 같은

 

동작을 구현할수 있다

 

이게 좋은 방법인지 아닌지는 잘 모르겠다 하지만 필요할때가 있다

 

 

[C# Source Code]

using System;

using System.Collections.Generic;

using System.Linq;

using System.Windows.Forms;

using System.Diagnostics;

using System.Runtime.InteropServices;

using System.Threading;
namespace Ex_Process_Ctl

{

  static class Program

  {

     //이미 실행중이면 포커스

     [DllImport("user32.dll")]

     private static extern bool SerForegroundWindows(IntPtr handle);

     //이미 실행중일때 활성화

     [DllImport("user32.dll")]

     private static extern int ShowWindow(IntPtr hwnd, int nCmdShow);

     //이미 실행중일때 최상위 표시

     [DllImport("user32.dll")]

     private static extern void BringWindowToTop(IntPtr hwnd);

     //중복실행 방지

     [DllImport("user32.dll")]

     private static extern IntPtr FindWindow(string IpClassName, string IpWindowName);



    ///<summary>

   ///The main entry point for the application.

   ///</summary>

   [STAThread]

   static void Main()

   {

      try

      {

         //프로그램 중복 체크

         if(bCreated == true)

         {

            Application.EnbleVisualStyles();

            Application.SetCompatibleTextRenderingDefault(false);

            Application.Run(new frmMain());

         }

         else

         {

            foreach(Process process in Process.GetProcesses())

            {

               if(process.ProcessName == GV.ProcessName)

               {

                  ShowWindow(process.MainWindowHandle, 5);

                  BringwindowToTop(process.MainWindowHandle);

                  SetForegroundWindow(process.MainWindowHandle);

               }

            }

         }

      }

      catch(Exception Ex)

      {

          MessageBox.Show(Ex.ToString());

      }

   }

  }

}

 

반응형
반응형

안녕하세요

 

코딩하는남자의 코딩연습생입니다

 

프로그래밍을 하다보면 CallBack 함수를 사용해야 되는 상황이 발생합니다

 

예를들면 Soket통신을 구현할때 수신/응답을 비동기식으로 구현해야할 경우가 이에 해당하죠

 

Soket 통신의 원리르 보면 이더넷 환경의 장비에 응답코드를 송신하여 그에 해당하는 결과 값을 받아오게 되죠

 

이경우 두가지 형식으로 구현을 합니다 동기식/비동기식.

 

동기식은 송신후 응답코드가 수신될때까지 멈춰 대기 하게 됩니다

 

구현할려는 장비와 운영 방법에 따라 즉시 응답이 가능하다면 동기식으로 구현하는것이 맞겠죠

 

하기만 한번에 여러대의 장비와 통신을 해야 하는 경우 혹은 수신까지 걸리는 ReadTime이 길 경우에는 무한정

 

기다릴수 만은 없기 때문에 비동기식 구현 방법을 사용 합니다

 

비동기식은 동기식과 반대로 응답코드를 송신하고 수신이 올때까지 다른 대기자가 대기하고 다른 프로세스를 실행 할 수 있죠 

 

 

비동식에서 사용해야 하는 "대리자"가 바로 이번 작성할려는 주제입니다

 

CallBack 함수를 사용할려면 누군가는 항상 값을 받을 준비를 하고 있어야 합니다

 

그 대리자가 Delegate 입니다

 

이번 C#으로 RFID 통신을 구현하면서 사용한 Source Code의 일부를 가지고 설명을 해드릴께요

 

 

1. Delegate 선언

   - RFID Tag와 Reader간 통신을 하면서 Reader가 Tag의 값을 읽기 전까지 무한 대기 합니다

   - 그후 선언된 Delegate를 통해 Tag가 읽히게 되면 나머지 프로세서를 처리하게 되는 구조 입니다

   - Delegate선언 방법은 간단합니다

     

//delegate 선언
//CallBack Fucntion 타입 정의
public delegate void OnSendToRFIDDelegate(int nCmdID, byte[] bBuf, int nSize, string sDump);
public delegate void OnReceiveFromRFIDDelegate(int nCmdID, byte[] bBuf, int nSize, string sDump);
public delegate void OnAddRFIDMsgDelegate(string sMsg);
public delegate void OnRFIDExceptionDelegate();

//CallBack 호출을 위한 전역함수 정의
Public OnSendToRFIDDelegate OnSendToFRID;
public OnReceiveFromRFIDDelegate OnReceiveFromRFID;
public OnAddRFIDMsgDelegate OnAddRFIDMsg;
public OnRFIDExceptionDelegate OnRFIDException;

 

다음과 같이 Delegate를 선언하고 그 Delegate 호출을 위한 전역함수를 선언합니다

 

그리고 Soket 통신을 위한 구문을 작성하죠.

private byte[] RcvBuf = new byte[4096];
byte[] sndBuf = new byte[12];

using(Socket socket = new Socket(AddressFamily.interNetwork, SocketType.Stream, ProtocolType.Tcp))
{
	EndPoint plcEP = new IPEndPoint(IPAddress.Parse("IP주소"), 포트번호);
    
    socket.Connect(plcEP);
    socket.Send(sndBuf);
    
    //Delegate 사용
    if(OnSendToRFID != null)
    	OnSendToRFID(CurCmd, SndBuf, 12, DumpBytes(sndBuf, 12));
        
    int nRecv = 0;
    
    nRecv = socket.Receive(RcvBuf);
    
    //Delegate 사용
    if(OnReceiveFromRFID != null)
    	OnReceiveFromRFID(CurCmd, RcvBuf, nRecv, DumpBytes(RcvBuf, nRecv));
    
    socket.Close();
}

 

운영 원리는 다음과 같습니다 Socket으로 접속하여 송신 응답 메세지를 보낸뒤 대리자를 선언하여 대리자가 null이 아닐때까지 By Psss 되고 null이 아닌 값이 들어오게 되면 대리자를 호출하여 처리 요청을 한뒤 바로 다름 응답코드를 수신할 수 있도록 운영합니다.

 

해당 글은 필자가 개인적인 코딩 연습을 위한 구현이므로 실제 정의와는 다른 의미가 있을 수 있으니

 

참고하시기 바랍니다

 

반응형

+ Recent posts