Hosting a VFP App


Author
Message
Troy Murphy
Troy Murphy
StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)
Group: Forum Members
Posts: 12, Visits: 1.1K

I am trying to host an VFP application (MDI application withing the _screen) in .NET.

What am I doing wrong below?  When I run this and step into the Process.Start method, everything works fine.  However, when I run the code without a break-point, the VFP Application is not hosted.

A second question is: What would be the best practice in managing the process?  In other words: I would like the hosted application to terminate when the .NET app is exited.  Currently, unless I close the VFP app 1st, it is left in the running process collection (task manager).

 

Private Sub DocMgrToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DocMgrToolStripMenuItem.Click

Dim AppPath As String = "c:\MyVfpAppFolder\"

Dim App2Run As String = "MyGreatFoxProApp.exe"

Dim loStart As New System.Diagnostics.ProcessStartInfo(AppPath + App2Run)

loStart.WindowStyle = Diagnostics.ProcessWindowStyle.Normal

'loStart.UseShellExecute = False

loStart.WorkingDirectory = AppPath

Dim oProcess As System.Diagnostics.Process = System.Diagnostics.Process.Start(loStart)

'oProcess.WaitForInputIdle(3000)

Do While oProcess.MainWindowHandle.ToInt32() = 0

Loop

Dim lnHandle As Integer = oProcess.MainWindowHandle.ToInt32()

SetParent(lnHandle, Me.Handle)

End Sub


StrataFrame Team
S
StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)
Group: StrataFrame Developers
Posts: 3K, Visits: 2.5K
So, the VFP process is started, but the _Screen never gets into the MDI container?

I think you need to change the second parameter in the SetParent() call from Me.Handle to Me.mdiContainer.Handle so that the screen gets hosted in the MDI container.  A basic .NET form does not have the necessary Win32 properties set for it to host other forms, so you have to have an MDI container. 

If you're taking a form and setting the IsMdiContainer property to True, then it's not really an MDI container... .NET adds an MdiClient control to the form for you... We have a private variable on the form that holds the MdiClient reference and then initialize it in the New().

_Mdi = New MdiClient()
_Mdi.BackColor = System.Drawing.Color.White
Me.Controls.Add(_Mdi)
Me.IsMdiContainer = True

When you call SetParent(), you then pass the _Mdi.Handle as the second parameter.


Troy Murphy
Troy Murphy
StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)
Group: Forum Members
Posts: 12, Visits: 1.1K
Ok, Making progress...  I hope you are patient with my total overwhelmed attempt to master .NET.

Actually the main problem was that I had to sleep the thread after the process.start (the loop watching the window handle did nothing but lock the application indefinitely).  Anyway, I can't help but think there must be a better way since the sleep command is flaky based on the speed of application launch.  Setting it too high is a significant performance loss - and an apparent interface freeze, but setting it too low and the VFP app does not become hosted.

A related issue is: I would like the VFP app to be in the proper state before making it visible.  Currently a lot of window activity is going on which is very ugly!  I tried to set the ProcessStartInfo instance to hidden, but that did nothing.

Another problem I am having is on the resize:  Can you tell me why I get scrollbars in the _MDI region when I resize the form?  How do I remove them?

Last, but not least (at least for now): What is the best way of having the hosted VFP app shutdown when the .NET form is closing?  Here I am killing the process, but I don't suspect that is the recommended way of doing this!

Public Class Form1

Declare Function ShowWindow Lib "user32" (ByVal hWnd As System.IntPtr, ByVal nCmdShow As Integer) As Boolean

Declare Function SetParent Lib "user32" (ByVal hWndChild As System.IntPtr, ByVal hWndNewParent As System.IntPtr) As System.IntPtr

Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As System.IntPtr) As Integer

Declare Function SetWindowPos Lib "user32" (ByVal hWnd As Integer, ByVal hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As Integer) As Boolean

Dim _Mdi As MdiClient

Dim _MdiClientHandle As Integer

Dim _Processes As Collection = New Collection()

 

Private Sub DocMgrToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DocMgrToolStripMenuItem.Click

Dim AppPath As String = "c:\MyVfpApp\"

Dim App2Run As String = "TheBestVfpAppEver.exe"

If Not _Processes.Contains(App2Run) Then

Dim loStart As New System.Diagnostics.ProcessStartInfo(AppPath + App2Run)

loStart.WorkingDirectory = AppPath

Dim oProcess As System.Diagnostics.Process = System.Diagnostics.Process.Start(loStart)

System.Threading.Thread.Sleep(5000)

_MdiClientHandle = oProcess.MainWindowHandle.ToInt32()

SetParent(_MdiClientHandle, _Mdi.Handle)

SetWindowPos(_MdiClientHandle, 0, 0, 0, _Mdi.Width, _Mdi.Height, &H20)

_Processes.Add(oProcess, App2Run)

Else

MsgBox(App2Run + " is already open.")

End If

End Sub

 

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

_Mdi = New MdiClient()

_Mdi.BackColor = System.Drawing.Color.White

Me.IsMdiContainer = True

Me.Controls.Add(_Mdi)

End Sub

 

Private Sub Form1_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing

For Each oProcess As System.Diagnostics.Process In _Processes

If Not oProcess.HasExited Then

oProcess.Kill()

End If

Next

End Sub

 

Private Sub Form1_SizeChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.SizeChanged

If _MdiClientHandle > 0 Then

SetWindowPos(_MdiClientHandle, 0, 0, 0, _Mdi.Width, _Mdi.Height, &H20)

End If

End Sub

End Class

Also: Why does my code sample insist on adding blank lines between each pasted line of code?


StrataFrame Team
S
StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)
Group: StrataFrame Developers
Posts: 3K, Visits: 2.5K
You will probably have to use that same loop to test on whether the VFP application is ready to host, but within the loop you can use Thread.SpinWait()... it's more efficient for pausing the current thread without chewing up processor cycles than sleeping or an empty loop is.

I believe we set the start position of our _Screen to be some set of coordinates that is assured to be off screen so that the form can popup and we can process some UI stuff on it before we move it to be hosted within the MDI window.  That should help remove some of the flickering and unsightlyness.

The problem is that the VFP _Screen is not exactly meant to be an MDI child.  So, when you resize the MDI container, the _Screen doesn't get resized.  We have to send a resize command over the UDP listener between the processes to resize the _Screen when the MDI container is resized.

And yes, we just Process.Kill() the VFP process when .NET exits.  We also have a listener on an error event from FoxPro so that if an unexpected error occurs within the VFP process, the .NET process knows that it needs to exit as well.

StrataFrame Team
S
StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)StrataFrame Developer (4.2K reputation)
Group: StrataFrame Developers
Posts: 3K, Visits: 2.5K
As for your code having the extra lines inserted, it's the silly Javascript-based HTML editor that this forum uses.  You can try to post your code within [codesnippet][/codesnippet] brackets, and that might help... otherwise, you might try posting it within the HTML view rather than the Design view.
Troy Murphy
Troy Murphy
StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)
Group: Forum Members
Posts: 12, Visits: 1.1K
I am trying to follow your lead in using the Dart UDP control to establish a communication channel between the .NET and the VFP application.  Please tell me what I am doing wrong here. 

I think the port is opened correctly since the Receive event fires, but nothing seem to happen!  The receive parameters (passed by reference) do not get populated.

This is the VFP snippet (Dart Control Receive Event):

lcString=SPACE(1024)

luRemoteIp=SPACE(40)

luRemotePort=1E9

llRetVlu=thisform.oleDartUdp.receive(@lcString,@luRemoteIp,@luRemotePort)

thisform.edit1.Value=lcString

The .NET Sender Method

Dim sendbuf As Byte() = Encoding.ASCII.GetBytes(Me.txtDataGram.Text.Trim())

Dim oUDP As UdpClient = New UdpClient()

Dim iRet As Int32 = oUDP.Send(sendbuf, sendbuf.Length, Me.txtIP.Text, Me.txtPort.Text)


Trent Taylor
Trent Taylor
StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)
Group: StrataFrame Developers
Posts: 6.6K, Visits: 6.9K
I went ahead and stopped and created the .NET with embedded VFP EXE sample.  You can get to it here:

http://forum.strataframe.net/Topic5903-17-1.aspx 

Let me know if you have any questions.  This should give you a great starting point.

Troy Murphy
Troy Murphy
StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)
Group: Forum Members
Posts: 12, Visits: 1.1K
Thanks!

I look forward to digesting it.  I think I was making progress, but I am sure your tecnhiques are much more robust and certainly better tested...

Troy Murphy
Troy Murphy
StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)StrataFrame Beginner (12 reputation)
Group: Forum Members
Posts: 12, Visits: 1.1K
Very impressive - I must say!  I wish there was a shortcut to learning the .NET stuff. 

Anyway, using your provided example I have been able to test most of the needed functionality, but I do have a question on the UDP communication:  If I send a message via MyMainForm (I made the UDP stuff public) in the sample CHILD .NET form as below - an "Object reference not set to an instance..." exception occurs at the "_UDP.Send(..." line in SendMessage method of UdpSession.vb and the _UDP object is null. 

Obviously I am trying to leverage the code from MyMainForm to pass messages to and from both applications.  Is this going about it the wrong way? 

MyMainForm._UDP.SendMessage(UDPMessageType.VisualFoxProCommand, "ChangeActiveCaseTo('" + CaseNo + "')")


Trent Taylor
Trent Taylor
StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)StrataFrame Developer (10K reputation)
Group: StrataFrame Developers
Posts: 6.6K, Visits: 6.9K
Hey Troy,

I am glad that you are getting this going.  I am sure that you have learned that there are some frustrations making the two talk, but at least there is a solution!

As for your question, it just looks to me like you are attempting to reference the _UDP variable before it has been referenced.

Generally you should never directly reference a variable in another class like this.  Instead of making the _UDP property public, you should leave this private and then create a public property that exposes this variable.  You can also make it shared so you do not have to have a direct reference to the main form.  This will also require that the variable is shared and will require all of your current referneces as well to reference the shared property rather than the internal variable.

Private Shared _UDP As UDPSession
Public Shared ReadOnly Property UDP As UDPSession
    Get
        '-- Make sure the UDP object has been created
        If _UDP Is Nothing
             _UDP = New UDPSession(...)
        End If
    End Get
End Property

If you choose not to make the property shared and are going to just pass over a reference to any form that references the UDP property then the property would look something like this:

Private _UDP As UDPSession
Public ReadOnly Property UDP As UDPSession
    Get
        '-- Make sure the UDP object has been created
        If _UDP Is Nothing
             _UDP = New UDPSession(...)
        End If
    End Get
End Property

GO

Merge Selected

Merge into selected topic...



Merge into merge target...



Merge into a specific topic ID...




Similar Topics

Reading This Topic

Login

Explore
Messages
Mentions
Search