Solved

Using an ActiveX component for connecting to an electronic scale

Posted on 2007-11-23
11
965 Views
Last Modified: 2009-03-16
I have been trying to connect my application to an electronic scale.  The scale is compatible with the WeighTronix 7010 family.  WeighTronix has developed an ActiveX component for working with this scale, which you can read about here: http://www.salterbrecknell.com/pdfs/WTCommSclV2.pdf.  I find the documentation on it very poor and admittedly I just am not sure where to even start with working with an ActiveX component.  So far I have imported the ActiveX component into an MFC enabled static library I use in my application.  I can create a member of the main class, which I have called WTCommScl in my imported classes.  Here is the code I have so far trying to use the class:

WTCommScl wtCommScl;

short scalePort,
  scaleStatus,
  scaleOpen;

try{
      wtCommScl.CreateDispatch("WTCommScl_OCX.WTCommScl");
      for(i=1, bReturn=false; i<=16; i++){
            wtCommScl.put_CommPort(i);
            scalePort = wtCommScl.get_CommPort();
            scaleOpen = TRUE;
            try{
                  wtCommScl.ScaleOpen(&scaleOpen);
                  scaleStatus = wtCommScl.get_ConnectStatus();
                  if(scaleStatus==wtSCALE_ONLINE){
                        strError.Format("Connected to scale on COM%d", i);
                        AfxMessageBox(strError, MB_OK, NULL);
                        bReturn=true;
                        break;
                  }
            }catch(...){
            }
      }
      if(!bReturn){
            strError="Scale not found on COM1-COM16";
            throw strError;
      }
      strError = wtCommScl.get_FormattedWt();
      AfxMessageBox(strError, MB_OK, NULL);
      scaleOpen = FALSE;
      wtCommScl.ScaleOpen(&scaleOpen);
}catch(COleDispatchException *e){
      TCHAR szCause[255];
      CString strFormatted;
      e->GetErrorMessage(szCause, 255);
      strFormatted += szCause;
      AfxMessageBox(strFormatted);
      return false;
}

My device manager tells me the scale is connected on Comm2.  When it tries to run the wtCommScl.ScaleOpen(&scaleOpen); line it throws an exception:

First-chance exception at 0x7c812a5b in MyApp.exe: 0xC000008F: Floating-point inexact result.
First-chance exception at 0x7c812a5b in MyApp.exe: Microsoft C++ exception: COleDispatchException @ 0x0012c158.

So - it seems to not be working right somehow.

The other thing I tried was by creating a CWTCommSclWnd object and calling the Create() function.  I'm not sure what this Wnd class is for, but I thought I would give it a try:

CWTCommSclWnd wtCommSclWnd;
CRect sclWndRect;

sclWndRect.left = 0;
sclWndRect.right = 1000;
sclWndRect.top = 0;
sclWndRect.bottom = 500;
wtCommSclWnd.Create("Testing", WS_POPUP | WS_CAPTION, sclWndRect, pParent, IDOK);

This creates the following:
CoCreateInstance of OLE control {C21F0A43-C3EB-11D1-927C-0020781072B4} failed.
>>> Result code: 0x80040154
>>> Is the control is properly registered?

I know this is a pretty niche thing, but can anyone help me with this?
0
Comment
Question by:david_johns
  • 6
  • 5
11 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 20339644
>>So - it seems to not be working right somehow.

No, it is working just fine. 'First-chance exception in xxx...' just means that a function from within the 'xxx' caused an exception that was handled successfully inside the SEH frame that was active when the exception occurred. You can think of it being the same as if you use code like this:

long l;

__try // set up current SEH frame
{
CopyMemory ( &l, 0, sizeof ( long)); // read from 0x00000000
}
__except( EXCEPTION_EXECUTE_HANDLER) // handler for current frame
{
puts ( "We knew that this would go wrong...");
}

(Additional info: MS KB Article Q105675)

The article can be found at http://support.microsoft.com/support/kb/articles/q105/6/75.asp

A first chance exception is called so as it is passed to a debugger before the application 'sees' it. This is done by sending a 'EXCEPTION_DEBUG_EVENT' to the debugger, which can now decide whether it is passed to the apllication to handle it or 'ignore' it (e.g. like an 'EXCEPTION_BREAKPOINT' aka 'int 3'). If the exception isn't handled, it becomes a '2nd chance' exception, the debugger 'sees' it the 2nd time and will usually terminate the program (without using a debugger, these exceptions end up at 'UnhandledExceptionFilter()' which will also signal the exception to the user with one of these 'nice' message boxes and terminate the program, also...)


In short: This message is only generated by a debugger & you can safely ignore it...
0
 
LVL 86

Expert Comment

by:jkr
ID: 20339655
Sorry, updated MSDN link: http://support.microsoft.com/kb/q105675/ ("First and second chance exception handling")
0
 

Author Comment

by:david_johns
ID: 20341547
jkr,

I know that in some cases this is no cause for alarm, but in this case I beleive it is.  It doesn't finish the OpenScale() function.  It breaks out of it and my try...catch() block catches it.  Its not just a handle exception.

Thanks,
David
0
 
LVL 86

Expert Comment

by:jkr
ID: 20341747
Then the reason lies elswhere - mind posting the code?
0
 

Author Comment

by:david_johns
ID: 20342515
jkr,

I have attached the header file and the source file for the library that attempts to connect to the scale.  The problem is occurring in the ElectronicScale::Connect() function.  In this case I am entering the function with scaleType=ESCALE_WT7010.  The pParent is left NULL since that is just something I was playing with trying to get the CWTCommSclWnd approach to work.

Let me know if you need the ActiveX imported classes as well.  I just imported them from the OCX file and only changed perhaps the names of the classes in doing so.

Thanks,
David
// Devices.h: declaration of hardware integration functions.

//

//////////////////////////////////////////////////////////////////////

//Copyright (c) 2004 all rights reserved by David Johns and Michael Griggs

//////////////////////////////////////////////////////////////////////
 

#if !defined(AFX_DEVICES_H__SJD56735_DKD928_4123_AJSD92384JSKD__INCLUDED_)

#define AFX_DEVICES_H__SJD56735_DKD928_4123_AJSD92384JSKD__INCLUDED_
 

//Trick to have VisualStudio.Net recognize as an MFC project

#if !(!defined(_DEBUG) || defined(_DEBUG))

class CBogusApp : public CWinApp { }; CBogusApp theBogusApp;

#endif
 

#ifndef WINVER

#ifdef _WIN32_WINNT

#define WINVER _WIN32_WINNT

#else

#pragma message("Devices Library - WINVER not defined. Defaulting to 0x0400 (Windows 95)")

#define WINVER 0x0400

#endif

#else

#if WINVER < 0x0400

#error Devices Library requires WINVER to be #defined to 0x0400 or greater

#endif

#endif
 

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000
 

#include "afxwin.h" //Defines CString and Windows.h headers

#include "..\Devices\WTCommScl.h"
 

#define ESCALE_TYPE_RADIOSHACK26_950	1

#define ESCALE_WT7010					2

#define ESCALE_TYPE_NEW					3
 

extern "C" { 

// Declare the C libraries that are used for the USB HID device APIs 

#include "hidsdi.h" 

#include <setupapi.h> 

} 

#pragma comment(lib, "hid.lib")

#pragma comment(lib, "setupapi.lib") 
 

class ElectronicScale

{

public:

	HANDLE hDevice;

	HANDLE hWrite;

	HANDLE hRead;

	HANDLE hEvent;

	HIDP_CAPS hidCapabilities;

	OVERLAPPED hidOverlapped;
 

	unsigned long tare;

	double conversionToGrams;

	CString text;
 
 

	ElectronicScale();

	virtual ~ElectronicScale();
 

	bool Connect(int scaleType, CWnd *pParent=NULL);

	unsigned long Weight();

	bool Weight(double &weight);

	void Tare();

};
 

#endif //AFX_DEVICES_H__SJD56735_DKD928_4123_AJSD92384JSKD__INCLUDED_
 
 
 

// ElectronicScale.cpp: implementation electronic scale integration functions

//

//////////////////////////////////////////////////////////////////////

//Copyright (c) 2004 all rights reserved by David Johns and Michael Griggs

//////////////////////////////////////////////////////////////////////

#include "Devices.h"
 
 

ElectronicScale::ElectronicScale()

{

	hDevice=NULL;

	hWrite=NULL;

	hRead=NULL;

	hEvent=NULL;
 

	tare = 0;

}
 

ElectronicScale::~ElectronicScale()

{

	if(hDevice) CloseHandle(hDevice);

	if(hWrite) CloseHandle(hWrite);

	if(hRead) CloseHandle(hRead);

	if(hEvent) CloseHandle(hEvent);

}
 
 

bool ElectronicScale::Connect(int scaleType, CWnd *pParent/*=NULL*/)

{

	GUID hidGUID;
 

	HDEVINFO hDevInfo=NULL;

	

	int	i,

		nIndex = 0,

		vendorID,

		productID;
 

	SP_DEVICE_INTERFACE_DATA devInfoData;
 

	ULONG length,

		  required;
 

	PSP_DEVICE_INTERFACE_DETAIL_DATA detailData;

	

	HIDD_ATTRIBUTES	hidAttributes;

	

	PHIDP_PREPARSED_DATA hidPreparsedData;
 

	DWORD dwUsage;
 

	CString strUsage,

			strError,

			strDevices;
 

	bool bReturn=true;
 

	WTCommScl wtCommScl;

	short scalePort,

		  scaleStatus,

		  scaleOpen;

/*

	CWTCommSclWnd wtCommSclWnd;

	CRect sclWndRect;

*/
 
 

	try{

		switch(scaleType){

			case 0: return false;

			case ESCALE_TYPE_RADIOSHACK26_950:

				vendorID = 0x2233;

				productID = 0x6323;

				conversionToGrams = 0.3718592964;

				break;

			case ESCALE_WT7010:

				/*

				sclWndRect.left = 0;

				sclWndRect.right = 1000;

				sclWndRect.top = 0;

				sclWndRect.bottom = 500;

				wtCommSclWnd.Create("Testing", WS_POPUP | WS_CAPTION, sclWndRect, pParent, IDOK);

				*/

				try{

					wtCommScl.CreateDispatch("WTCommScl_OCX.WTCommScl");

					for(i=1, bReturn=false; i<=16; i++){

						wtCommScl.put_CommPort(i);

						scalePort = wtCommScl.get_CommPort();

						scaleOpen = TRUE;

						try{

							wtCommScl.ScaleOpen(&scaleOpen);

							scaleStatus = wtCommScl.get_ConnectStatus();

							if(scaleStatus==wtSCALE_ONLINE){

								strError.Format("Connected to scale on COM%d", i);

								AfxMessageBox(strError, MB_OK, NULL);

								bReturn=true;

								break;

							}

						}catch(...){

						}

					}

					if(!bReturn){

						strError="Scale not found on COM1-COM16";

						throw strError;

					}

					strError = wtCommScl.get_FormattedWt();

					AfxMessageBox(strError, MB_OK, NULL);

					scaleOpen = FALSE;

					wtCommScl.ScaleOpen(&scaleOpen);

				}catch(COleDispatchException *e){

					TCHAR szCause[255];

					CString strFormatted;
 

					e->GetErrorMessage(szCause, 255);

					strFormatted += szCause;

					AfxMessageBox(strFormatted);

					return false;

				}

				return true;
 

			case ESCALE_TYPE_NEW:

                vendorID = 0x10C4;

				productID = 0xEA60;

				conversionToGrams = 1.0;	//<---Update

				break;

			default:

				strError = "An invalid scale type was requested.";

				throw strError;

		}
 

		//Get the GUID for all system HIDs.

		HidD_GetHidGuid(&hidGUID);	

		

		//Get a handle to a device information set for all installed devices.

		hDevInfo=SetupDiGetClassDevs(&hidGUID, NULL, NULL, DIGCF_PRESENT|DIGCF_INTERFACEDEVICE);

		devInfoData.cbSize = sizeof(devInfoData);
 

		//Step through the available devices looking for the one we want. 

		do{

			if(!SetupDiEnumDeviceInterfaces(hDevInfo, 0, &hidGUID, nIndex, &devInfoData)) break;		//No more devices
 

			//A device has been detected, so get more information about it.

			//Run twice - first time to get the length and second time to get the data

			SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInfoData, NULL, 0, &length, NULL);
 

			//Allocate memory for the hDevInfo structure, using the returned Length.

			detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(length);

			

			//Set cbSize in the detailData structure.

			detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
 

			//Call the function again, this time passing it the returned buffer size.

			if(!SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInfoData, detailData, 

												length, &required, NULL)) continue;
 

			// Open a handle to the device.

			hDevice = CreateFile(detailData->DevicePath, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, 

								(LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING, 0, NULL);
 

			hidAttributes.Size = sizeof(hidAttributes);

			if(!HidD_GetAttributes(hDevice, &hidAttributes)) continue;
 

			//Get the device's capablities.

			HidD_GetPreparsedData(hDevice, &hidPreparsedData);

			HidP_GetCaps(hidPreparsedData, &hidCapabilities);
 

			// Find out if the device is a system mouse or keyboard.

			dwUsage = (hidCapabilities.UsagePage * 256) + hidCapabilities.Usage;
 

			strUsage.Format("\n\tVendorID: %x, ProductID: %x, Usage: %x", hidAttributes.VendorID, hidAttributes.ProductID, dwUsage);

			strDevices+=strUsage;

			

			if(hidAttributes.VendorID == vendorID){

				if(hidAttributes.ProductID == productID){

					//Both the Vendor ID and Product ID match.

					//MyDevicePathName = detailData->DevicePath;

					if(dwUsage == 0x102){

						strUsage = "mouse";

					}else if(dwUsage == 0x106){

						strUsage = "keyboard";

					}
 

					if((dwUsage == 0x102) || (dwUsage == 0x106)){

						strError.Format("Error - attempted to connect to a system %s device.\n"

									"Windows 2000 and Windows XP don't allow applications to directly connect to these devices.", strUsage);

						throw strError;

					}
 

					// Get a handles for reading and writing reports

					hWrite=CreateFile(detailData->DevicePath, GENERIC_WRITE, 

						                FILE_SHARE_READ|FILE_SHARE_WRITE, 

                                        (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0,

										NULL);
 

					hRead=CreateFile(detailData->DevicePath, GENERIC_READ,

									 FILE_SHARE_READ|FILE_SHARE_WRITE, 

									 (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,

									 FILE_FLAG_OVERLAPPED, NULL);

					if(hRead==INVALID_HANDLE_VALUE) continue;
 

					hEvent = CreateEvent(NULL, TRUE, TRUE, "");
 

					//Set the members of the overlapped structure.

					hidOverlapped.hEvent = hEvent;

					hidOverlapped.Offset = 0;

					hidOverlapped.OffsetHigh = 0;

				}else CloseHandle(hDevice);

			}else CloseHandle(hDevice);
 

			//Free the memory used by the detailData structure

			free(detailData);
 

			nIndex++;

		}while(!hRead);
 

		if(!hRead){

			if(nIndex>0) strError.Format("Requested scale not found.  Found the following human interfaces devices:%s", strDevices);

			else strError = "Requested scale not found.  No human interface devices detected.";

			throw strError;

		}
 

		Tare();

		if(tare==0){

			strError = text;

			throw strError;

		}

	}catch(...){

		if(strError.IsEmpty()) text = "An unknown error has occurred.";

		else text = strError;
 

		bReturn = false;

	}
 

	//Free the memory reserved for hDevInfo by SetupDiClassDevs.

	SetupDiDestroyDeviceInfoList(hDevInfo);
 

	return bReturn;

}
 
 

unsigned long ElectronicScale::Weight()

{

	DWORD dwBytes,

		  dwResult;
 

	char report[256];
 

	int i;
 

	CString strError,

			character,

		    strWeight;
 

	unsigned long weight=0;

	
 

	try{

		for(i=0; i<hidCapabilities.OutputReportByteLength; i++) report[0]=0;

		if(!WriteFile(hWrite, report, hidCapabilities.OutputReportByteLength,

			          &dwBytes, NULL)){

			strError = "Unable to access scale.";

			throw strError;

		}
 

		ReadFile(hRead, report, hidCapabilities.InputReportByteLength, &dwBytes,

				(LPOVERLAPPED) &hidOverlapped); 

		

		dwResult = WaitForSingleObject(hEvent, 6000);

		switch(dwResult){

			case WAIT_OBJECT_0:

				for(i=1; i<hidCapabilities.OutputReportByteLength; i++){

                    character.Format("%02X", report[i]);

					strWeight+=character.Right(2);

				}

				if(sscanf(strWeight, "%x", &weight)<1){

					strError = "Unable to interpret scale response";

					throw strError;

				}

				break;
 

			case WAIT_TIMEOUT:

				strError = "Timed out while waiting for scale to respond.";

				throw strError;

			

			default:

				strError = "Undefined error while waiting for scale to respond.";

				throw strError;

		}

	}catch(...){

		CancelIo(hRead);

		if(strError.IsEmpty()) text = "An unknown error has occurred.";

		else text = strError;

	}
 

	return weight;

}
 

bool ElectronicScale::Weight(double &weight)

{

	unsigned long ulWeight;
 

	

	ulWeight = Weight();

	if(ulWeight==0) return false;
 

	weight = ((double) ulWeight - (double) tare) * conversionToGrams;

	return true;

}
 

void ElectronicScale::Tare()

{

	tare = Weight();

}

Open in new window

0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 86

Expert Comment

by:jkr
ID: 20344856
I hate to ask this, but which handler in the above catches the exception?
0
 

Author Comment

by:david_johns
ID: 20347854
The one on line 173-174
0
 
LVL 86

Expert Comment

by:jkr
ID: 20348167
>>}catch(...){
>>}

So, that's the "catch all" handler... What details do you have for that excepton?

0
 
LVL 86

Expert Comment

by:jkr
ID: 20350883
Err, to get those details, debug the app and check the debug output. Also, set the debugger to break when exceptions are thrown.
0
 

Author Comment

by:david_johns
ID: 20363504
Those are what I showed in my original post - it actually throws two exceptions at the same time - or at least two show up at the same time:

First-chance exception at 0x7c812a5b in SuperManager.exe: 0xC000008F: Floating-point inexact result.
First-chance exception at 0x7c812a5b in SuperManager.exe: Microsoft C++ exception: COleDispatchException @ 0x0012c158.

I figured your next question would be what is in the COleDispatchException.  It reports "Client Site not available."

Thanks,
David
0
 

Accepted Solution

by:
david_johns earned 0 total points
ID: 23864413
Since I couldn't get this ActiveX to work I ended up using CreateFile to interact with the scale using standard serial communication.
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now