Dann will ich mal versuchen, etwas Licht ins Dunkel zu bringen:
Zuallererst vielleicht mal eine kurze Erklärung
was genau diese ominöse ICQ API ist: Dabei handelt es sich um eine
DDE (Dynamic data exchange) Implementation. Im Klartext heißt das, daß die API lediglich die Fernsteuerung eines ICQ Clients erlaubt, nicht aber, daß man z.B. einen eigenen Client auf Basis des ICQ Netzwerk Protokolls schreiben kann.
laut der doku soll man nur diesen einen aufruf machen um die lizenz zu aktivieren
Das stimmt so nicht ganz --
ICQAPICall_SetLicenseKey sagt lediglich der API, daß Du (theoretisch) registrierter und damit legaler Nutzer bist. Ohne diese Validierung der Daten sind keine weiteren API-Aufrufe möglich. Obwohl diese Funktion von zentraler Bedeutung für die Nutzung der API ist, ist sich die Dokumentation zu fein ihr auch die nötige Beachtung zu schenken.
Das ganze wird auf 2 Arten verwendet.
1. Man verwendet die Exportierten Funktionen der DLL oder
2. Man linkt die .lib statisch in die Applikation rein.
Hmmm, nein. Die .lib selbst bestimmt, ob sie statisch gelinkt werden kann, oder nur dem Linker die Möglichkeit für eine automatische Importierung der exportierten .dll-Funktionalität zur Verfügung stellt. Bei der vorliegenden Version der API ist lediglich die Nutzung in Form einer .dll möglich. Richtig ist, daß man dies auf unterschiedliche Arten bewerkstelligen kann:
- Project -> Settings -> Link -> General [für MSVC]
- #pragma comment( lib, "icqmapi" ) [für MSVC]
- zur Laufzeit mittels LoadLibrary und GetProcAddress
zu 1.) wird
implizites Linken genannt, weil der Linker automatisch den Code generiert, um die Bibliothek bei Programmstart zu laden und die Adressen der exportierten Funktionen zu erfragen. Sollte dabei irgendetwas nicht funktionieren, bricht das Programm umgehend die Ausführung mit einer Fehlermeldung ab, noch bevor überhaupt
main bzw.
WinMain aufgerufen worden sind. Somit hat der Entwickler keinerlei Möglichkeiten selbst auf Fehler zu reagieren.
zu 2.) ist identisch zu 1.), hat allerdings den Vorteil, daß das #pragma direkt in den Code geschrieben wird und damit zum einen diesen aussagekräftiger macht, zum anderen der Code auch ohne das .dsp [MSVC] noch vollständig kompilierbar bleibt. Letzteres hilft, wenn man Source Code mit anderen Entwicklern austauscht.
zu 3.) wird
explizites Linken genannt. Der Entwickler hat die volle Kontrolle. Falls z.B. die Bibliothek nicht geladen, oder eine Funktion nicht importiert werden konnte, kann darauf von Programmseite reagiert werden, in welcher Form auch immer. Beim expliziten Linken braucht man auch keine .lib, da die enthaltenen Informationen nur für das implizite Linken gebraucht werden. Das explizite Linken bietet aber noch andere Vorteile. So muß die .dll nicht mehr mit der eigentlichen Applikation weiter gegeben werden, sondern die Applikation kann selbständig auf dem Zielrechner nach der installierten .dll suchen (ICQ-Installationspfad aus der Registry auslesen und ab geht's). Damit bleibt die Applikation auch mit zukünftigen .dll Versionen (theoretisch) lauffähig und kann dadurch von eventuellen Bugfixes profitieren. Da aber mittlerweile AOL und Macromedia ihre Finger im Spiel haben, und beide Unternehmen bei mir einen zweifelhaften Ruf genießen, was die Qualität der Produkte betrifft, würde ich mich auf die Abwärtskompatibilität nicht verlassen.
Was sonst noch wichtig sein könnte:
ICQAPICall_GetFullUserData hat in der ausgelieferten .dll (Version 1.0001) einen Bug: Wird die Funktion für einen User aufgerufen, der zwar in den Contacts ist, aber als Offline geführt wird, wird dieser fälschlicherweise als Online gemeldet (
m_iStateFlags). Im Zuge des Umbaus meiner Anwendung auf explizites Linken will ich mal sehen, ob das in der aktuellen .dll (bei mir ist das v1.0002 - ICQ2003a) vielleicht behoben ist. Falls jemand Interesse hat, kann ich das Ergebnis hier posten.
So wie ich die Sache sehe, gibt es keine Möglichkeit in der API zuverlässig zu testen oder Notifications zu erhalten, wenn/ob der ICQ Client gestartet/beendet wurde. Im Moment fallen mir nur 2 Möglichkeiten ein:
- Falls ICQAPICall_SetLicenseKey fehlschlägt einen Timer zu setzen, der alle n Sekunden erneut versucht, die Funktion aufzurufen, bis der Aufruf erfolgreich ist.
- einen systemweiten Windows Hook zu setzen, der auf Erstellen und Zerstören von Toplevel-Windows anschlägt und entsprechend erneut versucht, ICQAPICall_SetLicenseKey aufzurufen, bzw. per ICQAPICall_GetWindowHandle und IsWindow überprüft, ob der Client noch läuft
Die erste Methode ist simpel, aber schlampig und wenig verlässlich, da man möglicherweise verpaßt, daß der Client kurz gelaufen ist. Die zweite Methode ist nicht ganz trivial und zwingt einen dazu, selbst eine .dll zu schreiben und über Prozessgrenzen hinweg zu kommunizieren. Machbar, aber irgendwie drängen sich dabei die Vokabeln
Spatzen und
Kanonen in meinem Kopf in den Vordergrund. Ist es wirklich zuviel verlangt, für dieses wirklich wichtige Ereignis eine Notification zu verschicken? Anscheinend....
Zum Schluß noch kurz was zum Versenden von Nachrichten: Das Problem an dieser scheinbar simplen Aktion ist, daß der Funktionsname selbst schon eine Lüge ist. Die Methode öffnet lediglich einen Message-Session-Dialog und schreibt den Text in das entsprechende Fenster. Das eigentliche Versenden der Nachricht wird verweigert. Um diesen Prozess zu automatisieren braucht man zunächst mal ein Window Handle (HWND). Dafür fallen mir im Moment nur 2 Möglichkeiten ein:
FindWindow und
EnumWindows, wobei die erste Methode wohl einfacher ist. Wenn Du dann ein entsprechendes Fenster gefunden hast, mußt du in diesem nach der Textbox suchen, die die ICQ-UIN enthält, diese in einen
int umwandeln und mit der eigentlichen UIN vergleichen, um sicherzustellen, daß Du das richtige Fenster hast. Anschließend per
SendInput ein [CTRL]+[RETURN] an das Fenster schicken, oder per
SendMessage eine WM_COMMAND-Nachricht mit dem entsprechenden Control-Identifier schicken. Anschließen kannst Du dann noch mit einem WM_CLOSE oder
DestroyWindow den Message-Session-Dialog schließen. Wenn Du allerdings komplett die Darstellung des Dialogs unterbinden willst, mußt Du unweigerlich Hooks benutzen und jedes ShowWindow( hWnd, SW_SHOW ) unterbinden. Wiederum alles machbar, aber der Aufwand ist wirklich schwer zu rechtfertigen.
Das soll's dann erstmal gewesen sein. Fragen? Kritik? Anregungen? Nur her damit.
.f