2018년 1월 17일 수요일

C# 코드상에서 .tlb 생성 및 레지스트리 등록하는 법





C#에서 만든 .DLL 라이브러리를 MFC(C++)에서 사용할려면 .tlb 파일을 생성 및 윈도우즈 레지스트리에 등록하는 과정이 필요하다.
이를 위해서는 MS 제공 RegAsm.exe라는 프로그램을 이용해서 처리를 하게 될텐데(자세한 정보는 여기를 클릭) 이를 위해서 Developer Command Prompt for VS 2017라는 일종의 커맨더 창에서 작업을 하게된다.
이는 개발자가 개발 단계에서는 문제가 되지 않으나 개발된 프로그램을 일반 사용자에게 배포하게 될때는 일반 사용자가 이 작업을 할수는 없는 것이다.
따라서 프로그램상에서 .DLL로부터 .tlb 생성 및 레지스트리 등록을 처리해야 할 것이다.

본 포스트는 C# 코드상에서 이 두 가지 작업을 수행하는 방법에 대해 정리하고자 한다.
C#에서 외부 실행 파일을 실행하는 것 자체는 그렇게 어려운 작업은 아니나 문제는 레지스트리에 뭔가를 쓰게 되는 일은 시스템의 보안상 간단하게 처리되어질수는 없는 일이기에 코드상에서 처리하는 일에 복잡성이 발생하게 된다.
이러한 상황 가운데서 위 2가지 작업을 프로그램적으로 어떻게 처리할수 있는지를 살피고자 한다.

핵심은 프로그램 자체가 '관리자 권한'으로 실행되는 프로그램이어야 한다는 것이다. 이렇게 되어야 코드를 통해서 레지스트리 등록이 가능해 진다.

(1) 관리자 권한으로 실행되는 프로그램 만들기
Visual Studio 2017을 기준으로 설명하면 '솔루션 탐색기' 탭에서 해당 프로젝트 이름위에 마우스 우 클릭 ⇒ 속성 ⇒ 보안 ⇒ "ClickOnce 보안 설정 사용"을 체크 ⇒ 이 상태를 저장(Ctrl-S)



이상의 작업을 거치면 '솔루션 탐색기' 탭의 프로젝트 명 아래의 Properties 항목 아랫쪽에 app.manifest라는 파일이 생성이 된다.
이 파일의 설정 값을 조정함을 통해서 현재의 프로그램이 관리자 권한으로 실행되는 프로그램으로 설정된다.


아래 그림의 노랑색 밑줄친 부분의 level의 값 asInvoker를 requireAdministrator로 변경을 해 준 후 저장을 한다.



Visual Studio 2017을 종료 후 '관리자 권한'으로 새로 시작한다.
(그렇지 않고 현재 상태에서 프로그램을 디버깅(실행)하면 "다른 자격 증명을 사용하여 다시 시작"할 것인지 묻는 창이 뜬다. 이 창이 의미하는 것은 Visual Studio 2017을 관리자 권한으로 다시 실행하겠느냐는 뜻이다.)

프로그램을 디버깅(실행)하면 현재 프로그램의 .exe 실행 파일이 생성이 되고 이 실행 파일은 매 실행시마다 관리자 권한으로 실행 여부를 사용자에게 묻게 된다.

(2) .tlb 파일 생성 및 윈도우즈 레지스트리 등록하는 소스 코드

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RegisterTLB
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("This program is registering .TLB to Windows registry.");

            var proc = new ProcessStartInfo();

            // .dll을 이용해서 .tlb생성 및 레지스트리 등록 명령어
            // regasm의 사용법은 DOS창(커맨더 창)에서 regasm /?을 하면 
            // regasm을 아래와같이 이 파일의 경로를 표시하지 않으면 현재의 프로그램의 
            //실행 파일과 같은 위치에
            // regasm.exe가 있어야 된다.
            string mCmd = "regasm EthernetClientLib.dll /tlb:EthernetClientLib.tlb";
            proc.UseShellExecute = true;

            //아래 경로에 .DLL가 있어야하고 .tlb가 이 위치에 생성이 되고 regasm.exe도 
            //이 위치에서 작업을 하게된다.
            proc.WorkingDirectory = @"D:\Joe\CSharp\EthernetClientLib\EthernetClientLib\bin\Debug";

            //위의 regasm EthernetClientLib.dll /tlb:EthernetClientLib.tlb를 실행시킬 명령어
            proc.FileName = "cmd.exe";

            //아래 속성의 runas 자체가 관리자 권한으로 실행시키겠다는 설정값이다.
            proc.Verb = "runas";
            proc.Arguments = "/C " + mCmd;
            proc.WindowStyle = ProcessWindowStyle.Hidden;
            Process rt = Process.Start(proc);

            Debug.WriteLine("##### .tlb 생성 및 레지스트리 등록을 마쳤습니다.");
        }
    }
}








2018년 1월 10일 수요일

C# .dll library에서 MessageBox 사용하기(메시지 창 띄우기)





C# .dll library에서 MessageBox 사용하기(메시지 창 띄우기)

C#의 프로젝트를 클래스 라이브러리(.dll 라이브러리)로 생성하면 기본적으로 MessageBox를 띄울수 없다.
그런데 C# .dll 라이브러리 단에서 굳이 메시지 창을 띄워야 할 경우가 있을 것이다. 그게 여러모로 편리할 것이다.
이런 경우에 대한 해법이다.

프로젝트 명에 우측 마우스 - 추가 - 참조 - 어셈블리 - 프레임워크 - System.Windows.Forms를 체크해 주면
아래 클래스가 자동으로 using에 포함된다.
만일 자동으로 추가되지 않으면 아래 내용을 직접 타이핑해서 추가해 주면 된다.

using System.Windows.Forms;




이후에 필요한 곳에서 아래와 같이 사용하면 되겠다.

MessageBox.Show("해당하는 이미지가 없습니다.", "적당한 제목");

(C# 프로그램을 .dll Library로 만드는 방법은 여기를 클릭)


2018년 1월 5일 금요일

python의 soocket 통신시 send()와 sendall()의 차이에 대해서





python의 soocket 통신시 send()와 sendall()의 차이에 대해서

Linux 시스템이 구동되는 임베디드 장비의 python과 Windows PC의 C#간 Socket통신을 하면서 임베디드 장비에서 binary 파일(이미지 파일)을 읽어서 Ethernet을 통해 PC로 전송하는 프로그램을 개발하다 보니 묘하게도 전송된 파일의 사이즈가 원본과 약간의 차이가 나는 것을 발견하게 되었다.
몇 byte에서 몇 십 byte까지...

로그를 출력해보면 데이터를 전송하는 쪽(python)에서 뭔가 모르게 데이터를 다 전송하지 못하는것 같았다. 이게 말이 되는지 모르지만 아무튼 전송된 파일의 파일 사이즈가 약간 적게 전송되는 기현상이 계속되었다.

결국은 python socket의 send()와 sendall()이 차이가 있음을 보게된다. 황당함~
Stackoverflow에 다음과 같은 설명이 있다.

https://stackoverflow.com/questions/34252273/what-is-the-difference-between-socket-send-and-socket-sendall

socket.send is a low-level method and basically just the C/syscall method send(3) / send(2). It can send less bytes than you requested, but returns the number of bytes sent.

socket.sendall is a high-level Python-only method that sends the entire buffer you pass or throws an exception. It does that by calling socket.send until everything has been sent or an error occurs.

If you're using TCP with blocking sockets and don't want to be bothered by internals (this is the case for most simple network applications), use sendall.

send()
 -. low-level에서 작동되는 시스템콜 형태
 -. 요청한 데이터보다 더 적게 전송할수도 있다.
 -. 그러나 요청한 만큼 전송한 걸로 return해 준다.

sendall()
 -. high-level단의 메소드로 python 메소드이다
 -. 요청한 데이터의 모든 버퍼 내용을 모두 전송한다. 그렇게 되지않을시 Exception발생
 -. sendall()도 내부적으로는 send()를 이용하는데 단지 모두 전송할 때까지 send()를 호출한다.

황당하지만 이렇다고 한다.
테스트해보면 5Mb정도의 이미지 파일을 전송해 볼때 sendall()이 send()보다는 확실히 속도가 좀 떨어지는 것 같다.