Click here to Skip to main content
15,867,686 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi
I'm sitting on the proverbial million lines of nasty VB6 code that does actually do what someone wants, but not badly enough to recode it. Now they want to send email via office 365, which requires TLS. VB6 can't do this natively, so I'm trying to launch a python script from VB6, and I think I'm pretty close, but it's not working. It feels like python has the wrong path, and I'm not sure how to fix that.

What I have tried:

First off, here's the code to check that python is installed. This works.

VB
Private Sub Test_Python(Success As Boolean)
    On Error GoTo Error_Hand

    Dim LogFileHandle As Integer
    Dim LogFileName As String
    Dim LogDateTime As String
    Dim LogFileText As String
    Dim PythonCommand As String

    'Run a python script which writes a timestamp to a text file.
    'Then read the file and check it contains the timestamp.

    Success = False

    'Python only logs warning, error, or critical levels, debug and info levels are not logged by default.
    LogFileName = App.Path & "\Python.log"
    LogDateTime = CStr(Now())

    'Python appends to the file by default. filemode='w' makes it write a new file.

    PythonCommand = "python.exe -c " & Chr$(34) _
                                     & "import logging; " _
                                     & "logger = logging.getLogger(); " _
                                     & "logging.basicConfig(filename=r'" & LogFileName & "', format='%(message)s', filemode='w'); " _
                                     & "logging.warning('" & LogDateTime & "'); " _
                                     & "raise SystemExit(0)" _
                                     & Chr$(34)

    Call ExecCmd(vbNullString, PythonCommand, vbMinimizedNoFocus)
    If gblnError_Occured Then
        lblX(0).Caption = "ExecCmd [" & glngError_Number & "] " & gstrError_Description
        Exit Sub
    End If

    'Check the logfile
    LogFileHandle = FreeFile()
    Open LogFileName For Input As LogFileHandle
    Input #LogFileHandle, LogFileText
    Close #LogFileHandle

    If LogFileText = LogDateTime Then
        'Python works
        Success = True
    End If

    Exit Sub
Error_Hand:
    'Python either not installed, or not in windows path.
    lblX(0).Caption = "Test_Python [" & Err.Number & "] " & Err.Description
End Sub


ExecCmd is a fairly generic function running CreateProcessA, WaitForSingleObject, GetExitCodeProcess, CloseHandle.

Note that this works because my entire python script is in one line, run using the -c switch.

Here's my actual code.

VB
Private Sub Email_Python()
    On Error GoTo Error_Hand

    Dim LogWasWritten As Boolean
    Dim LogError As Boolean

    Dim LogFileHandle As Integer
    Dim LogFileName As String
    Dim LogDateTime As String
    Dim LogFileText As String

    Dim PythonFileHandle As Integer
    Dim PythonFileName As String
    Dim PythonScript As String
    Dim PythonCommand As String

    PythonFileName = "3mail.py"
    LogFileName = App.Path & "\Python.log"
    LogDateTime = CStr(Now())


    'Clear logfile (Not Kill)
    LogFileHandle = FreeFile()
    Open LogFileName For Output As LogFileHandle
    Close #LogFileHandle


    'Build python command
    PythonCommand = "python.exe " & PythonFileName & " '" & gstrSMTPWord & "' '" & mstrRecipient & "' '" & mstrSubject & "' '" & mstrAttach & "' '" & mstrMessage & "'"


    'Build python script to be written to PythonFileName.

    '# Set constants, configure logging.
    PythonScript = "import logging" _
     & vbNewLine & "import os" _
     & vbNewLine & "import smtplib" _
     & vbNewLine & "import ssl" _
     & vbNewLine & "from sys import argv" _
     & vbNewLine & "from email import encoders" _
     & vbNewLine & "from email.mime.base import MIMEBase" _
     & vbNewLine & "from email.mime.multipart import MIMEMultipart" _
     & vbNewLine & "from email.mime.text import MIMEText" _
     & vbNewLine _
     & vbNewLine & "smtp_mailserver = '" & gstrSMTPServer & "'" _
     & vbNewLine & "smtp_port = " & CStr(gintSMTPPort) _
     & vbNewLine & "from_email = '" & gstrSMTPUser & "'" _
     & vbNewLine _
     & vbNewLine & "script, from_word, to_email, to_subject, file_name, text_part = argv" _
     & vbNewLine _
     & vbNewLine & "logger = logging.getLogger()" _
     & vbNewLine & "logging.basicConfig(filename=r'" & LogFileName & "', format='%(message)s')" _
     & vbNewLine & "logging.warning('DEBUG Start')" _
     & vbNewLine & "#DEBUG " & PythonCommand _
     & vbNewLine

    '# Create a multipart message, set headers, Add message to email body.
    PythonScript = PythonScript _
     & vbNewLine & "message = MIMEMultipart()" _
     & vbNewLine & "message['From'] = from_email" _
     & vbNewLine & "message['To'] = to_email" _
     & vbNewLine & "message['Subject'] = to_subject" _
     & vbNewLine & "message.attach(MIMEText(text_part, 'plain'))" _
     & vbNewLine


    If Not Len(mstrAttach) = 0 Then
        If FileExists(mstrAttach) Then
            PythonScript = PythonScript _
            & vbNewLine & "logging.warning('DEBUG Attachment')" _
            & vbNewLine _
            & vbNewLine & "try:" _
            & vbNewLine & "    attachment = open(file_name, 'rb')" _
            & vbNewLine & "    file_part = MIMEBase('application', 'octet-stream')" _
            & vbNewLine & "    file_part.set_payload(attachment.read())" _
            & vbNewLine & "    encoders.encode_base64(file_part)" _
            & vbNewLine & "    file_part.add_header('Content-Disposition', 'attachment; filename=' + os.path.basename(file_name),)" _
            & vbNewLine & "    message.attach(file_part)" _
            & vbNewLine & "except Exception as e:" _
            & vbNewLine & "    logging.error(e)" _
            & vbNewLine & "finally:" _
            & vbNewLine & "    attachment.close()" _
            & vbNewLine
        End If
    End If


    '# convert message to string

    PythonScript = PythonScript _
     & vbNewLine & "to_message = message.as_string()" _
     & vbNewLine _
     & vbNewLine & "logging.warning('DEBUG Send')" _
     & vbNewLine _
     & vbNewLine & "try:"

    '# Try to log in to mailserver and send email

    If gblnSMTP_TLS Then
        '# Create a secure SSL context
        PythonScript = PythonScript _
         & vbNewLine & "    Security_context = ssl.create_default_context()" _
         & vbNewLine & "    mailserver = smtplib.SMTP(smtp_mailserver,smtp_port)" _
         & vbNewLine & "    mailserver.starttls(context=Security_context)"

                       '# mailserver = smtplib.SMTP_SSL(smtp_mailserver, smtp_port, context=Security_context)
    Else
        PythonScript = PythonScript _
         & vbNewLine & "    mailserver = smtplib.SMTP(smtp_mailserver,smtp_port)"
    End If

    PythonScript = PythonScript _
     & vbNewLine & "    mailserver.login(from_email, from_word)" _
     & vbNewLine & "    mailserver.sendmail(from_email, to_email, to_message)" _
     & vbNewLine & "except Exception as e:" _
     & vbNewLine & "    logging.error(e)" _
     & vbNewLine & "finally:" _
     & vbNewLine & "    mailserver.close()" _
     & vbNewLine

    PythonScript = PythonScript _
     & vbNewLine & "logging.warning('" & LogDateTime & "')" _
     & vbNewLine & "raise SystemExit(0) "


    'Write script
    PythonFileHandle = FreeFile()
    Open App.Path & "\" & PythonFileName For Output As PythonFileHandle
    Print #PythonFileHandle, PythonScript
    Close #PythonFileHandle
    PythonScript = ""


    'Execute script
    Call ExecCmd(App.Path & "\", PythonCommand, vbMinimizedNoFocus)
    If gblnError_Occured Then
        lblX(0).Caption = "ExecCmd [" & glngError_Number & "] " & gstrError_Description
        Exit Sub
    End If
    
    PythonCommand = ""


    'Kill script
    'DEBUG Kill App.Path & "\" & PythonFileName


    'Check the logfile
    LogFileHandle = FreeFile()
    Open LogFileName For Input As LogFileHandle

    LogWasWritten = False
    LogError = False
    lblX(0).Caption = LogDateTime & "|"

    Do While Not EOF(LogFileHandle)
        Input #LogFileHandle, LogFileText

        If LogFileText = LogDateTime Then
            LogWasWritten = True
            Exit Do
        Else
            LogError = True
            lblX(0).Caption = lblX(0).Caption & LogFileText & vbNewLine
        End If
    Loop
    Close #LogFileHandle

    If LogWasWritten Then
        If LogError = False Then
            SaveSetting ...
        End If
    End If

    Exit Sub
Error_Hand:
    lblX(0).Caption = "Python [" & Err.Number & "] " & Err.Description
End Sub


Now if I run this, Python.log remains zero length. However, if I then execute the 3mail.py script, using the PythonCommand DEBUG settings I wrote into the script, it .. gives a stack of useful errors I can fix later. For now, I want the script to run when called from VB. Any ideas? Yes, not that idea, I already said I'm stuck with VB :p
Posted
Updated 12-Jul-19 3:18am
v2
Comments
[no name] 11-Jul-19 9:37am    
"Code that checks Python is installed" ... words fail me (you're lucky).
ThePotty1 11-Jul-19 10:01am    
Chuckle. Oh go on, I reckon I can take it.
ThePotty1 12-Jul-19 6:02am    
Actually, this may be a teachable moment, why do I not need to check if python is installed? I simplified that check to just run raise SystemExit(0), and rely on the value from GetExitCodeProcess being zero. I tested it on a computer that doesn't have python, and my program acted as if it was. Obviously my new check is garbage, but the old one not only tested python itself, it also checked logfile access. Which it turns out I don't need anymore, hence the simplified test.
Richard MacCutchan 11-Jul-19 9:42am    
Have you checked the suggestions already provided in your original post of this question?
ThePotty1 11-Jul-19 9:52am    
Sorry, I originally posted my question as the solution, so I edited it into the question, and deleted the 'solution'. Now I can't see the other suggestions anymore.

1 solution

I'd previously hacked a solution together using the -c switch, just so I had something to deliver. Having bought some time, I have now figured out the problem.

It's really simple. This was my original command:
PythonCommand = "python.exe " & PythonFileName & " '" & gstrSMTPWord & "' '" & mstrRecipient & "' '" & mstrSubject & "' '" & mstrAttach & "' '" & mstrMessage & "'"

Turns out you shouldn't parse into argv using single quotes on windows. I prefer Chr$(34) to chaining double quotes, but use whatever you find easier to read. This is the command I'm using now:
'Execute command:
'script, from_word, to_email, to_subject, text_part, file_name, log_time = argv
PythonCommand = "python.exe " _
& Chr$(34) & PythonScript_Path & PythonScript_File & Chr$(34) _
& " " & DecryptData(ParmGU_SMTPWord) _
& " " & Chr$(34) & mstrRecipient & Chr$(34) _
& " " & Chr$(34) & Replace$(mstrSubject, "\", "\\") & Chr$(34) _
& " " & Chr$(34) & Replace$(mstrMessage, "\", "\\") & Chr$(34) _
& " " & Chr$(34) & Replace$(mstrAttachPath & "\" & mstrAttachName, "\", "\\") & Chr$(34) _
& " " & Chr$(34) & Replace$(LogDateTime, "\", "\\") & Chr$(34)
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month


CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900