/* @(#) $Id: //bas/710_REL/src/krn/rfc/rfcpump.h#1 $ SAP*/ #ifndef _RFC_PUMP_H_ #define _RFC_PUMP_H_ #include "saprfc.h" /********************************************************* @doc @module RFC and the Windows Message Pump | This section describes the precautions which must be taken when writing RFC programs which runs under Windows. The information given here applies to Windows programs under Win16 (Windows 3.0, Windows for Workgroup) as well as under Win32 (Windows 95, Windows NT) which have a Windows. It does not apply to single threaded console applications written under Win32. Authors from multithreaded Win32 might want to read the classification of the RFC-API-calls. @head3 1. Problem description | If a long lasting calls is issued in the main thread of a Windows programs, the thread remains blocked in the RFC-API-call and cannot serves it's Windows message queue. On Win16, this blocks the entire machine until the RFC-API-call returns On Win32, the Windows application cannot repaint. If a Windows is moved above the non pumping applications, the non pumping applications gets white patches. On Win32, a deadlock may also occurs if an other application sends a message via SendMessage() to the unserviced Windows This problems are ignored most of the time since most of the RFC-API-call return quickly. A SAP system is expected to respond within 2 seconds so that the problems are transients and ignored. There are however noticeable exception to this: @flag 1)| Some Function Modules runs significantly longer than 2 seconds. @flag 2)| Function Modules which uses the SAPGUI (RFC with dialog) returns only when the dialog or the transaction is left. Depending on the user, this can take any amount of time. @flag 3)| RFC server API calls (RfcDispatch(), RfcGetName(),..) returns only when a request is made by a client program. @head3 2. Solutions | The RFC-SDK cannot offer a generic solution to all users. It can only documents the issue and offer 2 simple implementations of a Windows Pump. You can build on this sample implementations and modify them to you needs. @head3 2.1 Use threads. | This is the prefered solution for RFC-Server programs. This is the CPU savy solution. You can design your Windows Program from the ground up take advantage of Windows threads capabilities. Have a thread servicing the UI. And have other threads performing the work and thus performing the RFC calls. This solution requires threading issues to be considered from the begining of the project. @head3 2.2 Use an AbortProc.| This is the easiest solution to implement. Because the programs polls on 2 resources (the TCP-IP socket and the Windows Message Queue) this solutions consumes CPU in the idle mode. This technique is an time honored solutions known since the Win16 ages It is described extensively in Petzold's Programming Windows 3.1 in Chapter 15: Using the printer. In the Win32 community, the technique is rarely used. Since each application has it's own Message Queue, repain problems are less frequent. But also on Win32 some tasks such as printing or RFC calls take to long and the message queue must be serviced. One find a sample of the technique in MFC sources in the CPrintingDialog class. This solution as the advantage to easily added to existing projects. is a minimal pump implementation. is a more sophisticate pump with a progress/cancel dialog. @head3 2.3 Classification of the RFC-API-Calls.| @group Most RFC-API calls are blocking calls: @comm Listed here are the RFC-API calls which read from the network. They all have the potential to be long lasting calls. However most of this call generally returns fast. While other (RfcCallReceive, RfcReceive, RfcGetName, RfcDispatch) have really unpredictable response time. This is particularly true if you are using RFC with dialog. You should consider the techniques described here mainly when using these 4 API calls. @flag | opens an RFC connection including SAP logon @flag | Calls an ABAP/4 function module. (blocking only on large data volume) @flag | Receives the return values from an ABAP/4 function module. @flag | Calling a function module and receiving the return values in one step. @flag | Gets a unique Transaction-ID for calling an ABAP/4 function module using the transactional RFC Interface. @flag | Calls an ABAP/4 function module using transactional RFC Interface in R/3 (implicit confirmation). @flag | Calls an ABAP/4 function module using transactional RFC Interface in R/3 ( (explicit confirmation). @flag | Confirms a transaction called by RfcIndirectCallEx. @flag | accepting a RFC request or registering at a SAP gateway. @flag | waiting for the next function call. @flag | receiving the parameters of a function (blocking only on large data volume) @flag | raising an exception. @flag | reading the symbolic function name. @flag | reading the symbolic function name. @flag | check for registered RFC server at a SAP gateway @flag | cancel all registered RFC servers at a SAP gateway @flag | wait for incoming RFC requests. Only available after in register mode. (blocking the specified amount of time) @group Some RFC-API calls are non blocking calls: @flag | registering functions as callable function modules for transactional RFC. @flag | getting the (ON or OFF) @flag | getting the SNC name of the RFC client @flag | getting the SNC ACL key of the RFC client @flag | listen for incoming RFC events. @flag | get some information about this RFC connection @flag | define a structure for working with inhomogeneous structure or internal table in an RFC client or server program. @flag | convert an SNC name in an SNC ACL key @flag | convert an SNC ACL key in an SNC name **********************************************************************/ #ifdef __cplusplus extern "C" { #endif /*--------------------------------------------------------------------- * @func int | RfcPump | Sample simple Windows pump for RFC calls * * This is a sample implementation. * Change it to suite your requirements * * Compile and link .../rfcsdk/text/rfcpump.c to your program. * * @group * This is a sample implementation. * Change it to suite your requirements * * Compile and link .../rfcsdk/text/rfcpump.c to your program. * * @comm * Call RfcPump() between and * or before or . * * If you do only one RFC Function module call * in a client program you can pass you main Window handle as *

. RfcPump() will disable the Windows durring it's run * preventing re-entrancy problems in your application. If you * are performing several clients call to SAP systems, then you will * want to call EnableWindows(hWndMain, FALSE); before the calls * and EnableWindows(hWmdMain, TRUE); when all the RFC calls * have been carried out. * * If you are an RFC server program or if you have a modeless dialog box * on the screen, the RfcPump will monitor the address passed by

. * if it changes to != 0, the pump will stop and return a negative value. * * The function presented here attempts to cover most simples cases * while remaining simple. Likely enhancements are: * - Timeout parameter * - A Sleep() to reduce CPU drain * - Additional parameters for Accelerator (TranslateAccelerator()) * which could be required in some RFC-server implementation. * But those RFC-server are expected to have a multithreaded * architecture. And thus will not use this simple function. * RFC-Clients should not need accelerators since the Windows should * disabled during the call to RfcPump. * * Because the programs polls on 2 resources (the TCP-IP socket and the * Windows Message Queue) this solutions consumes CPU in the idle mode. * * * @ex Using RfcPump(): | * * * //assume hRfc is an open handle * * RFC_PARAMETER importing[1], * exporting[2]; * RFC_TABLE tables[1]; * memset(importing,0,sizeof(importing)); * memset(tables, 0,sizeof(tables)); * * memset(exporting,0,sizeof(exporting)); * exporting[0].name = "TCODE"; * exporting[0].nlen = 5; * exporting[0].type = RFCTYPE_CHAR; * exporting[0].addr = "SE38"; * exporting[0].leng = 4; * * RFC_RC rfc_rc = RfcCall(hRfc, * "ABAP4_CALL_TRANSACTION", * exporting, * tables * ); * if (RFC_OK != rfc_rc) * { * MyRfcError(); * RfcClose(hRfc); * return rfc_rc; * } * * int rc = RfcPump(g_hWndMain, hRfc, NULL); * if (rc < 0) * { * //user pressed CANCEL * RfcClose(hRfc); * hRfc = RFC_HANDLE_NULL; * return RFC_SYS_EXCEPTION; * * } * else * rfc_rc = (RFC_RC)rc; * * * if (RFC_OK == rfc_rc && RFC_HANDLE_NULL != hRfc) * { * char *exception = NULL, * rfc_rc = RfcReceive(hRfc,importing, tables, &exception); * if (RFC_OK != rfc_rc) * { * MyRfcError(); * RfcClose(hRfc); * return rfc_rc; * } * } * * @rdesc Continue or abort * @flag if = 0 | RFC_RC as returned from * @flag if 0 | Abort has been requested *-------------------------------------------------------------------- */ int RfcPump( HWND hWndMain, /* @parm Main Windows, may be NULL */ RFC_HANDLE handle, /* @parm the RFC connection performing * the long lasting call */ int *piAbort /* @parm Abort if *piAbort != 0. * Pass a NULL pointer if not needed */ ); #ifdef __cplusplus } #endif #ifdef __cplusplus // // @enum RFCP_PUMPSTATE | cached dialog state, // // used to reduce IsVisible and SendMessage trafic // // @xref typedef enum tagRFCP_PUMPSTATE { RFCP_INIT, //@emem initial, no dialog RFCP_CREATED, //@emem Dialog created, hidden RFCP_VISIBLE, //@emem Dialog Visible RFCP_CANCEL, //@emem Dialog with cancel button RFCP_ERROR //@emem Error, dialog cannot be created } RFCP_PUMPSTATE; // // @enum RFCP_DIALOG_MODE | dialog creation mode // // @xref typedef enum tagRFCP_DIALOG_MODE { RFCPDM_DELAYED, //@emem create dialog only when needed RFCPDM_IMEDIATE //@emem create dialog immediately } RFCP_DIALOG_MODE; //dialog proc extern BOOL CALLBACK RfcPumpDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); //--------------------------------------------------------------------- // @class CRfcPump | a simple Windows Message Pump for RFC Program // // @group // This is a sample implementation. // Change it to suite your requirements // // Compile and link .../rfcsdk/text/rfcpumpd.cpp to your program. // @comm // // This class provides the same basic / PeekMessage // loop as and additionally provides a CancelDialog // with a progress indicator. // // The class provide for severall scenario: // // The progress dialog is never shown // The progress dialog is shown after a timeout // The progress dialog is shown immediately with/without cancel button // The cancel button is enable after a timeout // // // Likely changes to this code would be: // To strip it down to the scenario which you want to implement. // Adding a Sleep() to reduce CPU drain. // // @ex Using CRfcPump: | // // RFC_PARAMETER importing[1], // exporting[2]; // RFC_TABLE tables[1]; // memset(importing,0,sizeof(importing)); // memset(tables, 0,sizeof(tables)); // // memset(exporting,0,sizeof(exporting)); // exporting[0].name = "TCODE"; // exporting[0].nlen = 5; // exporting[0].type = RFCTYPE_CHAR; // exporting[0].addr = "SE38"; // exporting[0].leng = 4; // // RFC_RC rfc_rc = RfcCall(hRfc, // "ABAP4_CALL_TRANSACTION", // exporting, // tables // ); // if (RFC_OK != rfc_rc) // { // MyRfcError(); // RfcClose(hRfc); // return rfc_rc; // } // // CRfcPump rfc_pump(RFCPDM_DELAYED, // g_hInst, // IDD_DIALOG1, // g_hWndMain, // IDC_PROGRESS1, // 100, // 3); // // // int rc = rfc_pump.RfcPump(hRfc, 3000, 6000); // if (rc < 0) // { // //user pressed CANCEL // RfcClose(hRfc); // hRfc = RFC_HANDLE_NULL; // return RFC_SYS_EXCEPTION; // } // else // rfc_rc = (RFC_RC)rc; // // if (RFC_OK == rfc_rc && RFC_HANDLE_NULL != hRfc) // { // char *exception = NULL, // rfc_rc = RfcReceive(hRfc,importing, tables, &exception); // if (RFC_OK != rfc_rc) // { // MyRfcError(); // RfcClose(hRfc); // return rfc_rc; // } // } // // class CRfcPump { public: /* @access Public Members */ /* @cmember | CRfcPump | (RFCP_DIALOG_MODE dm, HINSTANCE hInst, UINT DlgID, HWND hWndParent, UINT idProgress = NULL, UINT uiRange = 100, UINT uiStep = 1) | * Construct with progress/cancel dialog. * Refer to for details */ CRfcPump(RFCP_DIALOG_MODE dm, HINSTANCE hInst, UINT DlgID, HWND hWndParent, UINT idProgress = NULL, UINT uiRange = 100, UINT uiStep = 1 ) ; /* @cmember Construct without progress/cancel dialog. * * If a cancel dialog is needed later on (on a timeout for example) * then should be called. */ CRfcPump() : m_bUserAbort(FALSE), m_hWndParent(0), m_hWndDlg(0), m_hWndDlgG(0), m_hWndProgress(0), m_State(RFCP_INIT), m_hInst(0), m_DlgID(0), m_idProgress(0), m_uiRange(100), m_uiStep(1) { } /* @cmember Distroy dialog. Enables the parent window. */ virtual ~CRfcPump() { if (!m_bUserAbort) { if (0 != m_hWndParent) EnableWindow(m_hWndParent, TRUE); if (0 != m_hWndDlg) DestroyWindow(m_hWndDlg); } } /*@cmember HWND | CreateCancelDialog | (HINSTANCE hInst, UINT DlgID, HWND hWndParent, UINT idProgress = NULL, UINT uiRange = 100, UINT uiStep = 1) | * Delayed dialog creation. */ HWND CreateCancelDialog(HINSTANCE hInst, UINT DlgID, HWND hWndParent, UINT idProgress = NULL, UINT uiRange = 100, UINT uiStep = 1 ); /*@cmember The inner Windows message pump * *Refer to for details */ BOOL Pump() ; /*@cmember The high level RFC-Windows pump. * *Refer to for details */ int RfcPump(RFC_HANDLE hRfc); /*@cmember int |RfcPump |(RFC_HANDLE hRfc, DWORD dwHidden, DWORD dwCancel)| * The high level RFC-Windows pump, with timeout. */ int RfcPump(RFC_HANDLE hRfc, DWORD dwHidden, DWORD dwCancel); /*@cmember Show the dialog which is initialy hidden.*/ void Show(BOOL b = TRUE); /*@cmember Enable the cancel button which is initially hidden.*/ void EnableCancel(void) ; /*@cmember Abort the pump.*/ BOOL SetAbort(BOOL b = TRUE) { return m_bUserAbort = b; } /*@cmember Retrieve abort state.*/ BOOL GetAbort(void) const { return m_bUserAbort; } /*@cmember Retrieve dialog state.*/ RFCP_PUMPSTATE GetState(void) const { return m_State; } /*@cmember Retrieve dialog handle.*/ HWND GetDialogHandle(void) const { return m_hWndDlgG; } protected: /* @access Protected Members */ friend BOOL CALLBACK RfcPumpDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); /*@cmember Close the pump.*/ void SetClosed(void) {m_hWndDlgG = NULL; m_State = RFCP_INIT;} private: BOOL m_bUserAbort; HWND m_hWndParent; // parent/owner window HWND m_hWndDlg; // dialog's HWND m_hWndDlgG; // dialog's Petzold's Global HWND m_hWndProgress; // Progress bar RFCP_PUMPSTATE m_State; // reduce IsVisible calls in the pump //cache for delayed creation HINSTANCE m_hInst; //instance, UINT m_DlgID; //dialog resource id UINT m_idProgress; //progress indicator id UINT m_uiRange; //progress indicator range UINT m_uiStep; //progress indicator stepping //do not copy a dialog => private and not defined CRfcPump(const CRfcPump&); CRfcPump & operator=(const CRfcPump&); }; //CRfcPump #endif /*c++*/ #endif /*!_RFC_PUMP_H_*/