IP 및 호스트 이름으로 같은 망내에 있는 PC의 MacAddress를 얻어오는 코드입니다.

 

 

 

   

    static class MacAddressProvider

    {

        const int PING_TIMEOUT = 1000;

 

        [DllImport("iphlpapi.dll", ExactSpelling = true)]

        static extern int SendARP(int DestIP, int SrcIP, byte[] pMacAddr, ref uint PhyAddrLen);

 

        // *********************************************************************

        /// <summary>

        /// Gets the MAC address from ARP table in colon (:) separated format.

        /// </summary>

        /// <param name="hostNameOrAddress">Host name or IP address of the

        /// remote host for which MAC address is desired.</param>

        /// <returns>A string containing MAC address; null if MAC address could

        /// not be found.</returns>

        public  static string GetMACAddressFromARP(string hostNameOrAddress)

        {

            if (!IsHostAccessible(hostNameOrAddress))

                return null;

 

            IPHostEntry hostEntry = Dns.GetHostEntry(hostNameOrAddress);

 

            if (hostEntry.AddressList.Length == 0)

                return null;   

           

 

            byte[] macAddr = new byte[6];

 

            uint macAddrLen = (uint)macAddr.Length;

 

            if (SendARP((int)hostEntry.AddressList[0].Address, 0, macAddr, ref macAddrLen) != 0)

                return null;

 

            StringBuilder macAddressString = new StringBuilder();

            for (int i = 0; i < macAddr.Length; i++)

            {

                if (macAddressString.Length > 0)

                    macAddressString.Append(":");

                macAddressString.AppendFormat("{0:x2}", macAddr[i]);

            }

            return macAddressString.ToString();

        }

 

        // *********************************************************************

        /// <summary>

        /// Checks to see if the host specified by

        /// <paramref name="hostNameOrAddress"/> is currently accessible.

        /// </summary>

        /// <param name="hostNameOrAddress">Host name or IP address of the

        /// remote host for which MAC address is desired.</param>

        /// <returns><see langword="true" /> if the host is currently accessible;

        /// <see langword="false"/> otherwise.</returns>

        private static bool IsHostAccessible(string hostNameOrAddress)

        {

            Ping ping = new Ping();

            PingReply reply = ping.Send(hostNameOrAddress, PING_TIMEOUT);

            return reply.Status == IPStatus.Success;

        }

    }

 

 

타 프로세스에 메세지를 전송하는 방법은 여러가지가 있겠지만,

 

2개의 Winform 프로그램에서 SendMessage 함수를 이용해 타 프로세스에 문자열 메세지를 전송하는 방법을 포스팅하겠습니다.

 

 

 

WM_COPYDATA 메세지를 이용하겠습니다. 예제를 위한 코드이니 코드 중복은 이해해주세요^^;

 

메세지를 수신할 Winform에서는 WndProc(윈도우 프로시져) 메서드를 오버라이드합니다.

 

 

 

 

 

 const int WM_COPYDATA = 0x4A;

 

        public struct COPYDATASTRUCT

        {

            public IntPtr dwData;

            public int cbData;

            [MarshalAs(UnmanagedType.LPStr)]

            public string lpData;

        }

 

        protected override void WndProc(ref Message m)

        {

            try

            {

                switch (m.Msg)

                {

                    case WM_COPYDATA:

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

                        MessageBox.Show(cds.lpData);

                        break;

                    default:

                        base.WndProc(ref m);

                        break;

                }

            }

            catch (Exception ex)

            {

                MessageBox.Show(ex.Message);

            }

        }

 

 

 

 

 

메세지를 전송할 Winform에서는 SendMessage 함수를 이용해 메세지를 송신합니다.

 

 

 

 

const int WM_COPYDATA = 0x4A;

  

        [DllImport("user32.dll", CharSet = CharSet.Auto)]

        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, uint wParam, ref COPYDATASTRUCT lParam);

 

        public struct COPYDATASTRUCT

        {

            public IntPtr dwData;

            public int cbData;

            [MarshalAs(UnmanagedType.LPStr)]

            public string lpData;

        }

 

        private void OnButtonSendClick(object sender, EventArgs e)

        {

            string msg = this.tbMsg.Text.Trim();

 

            if (string.IsNullOrEmpty(msg))

            {

                MessageBox.Show("메세지를 입력해주세요");

                return;

            }

 

           Process []pro  =  Process.GetProcessesByName("HowToPostMessage");

           if(pro.Length > 0)

            {

                byte[] buff = System.Text.Encoding.Default.GetBytes(msg);

 

                COPYDATASTRUCT cds = new COPYDATASTRUCT();

                cds.dwData = IntPtr.Zero;

                cds.cbData = buff.Length+1;

                cds.lpData = msg;

 

                SendMessage(pro[0].MainWindowHandle, WM_COPYDATA, 0, ref cds);

            }

        }

 

 

 

 

프로젝트 압축 파일입니다.

 

 

HowToPostMessage.zip

 

Winform에서 DaumMapAPI를 컨트롤하기 위해

 

그동안 WebBrowser 컨트롤을 이용해 띄워었습니다.

 

잘 동작하는줄만 알았지만

 

문제가 하나 있었습니다.

 

 

 

 WebBrowser 컨트롤이 Focus를 잃었다가 다시 얻으면 Wheel Event가 발생되지 않는 문제였습니다.

 

 

 

 

해결한 내용입니다.

 

우선 WebBrowser컨트롤을 상속받은 Class에서 WndProc(윈도우 프로시져) 메서드를 override합니다.

 

그 후 아래의 코드를 작성합니다.

 

Msg 528은 마우스 Button Click에 관한 내용입니다.

 

 

 

mshtml.HTMLDocumentClass hDoc;

 

        private void DaumMapAPI_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)

        {

            this.hDoc = (mshtml.HTMLDocumentClass)this.Document.DomDocument;

        }

 

 

 

        protected override void WndProc(ref Message m)

        {

            //Mouse Button Click할때 포커스 발생시키기

            if (this.hDoc != null && m.Msg==528&&this.hDoc.hasFocus()==false)

                this.hDoc.focus();

            base.WndProc(ref m);

        }

 

 

 

 

 

 

 

 

 

응용 프로그램 개발 후 테스트를 하다보면 가끔 특정 PC에서 파일에 접근할 수 없는 권한 문제가 발생하곤 합니다.

 

얼마 전 Window8에서 팀장님이 테스트를 해보셨는데 권한 문제가 발생하였습니다…T T…

ClickOnce를 이용해 아주 간단하게 항상 관리자 권한을 얻을 수 있도록 설정 할 수 있습니다.

ClickOnce를 설정하면 자동으로 app.manifest라는 xml파일이 생성 되는데, 특정 부분만 살짝 변경해주면 됩니다.

 

VisualStudio2010 기준으로 설명하겠습니다.

 

 

프로젝트 속성->보안->ClickOnce 보안 설정 사용 체크해주세요.

                              [이미지 1] ClickOnce 보안 설정 사용 체크

 

 

app.manifest 파일이 생성되었는지 확인해주세요.

                               [이미지 2] 생성된 app.manifest 파일 확인

 

 

ClickOnce 보안 설정 사용을 다시 체크 해제해주세요.

                                              [이미지 3] ClickOnce 보안 설정 사용 해제

 

requestedExcutionLevel의 level 속성을 asInvoker에서 requireAdministrator로 변경해주세요.

 <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

  

 

                                         [이미지 3] app.manifest 파일 수정

C#에서는 아래 이미지와 같은 전처리기들을 지원합니다.  C# 전처리기 지시문 MSDN 바로가기

 

 

[이미지 1] C#의 전처리기 지시문

 

그 중에 #warning과 #error에 대해서 진행하도록 하겠습니다.

#warning과 #error는 말그대로 warning을 발생시키거나 error를 발생시킵니다.

아래의 소스는 #warning과 #error의 MSDN 예제입니다.

 

// preprocessor_warning.cs // CS1030 expected #define DEBUG class MainClass { static void Main() { #if DEBUG #warning DEBUG is defined #endif } }


[소스 1] #warning 예제

 

 

// preprocessor_error.cs // CS1029 expected #define DEBUG class MainClass { static void Main() { #if DEBUG #error DEBUG is defined #endif } }

[소스 2] #error 예제

 

 

저는 #warning을 자주씁니다. 회사에서 업무를 마치고 업무일지를 적으면서 내일 할 작업을 정리하지만

소스의 세부적인 내용까지는 적을 수 없습니다. 다음 작업 진행할 Entry부분에 아래와 같은 형식으로 적어둡니다.

 

 

 

#warning 여기서부터 어떠한어떠한 작업을 진행해야함 

 


 

 

 

 

 

업무 일지를 통해 작업해야할 부분을 확인하고 VisualStudio 2010를 켰는데 아래 이미지와 같이 되어있네요..^^;

제가 작성했지만 월요일 아침에 피식 웃을 수 있게 해주더라구요...^^;;;;

 

 

 

 

[이미지 2] #warning을 이용한 기분 좋은 하루의 예제..

 

 

 

[이미지 3] #warning을 이용한 기분좋은 하루의 예제

 

하하 ...즐거운 하루보내세요..^^;

Excel 매크로를 C#4.0을 이용해서 쉽게 변환하는 과정의 동영상입니다.

 

 

 

 

 

출처 : MSDN - How Do I: Convert Visual Basic for Applications Macro to C# 4.0

익명 메서드를 통한 깔끔한 코딩~

 

 

Winform

        /// <summary>
        /// 관망 해석 중 Percent가 변경되면 발생합니다.
        /// </summary>
        /// <param name="value"></param>
        void global_PecentageChangedEvent(int value)
        {
            this.Invoke(new MethodInvoker(
                delegate()
                {
                    this.ts_proBar.Value = value;
                }
            )
            );
        }

        /// <summary>
        /// 관망 해석 시작 전 Percent 최소 최대값을 Callback 받습니다.
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        void global_InitProgressBarCallback(int min, int max)
        {
            this.Invoke(new MethodInvoker(
                delegate()
                {
                    this.ts_proBar.Minimum = min;
                    this.ts_proBar.Maximum = max;
                }
            )
            );
        }


 

 

 WPF

        private void btnTest1_Click(object sender, RoutedEventArgs e)
        {
            Thread thread = new Thread(UpdateButton);
            thread.Start();
        }
        public void UpdateButton()
        {
            if (this.btnTest2.Dispatcher.CheckAccess())
            {
                this.btnTest2.Content = "hi";
            }
            else
            {
                this.btnTest2.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                    (Action)delegate() { UpdateButton(); });
            }
        }


 

현재 회사 진행 중인 프로젝트 중에 Rectangle 영역의 색상을

반전 시키는 기능이 있습니다.

 

ControlPaint Class의 static 메서드인

FillReversibleRectangle을 사용하여 반전 효과를 얻었지만.........

 

아래와 같은 문제점이 있었습니다.

 

 

 

 

 

 

Form에 Draw되는게 아니라 화면에 Draw되는 문제점......T_T

Activated와 Deactive Event를 통해 해결하려 했지만

Focus가 없는 최상위 Form이 생기면.......GG........

 

그러는 도중 구글링 중에

WinAPI에 InvertRect 함수 발견....

 

시그니쳐는 다음과 같습니다.

BOOL WINAPI InvertRect( __in HDC hDC, __in CONST RECT *lprc);

 

 

C#에서 사용하기 위해

Rect 구조체를 마이그레이션 하고

StructLayout 특성의 매개변수 LayoutKind.Sequential는 메모리를 순서대로 잡아주죠

Left->Top->Right->Bottom 이렇게..

 

아래의 LayoutKind.Explicit를 인자로 넘겨주게되면

멤버의 메모리 위치를 명시적으로 표시해주어야합니다.

        /// <summary>
        /// Rectangle
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct Rect
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }
        /// <summary>
        /// Rectangle
        /// </summary>
        [StructLayout(LayoutKind.Explicit)]
        public struct Rect
        {
            [FieldOffset(0)]
            public int Left;
            [FieldOffset(4)]
            public int Top;
            [FieldOffset(8)]
            public int Right;
            [FieldOffset(12)]
            public int Bottom;
        }



 

 

API 함수를 호출 하기 위해 메서드를 정의하였습니다.

        /// <summary>
        /// 해당 Rectangle영역의 색상을 반전
        /// </summary>
        /// <param name="hdc"></param>
        /// <param name="lpRect"></param>
        /// <returns></returns>
        [DllImport("user32")]
        public static extern bool InvertRect(IntPtr hdc, ref Rect lpRect);


 

 

아래와 같이 코드를 작성하여서

프로그램에 적용 완료하였습니다. 

        /// <summary>
        /// Highlight를 생성할때 발생합니다
        /// </summary>
        /// <param name="rect"></param>
        void DrawManager_HighlightEvent(Rectangle rect)
        {
            try
            {
                if (rect == Rectangle.Empty)
                    return;
                using (Graphics g = this.panel_drawPaper.CreateGraphics())
                {
                    Websolus.NativeFunction.NativeGDI.Rect r = new Websolus.NativeFunction.NativeGDI.Rect();
                    r.Left = rect.Left;
                    r.Right = rect.Right;
                    r.Bottom = rect.Bottom;
                    r.Top = rect.Top;
                    Websolus.NativeFunction.NativeGDI.InvertRect(g.GetHdc(), ref r);
                }
            }
            catch (Exception ex)
            {
                MessageManager.ShowDetailErrorMessge(ex);
            }
        }


 

 

 

 

EPANET2 프로그램에 프로그램 설정을 저장해두는 기능이 있다.

EPANET2 프로그램이 설치된 경로에 가보니 *.ini 파일과 같이 사용자의 설정을

담아두는것으로 보이는 파일은 보이지 않았다.

 

결국 찾은 곳이 아래의 경로였다.

C:\Users\사용자\AppData\Roaming

 

Roaming 폴더를 처음 봤다..ㅠㅠ

정확한 의미는 모르겠지만

내 생각엔 Window사용자별 log, db, config 등등을

제공해주려고 만든 것 같다.

(프로그램의 사용자 옵션 설정을

설치된 경로 또는 절대 경로로 설정해두면

다른 사용자도 그 설정에 따르므로)

 

msdn을 뒤적거리다가

격리된 저장소에 대해 검색하게 되었다.

예전에 window phone7 잠깐 했을때 격리된 저장소 읽었었는데..

흠...

 

 

예제코드는 다음 기회에 다시 포스팅해야겠다.

http://msdn.microsoft.com/ko-kr/library/3ak841sy(v=vs.100).aspx

 

[이미지 1] 속성 변경

 

ListBox 개체의 DrawMode를 OwnerDrawFixed로 변경하고

ItemHeight를 변경합니다.

 

 

 

[이미지 2] DrawItem 이벤트 등록

 

사용자가 그리기로 속성을 변경하였으므로

Item을 직접 그려줘야합니다.

DrawItem 이벤트를 등록합니다.

 

이벤트 등록 하고

아래와 같이 구현하시면 완성된 ListBox를 확인하실수 있습니다.

 

 

        private void lb_propertyList_DrawItem(object sender, DrawItemEventArgs e)
        {
            e.DrawBackground();
            e.Graphics.DrawString(this.lb_propertyList.Items[e.Index].ToString(),
                e.Font, Brushes.Black, e.Bounds, StringFormat.GenericDefault);
            e.DrawFocusRectangle();
        }


 

 

 

[이미지 3 ] 결과 화면

 

 

 

 

아래의 사항에 대해 더 적어보겠습니다.

 

1. Item 문자의 위치를 중간으로 위치

2. 아이템이 선택 되었을 때 폰트는 흰색

 

 

 

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

namespace Intelligent
{
    public partial class FormMapOptions : Form
    {
        /// <summary>
        /// ListBox 개체의 SelectedIndex 이전 값입니다.
        /// </summary>
        int prevLBSelectedIndex = -1;

        public FormMapOptions()
        {
            InitializeComponent();
            this.InitListBox();
        }

        private void InitListBox()
        {
            //ItemHeght는 아이템의 개수에 따라 달라집니다.
            this.lb_propertyList.ItemHeight = (this.lb_propertyList.Height / this.lb_propertyList.Items.Count);
            this.lb_propertyList.SelectedIndex = 0;
        }

        private void lb_propertyList_DrawItem(object sender, DrawItemEventArgs e)
        {
            e.DrawBackground();

            Brush brush;
            if (this.lb_propertyList.SelectedIndex == e.Index)
                brush = Brushes.White;
            else
                brush = Brushes.Black;

            //Font의 Height를 더한 만큼 좌표를 변경합니다.
            int x = e.Bounds.X + e.Font.Height;
            int y = e.Bounds.Y + e.Font.Height;

            e.Graphics.DrawString(this.lb_propertyList.Items[e.Index].ToString(),
                e.Font, brush, x, y, StringFormat.GenericDefault);
            e.DrawFocusRectangle();
        }
        
        private void lb_propertyList_SelectedIndexChanged(object sender, EventArgs e)
        {
            //맨처음을 제외하고 Item 선택이 변경되면
            //이전의 선택 되어있던 아이템의 문자 색상을 White->Black으로 변경 하기 위해
            //Invalidate 메서드를 호출해 아이템을 다시그립니다.
            if (this.prevLBSelectedIndex != -1)
                this.lb_propertyList.Invalidate(this.lb_propertyList.GetItemRectangle(this.prevLBSelectedIndex));

            this.prevLBSelectedIndex = this.lb_propertyList.SelectedIndex;
        }
    }
}


 

 

 

 

 

 

[이미지 4] 결과화면

'.Net > Winform' 카테고리의 다른 글

C# GDI+ Rectangle 영역 색상 반전  (0) 2012.12.28
C# 사용자별 개별 저장소 제공하기  (0) 2012.12.21
C# TabControl Tab Background 변경  (0) 2012.12.11
C# 디스플레이 정보 가져오기  (0) 2012.12.06
C# Color->Brush  (0) 2012.11.26

+ Recent posts