Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form
Go Back  Xtreme Visual Basic Talk > > > Object Reference not Visible in a Different Form


Reply
 
Thread Tools Display Modes
  #1  
Old 07-24-2011, 11:54 AM
stevenw1956 stevenw1956 is offline
Newcomer
 
Join Date: Jul 2011
Posts: 21
Default Object Reference not Visible in a Different Form


The problem with the following code is that the object oWB is visible and initialized in Form2, but when I show SaveDialog1, the object gives a NullReferenceException. I already know, because I checked in the debugger, that oWB has a valid reference when I leave Form2 and also when I come back from Form2. So I am sure the problem is that the reference simply is not visible in SaveDialog1 for some reason. So please don't tell me that the initialization of oWB returned a Nothing reference because I know it did not.

I get a NullReferenceException on the following line:

Form2.oWB.Save()

I researched this for a whole day and came up with a Microsoft article that explains that it is necessary to run each form of a .NET COM application on its own thread. It gave some code to accomplish this. I incorporated this code into my program. But I still have the NullReferenceException.

Can anyone explain how to fix this exception?

Simplified version of Code with most processing deleted below:

Public Class Form2
'Define Microsoft Excel Objects
Dim oXL As Excel.Application
Public oWB, oWBOld As Excel.Workbook
Friend testvar As String = "This is a test to see whether I can access this variable from SaveDialog1"
Dim frm As COMWinform.COMForm

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
' Start Excel and get Application object.
oXL = New Excel.Application()
oXL.Visible = True
' Open MainList Workbook and Create Worksheet Objects
oWB = oXL.Workbooks.Open("E:\My Documents\Excel Files\Michael Hughes\Test Folder\MainList.xls")
oWBOld = oXL.Workbooks.Open("E:\My Documents\Excel Files\Michael Hughes\Test Folder\OldFile.xls")
frm = New COMWinform.COMForm
frm.ShowForm1(SaveDialog1)
End Sub
End Class

Public Class SaveDialog1

Private Sub OK_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Save_Button.Click
'Debug.WriteLine("Value of testvar is " & Form1.testvar)
Form2.oWB.Save()
Form2.oWBOld.Save()
Form2.oWB.Saved = True
Form2.oWBOld.Saved = True
Me.DialogResult = DialogResult.OK
Me.Close()
End Sub

Private Sub Cancel_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Cancel_Button.Click
Me.DialogResult = DialogResult.Cancel
Me.Close()
End Sub
End Class

Imports System.Runtime.InteropServices
Imports System.Windows.Forms
<ComClass(COMForm.ClassId, COMForm.InterfaceId, COMForm.EventsId)> _
Public Class COMForm

#Region "COM GUIDs"
' These GUIDs provide the COM identity for this class
' and its COM interfaces. If you change them, existing
' clients will no longer be able to access the class.
Public Const ClassId As String = "4dd738c8-3ffa-43c3-a30f-9d265d85ca37"
Public Const InterfaceId As String = "bb66fc61-63af-4e7d-8363-66ec68bf7cd7"
Public Const EventsId As String = "29f63033-3afa-4fd1-a3ab-7765f2e88b78"
#End Region

' A creatable COM class must have a Public Sub New()
' with no parameters, otherwise, the class will not be
' registered in the COM registry and cannot be created
' via CreateObject.
Public Sub New()
MyBase.New()
End Sub

Private WithEvents frmManager As FormManager

'Public Sub ShowForm1()
' ' Call the StartForm method by using a new instance
' ' of the Form1 class.
' StartForm(New Form1)
'End Sub

Public Sub ShowForm1(ByVal Form1 As Form)
' Call the StartForm method by using a new instance
' of the Form1 class.
StartForm(Form1)
End Sub



Private Sub StartForm(ByVal frm As Form)

' This procedure is used to show all forms
' that the client application requests. When the first form
' is displayed, this code will create a new message
' loop that runs on a new thread. The new form will
' be treated as the main form.

' Later forms will be shown on the same message loop.
If IsNothing(frmManager) Then
frmManager = New FormManager(frm)
Else
frmManager.ShowForm(frm)
End If
End Sub

Private Sub frmManager_MessageLoopExit() _
Handles frmManager.MessageLoopExit

'Release the reference to the frmManager object.
frmManager = Nothing

End Sub
End Class

Imports System.Runtime.InteropServices
Imports System.Threading
Imports System.Windows.Forms

<ComVisible(False)> _
Friend Class FormManager
' This class is used so that you can generically pass any
' form that you want to the delegate.

Private WithEvents appContext As ApplicationContext
Private Delegate Sub FormShowDelegate(ByVal form As Form)
Event MessageLoopExit()

Public Sub New(ByVal MainForm As Form)
Dim t As Thread
If IsNothing(appContext) Then
appContext = New ApplicationContext(MainForm)
t = New Thread(AddressOf StartMessageLoop)
t.IsBackground = True
t.SetApartmentState(ApartmentState.STA)
t.Start()
End If
End Sub

Private Sub StartMessageLoop()
' Call the Application.Run method to run the form on its own message loop.
Application.Run(appContext)
End Sub

Public Sub ShowForm(ByVal form As Form)

Dim formShow As FormShowDelegate

' Start the main form first. Otherwise, focus will stay on the
' calling form.
appContext.MainForm.Activate()

' Create a new instance of the FormShowDelegate method, and
' then invoke the delegate off the MainForm object.
formShow = New FormShowDelegate( _
AddressOf ShowFormOnMainForm_MessageLoop)

appContext.MainForm.Invoke(formShow, New Object() {form})
End Sub

Private Sub ShowFormOnMainForm_MessageLoop(ByVal form As Form)
form.Show()
End Sub

Private Sub ac_ThreadExit( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles appContext.ThreadExit
appContext.MainForm.Dispose()
appContext.MainForm = Nothing
appContext.Dispose()
appContext = Nothing
RaiseEvent MessageLoopExit()
End Sub
End Class
Reply With Quote
  #2  
Old 07-25-2011, 10:35 AM
DrPunk's Avatar
DrPunkObject Reference not Visible in a Different Form DrPunk is offline
Senior Contributor

* Expert *
 
Join Date: Apr 2003
Location: Never where I want to be
Posts: 1,403
Default

I think this has something to do with how your referring to Form2.

Your class is called Form2, but this will not be the form that's being displayed. So when you make a call to Form2 it's not trying to talk to the Form2 that you're showing.

I'm not sure how best to explain. Maybe someone else can put it better.
__________________
There are no computers in heaven!
Reply With Quote
  #3  
Old 07-25-2011, 10:44 AM
DrPunk's Avatar
DrPunkObject Reference not Visible in a Different Form DrPunk is offline
Senior Contributor

* Expert *
 
Join Date: Apr 2003
Location: Never where I want to be
Posts: 1,403
Default

I don't know if this maybe helps explain what I'm trying to say.

I've got a project that contains 2 forms (Form1 and Form2) and a class (Class1).

Class1 has a method that changes the text of Form2, as your code appears to be using the form
Code:
Public Class1

    Sub DoSomething()
        Form2.Text = "Something"

        ' Confirm that Form2.Text is set
        messagebox.show(Form2.Text)
    End Sub
End Class
Form1 has a button on it. On clicking the button it's going to create a form2 then use the class to change the form's text
Code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim frm As New Form2

        frm.Show()

        Dim b As New Class1

        b.DoSomething()

        MessageBox.Show(frm.Text)
    End Sub
On running this Class1's DoSomething shows that it has changed Form1's text, but you can see from the loaded Form1 (frm in the click event) that the text hasn't changed on Form1.

Why? Because DoSomething doesn't change the loaded form's text. It's changing the text of something else (and I'm not too sure myself what it has changed).

To change Form2's text I need to refer to frm rather than Form2.

Such as...
Code:
Public Class1
    ' For class1 to work on the form I pass the form to it and refer to it
    ' in the routine
    Sub DoSomething(byval frm as Form2)
        frm.Text = "Something"

        ' Confirm that Form2.Text is set
        messagebox.show(frm.Text)
    End Sub
End Class
Code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim frm As New Form2

        frm.Show()

        Dim b As New Class1
        ' Pass the form to the routine so that it can work on it
        b.DoSomething(frm)

        MessageBox.Show(frm.Text)
    End Sub
So, ultimately, it's like VB's creating a new form for you to refer to Form2 as, but the one that it has created will not have a oWB set and that's why it's nothing. I think.
__________________
There are no computers in heaven!

Last edited by DrPunk; 07-25-2011 at 10:50 AM.
Reply With Quote
  #4  
Old 07-25-2011, 12:14 PM
stevenw1956 stevenw1956 is offline
Newcomer
 
Join Date: Jul 2011
Posts: 21
Default

I created Form2 by using "Add New Item" -> Windows Form. Do you see anywhere in my code where I created it again??? I sort of understand your example, but don't understand how it applies to my code.

When I tell you that I can display the value of testvar like this:

Debug.WriteLine("Value of testvar is " & Form2.testvar)

from SaveDialog1 without generating an exception, does that change your opinion?

When I read the following article from Microsoft:

Supporting COM Interop by Displaying Each Form in a Separate Thread
http://msdn.microsoft.com/en-us/libr...=vs.80%29.aspx

I thought I had found the answer. The article says there are known issues with Windows Forms not behaving correctly in a COM interop environment and that putting each one on a separate thread solves many of these problems. But it did not solve mine.

I modified the code slightly so that it would allow me to pass in the name of SaveDialog1, which I had already created. I removed the "New" keyword so that I would not be creating the form again.

In any case, I don't understand how to apply your theory to my code. I don't really understand the code I copied from the article above and incorporated into my program. Perhaps I created a problem by my changes. The code I changed is below. Can you be more specific about suggested changes to my code?

The original code is commented out, and the code with my changes is below it.

'Public Sub ShowForm1()
' ' Call the StartForm method by using a new instance
' ' of the Form1 class.
' StartForm(New Form1)
'End Sub

Public Sub ShowForm1(ByVal Form1 As Form)
' Call the StartForm method by using a new instance
' of the Form1 class.
StartForm(Form1)
End Sub

The code in Form2 that shows the dialog is:

frm.ShowForm1(SaveDialog1)

Thanks for your help.
Reply With Quote
  #5  
Old 07-25-2011, 12:23 PM
stevenw1956 stevenw1956 is offline
Newcomer
 
Join Date: Jul 2011
Posts: 21
Default

I admit that your theory makes intuitive sense. Do you think it could have something to do with the interop layer between my program and Excel (using COM) creating another copy of Form2, which is different from the one in my program?

Also, the fact that the string testvar (native .NET type) displays fine but oWB (COM object) does not seems to be a big clue. But I know almost nothing about the guts of the Runtime Callable Wrapper or PIA or whatever you call that intermediate layer between .NET and COM.
Reply With Quote
  #6  
Old 07-25-2011, 01:04 PM
PlausiblyDamp's Avatar
PlausiblyDampObject Reference not Visible in a Different Form PlausiblyDamp is offline
Ultimate Contributor

Forum Leader
* Expert *
 
Join Date: Nov 2003
Location: Newport, Wales
Posts: 2,058
Default

This is a "feature" of VB.Net were referring to a form by it's class name rather than by an instance variable VB will create a default instance of the form. This means id you do something as simple as
Code:
Dim f as new Form1
f.Show()
Form1.Text = "Testing"
you will see a new instance of Form1 appear and then no change will be made to it's caption - the final line of code will create an invisible instance of the form and make a change to the invisible caption.

In all honesty I have no idea why this feature was added in .Net 2 as it seems to cause far more problems and doesn't really seem to solve any existing problems either.
.
__________________
Intellectuals solve problems; geniuses prevent them.
-- Albert Einstein

Posting Guidelines Forum Rules Use the code tags
Reply With Quote
  #7  
Old 07-25-2011, 01:48 PM
stevenw1956 stevenw1956 is offline
Newcomer
 
Join Date: Jul 2011
Posts: 21
Default

So, ok, that's good to know. Actually, I am trying hard to understand how to apply it to my program. I have been coding in VB.NET only for 3-4 months. The original problem happened in the default Form1 that Visual Studio created when I created my Windows Form project. I created Form2 using Project->Add New Item -> Windows Form in order to create a simplified version of my program, with most processing deleted. that would be easier to understand I have not encountered a situation yet where I needed to create an instance variable for a form. Now I understand that VB.NET is creating an instance for me under the covers. But as a beginner, I am struggling to see how to apply what you explained to this particular situation. All the books I have studied show referring to the form by the Class name. I tried saying:

Public f2 as New Form2

within the declarations section of the Form2 class

and then saying

f2.oWB.Save()

in SaveDialog1

but that creates a syntax error saying f2 is not declared.

I tried declaring

Public f2 as New Form2

in SaveDialog1

But that doesn't get rid of the NullReferenceException.

So can you show me how to apply this to my actual code?
Reply With Quote
  #8  
Old 07-25-2011, 02:23 PM
stevenw1956 stevenw1956 is offline
Newcomer
 
Join Date: Jul 2011
Posts: 21
Default

I think the reason I am having trouble applying the given examples to my code is because in the examples there are three entities
Class1
Form1
Form2

But I have only:
Form2
SaveDialog1

Now Form2 is the base class. How am I going to create an instance of Form2from within Form2 in order to pass it to SaveDialog1 so the latter can work on it. That is my problem. What am I missing here?

I know it is probably all to obvious to you. But please try to remember what it was like when you were first starting to code.

Thanks
Reply With Quote
  #9  
Old 07-25-2011, 05:27 PM
stevenw1956 stevenw1956 is offline
Newcomer
 
Join Date: Jul 2011
Posts: 21
Default

I am continuing to research the problem. On the following page:

http://stackoverflow.com/questions/1...cannot-be-used

I find it said that one cannot access a COM object from a thread different from the one it was created on. This confuses me because of the Microsoft article I posted in an earlier post on this thread where I was advised to show each form on a separate thread. If both of these pieces of advice are true, it would seem both that one would not be able to access any COm objects on a different form, which is my problem.

Can help to clear up this confusion? Is it necessary to show each form on a separate thread? Is it then impossible to access any objects on a different form if one does so?

I actually took the multi-threading code out of the program and went back to trying to show SaveDialog1 using

SaveDialog1.Show()

When I did that I got an InvalidComObjectException on the line

Form2.oWB.Save()

saying the object had been separated from the Runtime Callable Wrapper.

Please is there anyone who understands what is going on and can explain to me clearly what I have to do to resolve it.

Thanks
Reply With Quote
  #10  
Old 07-26-2011, 05:25 AM
DrPunk's Avatar
DrPunkObject Reference not Visible in a Different Form DrPunk is offline
Senior Contributor

* Expert *
 
Join Date: Apr 2003
Location: Never where I want to be
Posts: 1,403
Default

It's difficult to suggest something because it's difficult to understand your code and work out how you are using the form.

But a couple of things. If you really want the save dialog to be saving the information in form2 then you need to somehow pass the form to the savedialog for the save dialog to work with. You could pass the form in the savedialog's Save method, or in its constructor.

But, ultimately, it shouldn't really be the savedialog's task to do the saving. Take Window's savedialog, for example. It's impossible to write a dialog that could save the almost limitless amount of things a savedialog might be used to save. So all the savedialog's task really is is to get a path and return that path for the caller to then act on whatever the chosen path is. So all that Form2 should be doing is showing the dialog and getting a path back from the dialog. Form2 then saves the workbook that it knows about (and the savedialog doesn't need to know about) to that path.

If say your next program needs to save a text file and you want to reuse your savedialog then you can't because it's limited to saving Excel workbooks.
__________________
There are no computers in heaven!
Reply With Quote
  #11  
Old 07-26-2011, 08:18 AM
stevenw1956 stevenw1956 is offline
Newcomer
 
Join Date: Jul 2011
Posts: 21
Default

Thanks, Dr. Punk

I know it can be difficult to understand someone else's code, especially if, as in my case, they are trying to do something that doesn't make sense.

I understand what you are saying and will try what you suggest.

Could you explain when it is possible to access resources from another form directly and when not? For instance books on VB for beginners show things like this:

Form2.TextBox1.Text = "some string"

And they say one can access variables on another form by defining them as Public in the declarations section and then accessing them like this:

Form2.Variable1.Text = "some text"

So I extrapolated from that explanation that I should be able to say:

Public oWB As Excel.Workbook

oWB = oXL.Workbooks.Open("E:\My Documents\Excel Files\Michael Hughes\Test Folder\MainList.xls")

in Form2

then

Form2.oWB.Save

in SaveDialog1

what is the difference between those scenarios that necessitates passing the object to the form in this case. I need to know for future reference.

What I am trying to do:

I process some Excel spreadsheets. Then I want to stop execution and give the user a chance to review the data and decide whether to save the files and continue to a Word mail-merge or reject the changes and exit. I didn't think I could do that with a modal dialog. Hence, the approach of creating my own SaveDialog.

I will try your approach, and it will probably work. I'll let you know how it comes out.

Thanks for your patience and help
Reply With Quote
  #12  
Old 07-26-2011, 09:06 AM
DrPunk's Avatar
DrPunkObject Reference not Visible in a Different Form DrPunk is offline
Senior Contributor

* Expert *
 
Join Date: Apr 2003
Location: Never where I want to be
Posts: 1,403
Default

The main thing you have to understand is the difference between a class as code and an instance of a class created in code (I don't know what the proper terms should be for that).

It doesn't really help that your examples refer to Form2 and VB creates forms with that type of name.

So chances are that in your examples, somewhere before the code they show would be...
Code:
Dim Form2 as New Form
So they are refering to an instance of the class, not the class itself.

The thing is that VB is treating a form class differently to how it treats any other class and that seems to be leading to your confusion.
__________________
There are no computers in heaven!
Reply With Quote
  #13  
Old 07-26-2011, 02:43 PM
stevenw1956 stevenw1956 is offline
Newcomer
 
Join Date: Jul 2011
Posts: 21
Default

After reading Dr. Punk's explanation in his last post, I easily corrected my problem by using a MsgBox in Form2 to query the user about saving the files and acting according to whether they pressed 'Yes' or 'No'. So I circumvented the whole issue of accessing the oWB object in another form.

I would like to state what I understand just to make sure I have it right, for there is still some unclarity in my mind about all this. This is an important issue that I need to understand for the future even though I have corrected the original problem

I understand that usually, unless we are dealing with shared methods, that we need to create an instance of a class with the 'New' keyword in order to use it. Further I understand that VB.NET makes an exception to that rule in the case of Form classes. In the case of Forms, VB creates, under the covers, an instance of the Form automatically. So presumably, and from looking at the examples given here, when I create a form using "Add New Item, and Form2 is created, it is already instantiated. Further that default instance will have the same name as the class, namely, Form2. I think the answer to this question is important.

So if I am correct so far (and please correct me if I am wrong) it follows that when I refer to Form2 as in

Form2.oWB.Save()

I am referring to the default instance that VB created for me.

But if so, then I don't understand how the issue of having two different instances arose in my project. For I never created another instance of the form myself. I'm not clear that was the real issue all along because I still can't see that I had two different instances of Form2. I think I had only the default instance that VB created for me. Did I create another instance when I showed the form using that multi-threading code that I still don't understand?

So was the issue that oWB was never set in the default instance? Is it a timing issue? In other words, the default instance was created by VB when I added the form in Visual Studio. Then afterward I added objects to the class and instantiated them. Was that the problem? Or was it that I always needed to pass Form2 to SaveDialog1 no matter what? I'm afraid I still don't get it! Sorry to be so thick, but I really want to understand this.
Reply With Quote
  #14  
Old 07-27-2011, 04:57 AM
DrPunk's Avatar
DrPunkObject Reference not Visible in a Different Form DrPunk is offline
Senior Contributor

* Expert *
 
Join Date: Apr 2003
Location: Never where I want to be
Posts: 1,403
Default

Quote:
Originally Posted by stevenw1956 View Post
So presumably, and from looking at the examples given here, when I create a form using "Add New Item, and Form2 is created, it is already instantiated. Further that default instance will have the same name as the class, namely, Form2. I think the answer to this question is important.
I have to admit that I didn't think that was the case. But looking at it more it appears that it is the case.

Forms created by the IDE at startup appear to be able to be referenced by the Form's class name in the project.

So, yeah, I'm kinda wondering what the original problem was now.

Sorry for causing more confusion than I should have done.
__________________
There are no computers in heaven!
Reply With Quote
  #15  
Old 07-27-2011, 09:47 AM
stevenw1956 stevenw1956 is offline
Newcomer
 
Join Date: Jul 2011
Posts: 21
Default

I suspected all along that the problem had something to do with the fact that, according to some articles I have read, Windows Forms don't always behave as expected in a .NET application using COM objects. (See the URL's in previous posts.) If there is anyone here who is an expert on such matters, I would certainly like to hear their opinion as to whether the original problem is related to the interaction between COM and the Windows Form. For instance, and this seems like an important clue, I was able to display the value of a string (testvar), defined in Form2, from SaveDialog1 using Debug.Write(Form2.testvar). That by itself told me that it wasn't that I was dealing with an invisible instance of Form2. But the COM object oWB was appearing to be Nothing. So there was a difference between a native .NET variable (testvar) and a COM object. By the way, I tried a Date object the same way. While it was not Nothing, it also was not the value I had set in Form2.

Has anyone ever been able to make multiple forms work in a .NET Office Automation scenario similar to the one here? If so can you post the code?

If anyone who reads this knows of such an expert, please ask them to comment on this.

Thanks
Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form
 
Object Reference not Visible in a Different Form
Object Reference not Visible in a Different Form
 
-->