SafeArray 객체 생성 및 파괴
SafeArray를 생성하고 파괴하는 과정을 알아보기 위해 다음 코드를 작성해 본다. 배열을 구성하는 각 원소의 자료형이 부호 없는 8비트 정수(바이트 형)이고 10개로 구성된 배열의 생성법은 다음과 같다.
/* Example: SafeArrayCreate, SafeArrayDestroy */
#include
#include
#include
#include
int main(int argc, char * argv[])
{
HLOCAL hLocal = NULL;
LPSAFEARRAYBOUND lpSafeArrayBound = NULL;
LPSAFEARRAY lpSafeArray = NULL;
hLocal = LocalAlloc(LHND, sizeof(SAFEARRAYBOUND));
/* SafeArray 생성을 위한 개수 및 시작 인덱스 지정 */
lpSafeArrayBound = (LPSAFEARRAYBOUND)LocalLock(hLocal);
lpSafeArrayBound->lLbound = 0; // 첫 번째 항목의 인덱스는 0번부터 시작함.
lpSafeArrayBound->cElements = 10; // 배열 항목의 총 개수는 10개임.
/* 배열 생성하기 */
assert((lpSafeArray = SafeArrayCreate(VI_UI8, 1, lpSafeArrayBound)) != NULL);
/* TODO: Something 0 */
/* 배열 파괴하기 */
assert(SUCCEEDED(SafeArrayDestroy(lpSafeArray)));
printf("END");
return 0;
}
배열을 생성하기 위해 호출하는 함수는 SafeArrayCreate
이고 파괴하는 함수는 SafeArrayDestroy
이다. 이 두 함수에서 사용되는 자료형은 SAFEARRAYBOUND
, SAFEARRAY
이다.
SAFEARRAYBOUND 구조체
typedef struct tagSAFEARRAYBOUND {
ULONG cElements;
LONG lLbound;
} SAFEARRAYBOUND, *LPSAFEARRAYBOUND;
1차원 배열에 대해 그 크기와 시작 인덱스를 명시하는 구조체이다.
- cElements
- 1차원 배열에 대해 원소의 총 개수를 명시한다.
- lLBound
- 첫 번째 원소의 인덱스를 지정한다. C 계열의 언어는 첫 번째 원소의 인덱스가 항상 0번이지만, Basic 계열의 언어는 첫 번째 원소의 인덱스가 기본적으로 1번이고, Pascal 계얼의 언어는 0, 1번 이외의 다른 번호로 직접 지정할 수 있다. COM(Component Object Model)은 여러 언어의 프로젝트에서 코드를 재사용할 수 있으므로 이를 고려하여 지정하는 속성이다.
SAFEARRAY 구조체
typedef struct tagSAFEARRAY {
USHORT cDims;
USHORT fFeatures;
ULONG cbElements;
ULONG cLocks;
PVOID pvData;
SAFEARRAYBOUND rgsabound[1];
} SAFEARRAY, *LPSAFEARRAY;
현재 메모리의 어딘가에 할당되어 있는 SAFEARRAY 객체에 대한 정보를 보관하는 구조체이다.
- cDims
- 이 배열의 차원을 지정한다. 1차원 배열이면 1, 2차원 배열이면 2의 순서이다.
- fFeatures
- 이 배열이 갖는 특성을 나타내는 비트 플래그 집합이다. 위의 MSDN에서 지정 가능한 플래그 상수와 그 의미를 확인할 수 있다.
- cbElements
- 이 배열을 구성하는 원소 1개의 크기를 지정한다.
- cLocks
- 일종의 레퍼런스 카운트와 비슷하다. Lock 계열의 함수에 의해 잠겨진 회수를 보관한다. 멀티 스레드로 운영되는 프로그램에서 스레드 안정성을 위해 내부적으로 사용된다.
- pvData
- 배열의 실제 원소들이 있는 메모리의 번지를 가리킨다.
- rgsabound
- 이 배열을 구성하는 각 차원에 대해 지정된 위의
SAFEARRAYBOUND
속성이 시작되는 부분이다. 즉, 각 차원별로 배열의 크기와 첫 번째 원소의 인덱스 번호가 기록되어 있다.
SafeArrayCreate 함수
SAFEARRAY* SafeArrayCreate(
_In_ VARTYPE vt,
_In_ UINT cDims,
_In_ SAFEARRAYBOUND * rgsabound
);
SafeArray 객체를 생성하는 함수이다.
- vt
- 배열을 구성하는 원소들의 자료형이다.
VARTYPE
자료형이 가질 수 있는 옵션 중 하나를 선택할 수 있다. 단,VT_ARRAY
,VT_BYREF
,VT_EMPTY
,VT_NULL
옵션을 지정하는 것은 유효하지 않다. - cDims
- 본 배열의 차원을 지정한다.
- rgsabound
- 각 차원별로 지정할 bound 속성이다.
vt
매개변수로 올 수 있는 값은 다음과 같다.
속성 | 뜻 | |
VT_VOID | C-style의 void형을 나타내는 자료형이다. | |
VT_BOOL | Boolean 값이다. | |
VT_INT | 시스템 정의 크기의 부호 있는 정수이다. | |
VT_I1 | 1바이트 부호 있는 정수이다. | |
VT_I2 | 2바이트 부호 있는 정수이다. | |
VT_I4 | 4바이트 부호 있는 정수이다. | |
VT_I8 | 8바이트 부호 있는 정수이다. | |
VT_UINT | 시스템 정의 크기의 부호 없는 정수이다. | |
VT_UI1 | 1바이트 부호 없는 정수이다. | |
VT_UI2 | 2바이트 부호 없는 정수이다. | |
VT_UI4 | 4바이트 부호 없는 정수이다. | |
VT_UI8 | 8바이트 부호 없는 정수이다. | |
VT_PTR | 포인터형이다. | |
VT_INT_PTR | 레지스터가 지원 가능한 크기의 부호 있는 포인터형이다. | |
VT_UINT_PTR | 레지스터가 지원 가능한 크기의 부호 없는 포인터형이다. | |
VT_R4 | 단정밀도 부동 소수점의 실수이다. | |
VT_R8 | 배정밀도 부동 소수점의 실수이다. | |
VT_DECIMAL | 16바이트 고정 소수점의 실수이다. | |
VT_CY | 통화형 실수이다. | |
VT_DATE | 날짜 및 시각을 나타내는 자료형이다. | |
VT_BSTR | Basic/Pascal 형식의 문자열이다. | |
VT_LPSTR | 멀티바이트 또는 싱글바이트의 문자열이다. | |
VT_LPWSTR | 와이드 문자열이다. | |
VT_CARRAY | C-style의 배열이다. |
그 외, VT_DISPATCH
, VT_ERROR
, VT_VARIANT
, VT_UNKNOWN
, VT_HRESULT
, VT_SAFEARRAY
, VT_USERDEFINED
, VT_RECORD
, VT_FILETIME
, VT_BLOB
, VT_STREAM
, VT_STORAGE
, VT_STREAMED_OBJECT
, VT_STORED_OBJECT
, VT_BLOB_OBJECT
, VT_CF
, VT_CLSID
, VT_VERSIONED_STREAM
, VT_BSTR_BLOB
, VT_VECTOR
등을 지정할 수 있다.
객체 생성에 성공하면 그 객체의 주소를 반환하고, 그렇지 않을 경우 NULL을 반환한다.
SafeArrayDestroy 함수
HRESULT SafeArrayDestroy(
_In_ SAFEARRAY *psa
);
할당받은 SafeArray 객체를 파괴한다.
파괴에 성공하면 S_OK
를 반환하고 그렇지 않을 경우 E_INVALIDARG
또는 DISP_E_ARRAYISLOCKED
등을 반환한다.
원소의 자료형 구하기
SafeArrayGetVartype
함수를 사용하여 배열을 구성하는 원소들의 자료형을 가져올 수 있다.
HRESULT SafeArrayGetVartype(
_In_ SAFEARRAY * psa,
_Out_ VARTYPE * pvt
);
- psa
- 자료형을 알아 볼 SafeArray 객체이다.
- pvt
- 자료형이 반환될 변수이다.
반환 값 얻기에 성공하면 S_OK
를 반환하고 그 외 문제가 있으면 E_INVALIDARG
을 반환한다.
원소의 개수 구하기
앞서 설명한대로 SafeArray에서는 인덱스가 0 또는 1부터 시작한다는 것이 보장되지 않는다. 특정 차원의 원소의 개수를 구하기 위해서는 그 차원에서 (맨 마지막 원소의 인덱스) - (맨 처음 원소의 인덱스) + 1를 계산하는 방법을 사용한다. 이를 위해 SafeArrayGetLBound
, SafeArrayGetUBound
함수가 사용된다.
위 예제 코드의 /* TODO: Something */
영역에 다음 코드를 넣어 실행해본다.
// LONG lLBound = 0, lUBound = 0, lCount = 0;
assert(SUCCEEDED(SafeArrayGetLBound(lpSafeArray, 1, &lLBound)));
assert(SUCCEEDED(SafeArrayGetUBound(lpSafeArray, 1, &lUBound)));
assert((lCount = (lUBound - lLBound + 1)) >= 0);
SafeArrayGetLBound / SafeArrayGetUBound 함수
HRESULT SafeArrayGetLBound(
_In_ SAFEARRAY * psa,
_In_ UINT nDim,
_Out_ LONG * plLbound
);
HRESULT SafeArrayGetUBound(
_In_ SAFEARRAY * psa,
_In_ UINT nDim,
_Out_ LONG * plUbound
);
SafeArray의 특정 차원에서 각각 시작 원소의 인덱스와 끝 원소의 인덱스를 가져온다.
- psa
- 인덱스를 구할 SafeArray의 포인터이다.
- nDim
- 인덱스를 구할 특정 차원이다.
- plLbound / plUbound
- 지정한 차원에서 시작 인덱스 또는 끝 인덱스를 반환받을 변수이다.
반환 값 얻기에 성공하면 S_OK
를 반환하고 nDim
의 값 또는 다른 인덱스 등이 잘못되었으면 DISP_E_BADINDEX
을 반환한다. 만일 인덱스로 쓰이는 숫자가 너무 커서 연산 시 CPU에 오버플로우가 발생한다면 DISP_E_OVERFLOW
를 반환하고 그 외 매개변수에 문제가 있으면 E_INVALIDARG
을 반환한다.
원소에 값 설정하고 가져오기
원소에 값 설정하기
// LONG lIndex = 0; unsigned char * lpByte = NULL;
assert(SUCCEEDED(SafeArrayLock(lpSafeArray)));
assert(SUCCEEDED(SafeArrayAccessData(lpSafeArray, &lpByte)));
for (lIndex = 0; lIndex < lCount; lIndex++) {
/* TODO: Something */
lpByte[lIndex] = 100 + (lIndex - 1);
}
lpByte = NULL;
assert(SUCCEEDED(SafeArrayUnaccessData(lpSafeArray)));
assert(SUCCEEDED(SafeArrayUnlock(lpSafeArray)));
SafeArray의 특정 원소를 가져오고 설정하기 위해 SafeArrayLock
, SafeArrayUnlock
함수를 사용해 이 스레드에서 독점할 수 있도로 객체를 잠그고, 잠겨있는 동안 SafeArrayAccessData
, SafeArrayUnaccessData
함수를 사용해 배열의 포인터를 얻어서 값을 가져오거나 설정한다.
원소의 값 가져오기
// LONG lIndex = 0; unsigned char * lpByte = NULL;
assert(SUCCEEDED(SafeArrayLock(lpSafeArray)));
assert(SUCCEEDED(SafeArrayAccessData(lpSafeArray, &lpByte)));
for (lIndex = 0; lIndex < lCount; lIndex++) {
/* TODO: Something */
printf("item[%ld] = %d\n", lIndex, lpByte[lIndex]);
}
lpByte = NULL;
assert(SUCCEEDED(SafeArrayUnaccessData(lpSafeArray)));
assert(SUCCEEDED(SafeArrayUnlock(lpSafeArray)));
SafeArrayLock / SafeArrayUnlock 함수
HRESULT SafeArrayLock(
_In_ SAFEARRAY * psa
);
HRESULT SafeArrayUnlock(
_In_ SAFEARRAY * psa
);
SafeArray 객체의 cLocks 필드를 증가하거나 감소시킨다.
- psa
- 잠금 회수를 증가하거나 감소시킬 SafeArray 객체이다.
성공하면 S_OK
, psa
매개변수가 잘못되었으면 E_INVALIDARG
, 그 외 오류는 E_UNEXPECTED
를 반환한다.
SafeArrayAccessData / SafeArrayUnaccessData 함수
HRESULT SafeArrayAccessData(
_In_ SAFEARRAY * psa,
_Out_ void ** ppvData
);
HRESULT SafeArrayUnaccessData(
_In_ SAFEARRAY * psa
);
SafeArray로부터 포인터 배열을 얻거나 해제한다.
- psa
- 포인터 배열을 얻거나 해제할 SafeArray 객체이다.
- ppvData
- 포인터 배열이 반환될 변수이다.
성공하면 S_OK
, psa
매개변수가 잘못되었으면 E_INVALIDARG
, 그 외 오류는 E_UNEXPECTED
를 반환한다.
참고 URL: [MSDN] C++ - Introducing the SAFEARRAY Data Structure https://msdn.microsoft.com/en-us/magazine/mt778923.aspx
'Programming Language > C&C++' 카테고리의 다른 글
[소켓 프로그래밍]UDP 브로드캐스트에 의한 데이터 보내기 및 받기 (0) | 2017.08.05 |
---|---|
소켓 통신 #3 - ICMP 클라이언트 구현하기(3) (0) | 2014.11.14 |
윈도우에서 IP 주소 얻는 방법 (0) | 2014.11.13 |
소켓 통신 #3 - ICMP 클라이언트 구현하기(2) (0) | 2014.11.11 |
소켓 통신 #3 - ICMP 클라이언트 구현하기(1) (0) | 2014.11.11 |