CWBX .NET Message Queue

First of all i'm not an expert on as400 OS/command.
I building a prgm in.NET that would monitor DSPMSG MSGQ(QSYS/QSYSOPR)
I tryed to use cwbx.DataQueue object but an error is raised tell me that it can't found DQ QSYSOPR on Library QSYS. Ok now i know that a DataQueue is not a Message Queue ;-) .
But, i wonder if it i can use any other object on CWBX to retreive the content of this MSGQ ?
May be using cwbx.Program object ? if so how can  i retreive the output parameters to get the list of all messages ?

OS400 V5R2M0
VB.NET 2005
Windows 7

Thanks in advance.
Laurent .Asked:
Who is Participating?
tliottaConnect With a Mentor Commented:
At V5R2, it's harder to be sure. Is there a reason you never went to V5R3?

I could do some testing at V5R3, but I can't determine V5R2 details. At V5R3, I'd use Management Central to monitor disk utilization. I think it's reasonably similar for V5R2.

But at V5R3 (and possibly V5R2), the messages don't have to go to QSYSOPR. They can go to a number of message queues (or users). See the Change Service Attributes (CHGSRVA) command to see message queues configured for it, and see system value QSTGLOWACN to see if it is set to *CRITMSG or *REGFAC. The <Help> text for both will give some guidance. With QSYSOPR out of the way (or simply one of a list of queues), a number of message handling options are less intrusive.

Other options exist besides Management Central and service messaging. In some rare cases, you might even have the AS/400 use NET SEND to send messages over the Windows Messenger service. (Not "NET SEND" but an API that does the work.) Almost all local networks block Messenger messages at the routers, and it's disabled by default at workstations nowadays, but it can be handy for some simple uses.

Gary PattersonVP Technology / Senior Consultant Commented:
.NET isn't great for this task, if you ask me.  IBM provides great tools for Java in the JTOpen project and in the functionally identical IBM Toolbox for Java, so if that is an option, do that:

JTOpen project:

Dealing with messages without using the Java classes is a pain.  The native API (QMHRTVM) is rather complex:

There is a slightly less complex command that you can use called RCVMSG:

If Java isn't an option, post back and I can give you some pointers.  Just be aware that this isn't a beginner task except in Java.

- Gary
IMO, the first step should be to decide if it's a good idea. Generally, the QSYSOPR message queue is not an object that ought to be allocated to a process running off-system. That's true of most objects named starting with "Q*". 'Normal' user message queues would usually be no problem, but QSYSOPR is one that has special meaning to the system itself.

A far better project would be to create a process that runs on the AS/400 to act as middleware. It would do any send/receive operations against QSYSOPR and relay to your .NET process. Data queues would be an excellent communications method between .NET and intermediate function.

Having the intermediary would allow creating methods that could be accessed and controlled by other processes on the AS/400. It would give capability of telling the intermediary to relinquish control when it becomes necessary, as well as other potential actions. That might be much more difficult if a networking issue suspends control from the .NET side.

It's certainly possible to build a .NET function as you seem to want, though I don't know of a direct API for it. It's just potentially a troublesome object to allocate in the way it would need to be done.

Cloud Class® Course: C++ 11 Fundamentals

This course will introduce you to C++ 11 and teach you about syntax fundamentals.

Laurent .Author Commented:
@Tom, @ Gary
Thank you guys for your feedback.
Well, since i prefere not put my nose on a JAVA for the moment, i'm a little bit in the rush so that would be the last option to me, i'm not a native JAVA dev...yet.

May be i need to come back to my original reason why i did open this thread. We have a System iSerie 9406-800, with OS400 V5R2 running MOVEX 10A and DataMirror v2.3 (To a SQL Server DB). Our Storage(HDD) is full at 75%, we can't upgrage it without upgrade the OS, we can't upgrade the OS without upgrade MOVEX/DataMirror. Our group have a plan to switch to SAP in 2014 so the upgrade is not a solution. MOVEX 10A has some knowed bugs, since we do not have support anymore on MOVEX, we have to deal with the situation.
Problem is, some time to time, i have some MOVEX prg that generate some messages on QSYSOPR MSGQ that required a reply.
In order to be as much responsive as possible, i was looking for solution to trigger an alert each time such messages are queued.

IBM Support provide me with a CL prg to send SMTP msg for each critical message (i put the code in attachment for further reference). I didn't test it yet, i would need some help from IBM to test it.
As Tom said, a server side middleware looks be a better solution for the moment.
I will post the result of this prg once tests have been done.

Thanks again to both to pointing me to the right direction.

Laurent .Author Commented:
@Tom: Thanks for the reply, i will investigate this way too.
Laurent .Author Commented:
just see that the attachment didn't work on my post from Jan-21th, so here the code of the prg that monitor critial message and perform an action. NEED TO BE CUSTOMIZED.
@courtizy of IBM Vienam


/* Declare the month, day, etc.                                      */
             DCL        VAR(&MONTH) TYPE(*CHAR) LEN(2)
             DCL        VAR(&DAY) TYPE(*CHAR) LEN(2)
             DCL        VAR(&YEAR) TYPE(*CHAR) LEN(4)
             DCL        VAR(&TIME) TYPE(*CHAR) LEN(8)
             DCL        VAR(&HOUR) TYPE(*CHAR) LEN(2)
             DCL        VAR(&MIN) TYPE(*CHAR) LEN(2)
             DCL        VAR(&SEC) TYPE(*CHAR) LEN(2)

/* The variables used to get the values from retrieve system value   */
/*                                                                   */
/* The QDATETIME is returned as this according to the help text :    */
/* The format of the field is YYYYMMDDHHNNSSXXXXXX :                 */
/*  YYYY is the year                                                 */
/*  MM is the month                                                  */
/*  DD is the day                                                    */
/*  HH is the hours                                                  */
/*  NN is the minutes, SS is the                                     */
/*  SS is the seconds                                                */
/*  XXXXXX is the microseconds                                       */
/*                                                                   */
/* DATFMT is the format of the date such like  YMD, MDY, DMY, JUL    */
             DCL        VAR(&QDATETIME) TYPE(*CHAR) LEN(20)
             DCL        VAR(&QDATFMT) TYPE(*CHAR) LEN(3)

/* Misc variables                                                    */
             DCL        VAR(&HEADER) TYPE(*CHAR) LEN(3)
             DCL        VAR(&POS) TYPE(*DEC) LEN(3)
             DCL        VAR(&SEVERITY) TYPE(*CHAR) LEN(2)
             DCL        VAR(&SEV#) TYPE(*DEC) LEN(2)
             DCL        VAR(&HEADERFND) TYPE(*CHAR) LEN(1)

/* Declaring a file to import the output of the history log          */
             DCLF       FILE(QTEMP/QHIST)


             RTVSYSVAL  SYSVAL(QDATFMT) RTNVAR(&QDATFMT)                /* Retrieving date format */

             DLTF       FILE(QTEMP/QHIST)                               /* Recreating the temp file used */
             MONMSG     MSGID(CPF0000)                                  /* for importing the hist log    */

/* Here we are getting the datetime from retrieve system value       */
/* Then we break down the hours/mins/secs                            */
/* and we put it in a format of 'HH:MM:SS' to pass to the dsplog cmd */
             CHGVAR     VAR(&HOUR) VALUE(%SST(&QDATETIME 9 2))
             CHGVAR     VAR(&MIN) VALUE(%SST(&QDATETIME 11 2))
             CHGVAR     VAR(&SEC) VALUE(%SST(&QDATETIME 13 2))
             CHGVAR     VAR(&TIME) VALUE(&HOUR *CAT ':' *CAT &MIN *CAT ':' *CAT &SEC)

/* We dump the history log and will copy it over to our file that we */
/* declared, QHIST in library QTEMP                                  */
/* Then we delete the spoolfile                                      */
             DSPLOG     PERIOD((&TIME)) OUTPUT(*PRINT)

             MONMSG MSDID(CPF0000) EXEC(DO)                             /* OK here what can happen is we can   */
                CHGJOB     LOG(*SAME *SAME *NOLIST)                     /* get an error saying that we can't   */
                SBMJOB     CMD(call histmon) JOB(HISTMON) +             /* spool anymore, if we spool too many */
                          JOBQ(QSYSNOMAX)                               /* times. Submit another monitor job   */
                RETURN                                                  /* and end cleanly                     */


/* The way this works is we need to scan the history for the the word*/
/* SEV like in the below example. The SEV in                         */
/*                                                                   */
/* 5761SS1 V6R1M0 080215                                History Log  */
/*MSGID    SEV MSG TYPE                                              */
/*CPA4067  99  INQUIRY      Save file MCASE in MCASE already contains*/
/*                                                                   */
/* After we find the SEV header, we can now scan the file for that   */
/* position on the next lines for the severity level                 */
             CHGVAR VAR(&POS) VALUE(0)
             CHGVAR VAR(&HEADERFND) VALUE('0')
             MONMSG     MSGID(CPF0864) EXEC(GOTO EXITREAD)              /* End of file, exit to  */
                                                                        /* EXITREAD              */

             IF COND(&HEADERFND *EQ '0') THEN(DO)                       /* Find the header first */
                CHGVAR VAR(&POS) VALUE(&POS + 1)
                IF COND(&POS *GT 129) THEN(DO)
                   CHGVAR VAR(&POS) VALUE(0)
                   GOTO READF
                CHGVAR VAR(&HEADER) VALUE(%SST(&QHIST &POS 3))
                IF COND(&HEADER *EQ 'SEV') THEN(DO)
                   CHGVAR VAR(&HEADERFND) VALUE('1')
                   GOTO READF
                GOTO FINDHDR

             IF COND(&HEADERFND *EQ '1') THEN(DO)                       /* Now to check for severity on  */
                CHGVAR VAR(&SEVERITY) VALUE(%SST(&QHIST &POS 2))        /* the next lines of the histlog */
                CHGVAR VAR(&SEV#) VALUE(&SEVERITY)                      /* We convert the severity #     */
                MONMSG MSGID(CPF0000) EXEC(DO)                          /* character string to decimal   */
                   GOTO READF
                IF COND(&SEV# *GT 50) THEN(DO)                          /* In this example, if the sev      */
                  SNDMSG MSG('got it!!') TOUSR(NNGUYEN)                 /* is greater than 50, do something */

              GOTO READF                                                /* Continue reading the file */

/* Get the current time so we can just dump the history for next     */
/* round. There is a chance we might miss a message but it should    */
/* slim chance                                                       */
              CHGVAR     VAR(&HOUR) VALUE(%SST(&QDATETIME 9 2))
              CHGVAR     VAR(&MIN) VALUE(%SST(&QDATETIME 11 2))
              CHGVAR     VAR(&SEC) VALUE(%SST(&QDATETIME 13 2))
              CHGVAR     VAR(&TIME) VALUE(&HOUR *CAT ':' *CAT &MIN *CAT ':' *CAT &SEC)

              CLOSE                                                     /* Close our temp file so we can */
                                                                        /* open it up again              */
              DLYJOB DLY(60)
              GOTO DMPLOG


Open in new window

I'm not sure what that program is supposed to do. It has a couple questionable parts that need to be examined.

For example, at line 71, the history log is dumped to a spooled file. The entries that are dumped are those beginning at the time specified in variable &TIME and ending at the current time. But the value in &TIME is set by retrieving the QDATETIME system value and extracting the time portion.

That means that all entries beginning at the current time and ending at the current time (because ending time defaults there) are dumped. The result should almost always be a CPF2447 *ESCAPE message saying that no entries exist.

And the result of that would be triggering the MONMSG at line 73 because it checks for the 'template' value of CPF0000 rather than a spool limit message. That causes the program to submit itself to QSYSNOMAX to start over again. And the result of that should be the same thing happening over again.

Also, it is reviewing the history log rather than a service message queue. That might be okay if you are only interested in history log entries. And given your description of your goal, that might be good enough.

But it needs a lot of work. There are numerous other things that would need changing if it is the start of what you want to do.

I would investigate the alternatives before going this way.

I'd look into Management Central first. If that wasn't satisfactory, I'd create a custom message queue monitor. Either way, I'd have my function running against an alternative service message queue that was created specifically for this purpose.

Laurent .Author Commented:
Upgrade OS to V5R3 solved the issue
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.