요즘 윈도우 7을 사용하다보면 윈도우 창 관리 기능이 마우스를 통해 쉽고 직관적으로 할 수 있다는 점이 마음에 들었습니다.

그래서 이런 기능들을 윈도우  XP에서도 할 수 있으면 좋지 않을까 생각 해보고 윈도우7의 창 관리 기능을 지원하는 응용 프로그램을 개발해 보면 어떨가 생각해보고 만들어 보고 있습니다.

우선은 창을 끌어 상단으로 옮기면 전체화면, 오른쪽 끝이나, 왼쪽 끝으로 움직인 후 놓으면 화면의 절반을 만들어 주는 기능입니다.

우선 프로토 타입으로 만들어 보았습니다. 꽤나 유사한 기능을 할 수 있도록 만들수 있었습니다.

우선 반응이나 가능성을 보고 본격적으로 프로그램 제작을 해보도록 하겠습니다. 이와 비슷한 기능의 프로그램들이 있는가도 찾아봐야 겠네요.




아직 안정성이나 편의성에 문제가 있어서 공개하지 않고 사용할 수준이 되고 반응도 좋다면 공개하도록 하겠습니다.

멜론 스나이퍼 이후 취직한 후 별로 시간이 나지 않아 개인적인 개발을 할 수 없었다가,  이직을 하는 관계로 약간의 시간이 나서 만들어본 프로그램입니다.


저작자 표시 비영리 동일 조건 변경 허락
신고

우선 우분투를 설치합니다. 우분투에서 빌드하기를 권장하기 때문에 VirtualBox에 우분투를 면저 설치 하였습니다.


하드 용량은 약간 넉넉하게 잡으세요. 빌드 도중에 용량이 부족할지도 모르겠네요.

공식 사이트에서도 아직 바이너리 형태로 배포하지 않습니다. 소스를 받아 빌드 해야 합니다. 시간이 꽤나 걸립니다. 특히 가상 머신에서 빌드 한다면 시간이 배로 걸리겠죠.

http://www.chromium.org/ 이 곳이 공식 사이트입니다.

http://sites.google.com/a/chromium.org/dev/chromium-os/building-chromium-os/build-instructions
빌드와 설치 방법이 나와있습니다. 차례대로 하시면 큰 어려운 없이 설치가능합니다.

아직 초기 버전이라 설치 환경들이 구축되지 않고 명령어 스크립트로 설치 가능합니다.

이미지 설치 방법중 USB에 설치 할 때 아래와 같이 하라고 합니다. USB드라이브 뿐만 아니라 일반 HDD도 아래와 같은 방법으로 설치 가능합니다. 단, 하드 디스크를 포맷하게 되므로 기존 데이터는 사라지게 됩니다. 주의 하세요.

./image_to_usb.sh --from=~/chromiumos/src/build/images/SUBDIR --to=/dev/USBKEYDEV

Virtual Box에서는 새로운 하드디스크 이미지를 만들어 우분투로 부팅합니다. 위 명령어로 설치할 하드를 지정하면 매우 빠른 시간에 설치가 가능합니다. 아무런 설정도 할 수 없으며 디스크에 그대로 이미지를 복사하며 설치 합니다.


설치된 하드디스크를 보면 위와 같이 파티션이 조정되어 있습니다.
첫번째 sdb1은 상태 저장용으로 말 그대로 상태를 저장하는 파티션이라 추정됩니다.
sdb2는 스왑 파티션으로 추정됩니다.
sdb3는 / 파티션으로 크롬 OS가 설치되어 있습니다. 하지만 읽기 전용으로 구성되어 있습니다.

나머지는 그대로 비워둡니다.



Virtual Box에서 새로운 가상 머신을 만든 다음 Chromium os를 설치한 HDD를 Primary로 지정한 후 부팅하면 다음과 같은 화면을 볼 수 있습니다.

google 계정의 ID와 Password로 로그인이 가능합니다.


로그인한 첫 화면입니다. Gmail이 바로 로그인 상태로 뜹니다. 한글은 출력을 잘되나 입력은 지원되지 않는 상태입니다.


첫번째 탭을 클릭하면 로그인 하라는 페이지가 뜹니다.

로그인 하면 위와 같이 애플리케이션을 실행 할 수 있는 메뉴가 뜹니다.

웹으로 사용할 수 있는 웹 애플리케이션 들입니다.


일정 탭입니다. 일정을 바로 접근 가능합니다.


네이버 페이지도 잘뜹니다. 오른쪽 상단에 위치한 버튼들에 기능입니다.
첫번째는 베터리 관련입니다.


두번째는 인터넷을 온오프 할 수 있는 메뉴입니다. 온/오프만 가능하면 아무런 셋팅을 할 수 없습니다.


마지막 메뉴는 크롬 브라우저에 자체 메뉴입니다.

모든 메뉴를 살펴 보았지만 종료 메뉴 조차도 없습니다. 종료는 ACPI의 종료를 바로 이용해서 끕니다. 종료 또한 무지하게 빠릅니다.

현재로서는 사용가능한 수준은 아닌듯 합니다. 우선 고정 IP설정이 불가능합니다. 터미널로 직접 셋팅해야 할 듯 합니다.

기본적인 설정을 할 수 있는 부분도 없습니다. 모니터 해상도이거나, 여러 운영체제에 관한 설정도 할 수 없었습니다.

또한 가상 머신이 아닌 직접 설치하였을 때도 반응이 매우 느립니다.(아마 그래픽 카드 드라이버가 제대로 잡히지 않은것으로 판단됩니다.) 그러나 부팅 및 종료는 매우 빠릅니다.

또한 운영체제에 모든 기능이 인터넷이 되어야만 사용가능합니다. 인터넷이 되지 않는 경우 아무것도 할 수 없게됩니다.

MID나 넷북용으로 사용할 수 있겠지만, 인터넷 접속이 안된다면 아무것도(로그인 조차) 제대로 할 수 없는 문제 점이 있습니다.

이런 문제점이 있지만, 어디서나 나의 작업 환경을 그대로 가져 갈 수 있다는 장점과 함께, 장치에 종속되지 않고 동일한 작업을 할 수 있다는 장점이 있게되죠..

일반 사용자가 설치해 사용하는 운영체제라기 보다 윈도우 임베디드나, 윈도우 모바일처럼 하드웨어 제조사에서 모든 셋팅해서 판매하는 제품같은데서 써야할 것 같은 느낌이 많이 듭니다.

언젠가는 유용해 질 수 있겠지만, 무선(WiFi, 3G, Wibro 등) 인터넷 환경이 잘 구축된 우리나라에서 조차 사용하기 어려울 듯 하니 지금 뉴스 기사에서처럼 지금 당장 막강한 영향력을 미치기는 힘들듯 합니다.

운영체제도 안정화되고, 인터넷 환경도 어는정도 개선된 상태에서 성장 가능하리라 판단됩니다. 특히 국내 통신사, 인터넷 서비스 업체들의 서비스 및 요금에 대한 발전도 중요하리라 생각됩니다.

지금 떠오른 생각이지만 IPTV의 셋탑 박스에 운영체제로 들어가도 괜찮을 것 같네요. 인터넷에 항상 연결되어 있고, PC같은 OS환경보단 크롬OS같은 환경이 더 구축하기도 쉬우면 많은 서비스를 제공해 줄테니까요.
저작자 표시 비영리 동일 조건 변경 허락
신고

멀티미디어 파일들을 읽기 위해서는 대부분 복잡한 포맷형식 때문에 이미 만들어진 라이브러리들을 통하여 읽는 것이 일반적이다. 하지만 PCM형식의 WAV파일은 비교적 간단하기 때문에 손쉽게 읽을 수 있을 것이라 생각된다.

WAV파일의 DFT변환을 위해 WAV파일의 저장된 내용을 읽을 필요가 있었는데.. 실제 음파의 값들을 읽을수 있는 라이브러리를 찾다가 직접 WAV파일을 읽는 프로그램을 작성하더라도 큰 어려움이 없을 것같아 만들어 보았다.

우선  PCM WAV파일의 포맷부터 보자..

 

이름

형식

Chunk Head

Chunk ID

4 Bytes ASCII String

“RIFF” - MS에서 사용하는 멀티미디어를 저장하는 포맷으로 항상 RIFF라 생각하면 됩니다.

Chunk Size

4 Bytes Little Endian

전체파일의 총 길입니다.

Format

4 Bytes ASCII String

“WAVE” - WAV파일일 경우

Sub Chunk 1

Sub Chunk 1 ID

4 Bytes ASCII String

“fmt “ - WAV파일일 경우

Sub Chunk 1 Size

4 Bytes Little Endian

Sub Chunk1의 길이

Audio Format

2 Bytes Little Endian

1 – PCM

Number of Channels

2 Bytes Little Endian

1 – Mono

2 – Stereo

Sample Rate

4 Bytes Little Endian

8000, 22000, 44000…… 등등

Byte Rate

4 Bytes Little Endian

(Sample Rate) *  (Number of Channels) * (Bit Per Samples) / 8

Block Align

2 Bytes Little Endian

(Number of Channels) * (Bit Per Samples) / 8

Bit Per Sample

2 Bytes Little Endian

8 또는 16

Extra Pram Size(선택적)

2 Bytes Little Endian

PCM의 경우는 사용하지 않음

Extra Pram(선택적)

N Bytes

확장 옵션을 위해

Sub Chunk 2

Sub Chunk 2 ID

4 Bytes ASCII String

“data”

Sub Chunk 2 Size

4 Bytes Little Endian

Sub Chunk 2의 크기

Data

N Bytes

실제 사운드 데이터

스테레오의 경우 좌 샘플 하나 우 샘플 하나씩 번갈아 가며 저장됨.


PCM의 경우 압축을 사용하지 않으므로 매우 간단하게 읽을 수 있다.

수치 값들은 모두 Little Endian으로 저장되며 모두 Unsigned 값들이다. 실수만 하지 않는 다면 간단하게 읽을 수 있을 것이다.

이곳으로 가면 조금더 자세한 WAV형식에 대한 설명을 볼 수 있습니다.
http://ccrma.stanford.edu/courses/422/projects/WaveFormat/

C#으로 작성해 보았으며 PCM형식의 WAV파일들의 값을읽습니다. 아 그리고 아래 소스는 실행가능 한 코드가 아니고 프로그램의 일부입니다..

또한 스트레오의 필요가 없어서 스트레오의 경우 모노로 바꾸어 읽어 버립니다..

=====================================================================================

using
System;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using System.Windows;
using System.Windows.Forms;

namespace WaveFile

{

    public class Wave

    {

       // string filePath;

        const string ChunkID = "RIFF";

        const string Format = "WAVE";

        const string Subchunk1ID = "fmt ";

        const string Subchunk2ID = "data";

        const int PCM = 1;

        public const int SampleUnit = 1024;

        bool successToOpen = false;

        //Stream waveFileStream;

                            //Offset    size

        string chunkId;     //0         4

        uint chunkSize;     //4         4       전체파일 크기

        string format;      //8         4

        string subchunk1ID; //12        4

        uint subchunk1Size; //16        4

        uint audioFormat;   //20        2

        uint numChannels;   //22        2

        uint sampleRate = 8000;    //24        4 

        uint byteRate;      //28        4       == SampleRate * NumChannels * BitsPerSample/8

        ushort blockAlign;    //32        2       == NumChannels * BitsPerSample/8

        uint bitsPerSample; //34        2       8 bits = 8, 16 bits = 16, etc.

        string subchunk2ID;   //36        4

        uint subchunk2Size; //40        4       == NumSamples * NumChannels * BitsPerSample/8

 

        const int chunkDescriptorSize = 12;

        const int subchunk2HeadSize = 8;

        private string fileName;

 

        private Stream pcmStream = new MemoryStream();

 

        public uint TotalLength

        {

            get { return (this.subchunk2Size-8) / this.BlockAlign ; }

        }

 

        public uint NumChannels

        {

            get { return this.numChannels; }

        }

 

        public uint SampleRate

        {

            get

            {

                return this.sampleRate;

            }

        }

 

        public bool SuccessToOpen

        {

            get

            {

                return successToOpen;

            }

        }

 

        public bool isStreo()

        {

            if (this.numChannels == 2)

                return true;

            return false;

        }

 

        public uint BlockAlign

        {

            get

            {

                return this.blockAlign;

            }

        }

 

        public uint BitsPerSample

        {

            get { return this.bitsPerSample; }

        }

 

        public string FileName

        {

            get{return this.fileName;}

            set { this.fileName = value; }

        }

 

        //현재 위치를 하나 읽음

        public int readSample()

        {

            BinaryReader br = new BinaryReader(this.pcmStream);

            if(br.BaseStream.Position >= br.BaseStream.Length) return 0;

            if (this.isStreo())

            {

                if (this.bitsPerSample == 8)

                {

                    return (int)((br.ReadSByte() + br.ReadSByte())/2);

                }

                else if (this.bitsPerSample == 16)

                {

                    return (int)((br.ReadInt16() + br.ReadInt16()) / 2);

                }

 

            }

            else

            {

                if (this.bitsPerSample == 8)

                {

                    return (int)br.ReadSByte();

                }

                else if (this.bitsPerSample == 16)

                {

                    return (int)br.ReadInt16();

                }

            }

            return 0;

        }

 

        public uint Subchunk2Size

        {

            get{ return this.subchunk2Size;}

        }

 

        public int FrameSize

        {

            get { return (int)(TotalLength / Wave.SampleUnit+1); }

       

        }       

           

          

        //오픈시 헤드를 읽음

        public Wave(Stream stream)

        {

            stream.Seek(0, SeekOrigin.Begin);

            byte[] buffer = new byte[stream.Length];

            stream.Read(buffer, 0, (int)stream.Length);

            this.pcmStream.Write(buffer, 0, (int)stream.Length);

            this.pcmStream.Seek(0, SeekOrigin.Begin);

           

            /////////////////////////////////

            // The "RIFF" chunk descriptor //

            /////////////////////////////////

            this.chunkId = readAscii4Byte(this.pcmStream);

            if (Wave.ChunkID != this.chunkId)

            {

                this.pcmStream.Close();

                //successToOpen = false;

            }

 

            BinaryReader br = new BinaryReader(this.pcmStream);

            this.chunkSize = br.ReadUInt32();

 

            this.format = readAscii4Byte(this.pcmStream);

            if (Wave.Format != this.format)

            {

                this.pcmStream.Close();

                return;

            }

 

            /////////////////////////

            // The "fmt" sub-chunk //

            /////////////////////////

            this.subchunk1ID = readAscii4Byte(this.pcmStream);

            if (this.subchunk1ID != Wave.Subchunk1ID)

            {

                this.pcmStream.Close();

                return;

            }

 

            this.subchunk1Size = br.ReadUInt32();

            this.audioFormat = br.ReadUInt16();

            this.numChannels = br.ReadUInt16();

            this.sampleRate = br.ReadUInt32();

            this.byteRate = br.ReadUInt32();

            this.blockAlign = br.ReadUInt16();

            this.bitsPerSample = br.ReadUInt16();

 

            //부가 정보가 있을 경우 넘김

            if (16 != this.subchunk1Size)

            {

                br.ReadBytes((int)this.subchunk1Size - 16);

            }

 

            //////////////////////////

            // The "data" sub-chunk //

            //////////////////////////

            this.subchunk2ID = readAscii4Byte(this.pcmStream);

            if (this.subchunk2ID != Wave.Subchunk2ID)

            {

                this.pcmStream.Close();

                return;

            }

           

            this.subchunk2Size = br.ReadUInt32();

 

            this.successToOpen = true;

           

            this.dftArray = new int[Wave.SampleUnit];

            //br.Close();

            return;

        } 

        private string readAscii4Byte(Stream fs)

        {

            byte[] buf = new byte[5];

 

            fs.Read(buf, 0, 4);

            buf[4] = 0;

            ASCIIEncoding asciiEn = new ASCIIEncoding();

            return asciiEn.GetString(buf, 0, 4);

           

        } 

 

    }

}


신고
c#, pcm, RIFF, wav

wget명령어를 GUI를 통해 사용할 수 있는 프로그램입니다.
허접하기는 하지만 다운로드 스크립트 또는 배치 파일을 내보낼 수 있는 기능이 있으며

데비안 패키지버젼과 윈도우 버젼을 포함 하고 있습니다. 윈도우 버젼에는 wget을 포함하였습니다.

http://wget-gui-kiwi.sourceforge.net

허접하지만 경험이 될 수 있을 것이라 생각하여 계속 진행 하였습니다. 아무튼 잘 되었으면 좋겠네요..

사용자 삽입 이미지

사용자 삽입 이미지

신고

wget의 강력한 기능에 놀라서--;; wget을 윈도우 상에서도 이용할 수 없을까 해서 윈도우용을 찾아보니 윈도우용으로 컴파일된 것도 있고, 메뉴을을 따라 하면 손쉽게 윈도우용으로 컴파일 할 수있었습니다.

wget의 강력한 기능을 윈도우에서 사용하려고 했으나?? Command Line기반의 프로그램은 윈도우에서 사용하기 매우 불편했고, 별로 손도 가지 않더군요.

그래서 GUI 툴이 없을까 찾아보니 몇개 있더군요.. 하지만 User Interface가 모두 맘에 안들더군요.. 그래서 언젠가 한번 만들어 봐야지 생각만 하고 있었는데..

오픈소스 챌린지 행사를 하더군요.. 거기에 힘입어 개발 하게 되었습니다..

현재 Debian Package(deb)를 릴리즈 했고 소스 포지에 등록 되어 있습니다.

http://sourceforge.net/projects/wget-gui-kiwi/

사용자 삽입 이미지

최종 목표는 11월 중순까지 (영문판, 한글판) X (rpm, deb, windows install )판을 만드는게 목표인데.. 시간상 어느 하나를 포기해야 할지도 모르겠네요..

글래도 이왕 시작한거 입상 못하더라도 최선을 다해서 최종 산출물을 완성해야 겠지요..ㅎㅎ

사용자 삽입 이미지

신고
Qt는 크로스 컴파일이 가능한 GUI개발 라이브러리이다.

자바처럼 한번의 소스코드 개발로 여러 운영체제에서 동일하게 작동한다. 자바는 컴파일도 한번만 하면 바이트코드 프로그램이 완성되지만..

Qt의 경우는 운영체제 별로 다시 컴파일 해야 한다. 하지만 네이티브로 실행되는 바이너리 파일이 만들어지게 된다..

물론 운영체제별로 약간 다르게 작동 할수 있겠지만 당연히 작동 결과에 대한 차이가 있을 경우에는 API문서에 포함 되어 있다. API문서도 비교적 깔끔하게 잘 정리 되어있다.

특별히 소스 수정없이 여러 운영체제에서 안정적으로 돌아 갈 수 있다.

그리고 오픈소스 프로그램을 개발 할땐 GPL라이센스로 무료로 이용할 수 있다. 상용 라이센스 가격은 정확히 모르겠으나 MS쪽이랑 비슷하거나 약간 더 저렴한 거 같았다..

Qt라이브러리 쓰기위해서 http://trolltech.com/ 로 가서 Qt With MinGW 받아서 깔면 된다.
하지만 여기에는 디자이너 툴은 포함되어있지만, 소스를 편집하기 위한 IDE툴이 없다. 따라서 Eclipse CDT나 다른 프로그램이 필요하다.

하지만 Visual Studio 가 익숙해 VS에서 사용하려면~~?? 인터넷에서 흔하게 Visual Studio에서 개발하고 MS 컴파일러로 컴파일 까지 하는 문서들은 있었지만..MS컴파일러로 컴파일 해야할 이유까지는 없었다.. 근냥 편집툴로만 MS Visual Studio를 쓰고 싶었다..

우선 PATH를지정 해야 한다.
Qt 4.3 Command Prompt 아이콘을 클릭하면 자동으로 패스가 지정된다. 하지만 우리가 원하는 것은 VS에서 Qt를 컴파일 하기 위한 것이므로 내 컴퓨터 등록정보에서 환경 변수를 등록해야 한다..
사용자 삽입 이미지

QTDIR = C:\Qt\4.3.1
PATH =  C:\Qt\4.3.1\bin;C:\MinGW\bin
QMAKESPEC = win32-g++


위와같이 환경 변수를 추가 한다.

Microsoft Visual Studio를 실행 하여
File- New - Project... 메뉴를 선택한다.
사용자 삽입 이미지
Visual C++ 카테고리에서 Makefile Project를 선택 한다.

사용자 삽입 이미지

마법사 대화 상자가 뜨면 우선 Finish를 누른다. 컴파일 명령어는 메뉴에서 다시 지정하도록 합니다.

Project - Property메뉴를 선택한다..
왼쪽 트리에서 Configuration Properties - NMake를 선택합니다.

사용자 삽입 이미지

Build Command Line오른쪽에 있는 [...]버튼을 눌러서 아래와 같이 입력한다.

qmake -project
qmake
make
사용자 삽입 이미지

Rebuild All Command Line라인과  Clean command Line에는 " make clean "등의 명령을 적절히 넣으면된다.

Output은 실행 파일이 생성될 위치이다..
기본적으로 Release 폴더 밑에 폴더 이름으로 실행파일이 생긴다.. 거기에 맞춰 입력 하면 된다.

Output은 "Release\프로젝트명.exe" 이렇게 주면 되다..

기본적인 설정을 마쳤다..

Solution Exploler Tool Windows에서 프로젝트를 선택하고 오른쪽 버튼을 눌러 Add- New Item을 선택한다.
사용자 삽입 이미지

C++ File 을 선택한다..
#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
     QApplication app(argc, argv);
     QLabel *label = new QLabel("Hello Qt!");
     label->show();
     return app.exec();
}


위와 같이 한번 입력해보고 컴파일 실행 버튼을 눌러보자... 컴파일과 실행이 되었었을 것이다.

하지만 Qt 라이브러리에 대한 인텔리센스는 작동하지 않을 것이다. 그럴 경우.. Tools에 Options...
Project and Solution - VC++ Directory - Include File에서 Qt관련 include가 있는 곳에 폴더를 지정해 준다..

{QTDIR}\include\ActiveQt
{QTDIR}\include\Qt
{QTDIR}\include\Qt3Support
{QTDIR}\include\QtAssistant
{QTDIR}\include\QtCore
{QTDIR}\include\QtDBus
{QTDIR}\include\QtDesigner
{QTDIR}\include\QtGui
{QTDIR}\include\QtNetwork
{QTDIR}\include\QtOpenGL
{QTDIR}\include\QtScript
{QTDIR}\include\QtSql
{QTDIR}\include\QtSvg
{QTDIR}\include\QtTest
{QTDIR}\include\QtUiTools
{QTDIR}\include\QtXml


위와 같이 폴더를 추가하면 인테리센스가 작동한다. 한결 편하게 작업 할 수 있을 것이다..

신고

멀티 쓰레드를 사용하고 프로그램이 작성가능하다면 좋겠지만, 실제 멀티 쓰레드를 사용하지 않고 프로그램을 작성하게 될경우 매우 복잡하게 되거나 구현이 불가능한 경우가 생기게된다.

예전에 만든 프로그램에서 멀티 쓰레드를 사용하지 않고, 짧은 간격으로 리턴하고, TIMER 이벤트로 호출하는 방법을 쓴적이 있다.

문제는 리턴하기 때문에 현재 수행한 상태를 저장해 두고 리턴 했고, 타이머에 의해서 호출되면 스위치 문에 의해 다시 이전 수행을 이어서 하게 만들었다.

그 뿐만 아니라 프로그램을 읽기가 매우 힘들고 유지하기도 어려웠다.

멀티 쓰레드는 또한 자원의 활용도를 높여 줘 시스템의 성능 또한 높여주게 된다.

//실제 수행 가능한 소스는 아니고 예제 입니다.

#define RUN_FLAG 1;
#define END_FLAG 2;

Class CTest
{
private:
  int m_threadFlag;
  CWinThread* m_threadMacro;
  //동시에 접근 하지 못하도록 막음
  CCriticalSection critical;
public:
  void run(void);
  void stop(void);
  void stopForce(void);
  int getListSize();
  void doSomething(void);
}

//수행 쓰레드를 시작
void CTest::run(void)
{
  m_threadFlag = RUN_FLAG;
  m_threadMacro = AfxBeginThread (macroThread,&*this);
}

//쓰레드를 종료
void CTest::stop(void)
{
  m_threadFlag = END_FLAG;
}

//강제로 쓰레드 종료
void CTest::stopForce(void)
{
  m_threadMacro->SuspendThread();
}

//크릭티칼 섹션이 필요한 경우
void CTest::doSomething(void)
{
  critical.Lock();
  //필요한 작업
  critical.Unlock();
}


//쓰레드 함수
UINT macroThread( LPVOID pParam )
{
  CTest* ct = ((CTest*)pParam);
  
  for(int i = 0; i < ct->geListSize(); i++)
  {
    //플래그가 종료일 경우 return함
    if(ct->getThreadFlag == END_FLAG) return 0;
    ct->doSometing(void);
  }
  
  return 0;
}



UINT macroThread( LPVOID pParam )


쓰레드로 수행될 함수를 전역 함수로 만든다. 파라미터는 LPVOID한개를 가지게 된다.

쓰레드에 넘겨줘야 할 값이 있으면 이 파라미터 한개로 넘겨주면된다. 갯수가 하나이기 여러개의 값을 넘겨주기 위해서는  때문에 구조체나 클래스 객체에 담아서 넘겨줘야 한다.

AfxBeginThread (macroThread,&*this);


아래와 같은 함수를 수행하게 되면 새로운 쓰레드가 생성되고 쓰레드가 시작된다.

반환 값은 CWinThread* 이다.

//플래그가 종료일 경우 return함
if(ct->getThreadFlag == END_FLAG) return 0;

일반적으로 쓰레드가 종료될 필요가 있으면 위와 같이 종료 플래그에 의해서 종료하면 된다.  이런 방식이 불가능할 경우는 아래와 같은 방법을 사용할 수 있다.

m_threadMacro->SuspendThread();


위와 같이 강제로 종료해도 되지만, 위와 같이 강제로 종료하게 될 경우, 쓰레드가 수행 도중 종료되게 된다. 위 예제에서는 doSometing()함수 호출되어 뭔가 하는 도중 일을 끝마치지 못한 상태에서 종료될 수도 있게된다.

이렇게 되더라도 특별한 문제가 없는 경우 위와 같이 종료하면되고, 문제 발생의 소지가 있다면 필요에 따라 조치를 취해줘야 한다.

critical.Lock();
//필요한 작업 Critical Section
critical.Unlock();


동기화가 필요할 경우에는 다음과 같이 동기화를 위해 lock과 unlock이 가능하다.

CCriticalSection critical;


같은 CCriticalSection 객체의 Critical Section에는 하나의 쓰레드만 수행되게 된다. Lock을 걸게 되면 다른 쓰레드에서는 이 부분에서 Unlock()이 될때 까지 기대리게 된다..
신고
윈도우 비스타에서는 UAC(사용자 계정 관리)를 사용합니다.

XP이전의 운영체제에서는 대부분의 응용프로그램이 관리자 권한으로 수행된다고 가정하고 설계되었습니다. 따라서 개인 PC의 사용자들은 대부분 관리자 권한의 계정을 만들어 사용하게 됩니다.

레지스트리에 접근하거나, 프로그램 폴더, 시스템 폴더, 드라이버, 설정등에 바로 접근 가능한 권한으로 로그인하여 PC를 사용합니다.

하지만 관리자 권한으로 프로그램을 수행하게 됨으로서 실행되는 프로그램 또한 PC의 모든 권한을 가지게 됩니다. 따라서 악의 적인 프로그램에 의해 시스템이 손상될 가능성이 높아지게됩니다.

그렇다고 권한이 낮은 계정으로 사용하려면 매우 불편합니다.. 만약 시간은 바꾼다던지, 드라이버를 설치한다던지, 프로그램을 설치할때마다, Administrator계정으로 로그인하여 다시 수행해야 합니다.

응용프로그램 중 단순히 프로그램 수행에도 Administrator계정을 필요로 하는 경우도 많습니다.

비스타에서는 이러한 문제를 해결하기 위해서 UAC를 사용하여, 관리자 권한이 필요로 하는 경우만 관리자 모드로 해당 프로그램을 권한 상승시키는 방법을 사용하게 됩니다.

사용자 삽입 이미지

비스타의 경우 심지어 Administrators 그룹에 속해 있는 사용자도 관리자 모드를 필요로하는 프로그램을 실행할때, 권한 상승을 하고 수행 할지? 취소할지? 물어봅니다.

사용자 삽입 이미지

그럼 프로그램이 수행될때 이 프로그램이 어떤권한을 필요로 하는 프로그램인 어떻게 알고, 위와 같이 권한 상승을 요구하게되는 것일까?

manifest에 내용에 따라 결정이된다. 또는 이 파일이 resource로 실행 파일 내부에 링크 되어 포함 될 수도 있다.

만약 Vista이전에 나온 프로그램이라면 대부분 이러한 정보가 포함되어 있지 않게 된다.

그런데 설치 프로그램일 경우 관리자 모드를 필요로 하게된다.

이 때는 아래와 같은 내용에 의해 설치 프로그램 여부를 판단하게 된다.


Installer Detection only applies to:

  1. 32 bit executables
  2. Applications without a requestedExecutionLevel
  3. Interactive processes running as a Standard User with UAC enabled


Before a 32 bit process is created, the following attributes are checked to determine whether it is an installer:

  • Filename includes keywords such as "install," "setup," and "update."
  • Keywords in the following Versioning Resource fields: Vendor, Company Name, Product Name, File Description, Original Filename, Internal Name, and Export Name.
  • Keywords in the side-by-side application manifest embedded in the executable.
  • Keywords in specific StringTable entries linked in the executable.
  • Key attributes in the resource file data linked in the executable.
  • Targeted sequences of bytes within the executable.


비스타가 설치 프로그램이라 판단하게 되면, 해당 프로그램을 권한 상승후 수행하게 된다.

 

User Interface Privilege Isolation(UIPI)
관리자 권한으로 수행되는 프로그램과 낮은 권한으로 수행되는 프로그램간의 관계도 제한 받게 된다.


A lower privilege process cannot:

  • Perform a window handle validation of higher process privilege.
  • SendMessage or PostMessage to higher privilege application windows. These Application Programming Interfaces (APIs) return success but silently drop the window message.
  • Use thread hooks to attach to a higher privilege process.
  • Use Journal hooks to monitor a higher privilege process.
  • Perform DLL injection to a higher privilege process.


With UIPI enabled, the following shared USER resources are still shared between processes at different privilege levels:

  • Desktop window, which actually owns the screen surface.
  • Desktop heap read-only shared memory.
  • Global atom table.
  • Clipboard

위 내용 때문에 요즘 만들고 있는 멜론 스나이퍼가 비스타에서는 전혀 작동하지 않았다.


자신이 만든 프로그램이 권한 상승이 필요할 경우 아래와 같은 작업을 해줘야 한다.


우선 아래와 같은 manifest파일을 작성한다.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <!-- Identify the application security requirements.-->
  <ms_asmv2:trustInfo xmlns:ms_asmv2="urn:schemas-microsoft-com:asm.v2">
    <ms_asmv2:security>
      <ms_asmv2:requestedPrivileges>
        <!-- 실행 수준에는 세 가지(asInvoker, highestAvailable, requireAdministrator)가 있습니다. -->
        <ms_asmv2:requestedExecutionLevel
          level="requireAdministrator"
          uiAccess="false"/>
        </ms_asmv2:requestedPrivileges>
       </ms_asmv2:security>
  </ms_asmv2:trustInfo>
</assembly>


"asInvoker"의 경우 이 프로세스를 생성한 상위 프로세스를 상속 받게 됩니다.
"requireAdministrator" 관리자 권한을 프로세스 생성시 요구 하게 됩니다.
"highestAvailable" 사용자와 연결된 프로세스들이 가진 권한중 가장 높은 권한을 요구 하게 됩니다.

파일이름 실행 프로그램 이름에 확장자를 추가로 manifest로 붙혀 준다. 만약 test.exe가 실행 파일이라면 test.exe.manifest로 저장한다. 저장 위치는 수행 프로그램이 위차한 같은 폴더여야 한다.

권한상승의 경우 DLL의 경우는 해당사항 없고 EXE실행 파일의 경우만 해당된다.

위 같은 작업을 해주고 프로그램을 수행하면 권한 상승을 요구하고 수행되게 된다.




실행 프로그램에 이 manifest파일을 포함하고 싶다면, 리소스 파일과 같은 위치에 해당 manifest파일을 놓아 두고..

리소스 파일 (.rc)파일 열어서 아래 와 같이 추가한다.

#define MANIFEST_RESOURCE_ID 1
MANIFEST_RESOURCE_ID RT_MANIFEST "test.exe.manifest"


해당 파일에 manifest파일이 포함되고 수행시 관리자 모드로 권한 상승을 요구 하게 됩니다.

참조 페이지
http://msdn2.microsoft.com/en-us/library/ms766454.aspx
http://msdn2.microsoft.com/en-us/library/aa905330.aspx
http://msdn.microsoft.com/msdnmag/issues/07/01/UAC/default.aspx?loc=ko
신고
운영체제 시간에 배운 스풀~~ 내가만든 프로그램에 직접 적용 해보았다..

요즘 만들고 있는 멜론 스나이퍼~~ 그 프로그램에 스풀을 써보았다.

프로그램 메인 쓰레드와 함께 변환 쓰레드, 스풀 쓰레드가 작동한다.

사용자 삽입 이미지
(테스트 : 204MByte , MP3파일 30개)


기존 버젼은 파일 변환 후.. 전송이 완료된 후에야 다음 변환에 들어간다.

하지만 스풀을 사용한 후 파일 변환후, 전송은 스풀에 모두 맡겨 버리고 다음 파일 변환으로 바로 넘어간다.

역시 느린 플래쉬 메모리를 가진 MP3P로 보낼 경우 무려 36%나 빨라졌다.

학교에서 이론만으로 배운 내용을 실제 적용해 봤다는 것에 매우 뜻깊다. 물론 적용 기념으로 벤치마킹도했다. 시간을 측정하지 않더라도 채감 할 수 있는 향상이 있었다..;;




컴퓨터 전공이 아니라도 프린터 스풀은 들어 보았을 것이다.

스풀이라는 것은 느린 입출력 장치로 데이터를 보내고 받을 때 직접 보내지 않고 스풀에 맡기는 방식이다.

한글에서 프린트 버튼을 누르고 진행 그래프가 순식간에 100%가 되고 사라진다. 하지만 아직 프린트는 열심히 프린트 하고 있을 것이다.

사용자 삽입 이미지

한글에서 프린트를 누르면 스풀 프로그램에 프린트 할것을 맡긴다. 스풀은 받은 문서를 프린트보다 훨씬 빠른 하드디스크에 쓴다. 매우 빠르 시간에 완료 될 것이다.

문서를 받은 스풀 프로그램은 계속 해서 하드디스크에 저장해둔 문서를 프린트한다.

그리고 한글은 스풀에다 문서 인쇄에 대한 것을 모두 맡겼으니... 프린팅이 완료되지 않아도 다른 기능을 계속 수행 할 수 있다.

즉 자원을 활용도를 극대화 할 수 있다.

아~! 그리고 버퍼라는 말도 들어봤을 것이다. 버퍼가 아마도 더 익숙하지 않을 까 생각한다.

버퍼란 느린 장치와 빠른 장치와의 서로 데이터를 주고 받을 때 원할 하게 수행하기 위한 중간 저장공간이다.

버퍼랑 스풀은 기능상으로는 동일하다. 하지만 특별히 디스크를 중간 저장공간으로 쓸 경우 일반적으로 스풀이라고 말한다.
신고
소스 코드에서 다음과 같은 매크로를 삽입한다.
#define CRTDBG_MAP _ALLOC

메모리 누수 확인을 위한 부분에 다음과 같은 코드를 삽입한다음
 _CrtDumpMemoryLeaks();

Visual Studio에서 디버그 모드로 수행하면 output윈도우에서 메모리 누수를 확인 할 수 있다.
사용자 삽입 이미지

프로그램이 종료 되면 반환되지 않은 메모리는 운영체제에서 일반적으로 반환 해준다. 하지만 수행도중 메모리 누수가 계속 해서 발생된다면 프로그램은 죽을 수 밖에 없다.

종료시점에 메모리 반환도 필요성이 없지만, 나중에 프로그램이 변경될 시 문제 발생의 가능성을 가지고 있을수도 있으므로 종료시점에서도 메모리 반환 하는 습관을 들이는 것이 좋을 것으로 생각 된다.

C나 C++로 과제를 할 경우 상당히 어렵다. 더디다고 할까나? Visual Studio같은 멋진 개발 툴이 있음에도 생각보다 쉽게 프로그램을 만들 수가 없다.

특히 버그가 있음에도 짧은 프로그램에서는 정상적으로 돌아가는 경우도 있었다.

함수안에서 new로 메모리를 활당하고 delete한후 그 포인터값을 반환하고...
함수를 호출 한 쪽에서 그 포인트로 멤버 변수에 접근 했는데..

컴파일러나 운영체제에 따라 정상 수행되는 경우도 있었다. 일부로 안되는 에러 상황을 만들려고 했었는데.. 정상적으로 수행되어.. 순간 혼란 스러웠던 적도 있다..

얼마전 C로 작성한 과제가 gcc/vc6/vc2007 모두에서 오류/경고 가 없었음에도 불구하고.--; 특정 컴파일러로 만든 실행파일은 실행 안되는 경우가 있었다.. 이런건 경고라도 떠야 뭐가 문제인지 찾지 과제 제출 기한도 바쁜데.. 어떻게 찾으란 말인가??ㅠㅠ;;

그래서 어쩔수 없이 컴파일러 별로 테스트한 후 성공 여부를 맨위에 주석으로 적어서 재출하는 방법을 사용했다.

정말 신경을 쓰고 짜지 않으면 짧은 프로그램이라도 문제가 발생할지 모른다..--;; 아직 스킬이 부족해서 그런거라 생각이 들지만..ㅠㅠ;; 언젠가 프로그래머의 대가 되고 싶다...
신고

김정민님이 어떻게 윈도우를 찾느냐고 물어보셔서~~ 작성합니다.. 많은 도움이 되었으면 좋겠네요..

CMelOnSniperMacro는 매크로를 수행하는 클래스입니다.

이 클래스의 맴버변수로는 변환해야할 파일들 현재 몇번째 파일 변환중인지.. 몇번째 스텝인지 등을 저장하는 변수들이 있습니다.

다이어로그 클래스에서 실제 이 Macro클래스 인스턴스가 만들어집니다.. 타이머에 의해 m_DoMacro맴버 함수가 정해진 시간에 호출 됩니다. 맴버 변수가 가지고 있는 현재 상태를 가지고 매크로를 수행하는 부분입니다.

그 중 윈도우창을 찾는 부분의 소스들입니다. 중복되는것은 생략했습니다.
 

bool CMelOnSniperMacro::m_FindMainWindow(void)
{
 m_wndMelon.m_hWnd = ::FindWindow(TEXT("MelOnFrameV30"),NULL);
 if(m_wndMelon.m_hWnd == false) return false;
 return true;
}

::FindWindow(TEXT("MelOnFrameV30"),NULL); 이 함수는
첫번째 인자로 윈도우의 클래스 이름
두번째 인자는 캡션 이름
으로 윈도우를 찾고 핸들을 반환합니다.. 최상위 윈도우를 찾을 수 있습니다. NULL을 입력 할 수 있습니다.

m_wndMelonCWnd타입입니다.

만약 윈도우 내부에 있는 자식 윈도우를 찾고자 하거나 특정 윈도우 다음에 위치한 윈도우를 얻고자 할때 는
FindWindowEx(HWND, HWND, LPCWSTR, LPCWSTR);함수를 사용하시면 됩니다..
첫번째 인자는 부모 윈도우의 핸들
두번째 인자는 인자 윈도우에 다음에 위치하는 윈도우 핸들을 얻어 옵니다.
세번째 인자는 클래스명
네번쨰 인자는 캡션 명입니다..

물론 NULL값을 인자로 넘겨 줄 수 있습니다.

int CMelOnSniperMacro::m_DoMacro()
{
 
 switch(m_nStep)
 {
  case 2://찾아보기창 띄우기
  //컨버팅 변환창에서 파일 찾기 버튼 누르기
  {
   CWnd* wndDCFConverting =m_findWindow(TEXT("#32770"), _T(""), 530,455);
   if(wndDCFConverting->m_hWnd != NULL){
    wndDCFConverting->PostMessage(WM_COMMAND,0x836,0x0);
    m_nStep++;
   }
   delete(wndDCFConverting);
  }
  break;
 }
 return 0;
}

그러나 멜론 3.0으로 업데이트 되면서 대부분의 윈도우에 캡션이 없습니다. 특이 윈도우 디자인에 이미지가 많은 경우 대부분 FindWindow로 한번에 찾기 어렵습니다..

또한 클래스명 또한 다이얼로그 박스에 기본값인 #32770입니다. 따라서 같은 클래스명과 이름의 윈도우가 수개 내지 수십개가 됩니다.

그래서 윈도우를 구별하기위해 윈도우 크기를 가지고 윈도우를 찾는 함수 m_findWindow라는 함수를 구현 했습니다... 물론 이 방법은 윈도우크기가 고정되어 바꿀 수 없는 윈도우에 한해서 쓰일 수 있는 방법입니다.. 윈도우 크기가 바뀔수 있는 경우라면 또 다른 방법을 생각 해봐야겠죠..

CWnd* wndDCFConverting =m_findWindow(TEXT("#32770"), _T(""), 530,455);

윈도우를 받아 옵니다.

    wndDCFConverting->PostMessage(WM_COMMAND,0x836,0x0);

혹시 윈도우를 찾지 못할 수도 있으므로 if문을 사용 찾은 경우에만 원하는 메세지를 찾은 창에 보냅니다. 위 다른 윈도우에 명령을 보내는 방법은 PostMessageSendMessage함수 가 있습니다.

PostMessage는 비동기로 메세지를 보내고 SendMessage는 동기로 메세지를 보냅니다..

이 함수의 인자, Msg와 wParam, lParam의 값은 Spy++이나 별도의 유틸을 통해서 쉽게 얻을 수 있습니다. WM_COMMAND, 0x836, 0x0은 멜론 스나이퍼에서 [파일찾기] 버튼을 눌러주는 역활을 합니다.

아래가 윈도우의 크기로 창을 얻어오는 함수의 실제 구현입니다.

CWnd* CMelOnSniperMacro::m_findWindow( CString strClass, CString strWindow, int height, int width)
{
 CWnd* findWindow = new CWnd();
 
 do{  
  findWindow->m_hWnd = ::FindWindowEx(NULL,findWindow->m_hWnd,strClass, strWindow);
  RECT r;
  //창의 크기로 윈도우 판별함
  findWindow->GetWindowRect(&r);
  int thisHeight = r.bottom-r.top;
  int thisWidth = r.right - r.left;
  if(thisHeight == height && thisWidth==width){
   return findWindow;
  }

 }while(findWindow->m_hWnd != NULL);

 findWindow->m_hWnd = NULL;
 return findWindow;
}


findWindow->m_hWnd = ::FindWindowEx(NULL,findWindow->m_hWnd,strClass, strWindow);
우선 가장 처음 do{}while()반복무을 수행할 경우 findWindow->m_hWndNULL입니다.. 따라서 해당 클래스이름과 윈도우명을 가진 첫번째 윈도우를 찾습니다..

그 다음 윈도우크기를 비교해 크기까지 동일하면 우리가 찾으려 했던 윈도우이므로~~ 해당 윈도우를 반환하고..

만약 윈도우 크기가 다르면 다음 윈도우를 찾아야합니다.

do{}while()반복문이 처음이 두번째 수행되는 것이라면  findWindow->m_hWnd가 이전에 찾았던 윈도우입니다.

FindWindowEx에서 두번째 인자가 이 다음 윈도우를 찾아라 하는 인자입니다.. 그럼으로 두번째 해당윈도우 클래스명과 이름을 가지는 두번째 윈도우를 찾게 되는 것입니다.
findWindow->m_hWnd = ::FindWindowEx(NULL,findWindow->m_hWnd,strClass, strWindow);

계속 해서 찾다가 더이상 다음 윈도우가 없을경우~~ null을 반환하고 while()문을 빠져 나오게 됩니다..
신고


VC6.0에서는 기본 설치 옵션이 유니코드를 사용하지 않는다. 따라서 설치시 유니코드를 사용하도록 옵션을 지정해주거나, 이미 설치를 하였다면 설치시디의

E:\VC98\MFC\LIB

에서 라이브러파일을 모두 복사해온다.

Project-Setting-C/C++탭에서 Processor Defination:항목의 _MBSC를 _UNICODE로 바꿔준다.

Link탭으로 이동한후 Category DropDown List에서 Output을 선택한후 Entry-point Symbol의 값에wWinMainCRTStartup를 입력하면된다.

Shared DLL을 사용할경우 DLL파일을 열수 없다는 에러가 뜨네요.. Static linked libary를 사용하는게 편할것 같네요..
신고

멜론 스나이퍼를 만들면서~~ 한참 고민했던문제 중에 하니이다..

기존에 쓰던 코드를 그대로 가져와서 VC++ 2007 붙혀 넣었더니 예전까지만해도 잘 작동하던 코드가 에러를 발생하였다.. 왠지 찾아보니 2007버젼은 유니코드를 기본으로 쓰기 때문이였다.

VC++로 프로그래밍을 그래도 가끔씩 사용하는데 항상 어려운것이 문자열을 어떻게 형변환해서 원하는 형으로 쓰는가이다.. 아직도 이거 잘모르겠다..

이것 한번 해보고 저거한번 두어번 바꿔봐도 에러로 안되면.. 검색을 해보는데 설명해놓은 글도 쉽사리 이해가 안되고.. 이해되더라도 금방까먹어버린다.

우리학교 과정이 JAVA위주로 되어있어 자주 쓰지 않기때문에~~ 지금도 VC++로 작업하면 언제나 변수 타입에서 골머리를 싸매게 된다..

멜론 스나이퍼를 만들면서 알게된사실~~ 함수뒤에 W가 붙어 있는 함수는 유니코드용 함수라는 사실~~.. 그런데~~ W가 붙어 있지 않는 함수도
#define FindWindowEx FindWindowExW

이렇게~~ 해놔서~~ 기본옵션은 항상 유니코드를 쓰도록 되어있었다.

유니코드를 사용해 윈도우의 창을 찾는 FindWindowExW함수를 이용할때
 LPCWSTR로 창의 클래스명이나 캡션명을 넘겨줘야 한다..
FindWindowExW(HWND, HWND, LPCWSTR, LPCWSTR);
 
이때..
FindWindowExW(hWnd,null, "문자열","문자열");
이런식으로 넘겨 주면 문자열이 깨져서~~ 못찾는다..

이때는
FindWindowExW(hWnd,null, TEXT("문자열"),TEXT("문자열"));
로 문자열을 유니코드로 변경해줘야 한다.

CString 변수를 넘겨 줄때는 별다른 형변환 없이 자동으로 casting 되는것 같았다.

다음 찾은 텍스트 박스의 CWnd를 통해 원하는 문자열을 찍는 방법이다.

CString str = m_strFileList.GetAt(m_nTime);
m_wndText->SendMessage(WM_SETTEXT,NULL,(LPARAM)(char *)(LPCTSTR)(str) );

(LPARAM)(char *)(LPCTSTR)
이렇게 형변환을 해주어야 했다..

아직도 잘모르겠다..~~ 형변환이 너무 어렵다~~ 이거 잘할 수 있고 완벽하게 이해될려면 얼마나 걸릴려나~~

학교에서는 전혀~~ VC++로 하는 수업을 들어본적이 없고.. 근냥 프로그램 만들때 필요한 거만 검색으로 찾아서 작업하다보니~~ 이해하고 있는게 없다.. 그렇다고 책을 한권 본것도아니고~~

인터넷에 있는 강의 한번본게 다라서~~ 위 내용이 확실히 맞는지도 모르겠다.. VC++어렵다~~

조금더 체계적으로 배워야 될텐데....

신고

일명 매크로 프로그램을 만드는 방법을 간단히 소개하겠다.

가장 먼저해야 할 일은 제어하고자 하는 프로그램의 핸들을 얻어 오는 것이다.

Spy+프로그램을 이용하면 쉽게 찾을 수 있다.

사용자 삽입 이미지

창 찾기 창이 뜨면 찾기 도구를 드래그 하여 원하는 응용 프로그램에 끌어 놓는다. FindWindow로는 최상위 부모 윈도우만 찾을 수 있다. 제어하고자 하는 응용프로그램에 최상위 윈도우에 끌어다 놓는다.
사용자 삽입 이미지


원하는 프로그램을 선택하면 아래와 같이 윈도우 정보가 표시 된다. 여기서 캡션과 클래스를 통해 원하는 윈도우를 찾을 수 있다.
사용자 삽입 이미지

파일-로그 메세지 메뉴을 이용해 창을 찾은후 확인을 누르면 현재 윈도우가 어떠한 메세지를 받는지 모두 볼 수 있다. 여기서 발생하는 이벤트중 키보드, 또는 마우스 관련 이벤트를 확인 하면 된다.
사용자 삽입 이미지


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

using System.Runtime.InteropServices;


namespace WindowsApplication1
{

    public partial class Form1 : Form
    {
        //사용할 API함수를 임포트 한다.
        [DllImport("USER32.DLL")]
        public static extern uint FindWindow(string lpClassName,
            string lpWindowName);

        [DllImport("user32.dll")]
        public static extern uint FindWindowEx(uint hWnd1, uint hWnd2, string lpsz1, string lpsz2);

        [DllImport("user32.dll")]
        public static extern uint SendMessage(uint hwnd, uint wMsg, uint wParam, uint lParam);

        [DllImport("user32.dll")]
        public static extern uint PostMessage(uint hwnd, uint wMsg, uint wParam, uint lParam);

        uint handle;


        private void button3_Click(object sender, EventArgs e)
        {
           //핸들을 찾는다. Spy+를 통해 찾은 클래스 이름과 캡션을 이용하면 된다. 둘 중 하나만 알경우에도 찾을 수 있다. 그때는 하나의 인자를 null로 넘겨 주면된다.
            handle = FindWindow("SciCalc", "계산기");
          //찾은 핸들에서 자식 윈도우 핸들을 찾기 위해서는 FindWindowEx를 이용한다.
            handle = FindWindowEx(handle, 0, "Shell DocObject View", null);
            handle = FindWindowEx(handle, 0, "Internet Explorer_Server", null);
            label1.Text = handle.ToString();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //찾은 핸들을 통해 메세지를 보낸다. 여기서는 아래 16진수들 의 값은 스파이 메세지 로그에서 찾은 값을 이용하면 된다. 여기서는 키를 누른 메세지(탭키누른후 엔터키를 누른다.)를 보내는 예제 이다.
            PostMessage(handle, 0x0100, 0x9, 0xF0001);
            PostMessage(handle, 0x0101, 0x9, 0xC00F0001);
            PostMessage(handle, 0x0100, 0xD, 0x1C001);
            PostMessage(handle, 0x0102, 0xD, 0xC01C001);
           
        }
}


FindWindow 함수는 윈도우를 찾는 API 함수이다. 첫번째 인자는 실행중인 프로그램의 클래스 이름이고 두번째 인자는 프로그램의 타이틀이다. 최상위 부모 윈도우만 찾을 수 있고 자식 윈도우는 FindWinowEx를 통해 찾으면 된다.

FindWindowEx는 부모윈도우 핸들을 통해 부모 윈도의 자식 핸들을 구하는 함수 이다. 예를 들면 프로그램에 자식으로 붙어 있는 버튼등을 찾으려면 FindWindowEx를 이용하면 된다. 첫번째 인자는 부모 핸들이다.

두번째 인자는  처번째 인자의 자식핸들 값 또는 0 값이 오면 된다. 만약 핸들 값이 들어오면 들어온 두번째 핸들 값 이후의 자식만 검색한다. 0 이면 모든 윈도우의 자식을 검색한다.

세번째 인자는 클래스 이름, 네번째 인자는 프로그램타이틀명이다.

PostMessage와 SendMessage는 메세지를 보내는 함수이다. 이 함수를 실행함으로서 원하는 제어를 할 수 있다.
첫번째 인자는 메세지를  보내고자하는 윈도우 핸들의 값이고, 두번째 인자는 보내고자 하는 메세지, 세번째 인자는 wParam ,네번째 인자는 lParam이다. lParam ,wParam은 메세지의 부가정보라고 할수 있다.  WM_LBUTTONDOWN메세지가 발생할때는 마우스의 버튼이 눌러진 위치 등의 부가 정보가 필요한데 이러한 정보를 실어보내는 인자이다. 역시 Spy+를 통해 확인이 가능하다.

PostMessage와 SendMessage의 차이점은 거의 없는데 PostMessage로 보내게 되면 메세지를 해당 윈도우에 던지고 바로 리턴하는 함수이고 SendMessage는 메세지를 보낸후 그 메세지가 처리된후 리턴된다. SendMessage동기화가 필요할때 사용하면 되고, 동기화 필요없이 메세지만 보내려면 PostMessage를 이용하면된다.
신고


티스토리 툴바