Setting based 1 Arrays

M

Mike

Hi, we have many 3rd party developers using our VB server interface
API with structures with fields that are based 1 array bounds, i.e.,

Type Tuser
...
Security(1 to NUM_USER_SECURITY) as String*SIZE_SECURITY_NAME
End type

type TFullFileRecord
..
LongDescription(1 to MAX_LONGDESC_LINES) as String*SIZE_LONGDESC
end type

etc.

For our VB.NET port, and for the sake of some level of USAGE
compatibility, I want (desire) to maintain the long traditional idea
for BASIC programmers that these fields are based one. We also have an
server-side embedded BASIC language that are based 1 as well. So it
would help to maintain this concept.

Currently, I am using a VB.NET structure like so for TUser

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Public Structure TSecurityName
<MarshalAs(UnmanagedType.ByValTStr, sizeconst:=SIZE_SECURITY_NAME)>
Public Name As String
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Public Structure TUser
...
<MarshalAs(UnmanagedType.ByValArray,sizeconst:=NUM_USER_SECURITY)>
Public Security() As TSecurityName

sub init()
redim Security(NUM_USER_SECURITY-1)
end sub
End Structure

and as we know the lower and upper bound here is 0 to NUM_USER_SECURITY-1.

Short of using a class wrapper, maybe a Get/Set Property, is there a
way or method to declare, maybe at the compiler level what the lower
bound would be?

I am trying to maintain with the <structurelayout> for marshalling
purposes, and avoid writing our own fixed structure/class marshalling
handler. But then again, it seems .NET is so rich with implementation
interfaces, it might be feasible to explore some replacement interface.

what say you?

Thanks in advance

--
 
A

Armin Zingler

Mike said:
[...]
Short of using a class wrapper, maybe a Get/Set Property, is there a
way or method to declare, maybe at the compiler level what the lower
bound would be?

Not that I know of.
I am trying to maintain with the <structurelayout> for marshalling
purposes, and avoid writing our own fixed structure/class marshalling
handler. But then again, it seems .NET is so rich with implementation
interfaces, it might be feasible to explore some replacement
interface.
what say you?

Can you replace the field by a property? (pseudo code)

private f_Security() As TSecurityName

public property Security(Index as integer)
set (value as TSecurityName)
f_security(index - 1) = value
end set
get
return f_security(index - 1)
end get
'...
end property


The only way creating an array with lower bound <> 0 is
Array.CreateInstance:

Dim a As Array
Dim lengths As Integer() = {10}
Dim lowerBounds As Integer() = {-5}

a = Array.CreateInstance(GetType(Byte), lengths, lowerBounds)

But, you can not use the array like any other array because lower bounds <>
0 are not supported. If you try to cast the array to "Byte()", you'll get an
InvalidCastexception ("System.Byte[*] can not be casted to System.Byte[]").
You'd have to call a.SetValue/a.GetValue to access the items.


Armin
 
M

Michael C

Mike said:
For our VB.NET port, and for the sake of some level of USAGE
compatibility, I want (desire) to maintain the long traditional idea for
BASIC programmers that these fields are based one. We also have an
server-side embedded BASIC language that are based 1 as well. So it would
help to maintain this concept.

Why would you want to do that? Everything in vb.net is zero based. It would
make a lot more sense to make you component zero based. In fact it would be
downright confusing if your component was one based.

Michael
 
C

Cor Ligthert[MVP]

Not for Michael,
Why would you want to do that? Everything in vb.net is zero based. It
would make a lot more sense to make you component zero based. In fact it
would be downright confusing if your component was one based.
I wished it was, but the influence of VB6 persons made that some things are
First based.
It makes VB a little bit confusing.

By instance Mid, Instr etc use First as base.

An array is always 1 longer then instanced to make it possible to use zero
and first as starting indexer.

That is the reason I very seldom use functions or whatever which use the
first as starting indexer.

Cor
 
M

Mike

Armin said:
Can you replace the field by a property? (pseudo code)

private f_Security() As TSecurityName

public property Security(Index as integer)
set (value as TSecurityName)
f_security(index - 1) = value
end set
get
return f_security(index - 1)
end get
'...
end property

Yes, a property is probably the best solution here.

Thanks for confirming this :)

--
 
M

Mike

Michael said:
Why would you want to do that? Everything in vb.net is zero based. It would
make a lot more sense to make you component zero based. In fact it would be
downright confusing if your component was one based.

Michael

You have a good point. I will agree with you for C based languages,
C/C++, #C, Java, Javascript, even PHP where traditionally, these
language are zero based.

But adding to Cor's note, for us, our 25+ years of VB code examples,
snippets, utilities written by developers, backward compatibility,
reuseability, single sourcing, documentation, developer understanding
of our system, etc, is more important here for BASIC-like languages.
For BASIC-like language, in principal these are based 1 indexing.

I am not saying you are wrong. Everything is easy when you start new
and fresh. Unforuntately, I don't have the luxury. :)

--
 
F

Family Tree Mike

Mike said:
Hi, we have many 3rd party developers using our VB server interface
API with structures with fields that are based 1 array bounds, i.e.,

Type Tuser
...
Security(1 to NUM_USER_SECURITY) as String*SIZE_SECURITY_NAME
End type

type TFullFileRecord
..
LongDescription(1 to MAX_LONGDESC_LINES) as String*SIZE_LONGDESC
end type

etc.

For our VB.NET port, and for the sake of some level of USAGE
compatibility, I want (desire) to maintain the long traditional idea
for BASIC programmers that these fields are based one. We also have an
server-side embedded BASIC language that are based 1 as well. So it
would help to maintain this concept.

Currently, I am using a VB.NET structure like so for TUser

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Public Structure TSecurityName
<MarshalAs(UnmanagedType.ByValTStr, sizeconst:=SIZE_SECURITY_NAME)>
Public Name As String
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Public Structure TUser
...
<MarshalAs(UnmanagedType.ByValArray,sizeconst:=NUM_USER_SECURITY)>
Public Security() As TSecurityName

sub init()
redim Security(NUM_USER_SECURITY-1)
end sub
End Structure

and as we know the lower and upper bound here is 0 to NUM_USER_SECURITY-1.

Short of using a class wrapper, maybe a Get/Set Property, is there a
way or method to declare, maybe at the compiler level what the lower
bound would be?

I am trying to maintain with the <structurelayout> for marshalling
purposes, and avoid writing our own fixed structure/class marshalling
handler. But then again, it seems .NET is so rich with implementation
interfaces, it might be feasible to explore some replacement interface.

what say you?

Thanks in advance

I'm just currious about whether anyone to date has used a .Net language to
hit your VB server.

They would have stuggled with this prior. Just because your server is VB 6
does not mean the clients must be as well. I suspect these users, even if
they are a few would have internally to their code had to handle the -1
offset issue.

I mean, it's not like your current customers are going to necessarily
rewrite their clients. It sounds like a predicament...

Mike
 
A

Armin Zingler

Mike said:
Yes, a property is probably the best solution here.

Thanks for confirming this :)


Ah, ok, you wrote "class wrapper", so I was not sure you meant the same.


Armin
 
M

Mike

Family said:
I'm just currious about whether anyone to date has used a .Net language to
hit your VB server.

Good point. One of our 3rd party developers was kind enough to write
the VB.NET marshalling module and in his examples he did use zero
based indices. His VB module was included in the SDK. Can't say how
many are using it since o official support or documentation for .NET
development was provided. That is what I am doing now.
They would have stuggled with this prior. Just because your server is VB 6
does not mean the clients must be as well. I suspect these users, even if
they are a few would have internally to their code had to handle the -1
offset issue.

The framework is a C/C++ RPC client/server system. The SDK is C/C++
WIN32 based and all the supported languages was done with header
converters that are appropriate for the target language.

- For C/C++, Java, PHP, zero based indices is expected,
- For classic VB, based 1 was expected.
- For the server embedded (BASIC) language, its based 1 as well.
I mean, it's not like your current customers are going to necessarily
rewrite their clients. It sounds like a predicament...

It is a predicament.

Its quite possible we will just stick with the fundamentals of .NET
and treat VB.NET with zero based indices and just make a BIG NOTE that
prior VB code is not 100% single source.

Thanks for your comment

---
 
A

Armin Zingler

Mike said:
I am not saying you are wrong. Everything is easy when you start new
and fresh. Unforuntately, I don't have the luxury. :)

But you do start new and fresh with VB.net, don't you? ;) I gave you a
possible, technical solution because I don't want to put everything in
question, though I think this can only be a temporary solution because the
current facts (0-based index) are different. I think it's more complicated
to live with a mixture of your 1-based indexes and 0-based indexes
everywhere else than completely get accustomed to 0-based indexes.


Armin
 
M

Mike

Armin said:
But you do start new and fresh with VB.net, don't you? ;)

Touche! :)
I gave you a
possible, technical solution because I don't want to put everything in
question, though I think this can only be a temporary solution because the
current facts (0-based index) are different. I think it's more
complicated to live with a mixture of your 1-based indexes and 0-based
indexes everywhere else than completely get accustomed to 0-based indexes.

I think your right. VB.NET is different enough that its needs to work
as VB was redesigned for .NET wrt arrays and coders will be going thru
relatively big changes anyway if they try to port their current VB
applets. So this would be more more thing thing to be aware of.

You convinced me. :)

--
 
T

Tom Shelton

Touche! :)


I think your right. VB.NET is different enough that its needs to work
as VB was redesigned for .NET wrt arrays and coders will be going thru
relatively big changes anyway if they try to port their current VB
applets. So this would be more more thing thing to be aware of.

You convinced me. :)

--

VB.NET works differently then VB6, not because of .NET - but because MS made
the decision to change it. .NET definately has support for non-zero based
arrays - but for single dimmensional arrays, the VB.NET (and C#) compilers
emmit code that works with a vector type - which does not support non-zero
based bounds.

In other words, you can have a non-zero based array in VB.NET IF it has more
then one dimmension (and you don't mind a bit of awkward syntax):

Option Strict On
Option Explicit On

Module Module1

Sub Main()
Dim ar As Array = Array.CreateInstance(GetType(Integer), New Integer() {2, 2}, New Integer() {-1, -1})
Dim a(,) As Integer = CType(ar, Integer(,))
Console.WriteLine(a.Rank)
For i As Integer = 0 To a.Rank - 1
Console.WriteLine("dim {0} upper bound = {1}", i, a.GetUpperBound(i))
Console.WriteLine("dim {0} lower bound = {1}", i, a.GetLowerBound(i))
Next
End Sub

End Module

While, I never used non-zero bounds in VB.CLASSIC, I would consider this
choice of MS's to be a non-platform, gratuitous change to the behavior of the
VB language.
 
M

Michael C

Mike said:
You have a good point. I will agree with you for C based languages, C/C++,
#C, Java, Javascript, even PHP where traditionally, these language are
zero based.

Personally I prefer 1 based arrays (and 1 based everything else). The only
reason we use zero based in so many languages is because it's convenient for
the computer, not the programmer. However after the inconsistancies of vb6
I'm quite happy to have everything in zero based if it's consistant.
But adding to Cor's note, for us, our 25+ years of VB code examples,
snippets, utilities written by developers, backward compatibility,
reuseability, single sourcing, documentation, developer understanding of
our system, etc, is more important here for BASIC-like languages. For
BASIC-like language, in principal these are based 1 indexing.

For dot net I don't think you could actually find 1 single component that
was 1 based (except maybe the functions Cor mentioned which I think is not a
good thing). Every other component you could add to dot net such as grid
control or whatever will 99.9% of the time be zero based. If you make a 1
based component it will really be an exception. This will lead to plenty of
confusion for your users.

Also, the other thing to consider is that you are writing a component for
the dot net group of languages, not just VB. VB.net developers might accept
1 based arrays but C# developers will be anything from annoyed to outraged.
I am not saying you are wrong. Everything is easy when you start new and
fresh. Unforuntately, I don't have the luxury. :)

But from what you posted before it sounds like it's actually more trouble to
make it 1 based.

BTW, this is a bit off on a tangent but the style you seem to be using, eg
"Security(1 to NUM_USER_SECURITY) as String*SIZE_SECURITY_NAME" seems to be
a bit old fashioned and out of place to me (it even predates VB classic).
When making a component for dot net it should be a bit friendlier and have a
UserSecurityCollection and UserSecurity object etc.

Michael
 
M

Michael C

Cor Ligthert said:
I wished it was, but the influence of VB6 persons made that some things
are First based.
It makes VB a little bit confusing.

By instance Mid, Instr etc use First as base.

An array is always 1 longer then instanced to make it possible to use zero
and first as starting indexer.

That is the reason I very seldom use functions or whatever which use the
first as starting indexer.

You really shouldn't tell me these things Cor. :) Everytime I get on here I
find out another thing I don't like about vb.net :) That is an absolute
tragedy imo. After years of dealing with inconsistancy in VB6 I was quite
happy to accept zero based in dot net under the assumption it was that way
everywhere. In C# *everything* is zero based.

Michael
 
M

Michael Williams

However after the inconsistancies of vb6 I'm
quite happy to have everything in zero based
if it's consistant.

That's like saying after the inconsistencies of restaurants I prefer to have
one spoon of sugar in everything! Personally I prefer to decide for myself
how much sugar I want in my coffee, and how much in my soup ;-)

The documentation regarding the base index of arrays in VB6 is very clear
and well documented. It wasn't VB6 that was inconsistent, it was you!

Mike
 
M

Mike

Michael said:
Personally I prefer 1 based arrays (and 1 based everything else). The only
reason we use zero based in so many languages is because it's convenient for
the computer, not the programmer. However after the inconsistancies of vb6
I'm quite happy to have everything in zero based if it's consistant.

So why stop at zero based indexing? Why not use other well known
common constructs or concept. Since its all ultimately C/C++ based,
why not introduce // or /**/ syntax for comments? Why not allow 0x
for hex notation? or \ for escaping? Why not use 1 for TRUE instead
of -1?

Why not introduce true constructor/destructor and static vs dynamic
local scope statements? I see Using/End Using is a close facsimile to
the idea. But you get the point? If you want to introduce OOPs into
VB, then with .NET, the strategy is to single source the RTE/RTL,
then have concept ideas behind all the .NET languages.
For dot net I don't think you could actually find 1 single component that
was 1 based (except maybe the functions Cor mentioned which I think is not a
good thing).

What Cor noted is a long time understand BASIC language concept. It
was always like that.
Every other component you could add to dot net such as grid
control or whatever will 99.9% of the time be zero based. If you make a 1
based component it will really be an exception. This will lead to plenty of
confusion for your users.

We are talking about arrays. Not classes.
Also, the other thing to consider is that you are writing a component for
the dot net group of languages, not just VB. VB.net developers might accept
1 based arrays but C# developers will be anything from annoyed to outraged.

Well, I wouldn't go that far. It depends on the developer and
language. For our C/C++, Pascal, Java, PHP developers, zero based is
understand. For VB Classic, 1 based is understood.

It it when you mixing OOPS, classes, components, etc, to a traditional
VB language where it was always based this where the confusion had
started long ago, and always was a thorn on the side, and I don't
think that will ever go away until the VB generation of developers
have died off, and IMO this is what Cor was essentially noting.
But from what you posted before it sounds like it's actually more trouble to
make it 1 based.

Just yesterday, in a voice support with a customer who uses our p-code
server side WCC language (BASIC syntax with a sprinkle of C concepts),
I asked what are they plans for .NET and was surprise to hear, not
yet, but soon. I am 100% sure that he will want to re-use some of
the WCC code in his VB.NET code. Sure, if we make a BIG-NOTE that
the fixed VB.NET structure for TUser has a security() array field that
is zero base, then it might not be an issue. We might find out we
might need a WCC to VB.NET CONVERT REPORT tool, etc.
BTW, this is a bit off on a tangent but the style you seem to be using, eg
"Security(1 to NUM_USER_SECURITY) as String*SIZE_SECURITY_NAME" seems to be
a bit old fashioned and out of place to me (it even predates VB classic).

Its standard stuff for fixed length operation. Believe it or not, it
is still used at the lower levels of xx.NET.
When making a component for dot net it should be a bit friendlier and have a
UserSecurityCollection and UserSecurity object etc.

And we do have a wrapper library to assist in applying VARIANT
methods, including the wrappers ideas of return values for TRUE vs
FALSE. In BASIC, TRUE is -1, the rest of the world is 1.

Look, your point is well taken, and more than likely, we will leave it
as so (zero based), but we don't know yet if this will have a support
issue when we do go 100% with SDK documented support VB.NET and more
and more of the "old school" people who tend to follow our lead begin
to have issues. We don't know. But I can tell ya, then if we show an
classic WCC or VB example of adding a new user with Security(1) having
the initial value, then we need make sure VB.NET is treated much
different than VB when it comes to documenting all this.

Right now I am trying to get a handle of the design needs so that we
write our CPP2VBNET converter, that it will automatically creates the
classes, constructor/destructor initializing etc, for all the
structures and API. We had to do the same thing for Java and PHP. We
have over 50 or so structures, over 250 API functions. Its a pretty
big task and I want to make sure, as you say, stay consistent, yet
take advantage of the language.

--
 
M

Michael C

Mike said:
So why stop at zero based indexing? Why not use other well known common
constructs or concept. Since its all ultimately C/C++ based, why not
introduce // or /**/ syntax for comments? Why not allow 0x for hex
notation? or \ for escaping? Why not use 1 for TRUE instead of -1?

Why things are like that is a different discussion. The point is that dot
net is zero based besides a few unfortunate cases which are only in vb.net.
What Cor noted is a long time understand BASIC language concept. It was
always like that.

Maybe so but the point is that things are just not 1 based in vb.net.
We are talking about arrays. Not classes.

Both arrays and classes are 0 based though.
Well, I wouldn't go that far. It depends on the developer and language.
For our C/C++, Pascal, Java, PHP developers, zero based is understand.

But this is vb.net we are talking about and things are 0 based now. Whether
you or I like it that's the way things are.
For VB Classic, 1 based is understood.

Is that really true? VB classic was all over the place in this regard but
the default for arrays was zero based was it not? (it's been a while:) A
lot of other stuff was zero based too.
It it when you mixing OOPS, classes, components, etc, to a traditional VB
language where it was always based this where the confusion had started
long ago, and always was a thorn on the side, and I don't think that will
ever go away until the VB generation of developers have died off, and IMO
this is what Cor was essentially noting.

I don't think that's such an issue, I got over it in a month if that. After
using VB classic for way too many years I could still not remember what was
1 based and what was zero based. In dot net it made perfect sense because it
was zero based everywhere. It's not really that complicated an issue and
it's certainly a *lot* simpler if it's always the same.
Just yesterday, in a voice support with a customer who uses our p-code
server side WCC language (BASIC syntax with a sprinkle of C concepts), I
asked what are they plans for .NET and was surprise to hear, not yet, but
soon. I am 100% sure that he will want to re-use some of the WCC code in
his VB.NET code. Sure, if we make a BIG-NOTE that the fixed VB.NET
structure for TUser has a security() array field that is zero base, then
it might not be an issue. We might find out we might need a WCC to
VB.NET CONVERT REPORT tool, etc.

Converting code from vb6 to vb.net is not that simple, whatever code they
have will need some fairly substantial modification by hand. Switching over
to 0 based won't be an issue.
Its standard stuff for fixed length operation. Believe it or not, it is
still used at the lower levels of xx.NET.

That is true but if we look at a dll that is written for VB.net then it
might have fixed length arrays internally but something more friendly will
be presented to the developer. This isn't of course the end of the world as
most developers are happy if they have a way to do what they need.
And we do have a wrapper library to assist in applying VARIANT methods,
including the wrappers ideas of return values for TRUE vs FALSE. In BASIC,
TRUE is -1, the rest of the world is 1.

Look, your point is well taken, and more than likely, we will leave it as
so (zero based), but we don't know yet if this will have a support issue
when we do go 100% with SDK documented support VB.NET and more and more
of the "old school" people who tend to follow our lead begin to have
issues. We don't know. But I can tell ya, then if we show an classic WCC
or VB example of adding a new user with Security(1) having the initial
value, then we need make sure VB.NET is treated much different than VB
when it comes to documenting all this.

When you think of dot net it's probably better to target dot net instead of
just vb.net as anything you write will work in all languages besides a few
minor differences (eg properties with parameters don't work in C# and C#
doesn't have optional parameters).
 
M

Michael C

Michael Williams said:
That's like saying after the inconsistencies of restaurants I prefer to
have one spoon of sugar in everything!

No because that affects the end result. It's more like saying that I like to
pay in Australian dollars at all restaurants around the world. Now, you
might say what happens if I don't have Australian Dollars? Well, if
everything is consistant then everyone has Australian Dollars.
Personally I prefer to decide for myself how much sugar I want in my
coffee, and how much in my soup ;-)

Me too.
The documentation regarding the base index of arrays in VB6 is very clear
and well documented.

It's zero based, right?
It wasn't VB6 that was inconsistent, it was you!

Really? Some areas of vb6 and zero based and some are 1 based. I'm not sure
how that's my fault mike :)
 
M

Michael Williams

Really? Some areas of vb6 and zero based and some
are 1 based. I'm not sure how that's my fault mike :)

The documentation is extremely clear. It's not my fault you didn't read it
;-)

Mike
 
M

Mike

Michael said:
When you think of dot net it's probably better to target dot net instead of
just vb.net as anything you write will work in all languages besides a few
minor differences (eg properties with parameters don't work in C# and C#
doesn't have optional parameters).

In principle, that is the approach I always do and look for. We have a
single RPC client API dll, wcsrv2.dll and depending on the target
language, we might have a helper dll:

- wcvb.dll for VB developers, provides the ASCIIZ/BSTR, VB
BOOLEAN marshalling, for some functions returns a List of
BSTR ready for list controls assignments.

- wcjava.dll, same ideas for Java
- wcphp.dll same ideas for PHP

The latter two has more consideration for OOPS, including overloads
that might return a list or one item at a time, etc.

I had explored an ATL/COM approach. The proof of concept was done - a
single DLL for all languages that support COM+ and automation.

Years ago with VS6, I did attempt an ActiveX control so that VS6
developers can just drop a control on a form, etc, and have right
click property features. I thought that was pretty neat. :) I did
revisit that with the ATL/COM control under VS2005 (have not updated
to VS2008 yet).

But ultimately, as it was then and even now with the .NET marshalling
approach it is how the RPC server "structures" now as UDTs or OOP
class objects are exposed and with what methods and/or properties.

One thing I tried to do a few months ago was to see if we can use the
RPC server's existing IDL, APC files as input to create the client
COM interface. Almost the same, but vastly different requirements.

The 3rd party vendor/developer (a VB/.NET expert I had turned to for
many years) who wrote the VB.NET marshalling .VB module, reading some
of past comments last year, he noted:

"You might want to also look into .NET assemblies. Since MS
has kinda forgotten COM in favor of .NET assemblies, you
might want to look at creating the SDK into a .NET assembly
as well. These .NET assemblies can be called by normal
Visual Studio 6 as well as any .NET version that it was
compiled against."

So that is something else I have to get a handle to see what he means
wrt .NET Assembly.

Anyway, conceptually, I think all the languages, regardless of the
interface, had the same needs, i.e, as in the case of TUser having to
be initialized when writing (out) one way or another.

So Yes, I agree with you, it won't (or shouldn't) be a big deal that a
minimum requirement to prepare a write, e.g.;

Dim user as Tuser
user.Init()
user.Name "Mike"
user.Password = "whatever"
user.Security(0) = "Normal"
wcAddNewUser(user)

or

Dim User as New TUser("Mike","Whatever","Norma")
wcAddNewUser(user) ' or make this a method of TUser
user.Dispose()

or

Using User as New Tuser
User.Name "Mike"
User.Password = "whatever"
User.PrimarySecurity = "Normal" ' sets User.Security(0)
User.Save() ' calls wcAddNewUser()
End user

etc is needed.

In all honesty, I am not 100% what is the best way to go yet, but now
that I have been working with the VS2005 more and the VB.NET
marshalling module provided by 3rd party developer, it does seem to be
a good standpoint point to learn .NET. Maybe he meant that this
module can be compiled into a COM assembly DLL?

I'm going to be spending the weekend, I guess, learning VB.NET and C#
classes because my development mind is molded around C/C++ oops;
constructors, destructors, inheritance, abstraction, polymorphism,
etc. Yes, I have my work cut out for me :)

Anyway, I appreciated all the useful comments and insights provided by
you and others.

thanks

--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top