Link to home
Start Free TrialLog in
Avatar of landerson999
landerson999

asked on

Want to assign using System.Runtime.InteropServices.GCHandle.Target, but not working

I am trying to use the following System.Runtime.InteropServices.GCHandle.Target to assign a value to a pointer of my object...maybe I am doing this wrong, but it doesn't work the way I think it should. Any help would be greatly appreciated! I included my code.

I am trying to create a sort of singleton class but driven by the new and not a seperate function call
Private Shared oSession As Session = Nothing
Public Sub New(ByVal viSessionId As Integer)
If oSession Is Nothing OrElse (oSession.SessionId <> viSessionId) Then
            SessionId = viSessionId
            miCieId = SYSessio.cie_id 'property
            miSuccId = SYSessio.succ_id 'property
            miDeptId = SYSessio.dept_id 'property
            miUserId = SYSessio.cle_user 'property
            'only set this if the first one of same id
            'as we allow to create more then one singleton object
            'if ids don't match
            If oSession Is Nothing Then
                oSession = Me
            End If
        Else 'here, we are trying to create a second of same object
             'so instead pass pointer to first then assign location 
            Dim gh As System.Runtime.InteropServices.GCHandle
            gh = System.Runtime.InteropServices.GCHandle.Alloc(Me)
            gh.Target = oSession ' at this point oSession is good
            ' problem here...gh.Target(ME) does not have same values as oSession, so not working???
            gh.Free()
        End If
    End Sub

Open in new window

Avatar of graye
graye
Flag of United States of America image

I'm not sure I'm following you completely, but it appears as if you're trying to a "shallow copy" (a reference increment in the old venacular)
If so, you might consider implementing the IClonable interface and then use the Clone method
http://msdn.microsoft.com/en-us/library/system.icloneable.clone.aspx 
Avatar of landerson999
landerson999

ASKER

Thank you for your time and effort.
I am usin the GCHandle to make a handle on the newly created Me inside the New() statement, then assign an already created variable (the singleton with the same ID) this is a way (i think) of trying to limit the resources used, by creating a single copy of an object (unless diff. ID selected as per my code) This way it means that I have a singleton object but without using the usual GetInstance() method, I was trying to do this on the New call of my object class.

This method you are stating wont work, as I would not be able to assign anything to Me inside the New method using Me = oSession.Clone, else I would have just done Me = oSession.(this gives error)
This is the reason for using the gchandle , as I am assigning the reference of my first object to my newly created object(using its handle).....sounding more like c++, but I figured it might be the only way to get my singleton implementation of an object already being used by many people without breaking their code.
Is there anything you could think of about my last comment that would make it possible to do what I need to accomplish, think of it if you will as using C like pointers to point to an object at the time of creation. If the New has a different Id as a parameter, you create a new instance of that class, but if the Id is the same, you pass the one already being help in a static variable to the pointer of the New statement (the Me)
I'm still not completely following you on what it is you're trying to do.   There are tons of articles about how to implement a "singleton pattern" in VB.Net... none of which come even close to your technique (which begs the question... "What are you trying to do")
The classic implementation is as shown below.... but I'm fairly certain you're trying to do soemthing else (but I'm not quite sure what)

Public Class Singleton
 
  Private Shared FInstance As Singleton = Nothing
 
  Private Sub New()
 
  End Sub
 
  Public Shared ReadOnly Property Instance()
    Get
      If (FInstance Is Nothing) Then
        FInstance = New Singleton()
      End If
 
      Return FInstance
    End Get
  End Property
 
End Class

Open in new window

Ok, so you have read the other 2 posts above....if you haven't let me again repeat, I am trying to create a singleton type class, that does NOT use the usual GetInstance() method to create a reference to the original object, but I want to use the New() method to figure out, if the Id passed into as parameter, will tell me to create a new one, or if one with the same Id already exists then create a reference to that object to pass back....AND ALSO I DONT WANT TO BREAK ANY CODE, so i am doing this in the new to avoid changing code everywhere else  (ie- if I use getinstance, i have to replace code everywhere (1000 diff places) from using New() to using GetInstance() , where as if I find a way, in the new, then everybody's code still works)
I am raising the points for a valid solution,
maybe I should put this inside C++ as well, seeing as it is about pointers.
Here is the more explained version to avoid confusion
Public Sub New(ByVal viSessionId As Integer)
If oSession Is Nothing OrElse (oSession.SessionId <> viSessionId) Then
' if we r in here then we have a new instance to create, we allow to 
' continue normally and assign variables needed for class to function.
            SessionId = viSessionId
            miCieId = SYSessio.cie_id 'property
            miSuccId = SYSessio.succ_id 'property
            miDeptId = SYSessio.dept_id 'property
            miUserId = SYSessio.cle_user 'property
            'if first of its kind, make it the singleton
            If oSession Is Nothing Then
                oSession = Me
            End If
        Else 
'here, we have same Id and seeing as Me = oSession doesn't work
'my guess is the only way to replace Me with the already created
'session object is to get a handle on the pointer to Me, 
'and make it point to the already existing object oSession
            Dim gh As System.Runtime.InteropServices.GCHandle
            gh = System.Runtime.InteropServices.GCHandle.Alloc(Me)
            gh.Target = oSession '***this does not work as it should
            gh.Free()
        End If
    End Sub

Open in new window

Sorry for the look of the code snippet, I don't now how experts-exchange thinks it is doing a good job for separating comments in the code from the actual code...
Unfortunately, in .NET it is not possible to cause the "new" operator to return anything other than a brand new instance of the exact class that you specified to the new operator. I think you've misunderstood what the setter for the Target property does. It doesn't force GC to "move" the original object to the location of the new object; it replaces the original Target and makes it as though you had passed the new object to the Alloc() method in the first place.

The bottom line is that if you want to pool objects in .NET, you *have* to go through some sort of explicit accessor. The new operator isn't going to work for this. Sorry there isn't a better answer.
The line  << gh.Target = oSession >>
isn't assigning oSession to the handle holding the object containing the new???
Based on your statement, I am understanding that oSession will now hold the value of New()???

I thought the assignment was from right to left???

When you run the line "gh.Target = oSession", what you're doing there is assigning the Target property on the GCHandle to the value of oSession. oSession is completely unaffected -- gh.Target now simply references whatever value was in there, which happens to be the same value as session1 in the calling scope. It doesn't change oSession or its value, and it certainly doesn't change the value of Me. Does this make sense?

The bottom line is that what you're trying to achieve is not possible in .NET. Constructors of reference types *always* return a newly-allocated instance of the specified type -- you have to use an accessor of some sort if you want to pool instances. What you're probably going to end up having to do is have a shared GetSession(viSessionId As Integer) method that looks in a shared dictionary and finds the appropriate session, creating a new one if it doesn't already exist.
I am aware that oSession is still oSession, I was wondering about the Target og the GCHanlde.Alloc(Me) which is Me wouldn't ME now have the pointer location pointing to oSession?

I think I understand what you mean, but I am wondering about C++.
If you think about how in C you can actually go read certain values where the pointer points to, then you can assign them to those places as well, then it should only be a matter of changing the pointer of the New object to point to the oSession object in memory, unless the .Net framework has a lock on that object....is this what you mean???

 If there is a lock and we are not allowed by .Net to do this, I guess i would agree, but if you are able to change the pointer and there is no lock, I don't see why it would not work as regular c pointers would???
After the Alloc(Me) call, gc.Target points to the same value that Me does, but it's just a reference -- changing it to point to something else will not affect what Me points to. The equivalent in C++ might look like this:

Session* me = new Session();
Session* other = me;

other = NULL;

Would you now expect the "me" variable to point to NULL? Of course not. In order to get me to point to NULL, you would need to dereferencing something that points to me's address, not something that points to the same thing that me does. VB probably won't actually let you assign the Me variable, but even if it did, all you'd be changing is the value of a parameter to your local variable, not the return value of the constructor.
OK, let me get this right,  
When I do gchandle.Alloc(Me) I now have a pointer to the Me object by using gchandle.
If i assign to this pointer like so gcHanle.Target = oSession, is this not supposed to work
as if I had said object1 = object2 where object 2 is oSession, which object1 now references as well?

If this is not what is happeneing in my code, this is what I was trygin to do, replacing the information at memroy location of xyz which is the object Me with infromation from locatiobn zyx which is the object oSession.

I guess maybe because I didn't do C (1 year) as much as vb.net ( 7 years ) I am thinking of soemthing that I am unfamiliar, but some reason I was sure that the memory location on the heap/stack/on the drive
could be manipulated directly as raw binary, if this is the case, would I not then be able to affect the location of Me and set the niformation manually at those bytes, rather then saty locked in with the .Net framework way of doing things.
Yes, that's exactly what happens -- after you assign gc.Target, it now references the same value as oSession. But gc.Target isn't an alias for Me, it's another variable that happens to point to the same value. Reassigning it isn't going to reassign Me.

In C terms, gc.Target is just a pointer that happens to point to the same data as Me...setting it to something else is not going to affect the value of Me. If you had code that looked like this:

Dim i1 As Integer = 1
Dim i2 As Integer = i1

i2 = 2

You wouldn't expect i1 to have a value of 2, would you? Of course not, because they're separate variables -- setting one to have the same value as the other and then changing it isn't going to affect the first one. That's essentially what you're doing here.
Actually you can't be using data types instead of objects for your example...as they do not work the same. IIf you assign one object to another, they effectively become the same object.
So if have oSession1.Name = "eric" and oSession2.Name = "stan"
And I do the following oSession1 = oSession2 then print oSession1.Name, what will it print?
"stan" is the correct response.

If we cannot assign information using the gchandle, I am ok with that, but there must be a way to assign info to the New() that has just been created, because it is sitting on the hdd somewhere. If we learn of its location (through pointers?) then I guess we can then use that location to pinpoint the object and be able to set its information manually on the hdd to be able to have the correct info in our object?


Actually, they work exactly the same. They are both slots that can hold 32 bits of data. In one case, those 32 bits are interpreted as a raw number you can do math on, and in the other they are interpreted as a reference to an object, but there's no fundamental difference between them. Setting oSession1 to the same value as oSession2 doesn't turn oSession1 into an alias for oSession2. If you then set oSession1 = oSession3 ("bill") and then print out oSession2.Name, you're still going to get "stan" because oSession1 and oSession2 are completely separate variables, and changing one doesn't affect the other.

Even if you managed to reassign the Me variable (theoretically possible, although VB isn't likely to make it easy on you), you still won't be changing the return value of the constructor, because Me is essentially just a local variable in your stack frame -- changing it to point to something else won't cause everything that points to the same value to point to the new value, any more than setting an integer variable whose value is 1 to 2 would cause every variable whose value was 1 to now be 2.

Theoretically, yes, you could probably locate the variable holding the reference to Me that's going to end up being returned from the constructor by going to native code and walking up the call stack, but it would be so prohibitively complicated that it really wouldn't be worth it. Remember, in the .NET world, code is compiled every time you run it -- even if you managed to find it, as soon as you put your code on another machine, the calling convention used to invoke your constructor could change, and you'd have to start all over. It would also look a lot like a virus and might trip some anti-virus programs.
>Setting oSession1 to the same value as oSession2 doesn't turn oSession1 into an alias for oSession2. If >you then set oSession1 = oSession3 ("bill") and then print out oSession2.Name, you're still going to get >"stan" because oSession1 and oSession2 are completely separate variables, and changing one doesn't >affect the other.

Yes, but if you were to print oSession1.Name, you would now get "bill" because oSession1 now points to oSession3....this much I know, however maybe I am mixing up which way the gchandle.Alloc(Me) works.

oSession1.Name = "bob"
oSession2.Name = "stan"
So let's say that I use gchandle.Alloc(oSession1) and then do gchandle.Target = oSession2, then do print oSession1.Name, it should return "stan" correct, seeing as I have now done the same as if I had done oSession1 = oSession2?
Maybe that's the source of the confusion. gc.Target is just another variable that can point to an object. Setting it to something else doesn't have any effect on other variables that happen to point to the same value. The purpose of GCHandle is to hold things still so you can work with them (pinning) or to be able to store references to objects without preventing GC from collecting them (weak references) -- setting Target to a new value simply pins or creates a weak reference to a different object, it doesn't affect other variables that happen to point to the same value.

So to answer your question: no, gchandle.Target = oSession2 is absolutely not equivalent to oSession1 = oSession2. oSession1.Name will still be "bob" in this example.
OK, this makes sense then, I believe that my implementation of the gchandle was faulty.
I would need to know then how to go about getting a pointer to the variable's location created by the New()
, so I could try to affect it. I understand that it might be tough to get at that location as it may change
for the reasons you mention above, but when the .Net framework is about to pass the New()
object as an instance to where ever the call was made, then it must use some sort of object reference
at that moment to represent its values, no??

I would need to get a hold of that object somehow...so that I may affect its values.
I am not sure how it is possible, as like I explained, it is difficult to maybe do this, but I have never backed down before because something was hard....

If you tell me that you don't know a way to do this, then I can agree,
if you tell me it is impossible, I would have to disagree,
seeing as so many rootkits and viruses are capable of such things.

Is it worth the amount of time to develop a smart stack finder to assign values to the New object, only if the amount of time saved is greater then the amount spent on this project. That would calculated by how many times this object is called in the code, and how many performance issues it would resolve by using a singleton class.




Obviously its not impossible, but I would say that it's prohibitively difficult, especially given the benefits -- you don't need to walk the call stack to implement a singleton, you can just go through a static accessor. Those rootkits that do this sort of thing usually aren't written in .NET, and generally speaking, they don't work target .NET code either, because it gets compiled differently on different machines. Such exploits generally target a specific compilation of a specific piece of code. They don't walk the stack and intelligently look for values that match some sort of pattern, they know that a value is stored at 0xwhatever and they go straight there to muck with it. Again, it's possible to implement this dynamically, but a real solution would be so complicated that I can't imagine it ever being worth doing.

Also, bear in mind that you're not talking about intercepting the call to the constructor, you're just trying to replace the value that it returns. This means that you'll still be allocating new Session objects every time someone asks for one -- I doubt this will save you anything measurable, as all you're doing at that point is avoiding populating a few simple fields, and you're replacing it with something that has to dynamically walk the call stack and perform a bunch of computations to figure out where the value is. This is likely to take longer than assigning those fields.

I really think you'd be much better off simply replacing the constructor with a call to a static accessor like GetSession(sessionID) and storing your sessions in a static dictionary.
Ok, well thank you for your time and effort, I appreciate the help. although I am not sure how to proceed, do I give you points even though I was not able to do what I needed to get done? How does this work, I am sort of new here.
ASKER CERTIFIED SOLUTION
Avatar of Ceiled
Ceiled
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial