Das Notes Forum

Lotus Notes / Domino 10 => ND10: Entwicklung => Thema gestartet von: SD am 07.06.19 - 13:05:55



Titel: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: SD am 07.06.19 - 13:05:55
Guten Tag allerseits!

Benutzt hier zufällig jemand ide CLASSUserActivity-Scriptbibliothek von hier? https://www.agecom.com.au/agecom.nsf/0/8C9C50BC861E4A23CA2576DE003A2BB8?OpenDocument&IsDialog=1

Man kann damit auf die User Activities einer Notes-DB zugreifen, um die einzelnen Log-Einträge auszulesen.

Das haben wir bisher mit einem zeitgesteuerten, serverseitigen Agenten auf einem 64bit Windows2008R2 Server mit einem Domino 9.0.1FP10 gemacht und es hat wunderbar funktioniert. Die neuste Version der Scriptbibliothek ist offenbar von 2012 (zumindest laut dem Changelog im Code selbst). Diese nutzen wir.

Seit neustem sind wir auf einem 64bit Windows Server 2016 mit einem Domino 10.0.1FP2 und leider bringt dieser Code jetzt den kompletten Domino zum Absturz. Die neuen NSDs sind leider furchbar nichtssagend. :(

Durch ein wenig testen, konnte ich herausfinden, dass es diese Zeile ist, die den Crash verursacht:

Code:
Call CopyMemory (puActivityEntryExtended, StructureOffset, Len(puActivityEntryExtended))

Aufgerufen wird hier folgendes:

Code:
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( hpvDest As Any, ByVal hpvSource As Any, ByVal cbCopy As Long)

Ich frage mich jetzt, ob das neuere Windows diesen Spaß nicht mehr mit macht, oder ob der neuere Domino etwas dagegen hat?! Auf dem alten Windows hat dieses kernel32-Zeugs noch funktioniert. Gibt es das vielleicht so nicht mehr auf Windows Server 2016? Hat damit zufällig jemand Erfahrung? Oder das gleiche Problem? Oder noch besser: einen Weg das zu fixen?


Grüße
Stefan


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: SD am 07.06.19 - 13:10:16
Nachtrag:

Später im Code kommt noch das:

Code:
Call CopyMemoryString(spUsername, UsernameOffset, strlenLP(UsernameOffset))

Code:
Declare Sub CopyMemoryString Lib "kernel32" Alias "RtlMoveMemory" ( ByVal hpvDest As String, ByVal hpvSource As Any, ByVal cbCopy As Long)

So weit schafft der Code es zwar aktuell nicht, aber ich fürchte das würde dann wahrscheinlich das gleiche Problem haben.


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 08.06.19 - 10:31:05
Was ich momentan sagen kann ist, daß es nicht am Windows liegt.
Ich habe die DB unter Windows 2016 ( latest patches ) mit Domino 9.0.1FP10 getestet. Dort läuft der Agent ohne Probleme.

Unter Windows 2016 ( als clone der 901er Installation ) mit 10.0.1FP1 & FP2 crasht der Server.

Also wird es irgend etwas im 10er codestream sein, was zu dem Crash führt.

Ist jetzt eine schwierige Situation. Auf der einen Seite scheint es ein Bug zu sein, auf der anderen Seite gibt es keinen Support für custom applications.

Ich werde aber mal einen case anlegen; evtl bekomme ich das irgendwie zu L3.

Case# TS002346973


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 10.06.19 - 11:54:06
Ich habe es noch weiter runterbrechen können.

Das Problem liegt in der Deklaration von CopyMemory ( und CopyMemoryString)

Code:
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( hpvDest As Any, ByVal hpvSource As Any, ByVal cbCopy As Long)

Das ByVal bei hpvSource löst den Crash aus.

Lässt man es weg, dann läuft der Server zwar durch, aber es werden keine Werte für die einzelnen User ermittelt. Statt dessen gibt es einen Fehler bei der Konvertierung des Datums, was aber auch klar ist.

Na ja, warten wir mal ab, was der Support so meldet.


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 10.06.19 - 12:13:55
Gerade im Moment kam die erste Antwort vom Support

Zitat
I have further worked on this issue and found that there is no issue with Domino 10.0.1 Server with out any Fix Pack.

Please let me know whether it is OK to currently use with Domino 10.0.1 with out any fix packs.

Leider crasht der Server bei mir auch ohne FP.


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 11.06.19 - 10:46:28
wie ich mir schon gedacht habe. Support kann das Problem nicht reproduzieren

Zitat
Hi Ulrich,

I have further tested on 10.0.1 FP2 as well and the server did not crash at all. I followed the below steps : Please correct me if I am wrong.

0. put the database (ua.nsf) on the server and sign it with the server ID; Open the appl using Notes Client.
1. Click Retrieve User Activity
2. Click For all databases on a server
3. Select Run on server
4. Click OK

Could you please check by installing Domino 10.0.1 FP2 server on a new test machine and see whether it crashes?

Looking forward to your reply.

Regards
Sudhakar

Nur komisch, daß ich es auf jedem beliebigen Server blind und mit gefesselten Händen nachstellen kann.


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 11.06.19 - 11:20:06
So langsam komme ich mir ein bisschen verarscht vor:

Zitat
Hi Ulrich,

I have tested the same with the given template, however no issue. I am testing on Windows 2008 64 bit server and that does make any difference with 2016.

In your test case, is the Domino Server upgraded from 9.0.1 or fresh installation of 10.0.1?

Also please let me know during which point of code, the server is crashing? (Note : please keep print statements in between the code and check at what point, it is crashing).

Please provide me the latest NSD files for further investigation along with console.log during the time of the crash.

Regards
Sudhakar

Er testet also Domino V10 auf Windows 2008/64. Mal abgesehen davon, daß es keine supportete Platform ist; wie oft habe ich ihm schon geschrieben, daß der mein environment 2016/64 ist.

NSDs hat er genug von mir bekommen, und die zeigen immer wieder die gleichen Informationen. Unter anderen auch die GENAUE Codezeile, an der der Server crasht.

Und im console log steht nicht von Belang.

Ich frage mich langsam, warum ich mir das immer wieder antue ...


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: SD am 11.06.19 - 14:09:50
Hallo,

also ich bin jedenfalls dankbar für die Mühen!

Leider sind solche Eingriffe direkt in den Speicher nicht so ganz mein Spezialgebiet. Gibt es hier vielleicht irgendwelche Chancen auf einen Workaround, oder kann man die Infos auf einer anderen Weise aus den Activity-Logs pfrimeln?


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 11.06.19 - 14:15:12
Activity Logging am Server aktivieren. Siehe Admin Hilfe


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 11.06.19 - 15:51:46
Du kannst Dir auch mit C ein kleines Programm schreiben, das dir die Werte ausliest und in eine Db schreibt. Oder du machst es in Java mit DominoJNA.



Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 12.06.19 - 13:38:29
OK, habe jetzt dem Support bestätigt, daß der Code auf Windows 2008 R2 FP1 und Domino 10.0.1 nicht crasht.
Mitlerweile sitzen aber auch Leute aus der Entwicklung an der Sache.



Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 13.06.19 - 07:17:12
So, nachdem wir jetzt einen kompletten Tag verloren haben, weil der Support in einer nicht unterstützten Umgebung den Fehler nicht reproduzieren konnte, jetzt endlich die Meldung:

Zitat
Hi Ulrich,
 
Issue is reproducible with Win2016 10.0.1 & Win2016 10.0.1 FP1 & FP2.
 
I will further collaborate this issue with our product development team and update you the
findings as soon as possible.
 
Regards

Lessons learned so far:

1. Support immer nach der dort eingesetzten Version von OS und Domino fragen.




Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 13.06.19 - 08:01:08
Die Ursache für den Crash ist eigentlich klar. negativer pointer



Das mag dann das CopyMemory nicht.

Die Frage ist nur, warum kommt da ein neg. pointer?



Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: SD am 14.06.19 - 13:08:50
Das hier scheint das falsche zu machen:

Code:
puActivity = W32_OSLockObject(Me.rethUserInfo)

Auf dem Server geht da 85 rein und es kommt eine solche negative Zahl raus. Das Script läuft auch auf eine Replik der Zieldatenbank, da geht 87 rein und es kommt eine negative Zahl raus.

Code:
Me.rethUserInfo: 85
puActivity: -532537288

Me.rethUserInfo: 87
puActivity: -1553004888

Lasse ich das selbe Script auf meinem Client laufen, dann geht rethUserInfo 4114 rein und es kommt eine positive Zahl raus.

Ich vermute mal die Zahl ist eine Speicheradresse, die angibt, wo im Speicher aktuell die ActivityLogs anfangen. Dann wird die Größe, die der Logeintrag hat (ohne Username) nach vorne gesprungen und der Teil wegkopiert, der so lang ist, wie der Usereintrag sein müsste. Jetzt weiß ich nur leider nicht, ob Me.rethUserInfo schon falsch ist, oder W32_OSLockObject den falschen Wert daraus macht.

Der Wert für Me.rethUserInfo kommt wohl hier her:

Code:
StatusResult = W32_NSFDbGetUserActivityExtended(Me.hDb, &h0, Me.pDbActivityExtended, Me.rethUserInfo, Me.retUserCount)

Me.retUserCount scheint richtig zu sein. Dafür bekomme ich auch auf dem Client und auf dem Server den gleichen Wert. Ich habe mal spaßeshalber Me.rethUserInfo mit einem leicht höheren Wert überschrieben und damit sofort den Server gecrasht mit "PANIC: LookupHandle: handle out of range". Ich schätze mal der Me.rethUserInfo-Wert, der da raus kommt stimmt wohl und W32_OSLockObject macht was seltsames draus.

Der Umstand, dass die Zahl negativ ist und dass sie so groß (sprich klein) ist, klingt für mich nach einem Problem der Long-Variable. Long geht von -2.147.483.648 bis 2.147.483.647. Kann es sein, dass puActivity hier einmal die Runde gemacht hat und hinten im Minus wieder rausgekommen ist?! Ich hab mal versucht W32_OSLockObject auf "As Double" zu ändern und die anderen Long-Variablen auch, aber das war wohl zu stumpf. Da kommt jetzt auf dem Server nur noch 0 dabei raus (auf dem Client "-1,#IND" ... weird).

Also mein best guess wäre, dass W32_OSLockObject intern mit Long-Variablen arbeitet, die zu klein sind für die Speicherwerte auf dem Windows2016-Server.


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: SD am 14.06.19 - 13:15:40
Oh, jetzt hab ich die Variablen zurück auf Long geändert und bei einem neuen Durchlauf ist puActivity jetzt 851806112 (was wohlgemerkt weniger ist als das Long-Maximum von 2.147.483.647). Ich habe dann den Rest des Codes wieder mitlaufen lassen und siehe da, funktioniert.

Ich schätze mal da sind die ActivityLogs zufällig in einem Speicherbereich mit kleiner Adresse gelandet, wodurch die Longs jetzt nicht mehr überfordert werden.

Hat zufällig jemand eine W32_OSLockObject mit longeren Longs? ^^


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 14.06.19 - 15:59:32
Gute Analyse. U d da sagst du, dass das mit der Soeichermanipulation nicht so dein Ding ist. :)

Ich denke auch, dass das LockObject ds Problem ist.
Habe es mit anderen memCopy methoden aus der MSVCRT mitdem gleichen crash Ergebnis probiert.

Das LockObject ist aber eine Notes i trne Methode, die tausendfach im sourccode eingesetzt wird.
Loegt also eher an der bridge zwischen cApi und LS.

Warten wir mal ab, was DEV sagt.


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 15.06.19 - 06:40:14
Habe zum Testen das Ganze mal in ein kleines server addin verpackt. Funktioniert einwandfrei.
Ist im Großen und Ganzen das Beispiel, das in der cApi Dokumentation zu NSFDbGetUserActivity steht.

Code:
/*
* copyright (c) 2019 Ulrich Krause www.eknori.de
*/

#pragma warning(disable:4005)

#include "main.h"

STATUS LNPUBLIC AddInMain (HMODULE hModule, int argc, char *argv[]) {

STATUS error = NOERROR;
HANDLE hStatusLine;
HANDLE hStatusLineDesc;
HMODULE hMod;

AddInQueryDefaults (&hMod, &hStatusLine);
AddInDeleteStatusLine (hStatusLine);
hStatusLineDesc = AddInCreateStatusLine(ADDIN_STATUS_LINE);
AddInSetDefaults (hMod, hStatusLineDesc);
AddInSetStatusText("Initialising");

CmdLine *cmdline = new CmdLine();

cmdline->addUsage("  user activity, V1.0.0.0, (c) 2019, Ulrich Krause\n");
cmdline->addUsage("Usage: lo ua [options] [flags]\n");

cmdline->addUsage( "Options:\n" );
cmdline->addUsage( "-d    --db\t\tdatabase path" );
cmdline->addUsage( "" );
cmdline->addUsage( "Flags:\n" );
cmdline->addUsage( "-h    --help\t\tPrints this help" );

cmdline->setOption( "db", 'd' );
cmdline->setFlag ( "help", 'h' );

cmdline->processCommandArgs( argc, argv );

if( ! cmdline->hasOptions()) {
cmdline->printUsage2();
delete cmdline;
return ERROR;
}

if( cmdline->getFlag( "help" )
|| cmdline->getFlag( 'h' ) ) {
cmdline->printUsage2();
return NOERROR;
}

string file_path;
if( cmdline->getValue( 'd' ) != NULL  
|| cmdline->getValue( "db" ) != NULL  ){
file_path = cmdline->getValue( 'd' );
}

DBACTIVITY DbSummaryActivity;
DBACTIVITY_ENTRY  *pDbUserActivity, DbUserActivity;
DHANDLE hDbUserActivity;
WORD wUserCount;
char *szUserName;
char far szTimedate[1000];
LONG lRptPeriod;
WORD far wLen;
int i;

cNotesDatabase _NotesDatabase;

try{

_NotesDatabase.open(file_path.c_str());
AddInLogMessageText(MSG("DbTitle: %s\n"),
NOERROR, _NotesDatabase.title().c_str());

error = NSFDbGetUserActivity (_NotesDatabase.h, 0L, &DbSummaryActivity,
&hDbUserActivity, &wUserCount);

if (error)
{
delete cmdline;
_NotesDatabase.close();
LAPI_RETURN (ERR(error));
}

pDbUserActivity = OSLock (DBACTIVITY_ENTRY, hDbUserActivity);

for (i = 0; i < wUserCount; i++)
{
DbUserActivity = pDbUserActivity[i];
error = ConvertTIMEDATEToText (NULL, NULL, &(DbUserActivity.Time),
szTimedate, MAXALPHATIMEDATE, &wLen);
if (error)
{
OSUnlock (hDbUserActivity);
OSMemFree (hDbUserActivity);
delete cmdline;
_NotesDatabase.close();
LAPI_RETURN (ERR(error));
}

szTimedate[wLen] = '\0';
szUserName = NSFDbGetActivityUserNamePtr (pDbUserActivity, i);

if (DbUserActivity.Reads)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate, szUserName, DbUserActivity.Reads);
if (DbUserActivity.Writes)
AddInLogMessageText(
MSG("%s  %s wrote %d\n"),
NOERROR, szTimedate, szUserName,
DbUserActivity.Writes);
}

OSUnlock (hDbUserActivity);
OSMemFree (hDbUserActivity);

AddInLogMessageText("                 Uses         Reads        Writes\n", NOERROR);
AddInLogMessageText(
MSG("Last Day        %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivity.PrevDayUses,DbSummaryActivity.PrevDayReads,
DbSummaryActivity.PrevDayWrites);
AddInLogMessageText(
MSG("Last Week       %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivity.PrevWeekUses,DbSummaryActivity.PrevWeekReads,
DbSummaryActivity.PrevWeekWrites);
AddInLogMessageText(
MSG("Last Month      %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivity.PrevMonthUses,DbSummaryActivity.PrevMonthReads,
DbSummaryActivity.PrevMonthWrites);
lRptPeriod = TimeDateDifference (&(DbSummaryActivity.Last),
&(DbSummaryActivity.First));

if (lRptPeriod % (60*60*24))
lRptPeriod = lRptPeriod / (60*60*24) + 1;
else
lRptPeriod = lRptPeriod / (60*60*24);

AddInLogMessageText(
MSG("Last %lu days   %5d         %5d         %5d\n"),
NOERROR, lRptPeriod,
DbSummaryActivity.Uses,DbSummaryActivity.Reads,
DbSummaryActivity.Writes);

} catch (cNotesErr& err) {
delete cmdline;
_NotesDatabase.close();
return ERROR;

} catch (...) {
delete cmdline;
_NotesDatabase.close();

AddInLogMessageText("Unexpected Error\n", NOERROR);
return ERROR;
}

delete cmdline;
_NotesDatabase.close();
return error;
}



Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 15.06.19 - 09:47:35
Der Vollständigkeit halber, das Ganze noch einmal mit NSFDbGetUserActivityExtended

Code:
/*
* copyright (c) 2019 Ulrich Krause www.eknori.de
*/

#pragma warning(disable:4005)

#include "main.h"

STATUS LNPUBLIC AddInMain (HMODULE hModule, int argc, char *argv[]) {

STATUS error = NOERROR;
HANDLE hStatusLine;
HANDLE hStatusLineDesc;
HMODULE hMod;

AddInQueryDefaults (&hMod, &hStatusLine);
AddInDeleteStatusLine (hStatusLine);
hStatusLineDesc = AddInCreateStatusLine(ADDIN_STATUS_LINE);
AddInSetDefaults (hMod, hStatusLineDesc);
AddInSetStatusText("Initialising");

CmdLine *cmdline = new CmdLine();

cmdline->addUsage("  user activity, V1.0.0.0, (c) 2019, Ulrich Krause\n");
cmdline->addUsage("Usage: lo ua [options] [flags]\n");

cmdline->addUsage( "Options:\n" );
cmdline->addUsage( "-d    --db\t\tdatabase path" );
cmdline->addUsage( "" );
cmdline->addUsage( "Flags:\n" );
cmdline->addUsage( "-h    --help\t\tPrints this help" );

cmdline->setOption( "db", 'd' );
cmdline->setFlag ( "help", 'h' );

cmdline->processCommandArgs( argc, argv );

if( ! cmdline->hasOptions()) {
cmdline->printUsage2();
delete cmdline;
return ERROR;
}

if( cmdline->getFlag( "help" )
|| cmdline->getFlag( 'h' ) ) {
cmdline->printUsage2();
return NOERROR;
}

string file_path;
if( cmdline->getValue( 'd' ) != NULL 
|| cmdline->getValue( "db" ) != NULL  ){
file_path = cmdline->getValue( 'd' );
}

DBACTIVITYEXTENDED DbSummaryActivityExt;
DBACTIVITY_ENTRY_EXTENDED *pDbUserActivityExt, DbUserActivityExt;

DHANDLE hDbUserActivityExt;
WORD wUserCount;
char *szUserName;
char far szTimedate[1000];
LONG lRptPeriod;
WORD far wLen;
int i;

cNotesDatabase db;

try{

db.open(file_path.c_str());

AddInLogMessageText(
MSG("DbTitle: %s\n"),
NOERROR, db.title().c_str());

error = NSFDbGetUserActivityExtended (
db.h, 0L,
&DbSummaryActivityExt,
&hDbUserActivityExt, &wUserCount);
if (error)
{
delete cmdline;
db.close();
LAPI_RETURN (ERR(error));
}

pDbUserActivityExt = OSLock (DBACTIVITY_ENTRY_EXTENDED, hDbUserActivityExt);

for (i = 0; i < wUserCount; i++)
{
DbUserActivityExt = pDbUserActivityExt[i];
error = ConvertTIMEDATEToText (NULL, NULL, &(DbUserActivityExt.Time),
szTimedate, MAXALPHATIMEDATE, &wLen);
if (error)
{
OSUnlock (hDbUserActivityExt);
OSMemFree (hDbUserActivityExt);
delete cmdline;
db.close();
LAPI_RETURN (ERR(error));
}

szTimedate[wLen] = '\0';
szUserName = NSFDbGetActivityUserNamePtr (pDbUserActivityExt, i);

if (DbUserActivityExt.AEDetails.DataAdds)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.DataAdds);

if (DbUserActivityExt.AEDetails.DataDeletes)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.DataDeletes);

if (DbUserActivityExt.AEDetails.DataReads)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.DataReads);

if (DbUserActivityExt.AEDetails.DataUpdates)
AddInLogMessageText(
MSG("%s  %s wrote %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.DataUpdates);

// NON-DATA
if (DbUserActivityExt.AEDetails.NonDataAdds)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.NonDataAdds);

if (DbUserActivityExt.AEDetails.NonDataDeletes)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.NonDataDeletes);

if (DbUserActivityExt.AEDetails.NonDataReads)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.NonDataReads);

if (DbUserActivityExt.AEDetails.NonDataUpdates)
AddInLogMessageText(
MSG("%s  %s wrote %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.NonDataUpdates);
}

OSUnlock (hDbUserActivityExt);
OSMemFree (hDbUserActivityExt);

AddInLogMessageText("                 Uses         Reads        Writes        Adds\n", NOERROR);
AddInLogMessageText(
MSG("Last Day        %5d         %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivityExt.PrevDayUses,
DbSummaryActivityExt.PrevDayReads,
DbSummaryActivityExt.PrevDayUpdates,
DbSummaryActivityExt.PrevDayAdds);

AddInLogMessageText(
MSG("Last Week       %5d         %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivityExt.PrevWeekUses,
DbSummaryActivityExt.PrevWeekReads,
DbSummaryActivityExt.PrevWeekUpdates,
DbSummaryActivityExt.PrevWeekAdds);

AddInLogMessageText(
MSG("Last Month      %5d         %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivityExt.PrevMonthUses,
DbSummaryActivityExt.PrevMonthReads,
DbSummaryActivityExt.PrevMonthUpdates,
DbSummaryActivityExt.PrevMonthAdds);

lRptPeriod = TimeDateDifference (&(DbSummaryActivityExt.Last),
&(DbSummaryActivityExt.First));

if (lRptPeriod % (60*60*24))
lRptPeriod = lRptPeriod / (60*60*24) + 1;
else
lRptPeriod = lRptPeriod / (60*60*24);

AddInLogMessageText(
MSG("Last %lu days   %5d         %5d         %5d         %5d\n"),
NOERROR,
lRptPeriod,
DbSummaryActivityExt.Uses,
DbSummaryActivityExt.Reads,
DbSummaryActivityExt.Writes,
DbSummaryActivityExt.Adds);

} catch (cNotesErr& err) {
delete cmdline;
db.close();
LAPI_RETURN (ERR(error));

} catch (...) {
delete cmdline;
db.close();
AddInLogMessageText(
"Unexpected Error\n", NOERROR);

LAPI_RETURN (ERR(error));
}

delete cmdline;
db.close();
LAPI_RETURN (ERR(error));
}


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 15.06.19 - 12:16:48
Und damit es richtig Spaß macht, hier noch eine Variante, die die unterschiedlichen ODS Versionen berücksichtigt

main.h

Code:
#ifndef _MAIN_H_
#define _MAIN_H_

#include <string>

#if defined (_MSC_VER) && !defined(ND64)
#pragma pack(push, 1)
#endif
#include <global.h>
#include <miscerr.h>
#include <addin.h>
#if defined (_MSC_VER) && !defined (ND64)
#pragma pack(pop)
#endif

#include "cNotesFramework.h"
#include "cmdline.h"

#if defined (W64)
#define HANDLE DHANDLE
#undef NOTEHANDLE
#define NOTEHANDLE DHANDLE
#else
#define DHANDLE HANDLE
#undef NOTEHANDLE
#define NOTEHANDLE HANDLE
#endif

#define ADDIN_STATUS_LINE "UA"
#define APP_NAME "UA: "
#undef MSG
#define MSG(fmt) APP_NAME fmt
#define ERROR -1
using namespace std;

STATUS error = NOERROR;

DBACTIVITY DbSummaryActivity;
DBACTIVITYEXTENDED DbSummaryActivityExt;

DBACTIVITY_ENTRY  *pDbUserActivity, DbUserActivity;
DBACTIVITY_ENTRY_EXTENDED *pDbUserActivityExt, DbUserActivityExt;

DHANDLE hDbUserActivity;
DHANDLE hDbUserActivityExt;

WORD wUserCount;
char *szUserName;
char far szTimedate[1000];
LONG lRptPeriod;
WORD far wLen;
int i;
WORD minor = 0;
WORD major = 0;

// prototypes
STATUS get_user_activity(DHANDLE hDb);
STATUS get_user_activity_extended(DHANDLE hDb);
#endif


main.cpp

Code:
/*
* copyright (c) 2019 Ulrich Krause www.eknori.de
*/

#pragma warning(disable:4005)

#include "main.h"

STATUS LNPUBLIC AddInMain (HMODULE hModule, int argc, char *argv[]) {

HANDLE hStatusLine;
HANDLE hStatusLineDesc;
HMODULE hMod;

AddInQueryDefaults (&hMod, &hStatusLine);
AddInDeleteStatusLine (hStatusLine);
hStatusLineDesc = AddInCreateStatusLine(ADDIN_STATUS_LINE);
AddInSetDefaults (hMod, hStatusLineDesc);
AddInSetStatusText("Initialising");

CmdLine *cmdline = new CmdLine();

cmdline->addUsage("  user activity, V1.0.0.0, (c) 2019, Ulrich Krause\n");
cmdline->addUsage("Usage: lo ua [options] [flags]\n");

cmdline->addUsage( "Options:\n" );
cmdline->addUsage( "-d    --db\t\tdatabase path" );
cmdline->addUsage( "" );
cmdline->addUsage( "Flags:\n" );
cmdline->addUsage( "-h    --help\t\tPrints this help" );

cmdline->setOption( "db", 'd' );
cmdline->setFlag ( "help", 'h' );

cmdline->processCommandArgs( argc, argv );

if( ! cmdline->hasOptions()) {
cmdline->printUsage2();
delete cmdline;
return ERROR;
}

if( cmdline->getFlag( "help" )
|| cmdline->getFlag( 'h' ) ) {
cmdline->printUsage2();
return NOERROR;
}

string file_path;
if( cmdline->getValue( 'd' ) != NULL 
|| cmdline->getValue( "db" ) != NULL  ){
file_path = cmdline->getValue( 'd' );
}

cNotesDatabase db;

try{

db.open(file_path.c_str());

AddInLogMessageText(
MSG("DbTitle: %s\n"),
NOERROR, db.title().c_str());

// get ODS
NSFDbMajorMinorVersionGet(db.h, &major, &minor);
AddInLogMessageText(MSG("ODS: %d\n"), NOERROR, major);
if(major < 43) {
error = get_user_activity(db.h);
}
else {
error = get_user_activity_extended(db.h);
}

} catch (cNotesErr& err) {
delete cmdline;
db.close();
LAPI_RETURN (ERR(error));

} catch (...) {
delete cmdline;
db.close();
AddInLogMessageText(
"Unexpected Error\n", NOERROR);

LAPI_RETURN (ERR(error));
}

delete cmdline;
db.close();
LAPI_RETURN (ERR(error));
}

STATUS get_user_activity(DHANDLE hDb){

error = NSFDbGetUserActivity (hDb, 0L, &DbSummaryActivity,
&hDbUserActivity, &wUserCount);

if (error)
{
return error;
}

pDbUserActivity = OSLock (DBACTIVITY_ENTRY, hDbUserActivity);

for (i = 0; i < wUserCount; i++)
{
DbUserActivity = pDbUserActivity[i];
error = ConvertTIMEDATEToText (NULL, NULL, &(DbUserActivity.Time),
szTimedate, MAXALPHATIMEDATE, &wLen);
if (error)
{
OSUnlock (hDbUserActivity);
OSMemFree (hDbUserActivity);
return error;
}

szTimedate[wLen] = '\0';
szUserName = NSFDbGetActivityUserNamePtr (pDbUserActivity, i);

if (DbUserActivity.Reads)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate, szUserName, DbUserActivity.Reads);
if (DbUserActivity.Writes)
AddInLogMessageText(
MSG("%s  %s wrote %d\n"),
NOERROR, szTimedate, szUserName,
DbUserActivity.Writes);
}

OSUnlock (hDbUserActivity);
OSMemFree (hDbUserActivity);

AddInLogMessageText("                 Uses         Reads        Writes\n", NOERROR);
AddInLogMessageText(
MSG("Last Day        %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivity.PrevDayUses,DbSummaryActivity.PrevDayReads,
DbSummaryActivity.PrevDayWrites);
AddInLogMessageText(
MSG("Last Week       %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivity.PrevWeekUses,DbSummaryActivity.PrevWeekReads,
DbSummaryActivity.PrevWeekWrites);
AddInLogMessageText(
MSG("Last Month      %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivity.PrevMonthUses,DbSummaryActivity.PrevMonthReads,
DbSummaryActivity.PrevMonthWrites);
lRptPeriod = TimeDateDifference (&(DbSummaryActivity.Last),
&(DbSummaryActivity.First));

if (lRptPeriod % (60*60*24))
lRptPeriod = lRptPeriod / (60*60*24) + 1;
else
lRptPeriod = lRptPeriod / (60*60*24);

AddInLogMessageText(
MSG("Last %lu days   %5d         %5d         %5d\n"),
NOERROR, lRptPeriod,
DbSummaryActivity.Uses,DbSummaryActivity.Reads,
DbSummaryActivity.Writes);

return error;
}

STATUS get_user_activity_extended(DHANDLE hDb){
error = NSFDbGetUserActivityExtended (
hDb, 0L,
&DbSummaryActivityExt,
&hDbUserActivityExt, &wUserCount);
if (error)
{
return error;
}

pDbUserActivityExt = OSLock (DBACTIVITY_ENTRY_EXTENDED, hDbUserActivityExt);

for (i = 0; i < wUserCount; i++)
{
DbUserActivityExt = pDbUserActivityExt[i];
error = ConvertTIMEDATEToText (NULL, NULL, &(DbUserActivityExt.Time),
szTimedate, MAXALPHATIMEDATE, &wLen);
if (error)
{
OSUnlock (hDbUserActivityExt);
OSMemFree (hDbUserActivityExt);
return error;
}

szTimedate[wLen] = '\0';
szUserName = NSFDbGetActivityUserNamePtr (pDbUserActivityExt, i);

if (DbUserActivityExt.AEDetails.DataAdds)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.DataAdds);

if (DbUserActivityExt.AEDetails.DataDeletes)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.DataDeletes);

if (DbUserActivityExt.AEDetails.DataReads)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.DataReads);

if (DbUserActivityExt.AEDetails.DataUpdates)
AddInLogMessageText(
MSG("%s  %s wrote %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.DataUpdates);

// NON-DATA
if (DbUserActivityExt.AEDetails.NonDataAdds)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.NonDataAdds);

if (DbUserActivityExt.AEDetails.NonDataDeletes)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.NonDataDeletes);

if (DbUserActivityExt.AEDetails.NonDataReads)
AddInLogMessageText(
MSG("%s  %s read %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.NonDataReads);

if (DbUserActivityExt.AEDetails.NonDataUpdates)
AddInLogMessageText(
MSG("%s  %s wrote %d\n"),
NOERROR, szTimedate,
szUserName, DbUserActivityExt.AEDetails.NonDataUpdates);
}

OSUnlock (hDbUserActivityExt);
OSMemFree (hDbUserActivityExt);

AddInLogMessageText("                 Uses         Reads        Writes        Adds\n", NOERROR);
AddInLogMessageText(
MSG("Last Day        %5d         %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivityExt.PrevDayUses,
DbSummaryActivityExt.PrevDayReads,
DbSummaryActivityExt.PrevDayUpdates,
DbSummaryActivityExt.PrevDayAdds);

AddInLogMessageText(
MSG("Last Week       %5d         %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivityExt.PrevWeekUses,
DbSummaryActivityExt.PrevWeekReads,
DbSummaryActivityExt.PrevWeekUpdates,
DbSummaryActivityExt.PrevWeekAdds);

AddInLogMessageText(
MSG("Last Month      %5d         %5d         %5d         %5d\n"),
NOERROR,
DbSummaryActivityExt.PrevMonthUses,
DbSummaryActivityExt.PrevMonthReads,
DbSummaryActivityExt.PrevMonthUpdates,
DbSummaryActivityExt.PrevMonthAdds);

lRptPeriod = TimeDateDifference (&(DbSummaryActivityExt.Last),
&(DbSummaryActivityExt.First));

if (lRptPeriod % (60*60*24))
lRptPeriod = lRptPeriod / (60*60*24) + 1;
else
lRptPeriod = lRptPeriod / (60*60*24);

AddInLogMessageText(
MSG("Last %lu days   %5d         %5d         %5d         %5d\n"),
NOERROR,
lRptPeriod,
DbSummaryActivityExt.Uses,
DbSummaryActivityExt.Reads,
DbSummaryActivityExt.Writes,
DbSummaryActivityExt.Adds);

return error;
}


Titel: Re: Zugriffe auf DB auslesen mit CLASSUserActivity
Beitrag von: eknori am 16.06.19 - 09:14:29
Die negativen Werte für die handles sind übrigens nicht das Problem. Ich habe es mit ähnlichem Code probiert, der die Werte für den FTIndex ausließt. Auch hier liefert das LockObject negative Werte, und der Code kackt nicht ab, wenn anschließend mit memcpy gearbeitet wird.