번역하신 김명신님 블로그에서 확인한 결과 CLR Via C# 4판 번역서가 11월 초에 나온다네요.

 

4판에는 Windows8의 WinRT와의 상호운용에 대한 부분까지도 포함되어 있다고 합니다.

 

11월 초에 나오면 바로 사서 읽어봐야겠습니다!!

 

 

출처 : http://himskim.egloos.com/viewer/3993925

 

 

회사에서 남는 3.5 하드를 백업용으로 사용하기 위해 외장하드 케이스를 구매하였습니다.

 

뭐를 살까 고민 중에 ipTIME HDD3035를 선택하였습니다.

 

택배로 받았는데, 박스 자체가 안전하게 외장하드케이스를 보호하고 있었습니다.

 

박스 사진입니다.

 

 

 

 

 

ipTIME HDD3035는 USB3.0을 지원해서 빠르게 데이터를 송수신 할 수 있습니다.

 

 

 

 

 

 

박스 내부는 아래와 같이 안전하게 외장하드케이스를 보관하고 있었습니다.

 

 

 

 

 

 

아래의 버튼을 누르고 외장하드케이스를 분리 할 수 있습니다.

 

 

 

 

 

분리한 모습입니다.

 

 

 

 

 

ipTIME HDD3035는 자동 백업 프로그램을 지원합니다.

 

 

 

 

 

 

기타 악세사리입니다.

 

 

 

 

 

 

 

제품 설명서입니다.

 

 

 

 

 

 

제품 보증서 입니다.

 

 

 

 

 

제품을 수령후에 테스트 하는데 드라이버 설치가 엄청 오래걸리고 인식이 안되었었습니다.

 

문제는 하드를 제대로 장착하지 않아서였습니다................ㅡㅡ;;;

 

제대로 장착되지 않은 상태에서 다른 직원 컴터에서 테스트하고.......ㅡㅡ;;;

 

고객센터 전화하고.............ㅠㅠㅠㅠㅠㅠㅠ

 

완전 삽질했습니다.........ㅠㅠㅠㅠㅠ

 

 

 

 

아래 이미지와 같이 하드를 더 밀어넣으셔서 합체(?)를 시켜주시면 되겠습니다.

 

 

 

 

이상 ipTIME HDD3035 구매후기를 마치겠습니다.

 

선착순 티스토리 초대장 12장 무료 배포합니다.

 

묻지도 따지지도 않습니다.

 

댓글로 이메일 주소 남겨주세요!

 

 

 

아래의 매크로를 이용해서 컨트롤들의 아이디를 확인하였습니다.

참조 : http://blog.naver.com/PostView.nhn?blogId=heesung2003&logNo=60196368045

 

Sub 컨트롤아이디()
For Each cb In CommandBars
    For Each ctl In cb.Controls
        Cells(Rows.Count, "A").End(3)(2).Resize(, 5) = Array(cb.Name, cb.NameLocal, cb.Visible, ctl.Caption, ctl.ID)
    Next
Next
End Sub 

 

 

컨트롤 아이디를 확인 후  아래와 같이 해당 컨트롤에 접근하여 이벤트를 등록하거나 해당 컨트롤을 숨길 수 있습니다.

 

 

Application app = Globals.ThisAddIn.Application;

 

CommandBarButton delRowBtn = app.CommandBars.FindControl(Id: 293) as CommandBarButton;

CommandBarButton insertRowBtn = app.CommandBars.FindControl(Id: 296) as CommandBarButton;

           

delRowBtn.Visible = true;

delRowBtn.Click += rowDelBtn_Click;

insertRowBtn.Click += insertRowBtn_Click;

 

 

엑셀2013 컨트롤 아이디.xlsx

 

리본 탭의 위치는 특정 컨트롤의 앞이나 뒤에 위치시킬 수 있습니다.

 

예를 들어 홈 탭에 앞에 넣거나 뒤에 넣을 수 있습니다.

 

탭에 대한 아이디는 아래의 경로의 문서를 받아서 확인할 수 있습니다.

 

Office 2013 Help Files: Office Fluent User Interface Control Identifiers
http://www.microsoft.com/en-in/download/details.aspx?id=36798


Office 2010 Help Files: Office Fluent User Interface Control Identifiers
http://www.microsoft.com/en-in/download/details.aspx?id=6627

 

 

 

 

홈탭에 바로 앞에 탭을 추가해보겠습니다.

 

디자이너 Position 속성에서 PositionType은 BeforeOfficeID, OfficeID는 엑셀에서 확인한 TabHome으로 설정합니다.

 

 

 

탭의 위치가 변경된것을 확인 하실 수 있습니다.

 

 

 

숫자를 문자열로 변환할 때 ToString 메서드를 이용합니다.

 

숫자를 문자열로 자주 변경할시에 아래와 같이 LookupTable을 이용하여 구현하시면

 

빠른 속도를 얻으실 수 있습니다.

 

 

 

 

 

 

 

테스트 코드입니다.

 

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Diagnostics;

 

namespace ToStringOptimization

{

    class Program

    {

        static void Main(string[] args)

        {

            //클래스 메모리 미리 올려두기

            ToStringExtensions.ToStringLookup(1);

 

            Stopwatch st = Stopwatch.StartNew();

            for (int i = 0; i < 256; i++)

            {

                string s = i.ToString();

            }

            st.Stop();

            Console.WriteLine("ToString : {0}", st.Elapsed.ToString());

 

            st = Stopwatch.StartNew();

            for (int i = 0; i < 256; i++)

            {

                string s = i.ToStringLookup();

            }

            st.Stop();

            Console.WriteLine("ToStringLookup : {0}", st.Elapsed.ToString());

 

            Console.ReadKey();

        }

    }

} 

 

테스트 결과 :

 

 

 

 

테스트 프로젝트 : ToStringOptimization.zip

 

C# Excel2007 추가 기능을 사용해 아래의 기능을 구현해보았습니다.

 

1. 특정 셀 클릭시 다른 시트의 셀 선택
2. 상태바 컨트롤
3. 툴팁 띄우기
4. Form 띄우기
5. 특정영역만 사용하기 (색표시 & 잠금(패스워드: test))
6. 콤보박스 사용

 

 

 

 

프로젝트 파일(VS2010) :TestExcelAddIn.zip

 

 

참조 : C# Excel 2007 추가기능(AddIn) 만들기

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Tools.Ribbon;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

namespace ExcelAddIn1
{
    public partial class Ribbon1
    {
        Worksheet _sheet;
        public double pppx = -1, pppy = -1;
        public double fbTop = -1, fbLeft = -1;
        public double hLeft = -1, hTop = -1;
        public bool init = false;

        const int LOGPIXELSX = 88;
        const int LOGPIXELSY = 90;
        [DllImport("user32.dll")]
        static extern IntPtr GetDC(IntPtr hWnd);
        [DllImport("user32.dll")]
        static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
        [DllImport("gdi32.dll")]
        static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

        private void PixelsPerPointX()
        {
            if (!this.init)
            {
                IntPtr hdc = GetDC(new IntPtr(0));
                int PixPerInchX = GetDeviceCaps(hdc, LOGPIXELSX);
                double pppx = PixPerInchX / 72.0; //72 is the points per inch
                ReleaseDC(new IntPtr(0), hdc);
                this.pppx = pppx;
            }
        }

        private void PixelsPerPointY()
        {
            if (!this.init)
            {
                IntPtr hdc = GetDC(new IntPtr(0));
                int PixPerInchY = GetDeviceCaps(hdc, LOGPIXELSY);
                double pppy = PixPerInchY / 72.0; //72 is the points per inch            
                ReleaseDC(new IntPtr(0), hdc);
                this.pppy = pppy;
            }
        }

        private void GetFormulaBarAndHeadingsDim()
        {
            if (!this.init)
            {
                bool formBar = Globals.ThisAddIn.Application.DisplayFormulaBar;
                bool headings = Globals.ThisAddIn.Application.ActiveWindow.DisplayHeadings;
                double beforeH, afterH, beforeW, afterW;

                //check the size of the formula bar                
                beforeH = Globals.ThisAddIn.Application.ActiveWindow.Height;
                beforeW = Globals.ThisAddIn.Application.ActiveWindow.Width;
                Globals.ThisAddIn.Application.DisplayFormulaBar = false;
                afterH = Globals.ThisAddIn.Application.ActiveWindow.Height;
                afterW = Globals.ThisAddIn.Application.ActiveWindow.Width;                
                Globals.ThisAddIn.Application.DisplayFormulaBar = true;                
                this.fbLeft = afterW - beforeW;
                this.fbTop = afterH - beforeH;



                this.hLeft = 0;
                this.hTop = 0;

                Globals.ThisAddIn.Application.DisplayFormulaBar = formBar;
                Globals.ThisAddIn.Application.ActiveWindow.DisplayHeadings = headings;
            }
        }

        private void log(String s)
        {
            this.label3.Label += s + " | ";
        }

        private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
        {            
            this._sheet = Globals.ThisAddIn.Application.ActiveSheet;
            this._sheet.SelectionChange +=new DocEvents_SelectionChangeEventHandler(sheet_SelectionChange);
        }

        private void sheet_SelectionChange(Range rng)
        {
            MessageBox.Show("Select changed!");
        }


        private void CellTopLeftPixels(Range rng)
        {
            if (!init)
            {
                PixelsPerPointX();
                PixelsPerPointY();
                GetFormulaBarAndHeadingsDim();
                init = true;
            }

            double appTop = Globals.ThisAddIn.Application.Top;
            double appLeft = Globals.ThisAddIn.Application.Left;
            long RibbonHeight = Globals.ThisAddIn.Application.CommandBars["Ribbon"].Height;
            

            this.log("aT " + appTop);
            this.log("aL " + appLeft);
            this.log("rH " + RibbonHeight);
            this.log("px " + this.pppx);
            this.log("py " + this.pppy);
            this.log("fY " + this.fbTop);
            this.log("fX " + this.fbLeft);
            this.log("hY " + this.hTop);
            this.log("hX " + this.hLeft);
            this.log("rT " + rng.Top);
            this.log("rL " + rng.Left);
            this.log("1T " + (appTop + RibbonHeight + rng.Top + this.fbTop + this.hTop));
            this.log("1L " + (appLeft + rng.Left + this.fbLeft + this.hTop));


            long top = (long)((appTop + RibbonHeight + rng.Top + this.fbTop + this.hTop) * this.pppy);
            long left = (long)((appLeft + rng.Left + this.fbLeft + this.hLeft) * this.pppx);
            this.label1.Label = "left: " + left + " top: " + top;


            long topc = (long)((appTop + RibbonHeight + rng.Top + this.fbTop + this.hTop + rng.Height) * pppy);
            long leftc = (long)((appLeft + rng.Left + rng.Width + this.fbLeft + this.hTop) * pppx);
            this.label2.Label = "left: " + leftc + " top: " + topc;
        }

        private void button1_Click(object sender, RibbonControlEventArgs e)
        {
            this.label3.Label = "";
            CellTopLeftPixels(Globals.ThisAddIn.Application.ActiveCell);
        }        
    }
}
 

출처 : https://svn.kwarc.info/repos/sissi/trunk/win_office_alex/ExcelAddIn1/ExcelAddIn1/Ribbon1.cs

Thread에서 Task

스레드를 만드는 데는 비교적 많은 리소스가 필요하고 개별 스레드의 가상 메모리 소비량도 큰 편이다(기본 1MB). 앞에서 살펴 본 것처럼 스레드 풀을 이용하면 스레드를 만들어서 비동기 작업을 처리하고 작업이 끝난 스레드를 재사용할 수 있기 때문에 매번 새로 스레드를 만드는 것보다 효과적이다.

 닷넷 프레임워크 4에서는 비동기 작업을 시작할 때마다 운영체제 스레드를 만들어서 사용하는 대신 TPL이 태스크(Task)를 만들고 작업 스케줄러에게 처리할 비동기 작업이 있음을 통보하는 방식을 도입했다. 태스크 스케줄러를 이용하는 수많은 방법이 있겠지만 기본적으로 태스크 스케줄러는 스레드 풀에 작업 스레드를 요청한다. 스레드 풀은 효율성을 높이려고 현재 진행 중인 다른 작업이 끝난 다음에 요청된 작업을 처리하는 것이 낫다고 판단할 수도 있고, 혹은 요청된 비동기 작업을 처리하기 위한 작업 스레드를 특정 프로세서에 할당할 수도 있다. 스레드 풀은 또한 새 작업을 실행하는 일에 기존의 스레드를 재활용하는 것이 나을지 혹은 새 스레드를 만들어서 사용하는 것이 효과적일지도 판단한다.

 Task 개체의 안쪽에 내장하도록 비동기 작업을 추상화함으로써 TPL은 비동기 작업을 대변하는 개체를 제공하고 필요한 작업과 상호작용이 가능한 개체 지향 API를 제공할 수 있다. 그리고 작업의 단위를 표현하는 개체를 제공하므로 TPL은 프로그래밍을 통해 작은 작업들로 구성된 큰 작업을 워크플로우로 구성할 수 있으며, 이 점을 이후에 살펴본다.

 태스크는 비동기로 수행할 작업을 캡슐화하는 개체다. 코드를 대변하는 개체라는 점에서 대리자의 개념과 비슷하게 들릴 수 있다. 대리자는 동기적이며 태스크는 비동기적이라는 점에서 차이가 난다. , 예를 들어 Action과 같은 대리자를 실행하면 현재 스레드의 제어점은 즉시 대리자 코드로 이동하고 대리자의 실행이 끝날 때까지 제어를 반환하지 않는다. 이와 대조적으로 태스크는 처리해야 할 작업의 양과 무관하게 태스크 시작과 동시에 호출측으로 제어를 반환한다. 태스크는 비동기적으로 실행되며 보통 별도의 스레드를 이용한다.(이번 장의 뒤에서 살펴 볼 것인데, 사실 스레드 한 개만 이용해서 태스크들을 비동기적으로 실행하는 것이 가능하며 또 이득이 되기도 한다.) 태스크는 본질적으로 대리자를 동기적은 형태에서 비동기 실행 패턴으로 변환한다.

 

 - Essential C# 5.0 (C#의 기초와 고급을 아우르는 핵심 바이블) - 18장 다중 스레딩

 

ManualResetEvent vs ManualResetEventSlim vs AutoResetEvent

재설정 이벤트에는 System.Threading.ManualResetEvent와 닷넷 프레임워크 4에서 추가된 간략 형태인 System.Threading.ManualResetEventSlim이 있다. 재설정 이벤트에서 제공하는 중요 메소드에는 Set() Wait()(ManualResetEvent에는 WaitOne())가 있다. Wait() 메소드를 호출하면 다른 스레드에서 Set()을 호출하거나 지정한 대기 시간이 끝날 때 까지 해당 스레드를 차단할 것이다.

ManualResetEventSlimManualResetEvent의 차이점은 후자의 경우 기본적으로 커널 동기화를 사용하도록 되어 있는 방면 전자는 피치 못할 경우를 제외하고는 커널을 통하지 않도록 최적화되어 있다. 따라서 ManualResetEventSlim이 더 많은 CPU 사이클을 사용할 가능성이 있지만 더 우수한 성능을 나타낸다. 그러므로 여러 개의 이벤트를 대기하거나 프로세스간 대기와 같은 요구가 없는 일반적인 경우에는 ManualResetEventSlim을 사용하자.

 System.Threading.AutoResetEvent라는 세 번째 재설정 이벤트가 있는데, 이것도 ManualResetEvent와 마찬가지로 스레드가 코드 상의 특정 위치까지 진행앴음을 다른 스레드에 신호(Set() 호출로 전달) 할 수 있게 해 준다. 차이점은 AutoResetEvent의 경우 신호를 받은 첫 번째 스레드가 자동 재설정 게이트를 통과하면 자동으로 다시 잠금이 설정되기 때문에 단 하나의 스레드만 차단해제한다는 것이다. 하지만 이와 같은 자동 재설정 이벤트를 사용할 때 생산자 스레드가 실수로 소비자 스레드의 개수보다 더 많은 반복을 할 위험성이 너무 쉽게 노출된다. 그러므로 일반적으로 Monitor Wait() / Pulse() 패턴 혹은 세마포어의 사용이 권장된다(특정 블록에 대해 참여할 수 있는 스레드가 n개보다 작은 경우). AutoResetEvent와 대조적으로 ManualResetEvent의 경우에는 Reset()을 명시적으로 호출하지 않으면 설정되기 전 상태(신호를 보내기 전)로 되돌아가지 않는다.

 

 - Essential C# 5.0 (C#의 기초와 고급을 아우르는 핵심 바이블) - 18장 다중 스레딩

 

+ Recent posts