220 likes | 244 Views
Programming ArcObjects with VC++. Lesson 2 overview. VC++ and ArcObjects/COM programming review How to import type libraries Smart pointers COM data types, HRESULTs , strings, and variants Microsoft Visual Studio Exercise 2: Simple console application.
E N D
Lesson 2 overview • VC++ and ArcObjects/COM programming review • How to import type libraries • Smart pointers • COM data types, HRESULTs, strings, and variants • Microsoft Visual Studio • Exercise 2: Simple console application
How VC++ facilitates ArcObjects development • Client-side COM support • Native compiler support: Direct-To-COM (DTC) • #import directive to import type libraries • Support classes for standard OLE types (BSTRs, VARIANTs, …) • Server-side COM support • ATL (Active Template Library) • Templates and wizards for building COM objects and servers
Importing the ArcObjects library • #importmodifiers are required • Specify esriCore.olb path: Tools > Options > Directories // stdafx.h #pragma warning(push) // Sets the current warning state #pragma warning(disable : 4146) // Ignore –2147483648 for uint #pragma warning(disable : 4192) // Exclude‘name’ while importing #import "c:\arcgis\arcexe81\bin\esriCore.olb" \ // Type lib to generate C++ mapping raw_interfaces_only, \ // Don’t add raw_ to method names raw_native_types, \ // Don’t map to DTC smart types no_namespace, \ // Don’t wrap with C++ name space named_guids, \ // Named guids and declspecs exclude("OLE_COLOR", "OLE_HANDLE") // Exclude conflicting types #pragma warning(pop) // Restores state of warnings – undo Instructor Demo
ArcObjects wrapper classes • #import automatically generates type library wrappers • Creates typedefs, smart pointers, structures… // Created by Microsoft (R) C/C++ Compiler Version 12.00.8168.0 (d727fb44). // // c:\student\arcobjectscpp\exercises\(1) - cpp\aoconsoleapp_solution\debug\esriCore.tlh // // C++ source equivalent of Win32 type library esriCore.olb // compiler-generated file created 12/20/01 at 11:33:49 - DO NOT EDIT! … /* interface */IMap; struct __declspec(uuid("34b2ef81-f4ac-11d1-a245-080009b6f22b")) … extern "C" const GUID __declspec(selectany) CLSID_Map = {0xe6bdaa76,0x4d35,0x11d0,{0x98,0xbe,0x00,0x80,0x5f,0x7c,0xed,0x21}}; … _COM_SMARTPTR_TYPEDEF(IMap, __uuidof(IMap)); Typedef _com_ptr_t<_com_IIID<IMap, __uuidof(IMap)>> IMapPtr; … struct __declspec(uuid("e6bdaa75-4d35-11d0-98be-00805f7ced21")) IMap : IUnknown { // Raw methods provided by interface virtual HRESULT __stdcall get_Name ( BSTR * Name ) = 0; virtual HRESULT __stdcall put_Name ( BSTR Name ) = 0; …
DTC smart types • VC++ classes that encapsulate data types • _com_ptr_t<> Encapsulates a COM interface pointer (smart pointer) • _bstr_t Encapsulates the BSTR data type • _variant_t Encapsulates variant type and automatically initializes • _com_error Represents a COM exception using IErrorInfo • Classes behave like raw types • Helpful constructors, functions, and operators • Maps all COM errors to C++ exceptions • Use to simplify ArcObjects code…
Initializing the COM library • Necessary for stand-alone ArcObjects applications • Steps 1. ::CoInitialize() - Loads the COM libraries 2. Cocreate and use ArcObjects objects 3. ::CoUninitialize() - Unloads COM libraries #include "stdafx.h" int main(int argc, char* argv[]) { ::CoInitialize(NULL); IMapPtr ipMap(CLSID_Map); // Simple code to create and call COM objects int iLayers; ipMap->get_LayerCount(&iLayers); _tprint(“Number of layers: %i \n“, iLayers); ::CoUninitialize(); }
Creating new ArcObjects objects • COM API – ::CoCreateInstance() • DTC smart pointers – Simplified but less control… // CoCreate a Map IMap* pMap; HRESULT hr = ::CoCreateInstance( CLSID_Map, // Class identifier (CLSID) of the object 0, // Pointer to outer unknown pointer CLSCTX_ALL, // Context for running executable code IID_IMap, // Reference to the interface identifier (void **) &pMap // Indirect pointer to requested interface ); if (FAILED(hr)) return hr; // Check HRESULT… // CoCreate a Map IMapPtr ipMap(CLSID_Map); if (ipMap == NULL) return E_POINTER; // Check for NULL
Querying interfaces • COM API – IUnknown::QueryInterface() • DTC smart pointers – Use assignment operator // CoCreate a Map IMap* pIMap; ::CoCreateInstance(CLSID_Map, 0, CLSCTX_ALL, IID_IMap, (void **) &pMap); IActiveView* pActiveView; pMap->QueryInterface(IID_IActiveView, (void **) &pActiveView); // QI // CoCreate a Map IMapPtr ipMap(CLSID_Map); // QI IActiveViewPtr ipActiveView(ipMap); // QI // or use the assignment “=“ operator directly IActiveViewPtr ipActiveView; ipActiveView = ipMap; // QI
Object lifetime control: Smart pointers // Smart pointer reference count test long refCount; // CoCreate the SimpleObject IUnknownPtr ipUnk(CLSID_SimpleObject); // AddRef ISimpleObjectPtr ipMagic(ipUnk); // AddRef ipMagic->get_ReferenceCount(&refCount); IAnotherInterfacePtr ipMagic2(ipMagic); // AddRef ipMagic->get_ReferenceCount(&refCount); ipMagic2 = NULL; // What happens? ipMagic->get_ReferenceCount(&refCount); ipMagic = NULL; // What happens? // What happens to ipUnk here? ::CoCreateInstance(CLSID_SimpleObject, 0, CLSCTX_ALL, IID_IUnknown, (void **) &ipUnk); ipUnk = NULL; Instructor Demo
COM data type mapping Language IDL Microsoft C++ Visual Basic Microsoft Java Boolean unsigned char unsupported char byte unsigned char unsupported char small char unsupported char short short Integer short long long Long int Base hyper __int64 unsupported long Types float float Single float double double Double double char unsigned char unsupported char wchar_t wchar_t Integer short enum enum Enum int Interface Pointer Interface Pointer Interface Ref. Interface Ref. Extended VARIANT VARIANT Variant ms.com.Variant Types BSTR BSTR String java.lang.String VARIANT_BOOL short (-1 true/0 false) Boolean [true/false]
BSTRs • COM API – SysAllocString, SysStringLen, SysFreeString • DTC smart type – _bstr_t int main(int argc, char* argv[]) { BSTR bstrDBPath = ::SysAllocString(L"..\\..\\..\\Data\\US.mdb"); BSTR bstrFCName = ::SysAllocString(L"States"); IFeatureClassPtr ipFeatureClass; SelectFeatureClass1(bstrDBPath, bstrFCName, &ipFeatureClass); ::SysFreeString(bstrDBPath); ::SysFreeString(bstrFCName); } int main(int argc, char* argv[]) { _bstr_t bstrDBPath(L"..\\..\\..\\Data\\US.mdb"); _bstr_t bstrFCName(L"States"); IFeatureClassPtr ipFeatureClass; SelectFeatureClass1(bstrDBPath, bstrFCName, &ipFeatureClass); }
VARIANTs • COM API – VariantInit() and VariantClear() • DTC smart type – _variant_t long l = 1000; VARIANT vValue1; ::VariantInit(&vValue1); vValue1.vt = VT_R4; // Set variant type here vValue1.lVal = l; printf("The value is: %d\n", vValue1.lVal); ::VariantClear(&vValue1); long l = 1000; _variant_t vValue2(l); printf("The value is: %d\n", vValue2.lVal);
Not so smart smart types • Understand the behavior beforehand // Variant and smart pointer test IUnknownPtr ipUnk(CLSID_SimpleObject); // Create an object #1 _variant_t vValue; vValue.vt = VT_UNKNOWN; vValue.punkVal = ipUnk;// What happens? ipMagic = ipUnk; ipMagic->get_ReferenceCount(&refCount); // Release these ipMagic = 0; ipUnk = 0; vValue.punkVal->QueryInterface(&ipMagic2); // What happens? ipMagic2 = 0; vValue.vt = VT_EMPTY; // What happens now?What is missing from this code? Instructor Demo
HRESULTs • Always check HRESULTs returned by ArcObjects • Use SUCCEEDED() or FAILED() macros • Non-zero severity means failed HRESULT hr; IWorkspaceFactoryPtr ipWorkspaceFactory(CLSID_AccessWorkspaceFactory); if (ipWorkspaceFactory == NULL) return E_FAIL; IWorkspacePtr ipWorkspace; if (FAILED(hr = ipWorkspaceFactory->OpenFromFile(bstrDatabasePath, NULL, &ipWorkspace))) return hr; …
Formatting HRESULTs • Possible to get and display the error code message int main(int argc, char* argv[]) { IMapPtr ipMap(CLSID_Map); IMxDocument ipMxDoc; if (FAILED(hr = ipMap->QueryInterface(&ipMxDoc)))// Force a failed HRESULT HRESULTMESSAGE(hr); } inline void HRESULTMESSAGE( HRESULT hr ) { LPVOID lpMsgBuf; ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, // HRESULT MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); _TCHAR buf[256]; _stprintf( buf, "%s", (_TCHAR *) lpMsgBuf ); ::LocalFree( lpMsgBuf ); MessageBox(0, buf, _T("HRESULT ERROR MESSAGE"), MB_OK | MB_ICONINFORMATION ); }
Exception handling for DTC types • Native DTC classes throw _com_error exceptions • _com_error • Wraps IErrorInfo object • Returns: Description, HelpContext, HelpFile, Source try { // Force smart pointer error... IMapPtr ipMap; IActiveViewPtr ipAV; ipMap->QueryInterface(&ipAV); } catch (_com_error & err) { ErrorHandler(err); } void ErrorHandler(_com_error err) { _bstr_t bstrError; if (&err == NULL) return; bstrError = _T("Smart type error:"+ err.Description().length() > 0 ? (LPCTSTR) err.Description() : err.ErrorMessage()); ::MessageBox(NULL, bstrError, _T("Generic Error"), MB_OK | MB_TASKMODAL); }
Developing console applications • Database related – Adding, deleting, and updating • Utility applications – Print jobs, describe feature classes int main(int argc, char* argv[]) { CoInitialize(NULL); HRESULT hr; IFeatureClassPtr ipFeatureClass; _bstr_t bstrDBPath(L"..\\..\\..\\Data\\US.mdb"); _bstr_t bstrFCName(L"States"); hr = SelectFeatureClass1(bstrDBPath, bstrFCName, &ipFeatureClass); if (FAILED(hr)) return -1; ::MessageBox(0, _T("Found feature class!"), _T("Console Applicaiton"), MB_OK); hr = PrintFeatureClassInformation(ipFeatureClass); if (FAILED(hr)) return -1; ::MessageBox(0, _T("Printed feature class info!"), _T("Console Applicaiton"), MB_OK); CoUninitialize(); return 0; }
Writing ArcObjects/Visual Studio exercises • Some VS projects are written from scratch • Format-related • All code inserts are prefixed with file name (e.g., File.h) • ‘…’ means ‘scroll down until you find’ • Some steps have ‘step insert numbers’ • Bold means ‘write code’ // MBFeature.h : Declaration of the CMBFeature … public: CMBFeature() : m_pInnerUnk(NULL) { } // ----------- Step 2.6 ---------- DECLARE_GET_CONTROLLING_UNKNOWN() … BEGIN_COM_MAP(CMBFeature) COM_INTERFACE_ENTRY(IMBFeature) COM_INTERFACE_ENTRY(ISupportErrorInfo) COM_INTERFACE_ENTRY(IFeatureDraw) END_COM_MAP() };
More exercise hints… • Follow conventions • Project and class names • Project and file paths • Copy and paste ExerciseFunctions.h file • Use exercise shortcuts • Be careful cutting and pasting solutions
Exercise 2 overview • Create a single ArcObjects COM client • Import the esriCore.olb correctly • Initialize the COM library • Accesses a geodatabase • Print featureclass name and field information • Challenge: Provide client-side error handling
Review • What is the first thing you need to do to work with ArcObjects in VC++? • Why might you want to create an ArcObjects console-type application? • What are the dangers of using smart types? • True or False: VARIANT_TRUE = 1 and VARIANT_FALSE = 0?