SettingUI : https://ghost1372.github.io/settingsui/

2022년 7월 25일 기준 SettingsUI의 Nuget 버전은 2.1.6이다. 이걸 이용해서 .Net6 용 WPF를 프로젝트를 만들고 nuget을 추가 후 바로 빌드하면 빌드 안될수 있다. 내가 발생한 부분에 대한 내용 및 해결방법을 정리한다.

1. nuget 자체가 설치 안될수 있다

이건 다른사람은 가능할 수 있다. 현재 작업환경이 윈도11(개발자 프리뷰)인데 그러다 보니 버전이 안맞아 생기는 문제로, 이는 프로젝트의 최소 OS 지원버전을 경고창에 나오는대로 맞춰주면 넘어간다.

대충 10.0.19041로 해주면 된다

2.  SettingUI의 icon을 찾을수가 없다?

nuget이 다운로드된 경로 이하에 icon이 없어서 실행이 안된다는 경고가 뜰수 있는데, 해당경로는 아래와 같다. (위에서 10.0.19041 버전을 설정했을 경우)

C:\Users\Admin\.nuget\packages\settingsui\2.1.6\lib\net6.0-windows10.0.19041

그럼 그 이하에 원하는 경로에 맞게 

SettingsUI\Assets\icon.png 파일을 넣어주면 된다. 폴더와 파일이 아예 없어서 발생하는 문제이니 일단 아무 128짜리 그림파일 하나 놔두면 된다.

일단 여기까지...

VS2019에서 .NET5 프로젝트들을 생성하면 빌드시
Bin\Debug\net5.0 이나 net5.0-windows 같은 폴더 이하에 빌드파일들이 생성되는것을 확인할 수 있는데,

이렇게 되면 여러 프로젝트들을 묶은 솔루션 같은 경우 전체 프로젝트들을 빌드시 빌드파일들의 경로가 중구난방이 된다.

기존의 4.8이하의 버전에서는 프로젝트 속성에서 설정한 출력경로 거기에 바로 파일들이 떨어졌었는데 이번에는 .Net5의 특성으로 인해 각종 설정이 붙다보니 뒤에 계속 접미사가 붙는것 같다.

 

솔루션의 해당 프로젝트마다 프로젝트 편집기를 통해 xml파일을 편집하는 화면으로 넘어가서 

맨 아래쪽 네 라인을 추가하면 된다

<PropertyGroup>
   <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
   <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>

위 코드를 추가하면 프로젝트 속성에서 설정한 출력경로로 바로 어셈블리들이 떨어지게 된다.

각 프로젝트마다 설정해 줘야 한다.

 

만일 이미지를 어느 패널에 출력한다고 할때나,

음악파일을 출력하고자 할때,


그 파일들이 어느 폴더에 있는데 여러 종류의 컨텐츠들이 섞여있는 폴더일때는 가려서 음악파일만 출력한다던지, 이미지 파일만 출력한다던지 해야한다.


예를 들어서,

내 그림 폴더에 있는 이미지 파일들만 불러서 출력하려고 하는데, 

string strPicPath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);

로 내 그림 폴더의 경로를 받아서

DirectoryInfo asd = new DirectoryInfo(strPicPath);

FileInfo[] arrFiles = asd.GetFiles();

m_strArrjpgPath = new string[arrFiles.Length];

int nCount = 0;

foreach(FileInfo File in arrFiles)

{

    m_strArrjpgPath[nCount++] = File.FullName;

}

이렇게 작성 한 후

윈폼.m_pnlMainBase.BackgroundImage = Image.FromFile(m_strArrjpgPath[0]);

이렇게 하면 m_strArrjpgPath[0]에는 어떤 파일이 들어가는지 아는가?


바로 "DeskTop.ini". 윈도 탐색기에서 따로 설정을 하지 않은 이상 숨김파일이라 보이지도 않을, 폴더 설정 파일이 불려지게 된다.

저렇게 바로 Image.FromFile에 경로로 들어가면 Exception이 발생하며 이미지 파일이 아닌 일반 텍스트 파일임에도 불구하고 로드를 실행하며 OutOfMemory Exception이 발생하는 예상과는 다른 예외를 뱉어낸다.


어찌됐든, 

1. 해결방법 첫번째는 위의 상황에서 Exception이 발생하니 catch로 상황을 잡아주면 되는거고(안잡아주면 에러발생으로 팝업 경고뜸)

2. 아님 try~catch로 꼭 처리 안하더라도 검증된 이미지 파일만 넘겨주면 되는것이므로 컨텐츠의 헤더를 분석하여 이미지파일인지를 확인 후 넘겨주는 방식이 있다. 

하지만 넘겨줄때마다 파일을 분석해야하는 오버헤드가 생길 수 있고, 또한 이미지 파일이더라도 이미지 출력이 안될수 있는 정말 어이없는 예외상황을 처리해야하는 try~catch가 필요하다면 결국은 try~catch 하나만으로 처리하는것이 오버헤드를 줄일 수 있는 나은 방법이 아닐까.


string strTempPath = Path.GetTempPath();


이거 쓰세요......

돌겠눼.......


.........................................................................................................................

사용자 계정 폴더 하위의 temp에 직접 임시파일을 기록할 일이 있다.

이 폴더는 윈도가 관리하는 폴더라 나중에 디스크 정리 때 같이 정리할 수 있으며,

윈도가 다중 사용자 계정일 상태에는 단일 사용자마다 temp가 따로 주어지므로 보안(?)이나 관리측면에서 용이한 면이 있다.


C#에서는 전역 클래스로 Environment클래스가 제공되며 이 클래스를 통해 실행되는 시스템의 OS관련 정보, 실행 프로그램에 대한 위치 등에 대한 관련 정보, 그리고 윈도가 제공하는 시스템 디렉토리등에 대한 정보들을 제공하는데 


보통 C:\Documents and Settings\{USERNAME}\ApplicationData 경로는 Environment.SpecialFolder의 열거자중에 골라서 리턴받을 수 있으며 다양하게 제공하기도 한다.


하지만  정작 {USERNAME}\Local Settings\Temp 의 경로, 즉 Temp 폴더는 열거자에서 찾아볼 수 없다.

그러나 다른 방법으로 Temp경로를 얻을 수 있는데


바로

Environment.GetEnvironmentVariable("temp");

을 통해 얻을 수 있다.

(이걸 그대로 출력하면 C:\Document~1\ ~~" 이런식으로 Dos에서의 출력처럼 8.3자 제한에 짤려서 출력이 된다(시샵 Console용으로 출력시. 그래서 아래를 참고)


위 메소드를 통해 

"C:\Documents and Settings\{USERNAME}\Local Settings\Temp"까지 얻을 수 있고 이걸 그대로 사용하면 파일로 인식되는 모호성이 발생하기 때문에 나는  


Path.GetFullPath(Environment.GetEnvironmentVariable("temp") + "\\");


로 static선언을 통해 리턴받아 사용한다.


그렇게 되면 정확하게 

"C:\Documents and Settings\{USERNAME}\Local Settings\Temp\"

이 출력되며, 그 뒤에 파일이나 하위 디렉토리를 추가하여 다른 작업을 할 수 있다.


Temp 의미상 임시로 즉각적인 임시파일의 기록이나 폴더를 사용하기 위한 용도로만 사용해야 맞는것이며 부팅하거나 다른 작업시 운영체제가 임의로 정리 가능한 권리를 가지는 폴더이기 때문에 파일의 일정기간의 저장을 위한 용도로는 사용하지 않는것이 좋다.


특정 프로그램에서 일정 기간의 저장, 예로 config.xml같은 실행시에만 초기에 프로그램의 환경설정 변수를 잡아주는 용도로 사용하는 파일을 저장할때는 C:\Documents and Settings\{USERNAME}\ApplicationData 같은 ApplicationData의 하위폴더에 대표하위폴더를 생성해서 그 안에서 작업하는것이 일반적이다.


한글 MSDN : http://msdn.microsoft.com/ko-kr/library/system.environment(v=vs.80).aspx (System.Environment 클래스) (.net v2.0)



요즘 좀 프로젝트 말미라 빡시게 돌아간다.


하면서 개인 스터디를 하고 있는데, 하면서 알게된 기초적인 프로그래밍 문법





다들 if문과 switch문이 비슷한 용도로 쓰이는것을 알것이다.

물른 다르지만 비슷하다.


즉 조건식으로 분기를 하기위해 사용되는데 개발자에 따라, 회사간의 정책에 따라 사용하는 방법은 다 다르겠지

Whatever!


나는 vs2005를 사용(.Net 2.0)


if문과 switch문에서 int a = 10을 주고

if( a == 0) ~

else if (a == 1) ~

else if (a == 2) ~

else if (a == 3) ~

~~~


이렇게 흘러가는데 만약 조건에 맞는 식이 저 밑에 있다고 치면 if문을 위에서부터 하나씩 전부 검사하고 내려온다.

이것을 어셈블리어로 확인할 수 있다.


if문에 진입하기 직전에 BreakPoint를 걸고 실행하면 if문앞에서 걸리면서 소스가 나올것이고,

소스화면에서 마우스 오른쪽 -> Go to Disassembly를 통해 확인할 수 있다.


대충 보면(나도 어셈블리어는 모른다 갸초보라 ㅠㅠ) jmp가 그 주소로 이동한다는  소리인데,

if문은 각 else if 문이 끝나는 지점에서 다음 else if 문의 주소로 jmp를 한다.(왜 jmp명령어를 사용하는지는 어셈블리어를 몰라서 모름. return 도 아닌데..)

if문은 이런식이고.....


switch문도 BreakPoint를 직전에 걸고 실행한 후 Disassembly를 통해 보면 switch인 조건식 검사 구문에서 다음 첫 case를 넘어가기 전까지 여기서 조건을 검사한 후 바로 해당 case로 넘어간다. case가 없으면 default가 지정되어 있으면 거기로 넘어가고  아니면 switch문 밖으로 jmp해버린다.


이것이 바로 if문과 switch문의 차이이기 때문에 실제 실행 속도면에서도 조건식이 많을경우 switch문이 빠른것을 알 수 있다.


무슨 키가 입력되는지
몇번 입력됐는지
마우스의 유효픽셀 이동거리는 얼마인지
-> 이거슨, 문제가 있는게, 1920x1680모니터에서 화면 끝으로 마우스를 한번에 빨리 움직이면 1680픽셀이 다 인식이 되는것이 아님. 하지만 천천히 움직이면 다 인식됨

 c#으로 만들었고,
윈도xp에서는 트레이로 축소시키면 몇초간의 딜레이가 있음
윈도7 에서는 딜레이 없이 작동 잘 되는듯


차후예상 추가기능 
마우스 클릭 세기
키보드의 키중 제일 많이 입력된 키 랭크 매기기
마우스 픽셀 이동거리 통한 모니터 크기를 받아서 실제 이동거리를 구하기(하루에 몇 m를 갔니 이런거)
 

ClicknTouch.exe

이 글은 C++기초플러스(5판)에서 발췌한 글입니다. 
 
실전 프로그래밍 : 조건 표현식의 연산자와 버그 예방법

'같다' 연산자(==)를 사용할 곳에 대입 연산자(=)를 입력하는 실수를 없애기 위하여,
많은 프로그래머들이 더 직관적인 표현인 'variable == value' 를 'value == variable'로 뒤집어 사용하고 있다.
예를 들면, 조건을 다음과 같이 입력하면

 if (3 == nCount)

바르게 동작한다. 그러나 실수로 다음과 같이 입력하면

if (3 = nCount)

리터럴에 어떤 값을 대입하여는 시도로 간주되어 컴파일러가 에러 메시지를 내보낼 것이다.
(3은 언제까지나 3일 뿐이며, 다른값을 대입할 수 없다.) 
그러나 'variable == value'형식을 사용하면서 다음과 같이 == 대신 = 를 입력하는 실수를 범했을 때에는 

if (nCount  = 3)

컴파일러가 nCount에 무작정 값 3을 대입할 것이고, 이 조건은 참이되어 if의 true 블록을 실행할 것이다.
이것은 매우 흔하게 발생하는 에러이지만 찾아내기가 무척 어렵다.
일반적으로, 컴파일러가 에러를 쉽게 찾을 수 있도록 프로그램을 작성하는 것이 찾기 어려운 에러를 만들어 놓고 이를 고치느라 애를 먹는것보다 훨씬 낫다. 

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

요약할께요.
if문 쓸때 'if ( nCount == 3)' 이렇게 쓰는것보다 'if (3 == nCount)로 쓰는버릇을 들이는게 좋다 입니다. 

if (nCount = 3) 으로 하면 문법적으로 문제가 없기 때문에 에러검출이 되지 않습니다.
학생분들이야 코드 몇줄 안되지만, 실무로 가면 이걸 일일히 찾아서 수정하기는 어려운 일입니다. 
 
예로 (출력부분에서 예가 잘못되었지만 조건식을 중점으로 든 예이므로 이해 부탁드립니다)

int MyAge = 20;
if (MyAge = 28)

    cout << "내 나이는 28 이다" << endl;

else
{
    cout << "내 나이는 20 이다." << endl;
}

이렇게 하면
"내 나이는 28이다" 가 출력되어 버립니다. 의도에 맞지 않는거죠.
if문에서 '='을 두번 넣는걸 실수로 한번 넣어서 생긴 오류입니다. 하지만 문법적으로 문제는 없죠.
컴파일러가 에러를 잡아주지 않기 때문에 뭐가 문제지? 하면서 오랜시간 디버깅을 해야할수도 있고, 재수좋아서 한방에 끝낼수도있습니다만, 애초에 에러를 안내기 위해서는

int MyAge = 20;
if ( 28 == MyAge)

    cout << "내 나이는 28 이다" << endl;

else
{
    cout << "내 나이는 20 이다." << endl;
}

로 하면  if문의 조건식에서 false를 리턴하기 때문에 if문 안으로 들어가지 않습니다. 그래서 "내 나이는 20 이다"가 출력되는게 정상이죠.

즉, 변경할수 없는 값, 상수값을 앞에 둬야 실수로 '='으로 입력했더라도 컴파일러 수준에서 에러를 검출해 줄수 있습니다. 

ps. 언어마다 다릅니다. java같은 경우는 c와 다른 조건식의 경우를 가집니다.
java는 if문의 조건식에서 대입을 할수 없는것으로 알고있습니다.
이 글은 c와 c++의 경우로 한정하고 있습니다.(제가 아는 범위 한정입니다. 제가 틀렸을경우 신속한 지적 부탁드립니다
삽질하다 결국 ㅈㅈ 쳤는데 알고보니 다 왔었다 참나 ㅋㅋㅋㅋ

솔루션 하나를 만들고, MFC용 프로젝트와 DLL을 만들 DLL프로젝트를 만든다.
그리고 MFC에 DLL과의 연결 속성들을 알아서 정해준다. (뭐 그런것들... Projects Dependencies...zz)
그런것들은 알아서 정해주고 이제 각 DLL을 구성하는 헤더들에 같은 선언들을 해준다.

//Component.h
#ifdef DLLCOMPONENT
#define CCOMPONENT __declspec(dllexport)
#else
#define CCOMPONENT __declspec(dllimport)
#endif

class CCOMPONENT CComponent{ ~~~~~~~};
 
//Component.cpp
#define CCOMPONENT //이건 해주는지 안해주는지 까먹었다. 알아서 하셈
#include <iostream> 
~~~~ 

이런식으로 선언을 모든 DLL구성 파일들에게 해줘야 한다. "똑같이"
즉, DLL로 만들 클래스들을 저런식으로 똑같이 선언해주면 된다는 거다.

젠장.....
난 클래스마다 다르게 했더만 아니라네? ㅠㅠ
클래스 이름이 달라서 다르게 구분지어 줬더만 똑같이하라니 이런 된장 ㅠㅠㅠㅠㅠ 

추가. 이거 삽질한다고 각종 리크에러들 다 나왔음 ㅠㅠ 
출처 : http://nicejinux.net/bbs/zboard.php?id=lecture&no=66
-- 감사합니다

using System;        
using System.Diagnostics;        
using System.Windows.Forms;        
using System.Runtime.InteropServices;        

class InterceptKeys        
{        
        private const int WH_KEYBOARD_LL = 13; 
        private const int WM_KEYDOWN = 0x0100; 
        private static LowLevelKeyboardProc _proc = HookCallback;        
        private static IntPtr _hookID = IntPtr.Zero; 

        public static void Main()        
        {        
                _hookID = SetHook(_proc);        
                Application.Run(); 
                UnhookWindowsHookEx(_hookID);        
        }        

        private static IntPtr SetHook(LowLevelKeyboardProc proc) 
        {        
                using (Process curProcess = Process.GetCurrentProcess()) 
                using (ProcessModule curModule = curProcess.MainModule)        
                {
                        return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0); 
                }        
        }        
        
        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);        
        private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) 
        {        
                if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
                {        
                        int vkCode = Marshal.ReadInt32(lParam);        
                        Console.WriteLine((Keys)vkCode); 
                }        
        
                return CallNextHookEx(_hookID, nCode, wParam, lParam); 
        }
        
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);   
        
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
        [return: MarshalAs(UnmanagedType.Bool)] 
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);   
        
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 
        
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
        private static extern IntPtr GetModuleHandle(string lpModuleName);  
}


콘솔어플리케이션으로 만들고 system.window.form하나 추가해주고 
프로젝트 만들때 MFC_Test라는 이름으로 만들었다면 MFC_TestDlg.cpp파일에서 #include 부분 바로 다음에 이걸 추가하시고
#ifdef _DEBUG
#pragma comment(linker, "/entry:WinMainCRTStartup /subsystem:console")
#endif
다음에 본인이 필요한 부분에
printf("인덱스 값은 %d" , m_nIndex); 
를 추가해서 컴파일 하면 어플리케이션 창과 콘솔창이 동시에 뜨는것을 확인할수 있을것이고,
입력이나 수정, 삭제시마다 실시간으로 확인이 가능합니다.


ps. 음하하하하 한문장으로 다 썼다 ㅋㅋㅋ

+ Recent posts