Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

VB program won't terminate after uploading by FTP 3

Status
Not open for further replies.

moron3

Technical User
Dec 2, 2008
17
US
No matter how I try to FTP from a VB program - either by using the Internet Transfer Control or by calling Win32 functions like FTPPutFile, the upload works fine but the program won't terminate. I can close the program fine but it's still running in the background and I have to CTRL-ALT-DEL to end it. I've tried multiple methods of ending the program even by using DestroyWindow, it still stays in the background.

It does the same thing even with examples that other people have written using different methods of uploading, including the AllAPI Guide example.

 
I always just use "End" and that works fine. That doesn't work for you?

Ortho

[lookaround] "you cain't fix 'stupid'...
 
>I always just use "End" and that works fine

I really wouldn't, unless really, really you have no other choice.

moron3, I think we'd need to see an example of the code that is failing to even begin to have a chance to diagnose this.
 
I had the same problem with a process I was running to create pdfs, my solution (right or wrong) was to use the "TerminateProcess" API and a function called "GetProcessID" which takes the process name as the parameter.

Code:
Public Function GetProcessId(Process As String) As Long
    Dim hSnapShot As Long, pe32 As PROCESSENTRY32
    hSnapShot = CreateToolhelpSnapshot(TH32CS_SNAPPROCESS, ByVal 0)
    pe32.dwSize = Len(pe32)
    ProcessFirst hSnapShot, pe32
    Do While ProcessNext(hSnapShot, pe32)
        If InStr(1, pe32.szExeFile, Process & vbNullChar, vbTextCompare) = 1 Then
            GetProcessId = pe32.th32ProcessID
            Exit Do
        End If
    Loop
    CloseHandle hSnapShot
End Function

Then "TerminateProcess"

Code:
lPID = GetProcessId(<PROCESS NAME>)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If lPID > 0 Then
  TerminateProcess lPID, 0
End If

I believe the code was sourced from this forum (don't remember who, sorry)

There might be a cleaner way to terminate the process though, so use this as a last resort.

Patrick
 
Are you closing the ftp connection?

"I think we're all Bozos on this bus!" - Firesign Theatre [jester]
 
Strongm,

Why is "End" bad? I always place it in a sub called EndProgram where I set objects to nothing, close DBs, etc, but I always thought you HAD to end with "END." No?

There is no END to what I don't know (lol)....

Thanks,

Ortho

[lookaround] "you cain't fix 'stupid'...
 
>I believe the code was sourced from this forum

Possibly Hypetia in thread222-834688, although as I recall it derives from an MSDN example.

> my solution (right or wrong) was to use the "TerminateProcess" API
I know you know this, but there is something very wrong if you have to use TerminateProcess to stop your own program ...

>but I always thought you HAD to end with "END." No?
No

>Why is "End" bad?

A keyword search in this forum should find a number of posts from many of us explaining this. Also a Google search for, say, 'vb "end statement"' will find a raft of explanations. Even the VB6 Help documentation advises against using it as a method of halting your program. (although they can't seem to bring themselves to get rid of it completely, since it still exists in VB.NET - but the documentation still basically says "Don't use it")
 
Well strongm....

I found this, but i get an error message "object variable or with block not set" at the asterisk when I tried to use it...

Code:
Dim obj As Object
Dim frm As Form

For Each frm In Forms
    For Each obj In frm

        Set obj = Nothing
        Unload obj '*
    Next
        Set frm = Nothing
        Unload frm
Next

Is this starting to be better than "End"?

Thank you,

Ortho


[lookaround] "you cain't fix 'stupid'...
 
No need for the Obj variable...
[tt]
Dim F As Form
For Each F In Forms
Unload F
Set F = Nothing
Next
[/tt]



Good Luck


 
I found this, but i get an error message "object variable or with block not set" at the asterisk when I tried to use it...
Of course you do. :)
Code:
        Set frm = Nothing
        Unload frm
Unload WHAT? You try to unload NOTHING.
==> Switch your lines
Code:
        Unload frm
        Set frm = Nothing
;-)

[navy]"We had to turn off that service to comply with the CDA Bill."[/navy]
- The Bastard Operator From Hell
 
MakeItSo

I was up at 4 am, so I wasn't thinking clearly. But that said, that was what someone posted, so I tend to trust....

>"No need for the object variable"

You know, I took that out and, indeed the shorter code works. When I added it back, reversing the code I had wrong, ie:

Code:
set obj = nothing  (this is WRONG; I reversed the order...)
unload obj

It STILL did not work (error message: "Can' load or unload this object")

Does EVERYONE agree with vb5prgrmr that you DON'T need this code (unload obj, set obj = nothing) if you ditching your forms anyway?

~~~~~~

And guess what?

Now I HAVE THE SAME PROBLEM THAT MORON HAS!!! My program won't end. after I have cycled through all the:

Code:
unload frm
set frm = nothing

above, my forms are still 'showing'

so I am adding frm.hide to the code. Now I have

Code:
frm.hide
unload frm
set frm = nothing

Now the forms are gone, but VB6 "run" is still going, which makes me think I'm missing something.

(You know, I will admit that some computers have slowed down since my software was added, so I guess I'm going to have to grapple with this...)

Good grief...

Ortho

[lookaround] "you cain't fix 'stupid'...
 
I think what happens is that people sometimes use components that use helper threads and even hidden windows internally to implement async operations. The FTP protocol is kind of nasty in some regards, partially because it was designed to be a user-driven process so automating it is never truly graceful.

The VB documentation lists a number of the "rules" involved in program and object lifetime. Most of these are expressed in terms of "object lifetime" for in-process and out-of-process components, but they apply to a standalone main program as well.


Without looking at a lot of code it is hard to say what you have going on there. The use of FTP is a big hint though, suggesting one thing:

WinInet FTP (using the control wrapper or API calls) is a complex process. It can be easy to leave an FTP session in a open state even when you think everything is idle. You can end up with objects that will not unload through simple scope-exit or even explicit release of your references to the parent object. This is probably because circular references are held internally, or an internal hidden window is open.

In some ways what is going on may be something like the case of automating an ActiveX EXE (like Excel) where your program fails to invoke the Quit method when done with it. Since WinInet is running in-process though, your program is left running rather than the case where an orphaned Excel instance gets left running after your program finishes.


I have found it fairly tricky to get WinInet to work predictably with FTP. I still won't claim to have it licked.

Some tricks I have found necessary are to avoid trying to use busy-wait loops checking its state. This just isn't ever going to be reliable. Instead I find I need to combine both the StateChanged event and a Timer event to monitor state changes thoroughly.

This seems absurd but it appears that some state changes do not trigger the event, while others happen quickly and a wait-loop will miss them though the event is raised for them. Or... well something.

Combine this with a need to remember to issue at least the QUIT command if not also a subsequent CLOSE command to finish the FTP session and you end up with some clunky programming.


I think a lot of programmers smugly assume they have FTP with WinInet working, blithely ignorant of the orphaned processes stacking up behind the scenes. The FTP sessions eventually time out, everything gets torn down and the process terminates. This means that unless they run a lot of copies of the program in quick succession (or the program holds references to objects holding huge amounts of memory) most people just don't realize they have a problem.
 
For the sake of anyone who was following MY comments through this thread:

I HAVE SUCCESS! without using "END"

Over the past years, I had been mucking around in the "QueryUnload" subs of some of my forms, and I had placed the code:

Cancel = 1

in some of them. (I think I was trying to disable the little "x" on the form.) So that when I cycled through at the end unloading my forms, I was dis-allowing the forms to close by that one line of code. So I have learned two lessons here....

So, Moron, maybe that is your problem? Are you interfering with the closing of your forms in a similar fashion?

Thanks to Strongm and others for your invaluable guidance.

Ortho

[lookaround] "you cain't fix 'stupid'...
 
Moron3

One thing I did that really helped me was to look at the forms that were still "up," after I thought I had unloaded and set to nothing all forms:

Code:
For Each frm in Forms

  msgbox frm.Name

Next

Just trying to help.

Ortho

[lookaround] "you cain't fix 'stupid'...
 
strongm

>>I know you know this, but there is something very wrong if you have to use TerminateProcess to stop your own program ...

I believe there was a miscommunication on my part. The process I wanted to terminate was called by my app, but I could never get it to close. So it wasn't my app that was the problem but the called process, apologies for the confusion.

Patrick
 
Like I said it happens whatever method of FTP I use, here is one example. It's the ALLApi Guide example. The FTP calls return values saying they were successful. I close the connection, and close the program, but it's still there when you CTRL-ALT-DEL. I've tried running the compiled version of the program also.

Code:
Const FTP_TRANSFER_TYPE_UNKNOWN = &H0
Const FTP_TRANSFER_TYPE_ASCII = &H1
Const FTP_TRANSFER_TYPE_BINARY = &H2
Const INTERNET_DEFAULT_FTP_PORT = 21               ' default for FTP servers
Const INTERNET_SERVICE_FTP = 1
Const INTERNET_FLAG_PASSIVE = &H8000000            ' used for FTP connections
Const INTERNET_OPEN_TYPE_PRECONFIG = 0                    ' use registry configuration
Const INTERNET_OPEN_TYPE_DIRECT = 1                        ' direct to net
Const INTERNET_OPEN_TYPE_PROXY = 3                         ' via named proxy
Const INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY = 4   ' prevent using java/script/INS
Const MAX_PATH = 260
Private Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type
Private Type WIN32_FIND_DATA
    dwFileAttributes As Long
    ftCreationTime As FILETIME
    ftLastAccessTime As FILETIME
    ftLastWriteTime As FILETIME
    nFileSizeHigh As Long
    nFileSizeLow As Long
    dwReserved0 As Long
    dwReserved1 As Long
    cFileName As String * MAX_PATH
    cAlternate As String * 14
End Type
Private Declare Function InternetCloseHandle Lib "wininet.dll" (ByVal hInet As Long) As Integer
Private Declare Function InternetConnect Lib "wininet.dll" Alias "InternetConnectA" (ByVal hInternetSession As Long, ByVal sServerName As String, ByVal nServerPort As Integer, ByVal sUserName As String, ByVal sPassword As String, ByVal lService As Long, ByVal lFlags As Long, ByVal lContext As Long) As Long
Private Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" (ByVal sAgent As String, ByVal lAccessType As Long, ByVal sProxyName As String, ByVal sProxyBypass As String, ByVal lFlags As Long) As Long
Private Declare Function FtpSetCurrentDirectory Lib "wininet.dll" Alias "FtpSetCurrentDirectoryA" (ByVal hFtpSession As Long, ByVal lpszDirectory As String) As Boolean
Private Declare Function FtpGetCurrentDirectory Lib "wininet.dll" Alias "FtpGetCurrentDirectoryA" (ByVal hFtpSession As Long, ByVal lpszCurrentDirectory As String, lpdwCurrentDirectory As Long) As Long
Private Declare Function FtpCreateDirectory Lib "wininet.dll" Alias "FtpCreateDirectoryA" (ByVal hFtpSession As Long, ByVal lpszDirectory As String) As Boolean
Private Declare Function FtpRemoveDirectory Lib "wininet.dll" Alias "FtpRemoveDirectoryA" (ByVal hFtpSession As Long, ByVal lpszDirectory As String) As Boolean
Private Declare Function FtpDeleteFile Lib "wininet.dll" Alias "FtpDeleteFileA" (ByVal hFtpSession As Long, ByVal lpszFileName As String) As Boolean
Private Declare Function FtpRenameFile Lib "wininet.dll" Alias "FtpRenameFileA" (ByVal hFtpSession As Long, ByVal lpszExisting As String, ByVal lpszNew As String) As Boolean
Private Declare Function FtpGetFile Lib "wininet.dll" Alias "FtpGetFileA" (ByVal hConnect As Long, ByVal lpszRemoteFile As String, ByVal lpszNewFile As String, ByVal fFailIfExists As Long, ByVal dwFlagsAndAttributes As Long, ByVal dwFlags As Long, ByRef dwContext As Long) As Boolean
Private Declare Function FtpPutFile Lib "wininet.dll" Alias "FtpPutFileA" (ByVal hConnect As Long, ByVal lpszLocalFile As String, ByVal lpszNewRemoteFile As String, ByVal dwFlags As Long, ByVal dwContext As Long) As Boolean
Private Declare Function InternetGetLastResponseInfo Lib "wininet.dll" Alias "InternetGetLastResponseInfoA" (lpdwError As Long, ByVal lpszBuffer As String, lpdwBufferLength As Long) As Boolean
Private Declare Function FtpFindFirstFile Lib "wininet.dll" Alias "FtpFindFirstFileA" (ByVal hFtpSession As Long, ByVal lpszSearchFile As String, lpFindFileData As WIN32_FIND_DATA, ByVal dwFlags As Long, ByVal dwContent As Long) As Long
Private Declare Function InternetFindNextFile Lib "wininet.dll" Alias "InternetFindNextFileA" (ByVal hFind As Long, lpvFindData As WIN32_FIND_DATA) As Long
Const PassiveConnection As Boolean = True
Private Sub Form_Load()
    'KPD-Team 2000
    'URL: [URL unfurl="true"]http://www.allapi.net[/URL]
    'E-Mail: KPDTeam@allapi.net
    Dim hConnection As Long, hOpen As Long, sOrgPath  As String
    'open an internet connection
    hOpen = InternetOpen("API-Guide sample program", INTERNET_OPEN_TYPE_PRECONFIG, vbNullString, vbNullString, 0)
    'connect to the FTP server
    hConnection = InternetConnect(hOpen, "your ftp server", INTERNET_DEFAULT_FTP_PORT, "your login", "your password", INTERNET_SERVICE_FTP, IIf(PassiveConnection, INTERNET_FLAG_PASSIVE, 0), 0)
    'create a buffer to store the original directory
    sOrgPath = String(MAX_PATH, 0)
    'get the directory
    FtpGetCurrentDirectory hConnection, sOrgPath, Len(sOrgPath)
    'create a new directory 'testing'
    FtpCreateDirectory hConnection, "testing"
    'set the current directory to 'root/testing'
    FtpSetCurrentDirectory hConnection, "testing"
    'upload the file 'test.htm'
    FtpPutFile hConnection, "C:\test.htm", "test.htm", FTP_TRANSFER_TYPE_UNKNOWN, 0
    'rename 'test.htm' to 'apiguide.htm'
    FtpRenameFile hConnection, "test.htm", "apiguide.htm"
    'enumerate the file list from the current directory ('root/testing')
    EnumFiles hConnection
    'retrieve the file from the FTP server
    FtpGetFile hConnection, "apiguide.htm", "c:\apiguide.htm", False, 0, FTP_TRANSFER_TYPE_UNKNOWN, 0
    'delete the file from the FTP server
    FtpDeleteFile hConnection, "apiguide.htm"
    'set the current directory back to the root
    FtpSetCurrentDirectory hConnection, sOrgPath
    'remove the direcrtory 'testing'
    FtpRemoveDirectory hConnection, "testing"
    'close the FTP connection
    InternetCloseHandle hConnection
    'close the internet connection
    InternetCloseHandle hOpen
End Sub
Public Sub EnumFiles(hConnection As Long)
    Dim pData As WIN32_FIND_DATA, hFind As Long, lRet As Long
    'set the graphics mode to persistent
    Me.AutoRedraw = True
    'create a buffer
    pData.cFileName = String(MAX_PATH, 0)
    'find the first file
    hFind = FtpFindFirstFile(hConnection, "*.*", pData, 0, 0)
    'if there's no file, then exit sub
    If hFind = 0 Then Exit Sub
    'show the filename
    Me.Print Left(pData.cFileName, InStr(1, pData.cFileName, String(1, 0), vbBinaryCompare) - 1)
    Do
        'create a buffer
        pData.cFileName = String(MAX_PATH, 0)
        'find the next file
        lRet = InternetFindNextFile(hFind, pData)
        'if there's no next file, exit do
        If lRet = 0 Then Exit Do
        'show the filename
        Me.Print Left(pData.cFileName, InStr(1, pData.cFileName, String(1, 0), vbBinaryCompare) - 1)
    Loop
    'close the search handle
    InternetCloseHandle hFind
End Sub
Sub ShowError()
    Dim lErr As Long, sErr As String, lenBuf As Long
    'get the required buffer size
    InternetGetLastResponseInfo lErr, sErr, lenBuf
    'create a buffer
    sErr = String(lenBuf, 0)
    'retrieve the last respons info
    InternetGetLastResponseInfo lErr, sErr, lenBuf
    'show the last response info
    MsgBox "Error " + CStr(lErr) + ": " + sErr, vbOKOnly + vbCritical
End Sub
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top