할일: ManagedDecoder.cpp에서, decodingRTSP에서 2종류의 avpacket을 한번만 선언해서 사용하기




pkt을 한번만 선언해서 av_free_packet(.)해도 계속 사용하는 사례

2015년에 작성된 H264 codec test 샘플코드임.


https://ffmpeg.org/doxygen/2.8/api-h264-test_8c_source.html#l00032


106  i = 0;
107  av_init_packet(&pkt);
108  do {
109  if (!end_of_stream)
110  if (av_read_frame(fmt_ctx, &pkt) < 0)
111  end_of_stream = 1;
112  if (end_of_stream) {
113  pkt.data = NULL;
114  pkt.size = 0;
115  }
116  if (pkt.stream_index == video_stream || end_of_stream) {
117  got_frame = 0;
118  if (pkt.pts == AV_NOPTS_VALUE)
119  pkt.pts = pkt.dts = i;
120  result = avcodec_decode_video2(ctx, fr, &got_frame, &pkt);
121  if (result < 0) {
122  av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n");
123  return result;
124  }
125  if (got_frame) {
126  number_of_written_bytes = av_image_copy_to_buffer(byte_buffer, byte_buffer_size,
127  (const uint8_t* const *)fr->data, (const int*) fr->linesize,
128  ctx->pix_fmt, ctx->width, ctx->height, 1);
129  if (number_of_written_bytes < 0) {
130  av_log(NULL, AV_LOG_ERROR, "Can't copy image to buffer\n");
131  return number_of_written_bytes;
132  }
133  printf("%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, 0x%08lx\n", video_stream,
135  number_of_written_bytes, av_adler32_update(0, (const uint8_t*)byte_buffer, number_of_written_bytes));
136  }
137  av_free_packet(&pkt);
138  av_init_packet(&pkt);
139  }
140  i++;
141  } while (!end_of_stream || got_frame);







https://ffmpeg.org/doxygen/2.8/decoding__encoding_8c_source.html#l00347



Posted by 세모아
,

출처: http://bluexmas.tistory.com/273



ffmpeg - rtmp sample 테스트

Programming/C++ 2012.05.25 17:19 Posted by 파란크리스마스

출처 : RTMPDump
Compiling RTMPdump on Max OS X
Compiling FFmpeg 0.9 with libRTMP and libmp3lame for Intel and ARM CPUs
Compiling FFMPEG 0.6 with RTMP Support for OSX 
ffmpeg ndk 빌드 #2 ffmpeg 옵션별 빌드
best-video-player
Fix rtmp double cflags in pkgconfig module

다운로드

RTMPDump 에서 rtmpdump-2.3.tgz 파일 다운로드

압축풀기

tar xvfz rtmpdump-2.3.gz

ffmpeg 폴더로 이동 시키기

mv rtmpdump-2.3/librtmp ffmpeg-0.10.3

rtmp 라이브러리 컴파일, 설치

cd ffmpeg-0.10.3/librtmp
make SYS=linux install
echo 'export PKG_CONFIG_PATH=/lib/pkgconfig:/usr/local/lib/pkgconfig' | tee -a ~/.bashrc

configure

config.sh 파일

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export TMPDIR=c:/ffmpegtmp
 
./configure --disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-avdevice \
--disable-devices \
--disable-filters \
--disable-yasm \
--enable-network \
--enable-protocol=tcp \
--enable-demuxer=rtsp \
--enable-decoder=h264 \
--enable-librtmp

./config.sh

make

testmain.c

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
  
  
int main(int argc, char** argv) {
  
    AVFormatContext* context = avformat_alloc_context();
    int video_stream_index;
  
    av_register_all();
    avcodec_register_all();
    avformat_network_init();
 
    //if(avformat_open_input(&context, "rtsp://192.168.0.40/vod/mp4:sample.mp4",NULL,NULL) != 0){
    //if(avformat_open_input(&context, "rtmp://192.168.0.40/vod/sample.mp4",NULL,NULL) != 0){      
    //if(avformat_open_input(&context, "d:\\windows\\b.mp4",NULL,NULL) != 0){
  
    //open rtsp
    if(avformat_open_input(&context, "rtmp://192.168.0.40/vod/sample.mp4",NULL,NULL) != 0){        
        return EXIT_FAILURE;
    }
  
    if(avformat_find_stream_info(context,NULL) < 0){
        return EXIT_FAILURE;
    }
  
    //search video stream
    int i;
    for(i =0;i<context->nb_streams;i++){
        if(context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
            video_stream_index = i;
    }
  
    AVPacket packet;
    av_init_packet(&packet);
  
    //open output file
    AVOutputFormat* fmt = av_guess_format(NULL,"test2.avi",NULL);
    AVFormatContext* oc = avformat_alloc_context();
    oc->oformat = fmt;
    avio_open2(&oc->pb, "test.avi", AVIO_FLAG_WRITE,NULL,NULL);
  
    AVStream* stream=NULL;
    int cnt = 0;
    //start reading packets from stream and write them to file
  
    av_read_play(context);//play RTSP
    while(av_read_frame(context,&packet)>=0 && cnt <100){//read 100 frames
        if(packet.stream_index == video_stream_index){//packet is video               
            if(stream == NULL){//create stream in file
                stream = avformat_new_stream(oc,context->streams[video_stream_index]->codec->codec);
                avcodec_copy_context(stream->codec,context->streams[video_stream_index]->codec);
                stream->sample_aspect_ratio = context->streams[video_stream_index]->codec->sample_aspect_ratio;
                avformat_write_header(oc,NULL);
            }
            packet.stream_index = stream->id;
  
            av_write_frame(oc,&packet);
            cnt++;
        }
        av_free_packet(&packet);
        av_init_packet(&packet);
    }
    av_read_pause(context);
    av_write_trailer(oc);
    avio_close(oc->pb);
    avformat_free_context(oc);
  
    return (EXIT_SUCCESS);
}

Makefile

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
32
33
34
35
36
37
38
39
TARGET = hello3
 
FFMPEGDIR = /home/bluesanta/ffmpeg-0.10.3
 
LIBDIR = /lib
 
CC  = gcc
AR  = ar
LD  = ld
NM  = nm
RANLIB  = ranlib
STRIP = strip
 
INCLUDE = -I.. -I.
 
CFLAGS = -Wall
LDFFMPEG = -L$(FFMPEGDIR)/libavformat -L$(FFMPEGDIR)/libavcodec -L$(FFMPEGDIR)/libavutil -L$(FFMPEGDIR)/librtmp
LDFLAGS = -L$(LIBDIR) $(LDFFMPEG) -lavformat -lavcodec -lavutil -lrtmp -lkernel32 -lcygwin -lm -lgcc -lc -lssl -lz -lcrypto
 
# application file
APPSOURCES = testmain.c
APPOBJS = $(APPSOURCES:.c=.o)
 
# define the rule
.SUFFIXES:.c .o
 
.c.o:
    @echo Compiling: $<
    $(CC) -c $(CFLAGS)  $(INCLUDE) -o $@ $<
 
all: app
 
app: $(APPOBJS)
    @echo Linking: $(TARGET)
    $(CC) -o $(TARGET) $(APPOBJS) $(LDFLAGS)
    $(STRIP) -s $(TARGET)
 
clean:
    @rm -vf $(APPOBJS) $(TARGET)

컴파일, 실행

 


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

av_free_packet 예제들  (0) 2016.03.27
[펌] How to: Debug from a DLL Project  (0) 2016.03.26
[펌] pragma에 관한 사용법  (0) 2016.03.07
Posted by 세모아
,


출처: https://msdn.microsoft.com/ko-kr/library/605a12zt(v=vs.120).aspx



Visual Studio 2013 대상


How to: Debug from a DLL Project


To specify the calling application in a C++ project

  1. In Solution Explorer, select the DLL project.

  2. On the View menu, choose Property Pages.

  3. In the ProjectProperty Pages window, in the Configuration drop-down list, choose Debug.

  4. Open the Configuration Properties folder, and select the Debugging category.

  5. In the Debugger to launch list, choose Local Windows Debugger or Remote Windows Debugger.

  6. In the Command or Remote Command box, click the drop-down arrow, and select Browse from the list to locate the application.Alternatively, type the path and name of the application.

  7. Type any necessary program arguments in the Command Arguments box.

To specify the calling application in a C# or Visual Basic project

  1. In Solution Explorer, select the DLL project.

  2. On the View menu, choose Property Pages.

  3. In the ProjectProperty Pages window, in the Configuration drop-down list, choose Debug.

  4. Click the Debug tab.

  5. You can start the application by either doing the following:

    1. Set the Start Action to Start external program.

    2. In the Start external program box, click the ellipsis button to browse for the application.

    3. Enter any necessary program arguments in the Command Line Arguments box.

  6. Or, you can invoke an application at a URL. (You might want to do this if you are debugging a managed DLL used by a local ASP.NET application.)

    1. Under Start Action, select the Start browser in URL: radio button.

    2. In the adjoining text box, type the URL.

To start debugging from the DLL project

  1. In Solution Explorer, select the DLL project.

  2. Set breakpoints as needed.

  3. Enter the name and location of the calling application in the ProjectProperty Pages dialog box or window. If the application is a console application, fill in the command line arguments, if it is necessary.


Posted by 세모아
,

출처: http://sjc333.egloos.com/3131382



c++ dll (/clr) 을 c#에서 dllimport 하고 디버그(debug) 하기

제목이 좀 어렵네요.
그냥 이쪽 전공에서나 필요한 지식이고, 귀찮으니 최대한 요점만 짧게 로그 남기듯이 적습니다.
보통은 이런 뻔한 시나리오는 안적는데 vs2013의 황당한 버그 비슷한 것이 저를 괴롭혀서 2시간 가량 낭비하는 바람에 
저와 같은 피해자(?)가 없도록 글을 남기는 것입니다. ㅎㅎ


1. C++ dll을 만든다. 이때 clr 옵션을 건다. 자세한건 구글링...
  한줄만 더 적자면 CLR은 managed가 아니다.

2. C# 어플리케이션을 만들고 1에서 만든 dll을 .cs 소스코드 안에서 직접 import. 하고 작동되는지 확인한다.
  ex ) [DllImport("../bin/test.dll")]

이 글을 보는 사람이라면 사실 1과 2는 이미 되어있는 상태일 것이라고 가정함.

3. 이제 셋팅 들어간다. 위의 1, 2는 하나의 솔루션에 있어야 한다.
  - 가장 중요한 것!! tools > options >  debugging > general > 마지막줄의 use managed compatibility mode 체크.
  - 솔루션에 속성 설정 > common properties > project dependencies >  여기서 c# 프로젝트는 c++ dll 프로젝트에 의존한다고 체크
  - c#프로젝트에 속성 설정 > debug > Enable Debuggers > Enable native code debugging 체크!!!
  - c#프로젝트를 set as startup project

4. 이제 준비는 끝났으니 c++ 코드에 브레이크 포인트 걸고 F5



3번에 중요하다고 한 부분이 내가 고생한 부분이다. 인터넷에서 c++ dll을 c#에서 로드하는 상황에서 디버깅하는 방법을 검색해봤는데 저 말을 찾을 수 없었다. 어딘가 누군가는 적어놨겠지만...아무튼 저 부분이 매우 중요하다.

만약에 use managed com... 이 것을 체크하지 않은 상태라면 이상한 현상이 발생한다.
 - enable native code... 이 옵션을 체크하면 c++에 브레이크 포인트가 작동하지 않는다.
 - enable native code... 이 옵션을 해제하면 c++에 브레이크 포인트가 작동은 하지만 변수값을 제대로 볼 수 없다.

그런데 인터넷에서 흔히 찾을 수 있는 글들에는 오히려 enable native code... 를 해제하면 브레이크 포인트를 쓸 수 없다는 식으로 설명하기 때문에 굉장히 혼란스럽다.
아마도 vs2013의 사소한 버그인듯...

다행인 것은 c#에서 c++ dll을 로드한 상태로 디버깅을 시도하면 use managed com... 이 옵션을 켜야한다고 메세지가 뜬다.
문제는 이 메세지가 제법 길기 때문에 실수로 확인버튼 눌러서 닫으면 옵션이 어디에 있는지 찾기 힘들다는 것.
나는 대충 읽고 옵션을 찾으려고 했는데 tools에서 안찾고 c#프로젝트에서 찾아 들어가는 바람에 ....그 때 부터 대혼란이 시작되었다 orz





Posted by 세모아
,


출처: http://blog.daum.net/silnoontee/13743552



*** 출처: 마이크로소프트웨어

*** 링크: 효과적인 C#메모리 기법

조명근 narlamy@ndoors.net|엔도어즈 기술 지원팀 팀장으로 근무하고 있으며, MMORPG ‘아틀란티카’를 개발했다. 덴마크에 있는 유니티 개발팀과 긴밀히 협조해 메모리 문제 등 여러 가지 이슈들을 처리하고 있으며 현재 유니티 3D엔진을 사용해 웹과 모바일 플랫폼을 지원하는 MMORPG ‘삼국지를 품다’를 개발하고 있다.

C#은 상당히 좋은 언어다. 가장 많이 알려진 C#의 특징 중 하나는 메모리 관리에 부담이 없다는 점이다.

So Cool~ C# 메모리
C/C++를 사용하면서 포인터 때문에 괴로워 해본 적이 있는가? 그렇다면 C#에 관심을 가져보는 것이 좋다. C#은 다음과 같은 특징들을 제공하기 때문이다.

- 메모리 해제에 신경 쓰지 않아도 된다.
- 이미 삭제된 메모리에 접근하는 실수를 방지해준다.
- 잘못된 캐스팅으로 엉뚱한 메모리에 접근하지 않게 한다.
- 배열 크기보다 큰 메모리에 접근하지 못한다.
- 메모리 단편화에 대해 신경 쓰지 않아도 된다.

편한 C#, 마구잡이로 사용하면 낭패
골치 아픈 메모리 관리를 신경 쓰지 않아도 된다는 점은 사용자들에게 무척 편리하게 다가온다. 하지만 C#에서도 메모리를 다루기 위해서는 세심한 주의가 필요하다. 마음 놓고 개발하다 당황했던 과거 필자의 경험을 살펴보도록 하자. 

개 발 초창기, 게임 플레이 중에 주기적으로 랙이 발생했다. 로직을 확인해 봤지만 특별히 로딩 중이거나 초기화된 부분이 없어 의아했다. 유니티 엔진에서 제공하는 프로파일러로 한 프레임에 걸리는 시간을 측정해봤다. 측정 결과, System.GC.Collect() 호출에 오랜 시간이 걸린다는 점이 발견됐다. 플레이 중에 프레임마다 소모되는 시간을 그래프로 보여주는 <그림 1>을 보면 System.GC.Collect() 호출 시 그래프가 크게 튀어 오른 모습이 확인된다. C#에서 사용하지 않는 메모리를 정리하면서 가비지 컬렉션(Garbage collection) 랙이 발생한 것이다.

 


<그림 1> 프로파일러 이미지

이때는 가비지 컬렉션이 동작하는 횟수를 줄여서 랙 발생을 줄이면 된다. 가비지 발생을 줄이면 가비지 컬렉션이 호출되는 시간을 늦출 수 있어 동작 횟수가 줄어든다. 가비지란 프로그램이 실행되면서 어디에서든 더 이상 참조되지 않는 메모리를 의미하므로 가능한 한 메모리를 할당했다 금방 버려지는 상황을 만들지 않는 것이 좋다. 몇 가지 사례들을 살펴보자.

‘+’ operator를 통한 문자열 조합 
C#은 문자열 조합이 쉽다. <리스트 1>에 보이는 것처럼 ‘+’로 연결하면 간단히 문자열 조합이 이뤄진다. 모든 객체가 ToString()을 지원하기 때문에 문자열끼리만 조합되는 게 아니라 int, float 등의 값도 알아서 문자열로 변환·조합된다.

<리스트 1> ‘+’로 연결한 문자열 조합

class Names
{
    public string[] name = new string[100];
    public void Print()
    {
        for (int index = 0; index < name.Length; index++)
        {
            string output = "[" + index + "]" + name;
            Console.WriteLine(output);
        }
    }
}

문제는 <리스트 1>에서 가비지가 많이 발생한다는 점이다. ‘+’ 연산자로 두 값을 연결할 때마다 새로운 string 인스턴스가 생성된다. 연이어 ‘+’ 연산자가 나오기 때문에 다시금 새로운 string 인스턴스가 생성되고, 이전에 만들어진 string 인스턴스는 가비지가 된다. string 조합을 위해 ‘+’ operator 호출이 많아질수록 많은 가비지가 만들어지는 것이다.

그래서 문자열을 조합하는 동안 새로운 객체를 생성하지 않는 System.Text.StringBuilder 객체를 소개한다. ‘+’ operator가 아닌 Append() 메소드를 통해 문자열을 추가하며, string 객체를 만들어내는 게 아니라 이미 잡아놓은 메모리 공간에 문자열만 복사해 뒀다가 한번에 ToString()으로 string 객체를 생성해낸다.

<리스트 2> System.Text.StringBuilder 객체 사용

class NewNames
{
    public string[] name = new string[100];
    private StringBuilder sb = new StringBuilder();

    public void Print()
    {
        sb.Clear();     // sb.Length = 0;
        for (int index = 0; index < name.Length; index++)
        {
            sb.Append("[");
            sb.Append(index);
            sb.Append("] ");
            sb.Append(name);
            sb.AppendLine();
        }
        Console.WriteLine(sb.ToString());
    }
}

과다한 Append() 메소드 호출이 필요해 ‘+’ 코드보다 깔끔하지 못하다고 생각된다면 AppendFormat()을 사용하는 것도 좋다.

 <리스트 3> AppendFormat() 활용

class NewNames
{
    public string[] name = new string[100];
    private StringBuilder sb = new StringBuilder();

    public void Print()
    {
        sb.Clear();     // sb.Length = 0;
        for (int index = 0; index < name.Length; index++)
        {
            sb.AppendFormat("[{0}] {1}", index, name.ToString());
        }
        Console.WriteLine(sb.ToString());
    }
}

string처럼 Immutable pattern을 사용한 객체들의 값에 접근할 때는 기존 메모리를 수정하지 않고 새로운 메모리를 만들어 반환하거나 입력받으므로 사용 시 주의가 필요하다.

메소드 안에서 생성한 객체
C#은 C++과 달리 클래스를 인스턴싱하려면 반드시 new를 해줘야 한다. 이때 heap에서 메모리 할당이 일어난다. 

<리스트 4>와 같이 메소드 안에서 new로 생성된 인스턴스는 메소드를 빠져나오면 더 이상 사용하지 않게 돼 가비지로 처리된다. 이런 패턴의 메소드가 자주 호출될수록 가비지도 많이 발생한다.

<리스트 4> new로 생성된 인스턴스

public class MyVector
{
    public float x, y;
    public MyVector(float x, float y) { this.x = x; this.y = y; }
    public double Length() { return System.Math.Sqrt(x * x + y * y); }
}

static class TestMyVector
{
    public static void PrintVectorLength(float x, float y)
    {
        MyVector v = new MyVector(x, y);
        Console.WriteLine("Vector=({0},{1}), lenght={2}", x, y, v.Length());
    }
}

Vector 클래스를 구조체로 바꿔보면, new 연산자로 인스턴스를 만들어도 heap 영역에 메모리가 할당되지 않는다. 구조체 역시 Value type이기 때문에 stack 영역에 메모리가 할당되며, 메소드를 빠져나갈 경우 자동으로 삭제된다. 물론 heap 영역에 생성된 메모리가 아니기 때문에 가비지 컬렉션의 대상이 되지도 않는다. 

<리스트 5> Vector 클래스를 구조체로 변환

public struct MyVector
{
    public float x, y;
    public MyVector(float x, float y) { this.x = x; this.y = y; }
    public double Length() { return System.Math.Sqrt(x * x + y * y); }
}

static class TestMyVector
{
    public static void PrintVectorLength(float x, float y)
    {
        MyVector v = new MyVector(x, y);
        Console.WriteLine("Vector=({0},{1}), lenght={2}", x, y, v.Length());
    }
}

구조체로 바꿀 수 없다면, <리스트 6>처럼 멤버변수 사용을 권장한다.

<리스트 6> 멤버변수 사용

public class MyVector
{
    public float x, y;
    public MyVector() { x = .0f; y = .0f; }
    public MyVector(float x, float y) { this.x = x; this.y = y; }
    public double Length() { return System.Math.Sqrt(x * x + y * y); }
}

static class TestMyVector
{
    private static MyVector m_cachedVector = new MyVector();
    public static void PrintVectorLength(float x, float y)
    {
        m_cachedVector.x = x;
        m_cachedVector.y = y;

        Console.WriteLine("Vector=({0},{1}), lenght={2}",
x, y, m_cachedVector.Length());
    }
}

속도 저하가 큰 Boxing
Boxing이란 Value type 객체를 Reference type 객체로 포장하는 과정을 뜻한다. C#의 모든 객체는 object로부터 상속되는데, 심지어 상속받지 못하는 int, float 등의 Value type 조차도 object로부터 상속된 것처럼 사용할 수 있다. 하지만 가비지 컬렉션에 의한 부하 못지않게 boxing으로 인한 부하도 크다. 무심코 만든 코드에서 boxing 과정이 일어나는 경우가 있으니 잘 이해하고 사용해야 한다.

<리스트 7>을 보면 리스트에 서로 다른 type의 값을 추가했지만, loop 안에서 추가 값을 object type으로 받아 하나의 코드로 처리할 수 있음을 알 수 있다.

<리스트 7> 서로 다른 type 값 추가

class MyClass
{
    public override string ToString() { return "다섯"; }

    static public void Sample()
    {
        ArrayList list = new ArrayList();
        list.Add(1);
        list.Add(1.5f);
        list.Add(‘3’);
        list.Add("four");
        list.Add(new MyClass());

        foreach (object item in list)
            Console.WriteLine(item.ToString());
    }
}

 


<그림 2> <리스트 7>의 실행 결과

매력적인 C#이지만 Value type의 값을 Reference type인 object로 바꿔주는 과정에는 많은 시간이 걸리며 변환 시에는 System.Object 내부에 랩핑하고 관리되는 heap에 저장된다. 즉 새로운 객체가 만들어지는 셈이다. MSDN에서 발췌한 <그림 3>을 참조하길 바란다.




https://msdn.microsoft.com/ko-kr/library/yz2be5wk.aspx

http://www.gamedevforever.com/322


<그림 3> boxing과 unboxing의 비교

따라서 한 번에 다양한 type을 처리하는 경우가 아니라면 collection에 사용된 값의 type을 명시해주는 Generic collection 사용을 권한다. Generic은 C++의 template와 비슷하다. 그래서 Generic collection들은 C++의 STL container들과 비슷하게 생겼다. <리스트 8>을 참고하자.

<리스트 8> Generic collection

class Example
{
    static public void BadCase()
    {
        ArrayList list = new ArrayList();
        int evenSum = 0;
        int oddSum = 0;

        for (int i = 0; i < 1000000; i++)
            list.Add(i);

        foreach (object item in list)
        {
            if (item is int)
            {
                int num = (int)item;
                if(num % 2 ==0) evenSum += num;
                else oddSum += num;
            }
        }
           
        Console.WriteLine("EvenSum={0}, OddSum={1}", evenSum, oddSum);
    }

    static public void GoodCase()
    {
        List<int> list = new List<int>();
        int evenSum = 0;
        int oddSum = 0;

        for (int i = 0; i < 1000000; i++)
            list.Add(i);

        foreach (int num in list)
        {
            if (num % 2 == 0) evenSum += num;
            else oddSum += num;
        }
           
        Console.WriteLine("EvenSum={0}, OddSum={1}", evenSum, oddSum);
    }
}

메모리가 계속 늘어나는 또 다른 문제의 발생!
이 글을 시작하며 C#에서 사용자는 메모리 해제에 신경 쓸 필요가 없다고 했지만 의도하지 않게 메모리가 늘어나기도 한다. C#에는 delete 같은 메모리 해제 명령이 없기에 메모리 릭(memory leak) 현상이 발생하면 당혹스러울 수 있다. 여기서 C# 메모리의 특징을 다시 한 번 떠올려보자.

시스템에서 더 이상 참조가 없는 메모리를 알아서 해제하는 것을 우리는 가비지 컬렉션이라 부른다. 가비지는 더 이상 참조가 없는 메모리다. C# 애플리케이션이 메모리가 해제되지 않고 계속 증가되고 있다면 어디선가 의도하지 않는 참조가 일어나고 있다고 보면 된다. 그렇다면 어디에서 의도하지 않은 참조가 일어나는 것일까? 예를 통해 확인해 보자.

 


<그림 4> #1 - 케릭터 매니저에서 케릭터를 생성한다

 


<그림 5> #2 - 누군가 디버깅을 위해 '캐릭터 위치 표시' 객체를 만들고 캐릭터 매니저에 접근해 등록된 캐릭터를 모두 참조한다

 


<그림 6> #3 - 캐릭터 매니저에서 필요없는 캐릭터를 삭제한다

 


<그림 7> #4 - 캐릭터 매니저에서 삭제됏지만 '캐릭터 위치 표시' 객체에서는 여전히 참조 중이다. 가비지가 아니기 때문에 메모리에 계속 남아있으며, 구현에 따라서는 의도하지 않게 화면에 남을 수도 있다.

WeakReference로 의도하지 않은 참조를 없애보자
System.WeakReference 는 가비지 컬렉션에 의한 객체 회수를 허용하면서 객체를 참조한다. 인스턴스를 참조하려면 Weak Reference.Target으로 접근해야 하는데 원본 인스턴스가 가비지 컬렉터에 의해 회수되면 WeakReference.Target은 null이 반환된다.

<리스트 9> WeakReference.Target

public class Sample
{
    private class Fruit
    {
        public Fruit(string name) { this.Name = name; }
        public string Name { private set; get; }
    }

    public static void TestWeakRef()
    {
        Fruit apple = new Fruit("Apple");
        Fruit orange = new Fruit("Orange");
           
        Fruit fruit1 = apple;   // strong reference
        WeakReference fruit2 = new WeakReference(orange);
        Fruit target;
           
        target = fruit2.Target as Fruit;
        Console.WriteLine(" (1) Fruit1 = \"{0}\", Fruit2 = \"{1}\"",
            fruit1.Name, target == null ? "" : target.Name);

        apple = null;
        orange = null;

        System.GC.Collect(0, GCCollectionMode.Forced);
        System.GC.WaitForFullGCComplete();

        // fruit1과 fruit2의 값을 바꾼 적은 없지만, fruit2의 결과가 달라진다.
        target = fruit2.Target as Fruit;
        Console.WriteLine(" (2) Fruit1 = \"{0}\", Fruit2 = \"{1}\"",
            fruit1==null ? "" : fruit1.Name,
            target == null ? "" : target.Name);
    }
}

<리스트 9>의 실행으로 <그림 8>을 확인할 수 있다. Fruit2가 참조하고 있던 orange 인스턴스는 가비지 컬렉터에 의해 회수돼 null이 됐다.

 


<그림 8> <리스트 9>의 실행 결과

‘캐릭터 매니저’처럼 객체의 생성·삭제를 직접 관여하는 모듈이 아닌 곳에서는 가능한 WeakRefernce를 사용하는 것이 좋다. ‘객체 위치 표시 객체’처럼 인스턴스를 참조하는 모듈에서 WeakReference를 사용하면, 의도하지 않은 참조로 메모리가 해제되지 않는 실수를 방지할 수 있다. 주의할 점은 Weak Reference.Target 값을 보관하면 안 된다는 것이다. 만약 그대로 보관하고 있으면 강한 참조(strong reference)가 일어나 이를 인식한 가비지 컬렉터는 회수를 실행하지 않게 된다.

C/C++처럼 원하는 시점에 객체를 삭제하고 싶다면
C#에서 는 할당된 메모리를 임의로 해제할 수 없다. 컬렉션에 보관된 인스턴스를 제거하거나 인스턴스를 담고 있던 변수에 null을 넣어 더 이상 참조하지 않는 방법이 있지만 실제 인스턴스가 삭제되는 시점은 가비지 컬렉션 동작 이후가 되므로, 언제가 될 지 정확히 알 수 없다. 의도한 시점에 맞춰 정확히 삭제할 수 없다는 점이 그렇게 중요하지는 않다. 하지만 캐릭터 매니저에서 캐릭터를 제거했는데도 여전히 캐릭터 인스턴스가 남아서 화면에 한동안 계속 나타나는 경우가 발생할 수 있다.

Dispose pattern 소개C#에서는 관리되지 않는 메모리(리소스)를 해제하는 용도로 System.IDisposable이라는 인터페이스를 제공한다. IDisposable 인터페이스를 상속받은 클래스라면 용도에 맞게 Dispose()를 구현해줘야 하는데 이는 FileStream 관련 객체들에서 많이 볼 수 있다.

리소스를 강제로 해제시키려면 직접 Release(), Delete(), Destroy(), Close() 등의 메소드를 만들어 사용하면 되는데 굳이 IDisposable을 사용할 필요가 있을까? 서로 다른 type의 객체여도 IDisposable 인터페이스를 상속받고 있다면, 하나의 코드로 다양한 type의 메모리를 정리할 수 있기 때문에 IDisposable을 사용할 필요가 있다. 또한 Dispose() 메소드만 보고도 “아, 이 클래스는 사용이 끝나면 Dispose()를 호출해서 메모리를 정리해야 하는구나” 라고 금방 알 수 있다.

캐릭터 객체에서 IDisposable 인터페이스를 구현해보자. 업데이트 목록에서도 제외시키고 렌더링 정보도 다 지우자. 캐릭터의 Dipsose()를 호출한 이후에 캐릭터는 어떠한 동작도 하지 못하게 된다. 물론 Dispose()를 호출한다고 캐릭터가 가비지 컬렉터에 의해 메모리 해제되는 것은 아니다.

WeakReference과 IDisosalbe의 조합원하는 시점에 메모리를 해제하려면 앞서 설명한 Weak Reference와 IDisposable을 개별적으로 사용하는 것으로는 부족하다. 둘을 함께 사용하는 것이 좋다. <리스트 10>을 보자.

<리스트 10> Disposable 인터페이스를 상속받아 구현된 캐릭터 클래스

namespace MyApp
{
    public class SampleChar : IDisposable
    {
        private IRenderObject m_Render = Renderer.CreateRenderObject();

        public void Dispose()
        {
            SampleCharManager.Remove(this);
            m_Render = null;
        }

        public bool isRemoved { get { return m_Render == null; } }

        public void Render()
        {
            if (m_Render == null) return;
            // 렌더링
        }

        public void Update() { }
    }
}

예제로 만들어 본 캐릭터 클래스는 Disposable 인터페이스를 상속받아 구현된다. Dispose 후에는 더 이상 업데이트가 되지 않도록 SampleCharManager에서 제거되며, 렌더링 객체를 null로 만들어 화면에 그려지지 않도록 했다.

IRenderObject 인터페이스는 <리스트 11>과 같이 구현된다.

<리스트 11> IRenderObject 인터페이스

namespace MyApp
{
    public interface IRenderObject
    {
        void Render();
    }

    public static class Renderer
    {
        public static IRenderObject CreateRenderObject()
{
return new DumyRenderObject(); // IRenderObject를 상속받은 더미 객체
}
    }
}

<리스트 12>의 캐릭터 매니저 클래스는 등록된 캐릭터들을 일괄적으로 업데이트시키고 렌더링한다.

<리스트 12> 등록 캐릭터 일괄 업데이트 및 렌더링

namespace MyApp 
{
    static class SampleCharManager
    {
        private static List<SampleChar> m_list = new List<SampleChar>();

        public static void Update()
        {
            foreach (SampleChar obj in m_list) 
                obj.Update();
        }

        public static void Render()
        {
            foreach (SampleChar obj in m_list)
                obj.Render();
        }

        public static void Add(SampleChar obj)
        {
            m_list.Add(obj); 
        }

        public static void Remove(SampleChar obj)
        {
            m_list.Remove(obj);
        }
    }
}

<리스트 13>의 디버깅을 위한 ‘캐릭터 위치 표시 객체’는 WeakReference를 통해 SampleChar 객체를 참조하도록 구현돼 있고, SampleCharManager에서 캐릭터를 삭제하더라도 안전하게 가비지가 회수된다. 업데이트 시 DisplayCharInfo는 삭제된 캐릭터를 스스로 판단해 목록에서 제거한다.

<리스트 13> 디버깅을 위한 캐릭터 위치 표시 객체

namespace MyDebug
{
    static class DisplayCharInfo
    {
        private static List<WeakReference> m_list = new List<WeakReference>();
        private static Queue<WeakReference> m_removeQueue =
new Queue<WeakReference>();

        public static void Update()
        {
            foreach (WeakReference item in m_list)
            {
                MyApp.SampleChar obj = (item.Target != null) ?
 item.Target as MyApp.SampleChar : null;

                if (obj == null || obj.isRemoved)
                {
                    m_removeQueue.Enqueue(item);
                }
                else 
                { 
                    /* 캐릭터 정보 표시 */ 
                }
            }

            while(m_removeQueue.Count > 0)
            {
                WeakReference item = m_removeQueue.Dequeue();
                m_list.Remove(item);
            }
        }

        public static void Add(MyApp.SampleChar obj)
        {
            m_list.Add(new WeakReference(obj));
        }
    }
}

C#에서 메모리를 관리하는 데 도움되길 바라며, 지금까지 설명한 내용을 요약하면 다음과 같다.

- string 조합이 많다면, StringBuilder 활용
- Immutable 객체의 값 접근 시 매번 메모리가 생성될 수 있으므로 주의
- 매번 호출되는 메소드 안에서 반복해서 일회성 인스턴스가 생성되지 않도록 주의
- Boxing / unboxing이 가능한 일어나지 않도록 주의
- WeakReference를 사용해서 의도하지 않은 참조 줄이기
- IDisposable 인터페이스를 사용해 사용자가 원하는 시점에 객체 삭제하기

Value type과 Reference type 비교

Value type은 stack 영역에 할당되며 값이 통째로 복사된다.

 

유니티 3D엔진에서의 메모리 관리
유니티 3D엔진으로 개발하면서 주의할 내용을 알아보자. 유니티 3D엔진은 크게 모노 메모리와 엔진에서 관리하는 메모리로 나뉜다. 둘 다 메모리가 부족하면 내부에 관리하는 heap 영역을 늘려 메모리를 할당한다. 이렇게 한 번 늘어난 heap은 줄어들지 않는 특징을 가진다. 물론 늘어난 heap 안에서 메모리가 재사용되므로, 무턱대고 늘어나진 않는다. 하지만 가비지를 너무 많이 생성시키면 GC.Collect()로 인한 성능저하와 더불어 최대 메모리가 늘어날 수도 있으니 주의해야 한다. 가능한 가비지가 덜 생성되도록 코드를 구현하는 게 좋다. 메모리는 한 번에 잡는 것이 좋고, caching이나 memory pool을 사용하는 것도 도움이 된다.

<리스트 14> Value typepublic static class Sample
{
    public static void TestValueType()
    {
        int a = 100;
        int b = a;
       
        a = 200;
        Console.WriteLine(" a={0}, b={1}", a, b);
    }
}

<리스트 14>를 실행하면 <그림 9>와 같은 결과를 확인할 수 있다. a와 b는 서로 다른 메모리 공간을 가지고 있다.

 


<그림 9> <리스트 14>의 실행 결과

Reference Type은 heap 영역에 할당되며, C/C++의 포인터나 레퍼런스처럼 new로 생성한 인스턴스를 참조한다.

<리스트 15> Reference type

public class MyInt
{
    public int Value { get; set; }
    public MyInt(int val) { this.Value = val; }

    public static void TestReferenceType()
    {
        MyInt a = new MyInt(100);
        MyInt b = a;

        a.Value = 200;
        Console.WriteLine(" a={0}, b={1}", a.Value, b.Value);
    }
}

<리스트 15>의 실행 결과로 <그림 10>을 확인할 수 있다. a와 b는 같은 메모리를 참조한다.

 


<그림 10> <리스트 15>의 실행 결과



Posted by 세모아
,

아래 내용중에서 필요한 것은,


#pragma comment()

이 중 가장 대표적인 사용법은 명시적인 라이브러리의 링크이다.

#pragma comment(lib, "xxxx.lib")

와 같이 사용하여 해당 라이브러리를 링크시켜 준다.
 여러사람이 같이 수행하는 프로젝트의 경우 이와 같은 방법을 사용하여 lib를 링크하는 것이 라이브러리가 링크되어있다는 사실을 알기에도 좋고 굳이 주석다라 설명할 필요도 없어 좋지 않나 싶다. (있다는 사실은 알지만 아직 프로젝트 수행중 실제로 사용해 본적은 없음)

굳이 project settings에서 link tab에 있는 input에 .lib file을 쓰지 않고 #pragma comment ( lib, "xxx.lib" ) 라고 써도 된다는 거죠..



My)

아래그림과 같이 Linker의 Additional Library Directories에  lib파일이 있는 경로에 포함시키고,

cpp파일에 #pragma comment(lib, "avformat.lib") 

을 기록한다.




참고 : http://oojjrs.tistory.com/35 (#pragma comment(lib, file) 에 임의의 상대경로 입력하기)





출처: http://iprinceps.egloos.com/1334766

매번 매번 사용해도 헷갈리는 pragma의 용법에 대해 모아 총정리 하였다.

#pragma는 define 이나 include와 같이 #으로 시작하는 전처리구문(precompiler)의 하나이다.
 컴파일러에 종속적인 구문이라 컴파일러가 변경되었을 경우 제대로된 동작을 보장하지 못하므로 프로젝트 진행중에 서로 다른 컴파일러를 사용한다면 사용하지 않음이 바람직 하겠다.
 - 대신 대체하는 문법을 사용해야 되겠다.

#pragma once
 이것은 "컴파일러에게 한번만 컴파일해!" 라고 명령한다.
 헤더의 중복을 막아준다.
 무슨말인가 하면
a.h를 구현한 a.cpp, a.h는 독립적이다.(include가 없다.)
b.h를 구현한 b.cpp, c.h, a.h순서로 include
c.h를 구현한 c.cpp, a.h를 include

 컴파일하면 b.h에서 c.h를 포함시키라고 되어있네? 하고 c.h에 들어가고 어? a.h를 포함하라고 그러네? 이러고 a.h를 포함한 c.h가 b.h로 돌아온다 그리고 a.h를 포함하라는 명령을 받고 a.h를 추가하다보면 같은 변수와 함수선언이 되어있다. 에러에러~
 같은 선언이 두 번 반복되니 당연히 충돌이 난다. 컴파일러가 똑똑하여 단순히 경고 처리만 해주고 알아서 하나로 종합해줄 수도 있지만 대부분의 기본적인 컴파일러는 이건 아니잖아~ 한다.

 이럴 때 써주는 것이다. pragma once
 이는 c기본문법을 사용하여 구현할 수 있다.

#ifdef _MYCOMPILECK
#define _MYCOMPILECK
// 헤더 파일의 내용 선언
#endif



#pragma comment()
기본적인 pragma comment()의 형식은 다음과 같다.

#pragma comment( comment-type, ["comment string"] )

[] 안의 구문은 comment-type에 따라 필요할 경우 사용하는 것이다.
comment type에는 compiler, exestr, lib, linker, user 등이 올 수 있다.

#pragma comment( linker, "/subsystem:windows" )
#pragma comment( linker, "/subsystem:console" )

linker 를 사용하면 프로젝트를 console application인지 win32 application인지 명시해줄 수 있다.

또한 섹션의 설정을 할 수 있다.

#pragme comment( linker, "SECTION:.SHAREDATA,RWS" )

#pragma data_seg("SHAREDATA") 와 함께 사용하여 공유 메모리를 생성한다.
위의 명령어 대신 def 파일 안에 아래와 같이 해주어도 된다.

SECTIONS
 SHAREDATA READ WRITE SHARED


이 중 가장 대표적인 사용법은 명시적인 라이브러리의 링크이다.

#pragma comment(lib, "xxxx.lib")

와 같이 사용하여 해당 라이브러리를 링크시켜 준다.
 여러사람이 같이 수행하는 프로젝트의 경우 이와 같은 방법을 사용하여 lib를 링크하는 것이 라이브러리가 링크되어있다는 사실을 알기에도 좋고 굳이 주석다라 설명할 필요도 없어 좋지 않나 싶다. (있다는 사실은 알지만 아직 프로젝트 수행중 실제로 사용해 본적은 없음)



#pragma data_seg()
pragma data_seg()의 형식은 다음과 같다.

#pragma data_seg( ["section-name"[, "section-class"] ] )

[]는 사용하지 않아도 된다는 의미이다.

#pragma data_seg( "SHAREDATA" )
 int x;
 char y;
#pragma data_seg()

 DLL 파일을 만들어보면서 제일 많이 사용해 보았고 가장 헷갈려 했던 부분이기도 하다.
 DLL의 데이터 공유를 하기 위해 사용한다.
 공유할 섹션을 만드는 것이다. 위의 명령어는 필수적으로 위에서 사용된 두 가지중 한가지 방법과 함께 사용 되어야 한다.

#pragme comment( linker, "SECTION:.SHAREDATA,RWS" )
SECTIONS
 SHAREDATA READ WRITE SHARED

 둘 다 해당 SECTION(SHAREDATA)의 허용 범위(?속성?)를 설정하는 것이다. READ, WRITE, SHARED 세 가지를 쓴다는 의미~
 해당 사항에 대해 msdn에서 자세한 정보를 발견하지 못해 적지 못하였다(검색능력의 부족!!)
 이제 변수 x와 y는 해당 dll을 사용하는 외부 파일과 같이 공유할 수 있는 변수가 되었다.(외부에서 접근 가능하게 되었다.)

 이렇게 공유하는 변수는 물론 new로 메모리를 할당한 변수도 공유 가능하다.
 특히 new 나 memalloc(이건 아직 미확인이지만 같은 메모리 할당이므로 가능할 것으로 본다)으로 메모리할당한 변수들은 dll외부에서도 해제(delete) 가능하다.



#pragma warning
특정 경고를 끄고 싶을 때 사용한다.
비쥬얼 스튜디오의 버전이 다르기 때문에 뜨는 경고는 더더욱이 귀찮은 존재이다.(하지만 수정해서 손해볼 것은 없다. 그것이 곧 버그로 이어질 수 있기 때문이다. 특히 형변환의 경우 강제 캐스팅하여 확실히 명시해주는 것이 좋다. 일부러 그 값을 떼어낸다는 프로그래머의 의지를 컴파일러에게 보여주자. 부지런할수록 후에 손이 가는 일이 적어진다. 노력하자~)

형식은 이와 같다.

#pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list...] )
#pragma warning( push[ ,n ] )
#pragma warning( pop )

실제 사용은 아래와 같이 한다.

#pragma warning( disable:4996 )




#pragma message()
컴파일 중에 메세지를 뿌려준다.
 말이 필요없다-.-/

#pragma message("merong")

Takes from: http://mayu.tistory.com/8

선행처리기중의 하나인 pragma에 관한 사용법을 정리하여 올립니다.
문법은 다음과 같습니다.

 

#pragma directive-name


#pragma는 이것을 지원하는 다른 compiler에서 방해가 없이 C++ Builder에서 원하는
지시어를 정의할 수 있도록 해줍니다. 만일 지시명을 인식하지 못한다면 에러 또는
경고 메세지를 수반하지 않고서 #pragma의 지시를 무시하게 됩니다.

Borland C++ Builder에서 지원하는 #pragma지시어는 모두 18가지가 있습니다.
이제부터 그것들을 하나 하나 살펴보기로 하겠습니다.

1. #pragma anon_struct
    . 사용법 
      #pragma anon_struct on
      #pragma anon_struct off
    . Class에 익명의 구조체를 포함하여 compile하는것을 허락할 것인지를
      지시합니다. 익명이란 tag를 갖지 않는다는것을 의미합니다.
    ex)
    #pragma anon_struct on
    struct S {
       int i;
       struct {  // 익명구조체를 포함한다.
          int   j ;
          float x ;
       };
       class {  // 익명 클래스를 포함한다.
       public:
          long double ld;
       };
    S() { i = 1; j = 2; x = 3.3; ld = 12345.5;}
    };
    #pragma anon_struct off

    void main() {
       S mystruct;
       mystruct.x = 1.2;  // 포함된 data에 값을 할당한다.
   }

//--------------------------------------------------------------------------
2. #pragma argsused
    . argsused 프라그마는 함수 정의 사이에서만 허용되고 바로 다음 함수에만
      영향을 미치며 경고 메세지를 disable시킵니다.
      이 pragma를 사용하지 않은 경우 사용되지 않은 argument가 있으면
      "Parameter name is never used in function func-name"
      라는 경고 메세지를 표시하게 됩니다.
    ex)
    #pragma argsused
    void __fastcall TImageForm::FileEditKeyPress(TObject* Sender, Char &Key)
    {     if (Key == 0x13) {
             FileListBox1->ApplyFilePath(FileEdit->Text);
             Key = 0x0;
          }
    }
    위의 예에서는 함수내에서 Sender라는 인수가 사용되지 않았지만 경고 메세지가
    표시되지 않습니다.

//--------------------------------------------------------------------------
3. #pragma codeseg
    . 사용법
      #pragma codeseg <seg_name> <"seg_class"> <group>
    . codeseg 프라그마는 함수들을 위치시킬 group, class 또는 segment의 이름을
      줄수 있도록 지시합니다. 만일 option없이 사용하였다면 함수의 배치를 위해서
      default code segment가 사용되어질것입니다. 결국 이 pragma를 사용하지 않는
      경우와 동일한 결과를 가져옵니다.

//--------------------------------------------------------------------------

4. #pragma comment
    . 사용법 
      #pragma comment (comment type, "string")
    . comment 프라그마는 출력되어지는 file에 주석을 기록시킬것을 지시합니다.
      comment type에 올수 있는 값들은 다음중의 하나가 될것입니다.
      * exestr
        linker가 ".OBJ" file에 string을 기록합니다. 이렇게 기록된 string은
        실행파일내부에 기록되어지며, 이것은 결코 메모리로 load되지 않습니다.
        하지만 적당한 파일 검색 유틸리티를 사용하여 실행파일에서 string을
        찾아볼 수 있습니다.
      * lib
        ".OBJ" file에 주석의 내용을 기록합니다.
        library에 새로운 module을 추가하는 경우 에만 comment 프라그마를 사용하여
        linker에게 결과 file에 명시할 수 있도록 지시할 수 있습니다. 다시 말하면
        기존에 작성되어진 module에는 comment 프라그마를 사용하여 string을 추가
        시킬수 없습니다. 새롭게 library를 작성한다면 예외일 수 있겠지요.
        linker는 최종의 library에서 string에 명시된 library module 이름을 포함
        합니다. 여러개의 module들도 이름지어질 수 있으며 이름을 만들기 위하여
        linke되어집니다.

        예) comment ( lib, * ) : comment로 사용할 수 있는 명령은 여러 개 있는데, 그중 가장 대표적인 것이 lib 으로, 해당 라이브러리를 링크시켜준다 .

             즉, 지정된 라이브러리 화일을 포함하여 컴파일한다. 프로젝트 설정에 라이브러리를 포함하는 것과 같다.

        예2) comment( lib, "ws2_32" ) 
              이것은, 컴파일시 ws2_32.lib 파일을 링크하라는 명령입니다. 보통 Visual studio같은
              IDE 개발환경에서는, 프로젝트 셋팅에서 해주지만, 혹 그런부분을 빼먹거나 환경이
              바뀔때를 대비해서 이렇게 해두면 편하죠.

        예3) #pragma comment( "comment-type" [, commentstring] )

              comment type에는 compiler, exestr, lib, linker, user 등이 올 수 있습니다.

              그 중 질문에서의 lib는 library file을 지정하는 것이라고 생각하시면 됩니다. comment string이라는 부분에는 file의 이름이나 path를 넣으시면 됩니다. ".lib" file 아시죠?

               그러니까 굳이 project settings에서 link tab에 있는 input에 .lib file을 쓰지 않고 #pragma comment ( lib, "xxx.lib" ) 라고 써도 된다는 거죠..


      * user
        compiler는 ".OBJ" file에 string을 기록합니다. 하지만 linker에 의해
        string은 무시되어집니다. object 파일에만 그 내용이 남게 됩니다.

//--------------------------------------------------------------------------

 

5. #pragma exit
    . 사용법
      #pragma startup function-name <priority>
      #pragma exit function-name <priority>
    . 이들 두 프라그마는 프로그램이 프로그램 시동시(main이 호출되기 전) 호출
      되어야 할 함수와 프로그램 탈출(프로그램이 _exit를 통해 종료하기 바로 전)
      을 명시할 수 있도록 합니다. 명시된 function-name은 반드시 인수를 취하지
      않고 void를 return하는 미리 선언된 함수여야합니다. 다시 말하면 다음과
      같이 선언될 수 있습니다.
      void func (void);
      priority는 반드시 64-255의 범위 내에 있는 정수여야하며 최상의 우선권은
      0입니다. 0-63사이의 priority는 C library에서 사용하므로 사용자가 이를
      사용해서는 안됩니다. 최상위 우선권을 가진 함수는 시동시에 맨 먼저 호출
      되고 탈출시에 맨 마지막으로 호출됩니다. 우선권을 명시해 주지 않을 경우
      기본적으로 100의 우선권을 갖게 됩니다. pragma startup 또는 exit에 사용된
      함수명은 반드시 프라그마 라인에 도달하기 전에 정의(또는 선언)되어야함에
      주의하십시요.
    ex)
      #include <stdio.h>
      void startFunc(void)
      {
          printf("Startup Function.\n");
      }
      #pragma startup startFunc 64 //우선권 64로 시동시에 맨 먼저 호출됩니다.

      void exit Func(void)
      {
          pirntf("Wrapping up execution.\n");
      }
      #pragma exit exitFunc //기본적으로 우선권이 100으로 지정됩니다.

      void main(void)
      {
          printf("This is main.\n");
      }

//--------------------------------------------------------------------------

6. #pragma hdrfile
    . 사용법
      #pragma hdrfile "filename.CSM"
    . 이 지시어는 프리컴파일된 헤더를 저장할 파일의 이름을 설정합니다. IDE
      프로젝트를 위한 디폴트 파일명은 <projectname>.CSM이고 command line용
      으로는 BC32DEF.CSM이라는 이름을 갖습니다.  프리컴파일된 헤더를 사용하지
      않으면 이 지시어는 효력이 없으며 명령라인 컴파일러 옵션 -H=filename 또는
      프리 컴파일된 헤더를 사용하면 프리 컴파일된 헤더를 저장하기 위해 사용되는
      파일명을 변경할 수 있습니다.
      명령라인 옵션은 다음과 같습니다.
      * 프리컴파일드 헤더를 사용하는 경우
        -H=filename
      * 프리컴파일드 헤더를 사용은 하지만 새로운 프리컴파일드 헤더파일을
        변환하지 않는 경우
        -Hu
      * 프리컴파일드 헤더를 사용하지 않거나 새로운 프리컴파일드 헤더파일을
        변환하지 않는 경우. (기본값)
        -H-

//--------------------------------------------------------------------------

7. #pragma hdrstop
    . 사용법
      #pragma hdrstop
    . 이 지시어는 프리컴파일에 적합한 헤더 파일의 목록을 종료시키는데, 이것을
      사용하면 프리컴파일된 헤더가 사용하는 디스크 공간의 양을 줄일 수 있습니다.
      프리컴파일드 헤더파일은 #pragma hdrstop이 선언되기 전에 #include를
      사용하여 포함된 헤더파일들을 동일하게 프로젝트 내의 source들간에 공유시킬
      수 있습니다. 그러므로 #pragma hdrstop전에 일반적인 헤더파일들을 포함하면
      최상의 콤파일러의 성능을 얻을 수 있습니다. 확실하게 #pragma hdrstop 전에
      #include를 사용한다면 모든 source file들에게 동일하게 적용되거나 아주
      조그마한 변화만이 있을 것입니다. IDE 환경에서는 강화된 프리컴파일드 헤더의
      성능을 가지는 코드로 변환합니다. 예를 들자면 다음의 New Application의 소스
      파일인 "Unit1.cpp"는 다음과 같이 될것입니다.

      #include <vcl.h> // 일반적인 헤더파일
      #pragma hdrstop  // 헤더파일의 리스트는 여기서 끝난다.

      #include "Unit1.h" // 헤더파일의 명시
      //....
      이 pragma 지시어는 오직 source file에서만 사용하며, 헤더파일에서 사용했다
      면 아무런 효과도 없을 것입니다.

//--------------------------------------------------------------------------

8. #pragma inline
    . 사용법
      #pragma inline
    . 이 지시어는 명령 라인 콤파일러 옵션 -B 또는 IDE의 인라인 옵션과 동일
      합니다. 이것은 컴파일러에게 프로그램 내에 인라인 어셈블리 언어 코드가
      있음을 알려줍니다. 컴파일러는 #pragma inline을 만날때 -B옵션을 사용하여
      스스로 재시동하므로 이 지시어는 파일의 상단에 배치되는 것이 최선입니다.
      실제로 -B옵션과 #pragma inline을 모두 off시켜둘 수 있습니다. 그러면
      컴파일러는 asm문을 만나자마자 스스로 재시동합니다. 이 옵션과 지시어의
      목적은 컴파일 시간을 다소 절약하는 것입니다.

//--------------------------------------------------------------------------

9. #pragma intrinsic
    . 사용법
      #pragma intrinsic [-]function-name
    . #pragma intrinsic를 사용하면 함수의 inline화를 위해 command-line 스위치나
      IDE의 옵션이 무시되어집니다.  intrinsic함수를 인라인화할 때는 그 함수를
      사용하기 전에 반드시 그것을 위한 원형을 포함시켜야만 합니다. 이것은
      인라인화 할 때 컴파일러가 인라인화한 함수를 내부적으로 인식하는 함수로
      개명하는 매크로를 실제로 생성하기 때문입니다. 가령 strcpy 함수를 인라인
      화 하기 위하여 다음과 같은 문장을 사용하였다면
      #pragma intrinsic strcpy
      컴파일러는 다음과 같은 매크로를 생성합니다.
      #define strcpy __strcpy__
      컴파일러는 두 선행 밑줄과 두 후미 밑줄을 사용하여 함수 호출을 인식하고
      그 함수의 원형을 내부적으로 저장해 둔 원형과 부합시키려 합니다. 그러므로
      원형을 공급하지 않거나 공급한 원형이 콤파일러 내부의 원형과 부합되지 않을
      경우, 콤파일러는 그 함수를 인라인화 하려는 시도를 불식시키고 에러를
      발생시킵니다. 이 프라그마 사용의 궁극적인 목적은 함수 호출에 대한
      오버헤드를 줄위기 위한것입니다. 함수호출은 빨라지겠지만 그만큼 크기는
      증가하게 될것입니다.
      ex)
      #pragma intrinsic strcpy
      #pragma intrinsic -strcpy

//--------------------------------------------------------------------------

10.#pragma link
    . 사용법
      #pragma link "[path]modulename[.ext]"
    . 이 지시어는 실행화일에 파일을 링크시킬것을 링커에세 지시합니다.
      기본적으로 링커는 -L옵션으로 지정된 패스와 로칼 디렉토리에서 modulename을
      찾습니다. path 아규먼트를 이용하여 디렉토리를 지정할 수도 있습니다. 또한
      링커는 확장자를 ".obj"를 기본으로 간주합니다.

//--------------------------------------------------------------------------

11. #pragma message

     컴파일 도중에 지정된 내용을 VC의 아웃풋 윈도우에 출력시켜 준다. 컴파일시 특정 문장을 표시
    . 사용법
      #pragma message ("text" ["text"["text" ...]])
      #pragma message text
    . #pragma message는 프로그램 코드 내에서 사용자 정의 메세지를 명시합니다.
      첫번째 형식은 하나 이상의 스트링 상수들로 구성된 문장을 필요로 하고
      메세지는 괄호안에 싸여있어야만 합니다.(이 형식은 MSC와 호환됩니다.)
      두번째 형식은 경고 메세지의 문장을 위해 #pragma에 연속되는 문장을
      사용합니다. #pragma의 두가지 형태와 함께 다른 메크로의 참조는 메세지가
      디스플레이 되기전에 확장되어집니다.  사용자 정의 메세지가 디스플레이
      되는것은 기본치이며 명령 라인 옵션의 Show Warnings를 사용하여
      on/off 시킬 수 있습니다. 이 옵션은 콤파일러의 -wmsg에 해당합니다.

    ex)
      // msacm.h
      #if defined(UNICODE) && !defined(_UNICODE)
      #ifndef RC_INVOKED
      #pragma message("MSACM.H: defining _UNICODE
                       because application defined UNICODE")
      #endif
      #define _UNICODE
      #endif

      // ustring.h
      #pragma message osl/ustring.h has been replaced by winsys/string.h
      #include <winsys/string.h>

//--------------------------------------------------------------------------

12.#pragma obsolete
    . 사용법
      #pragma obsolete identifier
    . #pragma obsolete 프로그램 코드에서 pragma의 선언 이후에 마주치게 되는
      identifier의 첫번째 사용에 대해서 경고를 발생합니다. 경고는 identifier를
      쓸모없는 상태로 만듭니다.
    ex)
    // io.h
    #if !defined(RC_INVOKED)

    /* Obsolete functions */
    #pragma obsolete _chmod
    #pragma obsolete _close
    #pragma obsolete _creat
    #pragma obsolete _open
    #pragma obsolete _read
    #pragma obsolete _write

    /* restore default packing */
    #pragma pack(pop)

    #if defined(__STDC__)
    #pragma warn .nak
    #endif

    #endif  /* !RC_INVOKED */

//--------------------------------------------------------------------------

13.#pragma option
    . 사용법
      #pragma option options
      #pragma option push options
      #pragma option pop
    . #pragma option은 프로그램 원시 코드 내에 명령라인 옵션을 포함시키고자
      할 때 사용하며 push 또는 pop 옵션과 함께 사용되어질 수 있습니다.
      options는 임의의 명령라인 옵션(단, 아래에 수록된 것은 제외합니다.)이며
      하나의 지시어 내에서 여러개의 option들을 나타낼 수 있습니다.
      예를 들자면 다음과 같습니다.

      #pragma option -C
      #pragma option -C -A

      toggle option(-a, -K같은)은 comman line에서 on/off될수 있습니다.
      이들 toggle option들은 option 다음에 마침표를 두면 그 명령라인, 구성 파일,
      옵션 메뉴 설정값에 대해 옵션을 리털할 수 있으며 이를 이용하면 정확한
      설정값을 기억하지 않고도(혹은 알 필요가 없거나) 옵션을 임시로 변경했다가
      다시 그것을 디폴트로 복귀시킬 수 있습니다.

      pragma optino에 포함하여 나타날 수 없는 옵션들은 다음과 같습니다.
      -B   -c   -dname
      -Dname=string   -efilename   -E
      -Fx  -h   -lfilename
      -lexset   -M   -o
      -P   -Q   -S
      -T   -Uname   -V
      -X   -Y
      다음의 경우에 #pragmas, #indluces, #define과 약간의 #ifs를 사용할 수
      있습니다.
      * #if, #ifdef, #ifndef 또는 #elif지시어 내에서 두 밑줄로 시작하는 매크로명
        (그리고 그에 따른 내장 매크로도 가능합니다.)의 사용 전.
      * 첫번째 실재 token이 발생하기 전(첫번째 C 또는 C++ 선언문)

      특정 명령 라인 옵션은 이들 사건 앞의 #pragma option 내에서만 나타날 수
      있는데 그러한 option들은 다음과 같습니다.
      -Efilename        -f      -i#
      -m*   -npath   -ofilename
      -u   -W   -z

      다른 option들은 어디서나 변경될 수 있는데 다음 option들은 함수 또는 대상
      선언문 사이에서 변경될 경우 컴파일러에만 영향을 미칩니다.
      -1        -h      -r
      -2   -k   -rd
      -a   -N   -v
      -ff  -O   -y
      -G   -p   -Z

      다음의 option들은 언제든지 변경될 수 있으며 즉시 영향을 미칠 수 있습니다.
      -A   -gn   -zE
      -b   -jn   -zF
      -C   -K   -zH
      -d   -wxxx
      이들 option들은 그 명령 라인 상태로 재설정하기 위해 점(.)앞에 추가로
      나타날 수 있습니다.

      push 또는 pop을 사용한 #pragma option
      18:41, 21 February 2007 (PST)18:41, 21 February 2007 (PST)18:41, 21 February 2007 (PST)18:41, 21 February 2007 (PST)18:41, 21 February 2007 (PST)18:41, 21 February 2007 (PST)18:41, 21 February 2007 (PST)~~
      또한 콤파일러 지시어들을 쉽게 변경할 수 있도록 push 그리고 pop 아규먼트들
      과 함께 #pragma option 지시어를 사용할 수도 있습니다.

      잠재적으로 많은 컴파일러 옵션과 경고들을 변경하는 파일들을 포함하기 위해
      #pragma option push를 사용할 수 있고, #pragma option pop은 단일 문장으로서
      이전의 상태를 되돌려준다. 예를 들자면 다음과 같다.

      #pragma option push
      #include <theworld.h>
      #pragma option pop
      #include "mystuff.h"

      #pragma option push 지시어는 첫번째로 모든 콤파일러 옵션들과 경고 설정들을
      스택에 push한 후에 다른 옵션들이 존재한다면 이를 처리한다. 다음의 예는
      #pragma option push가 옵션들을 사용하거나 혹은 그렇지 않을수 있음을
      보여줍니다.

      #pragma option push -C -A
      #pragma option push

      #pragma option pop directive은 스택으로부터 옵션들과 경고들의 마지막 설정
      을 pop함으로서 컴파일러 옵션과 경고들을 변경합니다. 만일 스택이 비어있고
      option pop과 일치하는 option push가 없으며 아무것도 발생하지 않은경우
      경고가 주어집니다. 다음은 빈 스택에대해서 경고를 발생시킵니다.

      #pragma option push
      #pragma option pop
      #pragma option pop      /* 경고가 발생합니다.

      권장하지는 않지만 지시어를 사용하여 이 경고를 off시킬 수 있습니다.
      #pragma warn -nop.

      만일 pop의 다음에 어떤 옵셥들을 명시할려고 한다면 에러가 발생하게되며
      pragma option pop 다음에는 어떤것도 허락하지 않습니다. 예를 들면, 다음은
      에러를 발생합니다.

      #pragma option pop -C         /* ERROR
      만일 push된 옵션들의 스택이 파일의 시작과 마지막이 동일하지 않다면
      다음과 같은 경고메세지가 발생합니다.

      Previous options and warnings not restored.

      이 경고메세지를 off시키기 위하여 지시어 #pragma nopushoptwarn를 사용할
      수 있습니다.

//--------------------------------------------------------------------------

14. #pragma pack

    변수 정렬을 인위적으로 변경시킨다.(보통은 4바이트로 지정되어 있다.)
    . 사용법
      #pragma pack(n)

         위에서, n의 값으로, 1,2,4,8등이 올수 있으며, 특히 네트웍통신쪽을 개발할때
         구조체의 멤버들 align할때 사용하는 것으로서, 빈번하게 사용됩니다.
         구조체 정렬부분은 중요하지만, 여기서는 그쪽까지 언급하기에는 양이 많아서
         여기까지만 설명함.
      #pragma pack(push, n)

      #pragma pack(pop)
    . #pragma pack 지시어는 콤파일러 옵션 -a와 함께 #pragma option을 사용하는
      것과 동일합니다. n은 콤파일러가 저장된 메모리에 데이터를 정렬하는 방법을
      결정하는 byte의 정렬이다. 보다 자세한 사항은 -a 콤파일러 옵션에 관한
      내용을 참고하십시요. #pragma pack은 또한 #pragma option지시어에 push나
      pop을 사용하는것과 동일한 기능을 제공 하도록 push나 pop 아규먼트와 함께
      사용할 수 있습니다.  아래의 내용은 #pragma pack과 #pragma option을 비교한
      내용입니다.
      ━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━
      #pragma pack              ┃     #pragma option
      ━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━
      #pragma pack(n)           ┃     #pragma option -an
      #pragma pack(push, n)     ┃     #pragma option push -an
      #pragma pack(pop)         ┃     #pragma option pop
      ━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━

    예) #pragma pack(push, before_pack, 1)
        구조체 정렬을 1 바이트로 만든다. 1 바이트로 맞추기 전의 정렬 기준을 before_pack에 저장한다

         #pragma pack(pop, before_pack)
        위와 같이 한쌍으로 사용된다. before_pack에 저장된 정렬 기준으로 복원한다.

        다음과 같이 사용할 수도 있다.
         #pragma pack(push, 1)
         #pragma pack(pop)

        다음과 같이 복원없이 지정할 수도 있다.
         #pragma pack(1)

    예) #pragma pack(pop) 이라...아마 이거랑 쌍이 되는게 있을텐데요
         #pragma pack(push,1) 이라던지요.
         이건 구조체를 1바이트로 바이트 정렬하는 겁니다. 디폴트로는 8바이트로 되어 있을겁니다.
         뭐 구조체 크기가 실제로 계산한 것과 맞지 않는 경우가 있는데..이 바이트 정렬 떄문입니다.
         따라서..반드시 맞아야 할 경우는... 위의 코드를 구조체 아래위로 써야 합니다.
//--------------------------------------------------------------------------

15.#pragma package
    . 사용법
      #pragma package(smart_init)
      #pragma package(smart_init, weak)
    . smart_init 아규먼트
      #pragma package(smart_init)는 패키지된 유닛들이 의존할 순서를 결정하기위해
      초기화 되어집니다.(패키지 소스파일 내에 기본적으로 포함됩니다.)
      일반적으로, 패키지들을 생성하는 .CPP 파일들에 #pragma package를 사용할 수
      있습니다.
      이 프라크마는 유닛을 콤파일하는기위한 초기의 순서에 영향을 미칩니다.
      초기화는 다음의 순서에 의하여 발생합니다.
      1. 만일 unitA가 unitB에 의존한다면 unitB는 반드시 unitA전에 초기화
         되어져야만하는 사용("uses")에 의존합니다.
      2. 링크의 순서(The link order)
      3. unit에서의 우선권의 순서.(Priority order within the unit.)

      보통의 .OBJ 파일들은(unit들로 생성하지 않은), 첫째로 우선권의 순서에 따라
      초기화가 일어나고서 링크가 됩니다. .OBJ 파일들의 링크 순서의 변경은
      글로발 오브젝트가 호출되어져 생성되는 순서에 의해 변경됩니다.

      다음의 예는 보통의 .OBJ 파일들과 unit들의 초기화에 어떤 차이점이 있는가를
      보여줍니다. 세개의 unit 파일들 A,B,C가 #pragma package(smart_init)를
      사용하여 "smart initialized"되고 우선권은 10, 20, 30의 값을 갖는다고
      예를 듭니다. 함수는 우선권의 값과 parent .OBJ에 의하여 이름지어져 a10,
      a20, a30, b10등과 같은 이름을 갖습니다. 세가지는 모두 unit들이며 A는 B와
      C를 사용하며 A,B,C의 순서로 링크되고 초기화의 순서는 다음과 같습니다.
           B10 B20 B30 C10 C20 C30 A10 A20 A30
      위와 같이 되었다면 .OBJ 파일들은 (unit들이 아니다)다음의 순서가 되어질
      것입니다.
           A10 B10 C10 A20 B20 C20 A30 B30 C30
      #pragma package(smart_init)를 사용한 .CPP 파일들은 또한 #pragma package
      (smart_init)를 정의한 .CPP 파일로부터 다른 .OBJ 파일들을 참조하는 #pragma
      link를 필요로 하며 unit에 의해 결정되어져야만 합니다. #pragma link는, 결정
      되지 않은 .OBJ는 라이브러리 등에 의하여 여전히 결정되어질 수 있도록 참조
      할 수 있습니다.
    . weak packages
      #pragma package(smart_init, weak)지시어는 .OBJ 파일이 패키지의 .BPI와
      .BPL 파일들에 정장되는 방법에 영향을 미칩니다. 만일 #pragma package(smart_
      init, weak)가 unit파일 내에 나타난다면 콤파일러는 가능하다면 BPL들로부터
      unit을 생략하고, 다른 에플리케이션이나 패키지에 의해 필요로 할 때면
      비 패키지화된(non-packaged) 로칼 복사본의 unit을 생성합니다. 유닛이 이
      지시어와 함께 콤파일 되었다는 것은 약하게 패키지화 되었음을 이야기 합니다.
      ("weakly packaged")
      #pragma package(smart_init, weak)는 동일한 외부 라이브러리(external librar
      y)들에 의존할수 있는 여러 패키지들 사이에서의 충돌을 제거하는데 사용되어
      집니다.  #pragma package(smart_init, weak) 지시어를 가지는 unit 파일들은
      글로발 변수들을 갖지 않아야 합니다.

//--------------------------------------------------------------------------
16.#pragma resource
    . 사용법
      #pragma resource "*.dfm"
    . 이 프라그마는 form unit에 의해 선정되어지는 파일로서 일치되는 .DFM 파일과
      헤더파일을 필요로 합니다. 이러한 모든 파일들은 IDE에 의해 관리되어집니다.
      만일 폼을 위한 다른 변수들을 필요로한다면 pragma resource가 사용되어지고난
      후에 즉시 선언되어져야만 합니다. 선언은 반드시 form이 되어져야만 합니다.
          TFormName *Formname;

//--------------------------------------------------------------------------
17.#pragma startup
    . 사용법
      #pragma startup function-name <priority>
      #pragma exit function-name <priority>
    . #pragma exit의 내용을 참조하십시요.

//--------------------------------------------------------------------------
18.#pragma warn
    . 사용법
      #pragma warn [+:-:.]www
    . warn지시어를 이용하면 특정 명령라인 옵션 -wxxx를 우선할 수 있습니다.
      #pragma warn -aus 스위치를 사용하면 함수 단위로 취급됩니다. 개별적인
      변수들을 위해서 함수 내부에서 경고를 off시킬수는 없습니다. 함수 전체를
      off시키거나 혹은 그렇지 않거나 둘중 하나입니다.
    ex)
    #pragma warn +xxx
    #pragma warn -yyy
    #pragma warn .zzz

    위의 예에서는 xxx경고문은 on되고 yyy경고문은 off되며 zzz경고문은 파일의
    컴파일이 시작할 때 갖고 있던 값으로 재저장됩니다.

//----- End of Document ---------------------------------------------------- 
19. #pragma once

 1. 한번 컴파일 되면 더 이상 컴파일 하지 않는다는 뜻입니다.
 여러개의 cpp파일이 있을때, 하나의 cpp화일이 수정되면, 그 화일만 컴파일하고
 나머지는 하지말아란 뜻이죠.   ( 여러 번 인클루드 되는 것을 컴파일러 차원에서 막아줌 )

 2. #ifndef ~ #endif 와 같은 역할을 한다.

 3. 매크로들의 중복 정의를 막고, 전역변수에 static 키워드를 써줄 필요가 없어짐.

 4. VC++ 에서는 되고, 다른 컴파일러는 안될 수도 있음.

 5. 분할 컴파일시

 

이것을 사용하면 빌드시 컴파일러에 의해 해당 파일을 단 한번만 열게 된다... 그러므로 컴파일 타임을 줄일 수 있고 모듈에서 가장 먼저 나온 #include문에서 해당 파일을 열게 되므로 재정의에 의한 오류를 방지할 수 있다...

 

예제)

// exam.h file

#ifndef __EXAM__

#define __EXAM__

...

#endif

->

// exam.h file

#pragma once

...

 

//--------------------------------------------------------------------------

20. #pragma data_seg

   dll에서 데이터 공유하고자 할 때 쓰임

  예) 실행파일에는 코드영역과 데이터영역이 구분되어 있다는 것은 아시지요.
┌───────┐
│                     │
│                     │
│   데이터영역   │
│                     │
│                     │
├───────┤
│                     │
│                     │
│     코드 영역   │
│                     │
│                     │
└───────┘
   이런 식의 그림은 아마 어디선가 많이 보셨을겁니다.(그림이 깨지네요.편집할 땐 제대로 보였는데...)
   이런 영역을 section이라고 하지요. 위의 설명에 나오는 section이라는 용어가 이것입니다.
   실제로 데이터 영역과 코드 영역외에도 exe나 dll에는 여러 section을 포함할 수 있습니다.
   이건 dumpbin이라는 툴로 살펴볼 수 있습니다.
   제 컴(Windows XP)에서 dumpbin c:\windows\notepadd.exe 를 해 보았더니

   2000 .data
   2000 .rsrc
   7000 .text

   이렇게 나오는 군요.

   여기서 .data에는 초기화된 데이터가 .rsrc에는 리소스들이, .text에 코드가 들어갑니다.
   이러한 .data, .rsrc, .text 등은 일반적으로 정해져 있는 것들입니다.
   위 MSDN의 설명에 있는 section-name이라는 것이 바로 .data, .rsrc, .text 등을 뜻하는 겁니다.
   즉, #pragma code_seg( .data ) 처럼 사용한다는 거지요.
   그리고 Specifies a code section where functions are to be allocated.라는 설명은 Specifies a section where functions or data or etc. are to be allocated. 이렇게 이해하면 더 나을 듯 하네요.

   그런데 이런 정해진 이름말고도 사용자가 새로운 영역을 정할 수 있습니다.

   #pragma data_seg("Shared")
   DWORD g_dwThreadIdPMRestore = 0;
   HWND g_hwnd = NULL;
   #pragma data_seg()

   이런 식으로 하면 g_dwThreadIdPMRestored와 g_hwnd가 디폴트 데이터 섹션인 .data에 배치되지 않고, Shared라는 이름으로 만들어진 섹션에 배치되는 것입니다.

 

//--------------------------------------------------------------------------

21. #pragma warning

    컴파일시에 어떤 조건(#if, #ifndef)에의해 개발자에게 어떤것을 알려주고 싶을 경우 사용.

    예) #pragma warning(disable:xxxx)
         지정된 xxxx번대의 경고 메세지를 디스플레이하는 것을 막는다. (xxxx는 번호)

          warning( disable : 4705 )

          : 특정 warnning 을 체크하지 않음, 이럴 경우 4705번 warnning은 나타나지 않는다

         #pragma warning(default:xxxx)
         지정된 xxxx번의 경고 메세지의 설정을 원래의 프로젝트 설정으로 복원한다

 

//--------------------------------------------------------------------------

22. #pragma code_seg

   MSDN에 있는 내용
   #pragma code_seg( ["section-name"[,"section-class"] ] )

   Specifies a code section where functions are to be allocated. The code_seg pragma specifies the default section for functions. You can, optionally, specify the class as well as the section name. Using #pragma code_seg without a section-name string resets allocation to whatever it was when compilation began.

 

//--------------------------------------------------------------------------

23. #pragma deprecated

    C#의 Obsolete attribute와 비슷한 의미입니다.
    즉, 경고가 발생한 클래스 혹은 메서드 등이 이후에는 지원되지 않음을 나타내는 의미입니다.
    그러므로 당장은 문제가 없습니다.

    컴파일러 specific 한..그런 옵션 지시자라고 보시면 됩니다.

 

//--------------------------------------------------------------------------

#pragma는 표준 C/C++ 문법인데 각 compiler 마다 다른 명령을 제공한다... 고로 Linux나 Unix의 cc에서는 작동하지 않을 수도 있다는 것! (Visual C++에선 언제나 call...)

 

  1. pragma 는 #로 시작하는 전처리구문 지시자 중 컴파일러에 종속적인 명령으로, 컴파일러에 특정한 옵션 명령을 내리기 위해 사용한다.

이것은 컴파일러에 종속적이기 때문에 컴파일러를 변경했을 경우 실행을 보장하지 못한다.

 

pragma 의 의미를 action 으로 많이들 알고 계시는데, 인터넷에서 제가 알아본바로는, 사전적인 의미는 "만능" 입니다.  어원설명 원문은, 아래를 참고하세요.

 

A pragma (from the Greek word meaning action) is used to
direct the actions of the compiler in particular ways, but
has no effect on the semantics of a program (in general).

Pragmas are used to control listing, to define an object
configuration (for example, the size of memory), to control
features of the code generated (for example, the degree of
optimization or the level of diagnostics), and so on.

Such directives are not likely to be related to the rest of
the language in an obvious way. Hence the form taken should
not intrude upon the language, but it should be uniform.

Thus, the general form of pragmas is defined by the language.
They start with the reserved word pragma followed by a pragma
identifier, optionally followed by a list of arguments enclosed
by parentheses, and terminated by a semicolon.

The overall syntax of the pragma identifier and arguments is
similar to that of a procedure call. Pragmas are allowed at
places where a declaration or a statement is allowed; also at
places where other constructs that play the role of declarations
(for example clauses) are allowed.

 

pragma 는 컴파일에게 그 뒤에오는 내용에 따라 어떤일을 하라는 전처리명령입니다.

 

C++는 컴파일하고 나면 함수의 이름이 바뀌게 되는데 이것을 name mangling이라고 합니다.

그래서 만일 dll로 작성한 함수를 불러 사용하게 되면 같은 이름을 찾을 수 없다는 오류가 나게 됩니다.

만일 dll에서 printString라는 함수를 만들었다고 가정을 합니다. 그러면 컴파일하고 난 후의 함수의 이름은 printString@@YAXXZ와 같은 형태로 만들어 집니다.
그런데 이 함수를 불러 사용하는 곳은 printString이라는 것만 알지 위의 것과 같은 알지 못합니다. 그래서 #pragma라는 키워드를 사용해서 name mangling을 방지하게 되는 것입니다.
이 mangling된 이름을 찾기 위해서는 VC++ Tool에 보면 depends를 실행하고 만들dll을 drag&drop하면 이것을 찾을 수 있습니다.

  1. pragma comment(linker, "/export:printString=?printString@@YAXXZ")와 같이사용하면됩니다.

즉 #pragma는 원래의 함수 이름을 c++의 암호명에 대한 별칭으로 추가하도록 지시하며 링커에게 /export옵션을 전달해 주는것입니다.
그리고 dll에서는 원래의 함수를 export(수출??말이 좀 이상하지만 대부분 이렇게 많이 쓰니까..^^)해야 하고 이 함수를 호출하는 쪽에서는 import(수입) 옵션을 써 주어야만 함수를 제대로 호출할 수 있습니다.

 

pragma 앞에 #이 있는 걸 보면 아시겠지만 pragma는 precompiler입니다.

compile할 때 platform이 틀려지거나 cpu가 틀려지거나 할 때 compile option을 주게 됩니다.

vc++을 써보셨으면 아실텐데, project settings( ALT+F7 )에서 c/c++ tab에 보면 project options이 있습니다. link tab에도 project options가 있죠.

pragma가 바로 그런 역할을 하는 precompiler입니다.

vc++이야 ide니까 project settings라는 편한 환경을 지원하지만 만약 code호환성을 생각한다면 pragma를 쓰는 게 좋죠.

 

< api나 mfc의 구분과 관계없이 해당 컴파일러에서 사용하는 명령 >

#pragma warn- // warning 디스어블

  1. pragma warn+ // warning 인에이블
  2. pragma opt- // 최적화 안 함
  3. pragma opt+ // 최적화 함
  4. pragma savereg- // 레지스터 저장 안 함
  5. pragma savereg+ // 레지스터 저장 함
  6. pragma library mylib.lib // 링크 라이브러리 지정

 
※  MSDN  /  "Programming Applications for Microsoft Windows"(Jeffrey Richer 저) 참조


Posted by 세모아
,




출처 : http://sysnet.pe.kr/100121443615


sos나 psscor 같은 도구들이 나오면서 닷넷 응용 프로그램의 디버깅에서 windbg 위치가 다소 상향되었지요. ^^

개발자 PC에서 재현되지 않는 문제에 대해 디버깅을 해야 하는 상황이라면, windbg를 이용해서 현재 실행중인 프로그램에 연결해서 살펴보는 것이 최선일 것입니다. 하지만, 만약 그것이 "운영 서버"라면 차선책으로 "풀 덤프"를 고려해 볼 수 있습니다. (또는, 저처럼 서툰 windbg 사용자라면 고객 앞에서 버벅거리는 것보다 회사에 와서 차근 차근히 검색도 해가면서 풀어보는 것이 더욱 현실적인 이유가 될 수 있겠고. ^^)

이번 글에서는, 바로 "풀 덤프"를 남기는 다양한 방법 중에서 가장 현실성이 있다고 생각되는 4가지 정도만 살펴보겠습니다.

1. Windbg 를 이용하는 방법


Windbg.exe를 실행하고 "File" / "Attah to a Process..." 메뉴를 선택해서 대상 프로세스에 디버거를 연결할 수 있습니다. 그 상태에서 명령행을 통해 다음과 같이 입력하면 "Full Dump"를 생성할 수 있습니다.

.dump /ma C:\temp\windbg_test.dmp


덤프를 뜨는 순간에 재미있는 현상을 하나 발견할 수 있는데요. Visual Studio 로 기본 생성한 윈폼 프로젝트를 실행한 경우, Working Set 메모리 크기가 26KB 정도에 불과했는데 덤프를 뜨자마자 120,488 KB로 증가했습니다. (풀 덤프 파일의 크기는 Working Set 메모리와 비슷하게 생성됩니다.)

2. ADPlus를 이용하는 방법


ADPlus는 "Debugging Tools for Windows"를 설치하면 Windbg와 함께 설치되는 유틸리티입니다. 사실, 덤프 과정을 보면 Windbg의 내부 덤프 기능을 공유해서 사용한다고 볼 수 있습니다.

실행 방법 및 예제 결과는 아래와 같습니다.

C:\...[생략]...>adplus -hang -pn WindowsFormsApplication1.exe -o D:\dump
Starting ADPlus
********************************************************
* *
* ADPLus Flash V 7.01.002 02/27/2009 *
* *
* For ADPlus documentation see ADPlus.doc *
* New command line options: *
* -pmn <procname> - process monitor *
* waits for a process to start *
* -po <procname> - optional process *
* won't fail if this process isn't running *
* -mss <LocalCachePath> *
* Sets Microsoft's symbol server *
* -r <quantity> <interval in seconds> *
* Runs -hang multiple times *
* *
* ADPlusManager - an additional tool to facilitate *
* the use of ADPlus in distributed environments like *
* computer clusters. *
* Learn about ADPlusManager in ADPlus.doc *
* *
********************************************************

Attaching to 5628 - WindowsFormsApplication1 in Hang mode 02/05/2011 22:46:07
Logs and memory dumps will be placed in D:\dump\20110205_224607_Hang_Mode


위와 같이 실행한 경우, 출력 폴더로 설정된 "D:\dump\20110205_224607_Hang_Mode" 경로에 가보면 로그 파일이 생성되는데, 내부를 살펴보면 adplus가 결국 Windbg 의 ".dump" 명령어와 동일하게 실행하는 것임을 알 수 있습니다.

0:000> *--- Configuring hang mode commands ---
0:000> .dump -u /ma /c HangMode ${AdpDumpDir}\FULLDUMP_HangMode_${AdpProcName}_.dmp
Creating D:\dump\20110204_210725_Hang_Mode\FULLDUMP_HangMode_WindowsFormsApplication1.exe__2b94_2011-02-04_21-07-25-956_1698.dmp - mini user dump
Dump successfully written



Windbg 에 비해 ADPlus 는 몇몇 상황에 대해 간단한 옵션으로 처리해주는 기능이 있어 사용이 더 편하긴 합니다. 예를 들어 예외가 발생한 시점에 덤프를 남기는 기능을 명령행 인자로 제공합니다.

물론, windbg로도 예외가 발생한 시점에 덤프를 남기는 것이 가능합니다.

예외 발생시 Minidump 생성 - WinDBG
; http://www.sysnet.pe.kr/2/0/472


ADPlus를 일종의 "Windbg 래퍼"라고 봐도 될 것 같습니다. ^^

3. Task Manager


운영 서버에, "Debugging Tools for Windows" 도구를 설치한다는 것은 때로 부담일 수 밖에 없습니다. 다행히 Windows Vista/7/2008/R2 에서는 "작업 관리자"를 이용해서 덤프를 뜨는 것이 가능해졌는데, 방법도 무척 간단합니다. Task Manager 를 실행하고 대상 프로세스를 선택한 다음 마우스 오른쪽 버튼을 눌러 "Create Dump File" 메뉴를 선택하면 됩니다.



덤프파일이 생성되는 위치는 다음과 같습니다.

C:\Users\[로그인 사용자 계정\AppData\Local\Temp\[응용 프로그램 이름].DMP 



4. Process Dump(ProcDump.exe)를 이용하는 방법


Sysinternals 의 Process Dump 도 나름 유용하게 사용할 수 있습니다. 제 경험상, Windows 2008 이후의 운영체제에서는 작업 관리자로 풀 덤프를 남기는 반면, Windows 2003에서는 "Process Dump"를 즐겨 사용합니다. 왜냐하면, "Debugging Tools for Windows"처럼 설치과정을 요하지도 않고 단 하나의 procdump.exe 파일만 다운로드 받아서 실행하면 되기 때문에 편리함면에서는 거의 작업관리자 수준에 가깝기 때문입니다.

다음은 실행 중인 프로그램에 대한 덤프를 남기는 예입니다.

D:\Dump>procdump -ma windowsformsapplication1.exe D:\dump\procdump.dmp

ProcDump v3.01 - Writes process dump files
Copyright (C) 2009-2010 Mark Russinovich
Sysinternals - www.sysinternals.com

Writing dump file D:\Settings\desktop\current\dump\procdump.dmp...
Dump written.


게다가, 가끔은 Task Manager로는 불가능한 덤프가 있는데요. 예를 들어 실행하자마자 예외가 발생해서 종료를 해버리는 경우가 있는데, 이런 경우에는 다음과 같은 옵션을 구성해서 덤프를 남길 수 있습니다.

procdump -e -ma -t test.exe


어찌 보면, adplus.exe 못지 않은 덤프 도구가 바로 ProcDump 입니다.




도구들을 사용하면서 한가지 주의할 점이 있다면 Mini dump를 남기는 옵션이 다양하다는 점인데, 문제는 닷넷 응용 프로그램의 경우 최소 크기의 미니 덤프 파일로는 거의 살펴볼 수 있는 것들이 없습니다. 이 때문에 풀 덤프의 용량이 너무 크다고 해서 최소 크기의 미니 덤프를 고객에게 요청하면 거의 99%는 다시 풀 덤프를 요청하게 될 것입니다. (위의 글에서 설명한 4가지 도구의 옵션들은 모두 힙영역까지 포함한 덤프를 뜨게 됩니다.)

참고로, Visual Studio 로도 풀 덤프를 뜰 수 있습니다. ^^

VS.NET 2005 를 이용한 미니덤프 파일 분석 (1) 
; http://www.sysnet.pe.kr/2/0/318



'Programming' 카테고리의 다른 글

rtsp url  (0) 2016.03.27
The Windows Programming Model  (0) 2015.12.19
FFmpeg [펌] [libav-user] Save AVFrame to jpg file  (0) 2015.04.27
Posted by 세모아
,

Win7 64bit에서 Windows Service를 대상으로 아래처럼 C# 코드를 사용하면,(일반 WinForm은 아래코드가 먹힘)

string fullpathFile = System.IO.Directory.GetCurrentDirectory() + "\\" + "Log\\";

결과는 C:\Windows\SysWOW64\Log  이다.

------------------------------------

Windows Service를 대상으로 아래처럼 C# 코드를 사용해야, Service의 exe파일이 있는 폴더가 제대로 나옴.

string fullpathFile = AppDomain.CurrentDomain.BaseDirectory + "\\" + "Log\\"; 

 


 

출처: http://mvcp.tistory.com/entry/C에서-실행되는-폴더-알아-내기-윈도우-서비스-테스트-프로젝트

 

 

C#에서 실행되는 폴더 알아 내기(윈도우 서비스, 테스트 프로젝트, ...)

 

참조 URL
  1. http://stackoverflow.com/questions/1658518/getting-the-absolute-path-of-the-executable-using-c

 

 

 

 

 

윈도우 서비스나 테스트 프로젝트에서 실행을 하면 내가 코딩한 DLL이 위치하는 폴더와 실제 실행되는 프로세스의 위치가 상이한 경우가 발생할 수 있다. 이럴경우 기대한 경과와 다르게 에러가 발생할 수 있다. 예로 파일을 읽어 오는 부분이나 환경설정을 읽어 오는 부분에서 경로가 맞지 않아 에러가 발생할 수 있다. 아래 코드와 같이 실행되는 프로세스나 어셈블리의 위치를 알아 내는 방법이 몇가지로 제공을 하고 있다.

 

 

var path1 = Environment.CurrentDirectory; var path2 = Directory.GetCurrentDirectory();
var path3 = new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName; var path4 = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); var path5 = Thread.GetDomain().BaseDirectory; var path6 = AppDomain.CurrentDomain.BaseDirectory;

[코드1] 폴더 위치 가져오기


위 "코드1"에서와 같이 폴더를 알아 낼 수 있지만 path1과 path2는 실행되는 프로세스를 중심으로 폴더를 알아내고 path3 ~ path6은 어셈블리를 기준으로 폴더를 알아내는 구조로 되어 있다.

 

 

위와 같은 코드를 윈도우 서비스에서 실행하면 아래와 같은 폴더의 위치가 달라지게 된다.

 

path1 : C:\Windows\System32

path2 : C:\Windows\System32

path3 : C:\User\xxxx\Visual Studio\Project\xxxx

path4 : C:\User\xxxx\Visual Studio\Project\xxxx

path5 : C:\User\xxxx\Visual Studio\Project\xxxx

path6 : C:\User\xxxx\Visual Studio\Project\xxxx

 

 

위와 같이 값을 확인 할 수 있을 것이다.

 

필요에 따라 값을 가져오는 방법을 달리 하여 가져오면 될 것이다.

 

 

 

 

 

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

[펌] 효과적인 C# 메모리 관리 기법  (0) 2016.03.10
C# delegate & event 작성순서- 20150604  (0) 2015.10.21
[펌] [STAThread]  (0) 2015.09.02
Posted by 세모아
,

 

 

 

Posted by 세모아
,

. Hostinger의 Website - auto installer에서  mediawiki 설치

  (mediawiki 버전이 1.23.6 에 주의)


. Advanced PHP Configuration 에서  php 5.5로 변경

d

Extension:Mantle 설치 (LocalSettings.php 수정 포함) :
    https://www.mediawiki.org/wiki/Extension:Mantle

Extension:MobileFrontend 설치 (LocalSettings.php 수정 포함) :
   https://www.mediawiki.org/wiki/Extension:MobileFrontend
. 모바일기기에서 보면 정상 으로 보임.



* 설치후 최종화면



Posted by 세모아
,




Posted by 세모아
,







* Working copy와 최신 revision의 비교

 - 파일/폴더 1개 : Diff 메뉴

 - 폴더 전체 : Check for modification


* commit하려면 conflict이 발생할 수 있으므로,

폴더전체를 비교한 후에 commit 이 좋다. My)

 <방법>

 1. 폴더 전체를 Check for modification 선택하여 최신리버전과의 차이있는 파일/폴더 리스트 창이 나옴

 2. 위 1번창의 항목들을 모두 선택 후 우버튼클릭하여 commit 선택

 3. 1번창에서 항목별(파일/폴더)의 Author를 working copy의 것과 비교하거나

    더블클릭하여 내용을 비교하여 commit여부를 결정하고

   이를 위 2번창의 체크박스에서 선택/해체함

 4. 3번을 반복후에 2번창에서 message입력후 commit 실시


* Visual Studio 2013에서 사용중인 방법 

 (기타환경: TortoiseSVN설치)//15.10.3

 . Pending Changes라는 창에서 필요한 파일들만 다중 선택하여 commit 하면  편리.

 - 너무 오래되었으면, update 하고 commit 하라고 나옴.

   Update하면 merge도 자동되면서 update 됨.

   이 걸로 테스트해보고 문제 없으면 commit 하면 됨.


Posted by 세모아
,

[펌] [STAThread]

Programming/C# 2015. 9. 2. 10:43

출처: http://grandmaster.tistory.com/9



자주 보이지만 무슨일을 하는지 모르고 항상 궁금하다고 생각됬던 [STAThread]

오늘은 찾아보았다..이놈의 귀차니즘..

네이버 지식in의 답변부터 듣도록 해보자

[System.STAThread]
[STAThread]

최초 어플리케이션 스레드 모델을 단일 스레드 어파트먼트로 지정하는 것을 의미한다.



아직까지는 먼소린지 모르겠다...

그래서 데브피아에 물어봤다.

Q. 

 [STAThread]

 

라는걸 찾아보다 보니

 

C# 코드에서 [STAThread] 가 의미하는 바는 : 
기본적으로, VS .NET에 의해 만들어진 응용 프로그램의 Main()메소드에는 [STAThread] 어트리뷰트가 첨가되어 있다. 
이 어트리뷰트는 해당 응용 프로그램이 COM형식을 이용하는 경우에 (단지 이 경우에만 해당하는 것인데) 해당 응용 프로그램이 
단일 스레드 아파트(single threaded apartment, STA) 모델로 설정되어야 한다는 것을 런타임에게 알려주는 역할을 한다. 
해당 응용 프로그램에서 COM 형식을 이용하지 않는다면, [STAThread] 어트리뷰트는 무시되기 때문에 삭제해도 무방하다

<출처 : 서비의 다락방 ( http://www.yunsobi.com )>

 
라고하는데요 com형식이 무엇인지 잘모르겠네요 검색해도 너무많은 결과가 나와서 어떤건지 잘모르겠구요.

 

도대체 com형식이 무엇인지  [STAThread] 가 왜필요한지 조금 쉽게 설명해주실분 계신가요?


A.


우선 com 형식이란 com 객체를 말합니다. 그리고 .net은 기본적으로 MTA(멀티쓰레드아파트먼트)로 동작됩니다. 그런데 전통적으로 com 객체는STAThread 기반이어서 이를 명시해줘야 합니다. 만약 명시하지 않고 단일쓰레드아파트먼트 기반인 com 객체를 사용시엔 런타임때 오류가 발생합니다. 이를 직접 눈으로 확인해보시려면 간단하게 form위에 웹브라우져 컨트롤을 올리고 [STAThread] 애트리뷰트를 주석처리하고 실행해보세요.




글쎄..........com객체라는걸 알아야 알수 있을꺼 같다...

그래서 com객체를 조사해보았다.

com 은요 Component Object Model 의 약자이지만 일반적으로 컴포넌트라고 발음합니다. 정확한 해석은 아니지만 줄여서 부르는 셈이지요. 

기존의 프로그램을 살펴보면 여러가지 문제점이 있었지요. 그 많은 문제점중 하나가 예를 들어 dll 의 이름 문제점을 들수 있는데요. 많이 보셨을 겁니다. ****.dll 이라고 하는 파일을요. 이는 메인 프로그램에서 다 처리하지 못하거나 특정한 모듈을 따로 처리하기 위해 만든 일종의 메인에 딸린 하부적인 실행파일이라고 볼수 있는데(일단은 dll 도 실행파일입니다. 자체 실행파일은 아닙니다만...) 이는 dll 의 이름을 가지고 접근하고 있습니다. 하지만 소프트 웨어를 만드는 회사가 많은만큼 같은 dll의 이름을 가진 실행파일들이 많아지고, 또 그것을 인스톨 하면서 윈도우 폴더에 복사하면 문제가 생기지요. 전혀 다른 회사에서 개발한 dll 이 이름만 같고 전혀 다른 기능을 하는데도 이를 모르고 덮어 쓰기 해버리면... 

이를 해소하기 위한 하나의 방법이 컴포넌트 입니다. 사용해 보셨는지는 모르지만 컴포넌트는 GUID 라는 128비트의 일종의 아이디 비슷한게 있습니다. 이걸로 접근하므로 이름의 문제로 인한 충돌이 일어나지 않는 것입니다.

여기까지는 com 을 사용하는 목적중의 하나만 예를 든것이고 com 을 한마디로 정의하자면 객체 지향적 모듈개발이라고 정의하고 싶네요. 아니 이건 제가 개인적으로 내린 정의입니다. 제가 느낀 바로는요...^^ 다른 분들은 어떻게 생각하실지 모르겠습니다. 

어떤 프로그램을 짤때, 예를 들어 메신져를 생각해 보겠습니다. 메신져를 짜려면 접속자 파악, 파일전송, 메시지 보내기 등의 작업들이 있습니다. 그런데 이경우 com 을 사용하지 않고 짜려면 한사람이 설계한 프로그램에 다른 사람들이 달라붙어서 거기에 맞춰가면서 프로그래밍 할 수 밖에 없습니다. 상당히 비효율적이고 또 그렇게 프로그래밍한 소스는 그 메신져에서 밖에 쓸수가 없죠. 하지만 컴포넌트화 해버리면... 쉽게 말해서 모듈별로 따로 만들어 버리면, 넌 메시지 보내는 루틴을 만들어, 난 파일 전송하는 루틴을 만들게 그리고 만들때는 com 으로 만들자. 라고 하면 쉽게 끝나는 거죠. 조립만 하면 되기 때문이죠. 

여기까지 말씀을 드리면 그렇다면 com 은 일정한 형식을 가지고 있어야 한다. 왜냐하면 조립을 하려면 표준형식이 있어야 하기 때문이다. 라고 생각하시는 분이 계실지 모릅니다. 네 맞습니다. 일정한 형식을 맞춰서 프로그램을 짜야 합니다. 그래서 com 을 하시는분들 보면 머리붙잡고 쥐어 뜯습니다.(으아..미쵸미쵸 하면서요...^^ 저도 그런 기억이 있죠) com 의 표준형식을 공부하는게 쉬운게 아니거든요. 그리고 객체지향적 설계가 필요합니다. 그래서 com 에 관해서는 바이블도 따로 나와 있습니다. 공부하시려면 com 은 바이블을 꼭 사셔야 합니다. 아니 꼭은 아니더라도 사시는게 좋습니다. 인터넷으로 공부하려고 하시면... 한계가 보일겁니다.

이외에 책에 적어진 com 의 장점을 적어보겠습니다. 이하는 책에 적혀있는 내용입니다.

1. 이식성이 강하다 : 새로운 프로그램에 대한 강력한 이식성은 개발기간을 단축시킨다.
2. 유연성이 있다 : 컴으로 작업한 소프트웨어를 업그레이드 하고자 할 경우 단순히 업그레이드된 컴포턴트만 복사하면 되고 업그레이드 되지 않은 컴포넌트를 사용하지 않은 경우에도 잘 수행된다.
3. 재 컴파일할 필요가 없다. 
4. 객체지향 개발 방법론(com) 은 프로그램의 구현방법을 획기적으로 바꾸었다. 
5. com 의 경우 플랫폼에 독립적이다. 

출처 : 네이버



어느정도 이해는 됬지만 깊숙하게 알기위해서는 좀더 내공을 길러야 할듯하다.



결론 : 지우면 혼난다.


Posted by 세모아
,

출처: http://agebreak.blog.me/60059787064



관리 코드와 네이티브 코드의 상호 운용성을 위한 최상의 방법
Jesse Kaplan
어떻게 보면 2009년 초반에 이런 칼럼이 MSDN Magazine에 등장하는 것이 이상하게 느껴질 수도 있습니다. 관리 코드와 네이티브 코드의 상호 운용성은 그 정도의 차이는 있을지 모르지만 2002년 버전 1.0부터 Microsoft .NET Framework에서 꾸준히 지원되어 왔기 때문입니다. 또한 API 및 도구 수준의 자세한 지원 문서도 수천 페이지는 찾을 수 있습니다. 그럼에도 불구하고 어떠한 경우에 interop을 사용하고 아키텍처와 관련하여 어떤 점을 고려하고 어떤 interop 기술을 사용해야 하는지를 설명하는 전반적이고 포괄적인 아키텍처 지침은 찾기 어렵습니다. 이 칼럼은 이러한 틈을 메우기 위한 것입니다.


관리되는 네이티브 Interop의 적합한 용도
관리되는 네이티브 interop을 어떤 경우에 사용해야 하는지에 대해서는 자료가 별로 없고 그나마 있는 자료들도 서로 내용이 상이합니다. 게다가 실제 검증 없이 지침을 작성된 지침도 있습니다. 본론을 시작하기 전에 이 칼럼의 모든 내용은 interop 팀이 다양한 규모의 내부 및 외부 고객을 지원하는 과정에서 실제 경험을 바탕으로 작성되었음을 밝힙니다.
이러한 경험을 취합하면서 interop의 성공적인 사용 사례이자 interop의 사용 유형을 잘 보여주는 세 가지 제품을 예로 들었습니다. Visual Studio Tools for Office는 Office용 관리되는 확장 가능 도구 집합으로, interop하면 가장 먼저 떠오르는 응용 프로그램입니다. 이 응용 프로그램은 관리되는 확장 또는 추가 기능 구현이 가능한 대규모 네이티브 응용 프로그램이라는 interop의 일반적인 사용 방식을 보여 줍니다. 다음으로 관리되는 응용 프로그램과 네이티브 응용 프로그램의 혼합형으로 완전히 새롭게 개발된 Windows Media Center를 들 수 있습니다. Windows Media Center는 주로 관리되는 코드를 사용하여 개발되었고 일부분(TV 튜너 및 기타 하드웨어 드라이버와 직접 작동하는 부분)만 네이티브 코드를 기반으로 합니다. 마지막으로 기존 네이티브 코드 기반에서 새로운 관리되는 기술을 활용하여 차세대 사용자 환경을 제공하려 하는 응용 프로그램의 예로서 Expression Design이 있습니다. 여기서 새로운 관리되는 기술이란 WPF(Windows Presentation Foundation)을 말합니다.
이 세 응용 프로그램은 interop을 사용하는 가장 중요한 이유 세 가지를 보여 줍니다. 첫째는 기존 네이티브 응용 프로그램에 관리되는 확장성을 제공하는 것이고, 둘째는 대부분의 응용 프로그램이 가장 기본적인 수준의 코드는 네이티브 코드로 작성하면서 관리 코드의 이점을 활용하도록 하는 것이고, 셋째는 기존 네이티브 응용 프로그램에 차별화된 차세대 사용자 환경을 추가하는 것입니다.
예전에 제공된 지침에서는 이러한 경우에 단순히 전체 응용 프로그램을 관리 코드로 작성할 것을 권장하고 있습니다. 많은 사람들이 이러한 지침을 따르려고 했지만 그러지 못한 것만 봐도 대부분의 기존 응용 프로그램의 경우 전체 응용 프로그램을 관리 코드로 작성하는 것이 불가능함을 입증한다고 하겠습니다. interop은 개발자가 네이티브 코드에 대해 기존에 투자한 자산을 유지하면서 새로운 관리되는 환경의 이점을 활용하는 데 없어서는 안 될 기술입니다. 다른 이유로 응용 프로그램을 다시 작성할 계획이라면 관리 코드로 작성할 수도 있겠지만 일반적으로 단지 새로운 관리되는 기술을 사용하고 interop을 사용하지 않기 위해 응용 프로그램을 다시 작성하는 것은 바람직하지 않습니다.


Interop 기술: 세 가지 선택
.NET Framework에서 사용 가능한 주요 interop 기술에는 세 가지가 있으며 interop에 사용하는 API의 형식과 경계 제어의 요구 사항과 필요성에 따라 세 가지 기술 중 하나를 선택하게 됩니다. 플랫폼 호출 또는 P/Invoke는 주로 관리되는 코드에서 네이티브 코드로의 interop 기술로서 관리 코드에서 C 스타일 네이티브 API를 호출할 수 있도록 합니다. COM interop은 관리 코드에서 네이티브 COM 인터페이스를 사용하거나 관리되는 API에서 네이티브 COM 인터페이스를 내보낼 수 있도록 합니다. 마지막으로 C++/CLI(이전의 관리되는 C++)는 관리되는 C++ 컴파일 코드와 네이티브 C++ 컴파일 코드가 혼합되어 있는 어셈블리를 만들 수 있도록 해주며, 관리 코드와 네이티브 코드를 연결하는 다리 역할을 합니다.


Interop 기술: P/Invoke
P/Invoke는 세 가지 기술 중 가장 간단하며 주로 C 스타일 API에 관리 코드에 대한 액세스를 제공할 목적으로 설계되었습니다. P/Invoke를 사용하는 경우 각 API를 개별적으로 래핑해야 합니다. 따라서 래핑할 API 수가 적고 서명이 그다지 복잡하지 않은 경우에 적합합니다. 그러나 관리되지 않는 API에 동등한 관리되는 항목이 없는 인수(변수-길이 구조, void *s, 중첩되는 공용 구조체 등)가 많으면 P/Invoke를 사용하기가 어려워집니다.
.NET Framework BCL(기본 클래스 라이브러리)에 포함된 여러 가지 API 예제는 수많은 P/Invoke 선언 주위의 두꺼운 래퍼일 뿐입니다. .NET Framework의 기능 중 관리되지 않는 Windows API를 래핑하는 기능 대부분은 P/Invoke를 사용하여 작성됩니다. 사실 Windows Forms도 거의 전체가 P/Invoke를 사용하는 ComCtl32.dll을 기반으로 합니다.
P/Invoke를 훨씬 쉽게 사용할 수 있도록 하는 유용한 리소스가 몇 가지 있습니다. 첫째로, 웹 사이트 pinvoke.net에는 CLR interop 팀의 Adam Nathan이 처음 설정한 Wiki가 있는데, 이 Wiki에는 많이 사용되는 다양한 Windows API에 대해 사용자가 만든 서명이 많이 포함되어 있습니다.
Visual Studio에서 pinvoke.net을 쉽게 참조할 수 있도록 해주는 유용한 Visual Studio 추가 기능도 있습니다. 개발자의 자체 라이브러리에 있는 API든, 다른 사람이 만든 API든, pinvoke.net에서 다루지 않는 API를 위해 interop 팀에서는 P/Invoke Interop Assistant라는 P/Invoke 서명 생성 도구를 발표했습니다. 이 도구는 헤더 파일을 기준으로 네이티브 API에 대한 서명을 자동으로 생성합니다. 다음 스크린샷은 이 도구의 작동 모습을 보여 줍니다.
P/Invoke Interop Assistant에서 서명 만들기


Interop 기술: COM Interop
COM interop은 관리 코드에서 COM 인터페이스를 사용하거나 관리되는 API를 COM 인터페이스로 제공할 수 있도록 해줍니다. TlbImp 도구를 사용하면 특정 COM tlb와 통신하기 위한 관리되는 인터페이스를 제공하는 관리되는 라이브러리를 생성할 수 있습니다. TlbExp는 반대의 작업을 수행하며 관리되는 어셈블리의 ComVisible 형식에 해당하는 인터페이스를 제공하는 COM tlb를 생성합니다.
응용 프로그램 내부적으로 또는 확장 모델로서 이미 COM을 사용하는 경우에 COM interop이 적합한 솔루션이 될 수 있습니다. 또한 관리되는 코드와 네이티브 코드 간에 COM 의미 체계를 정확히 일치하도록 유지하는 가장 손쉬운 방법이기도 합니다. CLR은 기본적으로 Visual Basic 6.0과 동일한 COM 규칙을 따르기 때문에 특히 Visual Basic 6.0 기반 구성 요소와 상호 작용하는 경우에 COM interop이 적합합니다.
반면 응용 프로그램에서 내부적으로 COM을 사용하고 있지 않거나, COM 의미 체계를 완전히 동일하게 유지할 필요가 없고 성능이 응용 프로그램에 적합하지 않은 경우에는 COM interop이 적합하지 않습니다.
COM interop을 관리되는 코드와 네이티브 코드를 잇는 교량으로 사용하는 응용 프로그램의 대표적인 예로 Microsoft Office를 꼽을 수 있습니다. Office에는 확장 메커니즘으로서 COM이 오래 전부터 사용되어왔고 VBA(Visual Basic for Applications) 또는 Visual Basic 6.0에서 많이 사용되었기 때문에 COM interop을 활용하기에 더할 나위 없이 좋습니다.
원래 Office는 관리되는 개체 모델로 TlbImp와 얇은 interop 어셈블리를 전적으로 사용합니다. 그러나 시간이 지나면서 VSTO(Visual Studio Tools for Office) 제품이 Visual Studio를 기반으로 하게 되고 이 칼럼에서 설명하는 여러 가지 원칙을 통합하여 보다 기능이 풍부한 개발 모델을 제공하게 되었습니다. 현재는 BCL의 많은 부분이 P/Invoke를 기반으로 한다는 사실을 잊기 쉬운 것처럼 VSTO 제품을 사용할 때 COM interop이 VSTO의 기반이 된다는 사실을 잊는 경우가 많습니다.


Interop 기술: C++/CLI
네이티브 환경과 관리되는 환경 사이의 교량 역할을 하도록 설계된 C++/CLI는 관리 C++ 코드와 네이티브 C++ 코드를 같은 어셈블리는 물론 같은 클래스에도 컴파일할 수 있도록 하고 어셈블리의 두 부분 간에 표준 C++ 호출을 만듭니다. C++/CLI을 사용하는 경우 어셈블리에서 관리되는 환경으로 구현할 부분과 네이티브 환경으로 구현할 부분을 선택하게 됩니다. 그 결과로 MSIL(모든 관리되는 어셈블리에 포함된 Microsoft Intermediate Language)과 네이티브 어셈블리 코드가 혼합된 어셈블리가 생성됩니다. C++/CLI는 interop 경계를 거의 완벽하게 제어할 수 있는 매우 강력한 interop 기술이지만 경계에 대해 개발자의 제어권을 지나치게 강요한다는 단점이 있습니다.
C++/CLI는 정적 형식 검사가 필요하고 성능이 엄격하게 요구되고 종료를 예측할 수 있어야 하는 경우에 교량 역할로서 적합합니다. 요구 사항에만 맞다면 일반적으로 P/Invoke 또는 COM interop을 사용하는 편이 간단합니다. 특히 C++에 익숙하지 않은 개발자에게는 더욱 편리합니다.
C++/CLI를 고려할 때는 몇 가지에 유의해야 합니다. 먼저 C++/CLI를 사용하여 처리 속도가 빠른 버전의 COM interop을 제공하려는 경우 COM interop은 자동으로 처리하는 작업이 많기 때문에 C++/CLI보다 느리다는 점을 알아야 합니다. 따라서 응용 프로그램에서 COM을 한정적으로만 사용하고 충실도 높은 COM interop이 필요하지 않다면 문제가 되지 않습니다.
그러나 COM 사양의 많은 부분을 사용하는 경우 필요한 COM 의미 체계를 C++/CLI 솔루션에 다시 추가하려면 작업량이 많을 뿐만 아니라 COM interop이 제공하는 성능보다 향상되지도 않습니다. 몇몇 Microsoft 팀에서 이런 전철을 밟았다가 문제를 깨닫고 다시 COM interop을 사용한 사례가 있습니다.
C++/CLI 사용과 관련하여 두 번째로 중요하게 고려해야 할 사항은 C++/CLI가 관리되는 환경과 네이티브 환경을 잇는 교량의 역할만 할 뿐이며 응용 프로그램의 대부분을 작성하기 위한 기술이 아니라는 점입니다. 물론 C++/CLI로 응용 프로그램의 대부분을 작성할 수도 있지만 순수하게 C++ 또는 C#/Visual Basic 환경에서 작성할 때보다 개발자 생산성이 훨씬 떨어지고 부팅 시에 응용 프로그램 실행 속도가 현저하게 느려집니다. 따라서 C++/CLI를 사용할 때는 필요한 파일만 /clr 스위치를 사용하여 컴파일하고 순수한 관리되는 어셈블리 또는 순수한 네이티브 어셈블리의 조합을 사용하여 응용 프로그램의 핵심 기능을 작성해야 합니다.


Interop 아키텍처 고려 사항
응용 프로그램에 interop을 사용하기로 결정하고 사용할 기술도 선택했다면 솔루션을 설계할 때 API 디자인이나 interop 경계에 대한 코드를 작성하는 개발자 환경 등 상위 수준에서 고려해야 할 사항이 있습니다. 또한 네이티브-관리 코드 전환과 이러한 전환이 응용 프로그램에 미치는 영향도 고려해야 합니다. 마지막으로 수명 관리를 고려하고 관리되는 환경의 가비지 수집 환경과 네이티브 환경의 수동/결정적 수명 관리 사이의 격차를 메울 수단이 필요한지를 결정해야 합니다.


API 디자인 및 개발자 환경
API 디자인과 관련해서는 스스로 몇 가지 질문에 대한 답을 구해야 합니다. 어떤 개발자들이 interop 계층에 대해 코드를 작성하게 되며 그러한 개발자의 환경을 개선하거나 경계 구축 비용을 최소화하기 위해 최적화해야 하는가? 이 경계에 대해 코드를 작성하는 개발자와 네이티브 코드를 작성하는 개발자가 동일한가? 아니면 회사의 다른 개발자들인가? 아니면 응용 프로그램을 확장하거나 서비스로 사용하는 타사 개발자들인가? 개발자의 숙련도는 어떤 수준인가? 네이티브 패러다임에 익숙한가 아니면 관리되는 코드를 작성하는 데만 익숙한가?
이러한 질문에 대한 답을 구하면 네이티브 코드의 매우 얇은 래퍼와 내부적으로 네이티브 코드를 사용하는 기능이 풍부한 관리되는 개체 모델 사이에서 적절한 지점을 결정하는 데 도움이 됩니다. 얇은 래퍼의 경우 모든 네이티브 패러다임이 명확히 드러나고 개발자가 네이티브 API에 대해 코드를 작성하고 있다는 사실과 경계를 확실하게 인식하게 됩니다. 반면 두꺼운 래퍼를 사용하는 경우 네이티브 코드가 기반이 된다는 사실이 드러나지 않습니다. BCL의 파일 시스템 API는 중요한 관리되는 개체 모델을 제공하는 매우 두꺼운 interop 계층의 좋은 예입니다.


성능과 Interop 경계의 위치
공연히 응용 프로그램을 최적화하느라 시간을 허비하지 않으려면 interop 성능이 문제가 되는지 여부를 확인하는 것이 중요합니다. 응용 프로그램에서 성능이 중요한 부분에 interop이 사용되기 때문에 주의를 기울여야 하는 경우가 많습니다. 그러나 사용자의 마우스 클릭 동작에 대한 응답으로 interop을 사용하고 interop 전환이 수십, 수백, 수천 번 발생하더라도 사용자에게 지연을 유발하지 않는 응용 프로그램도 많습니다. 따라서 interop 솔루션의 성능을 높이려면 interop 전환 횟수와 각 전환 시에 전달되는 데이터 양을 줄여야 합니다.
관리되는 환경과 네이티브 환경 간에 일정한 데이터 양으로 발생하는 일정 횟수의 interop 전환은 기본적으로 그에 해당하는 고정 비용을 발생시킵니다. 이 고정 비용은 선택하는 interop 기술에 따라 다르지만 해당 기술이 필요해서 선택했다면 변경할 수 없을 것입니다. 즉, 경계의 빈번한 전환을 줄이고 경계를 넘어 전달되는 데이터의 양을 줄이는 데 초점을 맞추어야 합니다.
이러한 목표를 달성하는 방법은 응용 프로그램에 크게 좌우됩니다. 그러나 많은 개발자가 성공적으로 활용한 일반적인 전략으로서 경계의 한쪽에 자주 사용되고 데이터 양이 많은 인터페이스를 정의하는 코드를 작성하여 격리된 경계를 이동하는 방법이 있습니다. 여기서 핵심은 매우 자주 사용되는 인터페이스에 대한 호출을 일괄 처리하는 추상 계층을 작성하거나 응용 프로그램 논리에서 경계를 넘어 이 API와 상호 작용해야 하는 부분을 이동하고 입력 및 결과만 경계를 넘어 전달하는 것입니다.


수명 관리
수명 관리에 있어서 관리되는 환경과 네이티브 환경의 차이점은 interop 고객에게 가장 큰 걸림돌이 되곤 합니다. .NET Framework의 가비지 수집 기반 시스템과 네이티브 환경의 수동 및 결정적 시스템 간의 근본적인 차이점은 진단하기 어려운 형태로 나타나는 경우가 많습니다.
interop 솔루션에서 가장 먼저 나타나는 문제는 관리되는 개체가 관리되는 환경에서 사용이 끝난 후에도 네이티브 리소스를 오랫동안 보유하게 되는 문제입니다. 이는 네이티브 리소스가 매우 부족하여 호출자가 사용이 끝나는 즉시 해제해야 하는 경우에 문제가 됩니다. 대표적인 예로 데이터베이스 연결을 들 수 있습니다.
이러한 리소스가 부족하지 않다면 가비지 수집기가 개체의 종료자를 호출하여 종료자가 암시적으로 또는 명시적으로 네이티브 리소스를 해제하도록 하면 됩니다. 반면 리소스가 부족한 경우에는 관리되는 dispose 패턴이 매우 유용할 수 있습니다. 네이티브 개체를 관리되는 코드에 직접적으로 노출하는 대신 네이티브 개체 주위에 최소한 얇은 래퍼라도 만들어 IDisposable을 구현하고 표준 dispose 패턴을 따르도록 해야 합니다. 이렇게 하면 리소스 고갈이 문제가 될 경우에 관리되는 코드에서 이러한 개체를 명시적으로 정리하고 사용이 끝나는 즉시 리소스를 해제할 수 있습니다.
응용 프로그램에 많이 영향을 미치는 두 번째 수명 관리 문제는 개발자가 까다로운 가비지 수집 문제로 오해하는 경우가 많습니다. 즉, 메모리 사용량이 계속 증가하지만 어떤 이유에서인지 가비지 수집기의 실행 빈도가 낮고 개체가 활성화된 상태로 유지되는 것입니다. GC.Collect에 대한 호출이 계속 추가되어 문제가 더욱 심각해지기도 합니다.
일반적으로 이러한 문제의 근본 원인은 크기가 매우 작은 관리되는 개체가 매우 큰 네이티브 데이터 구조를 계속 사용하면서 활성화된 상태로 유지되기 때문입니다. 그 결과로 가비지 수집기가 불필요하거나 효과가 없는 수집 작업에 시간을 낭비하지 않기 위해 자체 튜닝됩니다. 또한 가비지 수집을 추가로 실행할지 여부를 결정할 때 프로세스의 현재 메모리 사용량은 물론 각각의 가비지 수집으로 확보되는 메모리 양까지 확인합니다.
하지만 이러한 상황에서 가비지 수집기가 실행되면 확보되는 관리되는 메모리 양만 인식하므로 각각의 수집으로 확보되는 메모리 양이 적다고 판단하고 그러한 작은 개체를 정리함으로써 전반적인 메모리 부족 상황을 크게 개선할 수 있다는 사실은 인식하지 못합니다. 때문에 메모리 사용량은 계속 증가하지만 가비지 수집 실행 빈도는 점점 더 낮아지는 현상이 나타납니다.
이 문제는 이러한 작은 관리되는 래퍼에 따른 실질적인 네이티브 리소스 비용에 대한 힌트를 가비지 수집기에 제공함으로써 해결할 수 있습니다. .NET Framework 2.0에는 이러한 용도로 사용되는 API가 한쌍 추가되었습니다. 명시적으로 리소스를 해제하는 대신 dispose 패턴을 추가하는 데 사용하는 것과 동일한 형식의 래퍼를 부족한 리소스에 사용하여 가비지 수집기에 힌트를 제공하는 데 재활용할 수 있습니다.
이 개체의 생성자에서 GC.AddMemoryPressure 메서드를 호출하여 네이티브 개체의 네이티브 메모리에서 소모되는 대략적인 비용을 전달합니다. 그런 다음 개체의 종료자 메서드에서 GC.RemoveMemoryPressure를 호출하면 됩니다. 이 두 호출은 가비지 수집기가 이러한 개체의 실제 비용과 해당 개체를 해제했을 때 확보되는 실제 메모리를 인식하는 데 도움을 줍니다. 이 경우 Add/RemoveMemoryPressure에 대한 호출의 균형을 완벽하게 유지하는 것이 중요합니다.
관리되는 환경과 네이티브 환경의 수명 관리에 있어서 나타나는 세 번째 차이점은 개별 리소스나 개체의 관리보다 전체 어셈블리 또는 라이브러리와 관련이 있습니다. 네이티브 라이브러리는 응용 프로그램에서 사용을 마쳤을 때 쉽게 언로드할 수 있지만 관리되는 라이브러리는 자동으로 언로드되지 않습니다. 대신 CLR에는 개별적으로 언로드할 수 있고 언로드 시에 실행 중인 모든 어셈블리, 개체 및 스레드를 정리하는 AppDomains라는 격리 단위가 있습니다. 네이티브 응용 프로그램을 작성하는 개발자가 사용이 끝난 추가 기능을 언로드하는 데 익숙한 경우 관리되는 추가 기능마다 AppDomains를 사용하면 개별 네이티브 라이브러리를 언로드할 때와 동일한 유연성을 얻을 수 있습니다.


질문이나 의견이 있으면 clrinout@microsoft.com으로 보내시기 바랍니다.


Jesse Kaplan은 현재 Microsoft CLR 팀에서 프로그램 관리자로서 관리되는 환경/네이티브 환경의 상호 운용성을 담당하고 있으며 이전에는 호환성과 확장성을 담당하기도 했습니다.


Posted by 세모아
,

아래의 TRACE(.) 안될때는, OutputDebugString(.) 사용


#include <windows.h>


 char buf[256];

 sprintf(buf,"%d \n",nFingerCount);

 OutputDebugString(buf);




TRACE(.) 위한 define문 :

#define ENABLE_TRACE  // Can depend on _DEBUG or NDEBUG macros

---------------------------------

출처: http://blog.daum.net/pg365/45


DebugView는 프로그램이 실행되면서 출력하는 디버그 메시지를 표시하는 디버깅 도구입니다. Visual Studio에서 프로그램을 디버깅으로 실행(F5 키를 눌러 실행)할 때, TRACE가 내보내는 메시지는 통합환경 하단의 Output 창에 출력 됩니다. 하지만, 디버그로 컴파일 된 실행 파일을 Visual Studio가 없는 환경에서 실행할 때 출력되는 디버그 메시지들은 어떻게 볼 수 있을까요? 이런 경우 사용 가능한 도구가 DebugView 입니다.

 

아래와 같이, 코드내에 TRACE를 써서 두 변수 x, y 값을 디버그 메시지로 출력해 보겠습니다.

 

int x = 5;
double y = 5.5;

 

TRACE ("x = %d, y=%f\n", x, y);

 

Visual Studio 상에서 F5 키를 눌러 디버깅으로 실행하면 하단 Output 창에 x, y 변수의 값이 찍히는 것이 보입니다. 디버깅을 위해 주로 사용하던 익숙한 방법입니다.

 

 

 

이번에는, 코드는 디버그로 컴파일 되어 있지만, Ctrl+F5 키를 눌러 디버깅 없이 실행해 보겠습니다. 즉, Visual Studio가 코드 디버깅에 개입하지 않고 실행 파일만 따로 실행함을 의미합니다. 물론, DebugView는 먼저 실행되고 있어야 합니다.

 

그렇다면, 다음 그림과 같이 DebugView 창에서 TRACE로 내보낸 디버그 메시지가 표시됩니다.

 

 

실제로 DebugView를 실행해 보면, 정작 현재 디버깅 중인 프로그램의 디버깅 메시지만 출력되는 것이 아니라 다른 프로그램들이 내보내는 디버깅 메시지들도 뒤섞여 출력되는 것을 볼 수 있습니다. 이럴 때는 필터 옵션을 잘 사용하는 것이 좋겠습니다.

 

DebugView에 대한 소개 글을 읽어 보면, 로컬 PC의 디버깅 뿐만이 아니라 네트워크 상에 있는 다른 컴퓨터도 디버깅 가능하고. 커널-모드 디버깅과  WIN32 디버그 출력도 표시할 수 있다고 합니다.


아래 페이지에서 DebugView 프로그램을 무료로 다운받아 사용하실 수 있습니다.

http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx

Posted by 세모아
,

출처: https://lists.libav.org/pipermail/libav-user/2010-August/005159.html


Hoang Ong ngochoanghcm at gmail.com 
Fri Aug 6 19:33:28 CEST 2010


  Libavcodec can do it. This is the code from Anand, and his code only 
use ffmpeg libs.

First I also think about libjpeg and found a jpeg library including 
Anroid.mk to port to Android NDK (I'm working in android platform), but 
I search in ffmpeg source code and see that it has the CODEC_ID_JPEG, so 
I think it does not need to use libjpeg.

int WriteJPEG (AVCodecContext *pCodecCtx, AVFrame *pFrame, int FrameNo){
         AVCodecContext         *pOCodecCtx;
         AVCodec                *pOCodec;
         uint8_t                *Buffer;
         int                     BufSiz;
         int                     BufSizActual;
         int                     ImgFmt = PIX_FMT_YUVJ420P; //for the 
newer ffmpeg version, this int to pixelformat
         FILE                   *JPEGFile;
         char                    JPEGFName[256];

         BufSiz = avpicture_get_size ( 
ImgFmt,pCodecCtx->width,pCodecCtx->height );

         Buffer = (uint8_t *)malloc ( BufSiz );
         if ( Buffer == NULL )
                 return ( 0 );
         memset ( Buffer, 0, BufSiz );

         pOCodecCtx = avcodec_alloc_context ( );
         if ( !pOCodecCtx ) {
                  free ( Buffer );
                  return ( 0 );
                  }

         pOCodecCtx->bit_rate      = pCodecCtx->bit_rate;
         pOCodecCtx->width         = pCodecCtx->width;
         pOCodecCtx->height        = pCodecCtx->height;
         pOCodecCtx->pix_fmt       = ImgFmt;
         pOCodecCtx->codec_id      = CODEC_ID_MJPEG;
         pOCodecCtx->codec_type    = CODEC_TYPE_VIDEO;
         pOCodecCtx->time_base.num = pCodecCtx->time_base.num;
         pOCodecCtx->time_base.den = pCodecCtx->time_base.den;

         pOCodec = avcodec_find_encoder ( pOCodecCtx->codec_id );
         if ( !pOCodec ) {
                  free ( Buffer );
                 return ( 0 );
                  }
         if ( avcodec_open ( pOCodecCtx, pOCodec ) < 0 ) {
                  free ( Buffer );
                  return ( 0 );
                 }

                pOCodecCtx->mb_lmin        = pOCodecCtx->lmin = 
pOCodecCtx->qmin * FF_QP2LAMBDA;
         pOCodecCtx->mb_lmax        = pOCodecCtx->lmax = 
pOCodecCtx->qmax * FF_QP2LAMBDA;
         pOCodecCtx->flags          = CODEC_FLAG_QSCALE;
         pOCodecCtx->global_quality = pOCodecCtx->qmin * FF_QP2LAMBDA;

         pFrame->pts     = 1;
         pFrame->quality = pOCodecCtx->global_quality;
         BufSizActual = avcodec_encode_video( 
pOCodecCtx,Buffer,BufSiz,pFrame );

         sprintf ( JPEGFName, "%06d.jpg", FrameNo );
         JPEGFile = fopen ( JPEGFName, "wb" );
         fwrite ( Buffer, 1, BufSizActual, JPEGFile );
         fclose ( JPEGFile );

         avcodec_close ( pOCodecCtx );
         free ( Buffer );
         return ( BufSizActual );
  }

I searched on google and saw that many people ask the same question but 
not found the answer yet. So this will help for developer in the first 
use ffmpeg libs

Hoang Ong

On 8/6/2010 12:20 AM, Michael Chisholm wrote:
> Here we go again... :)  Can't you use libavcodec's builtin mjpeg encoder?
>
> On 8/5/2010 11:38 AM, Glen Ruedinger wrote:
>> You need libjpeg.
>>
>> On Thu, Aug 5, 2010 at 10:58 AM, Hoang Ong<ngochoanghcm at gmail.com>  
>> wrote:
>>
>>>   Hi everybody,
>>>
>>> I can get the AVFrame and output it to a raw image file (.ppm) I try 
>>> to use
>>> sws_scale to convert that image to variety of image type to save it 
>>> as a jpg
>>> file but it not work. I think it not a way to do.
>>>
>>> I dont have much knowledge about image processing. So anybody know 
>>> how to
>>> use ffmpeg's libs to convert AVFrame to jpg please help me. The code 
>>> I used
>>> to save frame as a ppm file like this:
>>>
>>> void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
>>> {
>>>     FILE *pFile;
>>>     char szFilename[32];
>>>     int  y;
>>>
>>>     // Open file
>>>     sprintf(szFilename, "frame.ppm", iFrame);
>>>     pFile=fopen(szFilename, "wb");
>>>     if(pFile==NULL)
>>>         return;
>>>
>>>     // Write header
>>>     fprintf(pFile, "P6\n%d %d\n255\n", width, height);
>>>
>>>     // Write pixel data
>>>     for(y=0; y<height; y++)
>>>         fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, 
>>> pFile);
>>>
>>>     // Close file
>>>     fclose(pFile);
>>> }
>>>
>>> Regards
>>> Hoang Ong
>>> _______________________________________________
>>> libav-user mailing list
>>> libav-user at mplayerhq.hu
>>> https://lists.mplayerhq.hu/mailman/listinfo/libav-user
>>>
>> _______________________________________________
>> libav-user mailing list
>> libav-user at mplayerhq.hu
>> https://lists.mplayerhq.hu/mailman/listinfo/libav-user
>>
>
>
> _______________________________________________
> libav-user mailing list
> libav-user at mplayerhq.hu
> https://lists.mplayerhq.hu/mailman/listinfo/libav-user



More information about the libav-user mailing list

Posted by 세모아
,


Posted by 세모아
,

Visual C++의 .NET 프로그래밍

How to: Parse Strings Using the Split Method (C++/CLI)


Example


// regex_split.cpp

// compile with: /clr

using namespace System;


int main(array<System::String ^> ^args)

//가능함. int main()

{

   String^ delimStr = " ,.:\t";

   Console::WriteLine( "delimiter : '{0}'", delimStr );

   array<Char>^ delimiter = delimStr->ToCharArray( );

   array<String^>^ words;

   String^ line = "one\ttwo three:four,five six seven";


   Console::WriteLine( "text : '{0}'", line );

   words = line->Split( delimiter );

   Console::WriteLine( "Number of Words : {0}", words->Length );

   for (int word=0; word<words->Length; word++)

      Console::WriteLine( "{0}", words[word] );


   return 0;

}

결과

delimiter : ' ,.:       '

text : 'one     two three:four,five six seven'

Number of Words : 7

one

two

three

four

five

six

seven



Posted by 세모아
,
C++/CLI Console 프로그램 코드에서
 MessageBox 사용시 다음 에러 발생하면 해결책은?
 - 에러 : /LNK2028: unresolved token(0A00003D) "extern "C" int __stdcall MessageBoxW .....
 - 해결책 : Edit your project properties, find the Linker Inputs option,
             and add kernel32.lib user32.lib advapi32.lib which are the usual libraries needed by Win32 code.


Posted by 세모아
,

CLR - Windows Forms Application이 VC++2013에는 없음.


참고 : 

이를 해결하는 편법이 인터넷에 있음. 내 Delicious에 저장함

아래가 그 링크임

http://mcn-www.jwu.ac.jp/~yokamoto/openwww/vsg/VCpp2012FormApp/


아래가 위 링크의 내용을 pdf로 저장.

VC++2010



VC++2013


Posted by 세모아
,