Working with MQSeries (4GL)

Posted by Fuelfire on 15-Oct-2015 02:02

Hello, everybody! 

I'm trying to open WebSphere MQ queuе through API function. I want to connect to the queue manager, open a specific queue, put in her message, get the message and then close and disconnect. I installed the WebSphere MQ client 7.5 and downloaded the manual "MQSeries Application Programming Interface".

I wrote a program:

&SCOPED-DEFINE MQCC_OK 0
&SCOPED-DEFINE MQRC_NONE 0
&SCOPED-DEFINE MQOO_INPUT_EXCLUSIVE 4
&SCOPED-DEFINE MQOO_OUTPUT 16
&SCOPED-DEFINE MaxMsgLength 104 857 600
&SCOPED-DEFINE DLL-LIB mqic32.dll
&SCOPED-DEFINE MQCO_NONE 0
&SCOPED-DEFINE MQCNO_VERSION_2 2

DEFINE VARIABLE QMgrName AS CHAR FORMAT "X(48)" NO-UNDO. /* Name of queue manager */

DEFINE VARIABLE Hconn AS INT64 NO-UNDO. /* Connection handle */
DEFINE VARIABLE CompCode AS INT64 NO-UNDO. /* Completion code */
DEFINE VARIABLE Reason AS INT64 NO-UNDO. /* Reason code qualifying CompCode */

DEFINE VARIABLE CharString AS CHARACTER.
DEFINE VARIABLE StrLength AS INT.

DEFINE VARIABLE ObjDesc AS MEMPTR NO-UNDO.
DEFINE VARIABLE ConnectOpts AS MEMPTR NO-UNDO.
DEFINE VARIABLE ClientConnPtr AS MEMPTR NO-UNDO.
DEFINE VARIABLE Hobj AS INT NO-UNDO.
DEFINE VARIABLE MsgDesc AS MEMPTR NO-UNDO.
DEFINE VARIABLE GetMsgOpts AS MEMPTR NO-UNDO.
DEFINE VARIABLE PutMsgOpts AS MEMPTR NO-UNDO.
DEFINE VARIABLE Buffer_ AS CHAR NO-UNDO.                         /*Area to contain the message data*/
DEFINE VARIABLE DataLength AS INT NO-UNDO.

/*connecting*/
PROCEDURE MQCONN EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT  PARAMETER QName AS CHAR NO-UNDO.              /*Name of queue manager*/
    DEFINE OUTPUT PARAMETER Hconn AS LONG NO-UNDO.              /*Connection handle*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG NO-UNDO.    /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG NO-UNDO.          /*Reason code qualifying CompCode*/
END PROCEDURE.
/*connecting with connection options*/
PROCEDURE MQCONNX EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT  PARAMETER QName AS CHAR NO-UNDO.                                      /*Name of queue manager*/
    DEFINE INPUT-OUTPUT  PARAMETER ConnectOpts AS MEMPTR NO-UNDO. /*Options that control the action of MQCONNX.*/
    DEFINE OUTPUT PARAMETER Hconn AS LONG NO-UNDO.                                     /*Connection handle*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG NO-UNDO.                           /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG NO-UNDO.                                 /*Reason code qualifying CompCode*/
END PROCEDURE.
/*opening*/
PROCEDURE MQOPEN EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT PARAMETER Hconn AS LONG NO-UNDO.                                   /*Connection handle*/
    DEFINE INPUT-OUTPUT PARAMETER ObjDesc AS MEMPTR NO-UNDO.  /*Object descriptor*/
    DEFINE INPUT PARAMETER OPTIONS_ AS LONG NO-UNDO.                        /*Options that control the action of MQOPEN*/
    DEFINE OUTPUT PARAMETER Hobj AS LONG NO-UNDO.                               /*Object handle*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG NO-UNDO.                 /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG NO-UNDO.                        /*Reason code qualifying CompCode*/
END PROCEDURE.
/*get the message*/
PROCEDURE MQGET EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT PARAMETER Hconn AS LONG NO-UNDO.                           /*Connection handle*/
    DEFINE INPUT PARAMETER Hobj AS LONG NO-UNDO.                              /*Object handle*/
    DEFINE INPUT-OUTPUT PARAMETER MsgDesc AS MEMPTR NO-UNDO.  /*message descriptor*/
    DEFINE INPUT-OUTPUT PARAMETER GetMsgOpts AS MEMPTR NO-UNDO.  /*Options that control the action of MQGET*/
    DEFINE INPUT PARAMETER BufferLength AS LONG NO-UNDO.                        /*Length in bytes of the Buffer area*/
    DEFINE OUTPUT PARAMETER Buffer_ AS CHAR NO-UNDO.                         /*Area to contain the message data*/
    DEFINE OUTPUT PARAMETER DataLength AS LONG NO-UNDO.             /*Length of the message*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG NO-UNDO.             /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG NO-UNDO.                   /*Reason code qualifying CompCode*/
END PROCEDURE.
/*put the message*/
PROCEDURE MQPUT EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT PARAMETER Hconn AS LONG NO-UNDO.                           /*Connection handle*/
    DEFINE INPUT PARAMETER Hobj AS LONG NO-UNDO.                              /*Object handle*/
    DEFINE INPUT-OUTPUT PARAMETER MsgDesc AS MEMPTR NO-UNDO.  /*message descriptor*/
    DEFINE INPUT-OUTPUT PARAMETER PutMsgOpts AS MEMPTR NO-UNDO.  /*Options that control the action of MQPUT*/
    DEFINE INPUT PARAMETER BufferLength AS LONG NO-UNDO.                        /*Length in bytes of the Buffer area*/
    DEFINE INPUT PARAMETER Buffer_ AS CHAR NO-UNDO.                         /*Area to contain the message data*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG NO-UNDO.             /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG NO-UNDO.                   /*Reason code qualifying CompCode*/
END PROCEDURE.
/*closing*/
PROCEDURE MQCLOSE EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT PARAMETER Hconn AS LONG NO-UNDO.                           /*Connection handle*/
    DEFINE INPUT-OUTPUT PARAMETER Hobj AS LONG NO-UNDO.         /*Object handle*/
    DEFINE INPUT PARAMETER OPTIONS_ AS LONG NO-UNDO.                   /*Options that control the action of MQCLOSE*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG NO-UNDO.           /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG NO-UNDO.                  /*Reason code qualifying CompCode*/
END PROCEDURE.
/*disconnecting*/
PROCEDURE MQDISC EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT-OUTPUT PARAMETER Hconn AS LONG.        /*Connection handle*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG.             /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG.                   /*Reason code qualifying CompCode*/
END PROCEDURE.

ASSIGN QMgrName = "UNIQM".

SET-SIZE(ConnectOpts) = 104857.
SET-SIZE(ClientConnPtr) = 104857.

ASSIGN CharString = "ChannelName=~"EXTMQUSER.SVRCONN.CH~""
                  StrLength = LENGTH(CharString).
PUT-STRING(ClientConnPtr,1) = CharString.

ASSIGN CharString = "StrucId = 'CNOb'"
                  StrLength = LENGTH(CharString).
PUT-STRING(ConnectOpts,1) = CharString.
ASSIGN CharString = "Version = 1 ".
PUT-STRING(ConnectOpts,StrLength + 1) = CharString.
ASSIGN StrLength = StrLength + 1 + LENGTH(CharString)
                   CharString = "Options = 0 ".
PUT-STRING(ConnectOpts,StrLength + 1) = CharString.
ASSIGN StrLength = StrLength + 1 + LENGTH(CharString)
                  CharString = "ClientConnOffset = 0".
PUT-STRING(ConnectOpts,StrLength + 1) = CharString.



/*
RUN MQCONNX (QMgrName,
                  INPUT-OUTPUT ConnectOpts,
                  OUTPUT Hconn,
                  OUTPUT CompCode,
                  OUTPUT Reason).*/
/*Connect to certain queue manager*/
RUN MQCONN (QMgrName,
                  OUTPUT Hconn,
                  OUTPUT CompCode,
                  OUTPUT Reason).

IF ((CompCode = {&MQCC_OK} ) AND (Reason = {&MQRC_NONE}))
THEN DO:
    MESSAGE "+++ MQCONN() ended OK!" VIEW-AS ALERT-BOX.
END.
ELSE DO: /* report reason and exit */
    MESSAGE "--- MQCONN() ended with Completion Code "
        (IF CompCode = -1 THEN "MQCC_UNKNOWN" ELSE (IF CompCode = 0 THEN "MQCC_OK" ELSE (IF CompCode = 1 THEN "MQCC_WARNING" ELSE "MQCC_FAILED")))
        "~nReason code " STRING(Reason) VIEW-AS ALERT-BOX.
    LEAVE.
END.

/*Open a query*/
RUN MQOPEN (HConn,
                  INPUT-OUTPUT ObjDesc,
                  /*{&MQOO_INPUT_EXCLUSIVE},*/
                  {&MQOO_OUTPUT},
                  OUTPUT Hobj,
                  OUTPUT CompCode,
                  OUTPUT Reason).

IF ((CompCode = {&MQCC_OK} ) AND (Reason = {&MQRC_NONE}))
THEN DO:
    MESSAGE "+++ MQOPEN() ended OK!" VIEW-AS ALERT-BOX.
END.
ELSE DO: /* report reason and exit */
    MESSAGE "--- MQOPEN() ended with Completion Code " STRING(CompCode) "~nReason code " STRING(Reason) VIEW-AS ALERT-BOX.
    LEAVE.
END.

And also create and initialize system environment variable MQServer.

I call this program from Windows 7 and faced with problem when calling MQOPEN. I must initialize memptr parameter ObjDesc. I have no thought how to do it. This parameter is a structure:

C declaration
typedef struct tagMQOD {
MQCHAR4 StrucId; /* Structure identifier */
MQLONG Version; /* Structure version number */
MQLONG ObjectType; /* Object type */
MQCHAR48 ObjectName; /* Object name */
MQCHAR48 ObjectQMgrName; /* Object queue manager name */
MQCHAR48 DynamicQName; /* Dynamic queue name */
MQCHAR12 AlternateUserId; /* Alternate user identifier */
MQLONG RecsPresent; /* Number of object records present */
MQLONG KnownDestCount; /* Number of local queues opened successfully
*/
MQLONG UnknownDestCount; /* Number of remote queues opened successfully
*/
MQLONG InvalidDestCount; /* Number of queues that failed to
open */
MQLONG ObjectRecOffset; /* Offset of first object record from
start of MQOD */
MQLONG ResponseRecOffset; /* Offset of first response record
from start of MQOD */
MQPTR ObjectRecPtr; /* Address of first object record */
MQPTR ResponseRecPtr; /* Address of first response record */
MQBYTE40 AlternateSecurityId; /* Alternate security identifier */
MQCHAR48 ResolvedQName; /* Resolved queue name */
MQCHAR48 ResolvedQMgrName; /* Resolved queue manager name */
} MQOD;

Maybe someone initialized similar parameters and give an example of how to do it? How should I pass the name of the field structure and its value? Something like that?

SET-SIZE(ObjDesc) = 104857.
ASSIGN CharString = "StrucId = 'ODbb'"
                  StrLength = LENGTH(CharString).
PUT-STRING(ObjDesc,1) = CharString.
ASSIGN CharString = "Version = 1 ".
PUT-STRING(ObjDesc,StrLength + 1) = CharString.
ASSIGN StrLength = StrLength + 1 + LENGTH(CharString)
                   CharString = "ObjectType = 1 ".
PUT-STRING(ObjDesc,StrLength + 1) = CharString.
ASSIGN StrLength = StrLength + 1 + LENGTH(CharString)
                  CharString = "ObjectName = ~"TEST.Q~"".
PUT-STRING(ObjDesc,StrLength + 1) = CharString.

I would be grateful for any help. Thanks!

Posted by Garry Hall on 15-Oct-2015 10:18

You have to determine the offset and length for each field of MQOD you are trying to set, then use the appropriate PUT-* function to write it. I don't have the MQ include files to figure this out, but Google gave me a little help. MCHAR4 is a 4-byte datatype (char[4]). MQLONG is a 4 byte integer. If you wanted to set StrucId to "ODbb" and Version to 1, you would do the following:

PUT-STRING(ObjDesc,1) = "ODbb".  /* StrucId is at offset 0 */

PUT-LONG(ObjDesc, 5) = 1.    /* Version starts at offset 4, because StrucId is 4 bytes */

For historical reasons, OpenEdge MEMPTR offsets are 1-based, not 0-based. This might also require an understanding of datatype alignment, and possibly an endianness considerations of the API. Once you start calling DLLs, you have to do a lot of the heavy lifting yourself.

All Replies

Posted by Garry Hall on 15-Oct-2015 10:18

You have to determine the offset and length for each field of MQOD you are trying to set, then use the appropriate PUT-* function to write it. I don't have the MQ include files to figure this out, but Google gave me a little help. MCHAR4 is a 4-byte datatype (char[4]). MQLONG is a 4 byte integer. If you wanted to set StrucId to "ODbb" and Version to 1, you would do the following:

PUT-STRING(ObjDesc,1) = "ODbb".  /* StrucId is at offset 0 */

PUT-LONG(ObjDesc, 5) = 1.    /* Version starts at offset 4, because StrucId is 4 bytes */

For historical reasons, OpenEdge MEMPTR offsets are 1-based, not 0-based. This might also require an understanding of datatype alignment, and possibly an endianness considerations of the API. Once you start calling DLLs, you have to do a lot of the heavy lifting yourself.

Posted by Fuelfire on 16-Oct-2015 01:31

Garry, thanks a lot! Now I will rewrite initialization of MEMPTR ObjDesc and report the results later.

If I understand correctly, I consistently pass the value structure field without field names. But how to build the correct sequence? How I can be pretty sure that firstly I must pass StructId and then Version, but not vice versa? Apparently, I have to focus on the sequence of fields, which is described in the section "Initial values of fields in MQOD"?

In this table, the first field is StructId, then Version, Object Type, ObjectName, ObjectQMgrName and so on. Is it right? Thanks.

Posted by Fuelfire on 16-Oct-2015 04:25

When open the queue I get an error 2044 (Object descriptor structure not valid). I guess I'm wrong pass Pointer parameters (MQPTR). How to pass parameters of type MQPTR?

Here my code, which initialize object descriptor:

SET-SIZE(ObjDesc) = 350 /*104857*/.
SET-SIZE(ObjectRecPtr) = 4.
SET-SIZE(ResponseRecPtr) = 4.

ASSIGN CharString = "ODbb" /*StructId (MQCHAR4), 4-byte: MQOD_STRUC_ID*/
                StrLength = LENGTH(CharString).
PUT-STRING(ObjDesc,1) = CharString.

PUT-LONG(ObjDesc,StrLength + 1) = 1. /*Version (MQLONG), 4-byte: MQOD_VERSION_1*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 1. /*ObjectType (MQLONG), 4-byte: MQOT_Q*/

ASSIGN StrLength = StrLength + 4
                CharString = "TEST.Q".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*ObjectName (MQCHAR48), 48-byte*/

ASSIGN StrLength = StrLength + 48
                CharString = "".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*ObjectQMgrName (MQCHAR48), 48-byte*/

ASSIGN StrLength = StrLength + 48
                CharString = "AMQ.*".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*DynamicQName (MQCHAR48), 48-byte*/

ASSIGN StrLength = StrLength + 48
                CharString = AlternateUserId.
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*AlternateUserId (MQCHAR12), 12-byte*/

ASSIGN StrLength = StrLength + 12.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*RecsPresent (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*KnownDestCount (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*UnknownDestCount (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*InvalidDestCount (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*ObjectRecOffset (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*ResponseRecOffset (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = GET-POINTER-VALUE(ObjectRecPtr). /*ObjectRecPtr (MQPTR), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = GET-POINTER-VALUE(ResponseRecPtr). /*ResponseRecPtr (MQPTR), 4-byte*/

ASSIGN StrLength = StrLength + 4
                CharString = "".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*AlternateSecurityId (MQBYTE40), 40-byte: MQSID_NONE*/

ASSIGN StrLength = StrLength + 40
                CharString = "".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*ResolvedQName (MQCHAR48), 48-byte*/

ASSIGN StrLength = StrLength + 48
                CharString = "".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*ResolvedQMgrName (MQCHAR48), 48-byte*/

/*Open a query*/
RUN MQOPEN (HConn,
                  INPUT-OUTPUT ObjDesc,
                  /*{&MQOO_INPUT_EXCLUSIVE},*/
                  {&MQOO_OUTPUT},
                  OUTPUT Hobj,
                  OUTPUT CompCode,
                  OUTPUT Reason).

Posted by Garry Hall on 16-Oct-2015 07:35

Your post didn't mention the processor architecture, 32-bit vs 64-bit. I am assuming 32-bit at the moment, based on your code, which means a pointer is 4 bytes. If you are using 64-bit, then a pointer is 8 bytes (use PUT-INT64 instead, and adjust later offsets accordingly).

There is no need to build the structure in sequence, but if that helps you (just incrementing between each element), that is fine.

Are the ObjectRecPtr and ResponseRecPtr objects correctly populated? I would guess the API is expecting these to contain valid data as well.

At this point, I think your best bet would be using an example from another language and mapping that to ABL.

Posted by Fuelfire on 16-Oct-2015 07:37

I solved this problem. The reason was StruсtId parameter. It must be initialized as follows:

ASSIGN CharString = "OD  ".

instead "ODbb" as described in the MQ API manual.

This is the final version (does not work MQGET and MQPUT yet). I hope this example will help someone else.[;)]

/*Usual sequence is:
MQ Connect (queue manager name)
   MQ Open (queue name)
   MQ Put / Get (message)
   MQ Close (q)
 MQ Disconnect (qm)
 */
 
&SCOPED-DEFINE MQCC_OK 0
&SCOPED-DEFINE MQRC_NONE 0
&SCOPED-DEFINE MQOO_INPUT_EXCLUSIVE 4
&SCOPED-DEFINE MQOO_OUTPUT 16
&SCOPED-DEFINE MaxMsgLength 104 857 600
&SCOPED-DEFINE DLL-LIB mqic32.dll
&SCOPED-DEFINE MQCO_NONE 0

DEFINE VARIABLE QMgrName AS CHAR FORMAT "X(48)" NO-UNDO. /*Name of queue manager*/
DEFINE VARIABLE AlternateUserId AS CHAR FORMAT "X(12)" NO-UNDO. /*Alternate user identifier.*/
DEFINE VARIABLE ObjectRecPtr AS MEMPTR NO-UNDO.
DEFINE VARIABLE ResponseRecPtr AS MEMPTR NO-UNDO.

DEFINE VARIABLE CharString AS CHARACTER.
DEFINE VARIABLE StrLength AS INT.

DEFINE VARIABLE Hconn AS INT64 NO-UNDO. /* Connection handle */
DEFINE VARIABLE CompCode AS INT64 NO-UNDO. /* Completion code */
DEFINE VARIABLE Reason AS INT64 NO-UNDO. /* Reason code qualifying CompCode */

DEFINE VARIABLE ObjDesc AS MEMPTR NO-UNDO.
DEFINE VARIABLE Hobj AS INT NO-UNDO.
DEFINE VARIABLE MsgDesc AS MEMPTR NO-UNDO.
DEFINE VARIABLE GetMsgOpts AS MEMPTR NO-UNDO.
DEFINE VARIABLE PutMsgOpts AS MEMPTR NO-UNDO.
DEFINE VARIABLE Buffer_ AS CHAR NO-UNDO.                         /*Area to contain the message data*/
DEFINE VARIABLE DataLength AS INT NO-UNDO.

/*connecting to queue manager*/
PROCEDURE MQCONN EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT  PARAMETER QName AS CHAR NO-UNDO.              /*Name of queue manager*/
    DEFINE OUTPUT PARAMETER Hconn AS LONG NO-UNDO.              /*Connection handle*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG NO-UNDO.    /*Completion code: MQCC_OK - Successful completion.*/
    DEFINE OUTPUT PARAMETER Reason AS LONG NO-UNDO.          /*Reason code qualifying CompCode*/
END PROCEDURE.
/*opening the queue*/
PROCEDURE MQOPEN EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT PARAMETER Hconn AS LONG NO-UNDO.                             /*Connection handle*/
    DEFINE INPUT-OUTPUT PARAMETER ObjDesc AS MEMPTR NO-UNDO.  /*Object descriptor*/
    DEFINE INPUT PARAMETER OPTIONS_ AS LONG NO-UNDO.                      /*Options that control the action of MQOPEN*/
    DEFINE OUTPUT PARAMETER Hobj AS LONG NO-UNDO.                           /*Object handle - õýíäë î÷åðåäè*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG NO-UNDO.             /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG NO-UNDO.                   /*Reason code qualifying CompCode*/
END PROCEDURE.
/*getting the message from queue*/
PROCEDURE MQGET EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT PARAMETER Hconn AS LONG NO-UNDO.                           /*Connection handle*/
    DEFINE INPUT PARAMETER Hobj AS LONG NO-UNDO.                              /*Object handle - õýíäë î÷åðåäè*/
    DEFINE INPUT-OUTPUT PARAMETER MsgDesc AS MEMPTR NO-UNDO.  /*message descriptor*/
    DEFINE INPUT-OUTPUT PARAMETER GetMsgOpts AS MEMPTR NO-UNDO.  /*Options that control the action of MQGET*/
    DEFINE INPUT PARAMETER BufferLength AS LONG NO-UNDO.                        /*Length in bytes of the Buffer area*/
    DEFINE OUTPUT PARAMETER Buffer_ AS CHAR NO-UNDO.                         /*Area to contain the message data*/
    DEFINE OUTPUT PARAMETER DataLength AS LONG NO-UNDO.             /*Length of the message*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG NO-UNDO.             /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG NO-UNDO.                   /*Reason code qualifying CompCode*/
END PROCEDURE.
/*putting the message in queue*/
PROCEDURE MQPUT EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT PARAMETER Hconn AS LONG NO-UNDO.                           /*Connection handle*/
    DEFINE INPUT PARAMETER Hobj AS LONG NO-UNDO.                              /*Object handle - õýíäë î÷åðåäè*/
    DEFINE INPUT-OUTPUT PARAMETER MsgDesc AS MEMPTR NO-UNDO.  /*message descriptor*/
    DEFINE INPUT-OUTPUT PARAMETER PutMsgOpts AS MEMPTR NO-UNDO.  /*Options that control the action of MQPUT*/
    DEFINE INPUT PARAMETER BufferLength AS LONG NO-UNDO.                        /*Length in bytes of the Buffer area*/
    DEFINE INPUT PARAMETER Buffer_ AS CHAR NO-UNDO.                         /*Area to contain the message data*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG NO-UNDO.             /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG NO-UNDO.                   /*Reason code qualifying CompCode*/
END PROCEDURE.
/*closing the queue*/
PROCEDURE MQCLOSE EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT PARAMETER Hconn AS LONG NO-UNDO.                           /*Connection handle*/
    DEFINE INPUT-OUTPUT PARAMETER Hobj AS LONG NO-UNDO.         /*Object handle - õýíäë î÷åðåäè*/
    DEFINE INPUT PARAMETER OPTIONS_ AS LONG NO-UNDO.                   /*Options that control the action of MQCLOSE*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG NO-UNDO.           /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG NO-UNDO.                  /*Reason code qualifying CompCode*/
END PROCEDURE.
/*disconnecting from queue manager*/
PROCEDURE MQDISC EXTERNAL "{&DLL-LIB}" CDECL PERSISTENT:
    DEFINE INPUT-OUTPUT PARAMETER Hconn AS LONG.        /*Connection handle*/
    DEFINE OUTPUT PARAMETER CompCode AS LONG.             /*Completion code*/
    DEFINE OUTPUT PARAMETER Reason AS LONG.                   /*Reason code qualifying CompCode*/
END PROCEDURE.

ASSIGN QMgrName = "UNIQM"
       AlternateUserId = "extmqusr".

/*Connect to certain queue manager*/
RUN MQCONN (QMgrName,
                  OUTPUT Hconn,
                  OUTPUT CompCode,
                  OUTPUT Reason).

IF ((CompCode = {&MQCC_OK} ) AND (Reason = {&MQRC_NONE}))
THEN DO:
    MESSAGE "+++ MQCONN() ended OK!" VIEW-AS ALERT-BOX.
END.
ELSE DO: /* report reason and exit */
    MESSAGE "--- MQCONN() ended with Completion Code "
        (IF CompCode = -1 THEN "MQCC_UNKNOWN" ELSE (IF CompCode = 0 THEN "MQCC_OK" ELSE (IF CompCode = 1 THEN "MQCC_WARNING" ELSE "MQCC_FAILED")))
        "~nReason code " STRING(Reason) VIEW-AS ALERT-BOX.
    IF Reason <> 2002
    THEN LEAVE.
END.

SET-SIZE(ObjDesc) = 350.
SET-SIZE(ObjectRecPtr) = 4.
SET-SIZE(ResponseRecPtr) = 4.

ASSIGN CharString = "OD  " /*StructId (MQCHAR4), 4-byte: MQOD_STRUC_ID*/
                StrLength = LENGTH(CharString).
PUT-STRING(ObjDesc,1) = CharString.

PUT-LONG(ObjDesc,StrLength + 1) = 1. /*Version (MQLONG), 4-byte: MQOD_VERSION_1*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 1. /*ObjectType (MQLONG), 4-byte: MQOT_Q*/

ASSIGN StrLength = StrLength + 4
                CharString = "TEST.Q".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*ObjectName (MQCHAR48), 48-byte*/

ASSIGN StrLength = StrLength + 48
                CharString = "".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*ObjectQMgrName (MQCHAR48), 48-byte*/

ASSIGN StrLength = StrLength + 48
                CharString = "AMQ.*".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*DynamicQName (MQCHAR48), 48-byte*/

ASSIGN StrLength = StrLength + 48
                CharString = AlternateUserId.
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*AlternateUserId (MQCHAR12), 12-byte*/

ASSIGN StrLength = StrLength + 12.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*RecsPresent (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*KnownDestCount (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*UnknownDestCount (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*InvalidDestCount (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*ObjectRecOffset (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = 0. /*ResponseRecOffset (MQLONG), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = GET-POINTER-VALUE(ObjectRecPtr). /*ObjectRecPtr (MQPTR), 4-byte*/

ASSIGN StrLength = StrLength + 4.
PUT-LONG(ObjDesc,StrLength + 1) = GET-POINTER-VALUE(ResponseRecPtr). /*ResponseRecPtr (MQPTR), 4-byte*/

ASSIGN StrLength = StrLength + 4
                CharString = "".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*AlternateSecurityId (MQBYTE40), 40-byte: MQSID_NONE*/

ASSIGN StrLength = StrLength + 40
                CharString = "".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*ResolvedQName (MQCHAR48), 48-byte*/

ASSIGN StrLength = StrLength + 48
                CharString = "".
PUT-STRING(ObjDesc,StrLength + 1) = CharString. /*ResolvedQMgrName (MQCHAR48), 48-byte*/

/*Open a query*/
RUN MQOPEN (HConn,
                  INPUT-OUTPUT ObjDesc,
                  /*{&MQOO_INPUT_EXCLUSIVE},*/
                  {&MQOO_OUTPUT},
                  OUTPUT Hobj,
                  OUTPUT CompCode,
                  OUTPUT Reason).

IF ((CompCode = {&MQCC_OK} ) AND (Reason = {&MQRC_NONE}))
THEN DO:
    MESSAGE "+++ MQOPEN() ended OK!" VIEW-AS ALERT-BOX.
END.
ELSE DO: /* report reason and exit */
    MESSAGE "--- MQOPEN() ended with Completion Code " 
        (IF CompCode = -1 THEN "MQCC_UNKNOWN" ELSE (IF CompCode = 0 THEN "MQCC_OK" ELSE (IF CompCode = 1 THEN "MQCC_WARNING" ELSE "MQCC_FAILED")))
        "~nReason code " STRING(Reason) VIEW-AS ALERT-BOX.
    
    RUN ClearMem.

    RUN MQDISC(INPUT-OUTPUT HConn,
            OUTPUT CompCode,
            OUTPUT Reason).

    IF ((CompCode = {&MQCC_OK} ) AND (Reason = {&MQRC_NONE}))
    THEN DO:
        MESSAGE "+++ MQDISC() ended OK!" VIEW-AS ALERT-BOX.
    END.
    ELSE DO: /* report reason and exit */
        MESSAGE "--- MQDISC() ended with Completion Code " 
            (IF CompCode = -1 THEN "MQCC_UNKNOWN" ELSE (IF CompCode = 0 THEN "MQCC_OK" ELSE (IF CompCode = 1 THEN "MQCC_WARNING" ELSE "MQCC_FAILED")))
            "~nReason code " STRING(Reason) VIEW-AS ALERT-BOX.
    END.
    LEAVE.
END.

/*Put the message in queue
RUN MQPUT (HConn, Hobj,
                  INPUT-OUTPUT MsgDesc,
                  INPUT-OUTPUT PutMsgOpts,
                  Buffer_,
                  DataLength,
                  OUTPUT CompCode,
                  OUTPUT Reason).
*/                  
/*
/*Get a message from queue*/
RUN MQGET (HConn, Hobj,
                  INPUT-OUTPUT MsgDesc,
                  INPUT-OUTPUT GetMsgOpts,
                  {&MaxMsgLength},
                  OUTPUT Buffer_,
                  OUTPUT DataLength,
                  OUTPUT CompCode,
                  OUTPUT Reason).
*/


/*Close  the queue*/
RUN MQCLOSE(HConn,
                                INPUT-OUTPUT Hobj,
                                {&MQCO_NONE},
                                OUTPUT CompCode,
                                OUTPUT Reason).

IF ((CompCode = {&MQCC_OK} ) AND (Reason = {&MQRC_NONE}))
THEN DO:
    MESSAGE "+++ MQCLOSE() ended OK!" VIEW-AS ALERT-BOX.
END.
ELSE DO: /* report reason and exit */
    MESSAGE "--- MQCLOSE() ended with Completion Code " 
            (IF CompCode = -1 THEN "MQCC_UNKNOWN" ELSE (IF CompCode = 0 THEN "MQCC_OK" ELSE (IF CompCode = 1 THEN "MQCC_WARNING" ELSE "MQCC_FAILED")))
            "~nReason code " STRING(Reason) VIEW-AS ALERT-BOX.
    RUN ClearMem.
    LEAVE.
END.

/*Disconnect  from queue manager*/
RUN MQDISC(INPUT-OUTPUT HConn,
                    OUTPUT CompCode,
                    OUTPUT Reason).

IF ((CompCode = {&MQCC_OK} ) AND (Reason = {&MQRC_NONE}))
THEN DO:
    MESSAGE "+++ MQDISC() ended OK!" VIEW-AS ALERT-BOX.
END.
ELSE DO: /* report reason and exit */
    MESSAGE "--- MQDISC() ended with Completion Code " 
            (IF CompCode = -1 THEN "MQCC_UNKNOWN" ELSE (IF CompCode = 0 THEN "MQCC_OK" ELSE (IF CompCode = 1 THEN "MQCC_WARNING" ELSE "MQCC_FAILED")))
            "~nReason code " STRING(Reason) VIEW-AS ALERT-BOX.
    RUN ClearMem.
    LEAVE.
END.

RUN ClearMem.

PROCEDURE ClearMem:
    SET-SIZE(ObjDesc) = 0.
    SET-SIZE(ObjectRecPtr) = 0.
    SET-SIZE(ResponseRecPtr) = 0.
END PROCEDURE.

Posted by Stefan Drissen on 16-Oct-2015 09:14

Beware that you are using 4-byte pointers - this will not work when using a 64-bit AVM.

Posted by Garry Hall on 16-Oct-2015 10:06

Thanks for posting the finished code. As to Stefan's observation, you would want to conditionalise the pointer lengths/values with PROCESS-ARCHITECTURE to allow it to handle both 32-bit and 64-bit platforms.

Posted by Fuelfire on 19-Oct-2015 05:22

Thanks for help and your advices. I'm coding in OpenEdge 10.1B. Therefore, it is not very important. But I'll remember your advice, if we buy a new version 11.5

Could you explain me please: how API understands what the structure field gets on the "other side"? Let's say I pass two values of type MQCHAR48: ObjectName and ObjectQMgrName. How does the interface understands that one of them is the name of the object (queue), and the other - the name of the manager and not vice versa?

It's important to understand for me, because when I use MQCONNX I have to pass a lot of structure parameters MQCD, many of which are not clear to me. And I do not know if I have to miss any of them. Thanks.

Posted by Garry Hall on 19-Oct-2015 07:36

The elements in the structure are sequential as per the definition, determined by offset and length of the prior element, and also by alignment rules. Each element has a known length, and based on that the offset of any element of the structure is known. The API knows that the ObjectName starts at offset X in the structure, and ObjectQMgrName starts at offset Y, according to the definition. it is up to the caller to create the data in the memptr in a way that matches this structure.

There are probably decent references online that explain the way structures are stored in memory. I would point you to the Wikipedia entries on "C structures" and "C structure alignment", but there are probably better tutorials. Someone in the OE community might have written a tool to assist structures, but OE does not provide this functionality by default. As I mentioned previously, once you start calling DLLs, you have to do the heavy lifting yourself.

Posted by Fuelfire on 19-Oct-2015 09:42

Thanks, Garry. Now it is clear for me.

This thread is closed