Search code examples
pythoncom

Get name of active Excel workbook from Python


I am trying to write a Python script that will access and modify the active Excel workbook using the Excel COM interface. However, I am having difficulty getting this to work when there are multiple Excel instances running. For example, the code

import win32com.client

xl = win32com.client.Dispatch("Excel.Application")
print(xl.ActiveWorkbook.FullName)

prints out the name of the active workbook from the first running instance of Excel only. What I really want is the workbook that I last clicked on, regardless of what Excel instance it was in.

Thanks.


Solution

  • EDIT FOR COMMENTS

    There might be a better way to do this.

    Install the excellent psutil

    import psutil
    excelPids = []
    for proc in psutil.process_iter():
      if proc.name == "EXCEL.EXE": excelPids.append(proc.pid)
    

    Now enumerate the windows, but get the window title and pid.

    windowPidsAndTitle = []
    win32gui.EnumWindows(lambda hwnd, resultList: resultList.append((win32gui.GetWindowThreadProcessId(hwnd),win32gui.GetWindowText(hwnd))), windowPidsAndTitle)
    

    Now just find the first pid that is in our excelPids

      for pid,title in windowPidsAndTitle:
        if pid in excelPids:
          return title 
    

    END EDITS

    There is a number of things to take into consideration here:

    Does one instance have multiple workbooks open? In this case

    xl = win32com.client.Dispatch("Excel.Application")
    xl.ActiveWorkbook.FullName
    

    Will indeed give you the last active workbook.

    Or are there separate instances of EXCEL.EXE running? You can get each instance with:

    xl = win32com.client.GetObjec(None, "Excel.Application") #instance one
    xl = win32com.client.GetObject("Name_Of_Workbook") #instance two
    

    But this defeats the purpose because you need to know the name AND this will not tell you which one last had focus.

    To @tgrays comment above, if your excel instance is guaranteed to be the foreground window then:

    import win32gui
    win32gui.GetWindowText(win32gui.GetForegroundWindow()) 
    #parse this and use GetObject to get your excel instance
    

    But worst case scenerio, multiple instances and you have to find which had focus last, you'll have to enumerate all the windows and find the one you care about:

    windows = []
    win32gui.EnumWindows(lambda hwnd, resultList: resultList.append(win32gui.GetWindowText(hwnd)),windows)
    #enumerates all the windows open from the top down
    [i for i in windows if "Microsoft Excel" in i].pop(0)
    #this one is closest to the top
    

    Good luck with this one!