Search

'Program Visual C++'에 해당되는 글 36건

  1. 2012.11.05 Windows8 개발환경..
  2. 2012.10.10 ACM 오디오 프로그래밍
  3. 2012.10.10 AVIStreamWrite code..
  4. 2012.04.18 ActiveX 권한2

Windows8 개발환경..

Program Visual C++ 2012. 11. 5. 15:54 Posted by HisPark

Windows 8 스타일앱개발이한창유행이다. 물론모바일생태계전반전인유행은아니더라도 Microsoft 기술을하는사람들에게는큰관심대상이다. Windows 8 운영체제가탑재되는테블릿도출시가되고, New iPad 보다하드웨어스팩이더좋은테블릿출시도준비중인곳이많다고들었다. 새로운마켓이열리는만큼테블릿사용자에게는새로운재미를선사해줄것은분명한사실일것이다.

Windows 8 스타일앱! 개발을위해몇가지알아야할구조적인개념이나유의사항정도만언급하기위해글을써나간다. C++/CX, C#,VBXAML(eXtensible Application Markup Language)를이용하여 WPF 데스크탑응용프로그램처럼프로그래밍을할수있다. 그리고 HTML/JavaScript 조합으로웹개발환경과유사하게개발을할수있다. 아마대부분의 Windows 8 스타일앱개발자라면알고있는내용일것이다. 그리나이포스팅에서는 C++/CX C# 개발언어를기준으로아티클내용을채울것이다.

Windows 8 스타일앱런타임관점의구조

[이미지링크]

WinRT(Windows Runtime)플랫폼의구조적아키텍처이미지이다. 기존 Windows Desktop 응용프로그램환경과다른점은 WinRT APIs 가중간에끼어있다. WinRT Windows 8 스타일앱의핵심이며, Windows 로시작하는 Namespace는모두 WinRT 이다.

C#,VB 개발환경은그나마편리한 Library Subset 을제공한다. .NET Framework의최소화버전이라고보면된다. 이를 .NET for Windows Store apps 이라고부르며, 위의이미지에는이내용이빠져있다.

C++/CX는딱잘라 .NET for Windows Stores apps가제공되지않는다. .NET 개발자라면 System(mscorlib.dll) 으로시작하는 Namespace 가얼마만큼편한지알텐데, Library Subset이제공되지않으니다른방법을사용해야한다. 쉬운예로 HttpClient와같은클래스도 C++/CX MsXml COM 컴포넌트를이용하는편이낫다. 그렇다고모든 C++ 라이브러리를사용할수있는것도 아니다. 이 부분에서 특히 라이브러리의 제한이 있으므로 C:\Program Files (x86)\Windows Kits\8.0\Include\shared 폴더에서사용가능한 Header 파일을확인해보도록하자.

만약 Header 파일의 pragma 선언이 Dektop Family 라면 Windows 8 스타일앱에서는사용할수없는라이브러리이며, 상당한 Header들이 Desktop Family에속하여, 당장 .NET for Windows Stores apps 만큼쓸만한클래스들이없다는것이조금슬프다. 다만, 좋은소식이라면 C++ boost Library 등이 C++/CX 용으로컨버전을시도하는분들이많으므로, 조금만기다려보면쓸만한라이브러리들이대거출연할것으로보인다.

더불어 C#, C++ 개발자들도알고있어야하는것중에하나가 WinRT COM 컴포넌트기반의라이브러리라는것쯤은들어보았을것이다. 그래서 Windows 8 스타일앱개발자들은 COM에대한개념과더나아가이를구현할수있다면더좋다. 특히 C#, VB 에서 WinRT 컴포넌트를만드는것은몇가지지켜야할제약이있으므로다음의링크를꼭참고하는것이좋다.

C# 및 Visual Basic으로 Windows Runtime 구성요소만들기http://msdn.microsoft.com/ko-kr/library/windows/apps/br230301.aspx

대신 C++/CX WinRT 컴포넌트를만드는것이오히려더간단하다. 앞서말했다시피 WinRT COM 컴포넌트기반이지만, 기존 C++ 에서 COM 컴포넌트를만드는만큼어렵지가않다. C++ COM 구현의첫번째는 IUnknown 인터페이스를구현하는것이지만, WinRT 에서는 Iunknown을 상속하는 IInspectable 인터페이스가더 중요하다. IInspectable 인터페이스는 C++/CX로개발된응용프로그램이런타임중해당클래스의정보를제공하기위한인터페이스이다. 물론기존 C++ 에서도런타임상클래스정보가필요하여이를직접구현하는방법도있다. 하지만 C++/CX IInspectable 인터페이스는 C++/CX 컴파일과정에서자동으로구현을해준다. 이는곧 IUnknown 인터페이스까지자동으로구현해준다고보면된다. 그렇기 때문에 Iunknown 인터페이스의 AddRef, Release 메서드에대한객체수명주기를 WeakReference 클래스를통해위임할수있다. WeakReference를통해금방해제될수있는컴포넌트를가비지컬렉터대상이되도록지정한다. 그러므로사용빈도가매우많고, 매번자원해제에대한비용이반복되는것은 WeakReference로효과적으로객체수명주기를다룰수있게한다. 이러한 C++/CX에특별히제공되는라이브러리는 Microsoft.WRL Namespace에포함되어있다.

아직이러한개념적인부분이어렵게느껴진다면월간마이크로소프트 5월호특집기사로기고한필자의다음의글부터참고바란다.

[월간마이크로소프트 5월호특집기사] C++ 매트로앱개발을위한 C++/CX 언어http://blog.powerumc.kr/378

Windows 8 스타일앱응용프로그램관점

짧게말해 Windows 8 스타일앱개발은쉽다. Visual Studio 2012에서훌륭하게대부분이구현된템플릿을제공하기때문에, 메서드중간중간원하는기능을추가하고, 클래스나 XAML 쯤만들면된다. 다만, 이는만든다는것이쉽다는것이지응용프로그램구조적인측면에서는전혀쉽지않다.

먼저알아야할것이, Windows 8 스타일앱페이지를상태관리할것인지, 말것인지부터결정해야한다. 상태관리를유지할필요가없다는것은웹개발(ASP.NET/ASP/PHP/JSP) 와같은서버사이드개발환경과유사하다. 특히 IIS에서부터 ASP.NET까지연결되는 Application Pipeline은매번의 Request마다 Pooling Thread가활성화되어서버랜더링을통해사용자에게 HTML Response로전달이된다.

1. 상태관리를개별적으로유지하고싶다면

다음의두가지메서드를재정의하면된다. ASP.NETCustomControl을구현해보았다면VIEWSTATE에상태유지를위해이런유사한코드를구현해야하는것을알것이다.

Frame.Navigate 메서드는 Page Type을인자로받고, 매번새로운인스턴스를생성한다. (구현을다르게한다면인스턴스를이용하도록할수도있다.) 페이지의상태유지야위의메서드를재정의하는정도로끝낼수있지만, 페이지에포함된 UserControl이있다면상황은달라진다. 독자마다구현하는방법은다르겠지만, 효과적으로상태를관리하기위해 UserControl은조금귀찮아지는존재이다. 인스턴스의재사용을위해자주사용하는 UserControl에대한상태관리를 고민해야 하다니… (현재아티클은이내용이대해오픈소스제공으로효과적인방법을제안하도록필자는약속하겠다.)

2. 상태관리를자동으로캐싱하고싶다면

상태관리를자동으로캐싱하는방법도매우쉽다. Page.NavigationCacheMode 프로퍼티를 Enabled 해주면된다. 물론 XAML 코드에속성을추가해도된다. 하지만아쉽게도 Frame.Navigate 메서드를통해자동으로상태관리를하도록한 Page는새로운인스턴스가생성이된다. 상태관리캐싱에대한조건은 GoBack(), GoFoward() 와같은 Frame 이동에대해서만유효하다. 조금더이해할수없는부분은 Page에포함된 UserControl 은상태관리캐싱대상에서제외된다. (물론, 가능하도록할수있지만개념적으로더깊게이해하고구현해야한다.)

3. 남발되는 async/await에의한동기화문제

Windows 8 스타일앱을사용하다가자주멈짓멈짓한다면분명사용자는짜증날것이다. 때문에 async / await 키워드를더욱자주사용하는편이다. 그렇기때문에 UI 상태에대한비동기와컴포넌트나인스턴스메서드호출에대한비동기, 이둘모두정확하게 Threading 에대한지식이필요하고, 자유자재로 Threading 을다룰수있다면더욱좋다. Windows 8 스타일앱에서가장많이발생하는 Threading 문제는 Thread 실행중인스턴스해제에대한동기화와 Thread Cancel 에대한동기화다.

이문제를잘피하기위해서는클래스나라이브러리를만들때부터 async / await 에대한고려가필요하다. 쉽게이야기하자면자주쓰지않는편이좋고, 써야할곳에써야한다.

이를잘판단하려면 Thread 동기화에대해적어도몇가지는반드시익히는것이좋다. MSDN 에아래의글들을한번정도꼭필독하기바란다.

Windows 8 스타일앱배포와라이브러리배포문제

이부분은매우민감한부분이고조심스럽다. 실제 Windows App Store에서테스트할수없을뿐더러개발환경에서발생하는문제이므로, 실제 Windows App Store 를통해발생할수도있을가능성이있을것같다. COM 기반라이브러리나 DLL 구성요소등은공유메모리에로드가된다는것을알것이다. 특히이부분은 COM 컴포넌트에서민감하게다루는 IUnknown 인터페이스의의미와일맥상통한다. , COM 객체의참조카운트(Reference Cout) 0이되지않으면, 자원은해제되지않는다. 여기에서COM의가장고질적인문제가발생한다. 바로 DLL 지옥이다. Windows 8 스타일앱의메모리위치는앱메모리영역이아닌공유되는메모리영역에위치하고있고, DLL Verserning 이자유롭지못하다. .NET 처럼 GAC(Global Assembly Cache)에서 DLL 버전별로관리되지않는다.

따라서, WinRT 런타임라이브러리를개발하여배포된 Windows 8 앱이활성화상태일때새로운앱에서버전업된 WinRT 런타임라이브러리배포시에다른프로세스가점유하고있다는오류가발생한다. 이는앱이초절전유휴상태에진입되어도마찬가지이다.

이찌되었든개발환경에서는충분히발생된문제이니, 차후 Windows App Store에서도발생할지는지켜보아야할것이다.

Windows 8 Windows Runtime 의재배포정책및업데이트

첫런타임라이브러리인만큼라이브러리코드가완벽하지는않을것이다. .NET Framework 1.0부터 4.5까지버전업이되어왔는데과연 Windows 8 Windows Runtime은어떻게버전업이될까? 사용자의동의없이업데이트나패치는불가능하다.

Windows 8 Features Pack 1,2,3… 시리즈로업데이트가될까?
Windows 8 Service Pack 1,2,3…
시리즈로업데이트가될까?

만약이렇게되면, Windows 8 스타일앱간에호환성문제가발생할것이분명하다. AppleiPhone의모바일폰의운영체제업데이트가실시간으로, 온라인으로이루어진다. Windows 8 iPhone의업데이트와차원이달라진다. WinRT만지원하는 ARM 버전과기존의 Windows 8 Desktop을지원하는 x86 버전등몇가지의에디션이있다.

혹시이부분에대해서알고있는정보가있으면공유부탁드립니다.

 

 

Posted by POWERUMC 엄준일 (POWERUMC)

출처http://blog.powerumc.kr/trackback/390

'Program Visual C++' 카테고리의 다른 글

ACM 오디오 프로그래밍  (0) 2012.10.10
AVIStreamWrite code..  (0) 2012.10.10
ActiveX 권한2  (0) 2012.04.18
ActiveX 권한..1  (0) 2012.01.12
레지스트리 등록 (RegCreatekeyEx, RegDeleteValue, RegOpenkeyEX, RegSetValueEx  (0) 2012.01.05

ACM 오디오 프로그래밍

Program Visual C++ 2012. 10. 10. 18:05 Posted by HisPark

ACM 오디오 프로그래밍


윈도우 환경에서 오디오 프로그래밍은 쉽지가 않다. 쉽지가 않다고 하는 것은 비디오나 이미지에 비해서 어렵다는 이야기이다.
보통 비디오라고 하면 DirectShow 기반으로 개발하며, 간혹 다른 것도 사용하지만..
이미지하면 GDI나 DirectDraw등을 이용하게 된다. 혹은 다른 이미지 라이브러리를 사용...
오디오 역시 DirectShow 기반으로 하면 되지만 ACM)Audio Compression Manager)이라는 SDK가 있다. 이곳에서 오디오 인코딩, 디코딩 및 여러 효과를 적용할 수 있다.

그러나 윈도우 기반 오디오 코덱으로 작업할 수 있는 음질 한계가 있다. 특히 MP3인 경우는 기본 설치된 환경에 MP3 압축은 56KHz이하만 가능하다. WMP10버전 이상을 설치하게 되면 고품질 MP3까지 무료로 인코딩할 수 있다.

개인이라면 무료 인코더/디코더를 사용하거나 불법으로 설치가능하겠지만, 개발하는 입장에서는 라이센스 비용과 범위가 걸리게 된다. 가급적이면 윈도우 내에서 해결하는게 차후 개발에 따른 비용이 감소하기 때문이다. 그리고 무료 인코더/디코더도 라이센스를 자세히 읽어봐야되는데 귀찮아서 일단 차후에 고려하기 했다.

국내에서 오디오관련 프로그래밍을 물오볼수 있는 포럼같은 것이 거의 없다. 예전에 Directshow가 있던것 같은데... 기억이... 지금은 찾아도 찾기 힘드네. ㅡ.ㅡㅋ
데브피아에서 vc 마을에 가면 어느정도 도움이 될 수 있다.

여기서는 ACM를 이용한 오디오 프로그래밍을 다룰 것이다. 기본적인 처리절차를 볼 것이고 나머지 추가 사항을 살펴보겠다.


ACM 변환 과정

아래는 ACM에서 오디오 변환시 사용되는 기본적인 함수들이다. 호출되는 순으로 나열했다.

acmDriverOpen(...); // 원하는 오디오 드라이버 로드 (생략 가능)
acmSuggest(...); // ACM에서 변한시 제안하는 포멧 획득( 생략가능)
acmStreamOpen(...); // 스트림 열기
acmStreamSize(...); // Source 혹은 destionation 버퍼 크기 얻기
acmStreamPrepareHeader(...); // 변환위한 헤더 로드
acmStreamConvert(...); // 변환 (선택적으로 모든 데이터 처리위해 반복가능)
acmStreamClose(...); // 열어진 스트림 닫기
acmDriverClose(...); // 로드된 드라이버 종료 (생략가능)

각각의 함수 설명은 MSDN을 참고하기를 바란다.
위의 함수를 사용한 예제는 http://david.weekly.org/code/mp3acm.html에서 참고했다.

[열기] ACM 변환 예제

#include <windows.h>

#include <stdio.h>

#include <assert.h>

#include <mmreg.h>

#include <msacm.h>

 

#define MP3_BLOCK_SIZE 522

#define SOURCE_MP3 "C:\\audiograbber\\At The Club Last Night\\At_The_Club_Last_Night_-_Haven't_You_Heard.mp3"

#define OUTPUT_PCM_FILE "c:\\dump.pcm"

 

int g_mp3Drivers = 0;

 

BOOL CALLBACK acmDriverEnumCallback( HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport ){

if( fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC ) {

MMRESULT mmr;

 

ACMDRIVERDETAILS details;

details.cbStruct = sizeof(ACMDRIVERDETAILS);

mmr = acmDriverDetails( hadid, &details, 0 );

 

HACMDRIVER driver;

mmr = acmDriverOpen( &driver, hadid, 0 );

 

int i;

for(i = 0; i < details.cFormatTags; i++ ){

ACMFORMATTAGDETAILS fmtDetails;

ZeroMemory( &fmtDetails, sizeof(fmtDetails) );

fmtDetails.cbStruct = sizeof(ACMFORMATTAGDETAILS);

fmtDetails.dwFormatTagIndex = i;

mmr = acmFormatTagDetails( driver, &fmtDetails, ACM_FORMATTAGDETAILSF_INDEX );

if( fmtDetails.dwFormatTag == WAVE_FORMAT_MPEGLAYER3 ){

    OutputDebugString( L"Found an MP3-capable ACM codec: " );

    OutputDebugString( details.szLongName );

    OutputDebugString( L"\n" );

    g_mp3Drivers++;

}

}

mmr = acmDriverClose( driver, 0 );

}

return true;

}

 

HACMSTREAM g_mp3stream = NULL;

 

 

 

convertMP3(){

 

MMRESULT mmr;

 

// try to find an MP3 codec

acmDriverEnum( acmDriverEnumCallback, 0, 0 );

if(g_mp3Drivers == 0){

OutputDebugString( L"No MP3 decoders found!\n" );

return E_FAIL;

}

 

// find the biggest format size

DWORD maxFormatSize = 0;

mmr = acmMetrics( NULL, ACM_METRIC_MAX_SIZE_FORMAT, &maxFormatSize );

 

// define desired output format

LPWAVEFORMATEX waveFormat = (LPWAVEFORMATEX) LocalAlloc( LPTR, maxFormatSize );

waveFormat->wFormatTag = WAVE_FORMAT_PCM;

waveFormat->nChannels = 2; // stereo

waveFormat->nSamplesPerSec = 44100; // 44.1kHz

waveFormat->wBitsPerSample = 16; // 16 bits

waveFormat->nBlockAlign = 4; // 4 bytes of data at a time are useful (1 sample)

waveFormat->nAvgBytesPerSec = 4 * 44100; // byte-rate

waveFormat->cbSize = 0; // no more data to follow

 

 

// define MP3 input format

LPMPEGLAYER3WAVEFORMAT mp3format = (LPMPEGLAYER3WAVEFORMAT) LocalAlloc( LPTR, maxFormatSize );

mp3format->wfx.cbSize = MPEGLAYER3_WFX_EXTRA_BYTES;

mp3format->wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3;

mp3format->wfx.nChannels = 2;

mp3format->wfx.nAvgBytesPerSec = 128 * (1024 / 8); // not really used but must be one of 64, 96, 112, 128, 160kbps

mp3format->wfx.wBitsPerSample = 0; // MUST BE ZERO

mp3format->wfx.nBlockAlign = 1; // MUST BE ONE

mp3format->wfx.nSamplesPerSec = 44100; // 44.1kHz

mp3format->fdwFlags = MPEGLAYER3_FLAG_PADDING_OFF;

mp3format->nBlockSize = MP3_BLOCK_SIZE; // voodoo value #1

mp3format->nFramesPerBlock = 1; // MUST BE ONE

mp3format->nCodecDelay = 1393; // voodoo value #2

mp3format->wID = MPEGLAYER3_ID_MPEG;

 

g_mp3stream = NULL;

mmr = acmStreamOpen( &g_mp3stream, // open an ACM conversion stream

         NULL, // querying all ACM drivers

         (LPWAVEFORMATEX) mp3format, // converting from MP3

         waveFormat, // to WAV

         NULL, // with no filter

         0, // or async callbacks

         0, // (and no data for the callback)

         0 // and no flags

         );

 

LocalFree( mp3format );

LocalFree( waveFormat );

 

switch( mmr ) {

case MMSYSERR_NOERROR:

break; // success!

case MMSYSERR_INVALPARAM:

assert( !"Invalid parameters passed to acmStreamOpen" );

return E_FAIL;

case ACMERR_NOTPOSSIBLE:

assert( !"No ACM filter found capable of decoding MP3" );

return E_FAIL;

default:

assert( !"Some error opening ACM decoding stream!" );

return E_FAIL;

}

 

// MP3 stream converter opened correctly

// now, let's open a file, read in a bunch of MP3 data, and convert it!

 

// open file

FILE *fpIn = fopen( SOURCE_MP3, "rb" );

if( fpIn == NULL ){

assert( !"can't open MP3 file!" );

return E_FAIL;

}

 

// find out how big the decompressed buffer will be

unsigned long rawbufsize = 0;

mmr = acmStreamSize( g_mp3stream, MP3_BLOCK_SIZE, &rawbufsize, ACM_STREAMSIZEF_SOURCE );

assert( mmr == 0 );

assert( rawbufsize > 0 );

 

// allocate our I/O buffers

LPBYTE mp3buf = (LPBYTE) LocalAlloc( LPTR, MP3_BLOCK_SIZE );

LPBYTE rawbuf = (LPBYTE) LocalAlloc( LPTR, rawbufsize );

 

// prepare the decoder

ACMSTREAMHEADER mp3streamHead;

ZeroMemory( &mp3streamHead, sizeof(ACMSTREAMHEADER ) );

mp3streamHead.cbStruct = sizeof(ACMSTREAMHEADER );

mp3streamHead.pbSrc = mp3buf;

mp3streamHead.cbSrcLength = MP3_BLOCK_SIZE;

mp3streamHead.pbDst = rawbuf;

mp3streamHead.cbDstLength = rawbufsize;

mmr = acmStreamPrepareHeader( g_mp3stream, &mp3streamHead, 0 );

assert( mmr == 0 );

 

// let's dump this data off to disk (for debug checking!)

FILE *fpOut = fopen( OUTPUT_PCM_FILE, "wb" );

if( fpOut == NULL ){

assert( !"can't output output PCM!" );

return E_FAIL;

}

 

while(1) {

// suck in some MP3 data

int count = fread( mp3buf, 1, MP3_BLOCK_SIZE, fpIn );

if( count != MP3_BLOCK_SIZE )

break;

 

// convert the data

mmr = acmStreamConvert( g_mp3stream, &mp3streamHead, ACM_STREAMCONVERTF_BLOCKALIGN );

assert( mmr == 0 );

 

// write the decoded PCM to disk

count = fwrite( rawbuf, 1, mp3streamHead.cbDstLengthUsed, fpOut );

assert( count == mp3streamHead.cbDstLengthUsed );

};

 

// clean up after yourself like a good little boy

fclose( fpIn );

fclose( fpOut );

mmr = acmStreamUnprepareHeader( g_mp3stream, &mp3streamHead, 0 );

assert( mmr == 0 );

LocalFree(rawbuf);

LocalFree(mp3buf);

mmr = acmStreamClose( g_mp3stream, 0 );

assert( mmr == 0 );

 

return S_OK;

}


위의 예제를 변환 관련 정보가 고정된 값을 사용했다. 이 값들은 MP3 포멧에 따라서 변경해줘야한다. 이런 정보를 획득하기 위해서는 직접 MP3 파일을 파싱하거나, 혹은 IMediaType 인터페이스를 사용해서 획득할 수 있다. 이는 tag에 대한 정보를 가져오지 못하고, 오직 파일 포멧 정보만 얻을 수 있다.


파일 포멧 정보 알아내기

고정된 포멧이 아닌 상황에 따라 해당 파일에 대한 포멧을 획득해서 좀더 유연한 코드가 된다. 다음 링크의 예제가 IMediaType를 이용한 포멧정보 획득을 보여주고 있으니 참고하길 바란다. MSDN에서 IMediaType의 멤버함수를 보면 어떤 정보를 가져올 수 있는지 대충 알 수 있다.

DirectShow MediaDet를이용해미디어정보읽기


위의 예제에 의해서 get_StreamMediaType 함수에 의해서 MediaType을 획득할 수 있으면 우리는 포멧 정보를 다음과 같이 획득 할 수 있다.

AM_MEDIA_TYPE media_type;
//...
det->get_StreamMediaType(&media_type);
//...
WAVEFORMATEX *pAudioHeader = (WAVEFORMATEX *)media_type.pbFormat;

위의 WAVEFORMAT에서 채널 수, 샘플 레이트 등등을 알 수 있다. 해당 구조체를 MSDN에서 살펴보면 다음과 같다.

typedef struct {
WORD wFormatTag;
WORD nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD nBlockAlign;
} WAVEFORMAT;

내부 변수 이름만 봐도 대충 어떤 것인지 쉽게 짐작할 수 있다.
이렇게 획득한 정보를 이용해서 ACM 변환 과정시 acmStreamOpen 함수에 들어갈 source와 destination 포멧 정보를 입력할 수 있다.

추가로 직접 MP3 포멧을 읽어서 정보를 획득하는 방법을 간략하게 보겠다. 아래 코드는 Alessandro Angeli의 포스팅에서 가져온 것이다. 아직 코드가 완성된 것이 아니기 때문에 그대로 사용해서는 안된다. 이외에 다른 곳에서도 다양한 코드들이 있으니 직접 찾아서 사용해보길 바란다.
단지 어떤 방법으로 처리되는지에 대한 예제만 보인 것이다.

[열기] MP3 포멧 읽기

#include <stdlib.h>
#include <stdio.h>

#define MPA_SYNC_CODE 0xFFE00000UL

struct {
int major;
int minor;

} versions[/* version */] = { {2,5}, {0,0}, {2,0}, {1,0}, };

int layers[/* layer */] = { 0, 3, 2, 1, };

char *booleans[/* boolean */] = { "no", "yes", };

int bitrates[4 /* version */][4 /* layer */][16 /* bitrate */] = {
{ /* V2.5 */
/* L0 */ { 0, },
/* L3 */ { 0, },
/* L2 */ { 0, },
/* L1 */ { 0, },
},
{ /* V0.0 */
/* L0 */ { 0, },
/* L3 */ { 0, },
/* L2 */ { 0, },
/* L1 */ { 0, },
},
{ /* V2.0 */
/* L0 */ { 0, },
/* L3 */ { 0, },
/* L2 */ { 0, },
/* L1 */ { 0, },
},
{ /* V1.0 */
/* L0 */ { 0, },
/* L3 */ { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256,
320, -1, },
/* L2 */ { 0, },
/* L1 */ { 0, },
},

};

int samplerates[4 /* version */][4 /* samplerate */] = {
/* V2.5 */ { 11025, 12000, 8000, -1, },
/* V0.0 */ { -1, -1, -1, -1, },
/* V2.0 */ { 22050, 24000, 16000, -1, },
/* V1.0 */ { 44100, 48000, 32000, -1, },

};

char *chanmodes[] = { "stereo", "joint stereo", "dual channel", "mono", };

struct {
int intensity;
int ms;

} extensions[] = { {0,0}, {1,0}, {0,1}, {1,1}, };

char *emphasises[] = { "none", "50/12 ms", "(reserved)", "CCITT J.17", };

int coeffs[4 /* version */][4 /* layer */] = {
/* V2.5 */ { 0, 0, 0, 0, },
/* V0.0 */ { 0, 0, 0, 0, },
/* V2.0 */ { 0, 72000, 72000, 24000, },
/* V1.0 */ { 0, 144000, 144000, 48000, },

};

int main(int argc, char *argv[])
{
int rc = 0;
FILE *hf = NULL;
unsigned long hdr = 0x00000000UL;
unsigned char b;
long offset;
int version;
int layer;
int protection;
int bitrate;
int samplerate;
int padding;
int private;
int chanmode;
int extension;
int copyright;
int original;
int emphasis;
int size;

printf("*** fopen(\"%s\")...\n",argv[1]);
if(NULL == (hf = fopen(argv[1],"rb"))) { rc = errno; goto exit; }

while(1) {
if(fread(&b,1,1,hf) < 1) { rc = errno; goto exit; }
hdr = (hdr << 8) | b;
if((hdr & MPA_SYNC_CODE) == MPA_SYNC_CODE) {
if((offset = ftell(hf)) < 0) { rc = errno; goto exit; }
offset -= 4;
printf("*** frame header found @ 0x%08lX (%ld):\n",offset,offset);
/// hdr = hdr & ~MPA_SYNC_CODE;
version = (hdr >> 19) & 0x03;
layer = (hdr >> 17) & 0x03;
protection = (hdr >> 16) & 0x01; protection = 1 - protection;
bitrate = (hdr >> 12) & 0x0F;
samplerate = (hdr >> 10) & 0x03;
padding = (hdr >> 9) & 0x01;
private = (hdr >> 8) & 0x01;
chanmode = (hdr >> 6) & 0x03;
extension = (hdr >> 4) & 0x03;
copyright = (hdr >> 3) & 0x01;
original = (hdr >> 2) & 0x01;
emphasis = (hdr >> 0) & 0x03;
printf(">>>\t version .. = (%d) MPEG
%d.%d\n",version,versions[version].major,versions[version].minor);
printf(">>>\t layer .... = (%d) Layer %d\n",layer,layers[layer]);
printf(">>>\t protection = (%d) %s\n",protection,booleans[protection]);
printf(">>>\t bitrate .. = (%d) %d
Kbps\n",bitrate,bitrates[version][layer][bitrate]);
printf(">>>\t samplerate = (%d) %d
Hz\n",samplerate,samplerates[version][samplerate]);
printf(">>>\t padding .. = (%d) %s\n",padding,booleans[padding]);
printf(">>>\t private .. = (%d) %s\n",private,booleans[private]);
printf(">>>\t chanmode . = (%d) %s\n",chanmode,chanmodes[chanmode]);
printf(">>>\t extension = (%d) Intensity stereo = %s, MS stereo =
%s\n",extension,booleans[extensions[extension].intensity],booleans[extension
s[extension].ms]);
printf(">>>\t copyright = (%d) %s\n",copyright,booleans[copyright]);
printf(">>>\t original . = (%d) %s\n",original,booleans[original]);
printf(">>>\t emphasis . = (%d) %s\n",emphasis,emphasises[emphasis]);
size = coeffs[version][layer]
* bitrates[version][layer][bitrate]
/ samplerates[version][samplerate]
+ padding - 4;
printf(">>>\t payload .. = %d bytes\n",size);
if(fseek(hf,size,SEEK_CUR)) { rc = errno; goto exit; }
}
}

exit:
printf(">>> rc = %d\n",rc);
if(hf) (void)fclose(hf);
return rc;

}



자동으로 Destionation 파일 포멧 얻기

ACM에서 source와 destionation 포멧을 설정하는 것은 매우 민감하다. 잘못된 값이 입력되면 변화과정시 에러가 발생하면서 중단이 된다. Source 정보를 원래 파일에서 추출해서 입력하면 되지만 destination이 문제이다. 다양한 설정 값이 존재하기 때문이다.

이를 위해서 ACM에서는 source 포멧에 따라 destination 포멧을 결정해주는 API가 있다. 바로 acmFormatSuggest 함수이다. 이 역시 MSDN를 살펴보면 되지만, 예제가 없기 때문에 실제 사용에 있어서 어려움이 있다.

아래 예제는 다른 사이트에서 참고해서 다시 간략하게 정리해서 코딩했다.
이 예제를 MP3포멧에서 PCM으로 변환하기 위해서 포멧을 결정하는 코드이다.

[열기] acmFormatSuggest 예제

DWORD maxFormatSize = 0;
MMRESULT mmr = 0;

mmr = acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &maxFormatSize);


// 아래는 source인 mp3 포멧을 지정해준다.
LPMPEGLAYER3WAVEFORMAT mp3format = (LPMPEGLAYER3WAVEFORMAT) new BYTE[maxFormatSize];
memset(mp3format, 0, maxFormatSize);

mp3format->wfx.cbSize = MPEGLAYER3_WFX_EXTRA_BYTES;
mp3format->wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3;
mp3format->wID = MPEGLAYER3_ID_MPEG;
mp3format->wfx.nChannels = 2; // 스테레오
mp3format->wfx.wBitsPerSample = 0; // Must be 0?
mp3format->wfx.nBlockAlign = 1; // Must be 1?
mp3format->wfx.nSamplesPerSec = 22050;
mp3format->wfx.nAvgBytesPerSec = mp3format->wfx.nSamplesPerSec * mp3format->wfx.nChannels*2; // 16bit 데이터로 간주
mp3format->fdwFlags = MPEGLAYER3_FLAG_PADDING_OFF;
mp3format->nBlockSize = 512; // 이값은 임의로 지정했다.(블럭크기인데, 정확한 용도는...)
mp3format->nFramesPerBlock = 1;
mp3format->nCodecDelay = 1393;

// 아래는 destination인 pcm 포멧을 지정한다. 포멧에 지정되는 값은 아주 간략하다.
WAVEFORMATEX *waveOut = (WAVEFORMATEX*)new Byte[maxFormatSize];
memset(waveOut,0, sizeof(WAVEFORMATEX));

waveOut->cbSize = static_cast<WORD>(maxFormatSize) - sizeof(WAVEFORMATEX); // 추가되는 정보 크기
waveOut->wFormatTag = WAVE_FORMAT_PCM; // 변환될 포멧을 지정한다.

mmr = acmFormatSuggest(NULL, (LPWAVEFORMATEX)mp3format, waveOut, maxFormatSize, ACM_FORMATSUGGESTF_WFORMATTAG);



결과로 waveOut에서 값들이 채워질 것이다. 그래로 사용해도 되고 때에 따라서 값을 변경도 가능하다.


다중 포멧 변환

앞에서 포멧 변화은 단순하다. 입력 포멧과 출력 포멧만 정해주면 된다. 그러나 실제로는 단순하지 않다. ACM에서는 때때로 여러번 변환해야 원하는 포멧을 얻을 수 있다. 즉 앞에서 변환과정을 한번이 아닌 두번 이상 거치는 것이다. 보통 source와 destinatin 포멧이 서로 다른 경우, 특히 둘다 PCM이 아니거나 어느 하나가 아닌 경우에 종종발생한다.

MSDN에 따르면 16-bit, 44-kHz, stereo PCM을 11-kHz mono ADPCM으로 변화를 하려면, 바로 변환은 힘들고 중간에 16-bit, 11-kHz, mono PCM으로 변환한 후에 ADPCM으로 변환해야한다.

16-bit 44-kHz stereo PCM--<변환>-->16-bit 11-kHz mono PCM--<변환>-->11-kHz mono ADPCM

만약 source가 PCM이 아니면 먼저 PCM으로 변환을 한후 작업을 해야한다. 일반적으로 가장 간단한 변환 단계는 source와 destination이 모두 PCM이면 된다. 혹은 변환하는 destination이 채널이나 비트레이스, 비트 수는 일치하고 단순히 PCM으로만 변환해도 간단하게 한번의 변환으로 종료된다.

다시 정리하면 다음과 같다.

  • Source가 PCM이 아니면 PCM으로 변환
  • Destination이 PCM이 아니면 source에서 PCM으로 변환하고 최종 포멧으로 변환
  • Source와 destination이 PCM이면 직접 변환
  • Source와 destination이 PCM이 아니면 더많은 단계를 거쳐서 변환

이외에 더많은 정보를 원하면 아래 "Audio Compression Manager"를 참고하길 바란다.


MSACM에서 MP3 지원


윈도우즈에서는 기본적으로 MP3 인코딩과 디코딩이 가능하다. 이중에서 인코딩을 할 수 있는 포멧에 제한이 있다. 즉 56kbps/24kHz 이상으로 인코딩은 불가능하다.

이를 확인할 수 있는 방법은 먼저 "C:\Windows\system32" 폴더로 이동한다. 만약 다른 드라이브나 폴더에 윈도우를 설치했다면 해당 윈도우 밑의 "system32"폴더로 이동한다. 그 안에서 "l3codeca.acm"파일을 찾는다. 해당 파일 속성에서 버전을 확인해보면, 1.9.0.305라고 되어 있을 것이다. 이 파일이 앞에서 말한 MP3를 지원해주는 ACM 드라이버이다.

만약 WMP10 이상으로 설치했다면 "system32"폴더안에 l3codecp.acm파일이 있을 것이다. 이 파일도 MP3를 지원하는 ACM 드라이버인데 이파일은 모든 비트레이트에 대해 인코딩과 디코딩을 할 수 있게 한다.

그래서 만약 프로그램을 개발하는 입장이라면 MP3에 대한 사용하기 위해서 사용자에게 WMP10버전 이상을 필수 설치 항목에 넣어둬야 한다.

  • l3codecx.acm - 디코딩 전용, 인코딩은 안됨
  • l3codeca.acm - advanced, 모든 비트레이트 디코딩 가능, 56kbps이하만 인코딩 가능
  • l3codecp.acm - professional, 모든 비트레이트를 디코딩과 인코딩 가능
  • l3codecx.ax - amc용이 아니다. WMP 6.4에 의해서 설치되는 파일(무시)


프로젝트

아래 프로젝트는 MP3파일을 PCM으로 변환하는 예제이다. 완벽한 소스가 아니며, 기능 구현이 완변하게 된 것이 아니기에 안에 소스만을 참고해서 살펴보기를 바란다.
주의! 바로 컴파일해서 실행이 안된다. 내부 변수 값을 수정해야한다.

 

Test10_acm.zip

vc++ 2003 프로젝트


Troubleshooting

ACM에서 MP3 인코딩시 Halt

MP3는 다양한 버전과 포멧이 존재한다. 그리고 인코딩 품질에 따른 조합되는 경우의 수도 상당하다.
경험에 따르면 ACM에서

#define MP3_BLOCK_SIZE 522

//... (생략)

22-kHz stereo MP3 --<변환>--> 22-kHz stereo PCM (에러 발생)



ULONG rawbufsize = 0;

mmr = acmStreamSize(g_mp3stream, MP3_BLOCK_SIZE, &rawbufsize, ACM_STREAMSIZEF_SOURCE);
//...
LPBYTE mp3buf = new BYTE[MP3_BLOCK_SIZE];
LPBYTE rawbuf= new BYTE[rawbufsize];


ACMSTREAMHEADER mp3streamHead;
mp3streamHead.cbStruct = sizeof(ACMSTREAMHEADER);
mp3streamHead.cbSrc = mp3buf;
mp3streamHead.cbSrcLength = MP3_BLOCK_SIZE;
mp3streamHead.cbDst = rawbuf;
mp3streamHead.cbDstLength = rawbufsize;

mmr = acmStreamPrepareHeader(g_mp3stream, &mp3streamHead);

// ...

MMRESULT에서 512 에러가 발생한다. 위의 인터넷에 떠도는 예제를 그대로 사용했을 때 발생할 수 있는 문제이다. 512에러의 의미는 다음과 같다.

512 - ACM Operation Not Possible.

즉, ACM를 처리할 수 없다라는 의미이다. 원인이 뭘까? 한번 여러분이 곰곰히 생각해볼라.

정답:

이는 실제 해결책인지는 확인하지 못했다. 경험에 의한 해결이라고 보면 된다. 아직 ACM에대해서 정확히 알지 못하므로 단언하지는 못하겠다.

위의 MP3_BLOCK_SIZE의 522 값을 512값으로 변경하면 된다. 그러면 이상없이 인코딩이 된다.
근데 이렇게 하면 소리가 이상하게 들린다. 문제가 해결되었다고 생각했는데 다신 산이 나타났다.

해결:

정말 삽질 많이 했다. 짜증난다. 512값으로 변경된 것이 이상없다는 가정하에서 디버깅을 했지만 원인이 무었인지 몰랐다. 512값을 256값으로 변경해보았다. 앗!!! 소리가 조금 정상적으로 들렸다. 저품질 오디오에서도 다시 문제가 생겼다. 다시 128으로 변경했다. 이번에는 정상적으로 들렸다.
실제 이값이 인코딩시 어떤 영향을 미치는지는 알지못하겠다.
이 부분은 좀더 확인해보거나 자료가 필요하다. 혹시 이에 대해 알고 계신분은 저의 메일이나 댓글로 남겨주시길 바랍니다.


오디오 소리가 헬륨가스 먹은것 처럼 이상한 소리로 인코딩

간혹 오디오를 인코딩했는데 소리가 헬륨가스 먹으것 처럼 이상한 소리를 내면서 재생시간도 절반으로 줄어드는 경우가 있다.

해결:
이는 22050Hz 오디오를 44100Hz 오디오로 바로 인코딩했을 경우 발생한다. 즉 22050Hz 오디오를 44100으로 변환하는게 아니라 원래는 22050Hz인데 44100Hz으로 그냥 인코딩하면서 발생한다.
다시 22050Hz으로 인코딩하거나 중간에 44100Hz으로 인코딩하는 과정을 추가해서 처리하면 된다.



참고사이트:
Converting Data from One Format to Another, Microsoft(R)
DirectShow MediaDet를이용해미디어정보읽기
Multistep Format Conversion, Microsoft(R)
Audio Compression Manager, Microsoft(R)
acm Decoders, mp3-tech.org
Alessandro Angeli, Reading the MP3 file/frame format


MP3 참고사이트:
id3 (http://www.id3.org/)
mp3-tech(http://www.mp3-tech.org/)

기타 오디오 관련 자료:
Sampling Sound in Windows 32(http://www.relisoft.com/Freeware/recorder.html)
Simple DirectMedia Layer (http://www.libsdl.org/)
Steven De Toni, Simple Audio Out Oscilloscope and Spectrum Analyzer(http://www.codeproject.com/KB/audio-video/oscilloscope.aspx?df=100&forumid=324752&exp=0&select=1965375)

==출처

작성자: 박재성(omailto:ospace114@empal.컴)

** 편의상 평어를 사용하겠습니다. 제발 돌은 던지지 마세요. ㅠ.ㅠ
** 이자료를 어떤 분이 퍼가실지 모르지만, 퍼가실때에는 출처를 밝혀주세요.

'Program Visual C++' 카테고리의 다른 글

Windows8 개발환경..  (0) 2012.11.05
AVIStreamWrite code..  (0) 2012.10.10
ActiveX 권한2  (0) 2012.04.18
ActiveX 권한..1  (0) 2012.01.12
레지스트리 등록 (RegCreatekeyEx, RegDeleteValue, RegOpenkeyEX, RegSetValueEx  (0) 2012.01.05

AVIStreamWrite code..

Program Visual C++ 2012. 10. 10. 13:32 Posted by HisPark

 

I tried to use this to add a .wav to an existing .avi (although I had a class CWaveSoundRead). If you check the return codes, you get to AVIStreamWrite() which returns 0x80044065, which turns out to be AVIERR_UNSUPPORTED.

 

In hindsight, I'd say you called AVIFileCreateStream() before you filled in the AVISTREAMINFO object. Actually, now that I see it, it's hard to imagine your code compiling as-is, since audioInfo is defined AFTER AVIFileCreateStream!

 

Here's something I did, although it still mistakes the audio stream length:

struct FmtChunk {

char id[4]; //="fmt "

unsigned long size; //=16 or 0x28

short wFormatTag; //=WAVE_FORMAT_PCM=1

unsigned short wChannels; //=1 or 2 for mono or stereo

unsigned long dwSamplesPerSec; //=11025 or 22050 or 44100

unsigned long dwAvgBytesPerSec; //=wBlockAlign * dwSamplesPerSec

unsigned short wBlockAlign; //=wChannels * (wBitsPerSample==8?1:2)

unsigned short wBitsPerSample; //=8 or 16, for bits per sample

};

 

struct DataChunk {

char id[4]; //="data"

unsigned long size; //=datsize, size of the following array

unsigned char data[1]; //=the raw data goes here

};

 

struct WavChunk {

char id[4]; //="RIFF"

unsigned long size; //=datsize+8+16+4

char type[4]; //="WAVE"

};

 

bool Q_AVI_AddWav(cstring fnameVideo,cstring fnameAudio)

// Adds a .wav file to an existing .avi (with video stream)

{

IAVIStream* m_pStreamAudio=0;

HRESULT hr;

 

AVIFileInit();

 

PAVIFILE avi;

hr=AVIFileOpen(&avi, fnameVideo,OF_WRITE,NULL);

CHECK(hr,"AVIFileOpen");

 

WavChunk wav;

FmtChunk fmt;

DataChunk dat;

 

//read wav file

FILE *fr;

int pos;

 

fr=qfopen(fnameAudio,"rb");

 

// Read header

fread(&wav,1,sizeof(wav),fr);

 

// Read 'fmt' chunk; may be 16 or 40 in length

pos=ftell(fr);

fread(&fmt,1,sizeof(fmt),fr);

if(fmt.size==40)fseek(fr,40-16,SEEK_CUR); // Skip rest of fmt

// else it's ok

 

// Read data specs

fread(&dat,sizeof(dat),1,fr);

char *buf = new char[dat.size];

qdbg("Wav data %d bytes\n",dat.size);

fread(buf,1,dat.size,fr);

qfclose(fr);

 

// set wave format info

WAVEFORMATEX wfx;

wfx.wFormatTag=fmt.wFormatTag;

wfx.cbSize=0;

wfx.nAvgBytesPerSec=fmt.dwAvgBytesPerSec;

wfx.nBlockAlign=fmt.wBlockAlign;

wfx.nChannels=fmt.wChannels;

wfx.nSamplesPerSec=fmt.dwSamplesPerSec;

wfx.wBitsPerSample=fmt.wBitsPerSample;

 

// create audio stream

AVISTREAMINFO ahdr; ZeroMemory(&ahdr,sizeof(ahdr));

ahdr.fccType=streamtypeAUDIO;

ahdr.dwScale=wfx.nBlockAlign;

ahdr.dwRate=wfx.nSamplesPerSec*wfx.nBlockAlign;

ahdr.dwSampleSize=wfx.nBlockAlign;

ahdr.dwQuality=(DWORD)-1;

hr=AVIFileCreateStream(avi, &m_pStreamAudio, &ahdr);

CHECK(hr,"AVIFileCreateStream");

if(hr!=AVIERR_OK) {if (buf) QDELETE_ARRAY(buf); /*delete[] buf;*/ return false;}

hr = AVIStreamSetFormat(m_pStreamAudio,0,&wfx,sizeof(WAVEFORMATEX));

CHECK(hr,"AVIStreamSetFormat");

if(hr!=AVIERR_OK) {if (buf) QDELETE_ARRAY(buf); /*delete[] buf;*/ return false;}

 

//write audio stream

unsigned long numbytes = dat.size;

unsigned long numsamps = fmt.wChannels*numbytes*8 / wfx.wBitsPerSample;

hr = AVIStreamWrite(m_pStreamAudio,0,numsamps,buf,numbytes,0,0,0);

CHECK(hr,"AVIStreamWrite");

qdbg("Write numsamps %d, numbytes %d\n",numsamps,numbytes);

QDELETE_ARRAY(buf); //if(buf)delete[] buf;

 

// Release audio stream

AVIStreamRelease(m_pStreamAudio);

 

// Close AVI

hr=AVIFileRelease(avi);

CHECK(hr,"AVIFileRelease");

 

// Close VFW

AVIFileExit();

 

return hr==AVIERR_OK;

}

'Program Visual C++' 카테고리의 다른 글

Windows8 개발환경..  (0) 2012.11.05
ACM 오디오 프로그래밍  (0) 2012.10.10
ActiveX 권한2  (0) 2012.04.18
ActiveX 권한..1  (0) 2012.01.12
레지스트리 등록 (RegCreatekeyEx, RegDeleteValue, RegOpenkeyEX, RegSetValueEx  (0) 2012.01.05

ActiveX 권한2

Program Visual C++ 2012. 4. 18. 10:21 Posted by HisPark

사내에서 Vista 지원때문에 새로 입사자를 통해 Vista지원여부와 방법들의 확인을 요

청하여 결과물로 나온 1차본이다.

여러가지 자료들도 잘 정리되어 있고 많은 정보를 링크하여 놓은 좋은 정보이다.

1. Windows Vista 표준사용자의 File Save & Open

(0) Administrator 계정으로 로그인해도 프로그램은 표준 사용자 권한으로 실행됩니다.

(1) CFileDialog를 이용해서 파일을 Save, Open 하는 프로그램을 만들어서 파일을 생성해

보았습니다.

C:\ => Root Directory에서는 경고 메시지가 나타납니다.

(Root 대신 \My Documents에 저장할 것인지를 묻습니다.)

C:\TEMP\ => 이와 같이 '쓰기 권한'이 사용자에게 부여된 폴더에는 자유롭게 저장이

가능합니다.

C:\Program Files\ => '읽기/실행 권한' 이 주어져 있습니다. 파일을 쓰는 시도를 하면

에러 없이 파일을 자유롭게 쓸 수 있는 것처럼 보이지만, 실제로 이 폴더에 쓰는 것이

아니라 C:\Users\<사용자 이름>\AppData\Local\VirtualStore\Program Files\

이 곳에 따로 저장이 됩니다. 작업 시 사용자가 C:\Program Files\ 에 저장을 지시하

면, 성공적으로 파일이 저장된 것처럼 보이지만, 물리적으로는

C:\Users\...\VirtualStore\Program Files\ 에 파일이 저장됩니다.

(그러므로 C:\Program Files\ 에 찾아가보면 파일이 없습니다.)

C:\Windows\ => \Program Files\ 폴더와 동일하게 작동합니다.

C:\Users\<사용자 이름>\AppData\Local\VirualStore\Windows\ 로 Redirection

시킵니다.

이렇게 Vista에서 자체 Redirection 시키는 디렉토리는 C:\Windows\ 와 C:\Program

Files\ 두 Folder만이 확인 되었습니다. C:\Users\...\VirtualStore\ 의 하위 Folder가

\Windows\ 와 \Program Files\ 밖에 없기 때문에, 현재로서는 이 두 Folder가 전부인

듯 합니다.

(2) 역시 CFileDialog를 이용해서 C:\Windows\ 와 C:\Program Files의 파일을 읽어보면,

먼저 C:\Users\...\VirtualStore\Windows\ 와 C:\Users\...\VirtualStore\

Program Files\ 에 그 파일이 있는지를 확인해 본 후, 있으면 그 VirtualStore에 있는 파

일을 읽어오고, 없으면 C:\Windows\ 와 C:\Program Files\ 에 그 파일이 있는지 확

인해서 읽어옵니다. 두 폴더에 같은 이름의 파일이 모두 존재할 때, 어떤 파일이 열릴 것인

가에 대한 정리입니다.

\VirtualStore\Program Files\ C:\Program Files\ 어느 쪽의 파일이 열리는가?

X X 파일 없음
X O C:\Program Files\
O X \VirtualStore\Program Files\
O O \VirtualStore\Program Files\
즉, 항상 VirtualStore가 우선적으로 검토되고, 만일 적절한 파일을 발견하지 못한 경우에

한해서만, 물리적인 저장 위치인 C:\Program Files를 access한다고 정리하시면 됩니다.

(3) 김명신의 즐거운 하루 블로그에서 잘 정리된 정보를 얻을 수 있습니다.

- Vista Note #1. 환경설정 파일 저장, http://himskim.egloos.com/1471216

- Vista Note #2. Virtualization, http://himskim.egloos.com/1473911

(4) Registry에 대한 저장은 아직 직접 실행해보지 않았습니다.

위에 소개된 블로그에 나타나있는 텍스트를 그대로 붙여넣기 합니다.

Registry의 경우는 어떨까요? 핵심적인 메커니즘은 파일과 유사합니다. Registry

Virtualization은 HKEY_LOCAL_MACHINE\Software 이하만을 가상화 합니다. 즉

HKEY_LOCAL_MACHINE\Software\AppKey1 에 접근을 시도하면 실제로는

HKEY_USERS\<User SID>_Classes\VirtualStore\Machine\Software\AppKey1

에 접근하게 됩니다.

Registry Virtualization에 대한 보다 자세한 내용은 Registry Virtualization 를 참고하십시

오.

마지막으로 이러한 virtualization 기능은 다음 version의 windows 출시나 혹은 그 이전

에라도 제거될 수 있다는 것입니다. 실제로 64bit Vista에서는 이러한 virtualization 기

능이 전혀 동작하지 않습니다. 따라서 virtualization 기능에 대해서 전혀 기억하지 못하

더라도 다음 한가지만은 기억해야 합니다.

Program Files나 windows 혹은 windows/system 폴더 그리고

HKEY_LOCAL_MACHINE\Software 등에는 파일 혹은 정보를 수정,삭제,저장할 수 없으

므로 프로그램을 수정해서 다른 위치에 이러한 정보를 저장하도록 고쳐야 한다는 것입니

다.
(5) '관리자 권한으로 실행' 하면 물리적으로 C:\Program Files\ 혹은 C:\Windows\ 에도

파일을 저장할 수 있습니다.

(관리자 권한으로 C:\Users\...\VirtualStore\Program Files\ 에 저장하려면, 직접

찾아가야 됩니다!)

(6) 프로그램을 Install 하거나 Uninstall 할 때는 VirtualStore 저장하지 않고,

C:\Program Files\ 에 접근할 수 있도록 권한을 줍니다.

이에 대한 편의를 위해 Microsoft에서는 'MSI(Microsoft Windows Installer) 3.1 및 4.0을

사용하는 것' 을 권장하고 있습니다.

p.s. 파일명에 Setup이나 Install이 있으면 Vista에서 자동으로 권한을 상승시켜 준다고

합니다.

이전에 쓰던 프로그램들을 모두 재패키징해서 재배포해야 하는 수고를 덜어주기 위해

MS에서 지원하는 레거시 지원이라고 합니다.

http://aromi.tistory.com/29 에서 관련된 내용을 찾아보실 수 있습니다.

(7) http://www.microsoft.com/korea/windows/ie/ie7/technology/default.mspx 에서

Microsoft가 제공하는 GuideLine을 다운 받을 수 있습니다.

최소권한부여 환경에서 어플리케이션 개발자를 위한 모범사례 및 지침(Word 문서, 720KB)

2. Windows Vista 표준사용자의 ActiveX

(1) 위의 File Write 하는 프로그램을 ActiveX로 바꾸어서 같은 방법으로 실행을 하였습니다.

Local Resource로 실행 시, 보호모드로 작동하지 않기 때문에 IIS server를 열어서 접속

하는 방법으로 테스트를 진행하였습니다.

(2) Regsvr32 으로 등록 해줄 때, '관리자 권한' 이 필요합니다.

cmd.exe 를 '관리자 권한으로 실행' 하거나, 아예 regsvr32.exe 의 실행 권한을 바꾸어 버

리는 수가 있습니다.

(Administrator가 아닌 사용자가 '관리자 권한으로 실행' 을 시도하면 관리자 권한이 있는

계정의 비밀번호를 묻습니다.)

(3) Regsvr32 등록이 잘 이루어졌다면 VC6 에서 작성한 ocx가 보호모드에서 정상적으로 작

동합니다. 앞서 진행한 것과 동일한 테스트를 진행하였고, File Save & Open은 (기대한

바와 같이) 위와 동일한 결과가 나타납니다.

IE7.0을 '관리자 권한으로 실행' 하면 IE7.0 내의 ActiveX 프로그램에서도 VirtualStore 말

고, C:\Windows\ 와 C:\Program Files\ 에 저장할 수 있습니다.

(4) SNAIPER의 조그마한 블로그 에 가보시면 Windows Vista 를 위한 ActiveX 마이그레이션

작업 절차 정리 문서 1 (http://snaiper.tistory.com/206)

문서를 참고하시면 VC6 에서 작성된 ActiveX를 Vista용으로 Migration 하는 경험담이 잘

정리되어 있습니다. (PDF 파일이 Adobe Reader 구버젼에서는 열리지 않는 듯 합니다.)

(5) 기타 ActiveX 에 관해 참고할만한 게시물들을 링크시켜 두겠습니다.

MS Windows Vista와 ActiveX에 대한 오해 http://aromi.tistory.com/29

(위에서 이미 링크한 그 블로그 입니다.)

Vista 와 ActiveX 그리고 보안

- (1) http://blog.naver.com/p4ssion/50013835648

(사실 이 글보다는 이 다음 글에 원하는 내용이 들어 있을 것 같습니다.)

비스타에서 강제로 액티브X 설치

- http://itviewpoint.com/tt/index.php?pl=2445

비스타와 보안 (Vista & ActiveX) 관련하여

- http://blog.naver.com/simonsayz?Redirect=Log&logNo=120034059886

Vista에서 ActiveX 사용 팁 한가지

- http://www.delmadang.com/cwb-bin/CrazyWWWBoard.exe?mode=read&num=4136&page=2&db=dmdlec3&backdepth=3

이어지는 답글 Code Sign 생략하기

- http://www.delmadang.com/cwb-bin/CrazyWWWBoard.exe?backdepth=1&db=dmdlec3&mode=read&num=4139

(6) http://www.microsoft.com/korea/windows/ie/ie7/technology/default.mspx 에서

Microsoft가 제공하는 GuideLine을 다운 받을 수 있습니다.

Internet Explorer 7 보호 모드의 이해 및 작업 (Word 문서, 366KB)

Internet Explorer 7 기술 개요 읽기 (Word 문서, 2.09MB)

(Internet Explorer 7에서는 웹 및 응용 프로그램 개발자를 위해 최종 사용자 환경, 보안,

개인 정보 보호, 플랫폼이 대폭적으로 향상되었습니다. 이 놀라운 새 버전의 기능에 대해

알아보려면 이 문서를 읽어보십시오.)

Internet Explorer 7.0 호환성 백서 (Word 문서, 1.92MB)

(이 문서에서는, 변화된 IE 7.0으로 인해 발생할 수 있는 전형적인 문제 및 현재 다양한 사

이트에서 보고되는 사례들을 알아보고 그에 대한 대처 방법을 소개합니다.)

위의 자료를 보면 Vista의 권한에 대한 대처 방안을 미약하게나마 생각할 수 있을 거 같다.

[출처] Windows Vista - VirtualStore, ActiveX (1차)|작성자 자유인