title: How to write VBA code that works in multiple Office apps
body: <p>I want to write a single VBA code module that works on the three main Office Apps (Excel, PowerPoint, Word).</p>
  
  <p>Because the object models are different in each app, if I write code that's specific for PowerPoint while in the Excel VBE, the project won't compile. The way to go first appears to be to use conditional compiler constants. But this still causes the VBE to spit out errors depending on which MSO app the VBE is currently being hosted in.</p>
  
  <p>In the simplified example below, I want to add a picture to a sheet, slide or document, depending on which app the VBA code is being run from. If I try to compile it in Excel, the PowerPoint code doesn't compile (even though it's within a conditional compiler If...Then statement!) and vice-versa. How does one get round this without adding references to the other MSO apps (as this causes compatibility issues when distributing to different MSO versions)?</p>
  
  <p>The way the compiler continues to look at code that should be effectively "commented out" by the conditional compiler constants is very odd/annoying behaviour!</p>
  
  <pre><code>' Set the compiler constant depending on which MSO app is hosting the VBE
  ' before saving as the respective .ppam/.xlam/.dotm add-in
  #Const APP = "EXL"
  
  Option Explicit
  
  Dim curSlide As Integer
  Dim curSheet As Integer
  
  Public Sub InsertPicture()
      Dim oShp as Shape
      #If APP = "PPT" Then
          ' Do PowerPoint stuff
          ' The next 2 lines will throw "Invalid qualifier" and
          ' "Variable not defined" errors respectively when compiling in Excel.
          curSlide = ActiveWindow.View.Slide.SlideIndex
          Set oShp = ActivePresentation.Slides(curSlide).Shapes.AddPicture &amp; _
              (filename, msoFalse, msoTrue, 0, 0)
      #ElseIf APP = "EXL" Then
          ' Do Excel stuff
          curSheet = ActiveWindow.ActiveSheet
          Set oShp = ActiveSheet.AddPicture(filename, msoFalse, msoTrue, 0, 0)
      #ElseIf APP = "WRD" Then
          ' Do Word stuff
      #End If
  End Sub
  </code></pre>
  
  <p>Since I'm unable to answer my own question:</p>
  
  <p>Expanding on your idea KazJaw, I think something like this may work, replacing the CreateObject function with GetObject (because the instance will already exist since the procedure is being called from within an add-in):</p>
  
  <pre><code>' CONDITIONAL COMPILER CONSTANTS
  ' Set this value before saving to .ppam, .xlam or .dotm
  #Const APP = "EXL" ' Allowed Values : PPT, EXL or WRD
  
  Sub One_Sub_For_Word_Excel_PP(filename As String, Optional SlideIndex as Integer)
      #If APP = "PPT" Then
          Dim appPPP As Object
          Set appPPT = GetObject(, "PowerPoint.Application")
          appPPT.ActivePresentation.Slides(SlideIndex).Shapes.AddPicture &amp; _
              (filename,msoFalse,msoTrue,0,0)
      #ElseIf APP = "EXL" Then
          Dim appEXL As Object
          Set appEXL = GetObject(, "Excel.Application")
          appEXL.ActiveSheet.AddPicture(filename, msoFalse, msoTrue, 0, 0)
      #ElseIf APP = "WRD" Then
          Dim appWRD As Object
          Set appWRD = GetObject(, "Word.Application")
          appWRD.ActiveDocument.AddPicture(filename, msoFalse, msoTrue, 0, 0)
      #End If
  End Sub
  </code></pre>