no ATL headers

Post questions, comments and feedback to our 3Dconnexion Windows Development Team.

Moderator: Moderators

Post Reply
gjaegy
Posts: 9
Joined: Thu Mar 22, 2007 8:32 am

no ATL headers

Post by gjaegy »

Hi,

I wonder if there is a way to use the latest SDK without including all of these ATL headers ?

Should I go back to the old SDK or is there any other solution to have the SpaceNavigator support in our engine without those headers ?

Cheers,
Gregory Jaegy
ngomes
Moderator
Moderator
Posts: 3344
Joined: Mon Nov 27, 2006 7:22 am
Contact:

Post by ngomes »

Hi gjaegy,
I believe you only need the ATL headers to build the samples. The new API is based on COM and, if you prefer not to use ATL, the low-level system calls are defined in objbase.h.

What type of app are you building? MFC?
Nuno Gomes
gjaegy
Posts: 9
Joined: Thu Mar 22, 2007 8:32 am

Post by gjaegy »

Hi,

thanks for your answer. actually I am building a plain Win32 app (no MFC).

in order to use COM, one has to use CComPtr objects (CComPtr<ISensor> and CComPtr<IKeyboard>); this CComPtr objects are only defined in <atlbase>, so I have to include at least this header.

Does someone have an example of using the new COM API without any ATL include ?

thanks,
Gregory Jaegy

PS: currently my code looks like this. It works very well, but I still wonder if the same would be possible without this ATL header

Code: Select all

#define _WIN32_DCOM
#include <atlbase>
using namespace ATL;

#import "progid:TDxInput.Device.1" no_namespace

#include "gjdisplay.h"

class gjSpaceNavigatorWrapper : public gjISpaceNavigatorWrapper
{
/****************************************************************************/
// PROTECTED MEMBERS
/****************************************************************************/
protected:

	gjDouble			m_dSensitivity;
	CComPtr<ISensor>	m_p3DSensor;
	CComPtr<IKeyboard>	m_p3DKeyboard;
	gjInt64				m_iKeyStates;

	gjSVector3Dd		m_vTranslation;
	gjSVector3Dd		m_vAxis;
	gjDouble			m_dAngle;

/****************************************************************************/
// CONSTRUCTION
/****************************************************************************/
public:
	/**
	* Enter brief here...
	*/
	gjSpaceNavigatorWrapper() : gjISpaceNavigatorWrapper()
	{
		m_dSensitivity	= 1.0;
		m_iKeyStates	= 0;
		m_vTranslation.Set(0.0, 0.0, 0.0);
		m_vAxis.Set(0.0, 1.0, 0.0);
		m_dAngle		= 0.0;

		Initialize();
	}


	/****************************************************************************/
	// DESTRUCTION
	/****************************************************************************/
public:
	/**
	* Enter brief here...
	*/
	virtual ~gjSpaceNavigatorWrapper()
	{
		CleanUp();
	}


	/****************************************************************************/
	// PUBLIC METHODES
	/****************************************************************************/
public:
	void CleanUp()
	{
		// destroy
		CComPtr<ISimpleDevice> _3DxDevice;

		// Release the sensor and keyboard interfaces
		if (m_p3DSensor)
		{
			m_p3DSensor->get_Device((IDispatch**)&_3DxDevice);
			m_p3DSensor.Release();
		}

		if (m_p3DKeyboard)
			m_p3DKeyboard.Release();

		if (_3DxDevice)
		{
			// Disconnect it from the driver
			_3DxDevice->Disconnect();
			_3DxDevice.Release();
		}
	}

	void Initialize()
	{
		HRESULT hr=::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED );
		if (!SUCCEEDED(hr))
		{
			gjStringBase strError;
			strError.Format(gjT("CoInitializeEx failed / Error 0x%x"), hr);
			gjDisplayApp::s_pTheApp->LogDisplay(strError);
			return;
		}


		// create device
		CComPtr<IUnknown> _3DxDevice;

		// Create the device object
		gjBool bDeviceFound = gjFALSE;
		hr = _3DxDevice.CoCreateInstance(__uuidof(Device));
		if (SUCCEEDED(hr))
		{
			CComPtr<ISimpleDevice> _3DxSimpleDevice;

			hr = _3DxDevice.QueryInterface(&_3DxSimpleDevice);
			if (SUCCEEDED(hr))
			{
				// Get the interfaces to the sensor and the keyboard;
				m_p3DSensor		= _3DxSimpleDevice->Sensor;
				m_p3DKeyboard	= _3DxSimpleDevice->Keyboard;

				// Connect to the driver
				_3DxSimpleDevice->Connect();

				bDeviceFound = gjTRUE;
			}
		}

		// log
		gjStringBase sMessage;
		if (bDeviceFound)
			sMessage = gjT("SpaceNavigator device found");
		else
			sMessage = gjT("SpaceNavigator device not found");
		gjDisplayApp::s_pTheApp->LogDisplay(sMessage);
	}

	virtual void Update()
	{
		m_vTranslation.Set(0.0f, 0.0f, 0.0f);
		m_dAngle = 0.0;
		m_vAxis.Set(0.0, 1.0, 0.0);

		if (m_p3DKeyboard)
		{
			try 
			{			// Check if any change to the keyboard state
				gjU32 nKeys;
				nKeys = m_p3DKeyboard->Keys;
				for (gjU32 i = 1; i <= nKeys; i++)
				{
					gjInt64 mask = (gjInt64)1<<i>IsKeyDown(i);
					if (isPressed == VARIANT_TRUE)
					{
						if (!(m_iKeyStates & mask))
						{
							m_iKeyStates |= mask;
							ExecuteKeyFunction(i);
						}
					}
					else
					{
						m_iKeyStates &= ~mask;
					}
				}
				// Test the special keys
				for (gjU32 i = 30; i <= 31; i++)
				{
					gjInt64 mask = (gjInt64)1<<i>IsKeyDown(i);
					if (isPressed == VARIANT_TRUE)
					{
						if (!(m_iKeyStates & mask))
						{
							m_iKeyStates |= mask;
							ExecuteKeyFunction(i);
						}
					}
					else
					{ 
						m_iKeyStates &= ~mask;
					}
				}
			}		
			catch (_com_error &e)
			{
				// Some sort of exception handling
			}
		}
		if (m_p3DSensor)
		{
			try 
			{
				CComPtr<IAngleAxis> pRotation	= m_p3DSensor->Rotation;
				CComPtr<IVector3D> pTranslation = m_p3DSensor->Translation;

				// Check if the cap is still displaced
				if (pRotation->Angle > 0. || pTranslation->Length > 0.)
				{
					m_vTranslation.Set(pTranslation->X, pTranslation->Y, pTranslation->Z);
					m_vAxis.Set(pRotation->X, pRotation->Y, pRotation->Z);
					m_dAngle = pRotation->Angle;
				}

				pRotation.Release();
				pTranslation.Release();	
			}
			catch (_com_error &e)
			{
				// Some sort of exception handling
				OutputDebugString(e.Description());
				OutputDebugString(gjT("\n"));

				if (e.Error() == 0x800401f0)
				{
					//CleanUp();
					Initialize();
				}
			}
		}
	}
	virtual gjSVector3Dd& GetTranslation() { return m_vTranslation; }
	virtual void GetRotation(gjDouble& _dAngle, gjSVector3Dd& _vAxis) { _dAngle = m_dAngle; _vAxis.Set(m_vAxis.x, m_vAxis.y, m_vAxis.z); }

protected:

	void ExecuteKeyFunction(gjInt _iKey)
	{
		gjStringBase sMessage;
		sMessage.Format(gjT("SpaceNavigator button %i\n"), _iKey);
		gjDisplayApp::s_pTheApp->LogDisplay(sMessage);
	}
}; 
ngomes
Moderator
Moderator
Posts: 3344
Joined: Mon Nov 27, 2006 7:22 am
Contact:

Post by ngomes »

Hi gjaegy,
in order to use COM, one has to use CComPtr objects (CComPtr<ISensor> and CComPtr<IKeyboard>)
You do not have to use the CComPtr ATL class. This is an helper calls that wraps a pointer to an interface, a "smart pointer".
You have a couple of alternatives:
  1. You can use the smart pointer as defined in the .tlh file (generated from the #import directive). For example, the code

    Code: Select all

    CComPtr<ISimpleDevice> _3DxDevice; 
    would be replaced by

    Code: Select all

    SimpleDevicePtr _3DxDevice;
    .
    See the MSDN doc for more information on the "#import" directive.
  2. Use regular C++ pointers. For example, the line

    Code: Select all

    CComPtr<ISimpleDevice> _3DxDevice;
    would be replaced by

    Code: Select all

    ISimpleDevice* _3DxDevice;
    .
    Note that you need carefully manage the "Release()" of such pointers.
gjaegy
Posts: 9
Joined: Thu Mar 22, 2007 8:32 am

Post by gjaegy »

Hi, thanks a lot for the information, to be honnest I don't have any experience with COM.

Using plain pointers (or _Ptr suffix), how should I handle the instanciation of the objects ? I guess I would have to call CoCreateInstance(), but I have no idea what the parameters should be. The first one should be __uuidof(Device) I think, but what about the other?

Thanks for the help, I really appreciate!
ngomes
Moderator
Moderator
Posts: 3344
Joined: Mon Nov 27, 2006 7:22 am
Contact:

Post by ngomes »

There are several options. Looking at the source for CComPtr<IUnknown>::CoCreateInstance (defined in atlcomcli.h), you could write something like:

Code: Select all

IUnknown* pUnknown = NULL;
HRESULT hr = ::CoCreateInstance(__uuidof(Device), NULL,  CLSCTX_ALL, uuidof(IUnknown), &pUnknown);

if (SUCCEEDED(hr))
{
  ISimpleDevice* pSimpleDevice = NULL;
  hr = pUnknown->QueryInterface(__uuidof(ISimpleDevice), &pSimpleDevice);

  pUnknown->Release();
  pUnknown = NULL;

  if (SUCCEEDED(hr))
  {
    // etc

    pSimpleDevice->Release();
    pSimpleDevice = NULL;
  }
}
Using smart pointers as automatically generated by the #import directive should be even simpler:

Code: Select all

IUnknownPtr pUnknown;
HRESULT hr = pUnknown->CreateInstance(__uuidof(Device));
if (SUCCEEDED(hr))
{
  ISimpleDevicePtr pSimpleDevice = pUnknown; // This will call QueryInterface()
 
  if (pSimpleDevice != NULL)
  {
    // etc
  }
}
It is possible to write the above code in less lines of code but I think this is more clear.

Note: I wrote the above without actually trying to compile it. There may a few "spelling" mistakes.
gjaegy
Posts: 9
Joined: Thu Mar 22, 2007 8:32 am

Post by gjaegy »

Hi,

thanks again for the quick response!

I resolved my issue using the following code. I hope this may help other people as well:

Code: Select all

	ISimpleDevicePtr	m_pDevice;
	ISensorPtr			m_p3DSensor;
	IKeyboardPtr		m_p3DKeyboard;

		// create instance
		gjBool bDeviceFound = gjFALSE;
		hr = CoCreateInstance(__uuidof(Device), NULL, CLSCTX_ALL, __uuidof(ISimpleDevice), (void**)&m_pDevice);
		if (SUCCEEDED(hr))
		{
			m_p3DSensor		= m_pDevice->Sensor;
			m_p3DKeyboard	= m_pDevice->Keyboard;

			// Connect to the driver
			hr = m_pDevice->Connect();
			if (SUCCEEDED(hr))
				bDeviceFound = gjTRUE;
			else
				m_pDevice = gjNULL;
		}

There is no need to call release, simply call "m_pDevice->Disconnect();" and set all the pointers to NULL.
ngomes
Moderator
Moderator
Posts: 3344
Joined: Mon Nov 27, 2006 7:22 am
Contact:

Post by ngomes »

gjaegy wrote:There is no need to call release, simply call "m_pDevice->Disconnect();" and set all the pointers to NULL.
Ah, the eternal argument. In the end, it is a matter of style. For some, calling Release() makes the code more readable and easier to maintain.

Instead of:

Code: Select all

hr = CoCreateInstance(__uuidof(Device), NULL, CLSCTX_ALL, __uuidof(ISimpleDevice), (void**)&m_pDevice); 
It would be easier to write:

Code: Select all

hr = m_pDevice.CreateInstance(__uuidof(Device));
Last edited by ngomes on Wed Mar 28, 2007 8:42 am, edited 1 time in total.
gjaegy
Posts: 9
Joined: Thu Mar 22, 2007 8:32 am

Post by gjaegy »

Concerning your second remark, you are absolutely right, I have changed my code, thanks !

Concerning the first one, I got confused because in my first attempt I used plain pointers, called ->Release() and got a crash when the TDxInput.dll dll was unloaded. But with _Ptr objects you are right, it doesn't matter.

Thanks!
ngomes
Moderator
Moderator
Posts: 3344
Joined: Mon Nov 27, 2006 7:22 am
Contact:

Post by ngomes »

gjaegy wrote:I used plain pointers, called ->Release() and got a crash when the TDxInput.dll dll was unloaded.
Hmmm. When using plain pointers, Release() must be called. My earlier remark about style was in reference to calling Release() on smart pointers.
rodrigo.seabra
Posts: 44
Joined: Fri Apr 04, 2008 7:03 am

Post by rodrigo.seabra »

I have a doubt. In my application, I connected the SpaceNavigator, but I don't understand the reason of instruction "if (m_p3DSensor)" of the method Update ().

In my code, even if it does not happen any interaction with the device, the application enters this "if" and prints the values 0,0,0. I need to get the values of translation and rotation only when they actually occur.

I already have a main loop that controls the application. In this case, how I could use these methods in parallel without blocking the implementation of the application?

Thanks.
Post Reply