Introduction
This is my second project to learn C/C++ by programming previously performed MASM projects. The first was An Idea How to Use RichEdit50W for Syntax Highlighting, etc. For those interested, you can read the ASM code on my website (minor28.divdev.se). The two project's files, functions and variables are on the whole consistent so it would be quite easy to follow.
Application
This application is a main dialog application.
::DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_MAIN),0,MainDlgProc,0);
The first start of the application is done with the default values for position (Stockholm Central Station), Google maps hybrid
, Zoom 4
.
The application is controlled with the buttons in the upper toolbar and with popup menus. The clock is set by either clicking on the clock face gradation or with the Set time
menu item in a popup menu.
The buttons functionality are:
- Set Today resets time and date to the current value.
- Open Solar Form opens a dialog with the solar for the selected position and selected date and time. The solar elevation and azimuth at current time is visualized in two separate dialog boxes.
- Show Azimuth visualizes the solar location on the map image.
- Save as home and Save Map data saves map data to the registry.
- Go home restores the marker to home position.
- Street View toggles between Map View and Street View.
- - Zoom + controls the zooming value of the map.
- Open route planner enables planning of routes by clicking on the map to mark waypoints. Distance and course are calculated and are presented in a table that appears over the clock. Editing the route is done using a popup menu.
- Connect GPS searches for a serial port where the GPS is connected. If a GPS is found, illuminates the red traffic light to the right of the button. The yellow and green traffic light illuminates when a fix is found and the DOP is greater than five. The yellow light goes out when DOP is less than five.
The application has three modes, Time, Route and GPS mode.
Some application data are stored in the registry (HKEY_CURRENT_USER\Software\MINOR28\Solar).
Files:
- Solar.cpp
- ApplicationData.cpp
Time Zone Data
In time mode, a left button click on the map will move the red marker to the new position and following data are retrieved from googleapis.com
:
- daylight saving time
- GMT time offset
- time zone id
- time zone name
These are the main code lines:
::CLSIDFromProgID(L"Msxml2.XMLHTTP.6.0",&clsid)
::CoCreateInstance(clsid,0,CLSCTX_INPROC_SERVER,IID_IUnknown,(LPVOID*)&pXMLHTTPrequest)
pXMLHTTPrequest->open(L"GET",(BSTR)&bufferW,var,var0,var0)
pXMLHTTPrequest->send(var)
pXMLHTTPrequest->get_responseText(&pbstrBody)
bufferW
contains the URL to googleapis.com and pbstrBody
contains the time zone data.
Files:
Time and Date
The clock face is drawn and a new thread is started to run the clock.
hClock = ::CreateWindowEx(0,"static",0,SS_BITMAP + WS_CHILD + WS_VISIBLE,
15,150,220,220,hWin,(HMENU)IDC_PIC1,hInst,0);
SetClockToday();
DWORD ThreadId;
::CloseHandle(::CreateThread(0,0,UpdateClock,0,0,&ThreadId));
DWORD WINAPI UpdateClock(LPVOID Param)
{
if (ClockTimer != 0){
::KillTimer(hMainDlg,ClockTimer);
}
ClockTimer = ::SetTimer(hMainDlg,ID_TIMER,1000,(TIMERPROC)&Clock);
return 0;
}
VOID CALLBACK Clock(HWND hWin,UINT uMsg,UINT idEvent,DWORD dwTime)
{
if (uMsg == WM_TIMER){
Adjustment(&UTCDate);
Adjustment(&LocalDate);
DrawClockHandles();
}
return;
}
Startup time is retrieved from:
::GetSystemTime(&time);
I have found that in comparison to the pc clock the application clock is 1 to 2 seconds after.
Files:
- Clock.cpp
- DrawTheClock.cpp
Web Browser
The web browser is the MS WebBrowser Control
(Internet Explorer). Google maps suddenly stopped working with Internet Explorer and Firefox. It worked only with Chrome. The problem was solved by using a Java version no higher than 3.19 (http://maps.google.com/maps/api/js?v=3.19).
The webbrowser window is a static control with a custom class.
CreateWebClass();
::CreateWindowEx(0,"WebClass",0,WS_CHILD + WS_BORDER + WS_VISIBLE,
0,0,0,0,hWin,(HMENU)IDC_WEB,hInst,0);
This is part of the WebClass
window process.
if (message == WM_CREATE){
hWeb = hWin;
CreateWebBrowser(hWin);
::EnumChildWindows(hWin,&EnumChildProc,0);
}
These messages handle GPS plotting:
else if (message == WM_STARTPLOTT)
else if (message == WM_PLOTT)
else if (message == WM_ENDPLOTT)
This message handles route planning:
else if (message == WM_COMMAND)
This is the webbrowser control:
VOID CreateWebBrowser(HWND hWin)
{
if (::CLSIDFromString(L"{8856F961-340A-11D0-A96B-00C04FD705A2}",&clsid) != S_OK
if (::CoCreateInstance(clsid,0,CLSCTX_INPROC_SERVER,IID_IUnknown,
(LPVOID*)&pWebBrowser) != S_OK
pWebBrowser->QueryInterface(IID_IWebBrowser2,(LPVOID*)&pWebBrowser2);
if (pWebBrowser2->QueryInterface(IID_IOleObject,(LPVOID*)&pOleObject) != S_OK
if (pOleObject->SetClientSite((IOleClientSite*)&ClientSite.pOleClientSite) != S_OK
To open Google Maps, you first navigate to a blank page to create an empty document.
VARIANT var;
var.vt = VT_BSTR;
var.bstrVal = ::SysAllocString(L"about:blank");
pWebBrowser2->Navigate2(&var,0,0,0,0);
::SysFreeString(var.bstrVal);
Then stream the html-code to the browser document.
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE + GMEM_NODISCARD,0);
if (hMem){
if (CreateHTMLCode(&hMem)){
IStream* pStream;
if (::CreateStreamOnHGlobal(hMem,TRUE,&pStream) == S_OK){
if (pPersistStreamInit->Load(pStream) == S_OK){
AdviseEvent(pWebBrowser2,DIID_DWebBrowserEvents2);
pPersistStreamInit->Release();
}
pStream->Release();
}
}
::GlobalFree(hMem);
}
The ClientSite
was tricky. This is my definition of the clientsite
:
typedef struct tagCLIENTSITE
{
_IDispatch* pDispatch;
_IDispatch Dispatch;
_IOleClientSite* pOleClientSite;
_IOleClientSite OleClientSite;
_IOleInPlaceSite* pOleInPlaceSite;
_IOleInPlaceSite OleInPlaceSite;
_IDocHostUIHandler* pDocHostUIHandler;
_IDocHostUIHandler DocHostUIHandler;
LONG ref;
}CLIENTSITE, *LPCLIENTSITE;
All communication between the application and Google Maps is done with the ExecuteScript
function.
VOID ExecuteScript(LPSTR pszScript,LPSTR pszRetVal)
{
IHTMLDocument *pHtmlDocument;
if (pWebBrowser2->get_Document((IDispatch**)&pHtmlDocument) == S_OK){
DispHTMLDocument* pDispHTMLDocument;
if (pHtmlDocument->QueryInterface(DIID_DispHTMLDocument,
(VOID**)&pDispHTMLDocument) == S_OK){
pDispHTMLDocument->AddRef();
OLECHAR* member = L"parentWindow";
LCID lcid = ::GetThreadLocale();
DISPID memid;
if (pDispHTMLDocument->GetIDsOfNames(IID_NULL,&member,1,lcid,&memid) == S_OK){
DISPPARAMS dp;
::memset(&dp,0,sizeof(dp));
VARIANT Window;
if (pDispHTMLDocument->Invoke(memid,IID_NULL,lcid,
DISPATCH_PROPERTYGET,&dp,&Window,0,0) == S_OK){
IHTMLWindow2* pHtmlWindow2 = (IHTMLWindow2*)Window.pdispVal;
WCHAR buffer[256];
::MultiByteToWideChar(CP_ACP,0,pszScript,-1,
(LPWSTR)&buffer,sizeof(buffer));
VARIANT RetVal;
member = L"execScript";
if (pHtmlWindow2->GetIDsOfNames(IID_NULL,&member,1,lcid,&memid) == S_OK){
dp.cArgs = 2;
dp.rgvarg = (VARIANTARG*)::malloc(512);
dp.rgvarg[0].vt = VT_BSTR;
dp.rgvarg[0].bstrVal = ::SysAllocString(L"JavaScript");
dp.rgvarg[1].vt = VT_BSTR;
dp.rgvarg[1].bstrVal =
::SysAllocString((const OLECHAR*)&buffer);
pHtmlWindow2->Invoke(memid,IID_NULL,lcid,
DISPATCH_METHOD,&dp,&RetVal,0,0);
::SysFreeString(dp.rgvarg[0].bstrVal);
::SysFreeString(dp.rgvarg[1].bstrVal);
::free(dp.rgvarg);
}
pHtmlWindow2->Release();
}
}
pDispHTMLDocument->Release();
if (pszRetVal){
RetrieveInputValue(pszRetVal);
}
}
pHtmlDocument->Release();
}
return;
}
Files:
- ClientSiteMethods.cpp
- WebBrowser.cpp
- WebWindow.cpp
Solar Calculations
I'm not an astronomer so the calculation is based on Internet searches and my interpretations to write the calculations in the assembly language. Now I have translated them into C/C++ code. The data are mainly derived from NOAA's website.
Disclaimer:
The results of the calculations are the basis for the images of solar curve, solar altitude and solar azimuth.
Files:
- DrawAzimuth.cpp
- DrawDiagram.cpp
- SolarAzimuthDlg.cpp
- SolarCalculations.cpp
- SolarDlg.cpp
- SolarElevationDlg.cpp
Route Planner
You can create a route and edit it, but not save it. Distances in meter.
VOID CalcCourseDistance(UINT curMarkerIndex)
{
UINT row = curMarkerIndex + 1;
double k = DegToRad;
double dLat = CurDistMarker.Lat - PrevDistMarker.Lat;
double dLong = CurDistMarker.Lng - PrevDistMarker.Lng;
double A = pow(sin(dLat/2 *k),2) + cos(CurDistMarker.Lat * k)
* cos(PrevDistMarker.Lat * k) * pow(sin(dLong / 2 * k),2);
double C = 2 * atan2(sqrt(A),sqrt(1 - A));
double Dist = C * 6371000;
CHAR buf[128];
::wsprintf((LPSTR)&buf,"%d",(int)Dist);
ThousandsSeparator((LPSTR)&buf);
putTextMatrix(hLSV1,row,2,(LPSTR)&buf);
A = atan2(dLong / dLat, 1) * RadToDeg;
if (dLat < 0){
A += 180;
}
else if (dLong < 0){
A += 360;
}
::sprintf_s((LPSTR)&buf,sizeof(buf),"%0.1f",A);
putTextMatrix(hLSV1,row,1,(LPSTR)&buf);
return;
}
GPS
The program presupposes a GPS with baud rate 4800 and bytesize 8.
::memset(&dcb,0,sizeof(dcb));
dcb.DCBlength = sizeof(dcb);
dcb.BaudRate = 4800;
dcb.ByteSize = 8;
The Connect GPS
button will start a search in all serial ports for a connected GPS. If a GPS is found, this com port is saved to registry and the red trafficlight is illuminated.
A separate thread is created to handle the data from the GPS. There is a menu item to enable monitoring of NMEA sentences. For plotting actions, two sentences are parsed. Recommended minimum GPS data ($GPRMC)
and Overall satellite data ($GPGSA)
.
$GPRMC
sentence is used to retrieve:
- data status A (active OK) or V (void),
- latitude in format 5925.5148,
- north (N) or south (S) hemisphere,
- longitude in format 01820.5842 and
- west (W) or east (E) of prime meridian.
$GPGSA
sentence is used to retrieve mode and dilution of precision (DOP)
.
Mode values:
- no fix and red trafficlight,
- 2D fix and yellow and green trafficlight and
- 3D fix and just the green trafficlight.
Files:
Assembly vs C/C++
The big difference is the Automation coding. Much easier in C++. To facilitate automation coding in assembly, I have developed a tool (Automation Tool) with a static library (Automation static library).
In my last article, I wrote that calculations were much easier to carry out in C++. Now I have revised that opinion. The biggest difference is that C++ generates fewer but longer lines of text and MASM many but shorter lines of text.
Concluding Remarks
You may take it as it is. The application is probably not free from bugs. I think I have structured files and functions so it should be easy to adapt the code to your own needs.