A while ago, we were asked to create a small Desktop application that will go over photos and extract any Meta Data from them, including the location where each photo was taken (which is a topic for a separate article). To display the result, I looked for a way to display a map, powered by Google Maps, in an MFC application. Here is the solution I have found.
About Google Maps API
According to the Google Maps API "Getting Started" page, "Google Maps APIs are categorized by platform: Web, Android and iOS. These native platform APIs are complemented by our suite of HTTP web services.". As usual, especially these days, you almost never found API code samples for Windows Desktop application, and especially C++ ones.
Hosting a Web Browser in an MFC Application
In order to implement my solution, you would need to host a Web Browser in your MFC application:
- Choose an application type.
Open Visual C++ and follow these steps:
- Choose New from the File menu.
- Select MFC AppWizard (EXE).
- Enter the project name and select a location.
- Click OK.
- When the dialog box for Step 1 appears, select the application type that is appropriate for your application—single document, multiple document, or dialog-based. Select "Dialog-based" for this example.
- Click Next.
- Select application features and support.
The dialog box for Step 2 asks you to select any features and support your application will need—an About box or automation support, for example. The WebBrowser control is an ActiveX control, so select ActiveX Controls. Click Next to go to the next step.
- Select the project style.
The dialog box for Step 3 is used to define your project. You have only one project style option, Standard MFC. But you can indicate whether you want the IDE to generate comments in the source code, and you can specify how you want to use the MFC library. The default selections are appropriate for most applications. Click Next to go to the next step.
- Name the files and classes.
The dialog box for Step 4 displays the names of all the files and classes that Visual C++ created. You can change these to more descriptive names or to names required by your specification. Click Finish.
- Add a WebBrowser control.
You now have a skeleton application. Because this example uses a dialog-based application, a dialog box with OK and CANCEL buttons appears in the Dialog Editor. Follow these steps to add an ActiveX control to the dialog box.
- Right-click the Dialog Editor.
- Select Insert ActiveX Control from the menu.
- Select Microsoft Web Browser.
- Click OK.
- Position and size the WebBrowser control in the Dialog Editor.
- Delete the default OK and CANCEL buttons if your application does not require them.
- Add a
WebBrowser
class and a member variable.
When you insert a WebBrowser control, an identifier for the control is automatically assigned, but you must provide a member variable to access the control. To add a variable:
- Right-click the WebBrowser control.
- Select ClassWizard.
- Click the Member Variables tab to display the control identifiers.
- Select IDC_EXPLORER1.
- Click Add Variable, and the following dialog box appears:
- Click OK to display a Confirm Classes dialog box.
- Click OK again to add a
CWebBrowser2
class to your project. - Enter a name for the control variable.
You now have an application containing a browser. However, if you compile the code generated by Visual C++ and run the executable file, a browser does not appear.
Obtain Your Google Maps API
Please read the instructions to obtain your API KEY which will be needed for this code.
The Solution
First define a global variable:
CExplorer1 m_Browser;
After conducting some research, I have found out that the most efficient way to interact with Google Maps would be via a WebBrowser
control, and the best way of doing so, is to maintain a temporary HTML file which will then be opened by the WebBrowser
control.
We will be using this HTML file during runtime, so we assign its path using the following code:
wchar_t FileName[2048];
GetCurrentDirectory(2048, FileName);
wcscat(FileName, L"\\test.html");
The first building block would be a function for generating (or updating) this file.
Method 1
void WriteHTML(const wchar_t* html)
{
IDispatch* pHtmlDoc = m_Browser.get_Document();
if (!pHtmlDoc)
return;
CComPtr<IHTMLDocument2> doc1 = NULL;
doc1.Detach();
doc1.Attach((IHTMLDocument2*)pHtmlDoc);
if (!doc1)
return;
SAFEARRAY* psaStrings = SafeArrayCreateVector(VT_VARIANT, 0, 1);
if (!psaStrings)
return;
BSTR bstr = SysAllocString(html);
if (bstr)
{
VARIANT* param;
HRESULT hr = SafeArrayAccessData(psaStrings, (LPVOID*)¶m);
if (SUCCEEDED(hr))
{
param->vt = VT_BSTR;
param->bstrVal = bstr;
hr = SafeArrayUnaccessData(psaStrings);
if (SUCCEEDED(hr))
{
doc1->write(psaStrings);
doc1->close();
}
}
}
if (psaStrings)
SafeArrayDestroy(psaStrings);
}
Method 2
Assuming you have defined the Longitude
and the Latitude
along with the level of Zoom (set to "10
" in my example), and obtained the API_KEY
, you should use the following code:
CoInitialize(NULL);
m_Browser.put_Silent(TRUE);
CString HTML_TEXT;
CRect rect;
CWnd *pWnd = GetDlgItem(IDC_SGWEBBROWSER);
pWnd->GetWindowRect(&rect);
int w = rect.Width()-50, h = rect.Height()-50;
HTML_TEXT.Format(L"<!DOCTYPE html><html>
<meta http-equiv=\"IE X-UA-Compatible\" content=\"IE = edge\">
<body><div id =\"googleMap\" style=\"width:%dpx;height:%dpx\">
<script>function myMap(){var mapProp = {center:new google.maps.LatLng(%f, %f),
zoom : 10};var map = new google.maps.Map(document.getElementById(\"googleMap\"),
mapProp);marker = new google.maps.Marker({position: new google.maps.LatLng(%f, %f),
map: map});}</script>
<script src = \"https://maps.googleapis.com/maps/api/js?key=%s&callback=myMap\">
</script></div></body></html>",
w, h, Latitude, Longitude, Latitude, Longitude, API_KEY);
FILE *fp = _wfopen(FileName, L"w");
fwprintf(fp, L"%s", HTML_TEXT.GetBuffer());
fclose(fp);
m_Browser.Navigate(FileName, 0, 0, 0, 0);
Web Browser Compatibility Issues
Recently, it became harder to use the WebBrowser
control for interfacing with APIs such as Google Maps. Read the following article and following the instructions there, you get an error message when trying to display a Google Map in the WebBrowser control.
Basically, you need to manipulate the following Registry key:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\
Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION
Alternatively, add the META TAG
IE X-UA-Compatible
Displaying the Map
Then, when you wish to display the Google Map in your WebBrowser
control, you just call:
m_Browser.Navigate(FileName, 0, 0, 0, 0);
History
- 15th January, 2018: Initial version