2017년 12월 27일 수요일

Ubuntu 장비의 고정 IP(static IP) 설정법





임베디드 장비이든 혹은 웹 서버이든 IP를 고정해야 할 경우들이 있다.
DHCP로 IP를 자동 할당 받는 경우 IP가 고정이 아닌 유동적으로 바뀔수가 있기 때문에 고정 IP(static IP) 설정하는 법은 여러 모로 필요한 작업이다.
본 포스트에서는 Linux Ubuntu 상에서 IP를 어떻게 고정으로 할당할수 있을 것인지를 정리하고자 한다.
IP를 고정시키기 위해서는 다음의 정보가 필요하다.

-. 고정시킬 IP Address
-. Net mask
-. Default gateway
-. DNS Server

여기서 외부로 나갈 필요가 없는 상황이라면 즉 인터넷을 할 필요가 없이 임베디드 장비와 PC간의 통신만 되면 된다거나 아무튼 외부 통신을 필요로 하지 않는다면 gateway 정보나 DNS server 정보는 없어도 된다.

우선 현재의 네트워크 자원들에 대해 확인부터 해 보자.

ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:ff:a4:58 brd ff:ff:ff:ff:ff:ff

ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:ff:a4:58 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
       valid_lft 83702sec preferred_lft 83702sec
    inet6 fe80::c33f:ad86:8225:3597/64 scope link
       valid_lft forever preferred_lft forever

위의 정보를 보면 2개의 네트워크 interface가 보인다. 하나는 lo인데 이는 loop back으로 자기 자신을 나타내는 interface이다.
우리가 고정 IP를 설정할 네트워크는 enp0s3로 명명되어진 이 interface이다.
link/ether는 이 장비가 이더넷 장비라는 뜻이고 IP 주소는 10.0.2.15이다.
참고로 위의 정보는 Virtual Box에서 실행되는 Ubuntu이다.
보통 공유기 하위에 물려 있는 경우라면 192.168.x.x와 같은 형태가 될 것이다.

이제 IP정보를 설정해 보자. 아래 파일을 열어 보면 대체로 이런 정보가 들어 있다.

vi /etc/network/interfaces

# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback

위의 파일 내용에 고정 IP 할당에 대한 정보를 아래와같이 입력하고 파일을 저장한다.

auto enp0s3
iface enp0s3 inet static
address 192.168.0.77
netmastk 255.255.255.0
gateway 10.0.2.2
dns-nameserver 8.8.8.8 8.8.4.4

gateway는 현재의 gateway를 그대로 사용했고 dns-nameserver는 구글 것을 사용했다.
현재의 gateway 정보를 알려면 다음 명령으로 확인할 수 있다.

root@joe-VirtualBox:/etc/network# ip route
default via 10.0.2.2 dev enp0s3  proto static  metric 100
10.0.2.0/24 dev enp0s3  proto kernel  scope link  src 10.0.2.15  metric 100
169.254.0.0/16 dev enp0s3  scope link  metric 1000

현재의 gateway(인터넷을 할수 있는 출입문, 통신이 외부로 나갈수 있는 출입문 IP)정보는
default via 10.0.2.2 dev enp0s3로 되어 있는 10.0.2.2이다.
아래는 또 다른 경우에 대한 정보이고 192.168.0.1이 현재의 gateway이다.

root@localhost:/etc/network# ip route
default via 192.168.0.1 dev eth0  metric 100
169.254.0.0/16 dev eth0  scope link  metric 1000
192.168.0.0/24 dev eth0  proto kernel  scope link  src 192.168.0.88

위의 정보만으로 네임서버가 안된다면 다음 정보를 추가하도록 하자.
네임서버란 www.google.com, www.naver.com과 같이 숫자로 된 IP가 아닌 사람이 쉽게 파악할수 있는 형태의 IP 지정 방식이다.
아래의 경로에 tail이라는 파일을 생성해서(만일 없다면 새로 생성한다) nameserver 8.8.8.8을 입력하고 파일을 저장한다.

vi /etc/resolvconf/resolv.conf.d/tail
nameserver 8.8.8.8

이상으로 설정을 완료되었고 네트워크 장비를 재시작 한다.

#  ip addr flush enp0s3 && systemctl restart networking.service

만일 구버전 리눅스라면 위의 명령이 실행되지 않을수 있다. 그럴 경우는 아래와 같이 하자.

ip addr flush enp0s3 && /etc/init.d/networking restart


여기까지가 Ubuntu 시스템 상에서의 고정 IP 설정하는 법이다.
정상적으로 설정이 되었다면 터미널 창에서 ping이 정상적으로 송수신될 것이다.

# ping www.google.com

위 명령이 정상적으로 수행된다면 IP 설정과 nameserver 설정이 제대로 된 것이다.

그런데 ping 172.217.24.4과 같이 IP로는 실행되나 도메인 네임으로는 수행되지 않는다면 이건 nameserver 설정이 잘못되었다는 뜻이다.

2017년 12월 18일 월요일

C#의 델리게이트(delegate)에 대한 기본적인 개념 및 간단한 예제





delegate는 C#이 가진 독특한 개념인데 따라서 기본적으로 생소한 개념으로 다가온다.
아래는 delegate에 대한 기본적인 개념 소개와 간단한 예제를 통해 개념을 이해해 보고자 한다.

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

namespace EXdelegate1
{
    class Program
    {
        //delegate란 
        // (1) 일종의 데이터 type과 같다. 
        // (2) 어떤 메소드를 encapsulate할수 있다. 즉 어떤 메소드를 가리킬수 있고
        //      이를 통해 그 메소드를 실행할수 있다.
        // (3) similar to function pointer in C, C++
        // (C, C++에서 어떤 함수를 포인터로 가리키고 이 포인터를 이용해서 
        // 해당 함수를 실행하는 방식)
        //아래는 새로운 데이터 타입과 같은 delegate를 선언하는데 이 새로운 데이터 타입의
        //이름은 Del이고
        //이 데이터 타입이 가리킬수 있는 메소드는 argument로 string을 하나 받고
        //반환 값은 void인
        //메소드가 있다면 그들 메소드들은 모두 delegate 이름이 Del인
        //새로운 데이터 타입으로 객체를 선언하고
        //이 선언된 delegate 타입의 객체로 그들 메소드들을 실행할수 있다.
        private delegate void Del(string message);

        //아래는 새로운 데이터 타입인 MyDel이라는 이름의 데이터 타입을 선언하는데
        //이 데이터 타입은
        //특별히 delegate형태의 데이터 타입이다.
        //MyDel라는 데이터 타입으로 선언된 어떤 객체(참조변수)는 argument로 string 타입 하나,
        //int 타입 하나를
        //받고 반환 데이터는 string 타입을 반환하는 모든 메소드를 MyDel이라는
        //데이터 타입으로 모두 
        //가리키고 또 실행할수 있다는 개념이다.
        private delegate string MyDel(string info, int some);

        static void Main(string[] args)
        {
            //delegate 객체를 생성. 이 delegate 객체가 실행할 메소드는 DelegateMethod()이다.
            //handler는 delegate Del 타입의 object이다
            //delegate 타입의 객체 생성은 Java나 기존의 객체 생성 방식과 약간 다르다.
            //Del이라는 delegate 타입의 객체 handler가 실행할 메소드 이름을 대입해 주면 
            //delegate Del 타입의 새로운 객체가 하나 생성된다.
            Del handler = DelegateMethod;
            handler("delegate object가 보낸 메시지");

            MyDel another = AnotherDelegateMethod;

            Console.WriteLine("\n\n 이름 : " + another("홍길동", 25));

            Del myDel = mDel;
            myDel("Del이라는 이름의 delegate가 실행하는 두~~ 번째 메소드임");

            //익명함수 방식으로 delegate 객체 생성
            //익명함수란 원래 객체 생성 시점에 함수(메소드)의 이름은 없이 막바로 함수 본체가
            //주어지면서 객체 생성되는 형태이다.
            //따라서 아래에서 MyDel의 조건인 매개인자로 string 하나, int형 하나를 받고
            //반환 데이터 타입이 string이라는 조건만 만족시켜주면 함수 본체는 
            //내용이 어떠하든지 상관이 없다.
            MyDel anonymMyDel = delegate (string addr, int sex)
            {
                string gender = "";

                if (sex == 1)
                {
                    gender = "남성";
                } else if (sex == 0)
                {
                    gender = "여성";
                }
                return "주소 : " + addr + ", 성별 : " + gender;
            };

            Console.WriteLine(anonymMyDel("서울특별시 강남구 아무개길 77", 1));


            //익명함수 방식의 delegate 객체 생성2
            Del anonymDel = delegate (string msg)
            {
                Console.WriteLine("\n\nDel의 익명함수 방식 객체 : "+msg);
            };

            anonymDel("오 이런 식으로 되는구나\n\n\n");
        }


        private static void DelegateMethod(string msg)
        {
            Console.WriteLine("\n여기는 delegate method\nmessage sent from delegate object : \""+msg+"\"");
        }

        private static string AnotherDelegateMethod(string name, int age)
        {
            return name + ", age : " + age;
        }

        private static void mDel(string info)
        {
            Console.WriteLine("\n\n여기는 " + info + "~~~\n\n");
        }

    }
}

2017년 12월 14일 목요일

리눅스 Shell command를 이용한 Socket 통신하기





리눅스 Shell command를 이용한 Socket 통신하기

리눅스의 cat 명령어는 쓰임새가 다양하다. 해보진 않았지만 cat을 이용해서 리눅스 시스템을 백업도 가능한듯 하다.
리눅스에 있는 kkk.txt라는 파일의 내용을 Socket을 이용해서 내보낼수 있는가?
netcat(혹은 줄여서 nc)와의 조합을 통해서 가능하다.
아래와 같이

cat kkk.txt | nc 192.168.0.7 8801

여기서 192.168.0.7은 서버 소켓의 IP 주소이고, 8801은 서버 소켓의 포트번호이다.
혹은 간단한 문자열 정도를 보낼려면 이렇게도 가능하다.

echo "This string is from Linux" | netcat 192.168.0.7 8801

통신상태 확인을 위해 간단히 사용할수 있을 것이다.

2017년 12월 13일 수요일

python에서의 문자열 비교





파이썬(python)에서의 문자열 비교에 대한 간단한 예제 코드이다.

#-*- coding: utf-8 -*-

from operator import eq  # 문자열 비교를 위한 함수 eq 추가

chk = "joe7"

if chk == "joe":
    print "같다."
else:
    print "다르다."

위의 코드는 C style 언어들의 보편적인 비교 법이다. 
그런데 Java에서는 저런식으로 비교가 안된다는 약간이 당황스러움.
말이 나온김에 Java의 문자열 비교를 보자면
if (chk.equals("joe")) { ... }와 같이 해야 한다.

위 코드 실행 결과는 당연히 "다르다"가 출력될 것이다.
이번에는 문자열 비교 함수 eq()를 이용한 방식이다.

아래 코드에서 eq(chk, "joe")는 괄호 안의 2개의 매개인자가 같으면 true, 아니면 false가 된다.
결과는 당연히 "다름"이 출력된다.

if eq(chk, "joe"): 
    print "같음"
else:
    print "다름"

다음으로 문자열 비교에서 매우 유용하게 사용되는 특정 문자를 "포함하고 있는지"에 대한 파이썬에서의 용법이다.
find()라는 메소드를 활용하면 된다. find()메소의 syntax는 다음과 같다.

str.find(s, beg=0, end=len(str))

str이라는 문자열에서 문자열 s가 발견 되어지면 발견되어진 위치 값(index값)을 반환한다.
만일 발견되지 않으면 -1을 반환한다. 이때 문자열의 index는 0부터 시작한다.

beg : 문자열 str에서 s라는 문자열을 찾을 때 시작 Index 값이다. 지정하지 않으면 default로 0가 지정된다.

end : 문자열 str에서 s라는 문자열을 찾을 때 종료될 index 값이다. 지정하지 않으면 문자열 str의 length 값이 지정된다.

예를들어

str = "<EOF>"가 있다면
index 0 : <
index 1 : E
index 2 : O
index 3 : F
index 4 : >
가 된다.

print str.find("EOF")를 하면 1이 반환된다.
print str.find("<EOF>")를 하면 0이 반환된다.
print str.find("efgh")를 하면 -1이 반환된다.
print str.find("OF")를 하면 2가 반환된다.
print str.find("F")를 하면 3이 반환된다.
print str.find("OF>")를 하면 2가 반환된다. OF>라는 전체 문자열이 str에서 발견되기 때문이다.
print str.find("OF)")를 하면 -1이 반환된다. OF까지는 문자가 포함되어 있지만 OF)라는 문자는 포함되어 있지 않기 때문이다.

info = "Hello world<EOF>"
print info.find(str)를 하면 11이 반환된다.

print info.find(str, 5)를 하면 str을 찾는 위치를 5위치에서 찾지만 반환은 전체 위치에서의 index값을 반환하므로 11이 반환된다.

print info.find(str, 5, 7)를 하면 str을 info에서 찾을 때 시작 위치를 5에서 시작해서 7위치까지만 찾으므로 str이 발견되지 않는다. 따라서 -1이 반환된다.

아무튼 python에서 java의 contains()와 같은 기능을 find()를 이용해서 할수 있다는 얘기다.
java의 contains()와는 약간 개념이 다르지만 -1이 아니면 해당 문자열을 포함하고 있는 것이 되는 것이다. 예를 들어 data라는 변수에 소켓통신으로부터 데이터를 읽었을 경우 data "EOF"라는 문자열이 포함되어 있다면 소켓통신의 읽기 작업을 종료하는 코드를 작성할려면 다음과 같이 하면된다.

if data.find("EOF") != -1 :
    print "OK, Job completed"
    break;

과 같이하면 될 것이다.

2017년 12월 7일 목요일

C#의 .dll Library를 C++에서 활용하기





C#의 .dll Library를 C++에서 활용하기
C#에서 생성한 .dll파일과 .tlb 파일을 C++의 해당 프로젝트 폴더안과 .exe 실행파일이 있는 곳에 복사해 넣는다.
이제 남은 것은 C++ 소스코드 작성법만 알면 C#에서 만든 .dll Library를 C++에서 사용할수 있게 된다.
샘플 예제 코드는 다음과 같다. 각 부분에 대한 설명은 아래 코드상에 있다.
아래 코드는 MFC에서 정보 확인 버튼 클릭시 C# .dll Library의 특정 메소드(함수)를 실행하고 C# .dll이 던져주는 정보(결과)를 보여주는 기능을 구현한 코드 조각이다.

이때 다음과 같이 .tlb 파일을 #include 아랫쪽에 import해 주어야 정상적으로 C# .dll 라이브러리에 있는 메소드들을 MFC에서 사용할수 있다.

#import "ExMakeClassLibSerialRead.tlb" no_namespace named_guids


//MFC에서 C#의 라이브러리 .dll을 이용한다.
void CMFC_UseCsharpDllDlg::OnBnClickedButton1()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.

//ICallClass는 C#의 interface이름이다.
//public interface ICallClass { .... }과 같이 C#에서 선언되어 있는 부분이다.
ICallClass *csharp = NULL;

//CoInitialize()
//==> Initializes the COM library on the current thread and identifies 
     //        the concurrency model 
//==> as single-thread apartment (STA).
//COM이란, 컴포넌트 오브젝트 모델(Component Object Model, COM)은 
//마이크로소프트가 개발한 소프트웨어 구성 요소들의 응용 프로그램 이진 인터페이스이다. 
//COM을 이용해 개발된 프로그램들은 프로세스간 통신과 동적 오브젝트 생성이 가능하다. 
CoInitialize(NULL);

//typedef long HRESULT
//CoCreateInstance(CLSID_C#의클래스이름, NULL, CLSCTX_INPROC_SERVER, 
//                                IID_C#의interface이름, 
     //                                reinterpret_cast<void**>
     //                                (&C#의interface이름type으로선언된참조변수명));
//아래의 CLSID_Class1에서 Class1은 C#에서 사용자가 필요로하는 기능을 담고 있는 
     //클래스인데 C#에서 다음과 같이 정의되어 있다.
//public class Class1 : ICallClass { ... } 자세한 것은 C#의 ExMakeClassLibSerialRead
     //라는 이름의 프로젝트를 참조할 것
//아래에서 IID_ICallClass에서 ICallClass는 C#에서 public interface ICallClass { ... }
     //와 같이 선언된 부분의 interface 이름이다.
HRESULT hr = CoCreateInstance(CLSID_Class1, NULL, CLSCTX_INPROC_SERVER, IID_ICallClass, reinterpret_cast<void**>(&csharp));
//SUCCEEDED macro
//Provides a generic test for success on any status value.
//BOOL SUCCEEDED(HRESULT hr);
//==> hr : The status code.This value can be an HRESULT or an SCODE.
     //               A non - negative number indicates success.
//==> Return value : TRUE if hr represents a success status value; 
     //                  otherwise, FALSE.
if (SUCCEEDED(hr))
{
//showInfo() 함수는 C#에서 public void showInfo() { ... }과 같이 
          //정의되어 있는 C#용 메소드이다.
csharp->showInfo();

CString msg = csharp->getInfo();
MessageBox(msg);

} else {
MessageBox(_T("실패~\n\nC++의 exe 실행파일이 있는 위치에 \nC#에서 생성한 .dll과 .tlb가 있어야 합니다."));
}
}

C# 프로그램을 .dll Library로 만들기(C++에서 사용하기 위해)





C#에서 .dll로 library를 만들고 이것을 C++에서 사용하기
C#은 MFC에 비해 여러면에서 개발자 편의적이다. 따라서 C#으로 만든 프로그램을 .dll 라이브러리로 만들고 이것을 C++ 혹은 MFC에서 사용하는 방법에 대해서 다루고자 한다.

전개순서는

-. C#을 클래스 라이브러리로 개발하기
-. C#을 .dll library로 만들기 위한 Visual Studio 2017에서 속성 설정하기
-. C# 프로그램이 .dll library로 MFC(혹은 C++)와 동작하기 위한 소스 코드 작성법
-. C# 소스코드 빌드하기
-. 빌드된 C#의 .dll을 레지스트리 등록 및 .tlb 파일 만들기
-. C++ 쪽에서 C#용 .dll 파일 사용하기 위한 소스 코드 작성법

의 순서로 진행이 된다.

1) C#을 클래스 라이브러리로 개발하기

Visual Studio 2017 파일 - 새로 만들기 - 프로젝트 - 클래스 라이브러리(.NET Framework)


이렇게해서 소스코드 창이 열리면 

2) C#을 .dll library로 만들기 위한 Visual Studio 2017에서 속성 설정하기
다음과 같이 해당 프로젝트의 속성을 C# 클래스 라이브러리(.dll) 용으로 설정해 준다.





3) C# 프로그램이 .dll library로 MFC(혹은 C++)와 동작하기 위한 소스 코드 작성법
소스 코드는 크게 두 부분으로 구성이 된다. C++과 통신하기 위한 interface 하나와 필요한 사용자 기능을 위한 class 하나이다. 이 둘에 대한 Guid 값을 지정해 주어야 하는데 Guid 값을 얻기 위해서는 아래 그림과 같이 하면된다.



'복사' 버튼을 클릭하여 Guid 값을 소스코드에(interface와 사용자 클래스에) 붙여넣으면 된다.
소스 코드는 아래와 같다. Guid가 붙여진 곳을 확인해보자.
여기서 또 기억할 사항은 Guid값을 소스코드에서 사용할수 있기 위해서는 using System.Runtime.InteropServices를 추가해 주어야 한다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace ExMakeClassLib
{
    //여기가 C++과 연동하기 위한 interface부분이다.
    [Guid("357CCEDB-44A1-481E-A42A-0D4DCA8C5EEA")]
    public interface ICallClass
    {
        //여기에 C++에서 사용할 메소드들의 프로토타입을 지정해 주면 된다.
        //C++에서 사용하게 될 public 형태의 메소드들이 여기에 해당된다.
        //private 모드의 메소드는 여기에 지정이 불가능하다. 
        //생성자는 여기서 지정해 줄 필요가 없다.
        //메소드들만 지정해 주면 된다.
        void setInfo(String _name, int age, String _phoneNum);
        void showInfo();
    }


    //여기가 사용자가 원하는 기능을 구현한 클래스이다.
    [Guid("D00C0769-26E7-4B4E-A7D0-8CAAE2AB3702")]
    public class Class2 : ICallClass
    {
        private String name;
        private int age;
        private String phoneNum;
        //여기서 주의 해야할 사항은 SerialPort나 Thread 같은경우 처음 사용 후에 
        //포트를 닫는 동작이 필요하거나 Thread를 종료시키는 동작이 필요할 경우 등
        //여러 메소드들에서 사용을 해야하는 멤버 변수들은 반드시 static으로
        //선언해 주어야 한다. 왜냐하면 비록 전역 변수 형태로 선언되었다 할지라도
        //처음 사용했던 메소드가 아닌 다른 메소드에서 또 사용되어질 경우는
        //해당 멤버 변수(Thread, SerialPort...)가 null 상태가 되어 버린다.
        //따라서 C++에서 계속해서 해당 멤버 변수를사용할수 있도록 하기 위해서는
        //반드시 static으로 선언해 주어야 한다.

        public Class2() { }

        public Class2(String _name, int _age, String _phoneNum)
        {
            name = _name;
            age = _age;
            phoneNum = _phoneNum;
        }

        public void showInfo()
        {
            Console.WriteLine("▶ name : " + name + "\n▶ age : " + age + "\n▶ 폰번호 : " + phoneNum);
            Console.WriteLine("---- This is C# Library for C++ from Joe");
        }

        public void setInfo(String _name, int _age, String _phoneNum)
        {
            name = _name;
            age = _age;
            phoneNum = _phoneNum;
        }
    }
}


4) C# 소스코드 빌드하기
Visual Studio 2017의 메뉴에서 '빌드' - '해당프로젝트명 빌드'를 클릭하여 소스 코드를 빌드한다.
(아래 이미지의 경우는 프로젝트 명이 ExMakeClassLibSerialRead라고 가정할 경우이다)


C#의 프로젝트명\bin\Debug\해당프로젝트명.dll 이 생성이 되어 있을 것이다.


5) 빌드된 C#의 .dll을 레지스트리 등록 및 .tlb 파일 만들기
.tlb 파일을 만드는 이유는 C#은 .NET 기반이기 때문에 이를 C++에서 사용가능하도록 하기 위해서이다.

"Developer Command Prompt for VS 2017"창을 관리자 권한으로 열고 C#의 해당 .dll이 있는 경로로 이동한다.
"Developer Command Prompt for VS 2017"창을 열어야 regasm을 정상적으로 이용할수 있다.
여는 방법은 윈도우즈의 시작 - Visual Studio 2017 하위 항목 열기 - Developer Command Prompt for VS 2017 메뉴 위에서 마우스 우측 클릭 - 관리자 권한으로 실행한다.
(만일 DOS 창을 관리자 권한으로 열어서 사용할 경우는 regasm.exe를 C#의 .dll이 있는 폴더로 복사해서 아래 명령을 처리하면된다)

C#용 .dll이 있는 디렉토리로 이동해서 다음 명령을 한다.
아래에서 ExMakeClassLib는 프로젝트 명이다.

regasm ExMakeClassLib.dll /tlb:ExMakeClassLib.tlb

그러면 다음과 같은 메시지가 보이면 성공한 것이다.

Microsoft .NET Framework 버전 4.7.2556.0용
Microsoft .NET Framework Assembly Registration Utility 버전 4.7.2556.0
Copyright (C) Microsoft Corporation.  All rights reserved.

형식이 등록되었습니다.
어셈블리를 'D:\Joe\CSharp\ExMakeClassLib\ExMakeClassLib\bin\Debug\ExMakeClassLib.tlb'(으)로 내보내고 형식 라이브러리를 등록했습니다.

.dll과 .tlb 생성은 C# 소스 코드를 수정할 때마다 항상 같이 만들어 주어야 한다.
이렇게 만들어진 .dll과 .tlb를 C++의 프로젝트가 있는 디렉토리와 C++의 실행파일이 있는 위치에 복사해 준다.
이렇게 만들어진 C#용 library를 C++(혹은 MFC)에서 어떻게 사용하는지는 아래 링크에서 확인할수 있다.
(C++에서 C# .dll 라이브러리 사용하는 법)



















2017년 12월 5일 화요일

Visual Studio 2017에서 작업을 완료할수 없습니다. 해당 인터페이스를 지원하지 않습니다





Visual Studio 2017에서 C#으로 프로그래밍 중 "참조" - "참조 추가" 작업시 아래와 같은 에러 발생했을 경우에 대한 해법이다.

"작업을 완료할수 없습니다. 해당 인터페이스를 지원하지 않습니다."
(The operation could not be completed, no such interface supported)


아래 경로로 이동한다.
cd C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\PublicAssemblies

원하는 것은 Microsoft.VisualStudio.Shell.Interop.11.0.dll 파일이 있는 곳으로 이동해야 한다.

다음 명령을 실행한다.

C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\PublicAssemblies>gacutil -i Microsoft.VisualStudio.Shell.Interop.11.0.dll

그러면 다음과 같은 결과가 나타나면 문제가 해결 된 것이다. 

『  Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
    Copyright (c) Microsoft Corporation.  All rights reserved.

   Assembly successfully added to the cache  』

Visual Studio 2017을 다시 실행한다.

그런데 gacutil.exe가 해당 경로에는 없다. 그런데 gacutil.exe가 여러 군데, 여러 버전이 있는 것 같다. 파일 사이즈가 상이한 동일 이름의 gacutil.exe가 여러 군데 있다. 내 경우는 아래 경로에 있는 gacutil.exe로 문제 해결되었다.

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\gacutil.exe

자세한 내용은 아래 사이트 참조
https://developercommunity.visualstudio.com/content/problem/75105/cant-add-reference-error-pops-up-no-such-interface.html

2017년 12월 4일 월요일

MFC에서 문자열 합치기





Java나 C#을 사용하다가 MFC를 사용하면서 느끼는 짜증나는 불편함이 있다면 문자열 처리에 대한 것이다.
Java나 C#에서의 문자열 처리의 개념으로 MFC에서 시도해보면 당황 스러울 정도로 답답함이 느껴진다.
본 포스트는 MFC에서의 문자열을 사용할수 있는 나름 편리한 도구인 CString을 이용한 코드조각을 소개하고자 한다.

같은 CString끼리는 + 연산으로 쉽게 두 문자열을 합칠수 있으므로 여기서는 CString 타입과 UINT 타입을 합쳐서 하나의 문자열로 만드는 경우를 다뤄보고자 한다.

현재 사용가능한 Serial Port를 출력한다고 가정할 경우 COM이라는 문자열과 1,2,3...과 같은 숫자를 하나의 문자열로 합친다면 다음과 같이 처리하면 된다.

#include <iostream>
using namespace std;

CString str = "COM";
CString port;
//UINT k = 3;
//str += k; //불가능

for (UINT i = 0; i < 7; i++)
{
     //CString과 UINT가 막바로 port = str + i와 같이 합쳐지지 않으므로 
     //UINT를 CString형으로 변환하는 작업
port.Format("%d", i);
port = str + port;

cout << "Available Serial Ports : " << port << endl;
printf("Ports : %s\n", port);
}

그런데 여기서 또 답답한 건 이 소스 코드의 문자 집합이 유니코드 집합을 사용할 경우는 컴파일 단계에서 아예 에러를 뿜는다.

CString str = "COM"; 이 코드에서는 『"const char [4]"에서 "ATL::CStringT<wchar_t, StrTraitMFC_DLL<wchar_t, ATL::ChTraitsCRT<wchar_t>>>"(으)로 변환하기 위한 적절한 생성자가 없습니다라』는 에러를 뿜는다.

port.Format("%d", i); 이 코드에서는 
『인수 목록이 일치하는 오버로딩된 함수 ...의 인스턴스가 없습니다.
              인수 형식이(const char [3], UINT)입니다....』와 같은 에러가 발생한다.

이건 현재 Visual Studio에서 사용중인 문자 집합을 "멀티바이트 문자 집합 사용"으로 변경해 주어야 한다.
문자셋 인코딩 문제는 항상 모든 프로그래밍 언어들에서 신경쓰야 할 부분이지만 이건 정상적인 문법 자체가 잘못됐다고 에러를 내 보내니... 답답한 노릇이다.

문자 집합을 변경하는 방법은
해당 프로젝트 이름에 마우스 우측 클릭 ⇒ 팝업 메뉴에서 "속성" 클릭 ⇒ 해당 프로젝트의 속성 페이지 창에서 ⇒ 구성 속성의 하위 항목 중 "일반" ⇒ 우측 항목들 중 "문자 집합"의 드랍 다운 메뉴에서 "멀티바이트 문자 집합 사용"을 선택 ⇒ 확인


위의 코드를 실행하면 다음과 같을 결과를 내 보이게 될 것이다.

Available Serial Ports : COM0
Ports : COM0
Available Serial Ports : COM1
Ports : COM1
Available Serial Ports : COM2
Ports : COM2
Available Serial Ports : COM3
Ports : COM3
Available Serial Ports : COM4
Ports : COM4
Available Serial Ports : COM5
Ports : COM5
Available Serial Ports : COM6
Ports : COM6