StrPtr Question

D

dev

Hello,
How can the following code be converted to VB.Net? I am mostly interested
in how to handle the StrPtr calls in the function.

Private Declare Function PathRelativePathToW Lib "shlwapi.dll" _
(ByVal pszPath As Long, _
ByVal pszFrom As Long, _
ByVal dwAttrFrom As Long, _
ByVal pszTo As Long, _
ByVal dwAttrTo As Long) As Boolean

Public Function GetRelativePath(ByVal sFromDirectory As String, _
ByVal sToFile As String) As String

Dim sRelativePath As String
Dim bResult As Boolean

Const MAX_PATH As Long = 260

sRelativePath = Space(MAX_PATH)

' Set "Attr" to vbDirectory for directories and 0 for files
bResult = PathRelativePathToW(StrPtr(sRelativePath),
StrPtr(sFromDirectory), _
vbDirectory, StrPtr(sToFile), 0)

If bResult Then
GetRelativePath = Left(sRelativePath, InStr(sRelativePath,
Chr(0)) -1)
End If
End Function

TIA,
Steve
 
J

Jay B. Harlow [MVP - Outlook]

Steve,
How can the following code be converted to VB.Net? I am mostly interested
in how to handle the StrPtr calls in the function.
StrPtr is no more! As it is no longer needed!

I would recommend something like:

Declare Auto Function PathRelativePathTo Lib "shlwapi.dll" (ByVal
pszPath As System.Text.StringBuilder, ByVal pszFrom As String, ByVal
dwAttrFrom As FileAttributes, ByVal pszTo As String, ByVal dwAttrTo As
FileAttributes) As Boolean

Public Function GetRelativePath(ByVal sFromDirectory As String,
ByVal sToFile As String) As String
Const MAX_PATH As Integer = 260

Dim sRelativePath As New System.Text.StringBuilder(MAX_PATH)

Dim bResult As Boolean

' Set "Attr" to vbDirectory for directories and 0 for files
bResult = PathRelativePathTo(sRelativePath, sFromDirectory,
FileAttributes.Directory, sToFile, 0)

If bResult Then
Return sRelativePath.ToString()
Else
Return Nothing
End If
End Function

Passing the output string as a StringBuilder is safest as Strings themselves
are immutable. Just remember to initialize the capacity of the StringBuilder
to the maximum size of characters that may be returned.

Both String & Stringbuilder are correctly converted when defined in
parameters. String is good for input parameters, StringBuilder are good for
Output & Input/Output parameters.

Declaring dwAttrFrom & dwAttrTo as System.IO.FileAttributes simplifies the
usage of the parameters.

Declaring the function as Auto, ensures that the Unicode strings are
converted properly respective to the OS being run on. On NT based OS the
strings will stay Unicode & the W version of the function is called, on 95
based OS the strings will be converted to Ansi and the A version of the
function will be called!

BTW: Interesting function I needed it a few weeks ago! I'll have to file it
away in my grab bag of functions.

Hope this helps
Jay
 
H

Herfried K. Wagner [MVP]

* "Jay B. Harlow said:
Passing the output string as a StringBuilder is safest as Strings themselves
are immutable. Just remember to initialize the capacity of the StringBuilder
to the maximum size of characters that may be returned.

When using VB.NET's 'Declare', it's not necessary to declare the
parameter as 'StringBuilder'. VB.NET will do the marshalling
automatically.
 
J

Jay B. Harlow [MVP - Outlook]

Herfried,
My answer is based on what Adam Nathan wrote in ".NET and COM - The Complete
Interoperability Guide" from SAMS press. Plus other MSDN Magazine articles.

Yes VB.NET will marshal the string correctly for you.

However! Remember that a System.String is immutable, the API that the OP
gave requires a buffer passed in, to put the output string in. Passing a
System.String for this buffer breaks the "immutable" intent of the
System.String class. So although it may appear to work you just modified an
immutable object, which may lead to subtle runtime bugs elsewhere.

Based on the above book and other articles in MSDN Magazine on P/Invoke
using StringBuilder for output strings is the safest & easiest method to
use.

So although you can send in a System.String to an output parameter of an API
call, I would seriously not recommend it as you are running a high risk of
corrupting the runtime. Of course using any API call you are running a risk
of corrupting the runtime time, why elevate that risk?

Hope this helps
Jay
 
H

Herfried K. Wagner [MVP]

* "Jay B. Harlow said:
My answer is based on what Adam Nathan wrote in ".NET and COM - The Complete
Interoperability Guide" from SAMS press. Plus other MSDN Magazine articles.

Yes VB.NET will marshal the string correctly for you.

However! Remember that a System.String is immutable, the API that the OP
gave requires a buffer passed in, to put the output string in. Passing a
System.String for this buffer breaks the "immutable" intent of the
System.String class. So although it may appear to work you just modified an
immutable object, which may lead to subtle runtime bugs elsewhere.

ACK. But AFAIR it won't cause problems (except maybe performance
problems) when passing the string.
Based on the above book and other articles in MSDN Magazine on P/Invoke
using StringBuilder for output strings is the safest & easiest method to
use.

When using 'DllImport': Yes. When using 'Declare' it's not necessary
and IMHO not the "preferred" way. When using VB.NET's 'Declare', VB.NET
will automatically create the '<MarshalAs(UnmanagedType.VBByRefStr)>':

So although you can send in a System.String to an output parameter of an API
call, I would seriously not recommend it as you are running a high risk of
corrupting the runtime. Of course using any API call you are running a risk
of corrupting the runtime time, why elevate that risk?

As mentioned above, I don't think that there is a risk in this case when
passing the string directly.
 
D

dev

Thanks, I had Googled StrPtr and most of the answers stated the the way to
handle StrPtr in VB.Net was specific to how StrPtr was being used in the
original function., VB6 in this case.
 
J

Jay B. Harlow [MVP - Outlook]

Herfried,
Interesting!

I missed the section in the book on the VBByRefStr, thanks for pointing
VBByRefStr out as it is a handy MarshalAs parameter to know!

Note: Adam's book states the following about
MarshalAs(UnmanagedType.VBByRefStr), after explaining how it works in VB.NET
& how to use it in C#:

"This is not recommended, however. Even in Visual Basic .NET,
you should use StringBuilder rather then String if backwards
compatibility with existing source code isn't important"

Then he follows that with:

"TIP: You should use StringBuider to represent a buffer in
Visual Basic .NET, just like you would in C#.
Using String types for this case is simply a second
option for backward compatibility."

I'm sure when I read the above section 6 months ago the above info is what
stuck with me, and VBByRefStr slipped away. ;-)

I'm not certain what "backward compatibility" he is referring to, I would
think VB6. I wish he would dig deeper into why it is not recommended! Maybe
I just have not got to that section of the book yet ;-)

Looking closer at Adam's book, he indicates that you should use the
InAttribute & OutAttribute (from System.Runtime.InteropServices) to help
minimize the amount of copying going on, as StringBuilder & String will
actually copy the buffer for the call, then copy the buffer back. I suspect
VBByRefStr is doing its own amount of copying...

I normally try to include InAttribute & OutAttribute in my Declares &
DllImports.
When using 'DllImport': Yes. When using 'Declare' it's not necessary
and IMHO not the "preferred" way.
I'll let you, now that I know its safe!

However! I will continue to recommend StringBuilder even with Declare. As to
me it is more obvious what is happening (strings are immutable) and it
allows converting back & forth from DllImport easier.
As mentioned above, I don't think that there is a risk in this case when
passing the string directly.
In light of "MarshalAs(UnmanagedType.VBByRefStr)" I agree, there is no
greater risk then normal API calls.

Thanks for the info
Jay
 
J

Jay B. Harlow [MVP - Outlook]

Dev,
Did you see my earlier post? I gave you a complete working example on how to
call the API. As StrPtr does not exist in VB.NET!

You need to use one of the following Declare statements:

Declare Auto Function PathRelativePathTo Lib "shlwapi.dll" (ByVal
pszPath As System.Text.StringBuilder, ByVal pszFrom As String, ByVal
dwAttrFrom As FileAttributes, ByVal pszTo As String, ByVal dwAttrTo As
FileAttributes) As Boolean

Declare Auto Function PathRelativePathTo Lib "shlwapi.dll" (ByVal
pszPath As String, ByVal pszFrom As String, ByVal
dwAttrFrom As FileAttributes, ByVal pszTo As String, ByVal dwAttrTo As
FileAttributes) As Boolean

The second one maybe easier for VB6 developers to follow, however I prefer
the first one. Both should do the job equally well for you!

Hope this helps
Jay


dev said:
Thanks, I had Googled StrPtr and most of the answers stated the the way to
handle StrPtr in VB.Net was specific to how StrPtr was being used in the
original function., VB6 in this case.

modified
an

ACK. But AFAIR it won't cause problems (except maybe performance
problems) when passing the string.


When using 'DllImport': Yes. When using 'Declare' it's not necessary
and IMHO not the "preferred" way. When using VB.NET's 'Declare', VB.NET
will automatically create the '<MarshalAs(UnmanagedType.VBByRefStr)>':
<http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemRuntimeIntero
pServicesUnmanagedTypeClassTopic.asp>
an
risk
 
J

Jay B. Harlow [MVP - Outlook]

Herfried,
I believe I figured out why StringBuilder is recommended! ;-)

<MarshalAs(UnmanagedType.VBByRefStr)> can only be used with "InOut"
parameters, if you start adding InAttribute or OutAttribute to the declare
statement you need to give a MarshalAs with the 'regular' string types
<MarshalAs(UnmanagedType.LPTStr)> for example, other wise you get an
exception from the Marshaller

If you are not concerned with the InAttribute or OutAttribute then the
"default" behavior of VBByRefStr is reasonable for the Declare statement. As
it mimics what VB6 would do.

As I stated in my other post I normally include InAttribute & OutAttribute
on my Declare statements...

Hope this helps
Jay
 
D

dev

Yes, I saw it and I thank you for your example. My major point of interest
in my post was how to handle the non-existant StrPtr function in VB.Net. I
thought maybe there was another way to get a pointer to a string in VB.Net.
I am new to the VB.Net paradigm, but have programmed VB since VB3. I going
to try your example right now. Thanks again!

Jay B. Harlow said:
Dev,
Did you see my earlier post? I gave you a complete working example on how to
call the API. As StrPtr does not exist in VB.NET!

You need to use one of the following Declare statements:

Declare Auto Function PathRelativePathTo Lib "shlwapi.dll" (ByVal
pszPath As System.Text.StringBuilder, ByVal pszFrom As String, ByVal
dwAttrFrom As FileAttributes, ByVal pszTo As String, ByVal dwAttrTo As
FileAttributes) As Boolean

Declare Auto Function PathRelativePathTo Lib "shlwapi.dll" (ByVal
pszPath As String, ByVal pszFrom As String, ByVal
dwAttrFrom As FileAttributes, ByVal pszTo As String, ByVal dwAttrTo As
FileAttributes) As Boolean

The second one maybe easier for VB6 developers to follow, however I prefer
the first one. Both should do the job equally well for you!

Hope this helps
Jay


dev said:
Thanks, I had Googled StrPtr and most of the answers stated the the way to
handle StrPtr in VB.Net was specific to how StrPtr was being used in the
original function., VB6 in this case.

Herfried K. Wagner said:
* "Jay B. Harlow [MVP - Outlook]" <[email protected]> scripsit:
My answer is based on what Adam Nathan wrote in ".NET and COM - The Complete
Interoperability Guide" from SAMS press. Plus other MSDN Magazine articles.

Yes VB.NET will marshal the string correctly for you.

However! Remember that a System.String is immutable, the API that
the
Passing
a modified
method
<http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemRuntimeIntero of
 
D

dev

The example works as required! Thanks.

dev said:
Yes, I saw it and I thank you for your example. My major point of interest
in my post was how to handle the non-existant StrPtr function in VB.Net. I
thought maybe there was another way to get a pointer to a string in VB.Net.
I am new to the VB.Net paradigm, but have programmed VB since VB3. I going
to try your example right now. Thanks again!

Jay B. Harlow said:
Dev,
Did you see my earlier post? I gave you a complete working example on
how
to
call the API. As StrPtr does not exist in VB.NET!

You need to use one of the following Declare statements:

Declare Auto Function PathRelativePathTo Lib "shlwapi.dll" (ByVal
pszPath As System.Text.StringBuilder, ByVal pszFrom As String, ByVal
dwAttrFrom As FileAttributes, ByVal pszTo As String, ByVal dwAttrTo As
FileAttributes) As Boolean

Declare Auto Function PathRelativePathTo Lib "shlwapi.dll" (ByVal
pszPath As String, ByVal pszFrom As String, ByVal
dwAttrFrom As FileAttributes, ByVal pszTo As String, ByVal dwAttrTo As
FileAttributes) As Boolean

The second one maybe easier for VB6 developers to follow, however I prefer
the first one. Both should do the job equally well for you!

Hope this helps
Jay
way
to
handle StrPtr in VB.Net was specific to how StrPtr was being used in the
original function., VB6 in this case.

* "Jay B. Harlow [MVP - Outlook]" <[email protected]> scripsit:
My answer is based on what Adam Nathan wrote in ".NET and COM - The
Complete
Interoperability Guide" from SAMS press. Plus other MSDN Magazine
articles.

Yes VB.NET will marshal the string correctly for you.

However! Remember that a System.String is immutable, the API that
the
OP
gave requires a buffer passed in, to put the output string in.
Passing
a
System.String for this buffer breaks the "immutable" intent of the
System.String class. So although it may appear to work you just modified
an
immutable object, which may lead to subtle runtime bugs elsewhere.

ACK. But AFAIR it won't cause problems (except maybe performance
problems) when passing the string.

Based on the above book and other articles in MSDN Magazine on P/Invoke
using StringBuilder for output strings is the safest & easiest
method
to
use.

When using 'DllImport': Yes. When using 'Declare' it's not necessary
and IMHO not the "preferred" way. When using VB.NET's 'Declare', VB.NET
will automatically create the
<http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemRuntimeIntero
running
 
J

Jay B. Harlow [MVP - Outlook]

Dev,
Unfortunately that is on a case by case basis, as you stated.

In this case where you are passing the string to an API, you simply define
the API to accept a string, as I showed, and the Framework does all the work
for you. As Herfried & I discussed in our digression from your specific
question. VB.NET actually helps you a little over the normal framework
functions and allows you to use String for output parameters as well.

I have not really used StrPtr in VB6, so I don't know other what cases it is
used there. However if you give specific examples of StrPtr in VB6 I or
someone else will show you how to do the same thing in VB.NET. And as
Hefried & I have showed, there is probably more then one way to do it! ;-)

Hope this helps
Jay

dev said:
Yes, I saw it and I thank you for your example. My major point of interest
in my post was how to handle the non-existant StrPtr function in VB.Net. I
thought maybe there was another way to get a pointer to a string in VB.Net.
I am new to the VB.Net paradigm, but have programmed VB since VB3. I going
to try your example right now. Thanks again!

Jay B. Harlow said:
Dev,
Did you see my earlier post? I gave you a complete working example on
how
to
call the API. As StrPtr does not exist in VB.NET!

You need to use one of the following Declare statements:

Declare Auto Function PathRelativePathTo Lib "shlwapi.dll" (ByVal
pszPath As System.Text.StringBuilder, ByVal pszFrom As String, ByVal
dwAttrFrom As FileAttributes, ByVal pszTo As String, ByVal dwAttrTo As
FileAttributes) As Boolean

Declare Auto Function PathRelativePathTo Lib "shlwapi.dll" (ByVal
pszPath As String, ByVal pszFrom As String, ByVal
dwAttrFrom As FileAttributes, ByVal pszTo As String, ByVal dwAttrTo As
FileAttributes) As Boolean

The second one maybe easier for VB6 developers to follow, however I prefer
the first one. Both should do the job equally well for you!

Hope this helps
Jay
way
to
handle StrPtr in VB.Net was specific to how StrPtr was being used in the
original function., VB6 in this case.

* "Jay B. Harlow [MVP - Outlook]" <[email protected]> scripsit:
My answer is based on what Adam Nathan wrote in ".NET and COM - The
Complete
Interoperability Guide" from SAMS press. Plus other MSDN Magazine
articles.

Yes VB.NET will marshal the string correctly for you.

However! Remember that a System.String is immutable, the API that
the
OP
gave requires a buffer passed in, to put the output string in.
Passing
a
System.String for this buffer breaks the "immutable" intent of the
System.String class. So although it may appear to work you just modified
an
immutable object, which may lead to subtle runtime bugs elsewhere.

ACK. But AFAIR it won't cause problems (except maybe performance
problems) when passing the string.

Based on the above book and other articles in MSDN Magazine on P/Invoke
using StringBuilder for output strings is the safest & easiest
method
to
use.

When using 'DllImport': Yes. When using 'Declare' it's not necessary
and IMHO not the "preferred" way. When using VB.NET's 'Declare', VB.NET
will automatically create the '<MarshalAs(UnmanagedType.VBByRefStr)> ':
<http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemRuntimeIntero
running
 
H

Herfried K. Wagner [MVP]

* "dev said:
Thanks, I had Googled StrPtr and most of the answers stated the the way to
handle StrPtr in VB.Net was specific to how StrPtr was being used in the
original function., VB6 in this case.

Forget the 'StrPtr'.
 
H

Herfried K. Wagner [MVP]

Jay,

* "Jay B. Harlow said:
I missed the section in the book on the VBByRefStr, thanks for pointing
VBByRefStr out as it is a handy MarshalAs parameter to know!
;-)

Note: Adam's book states the following about
MarshalAs(UnmanagedType.VBByRefStr), after explaining how it works in VB.NET
& how to use it in C#:

"This is not recommended, however. Even in Visual Basic .NET,
you should use StringBuilder rather then String if backwards
compatibility with existing source code isn't important"
Interesting.

Then he follows that with:

"TIP: You should use StringBuider to represent a buffer in
Visual Basic .NET, just like you would in C#.
Using String types for this case is simply a second
option for backward compatibility."

Maybe 'Declare' shouldn't be used too because if's only a 2nd option for
backward compatibility? I don't think so. It's valid VB.NET syntax, if
it worked in VB6, why shouldn't VB.NET be able to cope with strings too?
I'm sure when I read the above section 6 months ago the above info is what
stuck with me, and VBByRefStr slipped away. ;-)

I'm not certain what "backward compatibility" he is referring to, I would
think VB6. I wish he would dig deeper into why it is not recommended! Maybe
I just have not got to that section of the book yet ;-)

Would be really interesting to know. If you find out why, please let
me/us know.
actually copy the buffer for the call, then copy the buffer back. I suspect
VBByRefStr is doing its own amount of copying...

ACK. That's what I suspect too.
I'll let you, now that I know its safe!

I would use a 'StringBuilder' too, but I only wanted to note that
'String' "should" work too in this case.
 
D

dev

I never used any of the *Ptr functions in any version of VB since these
functions are undocumented and not guaranteed to be in the next version of
VB, as in the case of VB.Net. The only reason I asked in this case was
because I need to display a relative path to the user. The documentation I
found for PathRelativePathW included the StrPtr function for the call, which
I posted. So after Googling, I decided to ask as the opinion was the
translation from previous versions of VB to .Net was on a case by case
basis. I wanted ideas for this specific case, which were provided and I am
happy.
 
D

dev

This was the only case I was interested in. If you ae interested, you can
Google Groups on 'StrPtr .Net +vb' and you will see the same posts I saw.
They included a hodgepodge of opinions, as always. Some used
GCHandle.Alloc and some other methods.

Jay B. Harlow said:
Dev,
Unfortunately that is on a case by case basis, as you stated.

In this case where you are passing the string to an API, you simply define
the API to accept a string, as I showed, and the Framework does all the work
for you. As Herfried & I discussed in our digression from your specific
question. VB.NET actually helps you a little over the normal framework
functions and allows you to use String for output parameters as well.

I have not really used StrPtr in VB6, so I don't know other what cases it is
used there. However if you give specific examples of StrPtr in VB6 I or
someone else will show you how to do the same thing in VB.NET. And as
Hefried & I have showed, there is probably more then one way to do it! ;-)

Hope this helps
Jay

dev said:
Yes, I saw it and I thank you for your example. My major point of interest
in my post was how to handle the non-existant StrPtr function in VB.Net. I
thought maybe there was another way to get a pointer to a string in VB.Net.
I am new to the VB.Net paradigm, but have programmed VB since VB3. I going
to try your example right now. Thanks again!

Dev,
Did you see my earlier post? I gave you a complete working example on
how
to
call the API. As StrPtr does not exist in VB.NET!

You need to use one of the following Declare statements:

Declare Auto Function PathRelativePathTo Lib "shlwapi.dll" (ByVal
pszPath As System.Text.StringBuilder, ByVal pszFrom As String, ByVal
dwAttrFrom As FileAttributes, ByVal pszTo As String, ByVal dwAttrTo As
FileAttributes) As Boolean

Declare Auto Function PathRelativePathTo Lib "shlwapi.dll" (ByVal
pszPath As String, ByVal pszFrom As String, ByVal
dwAttrFrom As FileAttributes, ByVal pszTo As String, ByVal dwAttrTo As
FileAttributes) As Boolean

The second one maybe easier for VB6 developers to follow, however I prefer
the first one. Both should do the job equally well for you!

Hope this helps
Jay


Thanks, I had Googled StrPtr and most of the answers stated the the
way
to
handle StrPtr in VB.Net was specific to how StrPtr was being used in the
original function., VB6 in this case.

* "Jay B. Harlow [MVP - Outlook]" <[email protected]> scripsit:
My answer is based on what Adam Nathan wrote in ".NET and COM - The
Complete
Interoperability Guide" from SAMS press. Plus other MSDN Magazine
articles.

Yes VB.NET will marshal the string correctly for you.

However! Remember that a System.String is immutable, the API
that
the
OP
gave requires a buffer passed in, to put the output string in. Passing
a
System.String for this buffer breaks the "immutable" intent of the
System.String class. So although it may appear to work you just
modified
an
immutable object, which may lead to subtle runtime bugs elsewhere.

ACK. But AFAIR it won't cause problems (except maybe performance
problems) when passing the string.

Based on the above book and other articles in MSDN Magazine on
P/Invoke
using StringBuilder for output strings is the safest & easiest method
to
use.

When using 'DllImport': Yes. When using 'Declare' it's not necessary
and IMHO not the "preferred" way. When using VB.NET's 'Declare', VB.NET
will automatically create the
' said:
<http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemRuntimeIntero
parameter
of running case
when
 

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