posted by 갱우덩 2020. 11. 10. 21:34

A.CS 파일을 복사해서 A-backup.CS 파일을 만들어 놓으니 이런 error가 생긴다.

 

A-backup.CS 파일은 project에 컴파일 대상으로 추가해 놓지 않았는데도 컴파일 대상이 되는건지

 

이런 error 가 생긴다. C# 시작한지 2일만에 2시간 넘게 잡아 먹은 황당한 error가 이런 간단한 이유였다니~ 

 

ㅠㅠ~ 

posted by 갱우덩 2020. 6. 9. 14:34

옮겨다 놓습니다.

 

modeless dialog 생성 후 삭제 시 내부적으로 자신을 삭제하지 않을 경우 GDI 객체가 소멸되지 않는 문제가 있다.

일반적으로 modeless dialog를 닫게 되면 PostNcDestroy가 호출되지 않아서 직접 PostNcDestory 메시지를 보내줘서 호출해야 한다. PostNcDestroy 내부에서는 자신을 delete 하는 코드가 있어야 정상적으로 메모리가 해제된다.

따라서 아래와 같이 해결할 수 있다.

.h

virtual void PostNcDestroy();

.cpp

void TestDlg::PostNcDestroy() { delete this; CDialogEx::PostNcDestroy(); }

PostNcDestroy()를 virtual로 override 해놓은 다음 외부에서 해당 dialog에 DestroyWindow를 호출한다면
CWnd의 OnNcDestroy()가 호출되면서 내부에서 PostNcDestroy()를 호출하기 때문에 virtual로 선언된 PostNcDestroy()가
호출 된다.



출처: https://ymcoder.tistory.com/247 [자료 저장소]

 

 

 

posted by 갱우덩 2020. 3. 5. 20:35

누가 물어본다 java 설치했냐고?,  java 뭘 설치해야 이거에 대해서

 

어~ 라고 대답해야 할지 고민하다가 아래에 정리 잘해놓은걸 퍼다 놓아요~

 

cmd 창 열고, java -version 이라고 입력해서 메세지가 나오면 설치된거고 아니면~ 설치 안된거임.

 

그리고 자바 설치하러가면 JRE 가 다운로드 됨~ ^^

 

 

https://devbox.tistory.com/entry/%EC%8B%A4%ED%96%89

 

[Java] Java SE, JDK, JRE

Java SE, JDK, JRE - Java SE(Java Platform, Standard Edition)는 자바의 표준안이다. 자바라는 언어가 어떠한 문법적인 구성을 가졌는지와 같은 것들을 정의하고 있다. 이것은 구체적인 소프트웨어가 아니고 그..

devbox.tistory.com

 

Java SE, JDK, JRE

 

- Java SE(Java Platform, Standard Edition)는 자바의 표준안이다. 자바라는 언어가 어떠한 문법적인 구성을 가졌는지와 같은 것들을 정의하고 있다. 이것은 구체적인 소프트웨어가 아니고 그 소프트웨어의 설계도라고 할 수 있다. 소프트웨어에서는 설계도라는 표현 대신에 명세서(spec, specification)이라는 말을 사용한다. 이 명세서에 따라서 Java가 만들어지게 된다. Java SE 7은 버전 7에 대한 명세서이다. 자바는 계속 진화하고 있는 기술이다. 이 명세서는 JCP(Java Community Process, http://jcp.org)라는 조직을 통해서 만들어진다.

 

- JDK(Java Development Kit)는 Java SE의 표준안에 따라서 만들어진 구체적인 소프트웨어다. Java 개발자라면 JDK를 다운받아서 설치해야 한다. 여기에는 Java 프로그램을 실행하면 Java 코드를 컴파일하는 컴파일러와 개발에 필요한 각종 도구 그리고 JRE가 포함되어 있다. 즉 개발자를 위한 자바 버전이다.

 

- JRE(Java Runtime Environment)는 자바가 실제로 동작하는 데 필요한 JVM, 라이브러리, 각종 파일들이 포함되어 있다. 자바로 만들어진 프로그램을 구동하려고 한다면 이것을 설치한다. 일반인을 위한 자바 버전이라고 할 수 있다.

 

- JVM(Java Virtual Machine) JVM은 자바가 실제도 구동하는 환경이다. 자바로 만들어진 소프트웨어는 JVM이라는 가상화된 환경에서 구동되고, 하드웨어나 운영체제에 따라서 달라질 수 있는 호환성의 문제는 운영체제 버전에 따라서 만들어진 JVM이 알아서 해결한다. 즉 하나의 자바 프로그램을 만들면 어떤 환경에서도 실행할 수 있는 것이 바로 JVM의 역할이라고 할 수 있다.

 

JVM은 자바를 실행하기 위한 가상 기계라고 할 수 있다. 컴퓨터(머신)를 사용해서 자바를 실행하기 위한 가상 컴퓨터라고 이해하면 좋을 것이다.

가상 기계는 소프트웨어로 구현된 하드웨어를 뜻하는 넓은 의미의 용어이며, 컴퓨터의 성능이 향상됨에 따라 점점 더 많은 하드웨어들이 소프트웨어화되어 컴퓨터 속으로 들어오고 있다.

그 예로는 TV와 비디로를 스프트웨어한 윈도우 미디어 플레이어던가, 오디오 시스템을 소프트웨어화한 윈엠프 등이 있다.

이와 마찬가지로 '가상 컴퓨터'는 실제 컴퓨터(하드웨어)가 아닌 스프트웨어로 구현된 컴퓨터라는 뜻으로 컴퓨터 속의 컴퓨터라고 생각하면 된다.

 

자바로 작성된 어플리케이션은 모두 이 가상 컴퓨터에서만 실행되기 때문에, 자바 어플리케이션이 실행되기 위해서는 반드시 JVM이 필요하다.

이 관계를 그림으로 나타내면 아래와 같다.

 

cp.)

자바로 프로그래밍을 하기 위해서는 먼저 JDK를 설치해야 한다. JDK를 설치하면, 자바가상머신과 자바클래스 라이브러리외에 자바를 개발하는데 필요한 프로그램들이 설치된다.

 

(1) JDK의 bin 디렉터리의 주요 파일

1) javac.exe: 자바 컴파일러, 자바소스코드를 바이트코드로 컴파일한다.

c:\jdk1.6\work\javac Hello.java

 

2) java.exe: 자바 인터프리터, 컴파일러가 생성한 바이트코드를 해석하고 실행한다.

c:\jdk1.6\work\java Hello

 

3) javap.exe: 역어셈플러, 컴파일된 클래스파일을 원래의 소스로 변환한다.

c:\jdk1.6\javap Hello > Hello.java

 

vo.) 바이트코드

JVM이 이해할 수 있는 기계어, JVM은 바이트코드를 다시 해당 OS의 기계어로 변환되어 OS로 전달한다.

 

cf.) jar.exe: 압축프로그램, 클래스파일과 프로그램과 프로그램의 실행에 관련된 파일을 하나의 jar파일(.jar)로 압축하거나 압축해제한다.

 

cf.) JDK와 JRE

JDK: 자바개발도구

JRE: 자바실행환경

 

JDK: JRE + 개발에 필요한 실행파일(javac.exe등)

JRE: JVM+클래스라이브러리

 

프롬프트 창에서 자바 파일 실행

Hello.java 작성 ---> javac.exe --->Hello.class 생성 ---> java.exe ---> 실행



출처: https://devbox.tistory.com/entry/실행 [장인개발자를 꿈꾸는 :: 기록하는 공간]

 

posted by 갱우덩 2020. 2. 6. 13:41


SELECT * FROM member WHERE name LIKE '홍%;

member 테이블에 name이 홍 씨를 모두 검색

(뒤에 자릿수는 상관 x, 홍 만 있어도 검색)

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

SELECT * FROM member WHERE name LIKE '% 길동;

member 테이블에 name이 이름이 x길동 인 사람을 검색

(앞에 자릿수는 상관 x, 길동 만 있어도 검색)

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

SELECT * FROM member WHERE name LIKE '% 길동%;

member 테이블에 name이 이름이 x길동x 인 사람을 검색

(앞, 뒤에 자릿수는 상관 x, 길동 만 있어도 검색)

 

 

 

 

 

 

Full Text Search 라는 방법도 제공함

 

 

 

posted by 갱우덩 2020. 1. 31. 16:25

 

COMMIT ROLLBACK 를 mysql로 이해하려고 하는데~ 갑자기 SAVEPOINT 가 보이네요~ ㅠㅠ

 

내용이 좋아서 저장!

 

(펌 : https://rh-cp.tistory.com/41?category=661522 )

 

https://rh-cp.tistory.com/41?category=661522

 

(SQL) TCL(Transaction Control Language) 이란 무엇일까?

TCL(Transaction Control Language)이란? ▷ DCL(Data Control Language)에서 트랜잭션을 제어하는 명령인 COMMIT과 ROLLBACK만을 따로 분리해서 TCL이라고 표현하고 있습니다. ◈COMMIT 연산 ▷ COMMIT 연산은..

rh-cp.tistory.com

 

TCL(Transaction Control Language)이란?

 ▷ DCL(Data Control Language)에서 트랜잭션을 제어하는 명령인

 COMMIT ROLLBACK만을 따로 분리해서 TCL이라고 표현하고 있습니다.

 

◈COMMIT 연산

  ▷ COMMIT 연산은 트랜잭션 처리가 정상적으로 종료되어 트랜잭션이 수행한 변경 내용을 데이터베이스에 반영하는 연산입니다.

  ▷ 내용을 변경한 트랜잭션이 완료되면 그 트랜잭션에 의해 데이터베이스는 새롭게 일관된 상태로 변경되며, 이 상태는 시스템 오류가 발생하더라도 취소되지 않습니다.

 

(COMMIT 연산 실행)

※ 지금까지 해온 작업들을 데이터베이스에 반영합니다.

 

 

◈ROLLBACK 연산

  ▷ ROLLBACK 연산은 하나의 트랜잭션 처리가 비정상적으로 종료되어 데이터베이스의 일관성이 깨졌을 때, 트랜잭션이 행한 모든 변경 작업을 취소하고 이전 상태로 되돌리는 연산입니다.

   ▷ ROLLBACK 연산 시 해당 트랜잭션은 받았던 자원과 잠금(LOCK)을 모두 반환하고, 재시작(Restart)되든지 폐기됩니다.

(ROLLBACK 연산 실행)

※ 마지막 COMMIT 이후의 작업으로 돌아갑니다.

   만약 COMMIT를 하지 않았다면 모든 작업이 사라집니다.

(COMMIT를 하지 않고, ROLLBACK 연산 실행)

 테이블의 데이터를 삭제한 후 다시 테이블의 데이터를 출력하면 데이터가 사라져 있습니다.

하지만, ROLLBACK를 하게 된다면 데이터 삭제 후 COMMIT를 삭제하지 않았기에, 데이터가 다시 복구가 됩니다.

 

(COMMIT를 한 뒤, ROLLBACK 연산 실행)

※ 테이블의 데이터를 삭제한 후 테이블의 데이터를 출력하게 되면 데이터가 사라져 있습니다.

그 후 COMMIT연산을 한 뒤, ROLLBACK연산을 하고, 다시 테이블 데이터를 출력하게 되면, 데이터는 사라진 채로 출력됩니다. 그 이유는 COMMIT연산에 의해 데이터가 DB에 반영되었기 때문입니다.

 

◈SAVEPOINT

   ▷ SAVEPOINT는 현재의 트랜잭션을 작게 분할하는 명령어 입니다.

   ▷ 저장된 SAVEPOINT는 ROLLBACK TO SAVEPOINT문을 사용하여 지정한 곳까지 ROLLBACK 할 수 있습니다.

   ▷ 여러개의 SQL문을 수반하는 트랜잭션의 경우, 사용자가 트랜잭션 중간 단계에서 SAVEPOINT를 지정할 수 있습니다.

 

   ▷ 이 SAVEPOINT는 차우 ROLLBACK과 함께 사용하며, 현재 트랜잭션 내의 특정 SAVEPOINT까지 ROLLBACK 할 수 있습니다.

 

(SAVEPOINT 만들기)

※ SAVEPOINT 포인터명;

 

 테이블에 첫번째 값을 넣은 후 SAVEPOINT를 만듭니다. 그후 두번째, 세번째 값을 넣고 테이블의 데이터를 출력하게 되면, 세개의 값이 출력됩니다. 그후 C1이라는 SAVEPOINT로 ROLLBACK연산을 수행하게 된다면, 마지막 COMMIT이 실행된 그 이후로 돌아가게 됩니다. 그 후 테이블의 데이터를 출력하게 되면 첫번째 값만 출력되게 됩니다.

 

※SAVEPOINT 유의사항

1. SAVEPOINT 후 COMMIT연산을 하게 된다면, COMMIT 연산 이전에 만든 SAVEPOINT들은 모두 사라지게 됩니다.

2. SAVEPOINT는 여러개 생성할 수 있습니다.

 

 

 

 

'IT-개발 > DB' 카테고리의 다른 글

(mariadb) like 문  (0) 2020.02.06
[mysql] MySQL 하지 말아야 할 것 17가지 (펌)  (0) 2020.01.15
[mariadb] utf-8 설정하기  (0) 2019.07.09
[mariadb] 유용한 명령어 모음 (펌 + 혼합)  (0) 2019.05.08
[mariaDB] UNION 연산자 (펌)  (0) 2019.05.08
posted by 갱우덩 2020. 1. 29. 15:20

Server RAID 설정을 내가 직접할 일이 있을까 했는데~ 생기네요.

 

정리 잘 해놓으신거 옮겨 놓습니다.

 

(펌 : https://m.blog.naver.com/PostView.nhn?blogId=vtechpia&logNo=220536967908&proxyReferer=https%3A%2F%2Fwww.google.com%2F)

 

https://m.blog.naver.com/PostView.nhn?blogId=vtechpia&logNo=220536967908&proxyReferer=https%3A%2F%2Fwww.google.com%2F

 

IBM MegaRAID 설정

MegaRAID Configuration BIOS 에서 RAID 설정하는것을 올립니다. 서버 X3650 M3 300GB ...

blog.naver.com

 

 

MegaRAID Configuration

BIOS 에서 RAID 설정하는것을 올립니다.
서버 X3650 M3   300GB SAS 1개 테스트 입니다. LSI Logic / Symbios Logic RAID bus controller

1. 부팅 후 RAID, Boot Device 연결 메시지 나온 후 <F1> Setup



2. BIOS – System Configuration and Boot Management

  System Settings > Adapter and UEFI Drivers > LSI SAS Driver PciRoot 엔터 > 도스 화면에서

  Press 1 for EFI WebBIOS

          2 for EFI CLI

 선택 항목에서  1번 입력후 엔터 > Start 엔터

(여기서는 디스크 하나만 사용했습니다.)

 

  

 

  

 

3. 왼쪽메뉴 Configuration Wizard
  처음 구성시 New Configuration

  추가 구성시 Add Configuration

 

 

 

  

4. Virtual Disk 깨지므로 데이터를 잃게 된다는 메세지 Yes 선택

 

 

  

5. Manual Configuration 선택 > Next

 

6. 왼쪽 Drives 에 있는 디스크를 선택 > Add To Array 선택, Next > Drive Group 추가됨

   Accept DG (오른쪽) > Next > Span Definition (Drive Group1 은 추가되어도 상관없음)

 

 

  

7. Add to SPAN > Next  

 

 

8. 여기서는 300GB 하드여서 RAID0 으로 두개 드라이브로 나누었습니다.
Select Size 100 GB 입력 후  Accept > Yes 선택 > VD0 생성

 

 

9. Back > 남은 용량 Add to Span > Next

 

10. 두 개의 VD 생성

 

 

11. Accept >  Save this Configuration ? Yes 선택 > All data on the … lost  Yes 선택

 

 

12. Fast Initialize 선택 > Go 클릭 > Home

 

13. WebBios 처음 화면에서 VD 확인 가능합니다. > Raid 구성 완료 > 재부팅 후  OS설치

 

'IT-개발 > 관리' 카테고리의 다른 글

Git...  (0) 2014.07.08
posted by 갱우덩 2020. 1. 15. 13:42

폭풍이 지나간것 처럼 문제를 해결하고 나서 “뭔가 문제가 터졌을때 빨리 확인해볼수 있는 체크리스트가 있어야 한다.”라는 생각을 자주 했다. 그래서 간단하게 확인해볼 거리들을 Linux 터미널에서 시도해볼수 있는 커맨드와 함께 정리해 보았다. 수많은 서버 관리 도구가 나왔지만 어떠한 환경에서도 확실하게 동작하는건 Linux 터미널 도구였다 = 결국 직접 문제가 생긴 환경에 들어가봐야 한다.

 

1) CPU 상태 확인

서버 퍼포먼스 문제가 있다면 top 명령어로 CPU 사용량을 확인한다. 당신이 만든 어플리케이션이 지나치게 많은 CPU를 (50% 이상) 먹고있지 않은지 확인한다.

$ top

 

2) 메모리 상태 확인

당연하겠지만 메모리가 부족하면 안된다. 아래 명령어로 얼마나 메모리가 남았나 확인한다. 최소한 500메가 이상은 남아줘야 커널패닉이 나지 않는다.

$ free -m

 

3) 프로세스(쓰레드 상태) 확인

프로세스가 너무 많이 생겼거나 아예 프로세스가 죽어버린 경우가 있다. 본인이 띄운 프로세스가 살아 있는지 확인한다.

$ ps -ef | grep <process name>

 

4) 서비스/데몬 확인

데몬(daemon)은 백그라운드에서 돌아가는 프로세스를 말한다. 윈도우즈의 서비스를 생각하면 된다. 본인의 서버 프로그램이 데몬으로 돌아간다면 프로세스가 잘 돌아가고 있는지 확인한다.

$ service <daemon name> status

 

5) 네트워크 상태 확인

당연하지만 현재 서버의 네트워크 설정 및 상태를 보고 싶다면 아래 명령어를 쓴다.

$ ifconfig

하지만 이게 다가 아니다. 네트워크 쪽은 특별히 몇가지 케이스로 다뤄본다.

내 서버에서 접속이 가능합니까?

TCP 80, 443등의 특수 포트가 열려 있는지 확인하고자 한다면

$ nc <hostname> <port>
$ nc localhost 80
$ nc localhost 443

UDP인 경우엔 u 옵션으로 빠르게 확인해볼수 있다.

$ nc -u <hostname> <port>

<hostname>을 외부 서버로 해두면 이 서버에서 해당 host로 접속이 가능한지 확인해볼수 있다. 예를들어, 방화벽으로 막혀 있는가 등을 확인해 볼수 있다. 좀더 자세한 output을 위해선 -v 옵션을, 연결테스트 하는데 3초만 기다리고 싶다면 -w3 옵션을 주자.

누가 내 포트를 먹었는가?

개발하다보면 다른 프로세스와 port가 겹치는 에러를 접하게 된다. 이때 어떤 프로세스가 내가 port를 독차지하고 있는지 확인해볼수 있다. 예를들어 TCP 80 포트를 잡아먹고 있는 프로세스를 찾는다면

$ lsof -i TCP:80

또는 현재 특정 프로세스가 어떤 포트를 차지하는지 알고 싶다면 (여기선 httpd 데몬)

$ lsof -c httpd

nc와 lsof 명령어는 쓸모가 매우 많으니 잘 알아두자

서버가 응답이 자꾸 늦어져요

종종 서버의 응답이 늦어질때 요청이 서버에 엄청 몰리는게 문제인지 알고 싶을때가 있다. 이때엔 네트워크 연결수로 확인해볼수 있다.

$ netstat -nap | grep ESTAB | wc

연결수가 지나치게 많다면 어디선가 자꾸 연결이 생성되고 있다고 보면된다. 요청이 외부에서 들어온다면 몰리는 것이고 아니라면 뭔가 서버에 이상이 있는 것이다.

$ netstat -nap | grep TIME_WAIT | wc

TIME_WAIT 수가 비정상적으로 많으면 들어오는 요청을 서버에서 제대로 처리를 못하고 있다고 보면된다. 한마디로 병목현상이 벌어지고 있는 것이다.

 

5) 디스크 상태 확인

서버는 한번 전원이 들어오면 오랜시간 방치하는 경우가 많기 때문에 디스크에 쓰레기가 쌓이는 경우가 많다. 디스크가 꽉 차버리면 시스템에서 파일을 못 만들게 되고 결국 서버가 죽는다. 디스크 볼륨별로 얼마나 차 있는지, 빈 공간이 얼마나 남았는지 알고 싶다면 다음명령어로 확인한다.

$ df -h

root와 swap 볼륨이 충분한 여유가 있어야 한다.

 

6) 로깅 상태 확인

 

이건 번외인데 서버 초보들이 자주 범하는 실수라 적어둔다. 리눅스의 서버 로그는 /var/log 폴더 아래 저장이 되는데 여기에 본인 어플리케이션의 로그를 log rotation 시켜놓지 않는 경우가 있다.

그래서 단일 로그파일이 수 기가 바이트를 우습게 넘기는걸 종종 목격하는데 파일이 크면 클수록 프로세스가 처리하기 힘들어지고 결국 성능 저하로 연결된다. 메모리가 점점 늘어나고, 디스크 읽기 쓰기 성능이 계속 저하되는 증상을 보이지만 모니터링을 하지 않기 때문에 모르는 경우도 많고 이게 문제인지도 몰라서 고생하는걸 여러번 봤다.

log rotate를 처음 들어보고, 본인 서버 어플리케이션 성능이 계속 저하된다면 본인 서버의 로그가 어디 찍히는지 확인해보자.

서버는 클라이언트와 다르게 한번 띄워두면 상당히 오랜시간 손대지 않는 편이다. 그러다보니 오래된 건물처럼 시간이 지날수록 삐거덕 거리고 금이 가게 되며 그런상태로 더 오래 방치하면 어느날 갑자기 무너져 내려 서비스에 큰 장애를 일으킨다. 결국 서버의 전원을 넣는 순간부터 우리는 숙명처럼 계속 서버를 관리해줘야만 한다.

그래서… 건물에 관리인이 있듯 서버도 모니터링 & 알람 솔루션 (APM)이 있다. 이러한 솔루션은 우리가 미처 신경쓰지 못한 장애의 징조들을 미연에 알아차릴수 있도록 해주며 신뢰성 높은 서비스를 제공할수 있도록 만들어 준다. 다만 기존의 솔루션들은 서버의 물리적 상태(CPU, 메모리, 디스크 등등)에만 집중해 왔기 때문에 서비스의 유저가 겪는 논리적, 기능적 관점에서의 장애엔 취약하다. 위에 필자가 언급한 테스트 방법들은 물리적인 상태를 확인하는 것에 국한되어 있기 때문에 이 테스트들이 모두 정상적이어도 서버의 기능(로직)이 이상하게 동작하는건 잡아내기 어렵다. 서버 자원은 펑펑 남아도는데 고객에게 중요한 로그인이나 결제가 안되는 일이 생긴다는 것이다. 특히 한 서비스가 다른 서비스들에 API로 서로 연결되어 있는 경우엔…

 

우리 회사가 만들고 있는 HBSMITH는 ....

 

펌 : https://medium.com/hbsmith/linux-%EC%84%9C%EB%B2%84-%EC%9E%A5%EC%95%A0%EC%9B%90%EC%9D%B8-%ED%8C%8C%EC%95%85%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-7accec423bb5

 

Linux 서버 장애원인 파악은 어떻게?

서버 문제인것 같지만 어떻게 해야할지 모르는 분들을 위해

medium.com

 

posted by 갱우덩 2020. 1. 15. 12:48

권장사항이다. 이것을 이해하면 당신의 어플리케이션이 더 나은 성능을 발휘할 것이다.

다만 이것이 사람의 실력을 판단하는 척도로 사용되서는 안 될 것이다.

 

 

작게 생각하기

- 조만간 규모가 커질거라면 MySQL ecosystem을 봐야된다.
- 그리고 캐싱 빡시게 안 하는 메이저 웹사이트는 없다.
- develooper.com의 Hansen PT랑 Ilia 튜토리얼 볼 것
- 처음부터 확장 가능하게 아키텍처 잘 쪼개놔야된다.
- 복제랑 파티셔닝 어떻게 할지 미리 계획 세워놔라.
- 파일 기반 세션 좀 쓰지마 -_-
- 그렇다고 너무 쓸데없이 크게 생각하지도 말 것
- 특히 성능하고 확장성 구분 못 하면 난감함

 

 

EXPLAIN 안 써보기

- SELECT 앞에 EXPLAIN 이라고 붙이기만 하면 되는 것을 (..)
- 실행 계획 확인
- 타입 컬럼에 index 써있는거랑 Extra 컬럼에 index 써있는거랑 “매우 큰” 차이 있음
* 타입에 있으면 Full 인덱스 스캔 (안 좋다.)
* Extra 컬럼에 있으면 Covering 인덱스 찾았다는 의미임 (좋다!)
- 5.0 이후부터는 index_merge 최적화도 한다.

 

 

잘못된 데이터 타입 선택

- 한 메모리 블럭 단위에 인덱스 레코드가 많이 들어갈수록 쿼리가 빨리 실행될 것이다. (중요)
- 아.. 정규화 좀 해 -_-… (이거 정말 충격과 공포인 듯)
- 가장 작은 데이터 타입을 써.. (진짜 BIGINT가 필요하냐고..)
- 인덱스 걸리는 필드는 정말 최소한으로 데이터 크기를 써야된다고.
- IP는 INT UNSIGNED로 저장해!! (아주 공감)
* 이럴 때 쓰라고 INET_ATON 함수가 아예 내장되어 있음.

 

 

PHP에서 pconnect 쓰는 짓

- 아파치에서 좀비 프로세스라도 생기면 그 커넥션은 그냥 증발하는거야..
- 어차피 MySQL 접속 속도는 Oracle이나 PostgreSQL 보다 10~100배 빠르다고.

너무 과도한 DB 추상화 계층을 두는 것
- 어디 포팅 열심히 할 거 아니면 추상화 계층 쓰지마 (ADODB, MDB2, PearDB 등)
- scale out 가능한걸 쓰라고.

 

 

스토리지 엔진 이해 못 하는 것

- 단일 엔진만으로 전체 아키텍처를 결정했다면 대부분 최적이 아님
- 엔진 별 장단점을 공부할 것
- ARCHIVE : zlib으로 압축해주고 UPDATE 안 되고 로그 Bulk Insert에 유용함.
- MEMORY : 서버 재시작하면 증발. 인덱스가 HASH나 BTREE로 가능함. 임시, 요약 데이터에 사용.
* 주간 top X 테이블 같은 것.
* 하여튼 메모리에 박아넣고 싶은 데이터 있으면..

 

 

인덱스 레이아웃 이해 못 하는 것

- 제대로 인덱스랑 스토리지 엔진 선택하려면 공부 좀 해
- 엔진은 데이터와 인덱스 레코드를 메모리나 디스크에 레이아웃하는 걸 구현한 것
- clustered 구성은 데이터를 PK 순서에 따라 저장함.
- non-clustered 구성은 인덱스만 순서대로 저장하고 데이터는 순서 가정하지 않음.
- clustered에서는 인덱스만 타면 추가적인 조회 없이 바로 데이터 가져오는 것임.
- 그래서 clustered PK는 작은 놈으로 할 필요가 있다는거
* 다른 인덱스는 각 레코드마다 PK를 앞에 더 붙이게 되니까.
* PK 지정 안 하면 아무렇게나 해버림

 

 

쿼리 캐시 이해 못 하는 것

- 어플리케이션 read/write 비율은 알고 있어야지
- 쿼리 캐시 설계는 CPU 사용과 읽기 성능 간의 타협
- 쿼리 캐시 크기를 늘린다고 읽기 성능이 좋아지는게 아님. heavy read라도 마찬가지.
- 과도한 CPU 사용을 막기 위해 무효화 할 때는 캐시 항목들을 뭉텅이로 날려버림
- 한마디로 SELECT가 참조하는 테이블 데이터 하나라도 변경되면 그 테이블 캐시는 다 날라간다는 얘기임
- 수직 테이블 파티셔닝으로 처방
* Product와 ProductCount를 쪼갠다든지..
* 자주 변하는 것과 변하지 않는 것을 쪼개는게 중요하다 이 말임.

 

 

Stored Procedure를 쓰는 것

- 무조건 쓰면 안 된다는게 아니고..
- 컴파일 할 때 무슨 일이 일어나는지 이해 못 하고 쓰면 재앙이 된다 이 말.
- 다른 RDBMS랑 다르게 connection thread에서 실행 계획이 세워짐.
- 이게 뭔 얘기냐 하면 데이터 한 번 가져오고 연결 끊으면 그냥 CPU 낭비 (7~8% 정도)하는 꼴이라는 것.
- 웬만하면 Prepared 구문과 Dynamic SQL을 써라.. 아래 경우를 제외하고
* ETL 타입 프로시저
* 아주아주 복잡하지만 자주 실행되지는 않는 것
* 한 번 요청할 때마다 여러번 실행되는 간단한 것 (연결한 상태로 여러번 써야 된다니까)

 

 

인덱스 컬럼에 함수 쓰는 것

- 함수에 인덱스 컬럼 넣어 호출하면 당연히 인덱스 못 탄다
- 함수를 먼저 계산해서 상수로 만든 다음에 = 로 연결해야 인덱스 탈 수 있다.
* 여기 실행 계획 보면 LIKE도 range type 인덱스 타는 것 보임

 

 

인덱스 빼먹거나 쓸모없는 인덱스 만들어 놓는 것

- 인덱스 분포도(selectivity)가 허접하면 안 쓴다.
- S = d/n
* d = 서로 다른 값의 수 (# of distinct values)
* n = 테이블의 전체 레코드 수
- 쓸모없는 인덱스는 INSERT/UPDATE/DELETE를 느리게 할 뿐..
- FK는 무조건 인덱스 걸어라. (물론 FK 제약 걸면 인덱스 자동으로 생긴다.)
- WHERE나 GROUP BY 표현식에서 쓰이는 컬럼은 인덱스 추가를 고려할 것
- covering index 사용을 고려할 것
- 인덱스 컬럼 순서에 유의할 것!

 

 

join 안 쓰는 짓

- 서브쿼리는 join으로 재작성해라
- 커서 제거해라
- 좋은 Mysql 성능을 내려면 기본
- 집합 기반으로 생각해야지 루프 돌리는거 생각하면 안 된다.

 

 

Deep Scan 고려하지 않는 것

- 검색엔진 크러울러가 쓸고 지나갈 수 있다.
- 이 경우 계속해서 전체 집합을 정렬한 다음 LIMIT로 가져와야 하니 무진장 느려진다.
- 어떻게든 집합을 작게 줄인 다음 거기서 LIMIT 걸어 가져올 것

 

 

InnoDB 테이블에서 WHERE 조건절 없이 SELECT COUNT(*) 하는 짓

- InnoDB 테이블에서는 조건절 없이 COUNT(*) 하는게 느리다.
- 각 레코드의 transaction isolation을 유지하는 MVCC 구현이 복잡해서 그렇다는..
- 트리거 걸어서 메모리 스토리지 엔진 쓰는 테이블에 통계를 별도로 유지하면 된다.

 

 

프로파일링이나 벤치마킹 안 하는 것

- 프로파일링 : 병목 찾아내기
- 벤치마킹 : 시간에 따른 성능 변화 추이 평가, 부하 견딜 수 있는지 테스트
- 프로파일링 할 때는 실제 데이터를 옮겨와서 할 것
- 어디가 병목이냐~ Memory? Disk I/O? CPU? Network I/O? OS?
- 느린 쿼리 로그로 남기기
* log_slow_queries=/path/to/log
* log_queries_not_using_indexes
- 벤치마킹 시에는 다 고정시키고 변수 하나만 바꿔가면서 해야 함. (쿼리 캐시는 끌 것.)
- 도구를 써라~~
* EXPLAIN
* SHOW PROFILE
* MyTop/innotop
* mysqlslap
* MyBench
* ApacheBench (ab)
* super-smack
* SysBench
* JMeter/Ant
* Slow Query Log

 

 

AUTO_INCREMENT 안 쓰는 것

- PK를 AUTO_INCREMENT로 쓰는건 무진장 최적화 되어 있음
* 고속 병행 INSERT 가능
* 잠금 안 걸리고 읽으면서 계속 할 수 있다는!
- 새 레코드를 근처에 놓음으로써 디스크와 페이지 단편화를 줄임
- 메모리와 디스크에 핫 스팟을 생성하고 스와핑을 줄임

 

 

ON DUPLICATE KEY UPDATE를 안 쓰는 것

- 레코드가 있으면 업데이트하고 없으면 인서트하고 이런 코드 필요없다!! 다 날려버려라!!
- 서버에 불필요하게 왔다갔다 할 필요가 없어짐
- 5-6% 정도 빠름
- 데이터 입력이 많다면 더 커질 수 있음
하지 말아야 할 것 총정리
Thinking too small
Not using EXPLAIN
Choosing the wrong data types
Using persistent connections in PHP
Using a heavy DB abstraction layer
Not understanding storage engines
Not understanding index layouts
Not understanding how the query cache works
Using stored procedures improperly
Operating on an indexed column with a function
Having missing or useless indexes
Not being a join-fu master
Not accounting for deep scans
Doing SELECT COUNT(*) without WHERE on an InnoDB table
Not profiling or benchmarking
Not using AUTO_INCREMENT
Not using ON DUPLICATE KEY UPDATEK

 

 

아래에서 가져왔는데 솔직히 이해안되는게 50% 이상이네요. DB 배울게 많네요.ㅠㅠ

 

https://blog.lael.be/post/370

 

MySQL 쓰면서 하지 말아야 할 것 17가지

*MySQL 쓰면서 하지 말아야 할 것 17가지* 권장사항이다. 이것을 이해하면 당신의 어플리케이션이 더 나은 성능을 발휘할 것이다. 다만 이것이 사람의 실력을 판단하는 척도로 사용되서는 안 될 것이다.   작게 생각하기 – 조만간 규모가 커질거라면 MySQL ecosystem을 봐야된다. – 그리고 캐싱 빡시게 안 하는…

blog.lael.be

 

'IT-개발 > DB' 카테고리의 다른 글

(mariadb) like 문  (0) 2020.02.06
(SQL) TCL(Transaction Control Language) 이란 ?  (0) 2020.01.31
[mariadb] utf-8 설정하기  (0) 2019.07.09
[mariadb] 유용한 명령어 모음 (펌 + 혼합)  (0) 2019.05.08
[mariaDB] UNION 연산자 (펌)  (0) 2019.05.08
posted by 갱우덩 2019. 10. 23. 13:23

결론 ) CreateProcess 보다는 되도록 shellExecuteEx 를 사용하자. 

 

내용이 많이 깊니다. 그냥 여기저기꺼 퍼온거만 모아놓아서요. 스크롤압박 있으니 주의하세요.

 

http://egloos.zum.com/yajino/v/782497

 

ShellExecute vs CreateProcess 차이점

Windows 95의 출현과 함께 문서의 개념이 중요성을 띠게 되었다. 이제는 실행파일이라는 개념이 좀더 복잡해 지고 단순히 구동한다는 의미를 떠나 아주 방대한 개념으로 자리 잡고 있다.문서라고 하는 것은 시스템의 네임스페이스의 일부인 보다 일반적인 객체를 말하고자 하며, 이문서에 대하여 '열기(open)', '인쇄(print)', '탐색(explore)'

egloos.zum.com

Windows 95의 출현과 함께 문서의 개념이 중요성을 띠게 되었다. 이제는 실행파일이라는 개념이 좀더 복잡해 지고 단순히 구동한다는 의미를 떠나 아주 방대한 개념으로 자리 잡고 있다.

문서라고 하는 것은 시스템의 네임스페이스의 일부인 보다 일반적인 객체를 말하고자 하며, 이문서에 대하여 '열기(open)', '인쇄(print)', '탐색(explore)', '찾기(find)'를 하는 프로그램이 있다. 다시 말해서, 문서라는 것은 그것에 대해서 프로그램이 어떤 동사(Verb)를 실행할 수 있는 모든 아이템을 말한다.

지금의 프로그램 실행자의 모체였던 WinExec()에서 ShellExecuteEx()라는 함수로 그 진행이 옮겨가는 이유도 이해 따른다.



이 장에서는 다음과 같은 것들을 다룰 것이다.

1. WinExec()와 CreateProcess() 사이의 차이점

2. ShellEcecute()와 ShellExecuteEx()가 다른 함수보다 우수한점

3. 동사들(Verb), 문서들 그리고 정책들(Policy)

4. 훅킹을 사용하여 프로세스 실행을 내 마음대로



그리고 다음과 같은 몇가지 예제 코드를 살펴볼 것이다.

1. 디폴트 브라우져 감지법

2. 프로그램을 실행시키고 종료를 기다리는법

3. 어떤 파일에 대한 등록정보 대화상자를 나타내는법

4. 찾기 대화상자를 화면에 출력하는 법

5. 사용자가 특정 폴더를 액세스하지 못하게 하거나, 다른 특정 어플리케이션을 실행하지 못하게 막는법



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



1. WinExec()에서 CreateProcess()로...



UINT WinExec(LPCSTR lpCmdLine, UINT uCmdShow);

이 함수는 Windows 3.x에서는 외부 프로그램을 실행시키는 유일한 방법이었다. 그리고 가장 간단한 사용법을 가지기도 한다.

하지만 단점이라 불리울 수 있는 것이 일단 실행을 시켜 놓으면 실행이 되는지 에러가 났는지, 종료 되었는지 전혀 알수가 없다는 것이다.



다음은 CreateProcess()의 프로토 타입이다.

BOOL CreateProcess(

LPCTSTR lpApplicationName, // 실행파일 모듈의 이름에 대한 포인터

LPTSTR lpCommandLine, // 커맨드 문자열에 대한 포인터

LPSECURITY_ATTRIBUTES lpPA, // 프로세스 보안 속성 포인터

LPSECURITY_ATTRIBUTES lpTA, // 스레드 보안속성 포인터

BOOL bInheritHandles, // 핸들 상속 여부 플래그

DWORD dwCreationFlags, // 생성 플래그

LPVOID lpEnvironment, // 환경 블록에 대한 포인터

LPCTSTR lpCurrentDirectory, // 현재 디렉토리

LPSTARTUPINFO lpStartupInfo, // STARTUPINFO 포인터

LPPROCESS_INFORMATION lpPI // PROCESS_INFORMATION 포인터

);



보시다 시피 여기에는 많은 파라미터들이 있다 하지만.. 대부분의 내용이 MSDN에 잘 문서화가 되어 있으므로 그렇게 어렵다거나 하지는 않다.

일단 가장 간단한 호출을 한번 살펴보자.



STARTUPINFO si;

PROCESS_INFORMATION pi;

ZeroMemory(&si, sizeof(STARTUPINFO));



CreateProcess(NULL, szPrgName, NULL, NULL, TRUE, NORMAL_PRIORITY_CLSS, NULL, NULL, &si, &pi);



WinExec보다는 복잡하지만 여러가지 부가적인 정보를 줄 수도 있고 받을 수도 있으므로 상대적인 저비용이다.



만약 위의 프로그램을 실행시키고 종료하기를 기다린다고 하면..



BOOL b = CreateProcess(............);

if(!b)

return FALSE;



WaitForSingleObject(pi.hProcess, INFINITE);

return TRUE;



정말 간단하지 않는가? ^^;

위의 결과를 외견상으로 보면 WinExec의 문제점들의 CreateProcess() 함수로써 모두 해결했다는 생각을 할 수도 있겠지만 현재의 문서라는 개념은 좀더 일반화 되었다. 위에서 사용된 실행가능한 프로그램(exe, com ...etc)등은 문서의 한가지 유형일 뿐이다.





2. ShellExecute()와 ShellExecuteEx()가 다른 함수보다 우수한점



HINSTANCE ShellExecute(

HWND hwnd, // 부모 윈도우 핸들

LPCTSTR lpVerb, // 동사 혹은 작업

LPCTSTR lpFile, // 실행 대상 문서

LPCTSTR lpParameters, // 컴맨드 인자

LPCTSTR lpDirectory, // 현재 디렉토리

INT nShowCmd // 보여주기 옵션

);



일단 위의 함수를 살펴보면 CreateProcess() 함수보다 기능이 많이 떨어지는 것 처럼 보이지만 이 함수의 진정한 의미는 동사 연결이라는 점이다.

보통 우리가 말하는 [연결 프로그램]을 지칭한다.



위에서 [동사 혹은 작업] 부분의 LPCTSTR lpVerb 에 사용될 수 잇는 것은데 대한 나열이다.

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

열기(open) - 프로그램/문서/폴더

탐색(explore) - 폴더

인쇄(print) - 문서

~~로 인쇄(printto) - 문서

찾기(find) - 폴더



------> 먼저 열기(open)에 대하여 살펴보자.



ShellExcute(hwnd, "open", "c:\\prog.exe", "/nologo", "d:\\", SW_SHOW);



ShellExcute(hwnd, "open", "c:\\file.txt", NULL, NULL, SW_SHOW);



만약 txt확장자를 가진 파일에 대한 연결이 존재하지 않으면 우리가 많이 보던 연결프로그램 대화상자가 나타날 것이다.



------> 탐색 작업



탐색 작업은 폴더에 대해서만 가능하고 open 인경우와 약간의 차이를 나타낸다.

open로 동사를 주면 pane이 하나로 된 창이 뜨고, explore로 주면 pane가 2개인 탐색기다 뜬다.



ShellExcute(hwnd, "expolre", "c:\\", NULL, NULL, SW_SHOW);



------> 인쇄 작업



인쇄작업은 문서를 인쇄하기 위한 것이지만, 지정된 문서를 인쇄할 수 있는 명령어 라인과 프로그램을 정확히 알나내기 위해 레지스트리에 저장된 정보에 의존한다.



ShellExcute(hwnd, "print", "c:\\file.txt", NULL, NULL, SW_SHOW);



이 함수는 텍스트 파일을 처리하기 위해 등록된 프로그램을 찾고, 그 프로그램에 인쇄를 위한 명령이 있는지 검사한다. 정상적이라면 이 프로그램은 notepad.exe가 될 것이고, 그 명령어 라인은 다음과 같다.



notepad.exe /p



디폴트 프린터로의 인쇄는 위에서 처럼 간단하게 사용이 가능하지만 만약 여러개의 프린터가 있거나 출력 포트를 설정하고 싶다면 printto를 사용해야 한다.



만약 printto가 문서에서 지원이 된다면 등록된 명령이 실행될것이고 그렇지 못하다면..

경고창이 뜨면서 디폴트 프린터로 출력할 것이지를 묻는다.



------> 찾기 작업



지정된 경로를 루트로 찾기 창이 생성된다.

예제는 생략한다. ^^;





그렇다면 파일을 열기 위해서 혹은 그 파일과 연결된 실행 프로그램은 어떻게 구할 수 있는가?

의외로 쉽게 구할 수 있다. SDK에서 API를 제공하니까 ^^;



HINSTANCE FindExecuteable(

LPCTSTR lpFile, // 알아볼 파일

LPCTSTR lpDir, // 경로

LPTSTR lpResult) // 찾은 결과값



하지만 위 함수에는 치명적인 결함이 있다.

1. 보통 연결 프로그램은 확장자를 기준으로 검색되지만 이 함수는 항상 파일이 존재하여야만 결과를 리턴한다.

2. 경로에 공백이 있어도 않된다.

3. 리턴하는 결과에도 공백이 있으면 잘린다.



한마디로 엉터리에 가까운 API라 말할 수 있다.

이렇듯 이 함수는 규칙없이 긴 이름을 가진 파일을 염두에 두지 않고 설계되었기 때문에 생기는 문제인데..

MS에서 알고 있지만 절대 고치지 않는다..

Windows 이후로 긴 이름의 파일을 지원하지만..

MS Windows에서 시스템 명령어 중에 8.3포멧을 지키지 않는 명령어는 아직까지도 존재하지 않는다.



이 문제를 바로잡기 위한 FindExecutableEx를 만들어보자.

HINSTANCE FindExecutableEx(... 인자는 동일하다 ...)

{

TCHAR drive[_MAX_DRIVE];

TCHAR dir[_MAX_DIR];

TCHAR dir1[_MAX_DIR];

TCHAR file[_MAX_FILE];

TCHAR ext[_MAX_EXT];



HINSTANCE hi = FindExecutable(file, dir, result);

result[lstrlen(result)] = 32;



_splitpath(result, deive, dir, file, ext);



LPTSTR p = strchr(dir, ':');

if(p != NULL)

{

--p;

dir[p-dir] = 0;

_splitpath(dir, NULL, dir1, file, ext);

p = strchr(ext, 32);

ext[p-ext] = 0;

_makepath(result, drive, dir1, file, ext);

}

return hi;

}

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

 

2번 문서

 

이전의 내용이 넘 길어서 짤라서 다시 이어쓴다..

조금 졸립기도 하고 해서 담에 이어서 써야겠다 ㅡㅟ



1. 디폴트 브라우저 감지하기

void GetDefaultBrowser(LPTSTR szBrowerName)

{

HFILE h = lcreate("dummy.htm", 0);

_lclose(h);



FindExecutable("dummy.htm", NULL, szBrowserName);

DeleteFile("dummy.htm");

}





2. URL로 연결

ShellExecute(NULL, NULL, "http://lop.com", NULL, NULL, SW_SHOW);



3. e-mail 보내기

ShellExecute(NULL, NULL, "mailto:crowback@a.co.kr", NULL, NULL, SW_SHOW);



4. 문서 인쇄

ShellExecute(NULL, "print", 문서명, NULL, NULL, SW_SHOW);



5. 파일과 폴더 찾기

ShellExecute(NULL, "find", 문서명, NULL, NULL, SW_SHOW);



자자...위에는 간단한 예문을 몇가지 들어본 것이다.

그럼 본격적으로 다음 단계를 진행해보자.



ShellExcute() vs CreateProcess()



여기서의 요점은 어느것이 더 좋은가가 아니라 어느것이 프로세스를 생성할 경우 더 유용한가를 논할 것이다.

고려해야할 첫번째 사항으로 ShellExecute()는 내부적으로 CreateProcess를 호출한다는 것과, 따라서 ShellExecute는 CreateProcess를 위한 더 작고 사용하기 간단한 wrapper이다. 다시 말해서, 문서들을 열고 인쇄하기에는 ShellExecute가 훨씬 더 융통성이 있으며, 문서에 대해 할 수 있는 다른 작업도 마찬가지이다.



ShellExecute()를 사용해서 프로그램을 실행하면 좋은이유



ShellExecute를 사용하는 쪽에 무게를 두는 이유는 MS의 지침이 MS의 새로운 LOGO에 대한 요구사항으로 방대한 안을 내놓았는데, Win98, WinNT 혹은 그이상의 운영체제에 사용되는 MS 어플리케이션 로고를 붙이려면 몇가지 ShellExecute를 사용해서 외부 어플리케이션을 실행시키도록 권장하고 있다. 그렇게 하면 시스템 관리자가 채택한 제한 정책이 모두 확실하고도 주의 깊게 검사될것이기 때문이다. 시스템 관리자는 어플리케이션이 Windows에서 시작될 수 있는지, 또는 없는지를 결정하게 한다. ShellExecute는 이 블랙리스트를 고려한 것이지만 CreateProcess는 그렇지가 않다.



ShellExecuteEx()로의 확장

정책적인 지원에도 불구하고 ShellExecute()는 여러가지 제한점을 많이 가지고 있다. 그 치명적인 단점의 하나가 새로 생성된 프로세스를 반환하지 않는다는 것이다. 다시 말해 프로그램이 실행되어서 종료됫는지, 아직도 실행중인지, 내 프로그램이 방금 실행한 프로그램을 기다릴 수 있는 방법이 없다는 것이다. 하지만 쉘버전 4.0에서 새로운 함수가 소개되었다. 바로 ShellExecuteEx() 이다.

이 함수는 프로토타입처럼 간결하고 많은 플래그를 지원하고 무엇보다도 PIDL을 지원할 수 있도록 ShellExecute()를 확장하여 놓았다는 점이다.



BOOL ShellExecuteEx(LPSHELLEXECUTEINFO lpExexInfo);



LPSHELLEXECUTEINFO 구조는 다음과 같다.

typedef struct _SHELLEXECUTEINFOA

{

DWORD cbSize;

ULONG fMask;

HWND hwnd;

LPCSTR lpVerb;

LPCSTR lpFile;

LPCSTR lpParameters;

LPCSTR lpDirectory;

int nShow;

HINSTANCE hInstApp;

// Optional fields

LPVOID lpIDList;

LPCSTR lpClass;

HKEY hkeyClass;

DWORD dwHotKey;

union {

HANDLE hIcon;

HANDLE hMonitor;

};

HANDLE hProcess;

} SHELLEXECUTEINFO, FAR *LPSHELLEXECUTEINFO;



이 구조를 사용하기 전에 이 구조체를 0으로 체구고 cbSize값에다가 실제 길이를 넣어두면 된다.



SHELLEXECUTEINFO si;

ZeroMemory(&si, sizeof(SHELLEXECUTEINFO));

si.cbSize(sizeof(SHELLEXECUTEINFO));



다음에서 ShellExecuteEx()를 실행하는 가장 간단한 방법을 살펴보자.



SHELLEXECUTEINFO si;

ZeroMemory(&si, sizeof(SHELLEXECUTEINFO));

si.cbSize(sizeof(SHELLEXECUTEINFO));



si.lpFile = __TEXT("explorer.exe");

si.nShow = SW_SHOW;

si.lpVerb = __TEXT("open");

ShellExecuteEx(&si);



다음에서는 부가적인 기능으로 PIDL을 이용하여 호출하는 것을 살펴보자.



LPITEMIDLIST pidl;

SHGetSpecialFolderLocation(NULL, CSIDL_PRINTER, &pidl);



SHELLEXECUTEINFO si;

ZeroMemory(&si, sizeof(SHELLEXECUTEINFO));

si.cbSize(sizeof(SHELLEXECUTEINFO));



si.nShow = SW_SHOW;

si.lpIDList = pidl;

si.fMask = SEE_MASK_INVOKEIDLIST;

si.lpVerb = __TEXT("open");

ShellExecuteEx(&si);



만약 fMask에 SEE_MASK_NOCLOSEPROCESS를 포함하였다면

hProcesss 멤버를 통하여 새로생긴 프로세스의 핸들을 반환 받을 수 있다.



WaitForSingoeObject(si.hProcess, INFINITE);



자 여기까지 기본적인 ShellExccute()와 그 확장 ShellExecuteEx()의 대략적인 용법과 사용예에 대하여 살펴보았다.



여기서 좀더 색다를 점을 강조해 보면 ShellExecuteEx()는 정적인 동사 뿐만 아니라 동적인 동사도 불러낼 수 있다는 점이다.

이것이 움직이는 방법은 다음과 같다. 만일 ShellEcecuteEx()가 정적 동사 목록에서 찾는 동사를 찾을 수 없으면 주어지 ㄴ파일에 대한 컨텍스트 메뉴를 뒤진다. 이런 탐색 과정은 IContextMenu 인터페이스에 대한 포인터를 낳는다. 그리고나서, 이 인터페이스에 노축될 함수를 통해 동적인 동사를 불러낸다.



그 결과로, 파일의 [등록정보] 대화살자를 쉽게 화면에 출력할 수 있게 된다. 이 대화상자는 파일 위에서 오른쪽 마우스로 등록정보를 클릭한것과 같은 대화상자를 출력한다.



void ShowFileProperties(LPCTSTR szPathName)

{

SHELLECECUTEINFO sei;

ZeroMemory(&sei, sixeof(SHELLECECUTEINFO));

sei.cbSize = sixeof(SHELLECECUTEINFO);



sei.lpFile = szPathName;

sei.nShow = SW_SHOW;

sei.fMask = SEE_MASK_INVOKEIDLIST;

sei.lpVerb = __T("properties");

ShellExecuteEx(&sei);

}

출처: 데브피아

 

 

http://egloos.zum.com/byung/v/5420292

 

CreateProcess 와 ShellExecute(Ex)

애플리케이션에서 Child Process를 실행하는 경우는 흔히 발생하는 데, 이때 많이 사용되는 것이 CreateProcess, 또는 ShellExecuteEx 가 아닐까 한다.

egloos.zum.com

애플리케이션에서 Child Process 실행하는 경우는 흔히 발생하는 , 이때 많이 사용되는 것이 CreateProcess, 또는 ShellExecuteEx  아닐까 한다. 일반적으로는  API 그냥 그때 그때  내키는 데로 사용해왔는 ,   API 약간의 차이가 있다. 특히, Vista 이후의 OS에서는 Child process Elevation이란 점에서 차이가 나타난다.

 일반적으로 ShellExecuteEx 내부적으로 CreateProcess 호출하는 것으로 알고 있다. 하지만, 다른 부분은 ShellExecuteEx 이용하여 애플리케이션을 시작할 때는 운영체제에서 자동적으로 많은 System Policy 설정을 확인하고 강제한다는 것이다. 그러므로, 이러한 System Policy 제약이 애플리케이션의 구동에 장애가 된다면, CreateProcess 정상적으로 애플리케이션을 수행하지 못할 것이다. 필요하다면, 이러한 Policy정보를 읽어와서 처리해야하는 루틴이 따로 필요할지도 모른다.


 
언급한 내용과 같은 선상에서 Vista 이후에 UAC 특징 가운데 이들 API 차이를 느낄  있다. 일반적으로 애플리케이션의 매니페스트 파일에 requestedExecutionLevel requireAdministrator 설정되어 있다고 가정해보자. 이와 같은 애플리케이션을 interactive child process 실행하기 위해서 CreateProcess 이용했다면 어떻게  ? Vista 이후의 OS에서 CreateProcess API 다소 달라졌는데, 애플리케이션의 매니페스트 파일을 확인하거나, installer인지를 확인하거나 또는 appcompat shim 관여가 있는 지를 확인하고 나서 process 실행하도록 한다. 문제는 해당 애플리케이션이 elevation 요구가 있을  ERROR_ELEVATION_REQUIRED(740) 오류가 발생하고 그것으로 끝이다. 그렇지 않고, elevation  특별한 요구가 없다면, XP에서와 같이 Child process 실행시킬 것이다.


 
하지만, ShellExecute(Ex)  CreateProcess대신하여 사용한다면, elevation되어 수행된다. 무슨 차이가 있을 ? 실제로, ShellExecute 애플리케이션에서 호출하면 내부적으로 CreateProcess 호출될 것이고, 동일한 상황에서 동일하게 ERROR_ELEVATION_REQUIRED 받을 것이다. 하지만, ShellExecute(Ex) 권한 상승된 process 수행을 위해 AIS(Application Information Service) contact 한다는 점이 차이가 있다.  이후에 AIS 적절한 Group Policy등의 정보를 기반으로 처리하게 되는 (elevation prompt   Consent 또는 valid Credential 주어지는 과정을 포함하여), 이러한 동작이 직접 CreateProcess API 호출하는 것과의 차이가   있다.


 
그러므로, 프로그램적으로 elevation process로써 Child process 수행하길 원한다면, Child process elevation 필요하다는 것을 명시화하고, (예를 들어, 매니페스트의 설정 등등) ShellExecute(Ex) 이용하여 호출하게 하는 것이 정석이다.

 

https://greenfishblog.tistory.com/118

 

프로세스 생성 종결자 (ShellExecute / CreateProcess)

Vista 이상에서 되도록 ShellExecute를 권장하고 있습니다. 그래서 ShellExecute를 그냥 사용하게 되는데, 간혹 Shell 쪽의 COM이 깨져 ShellExecute가 실패하는 경우가 있습니다. 그때는 CreateProcess를 해줘야..

greenfishblog.tistory.com

 

Vista 이상에서 되도록 ShellExecute를 권장하고 있습니다.
그래서 ShellExecute를 그냥 사용하게 되는데,
간혹 Shell 쪽의 COM이 깨져 ShellExecute가 실패하는 경우가 있습니다.
그때는 CreateProcess를 해줘야 합니다.

ShellExecute / CreateProcess 모두 많은 Argument가 있어 사용하기 복잡합니다.
그래서,

  • 실행 경로 (lpszExePath)
  • 종료때 까지 기다릴지 여부 (bBlock)
  • Exit Code (pnExitCode, Optional)
  • 실행 파라미터 (lpszParam, Optional)
  • 실행 프로세스의 Current Directory (lpszDirectory, Optional)

와 같이 실제 자주 사용되는 Argument로 종합한 함수를 공유합니다.
물론, Optional은 NULL이나 0을 전달해도 무방합니다.

내부에서 ShellExecute가 실패하면 CreateProcess로 연결되니, 걱정않으셔도 됩니다. :)


// TRACELOG는 TRACE로그를 찍는 것일 수 있음
// 각자 정의한 것으로 Replace 요망
// 삭제 가능

// Block이 TRUE이고 ERROR_SUCCESS 리턴일때 pnExitCode 의미 있음
DWORD ExecuteProcess(IN LPCTSTR lpszExePath, IN BOOL bBlock, OPTIONAL OUT PINT pnExitcode, OPTIONAL IN LPCTSTR lpszParam, OPTIONAL IN LPCTSTR lpszDirectory)
{
DWORD dwRtnValue = ERROR_SUCCESS;
DWORD dwResult = 0;
SHELLEXECUTEINFO stInfo = {0,};
HANDLE hCreateProcess = NULL;
STARTUPINFO si = {0,};
PROCESS_INFORMATION pi = {0,};
LPTSTR lpszCmd = {0,};
CString strCmdLine;

if (NULL != pnExitcode)
{
(*pnExitcode) = -1;
}

if (NULL == lpszExePath)
{
dwRtnValue = ERROR_INVALID_PARAMETER;
goto FINAL;
}

TRACELOG(TEXT("[INFO] ExecuteCalled"));
TRACELOG(TEXT("[INFO] lpszExePath : %s"), lpszExePath);
if (NULL != lpszParam)
{
TRACELOG(TEXT("[INFO] lpszParam : %s"), lpszParam);
}
if (NULL != lpszDirectory)
{
TRACELOG(TEXT("[INFO] lpszDirectory : %s"), lpszDirectory);
}
TRACELOG(TEXT("[INFO] Block : %d"), bBlock);

stInfo.cbSize = sizeof(stInfo);
stInfo.lpFile = lpszExePath;
stInfo.lpDirectory = lpszDirectory;
stInfo.lpParameters = lpszParam;
stInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI | SEE_MASK_CLASSNAME;
stInfo.nShow = SW_SHOW;
stInfo.lpClass = TEXT("exefile");

if (FALSE == ::ShellExecuteEx(&stInfo))
{
// ShellExcute가 실패했다.
TRACELOG(TEXT("[ERROR] Fail to ShellExecuteEx, %d"), ::GetLastError());

// CreateProcess로 재도전
strCmdLine = lpszExePath;
strCmdLine.TrimRight(TEXT("\""));
strCmdLine.TrimLeft (TEXT("\""));
strCmdLine.Insert(0, TEXT("\""));
strCmdLine += TEXT("\"");

si.cb = sizeof(si);

if (NULL != lpszParam)
{
strCmdLine += TEXT(" ");
strCmdLine += lpszParam;
}

lpszCmd = new TCHAR[_tcslen(strCmdLine)+1];
if (NULL == lpszCmd)
{
dwRtnValue = ERROR_NOT_ENOUGH_MEMORY;
goto FINAL;
}
ZeroMemory(lpszCmd, sizeof(TCHAR)*(_tcslen(strCmdLine)+1));
::StringCchCopy(lpszCmd, _tcslen(strCmdLine)+1, strCmdLine);
strCmdLine.Empty();
if (FALSE == ::CreateProcess(NULL, 
 lpszCmd, 
 NULL, 
 NULL, 
 FALSE, 
 0, 
 NULL, 
 lpszDirectory, 
 &si,
 &pi))
{
dwRtnValue = ::GetLastError();
TRACELOG(TEXT("[INFO] Fail to CreateProcess, %d"), dwRtnValue);
goto FINAL;
}

if (TRUE == bBlock)
{
TRACELOG(TEXT("[INFO] Try to Wait process(CP) %s"), lpszExePath);
dwRtnValue = ::WaitForSingleObject(pi.hProcess, INFINITE);
TRACELOG(TEXT("[INFO] Finished to Wait process(CP) %s"), lpszExePath);

if (NULL != pnExitcode)
{
if (FALSE == ::GetExitCodeProcess(pi.hProcess, (DWORD*)pnExitcode))
{
dwRtnValue = ::GetLastError();
TRACELOG(TEXT("[ERROR] Fail to GetExitCode(CP), GetLastError=%d"), dwRtnValue);
goto FINAL;
}

TRACELOG(TEXT("[INFO] ExitCode(CP)=%d"), (*pnExitcode));
}
}

if (NULL != pi.hProcess)
{
::CloseHandle(pi.hProcess);
pi.hProcess = NULL;
}

if (NULL != pi.hThread)
{
::CloseHandle(pi.hThread);
pi.hThread = NULL;
}
}
else
{
if (NULL != stInfo.hProcess)
{
if (TRUE == bBlock)
{
TRACELOG(TEXT("[INFO] Try to Wait process %s"), lpszExePath);
dwResult = ::WaitForSingleObject(stInfo.hProcess, INFINITE);
TRACELOG(TEXT("[INFO] Finished to Wait process %s"), lpszExePath);

if (NULL != pnExitcode)
{
if (FALSE == ::GetExitCodeProcess(stInfo.hProcess, (DWORD*)pnExitcode))
{
dwRtnValue = ::GetLastError();
TRACELOG(TEXT("[ERROR] Fail to GetExitCode, GetLastError=%d"), dwRtnValue);
goto FINAL;
}

TRACELOG(TEXT("[INFO] ExitCode=%d"), (*pnExitcode));
}
}

::CloseHandle(stInfo.hProcess);
stInfo.hProcess = NULL;
}
}

dwRtnValue = ERROR_SUCCESS;

FINAL:
if (NULL == lpszCmd)
{
delete [] lpszCmd;
lpszCmd = NULL;
}

if (NULL != stInfo.hProcess)
{
::CloseHandle(stInfo.hProcess);
stInfo.hProcess = NULL;
}
if (NULL != pi.hProcess)
{
::CloseHandle(pi.hProcess);
pi.hProcess = NULL;
}

if (NULL != pi.hThread)
{
::CloseHandle(pi.hThread);
pi.hThread = NULL;
}
return dwRtnValue;
}


너무 기네요. ㅠㅠ.. 여하튼 참고~