Let's say I've an object pointer.
I want a function that returns an object to me by simply giving it
an object pointer. Then I'll set it to an Object variable.
Then I'll use its function the late-bind way.
Is this possible?
Let's say I've an object pointer.
I want a function that returns an object to me by simply giving it
an object pointer. Then I'll set it to an Object variable.
Then I'll use its function the late-bind way.
Is this possible?
How can u use pointers in VB?
I used Structuer pointer like this
Dim mvRect As Rect
RtlMoveMemory mvRect, ByVal lParam, Len(mvRect)
I don't know what to do with Objects... Well..There's no object pointer in VB
ObjPtr. ;)Quote:
Originally posted by moinkhan
Well..There's no object pointer in VB
But CopyMemory doesn't work with them.
Could you elaborate a little more on this statement please?Quote:
But CopyMemory doesn't work with them.
Here is a simplistic example:
VB Code:
' cMyTest Class ' Just a simple class to test using ObjPtr and CopyMemory. Option Explicit Private Type MyUDT ID As Long Description As String End Type Private mudtData As MyUDT Public Property Get ID() As Long ID = mudtData.ID End Property Public Property Let ID(ByVal Value As Long) mudtData.ID = Value End Property Public Property Get Description() As String Description = mudtData.Description End Property Public Property Let Description(ByVal Value As String) mudtData.Description = Value End PropertyIt may not work for complex classes encapsulating other objects (although I will be testing this more to confirm), but using ObjPtr and CopyMemory does work (with at least simple objects).VB Code:
' frmMyTest form ' Just a simple form to test using ObjPtr and CopyMemory. Option Explicit Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) ' The object that we will be instantiating and setting its state. Private mobjSrc As cMyTest ' The object that we will not be instantiating but setting its state ' using CopyMemory. Private mobjDest As cMyTest Private Sub Command1_Click() ' Show what we have in the Source instance. With mobjSrc Debug.Print .ID Debug.Print .Description End With End Sub Private Sub Command2_Click() Dim lngSource As Long ' Obtain the Source instance's address in memory lngSource = ObjPtr(mobjSrc) ' Copy the state of Source to create and set the state of Dest. ' Note: We have not instantiated the Destination. CopyMemory mobjDest, lngSource, 4 ' Show what we have in the Destination instance. With mobjDest Debug.Print .ID Debug.Print .Description End With End Sub Private Sub Form_Load() ' Create the Source instance and set its state. Set mobjSrc = New cMyTest With mobjSrc .ID = 1001 .Description = "This is a test" End With End Sub
I didn't think that it would always work, like you said, with objects encapsulating objects, and so forth. Granted I have never really found the need to do such a thing, so I haven't invested much time in testing it. But if it can be done, then it can be done. What can I say?
:)
I've completed some testing on the subject and have found that it will work for encapsulated objects and to jian2587's original question, it will work with late-binding.
VB Code:
' cMyTestSub Option Explicit Private Type MySubUDT SubID As Long SubDescription As String End Type Private mudtData As MySubUDT Public Property Get SubID() As Long SubID = mudtData.SubID End Property Public Property Let SubID(ByVal Value As Long) mudtData.SubID = Value End Property Public Property Get SubDescription() As String SubDescription = mudtData.SubDescription End Property Public Property Let SubDescription(ByVal Value As String) mudtData.SubDescription = Value End PropertyVB Code:
' cMyTest Option Explicit Private Type MyUDT ID As Long Description As String End Type Private mudtData As MyUDT Private mobjSub As cMyTestSub Private mcolSubs As Collection Public Property Get ID() As Long ID = mudtData.ID End Property Public Property Let ID(ByVal Value As Long) mudtData.ID = Value End Property Public Property Get Description() As String Description = mudtData.Description End Property Public Property Let Description(ByVal Value As String) mudtData.Description = Value End Property Public Property Get EncapsulatedObject() As cMyTestSub Set EncapsulatedObject = mobjSub End Property Public Property Set EncapsulatedObject(ByVal Instance As cMyTestSub) Set mobjSub = Instance End Property Public Property Get Subs() As Collection Set Subs = mcolSubs End Property Private Sub Class_Initialize() Set mcolSubs = New Collection End SubVB Code:
' frmMyTest Option Explicit Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) Private mobjTest1 As cMyTest Private mobjTest2 As Object Private Sub Command1_Click() Dim objsub As cMyTestSub With mobjTest1 Debug.Print "Source " & .ID Debug.Print "Source " & .Description With .EncapsulatedObject Debug.Print " Source Enc Sub " & .SubID Debug.Print " Source Enc Sub " & .SubDescription End With For Each objsub In .Subs Debug.Print " Source Subs " & objsub.SubID Debug.Print " Source Subs " & objsub.SubDescription Next End With End Sub Private Sub Command2_Click() Dim lngSource As Long Dim lngDest As Long Dim objsub As cMyTestSub lngSource = ObjPtr(mobjTest1) CopyMemory mobjTest2, lngSource, 4 With mobjTest2 Debug.Print "Dest " & .ID Debug.Print "Dest " & .Description With .EncapsulatedObject Debug.Print " Dest Enc Sub " & .SubID Debug.Print " Dest Enc Sub " & .SubDescription End With For Each objsub In .Subs Debug.Print " Dest Subs " & objsub.SubID Debug.Print " Dest Subs " & objsub.SubDescription Next End With End Sub Private Sub Form_Load() Dim objsub As cMyTestSub Set mobjTest1 = New cMyTest With mobjTest1 .ID = 1001 .Description = "This is a test" End With Set mobjTest1.EncapsulatedObject = New cMyTestSub With mobjTest1.EncapsulatedObject .SubID = 500 .SubDescription = "This is a sub test" End With mobjTest1.Subs.Add mobjTest1.EncapsulatedObject Set objsub = New cMyTestSub With objsub .SubID = 501 .SubDescription = "This is another sub test" End With mobjTest1.Subs.Add objsub End Sub
one other thing to test is what happens when the
source object goes out of scope...
the ref count is not incremented with copymemory
so if you hung onto just the pointer to long, or the
dest object you could run into problems with invalid
pointer in some situations
you could get around this by copymemory to a temp
object then returning another one taken from the
temp with set= to increment the ref count (assuming
valid pointer passed in)
Nice codes! ;)
I haven't tested the code...but from what I see, apart from some
nice and good codes, is that the codes looks more like creating a
new copy of object than returning an object to me. But anyway
I'd be glad to know how to do that! Lots of thanks to rlwhealdon!
and dzzie, nice advice! I never knew that...
And one more question, if the object I want to refer is out of my
own process, is it possible to use rlwhealdon's method?
Apparently I was wrong.:p
It did return an object to me instead of creating a new one.
I just found out that it's actually changing the destination object
to point to the source object.:D
If the source object is out of my process, can I refer it?
Or is there a workaround for this?
I don't believe that you can set an instance of an Object variable to an instance of another class/object just by its pointer. To achieve this, you will need to create a copy using the methods demonstrated.
With something like:
You will get a Type Mismatch error because your are telling VB to set an instance of an Object to the value of a Long.VB Code:
Set mobjTest2 = ObjPtr(mobjTest1)
I have not tested this across processes, but my gut is telling me that since each process is running its own address space, that the memory address returned by ObjPtr in one process will not relate to the same memory address in another process. I could be wrong, and will need to test it to confirm.
Well yes that's what I mean.
But it's not really creating another copy, it's still pointing to the
source object.
So if I modify the destination object, the source object will be
modified as well, because both is pointing to the very same object
And BTW, thx rlwhealdon! u've been gr8 hlp!:p
and thx for doing research on that!:p :p :p :D :) :D :) ;) :cool:
Another issue to point out using this method is that you will not be able to explicitly garbage collect the instances of the objects.
Will procedure an "Unhandled Exception" stating that the memory at such-and-such an address could not be referenced.VB Code:
Private Sub Form_Unload(Cancel As Integer) Set mobjTest1 = Nothing Set mobjTest2 = Nothing End Sub
I use copy memory all the time with objects...it works fine...
U get the pointer by using ObjPtr Then pass the long value ino:
VB Code:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long) Public Function PtrObj(ByVal Pointer As Long) As Object Dim objObject As Object CopyMemory objObject, Pointer, 4& Set PtrObj = objObject CopyMemory objObject, 0&, 4& End Function
Woka
Yes, we have demonstrated that CopyMemory using the ObjPtr works.Quote:
I use copy memory all the time with objects...it works fine...
...Woka
However, the following was not something that I considered as a method for garbage collection.
Thank you for that.VB Code:
CopyMemory objObject, 0&, 4&
jian2587's outstanding question is though:
"Will this work across processes?"
Well using the above I read Byte arrays from different processes...I am sure that this can be modified slightly so return an object...will it work...??? Hmmmm dunno...give it a go :DVB Code:
Option Explicit Private Declare Function OpenProcess Lib "Kernel32.dll" (ByVal dwDesiredAccessas As Long, ByVal bInheritHandle As Long, ByVal dwProcId As Long) As Long Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long) Private Const PROCESS_VM_READ As Long = &H10 Private Type mtypDataStruct Pointer As Long Length As Long End Type Dim marBuffer() As Byte Dim mudtStruct As mtypDataStruct Public Function ReadMemory(ByVal ProcessID As Long, ByVal Pointer As Long) As Byte() Dim lngProcessHWND As Long Dim lngBytesRead As Long Dim arBuffer() As Byte Dim udtStruct As mtypDataStruct lngProcessHWND = OpenProcess(PROCESS_VM_READ, False, ProcessID) If lngProcessHWND Then Call ReadProcessMemory(lngProcessHWND, ByVal Pointer, ByVal VarPtr(udtStruct), LenB(udtStruct), lngBytesRead) ReDim arBuffer(0 To udtStruct.Length - 1) As Byte Call ReadProcessMemory(lngProcessHWND, ByVal udtStruct.Pointer, ByVal VarPtr(arBuffer(0)), udtStruct.Length, lngBytesRead) Call CloseHandle(lngProcessHWND) ReadMemory = arBuffer CloseHandle lngProcessHWND End If End Function Public Function ReadMemoryFromHWND(ByVal hwnd As Long, ByVal MemAddr As Long) As Byte() Dim lngProcessID As Long GetWindowThreadProcessId hwnd, lngProcessID ReadMemoryFromHWND = ReadMemory(lngProcessID, MemAddr) End Function Public Function SendByteArray(ByVal hwnd As Long, ByVal ByteArray As String) As Long Dim lngRet As Long marBuffer = ByteArray With mudtStruct .Length = UBound(marBuffer) + 1 .Pointer = VarPtr(marBuffer(0)) End With SendByteArray = SendMessage(hwnd, CUSTOM_MESSAGE, GetCurrentProcessId, mudtStruct) Erase marBuffer End Function Public Sub PostByteArray(ByVal hwnd As Long, ByVal ByteArray As String) marBuffer = ByteArray With mudtStruct .Length = UBound(marBuffer) + 1 .Pointer = VarPtr(marBuffer(0)) End With Call PostMessage(hwnd, CUSTOM_MESSAGE, GetCurrentProcessId, VarPtr(mudtStruct)) End Sub Public Function PtrObj(ByVal Pointer As Long) As Object Dim objObject As Object CopyMemory objObject, Pointer, 4& Set PtrObj = objObject CopyMemory objObject, 0&, 4& End Function Public Sub KillProcess(ByVal ProcessName As String) TerminateEXE ProcessName End Sub
Woka
Garbage collection???
Woka
Yes, that is my general term for cleaning up after one's self.Quote:
Garbage collection???
Woka
As I stated in a previous post in this thread. Once I've created a new reference to an object based on its memory address, issuing a:results in an "Unhandled Exception".VB Code:
Set objObject = Nothing
If I remove the statement, then the exception is not thrown.
I personally don't like having things left around waiting for VB to determine that they have gone out of scope.
When you posted your first response in this thread, you included a line that in essense, is cleaning up (setting the value of the object instance to 0&, aka Null, aka Nothing) after itself.
If I have mis-interpretted the intent of this statement, then please by all means set me straight.VB Code:
CopyMemory objObject, 0&, 4&
That line destroys the object...however, since it's private to that function it will get destroyed when the function looses scope anyways...but using that line destroys it anyways...
Woka
After that line, add:
Then run the code again, you should get a MsgBox appear...:DVB Code:
If objObject Is Nothing Then MsgBox "Woof" End If
Woka
Exactly my point. It does destroy the object instance.
I was thanking you for a method to do so in a manner that didn't produce an unhandled exception.
And congratulations on your promotion to "God".
Don't take it personally if I don't kneel down and provide you with subservient piety though.;)
technically it doesnt really "destroy" the object
put msgbox in class_terminate and watch when
it pops
with copy memory you arent creating a new class
or destroying one, you are getting a valid reference
to the com object from its pointer value.
so its not cleanup in the set = nothing sense
because the ref count was never incremented
when you copymemory obj, 0 , 4
you are just removing the reference
to the valid class in the tmp variable
if the objptr was not 0 , then when the
variable goes out of scope, vb would automatically
call the equilivent set obj= nothing to decrement its
ref count
if the object we to nolonger exist then you would get
the invalid memory error.
But it "is" cleaned up in the sense that testing the object variable against Nothing returns true. It's state is now the equivelant of the object variable had I issued the "Set ObjectInstance = Nothing" statement.
Both display True.VB Code:
CopyMemory mobjTest1, 0&, 4& CopyMemory mobjTest2, 0&, 4& MsgBox mobjTest1 Is Nothing MsgBox mobjTest2 Is Nothing
I understand that VB will eventually handle this for you. I however, am very anal. If I open it, then I try to close it. If I create it, then I try to destroy it. Regardless of whether or not I "need" to.
Actually, I am quite suprised that a simple thank you to Wokawidget for inadvertantly feeding my anal retentiveness, has exploded in the manner that it has.
I don't believe that I have misunderstood what has been communicated here. If any of my posts have given the impression to the contrary, then I guess that I need to work more on my communication skills.
Well the Terminate event is not going to get fired is it, considering there is still a reference to that object...
Create 2 objects...set one = to a valid object, use the PtrObj method I posted either, destroy the 1st object using SET , then Use the copy memory API to destroy the 2nd object and the terminate event will get fired...
So it does destroy the object :D
It works...
Did anyone manage to copy an object from another process using the code I provided???
Woka
PS Cheers...I have always been god, but just not let on ;)
No, I hadn't gotten to that yet.Quote:
Did anyone manage to copy an object from another process using the code I provided???
Hahahahahaha...Bugger :(
I feel stupid now :D
Does't work...HahahahahahahahahaVB Code:
CopyMemory objObject, 0&, 4&
Somewhere along the line my code has been mashed. I'm glad you spotted that....however I am confused where my original code has gone too :(
Anyways, the function should be:
The destination has to be the POINTER of the object, and not the object itself...just passing the object causes a memory leak :(VB Code:
Public Function PtrObj(ByVal Pointer As Long) As Object Dim objObject As Object CopyMemory objObject, Pointer, 4& Set PtrObj = objObject CopyMemory ObjPtr(objObject), 0&, 4& End Function
Hope that helps...
Oh, here'#s an example to prove it works...
Woka
Errrr...Arse :(
Something's not right here :(
Hmnmmmmm....
Woka
I think you had it right the first time. Your modified version, copying 0& to the pointer of the object results in an unhandled exception, where the previous version did not.
Change the code in that form, in the zip I posted, to:
Suggests that the object DOESN'T get destroyed...I am not God :(VB Code:
Option Explicit Private objOne As clsWoof Private objTwo As clsWoof Private Sub Command1_Click() Set objOne = New clsWoof objOne.Username = "Growling Weasel" Set objTwo = PtrObj(ObjPtr(objOne)) MsgBox ObjPtr(objTwo) Set objOne = Nothing CopyMemory ObjPtr(objTwo), 0&, 4& End Sub
However
Does allow the object to be destroyed once the function is was defined in gets destroyed...VB Code:
CopyMemory ObjPtr(objTwo), 0&, 4&
The CopyMemory only destroys the pointer to the object and not the contents on the object....Hmmmmmmm
How can you find the length of an Object in bytes??? :confused:
Can't find any API for it :(
Woka
Errr....so my ZIP file I posted gives you an error??? I don't get an error....but the object gets destroyed when the sub ends...Quote:
Originally posted by rlwhealdon
I think you had it right the first time. Your modified version, copying 0& to the pointer of the object results in an unhandled exception, where the previous version did not.
Just passing the object to copymemory does not terminate the object when the sub finishes, but when the process gets terminated...passing the pointer of the object terminates the object when, and ONLY when, the scope of the object runs out, ie when the form is unloaded, or when the sub finishes
Hmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm...
I thought I had this wrapped up months ago....yet this thread has proved otherwise...Hmmmm cool! back to the drawing board...
Woka
Your project doesn't give me any errors. The one that I had been using for this thread throws an unhandled exception when I try to use:
I don't receive the exception if I just use:VB Code:
CopyMemory ObjPtr(mobjTest2), 0&, 4&
Here's my test project.VB Code:
CopyMemory mobjTest2, 0&, 4&
The remmed out stuff dealt with the my tests on the encapsulated objects.
Yes, an object is only destroyed when it's out of scope.
But it's also destroyed when all reference to it is destroyed.
Woka,
if u add in Set objTwo = Nothing as well, the object will get
destroyed b4 the scope runs out, in this case, the form.
because all references to the object(both objOne and objTwo) is
destroyed.
I realized that if you create an object through CopyMemory,
u've to destroy it through CopyMemory Obj, 0&, 4&
and not Set Obj = Nothing
The same thing if you create an object by Set Obj = ObjSrc.
U've to Set Obj = Nothing instead of CopyMemory Obj, 0&, 4&
Because u create ObjTwo by Set ObjTwo = objObject, VB knows
there's another reference to it, and thus even Set ObjOne =
Nothing wouldn't destroy the object.
Different thing if u point the object urself through CopyMemory.
VB doesn't know it and lots errors crop up when u try to Set it to
Nothing.