You can open a PowerBuilder window from a C++ application or from an extension, but to make sure that events triggered in the window or control are processed, you need to make sure that the C++ application processes PowerBuilder messages. The IPB_Session ProcessPBMessage function lets you do this.
Each time the ProcessPBMessage function is called, it attempts to retrieve a message from the PowerBuilder message queue and process it. The function is similar to the PowerBuilder Yield function, which yields control to other graphic objects and pulls messages from PowerBuilder objects and other graphic objects from the queue. However, ProcessPBMessage processes only one message at a time, and it processes only PowerBuilder messages.
Messages are added to the PowerBuilder message queue when you call the PostEvent function.
ProcessPBMessage must be called repeatedly
You need to make sure that the ProcessPBMessage function is called repeatedly. For most C++ applications, you can provide a message loop in the main function and insert the IPB_Session ProcessPBMessage function in the message loop. This is shown in the example that follows.
If you use Microsoft Foundation Classes (MFC), you cannot modify the built-in message loop. To ensure that the ProcessPBMessage function is called repeatedly, you can overload the CWnd::WindowProc function and insert ProcessPBMessage into the overloaded function:
LRESULT CCallPBVCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { d_session->ProcessPBMessage(); return CDialog::WindowProc(message, wParam, lParam); }
The following code fragments are from a C++ program that opens a window. The window has a menu item that invokes the Open event of a PowerBuilder application.
Calling ProcessPBMessage
The call to ProcessPBMessage is in a loop in the WinMain function:
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; WNDCLASSEX wcex; // initialization code omitted ... RegisterClassEx(&wcex); HWND hWnd = CreateWindow(szWndClsName, "OpenPBWindow", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); try { while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); // Call to ProcessPBMessage if (session) session->ProcessPBMessage(); } } catch(...) { MessageBox(NULL, "Exception occurs", "Exception", MB_OK); } return msg.wParam; }
Loading the PBVM and triggering an event
In the WndProc function, when the WM_CREATE message is passed, the PBVM is loaded and the library list, containing openwin.pbl, is passed to CreateSession. When the user selects the menu item that opens the PowerBuilder window, the FindGroup, FindClass, and GetMethodID functions obtain the information needed to create a new application object, initialize the PBCallInfo structure, and trigger the application object's Open event:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; switch (message) { case WM_CREATE: { // Load the PBVM hPBVMInst = ::LoadLibrary("pbvm.dll"); P_PB_GetVM getvm = (P_PB_GetVM) GetProcAddress(hPBVMInst,"PB_GetVM"); IPB_VM* vm = NULL; getvm(&vm); // Define the library list and create the session static const char *liblist[] = {"openwin.pbl"}; vm-> CreateSession("openwin", liblist, 1, &session); break; } case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId) { case ID_PB_VISUAL: { // Initialize PBCallInfo and trigger the // application open event try { pbgroup group = session->FindGroup ("openwin", pbgroup_application); pbclass cls = session->FindClass(group, "openwin"); pbmethodID mid = session->GetMethodID (cls, "open", PBRT_EVENT, "QS"); pbobject obj = session->NewObject(cls); PBCallInfo ci; session->InitCallInfo(cls, mid, &ci); session->TriggerEvent(obj, mid, &ci); session->FreeCallInfo(&ci); } catch(...) { MessageBox(NULL, "Exception occurs", "Exception", MB_OK); } break; } default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); RECT rt; GetClientRect(hWnd, &rt); EndPaint(hWnd, &ps); break; case WM_DESTROY: session->Release(); session = NULL; FreeLibrary(hPBVMInst); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
Testing ProcessPBMessage
You can test the ProcessPBMessage function with a simple PowerBuilder application like this one:
-
Create a PowerBuilder application called openwin in openwin.pbl.
-
Create a main window, w_main, with three buttons.
-
Insert a window-level function, of_setcolor, that takes three integers as arguments and has this script:
this.backcolor = rgb(red,green,blue)
-
Insert a window-level user event, ue_test, with this script:
MessageBox("ue_test", "This is a user event")
-
Provide the following scripts for the clicked events of the buttons:
//cb_1: MessageBox("Button 1", "Clicked") parent.of_setcolor(255, 255, 0) //cb_2: MessageBox("Button 2", "Clicked") parent.PostEvent("ue_test") // not fired parent.of_setcolor(255, 0, 0) //cb_3: MessageBox("Button 3", "Clicked") cb_1.PostEvent(Clicked!) // not fired
-
Script the application's Open event:
open (w_main)
When the ProcessPBMessage function is included in the C++ application, the application runs from C++ as it does in PowerBuilder. The posted events in cb_2 and cb_3 are processed.
Now try commenting out these lines in the C++ application, recompiling, and running the application again:
if (session) session->ProcessPBMessage();
The message boxes still display (response windows have their own message loop) and the of_setcolor function is called, but the posted events do not fire.