Home > Archive > Visual Basic Syntax > February 2005 > Variable cloning
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
|
|
|
| I need to "clone" a user defined structure variable. Easiest explained
with an example. Given this:
Public Type UDS
Something As Long
SomethingElse As Long
End Type
Dim Original as UDS
Dim Clone as UDS
At this point:
VarPtr(Clone) <> VarPtr(Original)
because they are two different variables.
What do I need to do to make:
VarPtr(Clone) = VarPtr(Original)?
I tried playing with Set but I'm not getting anywhere. Thanks!
Danny
(You guessed it! ;o) Remove NOSPAMFOR before emailing.)
| |
| J French 2005-01-27, 3:58 am |
| On Thu, 27 Jan 2005 03:53:09 GMT, NOSPAMFORdaniel_ahorn@yahoo.com
(Danny) wrote:
>I need to "clone" a user defined structure variable. Easiest explained
>with an example. Given this:
>
>Public Type UDS
> Something As Long
> SomethingElse As Long
>End Type
>
>Dim Original as UDS
>Dim Clone as UDS
>
>At this point:
> VarPtr(Clone) <> VarPtr(Original)
>because they are two different variables.
>
>What do I need to do to make:
> VarPtr(Clone) = VarPtr(Original)?
>
>I tried playing with Set but I'm not getting anywhere. Thanks!
If by 'clone' you mean 'copy' then
Clone = Original
If you want both to refer to exactly the same data, so any change in
one is seen in the other - then you are in for a hard time
( in C that is called a 'Union' )
Set is used for Objects - not UDTs
If you really want to have two UDTs that are in fact the same data,
then you could get that effect by sending two references of the same
UDT to a Sub/Function - but it seems a bit pointless
Could you explain what you really want
| |
|
| Date: Thu, 27 Jan 2005 07:16:16 +0000 (UTC)
Name: erewhon@nowhere.uk (J French)
>
>If by 'clone' you mean 'copy' then
> Clone = Originalz
Not really, because that would only copy the value.
>If you want both to refer to exactly the same data, so any change in
>one is seen in the other - then you are in for a hard time
Tell me about it! ;o)
>( in C that is called a 'Union' )
That's it! Or in more generic terms, I want to "reference" a variable
(as opposed to de-reference).
>If you really want to have two UDTs that are in fact the same data,
>then you could get that effect by sending two references of the same
>UDT to a Sub/Function - but it seems a bit pointless
>
>Could you explain what you really want
It's messy... I have a linked list generated by a 3rd party DLL I have
no control over. In the process of using the DLL I also have to
allocate and maintain my own elements in this linked list from within
VB.
For example, I have a routine to locate a specific element which this
routine returns by reference. So far, so good. However, for example, I
need to pass this element to another routine by reference as well.
I could do that with a convoluted construction like this:
x = Sub2(Sub1(Element))
where Sub1 would, say, find the parent of Element and then pass this
parent to Sub2 by reference. (Element may be defined in VB, while
parent may be defined by the DLL i.e. outside VB.)
But that's not always practical or even possible. Of course, I also
want to do other things and it would be much more convenient to be
able to declare a variable and then "reference" it to the returned
element i.e. create a union, something like:
Dim y...
y = Sub1(Element) 'i.e. create a union
....massage y...
x = Sub2(y)
etc...
Danny
(You guessed it! ;o) Remove NOSPAMFOR before emailing.)
| |
| Tony Proctor 2005-01-27, 9:00 pm |
| You would have been better off here using classes rather UDTs Danny. UDTs
are odd because they're always assigned by value (i.e. value copied rather
than the reference being copied), and yet they're always passed to
procedures by reference (never by value)
Tony Proctor
"Danny" <NOSPAMFORdaniel_ahorn@yahoo.com> wrote in message
news:41f93143.8294535@News.Individual.NET...
> Date: Thu, 27 Jan 2005 07:16:16 +0000 (UTC)
> Name: erewhon@nowhere.uk (J French)
>
>
> Not really, because that would only copy the value.
>
>
> Tell me about it! ;o)
>
>
> That's it! Or in more generic terms, I want to "reference" a variable
> (as opposed to de-reference).
>
>
> It's messy... I have a linked list generated by a 3rd party DLL I have
> no control over. In the process of using the DLL I also have to
> allocate and maintain my own elements in this linked list from within
> VB.
>
> For example, I have a routine to locate a specific element which this
> routine returns by reference. So far, so good. However, for example, I
> need to pass this element to another routine by reference as well.
>
> I could do that with a convoluted construction like this:
> x = Sub2(Sub1(Element))
> where Sub1 would, say, find the parent of Element and then pass this
> parent to Sub2 by reference. (Element may be defined in VB, while
> parent may be defined by the DLL i.e. outside VB.)
>
> But that's not always practical or even possible. Of course, I also
> want to do other things and it would be much more convenient to be
> able to declare a variable and then "reference" it to the returned
> element i.e. create a union, something like:
>
> Dim y...
> y = Sub1(Element) 'i.e. create a union
> ...massage y...
> x = Sub2(y)
> etc...
>
> Danny
>
> (You guessed it! ;o) Remove NOSPAMFOR before emailing.)
| |
|
| Thanks, Tony. So far I've been able to avoid using classes, but I
guess it's time to bite the bullet...
What I'm afraid of is that I'll go down that road at considerable
expense of time and effort only to end up staring at yet another
roadblock. I know that VB hasn't really be conceived for the type of
things I'm doing but it successfully blocked every avenue I tried so
far (see parallel mail for the latest obstruction).
Date: Thu, 27 Jan 2005 19:44:10 -0000
Name: "Tony Proctor" <tony_proctor@aimtechnology_NoMoreSPAM_.com>
>You would have been better off here using classes rather UDTs Danny. UDTs
>are odd because they're always assigned by value (i.e. value copied rather
>than the reference being copied), and yet they're always passed to
>procedures by reference (never by value)
>
> Tony Proctor
>
>"Danny" <NOSPAMFORdaniel_ahorn@yahoo.com> wrote in message
>news:41f93143.8294535@News.Individual.NET...
>
>
Danny
(You guessed it! ;o) Remove NOSPAMFOR before emailing.)
| |
|
| Date: Thu, 27 Jan 2005 18:25:46 GMT
Name: NOSPAMFORdaniel_ahorn@yahoo.com (Danny)
>For example, I have a routine to locate a specific element which this
>routine returns by reference. So far, so good. However, for example, I
>need to pass this element to another routine by reference as well.
>
>I could do that with a convoluted construction like this:
> x = Sub2(Sub1(Element))
Actually I couldn't! :-(
When calls are nested like that, by the time the parameter reaches
Sub2 its address has changed. It works for the first level, though.
Presumably, VB makes a temporary copy in order to handle the nesting.
This doesn't do anything to the value, of course, but it nukes my idea
using nesting to pass the reference down the chain. Sigh...
Danny
(You guessed it! ;o) Remove NOSPAMFOR before emailing.)
| |
| Tony Proctor 2005-01-28, 8:59 am |
| I still don't really understand what you're doing Danny. You say you have a
"third-party DLL" that creates a linked list. Is that linked list composed
of VB UDT's, VB objects, or C structures? If C structures, are you trying to
manipulate them via VB UDTs?
If you have the address of a C structure, or even a VB UDT, you can
manipulate the fields by doing direct memory manipulation using either the
CopyMemory API (aka RtlMoveMemory), or the following set of lesser-known
(but more efficient) interfaces:
Declare Sub GetMem1 Lib "msvbvm60" (ByVal Addr As Long, RetVal As Byte)
Declare Sub GetMem2 Lib "msvbvm60" (ByVal Addr As Long, RetVal As Integer)
Declare Sub GetMem4 Lib "msvbvm60" (ByVal Addr As Long, RetVal As Long)
Declare Sub GetMem8 Lib "msvbvm60" (ByVal Addr As Long, RetVal As Currency)
Declare Sub PutMem1 Lib "msvbvm60" (ByVal Addr As Long, ByVal NewVal As
Byte)
Declare Sub PutMem2 Lib "msvbvm60" (ByVal Addr As Long, ByVal NewVal As
Integer)
Declare Sub PutMem4 Lib "msvbvm60" (ByVal Addr As Long, ByVal NewVal As
Long)
Declare Sub PutMem8 Lib "msvbvm60" (ByVal Addr As Long, ByVal NewVal As
Currency)
Private Declare Function IsBadReadPtr Lib "Kernel32.dll" (ByRef lp As Any,
ByVal ucb As Long) As Long
Tony Proctor
"Danny" <NOSPAMFORdaniel_ahorn@yahoo.com> wrote in message
news:41f96894.4788013@News.Individual.NET...
> Thanks, Tony. So far I've been able to avoid using classes, but I
> guess it's time to bite the bullet...
>
> What I'm afraid of is that I'll go down that road at considerable
> expense of time and effort only to end up staring at yet another
> roadblock. I know that VB hasn't really be conceived for the type of
> things I'm doing but it successfully blocked every avenue I tried so
> far (see parallel mail for the latest obstruction).
>
>
> Date: Thu, 27 Jan 2005 19:44:10 -0000
> Name: "Tony Proctor" <tony_proctor@aimtechnology_NoMoreSPAM_.com>
>
rather[color=darkred]
>
>
> Danny
>
> (You guessed it! ;o) Remove NOSPAMFOR before emailing.)
| |
|
| Date: Fri, 28 Jan 2005 10:17:51 -0000
Name: "Tony Proctor" <tony_proctor@aimtechnology_NoMoreSPAM_.com>
>I still don't really understand what you're doing Danny. You say you have a
>"third-party DLL" that creates a linked list. Is that linked list composed
>of VB UDT's, VB objects, or C structures? If C structures, are you trying to
>manipulate them via VB UDTs?
It's very, very messy... The linked list is composed of both. The DLL
requires C structures to communicate parameters and data back and
forth.
Therefore, VB needs to set up some of those C structures (I emulate
them with UDTs) before a DLL call is made. The DLL then adds various
links anchored in these UDTs i.e., their parent/child fields contain
the absolute address of the UDT.
This means I always need to know the physical location of the UDTs.
The problem is, if I Dim a variable and assign a UDT to it, the
address of this variable is no longer the same as the address of
original UDT (even though the values are). And that means the variable
is no longer a part of the linked list.
Here's some simplified pseudo-code to make it clearer:
GetChild(Parent as UDT) as UDT
GetChild = Call_DLL_to_get_child(Parent)
....
Sub1 (Parent as UDT)
Dim Child as UDT
Child = GetChild(Parent)
....
At this point in Sub1:
VarPtr(Child) <> VarPtr(Call_DLL_to_get_child(Parent))
So, what I really need is a "C Union" between these two.
>If you have the address of a C structure, or even a VB UDT, you can
>manipulate the fields by doing direct memory manipulation using either the
>CopyMemory API (aka RtlMoveMemory), or the following set of lesser-known
>(but more efficient) interfaces:
.... GetMem/PutMem ...
Yes, thanks for that, Tony! I do have to use memory copy in places to
maintain the linked list because some DLL calls may execute a
"realloc" which shifts everything around... As if I didn't have enough
trouble already! ;o)
Danny
(You guessed it! ;o) Remove NOSPAMFOR before emailing.)
| |
| Jim Mack 2005-01-28, 9:00 pm |
| Danny wrote:
> Date: Fri, 28 Jan 2005 10:17:51 -0000
> Name: "Tony Proctor" <tony_proctor@aimtechnology_NoMoreSPAM_.com>
>
>
> It's very, very messy... The linked list is composed of both. The DLL
> requires C structures to communicate parameters and data back and
> forth.
>
> Therefore, VB needs to set up some of those C structures (I emulate
> them with UDTs) before a DLL call is made. The DLL then adds various
> links anchored in these UDTs i.e., their parent/child fields contain
> the absolute address of the UDT.
>
> This means I always need to know the physical location of the UDTs.
> The problem is, if I Dim a variable and assign a UDT to it, the
> address of this variable is no longer the same as the address of
> original UDT (even though the values are). And that means the variable
> is no longer a part of the linked list.
>
This is all very odd, which I guess you know :-). Are you saying that
the DLL returns a UDT, not a pointer to a UDT? How? Could you post the
actual C prototypes for these functions? If what's actually returned is
a pointer, then couldn't you copy the structure to a VB UDT, pass it in,
then memcpy the results back to the structure to which you got a
pointer?
Or it might be as simple as changing the VB Declare from 'ByRef UDT' to
'ByVal MyLongPointer' and pass back the pointer you get in the first
place. I'd love to see both the C prototypes and your VB Declares.
> So, what I really need is a "C Union" between these two.
Not a union as I know them. A union is a variable allowing two 'views'
of the same memory, not literally two variables having the same address.
It sounds more and more like you need to deal with pointers directly.
--
Jim Mack
MicroDexterity Inc
www.microdexterity.com
| |
|
| Date: Fri, 28 Jan 2005 16:34:38 -0500
Name: "Jim Mack" <jmack@mdxi.nospam.com>
>
>This is all very odd, which I guess you know :-).
Tell me about it! I have wads of hair around my chair and scratch
marks on the walls to prove it! ;o)
>Are you saying that
>the DLL returns a UDT, not a pointer to a UDT? How? Could you post the
>actual C prototypes for these functions? If what's actually returned is
>a pointer, then couldn't you copy the structure to a VB UDT, pass it in,
>then memcpy the results back to the structure to which you got a
>pointer?
The DLL is a part of a development kit so I can't really post C
structures due to the NDA, but I hope this will shed further light
(sorry about the volume, and I wouldn't hold it against you if your
eyes glaze over - it's not your problem, really).
Essentially, it's a case of rewriting C code in VB. While in C you can
happily assign pointers to variables, you can't do that in VB (that I
know of - and that's my question, actually).
Take something like this (pseudo code):
LPObject GetChild( LPObject pRefParent, SLONG lIDChild )
LPRefObj pRefChild;
pRefChild = GetLevelDown(pRefParent, SomeIndex)
If you use LPRefObj to define pRefChild anywhere in the code, it will
always be the exact same address. But you can't do that in VB (can
you?). As far as I know all you can do is define a variable like so:
Sub GetChild(... etc
Dim RefChild as RefObj
RefChild = GetLevelDown(... etc
However, each of these RefChild (in other Subs as well) will have a
different address from the actual child in the linked list - and
different from each other. Not a problem if all you want is data, but
a big problem when it comes to linking, say:
pRefParent.ChildPointer
That will point to the real child in the linked list whose address
will be totally different from the address of any RefChild defined in
any of the subroutines. So any comparison based on that will fail.
Now, you may say, just throw away C stuff and define everything in VB.
Well, I can't do that because the DLL adds elements internally but
links to and from UDTs I *must* define within VB in order to
communicate with the DLL. So, both my VB UDTs and internal DLL C
structure elements are components of a single linked list.
BTW, this problem is only manifested in *callback* routines. I've
successfully told the DLL (using "AddressOf procedure" in the
structures) where the callbacks written in VB are, and that works. The
problem is when these callbacks are invoked and structures are passed
to them by the DLL.
>Or it might be as simple as changing the VB Declare from 'ByRef UDT' to
>'ByVal MyLongPointer' and pass back the pointer you get in the first
>place. I'd love to see both the C prototypes and your VB Declares.
I hope I skirted the NDA with the above pseudo-code and yet made it
clear(er)... ;o)
Anyway, I "solved" it by doing this (that's why I didn't reply
yesterday):
I put all my VB UDTs in arrays. So far I only need 4 arrays, which is
not too bad. I may need to add a couple more by the time it's over.
Each time I get a structure back from the DLL, and it's one of my own,
I search the above arrays by comparing the address of all elements
(VarPtr(ObjectArray(Ind))) to the address of returned element from the
DLL.
Having identified the element in my array I then use its index
throughout my code (instead of defining a variable to hold it) and
make all pointer comparisons I want, such as:
If ObjectArray(Ind).ChildPointer = VarPtr(ObjectArray(AnotherInd))
Initially, I made the arrays dynamic but each time I added an element,
VB reallocated the array moving it to a different memory location and
messing up my linked list.
So, I now use static arrays and keep a high-water mark. This goes
against the grain conceptually, but in practical terms the counts of
elements are < 100, so it's not too bad.
I could've stuck to dynamic arrays and re-linked the list each time I
allocated a new element but I just didn't feel like it at 6 AM... ;o)
Danny
(You guessed it! ;o) Remove NOSPAMFOR before emailing.)
| |
| Jim Mack 2005-01-31, 4:00 am |
| Danny wrote:
> Date: Fri, 28 Jan 2005 16:34:38 -0500
> Name: "Jim Mack" <jmack@mdxi.nospam.com>
>
>
> Tell me about it! I have wads of hair around my chair and scratch
> marks on the walls to prove it! ;o)
Well, if you have a working solution then I guess anything further is
moot.
I still think that given a complete look at the prototypes and Declares,
we could have come up with an answer, but no point now.
> While in C you can
> happily assign pointers to variables, you can't do that in VB (that I
> know of - and that's my question, actually).
You can assign pointers to variables in VB, and your own code shows that
you know that. pVarbl = VarPtr(varbl) does exactly that. What you can't
do so easily is then access varbl through pVarbl as you can in C.
Good luck, looks like you'll need it...
--
Jim Mack
MicroDexterity Inc
www.microdexterity.com
| |
|
| Date: Sun, 30 Jan 2005 21:40:09 -0500
Name: "Jim Mack" <jmack@mdxi.nospam.com>
>You can assign pointers to variables in VB, and your own code shows that
>you know that. pVarbl = VarPtr(varbl) does exactly that. What you can't
>do so easily is then access varbl through pVarbl as you can in C.
Precisely! That was my question! Even though I'm too far gone in this
project it would be a very good thing to know.
Could you give me a hint on how to access varbl through pVarbl in VB?
>Good luck, looks like you'll need it...
Thanks, I certainly will!
Danny
(You guessed it! ;o) Remove NOSPAMFOR before emailing.)
| |
| Tony Proctor 2005-01-31, 4:01 pm |
| My previous post gives a list of such access methods Danny, depending on the
size of the datum being accessed.
Tony Proctor
"Danny" <NOSPAMFORdaniel_ahorn@yahoo.com> wrote in message
news:41fe6013.4617077@News.Individual.NET...
> Date: Sun, 30 Jan 2005 21:40:09 -0500
> Name: "Jim Mack" <jmack@mdxi.nospam.com>
>
>
> Precisely! That was my question! Even though I'm too far gone in this
> project it would be a very good thing to know.
>
> Could you give me a hint on how to access varbl through pVarbl in VB?
>
>
> Thanks, I certainly will!
>
> Danny
>
> (You guessed it! ;o) Remove NOSPAMFOR before emailing.)
| |
| Jim Mack 2005-01-31, 4:01 pm |
| Danny wrote:
> Date: Sun, 30 Jan 2005 21:40:09 -0500
> Name: "Jim Mack" <jmack@mdxi.nospam.com>
>
>
> Precisely! That was my question! Even though I'm too far gone in this
> project it would be a very good thing to know.
>
> Could you give me a hint on how to access varbl through pVarbl in VB?
CopyMemory (a common alias of RtlMoveMemory) is the usual way. It's what
I suggested earlier for keeping your local UDTs as true clones of
anything passed to you. Of course, any memory movement on the C side
can invalidate the pointer(s), but if you eliminate that, it will be
stable.
Tony also showed you the simpler and faster internal VB functions for
copying small variables, but CopyMemory works for any size.
--
Jim
| |
|
| Date: Mon, 31 Jan 2005 13:27:06 -0500
Name: "Jim Mack" <jmack@mdxi.nospam.com>
>Danny wrote:
>
>CopyMemory (a common alias of RtlMoveMemory) is the usual way. It's what
>I suggested earlier for keeping your local UDTs as true clones of
>anything passed to you. Of course, any memory movement on the C side
>can invalidate the pointer(s), but if you eliminate that, it will be
>stable.
>
>Tony also showed you the simpler and faster internal VB functions for
>copying small variables, but CopyMemory works for any size.
Thank you both Jim and Tony!
I'm filing this whole thread and will get back to it in much more
detail after my current project is done.
Danny
(You guessed it! ;o) Remove NOSPAMFOR before emailing.)
|
|
|
|
|