Pobranie treści z innego okna

0

Cześć,
próbuję pobrać tekst zawarty w tabeli innej aplikacji. Jedyne co wiem to fakt, że została ona napisana w.... Javie (pomijam fakt, że musi być zainstalowane środowisko Java :P). Z tego co udało mi się wygooglować to, że muszę złapać uchwyt tej aplikacji i zdobyć nazwy klas. Udało mi się to zrobić w ten sposób:

var
  P : TPoint;
  Window : HWND;
  Str : string;
begin
  GetCursorPos(P);
  Window := WindowFromPoint(P);
  Setlength(Str, 128);
  GetClassName(Window, Pchar(Str), 128);

memo1.append(str);

Trochę błądzę po omacku bo nigdy tematem się nie zajmowałem więc proszę o wyrozumiałość:)

notowania.png

Informacje jakie udało mi się zdobyć to:
XTPToolBar - glowna belka aplikacji
Afx800000000:00000000 - zakladka z lewej strony
#32770 - belka tabeli
SysHeader32 - naglowki kolumn
AfxWnd100u - tabela

Nie wiem czy mając te dane byłbym w stanie pobrać dane zawarte w tabeli na screenie u góry? Chodzi mi o kursy giełdowe (najlepsze oferty kupna i sprzedaży).

Możecie mi pomóc i podpowiedzieć co teraz powinienem zrobić?

pozdrawiam

2

Wszystko zależy od tego jak jest napisana aplikacja. Ja jakiś czas temu miałem podobny problem i niestety ale tamta aplikacja w javie nie używała obiektów systemu windows przez co nie było uchwytów do których szło by się odnieść. Innymi słowy jeśli jest to aplikacja, która pobiera dane z neta to łatwiej Ci będzie przechwycić ramkę ethernetową np jakimś wiresharkiem i interpretować dane sieciowe niż odczyt z aplikacji java. Nie mniej jednak czytałem, że da się aplikację java "zdeasembloerować" używając odpowiednich frameworków ale tu by się jakiś ekspert od javy musiał wypowiedzieć.

0

Dzięki za odpowiedź.
Wiem, że sama aplikacja udostępnia dane przez DDE ale z tego co wiem Lazarus nie obsługuje na razie takich "technologii" ;) Wiesz moze jak obsluzyc takie cos przez Lazarusa i fpc bo przykladow nie znalazlem..

Aplikacja pobiera informacje z serwera danych wiec musze spradzic co tam lata.

0

Ja tam dziergam w Delphi XE5 i 2010, a Lazarusa nie znam ale wołam @furious programming ;)

2

Podstawowe pytanie - czy system Windows posiada kontrolkę typu StringGrid i zestaw funkcji z WinAPI do ustawiania takiego komponentu, odczytywania i modyfikacji danych? Bo jeśli nie to nie ma innej opcji jak ReadProcessMemory i szukanie danych w pamięci procesu;

Łatwiejszym sposobem było by przechwytywanie transmisji danych z sieci i ich parsowanie.

0

Program ma też możliwość łączenia się przez proxy więc postaram się napisać taki prosty serwer i łapać wszystkie pakiety w ten sposób bez kombinowania ze sniffingiem.

1

Ale jeżeli program obsługuje DDE (i ma udokumentowane metody) to przecież zwykłe API wiec nie widzę powodu dla którego nie dałoby się tego zrobić po ludzku. Nawet jeżeli w FPC nie ma gotowych klas ułatwiających obsługę to zrobić to na piechotę komunikatami choć trochę roboty to i tak może się bardziej opłacić od niepewnych pomysłów z odczytem pakietów pobieranych stron czy pamięci programu, bo to daje niemal 100% pewność (tylko jeżeli producent celowo coś zmieni to przestanie) że komunikacja będzie działała z przyszłą wersji programu.

0

W ramach weekendowych wyzwan postanowilem powalczyc z tym dde. Znalazlem w sieci przyklad i teoretycznie powinien on dzialac bo postujacy pisal ze mu smiga ;)

 var
  idInst: DWORD;

  CONST
   DMLERR_NO_ERROR = 0;
  APPCMD_CLIENTONLY = 10;

implementation

{$R *.lfm}

function DdeConnect(_para1:DWORD; _para2:HSZ; _para3:HSZ;_para4:PCONVCONTEXT ):HCONV; stdcall;

function DdeCallback(CallType, Fmt: UINT; Conv: HConv; hsz1, hsz2: HSZ;
    Data: HDDEData; Data1, Data2: DWORD): HDDEData; stdcall;
begin
  Result := 0
end;

function PobierzKurs: String; stdcall;
var
iReturn: DWORD;
hszApp, hszTopic, hszItem: HSZ;
hCnv: DWORD;
hData: HDDEDATA;
Buf: String[255];
begin
  Result := '';
  idInst := 0;
  iReturn := DdeInitialize(@idInst, @DdeCallback, APPCMD_CLIENTONLY, 0);
  if iReturn = DMLERR_NO_ERROR then
  begin
    //DDE Connect to Server using given AppName and topic.
    hszTopic := 0;
    hszApp := 0;
    hCnv := 0;
    hszApp := DdeCreateStringHandle(idInst, 'STATICA', CP_WINANSI); //nazwa serwera
    hszTopic := DdeCreateStringHandle(idInst, 'PZU', CP_WINANSI); //nazwa spolki
    hCnv := DdeConnect(idInst, hszApp, hszTopic, nil);
    DdeFreeStringHandle(idInst, hszApp);
    DdeFreeStringHandle(idInst, hszTopic);
    if hCnv <> 0 then
    begin
      hszItem := DdeCreateStringHandle(idInst, 'kurs', CP_WINANSI); //co chcemy pobrac
      hData := DdeClientTransaction(nil, 0, hCnv, hszItem, CF_TEXT, XTYP_REQUEST, 5000, nil);
      if hData <> 0 then
      begin
        DdeGetData(hData, @Buf, 255, 0);
        //strcopy(Result, PChar(Buf));
        Result := Buf;
      end;
      //DDE Disconnect and Uninitialize.
      DdeDisconnect(hCnv);
    end;
    DdeUninitialize(idInst);
  end;
end;

wszystko super tylko przy kompilacji dostaje ponizszy error

unit1.pas(55,49) Error: Incompatible type for arg no. 2: Got "<address of function(LongWord;LongWord;LongWord;LongWord;LongWord;LongWord;LongWord;LongWord):DWord is nested;StdCall>", expected "<procedure variable type of function(LongWord;LongWord;LongWord;LongWord;LongWord;LongWord;LongWord;LongWord):DWord;StdCall>" 

powinienem tam przekazywac te argumenty czy cos tutaj jest nie tak? Prosze o pomoc:)

0

Pod FPC może

 iReturn := DdeInitialize(@idInst, DdeCallback, APPCMD_CLIENTONLY, 0);
0

Probowalem juz tak i niestety to tez nie przechodzi.
Dostaje wtedy

 unit1.pas(55,38) Error: Wrong number of parameters specified for call to "DdeCallback"

W ogole pierwotne DDEConnect wygladalo tak:

 function DdeConnect(_para1:DWORD; _para2:HSZ; _para3:HSZ;_para4:PCONVCONTEXT ):HCONV; external 'user32' name 'DdeConnect';

ale ponoc powinno byc zawsze stdcall wiec zamienilem. O dziwo kiedy jest tak jak powyzej to wszystko kompiluje sie ladnie ale wyklada mi sie na:

     if hCnv <> 0 then

bo co bym nie robil to zawsze hCnv=0.

0

funkcja DdeConnect powinna być zadeklarowana jako

  function DdeConnect(Inst: DWORD; Service, Topic: HSZ; CC: PConvContext): HConv; stdcall; external 'user32.dll';

Jeżeli nie masz zadeklarowanych typów to:

type
  HConvList = Longint;
  HConv = Longint;
  HSz = Longint;
  HDDEData = Longint;

  PConvContext = ^TConvContext;
  TConvContext = record
    cb: UINT;
    wFlags: UINT;
    wCountryID: UINT;
    iCodePage: Integer;
    dwLangID: DWORD;
    dwSecurity: DWORD;
    qos: TSecurityQualityOfService;
  end;

Jeżeli chodzi o DdeCallback to pytanie jak zadeklarowałeś DdeInitialize w Delphi to tak wygląda:

type
  PFNCALLBACK = function (CallType, Fmt: UINT; Conv: HConv; hsz1, hsz2: HSZ;
    Data: HDDEData; Data1, Data2: DWORD): HDDEData stdcall;
  TFNCallback = PFNCALLBACK;
  function DdeInitialize(var Inst: Longint; Callback: TFNCallback; Cmd, Res: Longint): Longint; stdcall; external 'user32.dll' name 'DdeInitializeA'; //ew. DdeInitializeW w Unicode
1

@karpov - wszystko co jest Ci potrzebne, znajduje się w module Windows, w tym nagłówek funkcji DdeConnect:

function DdeConnect(_para1:DWORD; _para2:HSZ; _para3:HSZ; var _para4:CONVCONTEXT):HCONV; external 'user32' name 'DdeConnect';

Przy czym, jak widać, parametr _para4 wymaga referencji do zmiennej.

2

Wywal deklaracje DdeConnect skoro jest w Windows, możesz wywalić też ciało funkcji DdeCallback sprawdziłem kod dostosowując go do pobrania adresy aktywnej zakładki przeglądarki czyli odpowiednik tego Określenie jaka zakładka i jaki URL są w danej chwili aktywne i kod działa

var
  hszApp, hszTopic, hszItem: HSZ;
  hCnv: DWORD;
  hData: HDDEDATA;
  Buf: array [0..255] of Char;
  iTotalDataLength, iCurrentDataLength: Integer;
  sOutData: string;
begin
  idInst := 0;
  if DdeInitialize(@idInst, nil, APPCMD_CLIENTONLY, 0) = DMLERR_NO_ERROR then
  begin
    hszApp:= DdeCreateStringHandle(idInst, BROWSER_FIREFOX, CP_WINANSI);
    hszTopic:= DdeCreateStringHandle(idInst, 'WWW_GetWindowInfo', CP_WINANSI);
    hCnv:= DdeConnect(idInst, hszApp, hszTopic, nil);
    DdeFreeStringHandle(idInst, hszApp);
    DdeFreeStringHandle(idInst, hszTopic);
    if hCnv <> 0 then
    begin
      hszItem := DdeCreateStringHandle(idInst, '0xFFFFFFFF', CP_WINANSI); //co chcemy pobrac
      hData := DdeClientTransaction(nil, 0, hCnv, hszItem, CF_TEXT, XTYP_REQUEST, 5000, nil);
      if hData <> 0 then
      begin
        sOutData:= '';
        iCurrentDataLength:= 0;
        iTotalDataLength:= DdeGetData(hData, nil, 0, 0);
        repeat
          DdeGetData(hData, @Buf, 255, iCurrentDataLength);
          iCurrentDataLength:= iCurrentDataLength+ Length(Buf);
          sOutData:= sOutData + Buf;
        until iCurrentDataLength >= iTotalDataLength;
        Caption:= sOutData; //wyjscie
      end;
      DdeFreeDataHandle(hData);
      DdeFreeStringHandle(idInst, hszItem);

      //DDE Disconnect and Uninitialize.
      DdeDisconnect(hCnv);
    end;
    DdeUninitialize(idInst);
  end;
0

Dziala :)

Pojawil sie tylko problem w trakcie kompilacji przy linii:

   if DdeInitialize(@idInst, nil, APPCMD_CLIENTONLY, 0) = DMLERR_NO_ERROR then
  begin

dziwne, bo w dokumentacji sa zawarte te informacje a wyrzuca mi bledem:

 unit1.pas(44,34) Error: Identifier not found "APPCMD_CLIENTONLY"
unit1.pas(44,58) Error: Identifier not found "DMLERR_NO_ERROR"

dopiero kiedy zamienie powyzsza linie na:

   DdeInitialize(@idInst, nil, APPCLASS_STANDARD, 0);

bez sprawdzania poprawnosci inicjalizacji dde i zamianie APPCMD_CLIENTONLY na APPCLASS_STANDARD wszystko ladnie sie kompiluje.

Rozumiem, ze funkcja callback, ktora wywalilismy sluzy do odbierania danych, ktore ulegly zmianie?

pozdrawiam!

0

Sprawdzilem i wszystko dziala super. Dziekuje Ci @kAzek !
Pojawil sie tylko kolejny nieoczekiwany problem. Dane, ktore sa pobierane maja uciety pierwszy znak.

I tak jezeli wartosc to 1929,00 dostaje tylko 929,00... Masz moze jakis pomysl jak to ogarnac?

2

Sorry gapa ze mnie też nie zauważyłem. Zmień deklarację zmiennej Buf na Buf: array [0..255] of Char; (ja zmienię od razu w przykładowym kodzie).

1

A najlepiej na Buff: array [Byte] of Char - wtedy wszystko będzie oczywiste :D

1 użytkowników online, w tym zalogowanych: 0, gości: 1