confused about GetModuleFileName()
ok, i'm calling GetMOduleFileName from VB.NET. So i declared:
VB Code:
Public Declare Function GetModuleFileName Lib "kernel32" Alias "GetModuleFileNameA" (ByVal hModule As Integer, ByVal lpFileName As String, ByVal nSize As Integer) As Integer
notice that lpFileName is passed "ByVal". Now here's the thing, lpFileName is a pointer to the buffer that will receive the null terminated string. So shouldn't it be "ByRef"? Because if it's "ByVal", the api function GetModuleFileName will be getting the value instead, right? It should be passing ByRef, yet it only works if its ByVal.
Re: confused about GetModuleFileName()
A quick quote from MSDN will clear your confusion, indeed:
Quote:
ByVal
A way of passing the value of an argument to a procedure instead of passing the address. This allows the procedure to access a copy of the variable. As a result, the variable's actual value can't be changed by the procedure to which it is passed.
Re: confused about GetModuleFileName()
Sorry benmartin101 for posting in your thread.
I had subscribed the thread form the day you posted. :p
Rhino, I'm still confused. :confused:
As the MSDN quote says (and we all know) when passing ByVal, the "variable's actual value can't be changed by the procedure to which it is passed".
That means, as GetModuleFileName is getting lpFileName by value, it can't change the original string in our calling module.
But, in fact, it fills the string with new value in our calling module.
VB Code:
Option Explicit
Private Declare Function GetModuleFileName Lib "kernel32" _
Alias "GetModuleFileNameA" _
(ByVal hModule As Long, _
ByVal lpFileName As String, _
ByVal nSize As Long) As Long
Private Declare Function GetWindowWord Lib "user32" _
(ByVal hwnd As Long, _
ByVal nIndex As Long) As Integer
Const GWW_HINSTANCE = (-6)
Private Sub Form_Load()
Dim ModuleName As String * 128, FileName As String, hInst As Long
'create a buffer
ModuleName = String(128, Chr$(0))
'get the hInstance application:
hInst = GetWindowWord(Me.hwnd, GWW_HINSTANCE)
'--------------------------------
MsgBox "ModuleName [b]BEFORE[/b] calling GetModuleFileName : " & ModuleName
'--------------------------------
Call GetModuleFileName(hInst, ModuleName, Len(ModuleName))
'--------------------------------
MsgBox "ModuleName [b]AFTER[/b] calling GetModuleFileName : " & ModuleName
'--------------------------------
End Sub
Re: confused about GetModuleFileName()
In .NET Strings are immutable, so you would pass a StringBuilder ByRef and then call its .ToString() method to retrieve the string contents.
VB Code:
Imports System.Runtime.InteropServices
<DllImport("kernel32", EntryPoint:="GetModuleFileNameW")> Private Shared Function GetModuleFileName ( _
ByVal hModule As Integer, _
<MarshalAs(UnmanagedType.LPWStr)> ByRef lpFilename As StringBuilder, _
ByVal nSize As Integer _
) As Integer
Re: confused about GetModuleFileName()
@ iPrank - this is VB.NET, it's different. Strings in VB6 are not classes, they are just pointers to BSTRs which are string buffers. The buffer content CAN be changed. In VB.NET the String class is immutable, that means the buffer content CANNOT be changed.
Also, since we are using ByVal with a string, a futher caveat applies. In VB6 when you pass a string ByVal to an API function, VB makes a temporary ANSI buffer from your original Unicode string, and passes a pointer to that buffer to the API function. When the function returns the buffer is converted back to Unicode into your original string.
If you use the -W Unicode API functions instead, you declare it ByVal ... As Long, and use StrPtr(stringname) to pass a pointer to the Unicode buffer directly. That saves all the conversion stuff.
However, if you use ByVal to pass a string WITHIN your VB application, a Unicode copy is made. Therein lies the difference.
Re: confused about GetModuleFileName()
Quote:
Originally Posted by benmartin101
It should be passing ByRef, yet it only works if its ByVal.
I missed this part. In that case it is because you are calling the ANSI version of the function, so the same Unicode->ANSI->Unicode marshalling takes place as in VB6, which is why it works. If you use DllImport it allows you to specify Charset:=Charset.Unicode which means no Unicode->ANSI conversion will take place. Alternatively, if you must use the Declare syntax, you can use the Unicode keyword on it (Private Declare Unicode Function).
Re: confused about GetModuleFileName()
Quote:
Originally Posted by penagate
@ iPrank - this is VB.NET, it's different.
OOPS ! :o:p:D:bigyello::eek2:
Thanks for your reply. :)
Re: confused about GetModuleFileName()
still a bit confused. Tell me, in this code:
VB Code:
Public Declare Function GetModuleFileName Lib "kernel32" Alias "GetModuleFileNameA" (ByVal hModule As Integer, ByVal lpFileName As String, ByVal nSize As Integer) As Integer
is lpFileName's address or value being send to the function? Now, the way I see it, the address is being passed, even though its ByVal.
And in this code:
VB Code:
Public Function Func1(ByVal x As String)
x = "Hello"
end function
is x's address or value being passed? To me, x is being passed by value.
Re: confused about GetModuleFileName()
I just tried this out in c#:
[DllImport("kernel32")]
extern static int GetModuleFileName(int hMod, String lpFilename, int size);
and it doesn't work, but if I change String to StringBuilder, then it works, how come?
Re: confused about GetModuleFileName()
I've explained why it passes the address even with ByVal.
Quote:
when you pass a string ByVal to an API function, VB makes a temporary ANSI buffer from your original Unicode string, and passes a pointer to that buffer to the API function. When the function returns the buffer is converted back to Unicode into your original string.
and also about StringBuilder
Quote:
In VB.NET the String class is immutable, that means the buffer content CANNOT be changed.
which is why you use StringBuilder instead, which is NOT immutable.
Re: confused about GetModuleFileName()
oh ok, i got it now. Thanks.