Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / Interop / June 2004

Tip: Looking for answers? Try searching our database.

FoxPro public variable problem in multithreaded VB.NET app

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Brian Levitan - 21 Jun 2004 19:05 GMT
Hello,
  I am building a VB.NET form interface for a legacy Visual FoxPro
7.0 (VFP) DLL (which contains our business logic).  Basically, the
form instantiates multiple instances of the VFP object (via COM
interop) in separate threads.  I would like each VFP object to be
completely independent of the others.

  In the VFP DLL, we have VFP variables that are declared using the
"public" keyword.  As best as I can tell, VFP "public" variables have
the same scope as a variable declared as "shared friend" in VB.NET.

  MY PROBLEM is that, under our old VFP model these DLLS used to run
in several separate processes, so "public" variables were not in the
same memory space and would not conflict with each other.  But, now
that we have several instances running under different threads in the
same process, they are all interpreted as being the same instance.
This produces locking and overwrite errors.

  Is there any way of getting each instance declared in a separate
thread to avoid this conflict (maybe forcing a variable to be declared
in a completely new memory space, or something)?

  The only solution we have come up with so far is to recompile the
VFP DLL for as many threads as we could need, each with a different
name (VFPLegacyLogic1.DLL, VFPLegacyLogic2.DLL, VFPLegacyLogic3.DLL,
etc...), so that each variables is considered part of a different
assembly.  But, obviously this is hokey, inefficient, and limits the #
of threads we can run.

  I have attached a simplified example of our problem below
(StartExample, Method1, and Method2 are the functions of use in the
VB.NET code)...  you can see that setting the VFP "public" value in
one thread affects the instances value in the other thread.

  I cannot change the way our VFP "public" variable is accessed (ie.
THIS.gcString), but I can change the way it is declared (ie. some way
of declaring it with the .NET equivalent of "friend", but not
"shared".)

----VB.NET code----
Imports System.Threading
Public Class Form1
   Inherits System.Windows.Forms.Form

   Private components As System.ComponentModel.IContainer
   Friend WithEvents btnThread1 As System.Windows.Forms.Button

   Private Sub StartExample(ByVal sender As System.Object _
                , ByVal e As System.EventArgs) _
                  Handles btnThread1.Click
       Dim loThread1 As New Thread(AddressOf Method1)
       Dim loThread2 As New Thread(AddressOf Method2)

       loThread1.ApartmentState = ApartmentState.STA

       'start the first thread
       loThread1.Start()

       'loop until the first thread is suspended
       ' and has set the value to 1
       Do While Not loThread1.ThreadState = ThreadState.Suspended
       Loop

       loThread2.ApartmentState = ApartmentState.STA

       'start the second thread
       loThread2.Start()

       'loop until the second thread is suspended
       ' and has set the value to 2
       Do While Not loThread2.ThreadState = ThreadState.Suspended
       Loop

       'allow the first thread to finish
       loThread1.Resume()
       loThread1.Join()

       'allow the second thread to finish (does nothing)
       loThread2.Resume()
       loThread2.Join()
   End Sub

   Private Sub Method1()
       Dim loVFPTest1 As New vfpthreadtest.publicthreadvarClass

       'set the value
       loVFPTest1.SetValue("1")

       'suspend the thread
       ' will resume after Method2 sets the value to 2)
       Thread.CurrentThread.Suspend()

       'get the value (shows 2, because variable is VFP
       ' "public" and was set to 2 in the Method2)
       MsgBox("Thread1: " & loVFPTest1.GetValue())
   End Sub

   Private Sub Method2()
       Dim loVFPTest2 As New vfpthreadtest.publicthreadvarClass

       'get the value (shows 1, because variable is
       ' VFP "public" and was set to 1 in Method1)
       MsgBox("Thread2: " & loVFPTest2.GetValue())

       'set the value
       loVFPTest2.SetValue("2")

       'suspend the thread
       Thread.CurrentThread.Suspend()
   End Sub

   Public Sub New()
       MyBase.New()
       Me.btnThread1 = New System.Windows.Forms.Button
       Me.SuspendLayout()
       Me.btnThread1.Location = New System.Drawing.Point(8, 8)
       Me.btnThread1.Name = "btnThread1"
       Me.btnThread1.Size = New System.Drawing.Size(120, 32)
       Me.btnThread1.TabIndex = 0
       Me.btnThread1.Text = "Start"
       Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
       Me.ClientSize = New System.Drawing.Size(408, 272)
       Me.Controls.Add(Me.btnThread1)
       Me.Name = "Form1"
       Me.Text = "Form1"
       Me.ResumeLayout(False)
   End Sub

   Protected Overloads Overrides _
    Sub Dispose(ByVal disposing As Boolean)
       If disposing Then
           If Not (components Is Nothing) Then
               components.Dispose()
           End If
       End If
       MyBase.Dispose(disposing)
   End Sub
End Class
----END VB.NET code----

----VFP code (compiled using a VFP Project called "vfpthreadtest"----
DEFINE CLASS PublicThreadVarClass AS Custom

PROCEDURE Init
PUBLIC gcString

ENDPROC

PROCEDURE setvalue
LPARAMETERS tcValue

gcString = tcValue

ENDPROC

PROCEDURE getvalue
RETURN gcString

ENDPROC
ENDDEFINE
----END VFP code----

           Thanks in advance for any help that can be provided,
                     -Brian
Sietse Wijnker - 21 Jun 2004 20:34 GMT
Hi Brian,
There are a couple of things that strike me in your post, but first to give
the easy answer.

>As best as I can tell, VFP "public" variables have the same scope as a
variable declared as "shared friend" in VB.NET.
That's where you're wrong. public variables are variables with a global
scope.

As I read id I assume that you've compiled the VFP DLL ad a MT DLL?
That is generaly a good idea in aspect of performance, but this is actually
creating the problem you have. when you compile the dll as a single threaded
DLL, each instance of the COM-objects has its own VFP environment thus
giving you the behaviour you expect BUT in my opinion that's not the way you
want to do it because this can give you big problems in scalability and
performance.
A better way to make your COM-object have it's local 'public' variables
(like a 'shared friend') is to use a property on the COM-object.
>    I cannot change the way our VFP "public" variable is accessed (ie.
> THIS.gcString), but I can change the way it is declared (ie. some way
> of declaring it with the .NET equivalent of "friend", but not
> "shared".)
You can do this by letting the GetValue and SetValue method access the
property. You can even let it be created 'at runtime' using the AddProperty
method. This method is available for all baseclasses (starting from VFP6)

Furthermore: You're using a programmaticaly designed VFP OLEPUBLIC class.
The baseclass that you're using is a cusom. Starting form VFP6 SP3 a new
baseclass was added named 'Session'. This class was added specifically with
multithreaded COM objects in mind. When you're using fox data (and I assume
you do, what other reason would you have when you're using VB.NET to access
it?) it's a good idea to use it as a baseclass for your OLEPUBLIC classes
because it gives each object it's own datasession.

HTH,
Sietse Wijnker
Brian Levitan - 22 Jun 2004 22:03 GMT
> Hi Brian,
> There are a couple of things that strike me in your post, but first to give
[quoted text clipped - 4 lines]
> That's where you're wrong. public variables are variables with a global
> scope.

I assumed it was "shared friend" because when I compiled the VFP DLL
twice, as projects with different names, (and therefore in different
INTEROP namespaces), there was no overlap.  But, I think that may just
be a difference in our view of what constitutes an environment and is
not that important.

> As I read id I assume that you've compiled the VFP DLL as a MT DLL?
> That is generaly a good idea in aspect of performance, but this is actually
[quoted text clipped - 3 lines]
> want to do it because this can give you big problems in scalability and
> performance.

Actually, the VFP _IS_ compiled as a Single-Threaded DLL, which still
is giving us the overlapping environment problems.

> A better way to make your COM-object have it's local 'public' variables
> (like a 'shared friend') is to use a property on the COM-object.
[quoted text clipped - 13 lines]
> it?) it's a good idea to use it as a baseclass for your OLEPUBLIC classes
> because it gives each object it's own datasession.

I think instantiating the class as derived from the VFP Session class
is the way to go (at least with regard to keeping cursors from
overlapping).  But, we still have a problem with global.  What we have
decided to do to get around that is declare them instead as "PRIVATE"
which is available in methods called from whatever method the
"PRIVATE" variable is declared.  Of course, this will involve some
rewriting of our higher level functions, but is much more desirable
than rewriting all of our code to use properties. (We have many other
classes insantiated that refer to several PUBLIC variables and it
would be a MAJOR undertaking to change them all to receive properties,
something I did not show in the simplified example I included).

I DO wonder why the Single-Threaded DLL seems to share a session
between the VFP objects instantiated in different (STA) threads.

> HTH,
> Sietse Wijnker
Brian Levitan - 28 Jun 2004 20:33 GMT
> > As I read id I assume that you've compiled the VFP DLL as a MT DLL?
> > That is generaly a good idea in aspect of performance, but this is actually
[quoted text clipped - 6 lines]
> Actually, the VFP _IS_ compiled as a Single-Threaded DLL, which still
> is giving us the overlapping environment problems.

...
> I DO wonder why the Single-Threaded DLL seems to share a session
> between the VFP objects instantiated in different (STA) threads.

 After much further testing.  I've observed that EXACT OPPOSITE
EFFECT that regarding the environments of Multi-Threaded vs.
Single-Threaded DLLs.  That is, MT DLLs have a separate environment
for each instance declared in .NET (and therefore no PUBLIC var
overlap), Single-Threaded DLLs HAVE overlap.  All I had to do to get
my app working was work through a few other MT DLL issues.

 The session variable stuff did work though.  (Not that we will need
it for an MT DLL).

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.