on 09-18-2013 9:19 PM
I have a powerbuilder application and now we are taking part of this application and migrating them to our new
.net app. Our delimma is that we must use both applications at this time and in order to move back and forth
between these applications both applications must pass data between both apps to determine the modules user is
working with.
Our .net developers tell me that they have created a method in thier app that when there app is called, if app
is not already running it starts the app but if app is running the instance that is running somehow determines
this call to launch and then since it is already running does not start another instance of the app and simply
takes the parms passed via commandline and passes them to the instance of the app already running. (how they do that I have no idea because I am under the impression that even the .net app will fire the open event of the app (.net app is window based app) and will try to start a new instance why it tries to send this to an instance that is already running I am not sure but they claim it is happening)
Is this possible in my PB application? I know I can code the open event of my app and check for the main window
winhandle = FindWindow( 0, "w_main")
if I have defined the FindWindow fucntion from user32.dll as an external fucntion and if an instance is already
open then I can get the Commandline argument in the open event to get the commandline parms and not open another
instance but these commandline parms will be in the context of the second instance that was being strated.
The first instance already running have no clue about the commandline parms but I want these commandline parms
to be used in the first instance? is this even possible? does my question make sense? Please help.
Hi Javed:
I built a small test case that hopefully helps.
Here's the outline:
Global External functions:
PUBLIC FUNCTION unsignedlong FindWindow (long classname, string windowname) LIBRARY "user32.dll" ALIAS FOR FindWindowW
PUBLIC FUNCTION int SetForegroundWindow (unsignedlong hwnd) LIBRARY "user32.dll"
PB application Open:
unsignedlong hwnd
hwnd = FindWindow( 0, "MyPBApp")
if hwnd = 0 then
// No previous instance, so open the main window
open( w_myapp )
else
// Open the previous instance window and halt
SetForegroundWindow( hwnd )
// Save new command line in Registry
RegistrySet("HKEY_LOCAL_MACHINE\Software\MyPBApp","commandline", RegString!, commandline)
// Trigger pbm_custom01 event on currently open MyPBApp to read new commandline
Send(hwnd, 1024, 1, 0)
HALT CLOSE
end if
Define a user event on your main window (MyPBApp) mapped to pbm_custom01 populated with hte following Powerscript:
string commandline
RegistryGet("HKEY_LOCAL_MACHINE\Software\MyPBApp","commandline", RegString!, commandline)
MessageBox("New commandline", commandline)
I tested and it seems to work. Let me know if you need clarification.
Cheers...Bob
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I can tell without testing it that bob your idea will work great, however many thanks to scott before that for explaining, this is great. I know powerbuilder but I do not consider myself a guru but the advice I have received is definitely great advice, let me implement and I will report back.
Bob: I have not tried the solution u have suggested but I am surely going to try it since of all the suggestions I have this makes most sense in my case. I am going to put a proof of concept together using this approach and I will let you know how it goes, the only thing is that I am juggling so many different balls that this have to wait for right now.
Javed
Hi Bob: I knew I am going to end up asking u some questions about this, because my experience with using windows api function has not been very pleasant. As I expected findwindow(0,"w_main") is not bale to find this window even when it is open inside the exe. I am assuming that I will have to pass the same window to findwindow method that has my custom event coded in it since Send(hwnd, 1024, 1, 0) is the handle that I acquired via findwindow method. Is my understanding incorrect and do I have to pass something else to findwindow method, I doubt it but I am not sure what am I doing wrong. Thanks for your help.
Bob: never mind my previous question about findwindow method not working. I found out that the second parm passed to findwindow method must be the 'title' of the window and not the acutal name of the window which makes sense because api can probably find the title because title is an attribute of the window that stays valid through out the life of the object whereas the object name may only be internal to the program (it makes sense) and after I passed to window title the findwindow method was able to find the handle and hence I was able to call my custom event I wrote for that window and now everything is working seamlessley, thanks to you for your idea.
For talking to apps that are already running, DDE is an oldie but a goodie.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Instead of making apps that can send/receive data directly, have you considered a database table to be used as a queue for processing commands?
App A has a window (or object of some type) polling for data in a table (fifo order) every N seconds (or minutes). App B puts data into the table to be processed and it gets done when the timer hits.
Or possibly a combination of the two styles. App A won't process anything until it receives a message from App B that there's data to be processed. Then app A will process all pending records until none are left and stop processing until the next message comes in.
The only problem with using a database table is that both apps need to be connected to the same database.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
this is all good but I still do not understand how is my PB app going to know if a message has been sent from the .net app, even if I keep the window open and code an event in it, I guess i m just do not understand how sending a message form .net app will invoke anything in PB app?
I do agree that combination of two message/db table may be better but just a db table is not real practical since you will have to keep querying the db almost constantly.
Chris: Sorry, missed your DB idea before I posted min - if two of us came up with it, can't be all bad
To send a message between applications, you can use one of the ideas previously mentioned.
From App B you can get the handle of the processing app's window with FindWindow (by title or other way that can uniquely identify it). Use Send to send a message from App B to App A that triggers the processing.
Concept code only
App A - waiting window
event Other
if Message.Number = 4096 and wparam = 4096 and lparam = 4242 then
this.event post processdata()
end if
App B
insert process request into table
handle = FindWindow("App A Window name here")
If Handle > 0 then
Send(handle, 4096, 4096, 4242)
End If
Your PB app will receive a windows message in the same manner as it does if you were to execute the following PB code somewhere in your application:
w_main.event ue_open1()
You create an event in your window and associate it with one of the pbm_customxx event IDs. The particular event ID you choose determines the value passed in the send function as the message number. The 3rd and 4th arguments in the send function are the values that are passed as wparam and lparam in the event when it is executed in your PB app.
Your PB app already responds to windows events since it is a windows application - buried deep inside of the PB logic (that you cannot directly see) is a message queue and a message loop (aka pump) that receives messages in the queue and "calls" the appropriate application code.
To invoke this logic from another application (or even within your own PB app), you call the win api function to send a message to your PB app and passing the appropriate values for the parameters - window handle, message ID, wparam, lparam. Your app will define how it uses wparam and lparam (if at all) - they are just numeric values available for any purpose you want to give them.
The short answer is NO - you cannot provide information to a currently running processing via commandline parameters - at least according to the commonly understand definition of command line parameters. Your .net guys have likely implemented a method to "place" the appropriate information into a running application that is accessed (within the application) as the commandline parameters. I suggest you simply ignore "how" they did it in their app since that is mostly irrelevent.
The basic issue here is how to communicate (i.e., pass information) between running applications. This is called IPC - inter-process communication. A start in the MS documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365574(v=vs.85).aspx
Another very simply method is to get a handle to a window of the application and send messages to it (this has significant limitations) - this is what Chris mentioned. You will need to identify what information needs to be shared and how you want to share it in both directions.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Scott: thank you very much I actually replied to previous two posts while you were posting your answer and yes I get it now that I do have an option limited although it is to use sendmessage method of user32.dll. I have explained that in my reply but now I have also extended my question as how do I process this message that is sent to my window while window is not open. Basically is it possible to take action when the message is received by the window, specially if window is closed, or even if window is open new message to the qeue is not an event so it does not trigger anything, how do i make it trigger? is this even possible?
If you are using SendMessage, the event pbm_custom01 is eventid 1024, pbm_custom02 is 1025 and so on. So one app can find the window handle and send a message using the eventid that corresponds with the custom event you mapped on the window.
Another method would be to use Winsock. The main window of the PB app could listen on a port for messages from the other app.
And that is one of the many problems with this basic approach - you need a window that remains open (but not necessarily visible). So you need to change your application to do just that. To respond to that message, you create an event in the particular window associated with the message ID you are sending. In that event your application can then "respond" in any manner that is appropriate - by opening a window, creating a file, etc. You can use an event as both notification and data - that is you have one event to open window A, another to open window B, etc.
Scott/Ronlad: thank u very much for your response, this may be the solution to my problem it may be problem ridden but we will be using it only temporarily so I am willing to bite the bullet. Lets say that I do add a window to my pb app that remains open and sendmessage from our .net app will send a message to this window, i hope i understand what u both are trying to describe, and lets say that I do have a custom event coded that will respond to this message but will this event (on the window that stays open) execute on receiving a message from .net app. Because as I understand and based on online help on sendmessage function takes four parms (handle to the window, message, any message specific info, message specific info) based on these four parms I am not sure how the custom event that I will code to take further action will be invoked? am I missing something here? thanks
Hi,
You can send text messages Fom/To PowerBuilder and .Net applications using the window message WM_SETTEXT.
To do that, each application has to know a window control of the other one where to send the message.
I wrote a simple DLL as a gateway between the two application. (See Code Below.)
PB Application <---> PbDotNetInterface <---> .Net Application
The two applications have to register a Window control having the 'text' property to receive the message sent by the other application. The controls handles are stored in shared memory segment.
1-For Powerbuilder :
- Declare the external functions :
FUNCTION boolean registerControl (ulong hctl ) LIBRARY "Your_Path\DotNetToPB.dll" ALIAS FOR "registerPbControl;ansi"
FUNCTION boolean sendMessageToDotNet (ref string msg) LIBRARY "Your_Path\DotNetToPB.dll" ALIAS FOR "sendMessageToDotNet;utf8"
- To register for ex. a multiLineEdit control
boolean lb
lb = registerControl (handle (mle_1))
if lb THEN ..... //OK
- and you send the message to .Net like this :
sendMessageToDotNet(ref string)
2-For the .Net application, you declare the DLL function :
[DllImport("Your_Path\\DotNetToPB.dll", SetLastError=true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]static extern bool sendMessageToPB(string msg);
[DllImport("Your_Path\\DotNetToPB.dll", SetLastError=true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]static extern bool registerDotNetControl (Int32 hwnd);
- You register for example a textBox as this :
public Form1()
{
InitializeComponent();
registerDotNetControl (this.textBox1.Handle.ToInt32());
- And you send the string message to PB
sendMessageToPB(msg);
Hope this helps.
Best Regards.
Abdallah.
//-------------------DLL Code --------------------------
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _MANAGED
#pragma managed(push, off)
#endif
#pragma data_seg (".myseg")
static unsigned long hDotNetControl = 0;
static unsigned long hPbControl = 0;
static WCHAR msg [102400] = L" ";
#pragma data_seg()
#pragma comment(linker, "/section:.myseg,RWS")
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
WCHAR* getMessageFromPB ()
{
return msg;
}
bool sendMessageToPB (LPCWSTR msg)
{
if (hPbControl == 0) return false;
SendMessage((HWND) hPbControl, WM_SETTEXT, 0, (LPARAM) msg);
PostMessage((HWND) hPbControl, WM_ACTIVATE, 0, 0);
PostMessage((HWND) hPbControl, WM_NOTIFY, 0, 0);
return true;
}
bool registerPbControl (unsigned long hctl)
{
hPbControl = hctl;
return true;
}
bool registerDotNetControl (unsigned long hctl)
{
hDotNetControl = hctl;
return true;
}
bool sendMessageToDotNet (LPCWSTR msg)
{
if (hDotNetControl == 0) return false;
SendMessage((HWND) hDotNetControl, WM_SETTEXT, 0, (LPARAM) msg);
PostMessage((HWND) hDotNetControl, WM_ACTIVATE, 0, 0);
PostMessage((HWND) hDotNetControl, WM_NOTIFY, 0, 0);
return true;
}
#ifdef _MANAGED
#pragma managed(pop)
#endif
//----To Be Exported ----
EXPORTS
DllMain @1
registerPbControl @2
sendMessageToPB @3
sendMessageToDotNet @4
registerDotNetControl @5
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
OK: i get it, thanks to both you and Chris for your responses, I kept doing my research while I posed the question in the forum and yes there are functions at least in user32.dll like findwindow to find the handle and then you can use sendmessage method to send the message to a particular window both between .net and PB. As a matter of fact as Chris suggested i could use the send method in PB to send a message to any windnow as long as I have the handle for it. However my problem is that not only I want to send the message to the PB window from my .net app (which is not a problem now that I know about user32.dll fucntions) I actually want my PB app to open that window with a particular patient data if it is not already opened and process the data if it is already opened. I can retrieve the message from the message qeue only after I open the window but how do I invoke the window I am not sure if I can really do that?
Hi Javed;
Have you thought about using the PB "SEND" command?
Regards ... Chris
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
101 | |
13 | |
13 | |
11 | |
11 | |
7 | |
6 | |
5 | |
4 | |
4 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.