델파이에서 VC 8.0(VS 2005)에서 작성한 DLL을 사용하는데
개발환경이 셋업되어있지않은 곳에서 사용하는데 문제가 있어서 몇일간 삽질한 내용을
정리하고자한다.

테스트 환경
  우선 Visual C++ Ver 8.0에서 DLL을 만들었다.
  그리고 그것을 Delphi 2005에서 만든 실행 파일에서 불러서 쓰려했다.

  실행파일을 실행한 OS는 Windows XP Professional SP2로
  개발환경이 전혀 깔려있지않은 상태였다.
 


첫번째 삽질
델파이라 만든 실행파일을 실행하면 아래의 메세지가 떴다.
"응용 프로그램을 제대로 초기화하지 못했습니다(0xc0150002)."

→ 문제는 Visual C++ Ver 8.0에서 DLL을 만들 때 Debug 모드로 빌드를 해서
    Debug용 DLL로 테스트를 했었다.
    Release 모드로 바꾸어서 빌드 해봤지만 에러는 뜨지 않았지만
    dll이 제대로 동작하지 않았다.

두번째 삽질
VC 8.0(VS 2005)으로 DLL을 만들면 DLL만 카피한다고 DLL이 제대로 동작하지 않는다.
자세한 내용은 옆의 링크 참조 배포(C++)
여하튼 가장 편한 방법은 아래의 패키지를 설치하는 것이 가장 편한 것같다.
Microsoft Visual C++ 2005 재배포 가능 패키지(x86)
(무엇이 좋아졌는지는 잘 모르겠지만 왜 더 불편하게 만들어졌는지가 잘 이해가 안간다.)

세번째 삽질
호출 규칙이란 것을 모르고 DLL을 작성하였다.
(아직도 제대로 이해하지 못하고 있지만...)
Visual C++ 2005의 호출규칙(컴파일 옵션) 에는 아래와 같은 것들이 있다.
    __cdecl
    __fastcall
    __stdcall  

__cdecl, __pascal, __stdcall의 차이점 (구글검색)
위 링크를 보면 자세한 내용 있는 홈페이지를 볼 수 있다.
(주로 한 글이 계속 펌되어져있다.)

가변매개인자를 꼭 사용해야만 한다면 반드시 cdecl을 사용해야된다고 한다.
이런 위의 사실들을 모르고 dll을 만들었기 때문에 제대로 dll을 인식하지 못했다.

Visual C++ 2005의 호출규칙 컴파일 옵션을 설정하는 방법은
메뉴의 프로젝트(P)에서 속성(P)[Alt+F7]을 선택하면 속성 페이지가 선택된다.

구성에서 활성(Release)를 선택하고
구성 속성 밑의 C/C++를 선택하고 그 밑의 고급을 선택한다.
그러면 호출규칙이 표시된다.

물론 델파이에서 DLL 함수 선언할 때도 Visual C++ 2005에서 선택한
호출 규칙과 동일하게 선언해야 된다.

이런 것들을 제대로 파악을 못해서 몇일동안 정말 삽질을 했다.
왜 안돌아갈까 왜 안돌아갈까?
많이 고민했다.

시간이 지나서 또 삽질할까봐 적어둔다.

Posted by 띠리


시리얼 포트 오픈

시리얼 포트는 파일처럼 다룬다.
시리얼 포트를 오븐 하기위해서는 CreateFile()을 사용한다.

HANDLE CreateFile(
  LPCTSTR lpFileName,         // 포트 이름을 가르키는 버퍼의 포인터
  DWORD dwDesiredAccess,       // 억세스모드(READ, WRITE)
  DWORD dwShareMode,          // 포트의 공유 방법 비정(공유 불가: 0으로 설정)
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 시큐리티 속성
  DWORD dwCreationDisposition,  // 포트 여는 방법 지정(OPEN_EXISTING로 기존 파일 지정)
  DWORD dwFlagsAndAttributes,   // 포트의 속성 지정
  HANDLE hTemplateFile          // 템플레이트 파일의 핸들(항상 NULL로 지정)
 );


인수:

lpFileName: COM1이나 COM2등의 시리얼 포트 이름을 지정한다.
(LPT1같은 프린터 포트도 지정할 수 있다.)

dwDesiredAccess: 이하의 읽기 모드를 단독 또는 OR로 지정한다.

의미
0 디바이스의 속성을 묻는다.
GENERIC_READ 읽기 억세스.
읽기 쓰기를 할때는 GENERIC_WRITE를 조합해서 지정
GENERIC_WRITE 쓰기 억세스.
쓰기 쓰기를 할때는 GENERIC_READ를 조합해서 지정

일반적으로 시리얼 포트는 입출력이 가능하기 때문에 GENERIC_READ | GENERIC_WRITE로 설정한다.

dwShareMode : 포트의 공유모드로 이하의 값을 단독이나 OR로 지정한다.
0을 지정하면 공유하지 않는다.
 
 
의미
FILE_SHARE_DELETE Windows NT 일 경우만 : 
후속의 오픈 조작으로 삭제 억세스가 요구될 때, 그 오픈을 허가
FILE_SHARE_READ 후속의 호픈 조작으로 읽기 억세스가 요구될 때 그 오픈을 허가
FILE_SHARE_WRITE 후속의 호픈 조작으로 쓰기 억세스가 요구될 때 그 오픈을 허가

 
 포트는 파일과 달리 공유할수 없기 때문에 0으로 지정한다. 이 경우는 이미 오픈 되어진 포트를 다른 프로세스가 오픈 하려고 하면 CreateFile()은 에러가 된다. 하지만 같은 프로세스의 복수 스레드는 CraeteFile()로 반환된 핸들을 공유할 수 있다.

lpSecurityAttributes : 시큐리티 속성으로 SECURITY_ATTRIBUTES 구조체의 포인터이다.
NULL을 설정하면 핸들은 자식 프로세스에 계승되어지지않는다.

dwCreationDisposition : 파일이 존재할 때 또는 존재하지 않을 때, 각각의 동작을 지정한다.
시리얼 포트는 기존 파일 이외에 있을 수 없기 때문에 OPEN_EXISTING을 지정한다.

dwFlagsAndAttributes : 파일의 속성 및 플래그를 지정한다.
시리얼 포트에서 사용할만한 플래그는 FILE_FLAG_OVERLAPPED 뿐이다.

의미
FILE_FLAG_OVERLAPPED 시간이 걸리는 처리에 있어서 ERROR_IO_PENDING을 반환하도록 한다. 처리가 끝나면 이벤트가 시그널 상태로 설정된다.
이 플래그를 지정했을 때는 ReadFile 함수나 WriteFile 함수로 OVERLAPPED 구조체를 지정해야만 된다.


hTemplateFile : 시리얼 포트에 관계없기 때문에 NULL을 지정한다.


시리얼 포트 클로즈

클로즈는 CreateFile()이 반환하는 핸들을 인수로 하여 CloseHandle을 호출한다.

BOOL CloseHandle( HANDLE hObject );


반환값 : 성공하면 0 이외의 값, 실패하면 0을 반환한다.

시리얼 포트 상태 정보 얻기

자신의 컴퓨터에 시리얼 포트는 어느 정도의 성능인지 등 보통은 신경쓰지않지만 디바이스 드라이버를 포함한 시리얼 포트의 성능을 앎으로 서포트 하지않는 보레이트를 잘못해서 설정했을 때의 부정한 설정을 막아준다.

시리얼 포트에 관한 정보는 GetCommProperties()의 호출로 COMPROC 구조체 변수를 읽어온다.

BOOL GetCommProperties (HANDLE hFile, LPCOMMPROP lpCommProp );


인수 :

hFile : 통신 디바이스의 핸들
CreateFile함수가 반환하는 핸들

lpCommProp : 통신 특성 정보가 반환되어 COMPROC 구조체의 포인터

반환값 : 함수가 성공하면 0 이외의 값, 실패하면 0을 반환한다.

LPCOMMPROP 구조체는 이하의 구성의 시리얼 포트 정보를 가진다.

typedef struct _COMMPROP{
  WORD wPacketLength;     // 구조체 사이즈
  WORD wPacketVersion;    // 구조체 버젼
  DWORD dwServiceMask;    // 실행된 서비스
  DWORD dwReserved1;      // 예약
  DWORD dwMaxTxQueue;    // 바이트 단위의 최대 송신 버퍼 사이즈
  DWORD dwMaxRxQueue;    // 바이트 단위의 최대 수신 버퍼 사이즈
  DWORD dwMaxBaud;      // 보레이트 최대값
  DWORD dwProvSubType;       // 특정 프로바이더 타입
  DWORD dwProvCapabilities;     // 서포트된 함수
  DWORD dwSettableParams;      // 변경 가능 파라미터
  DWORD dwSettableBaud;     // 허가된 보레이트
  WORD wSettableData;        // 허가된 바이트 사이즈
  WORD wSettableStopParity;      // 스탑 비트 / 패리티 허가
  DWORD dwCurrentTxQueue;     // Tx 버퍼 사이즈
  DWORD dwCurrentRxQueue;     // Rx 버퍼 사이즈
  DWORD dwProvSpec1;       // 프로바이더 특정 데이터
  DWORD dwProvSpec2;       // 프로바이더 특정 데이터
  WCHAR wcProvChar[1];       // 프로바이더 특정 데이터
}COMMPROP;

dwServiceMask : 시링러 포트의 경우는 항상 SP_SERIALCOMM이다.
dwReserved1 : 미사용
dwMaxTxQueue : 출력 버퍼의 최대값(바이트), 0을 설정하면 무제한
dwMaxRxQueue : 입력 버퍼의 최대값(바이트), 0을 설정하면 무제한
dwMaxBaud : 이하의 상수로 설장 가능한 보레이트의 최대값

의미
BAUD_075 75 bps
BAUD_110 110 bps
BAUD_134_5 134.5 bps
BAUD_150 150 bps
BAUD_300 300 bps
BAUD_600 600 bps
BAUD_1200 1200 bps
BAUD_1800 1800 bps
BAUD_2400 2400 bps
BAUD_4800 4800 bps
BAUD_7200 7200 bps
BAUD_9600 9600 bps
BAUD_14400 14400 bps
BAUD_19200 19200 bps
BAUD_38400 38400 bps
BAUD_56K 56K bps
BAUD_57600 57600 bps
BAUD_115200 115200 bps
BAUD_128K 128K bps
BAUD_USER 유저 설정

dwProvSubType : 이하의 상수로 CreateFile()에 의해 작성된 통신 디바이스가 무엇을 서포트하는지를 알려준다. 시리얼 포트의 경우는 PST_RS232가 된다.

의미
PST_FAX 팩스
PST_LAT LAT 프로토콜
PST_MODEM 모뎁
PST_NETWORK_BRIDGE 미정의 네트워크 브릿지
PST_PARALLELPORT 패럴렐 포트
PST_RS232 RS-232 시리얼 포트
PST_RS422 RS-422 포트
PST_RS423 RS-423 포트
PST_RS449 RS-449 포트
PST_SCANNER 스캐너
PST_TCPIP_TELNET TCP/IP TelnetR 프로토콜
PST_UNSPECIFIED 미정의
PST_X25 X.25 스탠다드


dwProvCapabilities : 이하의 상수로 시리얼 포트가 서포트 하는 기능을 보여준다.
통산의 리리얼 포트 드라이버는 PCF_16BITMODE와 PCF_SPECIALCHARS 이외는 대체적으로 서포트한다.

의미
PCF_16BITMODE 특별한 16비트 모드를 서포트
PCF_DTRDSR DTR/DSR 서포트
PCF_INTTIMEOUTS 인터벌 타임아웃을 서포트
PCF_PARITY_CHECK 패리티 체크 서포트
PCF_RLSD RLSD(CD) 서포트
PCF_RTSCTS RTS/CTS 서포트
PCF_SETXCHAR 설정 가능한 XON/XOFF 서포트
PCF_SPECIALCHARS 설정 가능한 특수문자 서포트
PCF_TOTALTIMEOUTS 토탈 타임아웃 서포트
PCF_XONXOFF XON/XOFF 플로 제어 서포트

dwSettableParams : 이하의 상수로 설정 변경 가능한 파라미터를 나타낸다.

의미
SP_BAUD 보레이트
SP_DATABITS 1케릭터의 비트수
SP_HANDSHAKING 핸드 쉐이크 방식(플로 제어)
SP_PARITY 패리티 비트 수
SP_PARITY_CHECK 패리티 체크의 유무
SP_RLSD RLSD(CD)
SP_STOPBITS 스톱 비트 수

dwSettableBaud : 설정 가능한 모든 보레이트의 종류를 dwMaxBaud의 항목으로 정의된 상수값으로 OR한 값을 나타낸다.

wSettableData : 이하의 상수의 OR로 설정 가능한 1 케릭터의 비트 수를 나타낸다.
시리얼 포트에서는 5~8이 서포트 된다.

의미
DATABITS_5 5 data bits
DATABITS_6 6 data bits
DATABITS_7 7 data bits
DATABITS_8 8 data bits
DATABITS_16 16 data bits
DATABITS_16X 시리얼 회선을 사용한 특수한 와이드 버스

wSettableStopParity : 이항의 상수의 OR로 설정 가능한 스톱 비트 수와 패리티를 나타낸다.

의미
STOPBITS_10 1  스톱 비트
STOPBITS_15 1.5 스톱 비트
STOPBITS_20 2 스톱 비트
PARITY_NONE 패리티 없음
PARITY_ODD 홀수 패리티
PARITY_EVEN 짝수 패리티
PARITY_MARK 마크  패리티
PARITY_SPACE         스페이스 패리티


dwCurrentTxQueue : 현재 송신 버퍼 사이즈를 나타낸다.

dwCurrentRxQueue : 현재 수신 버퍼 사이즈를 나타낸다.

시리얼 포트의 상태를 얻기 예제

COMMPROP구조체의 주요 내용을 표시하는 프로퍼티 시트 예제

이 예제는 컴포트1에서 4까지를 고정으로 오픈한다.
(요즘 기본적으로 컴포트가 없는 컴퓨터가 많아서 USB를 쓰는 사람은
컴포트를 1에서 읽는 것을 좀 수정할 필요가 있다.
USB 컴포트가 3이나 4로 잡히면 그냥 실행시켜도 문제없다.)

컴포트 1에서 4를 열어 각 포트의 시리얼 포트 상태와
각 포트에 설정 가능값을 표시한다.


Posted by 띠리
정말 이상하다. 내가 머리가 나쁜건가?
Visual Studio 2005의 Visual C++은 Relese로 빌드를 하고 그 실행 파일만을
Visual Studio 2005가 설치 되어있지않은 컴퓨터에서 돌리면 에러가 난다.

Visual Studio 2005의 Visual C++로 빌드한 실행 파일을 돌리기 위해서는
여러가지 뭔가를 해주어야되나보다.
왜 MS는 이렇게 만들었을까?
이게 더 좋아진건가?
무엇이 어떻게 더 좋아진것일까?
난 머리가 딸려서 이해가 잘안온다.


http://msdn2.microsoft.com/ko-kr/library/zebw5zk9(VS.80).aspx

위 사이트에 가면 Visual Studio 2005의 Visual C++로 작성한 프로그램을
배포하기 위한 방법이 쓰여져있다.

MS는 정말 정말 정말 왜 이렇게 만들었을까?
정말 무엇을 얼마나 좋게 개선했는데 이렇게 만들걸까?
누군가 좀 속시원하게 알려주었으면 좋겠다.


비주얼 스투디오 2005 비주얼 C++ 배포 관련 참고 사이트
http://serious-code.net/moin.cgi/RedistributingVisualCppRunTimeLibrary
Posted by 띠리
Visual Studio 2005에는 클래스 위져드가 없어졌다.

다이알로그에서 OnTimer 메세지 함수 추가하려고
그냥 코드에서 OnTimer를 추가해 보았지만 이벤트가 발생하지 않았다. -.-;;
그래서 열심히 찾다 보니까 메세지 함수를 추가하는 방법이 있었다.

먼저 메세지 함수를 추가하려는 다이알로그를 클래스 뷰에서 선택한다.

사용자 삽입 이미지

선택한 다이알로그에서 Alt + Enter를 누르던가
오른쪽 클릭을 해서 속성을 선택하면 아래같이 속성창이 나타난다.

사용자 삽입 이미지

속성창에서 밑에 메세지를 선택하면 메시지 리스트가 표시된다.

사용자 삽입 이미지

WM_TIMER를 선택하면 <추가> OnTimer가 표시되고 <추가> OnTimer를 선택하면
아래 그림과 같이 OnTimer함수가 추가되어진다.


사용자 삽입 이미지


Posted by 띠리
C/C++ 분산 어플리케이션 및 side-by-side 어셈블리 트러블 슈팅
(Troubleshooting C/C++ Isolated Applications and Side-by-side Assemblies)

의존하는 Visual C++ 라이브러리가 발견되지않는 겨우는 C/C++ 어플리케이션의 읽어들이기에 실해하는 경우가 있다. 발생하는 가능성이 있는 런타임 에러의 일람에 관해서는 "Visual C++파일의 재배포"를 참조하면 된다.
이 섹션에서는 C/C++ 어플리케이션의 읽어들이기에 실패하는 가장 일반적인 이유에 대해서 문제해결의 방법을 알아본다.

Visual C++가 인스톨되어 있지 않은 컴퓨터에 어플리케이션을 카피하고 "Visual c++파일 재배포"에 열거되어진 에러 메세지로 어플리케이션이 크래쉬한 경우, 몇개의 포인트를 확인하여 에러의 원인을 찾아내야 된다.

1. "Visual c++ 어플리케이션의 의존관계의 이해"의 순서에 따른다. Dependency Walker에는 특정의 어플로케이션 또는 DLL의 대부분의 의존관계를 표시할 수 있다. 표시되어져 있지않은 DLL이 있는 경우는 어플리케이션을 실행하는 컴퓨터에 그 DLL을 인스톨한다.

2. 오퍼레이팅 시스템 로더는 메니페스트를 사용하여 어플리케이션이 의존하는 어셈블리를 읽어들인다. 메니페스트는 리소스로서 바이너리에 넣는 것도 외부 파일로서 어플리케이션의 모컬 폴더에 보전하는 것도 가능하다.
메니페스트가 바이너리 안에 들어가 있는지를 확인하기 위해서는 Visual Studio에서 바이너리를 열어 이 바이너리 리소스를 조사하면된다. 들어가 있는 경우에는 RT_MANIFEST라고 하는 리소스를 볼수있다. 바이너리에 넣어진 메니페스트가 없을때는 <binary_name>.<extension>.manifest와 같은 이름의 외부 파일을 찾는다.

3. 메니패스트가 없는 경우는 링커에 프로젝트의 메니페스트를 만들어야된다. 이 프로젝트의 "프로젝트 속성" 다이얼로그의 링커의 메니페스트의 생성 옵션을 예로 한다.

메모
메니패스트가 생성하지 않는 Visual C++ 프로젝트의 빌터는 서포트하지 않는다.
Visual C++ 2005에서 빌드되어지는 C/C++프로그램은 모두 Visual C++ 라이브러리에의 의존 관계를 기술한 메니패스트를 포함해야된다.

4. 메니페스트가 라이브러리안에 들어있는 경우는 RT_MANIFEST의  ID가 그 타입의 바이너리에 적절한 ID인지를 확인한다. 어플리케이션의 경우는 ID를 1로, 또는 거의 DLL의 경우는 ID를 2로  해야된다. 메니페스트가 외부 파일의 경우는 XML에디터나 텍스트 에디터를 열어서 메니페스트 및 배치의 룰의 상에 대해서는 "Manifests"를 참조한다.

메모
Windows XP에서는 외부 메니페스트가 어플리케이션의 로컬 폴더에 있는 경우, 오퍼레이팅 시스템 로더는 바이너리에 넣어진 메니페스트를 이용하지않고 이 외부 메니페스트를 사용한다.
Windows Server 2003 및 이 이후의 버젼의 Windows에서는 이 우선 순위가 바뀌어 바이너리에 넣어진 메니페스트가 있는 경우 그것을 이용하고 외부 메니페스트는 무시한다.

5 모든 DLL이 바이너리로 넣어진 메니페스트를 갖는 것을 권장한다.
DLL이 LoadLibrary 호출로 읽어지는 경우 외부 메니페스트는 무시되어진다.
자세한 내용은 "Assembly manifests"를 참조 바람.

6. 메니페스트에 열거되어진 모든 어셈블리가 컴퓨터에 바르게 인스톨되어져 있는지를 확인한다.
각 어셈블리는 이름, 버젼번호, 및 프로세스 아키텍쳐로 메니페스트에 지정한다. 어플리케이션은 side-by-side 어셈블리에 의존하고 있는 경우 이것들은 어셈블리가 컴퓨터에 적절하게 인스톨되어 오퍼레이팅 시스템 로더로 검출할 수 있는 것을 "Assembly Searching Sequnce"로 지정되어있는 순서를 사용해서 확인한다. 64비트 어셈블리는 32비트 프로세스로 읽어들이거나 32비트 오퍼레이팅 시스템으로 실행할 수 없는 것에 주의한다.

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

http://msdn2.microsoft.com/ja-jp/library/ms235342(VS.80).aspx

이 글은 위 글을 번역한 것
이해가 잘 안온다. -.-;;
메니페스트 개념도 잘 모르겠고
밥통같은 Visual C++ 2005는 DLL을 따로 카피해야되는건가?
Posted by 띠리
가장 먼저 기본은 윈도우를 만드는 것을 해보는 것이 좋을 것같다.
지금 내가 갖고 있는 개발 툴이 Microsoft Visual Studio 2005이기 때문에
Microsoft Visual Studio 2005로 짜는 방법을 소개하고자 한다.
(Microsoft Visual Studio 2005를 설치하는 방법은 생략)

VB에서 폼을 만들어 본사람이면
VB에서는 정말 간단하게 10분도 안되어도
폼을 만들어 띄울수있다.
하기야 SDK창 또한 아는 사람이라면
1분안에도 만들수 있겠지만
아무것도 모른다는 것을 전제하에
SDK폼을 만드는 것으로 설명한다.

사용자 삽입 이미지

Microsoft Visual Studio 2005을 실행해, 메뉴에서 파일을 선택하여 새프로젝트를 선택한다.

사용자 삽입 이미지

프로젝트 형식에서 다른 언어를 선택하고
Visual C++ 밑의 Win32를 선택해서
템플릿의 Win32를 선택한다.
그리고 이름(N): 에 적당한 이름을 넣고 확인 버튼을 누른다.
여기서 입력하는 이름이 프로젝트명이 된다.

사용자 삽입 이미지

그러면 위에 윈도우가 나타나고 다음 버튼을 클릭한다.

사용자 삽입 이미지

그러면 위의 윈도우가 나타나고 디폴트 설정대로 하고
마침 버튼을 누른다.

사용자 삽입 이미지

그러면 자동적으로 간단한 윈도우가 만들어진다.
간단한 윈도우를 만들기위한 최소한의 코스가 자동으로 작성되어진다.

사용자 삽입 이미지

지금 막 만들어진 이 소스를 돌려보기 위해서는
메뉴의 디버그에서 디버그 시작을 선택한다.
그러면 소스를 빌드에서 지금 만들어진 소스가 실행된다.

사용자 삽입 이미지

그래서 만들어진 어플리케이션이 위의 어플리케이션이다.
이렇게 간단하게 나마 윈도우를 SDK로 만들수있다.

다음 회에는 자동으로 생성된 소스에 대해서 간단히
설명하고 넘어가도록 하겠다.
Posted by 띠리
BLOG main image
프로그래밍 공부하면서 써가는 개인 노트 (따라서 여기에 씌여있는 소스의 신빙성을 보장 못함 -.-;;) 이 블로그 보면서 틀린 점이 있으면 꼬옥 알려주세요. by 띠리

공지사항

카테고리

분류 전체보기 (323)
Win32 SDK 초보 (27)
통신관련 (11)
MFC TIP (20)
C/C++ TIP (10)
개발기타 (10)
링크 (2)
견물생심 (24)
이것저것 (8)
용어메모 (3)
데이터베이스 (32)
비주얼 베이직 (10)
하드웨어 (3)
C# (42)
Xcode (3)
델파이 (82)
홈페이지 (5)
MindStorm (0)
낙서 (5)
스크래치 (0)
기타 (6)
아두이노 (1)
라즈베리파이 (2)
안드로이드 (6)
파이썬 (0)
WEB (2)
Total : 987,327
Today : 405 Yesterday : 409