달력

052018  이전 다음

  •  
  •  
  • 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
  •  
  •  


통신 프로그램을 만들 때 프로토콜을 구조체로 잡고 바이트 배열로 쏘는 경우가 생기는데

아래와 같이 하면 된다.

// 구조체를 바이트 배열로 변환
public static int CvtStructToBin(object obj, out byte[] bin)
{
      bin = new byte[Marshal.SizeOf(obj)];

      unsafe
      {
          fixed (byte* fixed_buffer = bin)
          {
              Marshal.StructureToPtr(obj, (IntPtr)fixed_buffer, false);
          }
      }

      return bin.Length;

}

// 바이트 배열을 구조체로 변환

        public static int CvtBinToStruct(byte[] bin, object obj)
        {
            
            unsafe
            {
                fixed (byte* fixed_buffer = bin)
                {
                    Marshal.PtrToStructure((IntPtr)fixed_buffer, obj);
                }
            }

            return 0;
        }

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

구조체 -> 바이트 배열, 바이트 배열 -> 구조체  (0) 2010.03.16
C#에서 로그 함수 만들기  (0) 2010.03.05
Posted by 알이씨


C나 C++에서 콘솔이나 UI의 디버깅창에 로그를 남기면서 파일로도 로그를 남기는 작업을 했었다.

주로 서버 개발이나 펌웨어 개발할 때 썼었는데 이번 프로젝트는 c#을 사용해야 하기에 맘먹고 만들어봤다.

소스코드는 아래에 있고 로그 발생지(eBlk)나 로깅 수준(eLevel)은 입맛대로 바꿔서 쓰면 될듯...

보통 릴리즈 후에는 어느정도 버그를 잡았다고 보기 때문에 ERROR, ABNORMAL, ESSENTIAL 정도만 남겨두고 나머지 로깅 수준은 꺼두는 것이 좋다.

이러한 로깅 수준 조정은 설정 파일을 따로 두어서 설정 내용에 따라 조정하면 되겠다. 아래 소스에서는 일단 모두 보이게 하는 것으로 해두었다.

public enum eBlk
    {
        BLK_GATE = 0,
        BLK_MNGR,
        BLK_MAIN,
        BLK_DB,
        MAX_BLK
    }

    public enum eLevel
    {
        D_ERROR = 0,
        D_ABNORMAL,
        D_ESSENTIAL,
        D_PRIMITIVE,
        D_NORMAL,
        D_TIMER,
        D_PERIODIC,
        D_HEXA,
        D_MAX_LEVEL
    }

    public static class MBzPrintf
    {
        static string[] S_BLK = new string[(int)eBlk.MAX_BLK];
        static string[] S_TYPE = new string[(int)eLevel.D_MAX_LEVEL];
        static byte[] BLK_OP = new byte[(int)eLevel.D_MAX_LEVEL];

        public static void MBzInitPrintf()
        {
            S_BLK[(int)eBlk.BLK_GATE] = "[G W]";
            S_BLK[(int)eBlk.BLK_MNGR] = "[MNG]";
            S_BLK[(int)eBlk.BLK_MAIN] = "[M N]";
            S_BLK[(int)eBlk.BLK_DB] = "[ DB]";

            S_TYPE[(int)eLevel.D_ERROR] = "[ERR]";
            S_TYPE[(int)eLevel.D_ABNORMAL] = "[ABN]";
            S_TYPE[(int)eLevel.D_ESSENTIAL] = "[ESS]";
            S_TYPE[(int)eLevel.D_PRIMITIVE] = "[PRI]";
            S_TYPE[(int)eLevel.D_NORMAL] = "[NOR]";
            S_TYPE[(int)eLevel.D_TIMER] = "[TMR]";
            S_TYPE[(int)eLevel.D_PERIODIC] = "[PRD]";
            S_TYPE[(int)eLevel.D_HEXA] = "[HEX]";

// 모든 로깅 수준을 다 풀었다.
            for(int i = 0; i < (int)eLevel.D_MAX_LEVEL; i++){
                BLK_OP[i] = 1;
            }           
        }

        public static int MBzWriteLine(eBlk blk, eLevel level, string format, params object[] args)
        {
            if((int)blk > (int)eBlk.MAX_BLK){
                return 0;
            }

            if (BLK_OP[(int)level] == 0)
            {
                return 0;
            }

            string log_str;
            log_str = String.Format(DateTime.Now.ToString("yyyy-MM-dd HH:mm") + " {0}{1} ", S_BLK[(int)blk], S_TYPE[(int)level]);
            log_str += String.Format(format, args);

            Console.WriteLine(log_str);

// Todo: 여기쯤에 파일에 기록하는 것도 넣으면 좋겠다.

            return log_str.Length;
        }
    }


사용하는 곳에서 아래와 같이 먼저 초기화 해주고.

MBzPrintf.MBzInitPrintf();

아래 처럼 로깅하면 된당...
MBzPrintf.MBzWriteLine(eBlk.BLK_MAIN, eLevel.D_NORMAL,
                    "=============== 서버를 시작합니다.==================");

MBzPrintf.MBzWriteLine(eBlk.BLK_MAIN, eLevel.D_NORMAL,
                    "=============== 서버를 끌께요.==================");

C#에 익숙치 않아서 시행착오가 참 많은데 아직까진 다 해결중...

enum 쓰는게 엄청 불편한데.. c처럼 되면 얼마나 좋아.. ㅜㅜ;

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

구조체 -> 바이트 배열, 바이트 배열 -> 구조체  (0) 2010.03.16
C#에서 로그 함수 만들기  (0) 2010.03.05
Posted by 알이씨
TAG C#, 로깅


최근 고민이 하나 생겼다.

회사에서 기본적으로 VS6.0에서 MFC로 Windows programming을 하는데

언젠가는 갈아타야하지 않나는 생각이 들었다.

최근 프로젝트에서 asp .net 2.0 with c# 환경에서 돌아가는 웹어플을 하나 만들었는데

이 때 사용한 툴은 vs2005이다.

유저의 요구사항의 추세가 인스톨 형태로 프로그램을 깔기는 싫어하고 기존의 리치 클라이언트 수준의 프로그램을 요구하는데 이를 만족할만한 솔루션이 마땅치가 않다.

Smart Client도 있고 ActiveX도 있고 여러가지 방법이 있겠지만 호환이니. 망해가는 솔루션이니 해서 손대기도 두렵고...

유저마다 웹에서 되게 해달라 그냥 어플로 되게 해달라 요구사항도 계속 바뀔지도 모르고..

결국 Windows Programming의 내용을 그대로 Web Programming 형태로 가져갈 수 있으면

가장 좋은 것인데...

코어 부분을 모듈화 하고 GUI(어플이든 웹이든) 파트는 따로 가져가는게 현재로썬 현명한 선택인것 같다.

기왕 하는거 c#으로 .net 환경에서 돌리면 나중에 좋지 않을까 생각도 들긴하지만..

c#이넘 그리 시원치 않은거 같기도 하고...

결국 코어는 c나 c++로 만들고 껍대기는 MFC로 꾸미고.. 다른 환경으로 바뀌면 그에 맞는

껍대기 부분을 따로 코딩하던지.. 해야될것 같다.

근데 GUI는 이 GUI 꾸미는게 엄청난 노가다인데...

ActiveX로 런처 만들고 윈어플 형태로 클라이언트 다운로드 하게 하는게 과연 좋은 선택인가?

앞으로를 봐도??

MFC를 하는데 굳이 vs6.0에서 vs2005로 옮겨타야 하는 이유가 있을까?

Windows 계열의 모든 OS에서 다 돌아가게 하는게 목적이라면 옮겨타야하나?

하여간 M$ 이넘들은 자꾸 새로운거 내놔서 사람 골치만 아프게 하니..원!!!
Posted by 알이씨


요구사항 : 동적으로 구성한 Table Control의 상태를 유지
배경 : Database에서 특정 데이터를 가져와 가공한 뒤 사용자의 입맛에 맞게 테이블로 그 결과를 구성하였다. 하지만 다른 컨트롤을 조작하여 PostBack이 일어난다면 이전에 바운딩한 테이블 정보들이 모두 초기화 된다. 따라서 PostBack이 일어나도 이전 단계의 테이블 상태를 유지하는 것이 필요하였다.
조건 : 테이블 내용이 바뀔 필요가 없는 경우 PostBack이 일어난 뒤 DB 접근은 하지 않는다.

예제 코드

 Table Table_Work_History;

protected void Page_Load(object sender, EventArgs e)
{
    if(Page.IsPostBack){            
        if(Session["Table"] != null){
            Table_Work_History = (Table)Session["Table"]; // 저장된 테이블 상태 로딩
            PH.Controls.Add(Table_Work_History); // PH : Place Holder
        }           
    }
}

protected void rend_working()
{
    // Bounding Code

    Session["Table"] = Table_Work_History; // 테이블 상태를 세션에 저장
}

참고 :
 - Place Holder를 쓴이유는 테이블을 로딩한 후에 특정 위치에 넣어주기 위함이다.
 - Session을 이용하여 상태를 저장한 이유는 ViewState를 사용하면 Serializable 예외가 발생하기 때문이다.
        

Posted by 알이씨


단순 예제

int aa = 3;
str = String.Format("{0:000}", aa);

결과 : 003

UInt32 aa = 10;
string s = String.Format("{0:X3}", aa);

결과 00A

UInt32 aa = 10;
string s = String.Format("{0:x3}", aa);

결과 00a
Posted by 알이씨


MSDN을 검색하다 굉장히 유용(?) 할것 같은 예제가 있길래 퍼왔다.
출저는 발켰으니 괜찮겠지.. 친절하게 MSDN에 소스 복사하기까지 있으니까 ㅡㅡ;


// This code example demonstrates the String.Format() method.
// Formatting for this example uses the "en-US" culture.

using System;
class Sample
{
    enum Color {Yellow = 1, Blue, Green};
    static DateTime thisDate = DateTime.Now;

    public static void Main()
    {
// Store the output of the String.Format method in a string.
    string s = "";

    Console.Clear();

// Format a negative integer or floating-point number in various ways.
    Console.WriteLine("Standard Numeric Format Specifiers");
    s = String.Format(
        "(C) Currency: . . . . . . . . {0:C}\n" +
        "(D) Decimal:. . . . . . . . . {0:D}\n" +
        "(E) Scientific: . . . . . . . {1:E}\n" +
        "(F) Fixed point:. . . . . . . {1:F}\n" +
        "(G) General:. . . . . . . . . {0:G}\n" +
        "    (default):. . . . . . . . {0} (default = 'G')\n" +
        "(N) Number: . . . . . . . . . {0:N}\n" +
        "(P) Percent:. . . . . . . . . {1:P}\n" +
        "(R) Round-trip: . . . . . . . {1:R}\n" +
        "(X) Hexadecimal:. . . . . . . {0:X}\n",
        -123, -123.45f);
    Console.WriteLine(s);

// Format the current date in various ways.
    Console.WriteLine("Standard DateTime Format Specifiers");
    s = String.Format(
        "(d) Short date: . . . . . . . {0:d}\n" +
        "(D) Long date:. . . . . . . . {0:D}\n" +
        "(t) Short time: . . . . . . . {0:t}\n" +
        "(T) Long time:. . . . . . . . {0:T}\n" +
        "(f) Full date/short time: . . {0:f}\n" +
        "(F) Full date/long time:. . . {0:F}\n" +
        "(g) General date/short time:. {0:g}\n" +
        "(G) General date/long time: . {0:G}\n" +
        "    (default):. . . . . . . . {0} (default = 'G')\n" +
        "(M) Month:. . . . . . . . . . {0:M}\n" +
        "(R) RFC1123:. . . . . . . . . {0:R}\n" +
        "(s) Sortable: . . . . . . . . {0:s}\n" +
        "(u) Universal sortable: . . . {0:u} (invariant)\n" +
        "(U) Universal sortable: . . . {0:U}\n" +
        "(Y) Year: . . . . . . . . . . {0:Y}\n",
        thisDate);
    Console.WriteLine(s);

// Format a Color enumeration value in various ways.
    Console.WriteLine("Standard Enumeration Format Specifiers");
    s = String.Format(
        "(G) General:. . . . . . . . . {0:G}\n" +
        "    (default):. . . . . . . . {0} (default = 'G')\n" +
        "(F) Flags:. . . . . . . . . . {0:F} (flags or integer)\n" +
        "(D) Decimal number: . . . . . {0:D}\n" +
        "(X) Hexadecimal:. . . . . . . {0:X}\n",
        Color.Green);      
    Console.WriteLine(s);
    }
}
/*
This code example produces the following results:

Standard Numeric Format Specifiers
(C) Currency: . . . . . . . . ($123.00)
(D) Decimal:. . . . . . . . . -123
(E) Scientific: . . . . . . . -1.234500E+002
(F) Fixed point:. . . . . . . -123.45
(G) General:. . . . . . . . . -123
    (default):. . . . . . . . -123 (default = 'G')
(N) Number: . . . . . . . . . -123.00
(P) Percent:. . . . . . . . . -12,345.00 %
(R) Round-trip: . . . . . . . -123.45
(X) Hexadecimal:. . . . . . . FFFFFF85

Standard DateTime Format Specifiers
(d) Short date: . . . . . . . 6/26/2004
(D) Long date:. . . . . . . . Saturday, June 26, 2004
(t) Short time: . . . . . . . 8:11 PM
(T) Long time:. . . . . . . . 8:11:04 PM
(f) Full date/short time: . . Saturday, June 26, 2004 8:11 PM
(F) Full date/long time:. . . Saturday, June 26, 2004 8:11:04 PM
(g) General date/short time:. 6/26/2004 8:11 PM
(G) General date/long time: . 6/26/2004 8:11:04 PM
    (default):. . . . . . . . 6/26/2004 8:11:04 PM (default = 'G')
(M) Month:. . . . . . . . . . June 26
(R) RFC1123:. . . . . . . . . Sat, 26 Jun 2004 20:11:04 GMT
(s) Sortable: . . . . . . . . 2004-06-26T20:11:04
(u) Universal sortable: . . . 2004-06-26 20:11:04Z (invariant)
(U) Universal sortable: . . . Sunday, June 27, 2004 3:11:04 AM
(Y) Year: . . . . . . . . . . June, 2004

Standard Enumeration Format Specifiers
(G) General:. . . . . . . . . Green
    (default):. . . . . . . . Green (default = 'G')
(F) Flags:. . . . . . . . . . Green (flags or integer)
(D) Decimal number: . . . . . 3
(X) Hexadecimal:. . . . . . . 00000003

*/

Posted by 알이씨


아래와 같이 char배열에서 string으로 string에서 char로 형변환 할 수 있다.
보너스는 char 배열 넘기기~ (복사해서 넘긴다. 주소를 넘기려면 ref를 사용)

string str = "12345";
char[] temp_str;
temp_str = str.ToCharArray(); //  string -> char []
Rcv_Char_arr(temp_str);

protected void Rcv_Char_arr(char[] arr)
{
    string str = new string(arr); // char[] -> string
}
Posted by 알이씨



목표

특정 이미지를 클릭하면 다른 이미지로 변경되고 다시 클릭하면 원래 이미지로 돌아오는 토글 형식의 이벤트를 구현, 단 해당 컨트롤은 서버쪽에서도 접근 가능해야 함


준비
이미지 컨트롤은 asp .net의 이미지 컨트롤을 사용하지 않고 html 컨트롤을 사용한다. 적당한 위치에 이미니 컨트롤을 추가

<span style="cursor:hand" onclick="return Test_Change_Img('Test_Img');">       
        <img src = "img/small-sphere-green.bmp" runat="server" alt="" id= "Test_Img" name = "Test_Img" style="border-width: 0" enableviewstate="false" /></span>

초기 이미지는 img 폴더의 small-sphere-green.bmp 로 설정하였다.


구현

함수를 호출하는 곳에서 해당 이미지의 이름을 함께 넘겨준다. 함수 구현부에서는 컨트롤 이름을 통해 이미지의 url을 알아내고 해당 url을 비교하여 토글링에 이용한다.

주의 : 이미지 src에는 full url이 들어가 있다 따라서 이미지 파일 이름만 얻어오기 위해 몇가지 string 관련 함수를 이용하였다.

function Test_Change_Img(){   
    var str = document[Test_Change_Img.arguments[0]].src;   
    var str_index = str.[각주:1]lastIndexOf("/");
    var img_src_str = str.[각주:2]slice(str_index+1, str.length);    
   
    if(img_src_str == "small-sphere-red.bmp"){
        document[Test_Change_Img.arguments[0]].src = "img/small-sphere-green.bmp";
    }else{
        document[Test_Change_Img.arguments[0]].src = "img/small-sphere-red.bmp";
    }   
}


비하인드 코드(*.cs)에서는 다음과 같이 이미지 컨트롤에 접근할 수 있다.

Test_Img.Src = "img/small-sphere-red.bmp";
  1. 매개변수 스트링이 나타나는 마지막 위치 인덱스 반환 [본문으로]
  2. 문자열의 특정 위치부터 특정 위치까지를 반환 [본문으로]
Posted by 알이씨



목표

 페이지 내에서 SQL SERVER의 특정 테이블의 내용이 변경될 때만 해당 테이블을 표현하는 컨트롤을 다시 파싱하여 읽고 싶다. 이외의 다른 컨트롤들은 테이블 변경과 상관없이 다시 파싱하게 한다.

준비

  1. 사용하는 데이터베이스가 캐싱이 가능하게 만들어 줘야한다.
  2. web.config에 캐싱 관련 코드를 입력해야한다.

1. 데이터베이스가 캐싱이 가능하게 등록

Visual Studio 2005 Command Prompt에서 다음과 같이 입력

==== DB를 캐싱 가능하게 등록 ====

aspnet_regsql -S "." -U "UserID" -P "UserPassword" -d "DBName" -ed

>> DB에 AspNet_SqlCacheTablesForChangeNotification 테이블이 생성됨

==== 테이블을 캐싱 가능하게 등록 ====

aspnet_regsql -S "." -U "UserID" -P "UserPassword" -d "DBName" -et -t "TableName"

>> AspNet_SqlCacheTablesForChangeNotification 테이블에 TableName 테이블 관련 정보가 추가됨

2. web.config에 캐싱 관련 코드 입력

<system.web>
    <caching>
       <sqlCacheDependency enabled="true" pollTime="1000">
        <databases>
         <add name="DBName" connectionStringName="ConnectionStringName" />
        </databases>
       </sqlCacheDependency>
    </caching>
</system.web>

시작

WebUserControl 생성

1. Add New Item -> Web User Control 추가
2. 생성된 ascx 소스에서 상단에 다음 코드 추가

<%@ OutputCache Duration="999999" SqlDependency="DBName:TableName" VaryByParam="none" %>


WebPage에 WebUserControl 삽입

1. aspx 소스 상단에 다음 코드 추가

<%@ Register Src="FileName.ascx" TagName="TagName" TagPrefix="uc1" %>

2. 적당한 위치에 WebUserControl 삽입

<uc1:TagName ID="UserControlID" runat="server" />


테스트

1. TableName에 INSERT, DELETE, UPDATE와 같은 쿼리가 수행이 되면
AspNet_SqlCacheTablesForChangeNotification 테이블에 changID 필드의 값이 변경됨

2. WebUserControl 페이지에 라벨을 하나 추가하고 Page_Load에 다음 코드 삽입

   [각주:1]Label1.Text = System.DateTime.Now.ToString();


3. 브라우저에서 aspx 페이지를 읽으면 현재 시간이 찍힌다. 하지만 F5든 CTRL+F5든 눌러서 새로고침을 해도 그 시간은 바뀌지 않는다. 즉 새로 파싱을 하는 것이 아니라 캐싱된 페이지를 그대로 읽는 것이다.

4. 캐싱으로 연결된 테이블의 내용을 변경하고 F5를 눌러 새로고침을 하면 시간이 업데이트 된다.

  1. 만약 페이지가 새로 파싱이 되면 시간이 변경될 것이다. [본문으로]
Posted by 알이씨


주의 : FileUpload 컴퍼넌트 추가 후 사용
참고 : 같은 이름의 파일이 존재할 경우 "filename_x.ext" 형식으로 파일을 업로드 함

========= 파일 업로드 ==========
        if (FileUpload1.HasFile)
        {
            string upDir = "E:\\Study\\Web\\EmsClient\\Upload\\";
            DirectoryInfo di = new DirectoryInfo(upDir);
            if (!di.Exists)
                di.Create();

            string fName = FileUpload1.FileName;
            string fFullName = upDir + fName;

            FileInfo fInfo = new FileInfo(fFullName);
            if (fInfo.Exists)
            {

                int fIndex = 0;
                string fExtension = fInfo.Extension;
                string fRealName = fName.Replace(fExtension, "");

                string newFileName = "";
                do
                {
                    fIndex++;
                    newFileName = fRealName + "_" + fIndex.ToString() + fExtension;
                    fInfo = new FileInfo(upDir + newFileName);
                } while (fInfo.Exists);

                fFullName = upDir + newFileName;
            }

            FileUpload1.PostedFile.SaveAs(fFullName);
            Label1.Text = "업로드 된 파일 : " + fFullName;
        }
        else
        {
            Label1.Text = "업로드 된 파일이 존재하지 않습니다.";
        }

Posted by 알이씨

티스토리 툴바