首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Windows 7 位置 API 開發實戰:Win32 主控台應用程式同步與非同步定位

     編輯:關於.NET

Windows 7 感應器與位置平台優勢

  • 硬體獨立:統一 API,無需廠商專有 SDK
  • 隱私優先:預設關閉,使用者控制啟用,應用程式需請求權限
  • 多應用共享:同一感應器資料可供多應用同時存取
  • 位置抽象化:Location API 自動選擇最佳定位來源(GPS、Wi-Fi、基地台),無需實作 NMEA 協定

改進後的 MSDN 閱讀器

實驗目標

  • 同步取得經緯度與市政地址位置報表
  • 非同步訂閱位置更新報表
  • 權限未授予時請求使用者授權

系統需求

  • Visual Studio 2008 SP1
  • Windows 7 RC 或更新版本
  • Windows 7 SDK
  • 市政地址提供者 (eDLP):下載連結

前置準備

確保未連接實體/虛擬位置感應器,避免覆蓋預設位置導致錯誤結果。透過「位置與其他感應器」控制台項目檢查。

搜尋感應器

位置與其他感應器視窗

建立 Win32 主控台專案

  1. File → New → Project → Visual C++ → Win32 → Win32 Console Application
  2. 名稱:LocationHOL,路徑:C:\Temp
  3. 專案屬性 → Linker → Input → Additional Dependencies 加入 locationapi.lib

任務 1:同步讀取位置資訊

StdAfx.h 加入標頭

#include <windows.h>
#include <atlbase.h>
#include <atlcom.h>
#include <LocationAPI.h>

LocationHOL.cpp 初始化與權限請求

IID REPORT_TYPES[] = { IID_ILatLongReport, IID_ICivicAddressReport };
class CInitializeATL : public CAtlExeModuleT<CInitializeATL> {};
CInitializeATL g_InitializeATL;

int _tmain()
{
    CComPtr<ILocation> spLocation;
    if (FAILED(spLocation.CoCreateInstance(CLSID_Location))) {
        wprintf(L"Failed to create Location COM object.\n");
        return 1;
    }

    if (FAILED(spLocation->RequestPermissions(NULL, REPORT_TYPES, ARRAYSIZE(REPORT_TYPES), TRUE))) {
        wprintf(L"Warning: Unable to request permissions.\n");
    }
    ...
}

報表狀態列印輔助函式

void PrintReportStatus(LOCATION_REPORT_STATUS status)
{
    switch (status) {
        case REPORT_RUNNING: wprintf(L"Report ready.\n"); break;
        case REPORT_NOT_SUPPORTED: wprintf(L"No devices detected.\n"); break;
        case REPORT_ERROR: wprintf(L"Report error.\n"); break;
        case REPORT_ACCESS_DENIED: wprintf(L"Access denied to report.\n"); break;
        case REPORT_INITIALIZING: wprintf(L"Report is initializing.\n"); break;
    }
}

取得經緯度報表狀態與資料


LOCATION_REPORT_STATUS status = REPORT_NOT_SUPPORTED;
if (FAILED(spLocation->GetReportStatus(IID_ILatLongReport, &status))) {
    wprintf(L"Error: Unable to obtain lat/long report status.\n");
    return 1;
}
wprintf(L"Lat./long. Report status: "); PrintReportStatus(status);

if (status == REPORT_RUNNING) {
    CComPtr<ILocationReport> spLocationReport;
    CComPtr<ILatLongReport> spLatLongReport;
    if (SUCCEEDED(spLocation->GetReport(IID_ILatLongReport, &spLocationReport)) &&
        SUCCEEDED(spLocationReport->QueryInterface(IID_PPV_ARGS(&spLatLongReport))) &&
        spLatLongReport.p != NULL) {

        SYSTEMTIME st;
        if (SUCCEEDED(spLatLongReport->GetTimestamp(&st))) PrintTimestamp(st);

        GUID sensorID = {0};
        if (SUCCEEDED(spLatLongReport->GetSensorID(&sensorID))) {
            wprintf(L"SensorID: "); PrintGUID(sensorID);
        }

        wprintf(L"\nReport:\n");
        PrintLatLongReport(spLatLongReport);
    }
}

經緯度報表列印函式

void PrintLatLongReport(ILatLongReport* pReport)
{
    DOUBLE lat, lon, alt, errRad, altErr;
    if (SUCCEEDED(pReport->GetLatitude(&lat))) wprintf(L"Latitude: %f\n", lat);
    if (SUCCEEDED(pReport->GetLongitude(&lon))) wprintf(L"Longitude: %f\n", lon);
    if (SUCCEEDED(pReport->GetAltitude(&alt))) wprintf(L"Altitude: %f\n", alt);
    else wprintf(L"Altitude: Not available.\n");
    if (SUCCEEDED(pReport->GetErrorRadius(&errRad))) wprintf(L"Error Radius: %f\n", errRad);
    if (SUCCEEDED(pReport->GetAltitudeError(&altErr))) wprintf(L"Altitude Error: %f\n", altErr);
    else wprintf(L"Altitude Error: Not available.\n");
}

GUID 與時間戳記列印輔助函式

void PrintGUID(const GUID guid) { ... }
void PrintTimestamp(const SYSTEMTIME time) { ... }

使用 eDLP 設定預設位置

  1. 放大地圖至所需位置
  2. 右鍵點選 → 選擇「Save Address」設為預設市政地址

eDLP 設定預設位置

編譯執行結果

同步讀取執行結果

任務 2:非同步讀取市政地址資訊

建立事件接收類別 CLocationEvents

// LocationEvents.h
#pragma once
#include "StdAfx.h"

class CLocationEvents : public CComObjectRoot, public ILocationEvents
{
public:
    DECLARE_NOT_AGGREGATABLE(CLocationEvents)
    BEGIN_COM_MAP(CLocationEvents)
        COM_INTERFACE_ENTRY(ILocationEvents)
    END_COM_MAP()

    STDMETHOD(OnLocationChanged)(REFIID reportType, ILocationReport* pLocationReport);
    STDMETHOD(OnStatusChanged)(REFIID reportType, LOCATION_REPORT_STATUS status);

private:
    void PrintCivicAddress(ICivicAddressReport* pReport);
};

實作事件處理

// LocationEvents.cpp
#include "LocationEvents.h"

STDMETHODIMP CLocationEvents::OnLocationChanged(REFIID reportType, ILocationReport* pLocationReport)
{
    if (IID_ICivicAddressReport == reportType) {
        CComPtr<ICivicAddressReport> spReport;
        if (SUCCEEDED(pLocationReport->QueryInterface(IID_PPV_ARGS(&spReport))) && spReport.p) {
            wprintf(L"Civic address location changed:\n\n");
            PrintCivicAddress(spReport);
        }
    }
    return S_OK;
}

STDMETHODIMP CLocationEvents::OnStatusChanged(REFIID reportType, LOCATION_REPORT_STATUS status)
{
    if (IID_ICivicAddressReport == reportType) {
        switch (status) {
            case REPORT_NOT_SUPPORTED: wprintf(L"\nNo devices detected.\n"); break;
            case REPORT_ERROR: wprintf(L"\nReport error.\n"); break;
            case REPORT_ACCESS_DENIED: wprintf(L"\nAccess denied to reports.\n"); break;
            case REPORT_INITIALIZING: wprintf(L"\nReport is initializing.\n"); break;
            case REPORT_RUNNING: wprintf(L"\nRunning.\n"); break;
        }
    }
    return S_OK;
}

void CLocationEvents::PrintCivicAddress(ICivicAddressReport* pReport)
{
    CComBSTR addr1, addr2, postal, city, state, country;
    if (SUCCEEDED(pReport->GetAddressLine1(&addr1)) && addr1.Length()) wprintf(L"\tAddress Line 1:\t%s\n", addr1);
    if (SUCCEEDED(pReport->GetAddressLine2(&addr2)) && addr2.Length()) wprintf(L"\tAddress Line 2:\t%s\n", addr2);
    if (SUCCEEDED(pReport->GetPostalCode(&postal)) && postal.Length()) wprintf(L"\tPostal Code:\t%s\n", postal);
    if (SUCCEEDED(pReport->GetCity(&city)) && city.Length()) wprintf(L"\tCity:\t\t%s\n", city);
    if (SUCCEEDED(pReport->GetStateProvince(&state)) && state.Length()) wprintf(L"\tState/Province:\t%s\n", state);
    if (SUCCEEDED(pReport->GetCountryRegion(&country))) wprintf(L"\tCountry/Region:\t%s\n\n", country);
}

主程式註冊非同步報表

#include "LocationEvents.h"

int _tmain()
{
    // ... 前述同步程式碼 ...

    CComObject<CLocationEvents>* pEvents = NULL;
    if (FAILED(CComObject<CLocationEvents>::CreateInstance(&pEvents)) || !pEvents) {
        wprintf(L"Error creation Location events sink.\n");
        return 1;
    }
    pEvents->AddRef();

    if (FAILED(spLocation->RegisterForReport(pEvents, IID_ICivicAddressReport, 0))) {
        pEvents->Release();
        wprintf(L"Error registering for report.\n");
        return 1;
    }

    wprintf(L"Press ENTER to exit.\n");
    getchar();

    if (pEvents) {
        spLocation->UnregisterForReport(IID_ICivicAddressReport);
        pEvents->Release();
    }
    return 0;
}

測試非同步更新

  1. 編譯執行,程式等待位置變更
  2. 使用 eDLP 地圖右鍵不同人口稠密城市位置 → 「Save Address」
  3. 觀察主控台即時輸出更新後的市政地址

非同步更新測試

總結

本實驗展示 Windows 7 Location API 同步(經緯度)與非同步(市政地址)存取模式,涵蓋 COM 物件建立、權限請求、報表狀態查詢、資料擷取與事件訂閱。產品級整合需額外處理錯誤、快取、UI 執行緒切換等,但核心概念已掌握。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved