Home All Groups Group Topic Archive Search About
Author
28 Mar 2005 4:07 PM
Cutlass
Hello
My build is failing when I call some api's.  Could someone help fix this
code.  Not sure how to declare the variables....already tried many different
ways.....Thanks

Public Declare Function WaitForSingleObject Lib "kernel32" _
    (ByVal hHandle As System.IntPtr, _
    ByVal dwMilliseconds As Integer) As Integer
Public Auto Declare Function FindWindow Lib "user32" _
    (ByVal lpClassName As String, _
    ByVal lpWindowName As String) As System.IntPtr
Public Declare Auto Function PostMessage Lib "user32" _
    (ByVal hWnd As  System.IntPtr, _
    ByVal wMsg As Integer, _
    ByVal wParam As Integer, _
    ByVal lParam As Integer) As Boolean
Public Declare Function OpenProcess Lib "kernel32" _
    (ByVal dwDesiredAccess As Integer, _
    ByVal bInheritHandle As Boolean, _
    ByVal dwProcessId As System.IntPtr) As System.IntPtr
Public Declare Function GetWindowThreadProcessId Lib "user32" _
    (ByVal hWnd As System.IntPtr, _
    ByRef lpdwProcessId As System.IntPtr) As System.IntPtr


    Dim hWindow         As Long
    Dim hThread         As Long
    Dim hProcess        As Long
    Dim lProcessId      As Long
    Dim lngResult       As Long
    Dim lngReturnValue  As Long
    Dim iLoopCnt        As Integer

    hWindow = FindWindow(vbNullString, "(Inactive " & sDir & "\XXXX.EXE)")
    hThread = GetWindowThreadProcessId(hWindow, lProcessId)
    hProcess = OpenProcess(SYNCHRONIZE, 0&, lProcessId)
    lngReturnValue = PostMessage(hWindow, WM_CLOSE, 0&, 0&)
    lngResult = WaitForSingleObject(hProcess, 20000)

Author
28 Mar 2005 4:28 PM
Crouchie1998
Your FindWindow is pointing to a filename & not the window caption I think.

Do you have a window caption called:

Inactive " & sDir & "\XXXX.EXE)")

Whatever you have for sDir.

Example Window Caption:
---------------------------

Inactive C:\XXXX.EXE)

I personally would change your 'Long's' for Integers & your IntPtr's to
Integers. In fact I would do it all different. what is your full code & what
is sDir?
Author
28 Mar 2005 4:55 PM
Cutlass
I changed the 'Long's' for Integers & your IntPtr's to Integers as you
suggested.  IT WORKS!  THANKS ALOT

You mentioned you would do it differently....I've tried using .NET methods
with no success...

The FindWindow is actually
hWindow = FindWindow(vbNullString, "(Inactive " & "C:\XXXX.EXE)")

What I'm trying to do is this...
My program kicks off an exe that creates a DOS window on the Desktop.  I've
tried for days to use the "pure .NET way" to close the spawned window.  When
I replace my XXXX.EXE, with for example Calc.exe, the .NET code works great. 
But when I use the XXXX.exe the code fails...acts as if the window isn't on
the desktop.  (No window hanlde ?) So now I'm trying the API methods which
work with my VB6 version of the app using the XXX.exe.   Now that I've got
the declares correct it works.

Show quoteHide quote
"Crouchie1998" wrote:

> Your FindWindow is pointing to a filename & not the window caption I think.
>
> Do you have a window caption called:
>
> Inactive " & sDir & "\XXXX.EXE)")
>
> Whatever you have for sDir.
>
> Example Window Caption:
> ---------------------------
>
> Inactive C:\XXXX.EXE)
>
> I personally would change your 'Long's' for Integers & your IntPtr's to
> Integers. In fact I would do it all different. what is your full code & what
> is sDir?
>
>
>
Author
28 Mar 2005 6:00 PM
Tom Shelton
In article <6227AD98-DD3A-41D4-83DA-63F687083***@microsoft.com>, Cutlass wrote:
Show quoteHide quote
> Hello
> My build is failing when I call some api's.  Could someone help fix this
> code.  Not sure how to declare the variables....already tried many different
> ways.....Thanks
>
> Public Declare Function WaitForSingleObject Lib "kernel32" _
>     (ByVal hHandle As System.IntPtr, _
>     ByVal dwMilliseconds As Integer) As Integer
> Public Auto Declare Function FindWindow Lib "user32" _
>     (ByVal lpClassName As String, _
>     ByVal lpWindowName As String) As System.IntPtr
> Public Declare Auto Function PostMessage Lib "user32" _
>     (ByVal hWnd As  System.IntPtr, _
>     ByVal wMsg As Integer, _
>     ByVal wParam As Integer, _
>     ByVal lParam As Integer) As Boolean
> Public Declare Function OpenProcess Lib "kernel32" _
>     (ByVal dwDesiredAccess As Integer, _
>     ByVal bInheritHandle As Boolean, _
>     ByVal dwProcessId As System.IntPtr) As System.IntPtr
> Public Declare Function GetWindowThreadProcessId Lib "user32" _
>     (ByVal hWnd As System.IntPtr, _
>     ByRef lpdwProcessId As System.IntPtr) As System.IntPtr
>
>
>     Dim hWindow         As Long
>     Dim hThread         As Long
>     Dim hProcess        As Long
>     Dim lProcessId      As Long
>     Dim lngResult       As Long
>     Dim lngReturnValue  As Long
>     Dim iLoopCnt        As Integer
>
>     hWindow = FindWindow(vbNullString, "(Inactive " & sDir & "\XXXX.EXE)")
>     hThread = GetWindowThreadProcessId(hWindow, lProcessId)
>     hProcess = OpenProcess(SYNCHRONIZE, 0&, lProcessId)
>     lngReturnValue = PostMessage(hWindow, WM_CLOSE, 0&, 0&)
>     lngResult = WaitForSingleObject(hProcess, 20000)
>
>

No disrespect for Crouchie1998 - he was trying to help you.  But, if you
took his suggestion, then your declares are not correct.  System handles
should always be declared as IntPtr...

I would leave your declares unchanged - but change your code to use them
like this:

Dim hWindow As IntPtr
Dim hThread As IntPtr
Dim hProcess As IntPtr
Dim lProccessId As IntPtr
Dim bResult As Boolean
Dim iRet As Integer

hWindow = FindWindow(Nothing, "(Inactive " & sDir & "\XXXX.EXE)")
hThread = GetWindowThreadProcessId(hWindow, lProcessId)
hProcess = OpenProcess(SYNCHRONIZE, False, lProcessId)
bResult = PostMessage(hWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero)
iRet = WaitForSingleObject(hProcess, 20000)

--
Tom Shelton [MVP]
Author
28 Mar 2005 8:39 PM
Cutlass
FYI
Tried coding Tom's way...it also works
Don't know who is correct, but both methods work

Show quoteHide quote
"Tom Shelton" wrote:

> In article <6227AD98-DD3A-41D4-83DA-63F687083***@microsoft.com>, Cutlass wrote:
> > Hello
> > My build is failing when I call some api's.  Could someone help fix this
> > code.  Not sure how to declare the variables....already tried many different
> > ways.....Thanks
> >
> > Public Declare Function WaitForSingleObject Lib "kernel32" _
> >     (ByVal hHandle As System.IntPtr, _
> >     ByVal dwMilliseconds As Integer) As Integer
> > Public Auto Declare Function FindWindow Lib "user32" _
> >     (ByVal lpClassName As String, _
> >     ByVal lpWindowName As String) As System.IntPtr
> > Public Declare Auto Function PostMessage Lib "user32" _
> >     (ByVal hWnd As  System.IntPtr, _
> >     ByVal wMsg As Integer, _
> >     ByVal wParam As Integer, _
> >     ByVal lParam As Integer) As Boolean
> > Public Declare Function OpenProcess Lib "kernel32" _
> >     (ByVal dwDesiredAccess As Integer, _
> >     ByVal bInheritHandle As Boolean, _
> >     ByVal dwProcessId As System.IntPtr) As System.IntPtr
> > Public Declare Function GetWindowThreadProcessId Lib "user32" _
> >     (ByVal hWnd As System.IntPtr, _
> >     ByRef lpdwProcessId As System.IntPtr) As System.IntPtr
> >
> >
> >     Dim hWindow         As Long
> >     Dim hThread         As Long
> >     Dim hProcess        As Long
> >     Dim lProcessId      As Long
> >     Dim lngResult       As Long
> >     Dim lngReturnValue  As Long
> >     Dim iLoopCnt        As Integer
> >
> >     hWindow = FindWindow(vbNullString, "(Inactive " & sDir & "\XXXX.EXE)")
> >     hThread = GetWindowThreadProcessId(hWindow, lProcessId)
> >     hProcess = OpenProcess(SYNCHRONIZE, 0&, lProcessId)
> >     lngReturnValue = PostMessage(hWindow, WM_CLOSE, 0&, 0&)
> >     lngResult = WaitForSingleObject(hProcess, 20000)
> >
> >
>
> No disrespect for Crouchie1998 - he was trying to help you.  But, if you
> took his suggestion, then your declares are not correct.  System handles
> should always be declared as IntPtr...
>
> I would leave your declares unchanged - but change your code to use them
> like this:
>
> Dim hWindow As IntPtr
> Dim hThread As IntPtr
> Dim hProcess As IntPtr
> Dim lProccessId As IntPtr
> Dim bResult As Boolean
> Dim iRet As Integer
>
> hWindow = FindWindow(Nothing, "(Inactive " & sDir & "\XXXX.EXE)")
> hThread = GetWindowThreadProcessId(hWindow, lProcessId)
> hProcess = OpenProcess(SYNCHRONIZE, False, lProcessId)
> bResult = PostMessage(hWindow, WM_CLOSE, IntPtr.Zero, IntPtr.Zero)
> iRet = WaitForSingleObject(hProcess, 20000)
>
> --
> Tom Shelton [MVP]
>
Author
28 Mar 2005 9:56 PM
Tom Shelton
In article <5732DEF6-9EDB-4360-8C6B-88ACD3D8D***@microsoft.com>, Cutlass wrote:
> FYI
> Tried coding Tom's way...it also works
> Don't know who is correct, but both methods work
>

Correct maybe a strong word here...  Both methods work.  It's a matter
of what's best practice.  Handles as returned by the windows api's are
the integer size of the underlying hardware.  For example, on 32-bit
systems the size of a handle is 32-bits, but on a 64-bit system they are
64-bits.  Using Integer for these types of values makes your code
non-portable to these systems.  IntPtr is specifically designed to match
that of the underlying platform (32-bit on 32-bit hardware and OS,
64-bit on 64-bit hardware and OS).

This is the reason that the handle properties of controls in
Windows.Forms are System.IntPtr :)

Some go further, and actually use the specific .NET type in their
declares rather then the VB.NET mapped keywords.  In other words, they
don't use Integer, Short, or Boolean in their declares - they use
System.Int32, System.Int16, or System.Boolean.  They do this to sheild
themselves from future changes in the mapping of VB.NET datatypes.

Anyway, when converting declares from VB.CLASSIC, you should be aware
that data types no longer match.  The sizes have changed in VB.NET.
Here is a quick rundown:

VB.CLASSIC            VB.NET            SIZE
Byte                Byte            8-bits
Integer                Short            16-bits
Long                Integer            32-bits
Currency            Long            64-bits

Strings should be treated differently as well.  When a string is being
passed as a constant to an API call, then using String is fine.  But, if
that String is to be modified by the API call, then it should be passed
as System.Text.StringBuilder instead....  For example, in VB6 you might
declare the function GetUserName like this:

Public Declare Function GetUserName _
    Lib "Advapi32.dll" Alias "GetUserNameA" _
    (ByVal lpBuffer As String, _
    ByRef nSize As Long) As Long

Well, in VB.NET that would translate to:
Public Declare Auto Function GetUserName Lib "Advapi32.dll" _
    (ByVal lpBuffer As System.Text.StringBuider,
    ByRef nSize As Integer) As Boolean

Notice the lack of the alias?  That's because .NET has the ability to
determine the appropriate function to call (A vs W), based on the
current OS.  Also, since this buffer is going to be filled by the api, I
declared it with System.Text.StringBuilder.  This is not strictly
necessary in VB.NET (it is in C#), since the VB.NET marshaller will
actually do the extra work required to return the value if you were to
declare it as string (remember, strings are immutable in .NET) - but, it
can have a negative impact on performance and memory usage if the call
is made frequently.  So, calling it using stringbuilder:

Imports System.Text

....

Dim buffer As New StringBuilder (256)
Dim nSize As Integer = buffer.Capacity

GetUserName (buffer, nSize)

Return buffer.ToString ()

Obviously, you might want to check the return value(s) etc.  But, this
should give you an idea.  You should read the documentation on p/invoke
and marshalling for further information.

--
Tom Shelton [MVP]
Author
28 Mar 2005 10:15 PM
Cutlass
Thanks for the explanation and your time!  I personally always like to know
the detailed explanation "how and whys" !

Show quoteHide quote
"Tom Shelton" wrote:

> In article <5732DEF6-9EDB-4360-8C6B-88ACD3D8D***@microsoft.com>, Cutlass wrote:
> > FYI
> > Tried coding Tom's way...it also works
> > Don't know who is correct, but both methods work
> >
>
> Correct maybe a strong word here...  Both methods work.  It's a matter
> of what's best practice.  Handles as returned by the windows api's are
> the integer size of the underlying hardware.  For example, on 32-bit
> systems the size of a handle is 32-bits, but on a 64-bit system they are
> 64-bits.  Using Integer for these types of values makes your code
> non-portable to these systems.  IntPtr is specifically designed to match
> that of the underlying platform (32-bit on 32-bit hardware and OS,
> 64-bit on 64-bit hardware and OS).
>
> This is the reason that the handle properties of controls in
> Windows.Forms are System.IntPtr :)
>
> Some go further, and actually use the specific .NET type in their
> declares rather then the VB.NET mapped keywords.  In other words, they
> don't use Integer, Short, or Boolean in their declares - they use
> System.Int32, System.Int16, or System.Boolean.  They do this to sheild
> themselves from future changes in the mapping of VB.NET datatypes.
>
> Anyway, when converting declares from VB.CLASSIC, you should be aware
> that data types no longer match.  The sizes have changed in VB.NET.
> Here is a quick rundown:
>
> VB.CLASSIC            VB.NET            SIZE
> Byte                Byte            8-bits
> Integer                Short            16-bits
> Long                Integer            32-bits
> Currency            Long            64-bits
>
> Strings should be treated differently as well.  When a string is being
> passed as a constant to an API call, then using String is fine.  But, if
> that String is to be modified by the API call, then it should be passed
> as System.Text.StringBuilder instead....  For example, in VB6 you might
> declare the function GetUserName like this:
>
> Public Declare Function GetUserName _
>     Lib "Advapi32.dll" Alias "GetUserNameA" _
>     (ByVal lpBuffer As String, _
>     ByRef nSize As Long) As Long
>
> Well, in VB.NET that would translate to:
> Public Declare Auto Function GetUserName Lib "Advapi32.dll" _
>     (ByVal lpBuffer As System.Text.StringBuider,
>     ByRef nSize As Integer) As Boolean
>
> Notice the lack of the alias?  That's because .NET has the ability to
> determine the appropriate function to call (A vs W), based on the
> current OS.  Also, since this buffer is going to be filled by the api, I
> declared it with System.Text.StringBuilder.  This is not strictly
> necessary in VB.NET (it is in C#), since the VB.NET marshaller will
> actually do the extra work required to return the value if you were to
> declare it as string (remember, strings are immutable in .NET) - but, it
> can have a negative impact on performance and memory usage if the call
> is made frequently.  So, calling it using stringbuilder:
>
> Imports System.Text
>
> ....
>
> Dim buffer As New StringBuilder (256)
> Dim nSize As Integer = buffer.Capacity
>
> GetUserName (buffer, nSize)
>
> Return buffer.ToString ()
>
> Obviously, you might want to check the return value(s) etc.  But, this
> should give you an idea.  You should read the documentation on p/invoke
> and marshalling for further information.
>
> --
> Tom Shelton [MVP]
>