Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

Using Microsoft Location api (locationapi.dll)

Posted on 2013-11-02
9
710 Views
Last Modified: 2013-11-06
I have written a routine to fetch the Latitude and Longitude from the inbuilt GPSr using the Microsoft LocationApi.dll

For more information see This question

I Personally don't have a computer with an inbuilt GPSr, but I have a user that has a Microsoft Surface Pro and he is testing for me.

He reports that the fetching of the lat/lon works OK the first time the program is run. If he clicks on the button a second time, the program does not do the fetch and it just loops in the WaitForReport function (the status value is always "REPORT_INITIALIZING") . It is like the code needs to release or cancel something before it can be successfully run again.

What needs to be added to the code so it can be run a second time without having to exit the program and start again.

FYI here is the tweaked WaitForReport function. It shows a message after every second of waiting. It is this message (status=3) that the user sees continually when trying to fetch the Lat/Lon a second time.

function Tform2.WaitForReport(loc: ILocation; reportType: TGUID): Boolean;
var
  status: LOCATION_REPORT_STATUS;
 begin
  Result := False;

  while (loc.GetReportStatus(reportType, status) = S_OK) do
  begin
    if status = REPORT_RUNNING then //report ready!
    begin
      Result := True;
      Exit;
    end;

    if status <> REPORT_INITIALIZING then //wait for report!
    begin
      //error occured
      Exit;
    end;
    application.ProcessMessages;
    Sleep(1000);
    showmessage('Waiting for locatin report to finish - Status = ' + inttostr(status));

  end;
end;

Open in new window


And here is the actual code for the get lat/lon button (originally had in the debug messages so the user could tell me how far the routine was getting)

procedure TForm2.Button1Click(Sender: TObject);
//type
 // PILocationReport = ^ILocationReport;
var
  loc: ILocation;
  rep: ILocationReport;
  LatLongRep: ILatLongReport;
  status: LOCATION_REPORT_STATUS;
  hres: HRESULT;
  fLat, fLon: double;
begin
  if IsLocationAPIAvailable then
  begin
//    showmessage('debug1');
    loc := CreateComObject(CLASS_Location) as ILocation;
    application.ProcessMessages;
//    showmessage('debug2');
    try
      status := REPORT_NOT_SUPPORTED;
      //have permission?
                                                                            // 0 for asynchronous calls
      if loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True))  = S_OK then
      begin
        application.ProcessMessages;
//        showmessage('debug3');
        if not WaitForReport(loc, IID_ILatLongReport) then
        begin
          showMessage(LocReportStatus(status));
          Exit;
        end;
//        showmessage('debug4');
        hres := loc.GetReportStatus(IID_ILatLongReport, status);
//        showmessage('debug5');
        if hres = S_OK then
        begin
//          showmessage('debug6');
          if status = REPORT_RUNNING then //report is ok?
          begin
//            showmessage('debug7');
            hres := loc.GetReport(IID_ILatLongReport, rep);
//            showmessage('debug8');
            if (hres = S_OK) and (rep<>nil) then
            begin
//              showmessage('debug9');
              hres := rep.QueryInterface(IID_ILatLongReport, LatLongRep);
//              showmessage('debug10');
              if (hres = S_OK) and (LatLongRep<>nil) then
              begin
                fLat := 0;
                fLon := 0;
                LatLongRep.GetLatitude(fLat);
                LatLongRep.GetLongitude(fLon);
                showmessage('Latitude: ' + FloatToStr(fLat) + chr(13) + chr(10) + 'Longitude: ' +  FloatToStr(fLon));
              //  Edit1.Text := FloatToStr(fLat);
              //  Edit2.Text := FloatToStr(fLon);
              end;
            end;
          end
          else
            ShowMessage(LocReportStatus(status));
        end
        else
          ShowMessage(IntToStr(Integer(hres)));
      end;
    finally
      LatLongRep := nil;
      rep := nil;
      loc := nil;
    end;
  end;
end;

Open in new window

0
Comment
Question by:Clyde24
  • 5
  • 4
9 Comments
 
LVL 26

Expert Comment

by:Sinisa Vuk
ID: 39620194
This is what I do (because I don't hw too):
- go to http://www.turboirc.com/gps7/ and get driver for gps on win7 and install it in simulation mode

- all is running first time as you said - then I change sync to async fetch request for report:

from:
if loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(True))  = S_OK then

Open in new window


to:
if loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(False))  = S_OK then

Open in new window


..this way goes all time
0
 

Author Comment

by:Clyde24
ID: 39620560
I don't really understand your answer. What exactly do you mean by this? Are you saying that the very first call should use sync, then after the first call we should use async. Or are you saying if you do have a sensor you should always use async, if you don't have a sensor you should always use sync.

One thing I will note that I found very strange. The user reported that the original code worked every time when the debug messages were shown. When I removed the debug messages, the code only worked the first time. Even after cancelling the program (via task manager and then restart) it would not work again. The user had to reboot the computer before the code would work again.  When the debug messages were shown it worked every time - why would this be?
0
 

Author Comment

by:Clyde24
ID: 39620591
OK, I installed the simulation driver and I now think I understand your answer, because I can now get it to work all the time using async.

What you are saying is that the code should *always* use async (your second block of code)

Basically the original code was using sync (which was wrong), where as it should have been using async all along?
0
Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
LVL 26

Expert Comment

by:Sinisa Vuk
ID: 39620859
Try to use async all time ... First time all worked fine because location engine load and took time - so next line - checking status will catch good value. Original work is based on "as is" couse I don't have hw as you do - later I decide do use simulator.
0
 

Author Comment

by:Clyde24
ID: 39620936
The async call seems to work OK for me in the simulator. However, when I sent my test program to the user who has the Microsoft surface pro, he says it made no difference - it works the first time and then just hangs on the second attempt. He needs to reboot the computer to get it to work again.

The user reports that it always works when the debug messages are displayed (using both sync and async calls). This I just don't understand. Perhaps it is a timing issue and the display of the status messages stop the issue from happening.

How can we fix this. Is there some initialization step that can be run to reset the api?
0
 
LVL 26

Expert Comment

by:Sinisa Vuk
ID: 39621170
Try this code (without waiting for status):

function GetLocationAPI: ILocation;
begin
  Result := nil;
  
  try
    Result := CreateComObject(CLASS_Location) as ILocation;
  except
  end;
end;

....
var
  loc: ILocation;
  rep: ILocationReport;
  LatLongRep: ILatLongReport;
  status: LOCATION_REPORT_STATUS;
  hres: HRESULT;
  fLat, fLon: double;
begin
  loc := GetLocationAPI; //GetLocationAPIClass;
  if loc<>nil then
  begin
    application.ProcessMessages;
    try
      status := REPORT_NOT_SUPPORTED;
      //have permission?
      hres := loc.RequestPermissions(_RemotableHandle(nil^), IID_ILatLongReport, 1, Integer(False));
      if hres = S_OK then
      begin
        application.ProcessMessages;

        hres := loc.GetReportStatus(IID_ILatLongReport, status);
        if hres = S_OK then
        begin
          if status = REPORT_RUNNING then //report is ok?
          begin
            hres := loc.GetReport(IID_ILatLongReport, rep);
            if (hres = S_OK) and (rep<>nil) then
            begin
              hres := rep.QueryInterface(IID_ILatLongReport, LatLongRep);
              if (hres = S_OK) and (LatLongRep<>nil) then
              begin
                fLat := 0;
                fLon := 0;
                LatLongRep.GetLatitude(fLat);
                LatLongRep.GetLongitude(fLon);
                showmessage('Latitude: ' + FloatToStr(fLat) + chr(13) + chr(10) + 'Longitude: ' +  FloatToStr(fLon));
              end;
            end;
          end
          else
            ShowMessage(LocReportStatus(status));
        end
        else
          ShowMessage(IntToStr(Integer(hres)));
      end
      else
        ShowMessage(IntToStr(Integer(hres)));
    finally
      LatLongRep := nil;
      rep := nil;
      loc := nil;
    end;
  end;

Open in new window


There is some issue with waiting and request for permission ... Without real hw it is hard to detect real problem. These example is compatible with MS Location Api example.
0
 

Author Comment

by:Clyde24
ID: 39623724
... Update

The code given still only works the first time. After the first time the user always gets the message "Report is initializing!"

In desperation I tried a modification to the finally block - basically I removed (commented out)  the := nil statements. so from
    finally
      LatLongRep := nil;
      rep := nil;
      loc := nil;
    end;

Open in new window

To
    finally
      //LatLongRep := nil;
      //rep := nil;
      //loc := nil;
    end;

Open in new window

This worked every time!

So it looks like it has something to do with the objects being set to nil. I don't like this code as I believe it would create a memory leak.  However, as "loc" is really the only object being created, I have made another test for the user with:

finally
      //LatLongRep := nil;
      //rep := nil;
      loc := nil;
end;

Open in new window


I will report back my findings after the user has tested.
0
 
LVL 26

Accepted Solution

by:
Sinisa Vuk earned 500 total points
ID: 39623819
instead of loc: ILocation try to define loc: TLocation.
Then you go with....
...
loc := TLocation.Create(nil);
....

Open in new window


replace all ILocation with TLocation and replace loc:=nil with loc.Free.
0
 

Author Comment

by:Clyde24
ID: 39629228
Thanks. My test user reports that this seems to be working.
0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Path  to current project in Delphi. 2 92
HTML text in the body of an email (delphi code) 12 166
Firemonkey webbrowser scrollbars ? 1 50
update joined tables 2 55
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Microsoft Active Directory, the widely used IT infrastructure, is known for its high risk of credential theft. The best way to test your Active Directory’s vulnerabilities to pass-the-ticket, pass-the-hash, privilege escalation, and malware attacks …
This video shows how to use Hyena, from SystemTools Software, to bulk import 100 user accounts from an external text file. View in 1080p for best video quality.

839 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