Mac 2011 using some functions in VBA - excel

I have recently transferred a working set of routines from Windows Excel 2003 and tried to run them in Excel for Mac 2011. It fails on the first attempt to use the left function. With the error message "cannot find project or library". I have tried setting up a test function to run various text functions and things like Len work but neither left right or mid. The routine is the start of a process to handle exported Access data. Do not think that is relevant as the routine does not start because of the problem with left.
This is my test code.
Function testfunctions()
Dim carrier, carrier2
carrier = "Alpha1"
carrier2 = Left(carrier, Len(carrier) - 4)
MsgBox carrier
End Function
Any suggestions greatly appreciated!

What happens if you remove an errant space in this line (yours)
carrier 2 = Left(carrier, Len(carrier) - 4)
Change to this:
carrier2 = Left(carrier, Len(carrier) - 4)

Related

Stepping through with F8 the code runs, stops in standard execution with F5

I've written a macro which sums numbers, grouping by year and by month based on our projection model.
It runs from start to end when stepping through with F8.
It stops immediately in standard execution with F5.
The first trouble is
Runtime error 91
in
issmIndex = Range("A1:Z1").Find("ck.IssMon").Column 'issmIndex an integer
Originally I tried Application.WorksheetFunction.Match(...) but had the same problem: runs in debug, but not in execute (Error 1004 instead).
I considered it could have been an Excel version issue (the Match function has a different name in the Italian version). I switched to a more neutral Find, but still no luck.
When you have an error with a line that is a combination of several commands, try breaking it down into the individual steps.
For example, this works:
Sub findDemo()
Const toFind = "blah"
Dim rg As Range, f As Range
Set rg = Range("A2:C5")
Set f = rg.Find(toFind)
If f Is Nothing Then
Stop 'not found
Else
Debug.Print "found in column #" & f.Column
End If
End Sub
Also see the example in the documentation for Range.Find().
Welcome to SO. Sometimes Excel reads code faster than executing, so when reading a command there is a previous one not finished. IT's weird but it happens a lot if your code does a lot of stuff and calculus.
Besides, when debugging, every command line is executed before reading next one, so you cannot detect this just debugging.
So if your code runs perfect when debugged but errors if executed as normal, try to add the command DoEvents right before the problematic line. Something like this:
' your previous code
'
'
'
Doevents
issmIndex = Range("A1:Z1").Find("ck.IssMon").Column 'issmIndex an integer
'
'rest of your code
This commands forces Excel to make sure everything has been executed before reading. It's kind of like a checkpoint, something like make sure you've done everything before going to next line.
DoEvents
function

How to query files (csv, bmp) using NI-488.2 from a GPIB port in VBA (Excel)

Using VBA, I tried to use the NI-488.2 calls directly, instead of using VISA COM. However, when I try to use NI-488.2 commands in VBA, I get the following error message :
''Sub or Function not defined''.
That is, when calling Call SendIFC(BOARD_ID) or Call EnableRemote(BOARD_ID, intAddrList()), Call SendGPIB(osa, ":mmem:stor:grap color,bmp,""test"",int"), = RecieveBinaryGPIB(osa, byteData()), etc...
My first guess would be that I am simply missing the correct library. (Or is there any deeper issue here?). For now, I added the VISA COM 3.0 Type Library and the VISA COM 488.2 Formatted I/O 1.0 to the Excel references.
Here is a sample code from YOKOGAWA (slightly edited) that can be found online and applies to Visual Basics, but that I could not run in VBA. I removed most of the code for clarity purpose. It simply saves a screenshot in internal memory.
Private Sub SaveImage()
Const BOARD_ID = 0 'GP-IB Interface card Address
Const osa = 1 'OSA GP-IB Address
Dim intAddrList(31) As Integer
'----- GP-IB Interface setting
' send IFC
Call SendIFC(BOARD_ID)
' assert th REN GPIB line
intAddrList(0) = NOADDR
Call EnableRemote(BOARD_ID, intAddrList())
' GPIB time out setting
Call ibtmo(BOARD_ID, T30s) 'Time out = 30sec
'----- send command to OSA
Call SendGPIB(osa, "CFORM1") ' Command mode set(AQ637X mode)
Call SendGPIB(osa, ":mmem:stor:grap color,bmp,""test"",int")
'----- Disconnect
Call EnableLocal(BOARD_ID, intAddrList())
End Sub
Now we have the code... you have now edited out the useful bits... I'm going to backtrack a bit and say that even if you get direct calls to work you’re still going to have a problem extracting the files.
I’m going to guess you’re getting "Undefined header" errors if you query the OSA with "SYST:ERR:ALL?" and this is quite possibly because the query command for the data is invalid.
I’ve notice you’re using including the data type your command:
":mmem:data? ""test.bmp"",int"
"test.bmp",int,0 is what you might see if you query the directory, however, it is not needed when extracting the file.
try using:
":mmem:data? ""test.bmp"""
(The number of quotations are to include them in the string)
If not, what is the GPIB error?

How can I programatically copy and paste with source formatting in PowerPoint 2010?

I am currently automating some PowerPoint 2010 functions in Groovy using Scriptom - though this problem may be generic to any PowerPoint automation approach (ie more of a "VBA macro" issue than the particular environment I'm using?).
(Scriptom allows you to use ActiveX or COM Windows components from Groovy. Underneath the hood it uses the Jacob library (Java COM Bridge), I believe. The underlying code is similar to what I'd use in a VBA macro or other Microsoft automation component, and is based on the PowerPoint 2010 Object API.)
My current code works well and opens PowerPoint visibly and does a range of functions on it - except for a component where I "copy and paste" slides from one document to another, "keeping source formatting".
I have tried two attemps to do this copy and paste step, both leading to a different problem. I wonder if anyone has thoughts on solving either (or both?) of these problems:
Method 1: I use a basic "copy" and "paste" methods, suggested by various others, namely:
sourceSlide.Copy()
destinationSlide = destinationPresentation.Slides.Paste(slideIndex+i-1)
destinationSlide.Design = sourceSlide.Design
destinationSlide.ColorScheme = sourceSlide.ColorScheme
destinationSlide.FollowMasterBackground = sourceSlide.FollowMasterBackground
... and so on copying formats...
That is, I manually copy all the formats across to keep slide-formatting. This is the method used prior to PowerPoint 2010. I've actually got this working, however to copy the formats I loop over each slide in the "source" slidepack and do the copy/paste code above. In this loop, the following line (alone) is problematic:
destinationSlide.Design = sourceSlide.Design
This line runs incredibly slowly once the destination SlidePack has a large number of "Designs" in the SlideMaster. I am copying a source slide-pack of 19 slides, each of which has a different SlideMaster Design Theme (that's how it comes to me). This single line of code takes about 0.01 seconds for copying the first slide over, but by the time it comes to the final slide in the loop, the single line of code takes over 20 seconds to run each time. Thus, copying the first five slides might take <1 second, but the total 20 slides takes around 100 seconds in total, with all the latter slides taking longer and longer to run just this single line. The rest of the code races by!
The slow-down isn't linear, and gets even worse beyond 20 slides. It isn't related to the content on the final slide(s), but seems to be that as the number of SlideMaster "designs/themes" increases it slows exponentially to copy across the "sourceSlide.Design". I realise having a different "Design" object for each slide is a bit of a waste, but I don't own the initial source presentation, and often they do come to me like this, with just slight difference between the Designs for each slide. If I remove the "destinationSlide.Design" line the time it takes can reduce from 100+ seconds to around 1 second!
Method 2: In order to avoid this, and given I'm using PowerPoint 2010, I tried to use the following code, instead:
sourceSlide.Copy()
def destinationPresentation = objPpt.Presentations.Open(destinationFilename)
destinationPresentation.CommandBars.ExecuteMso("PasteSourceFormatting")
I believe this should provide direct access to PowerPoint 2010 "Paste with Source Formatting" functionality. However, this fails with a "null pointer exception" at the ExecuteMso("PasteSourceFormatting") line.
What am I doing wrong? Is there any way to speed up the slow line in Method 1? Why is method 2 not working at all? It looks like "destinationPresentation.CommandBars" is not null, but that the "ExecuteMso" line throws a null pointer exception.
Are there any other suggestions for efficiently "copy and paste" slides that should work in a reasonable time frame for 20-100 slides, even if there are multiple different designs/themes?
Thanks, in advance, for any ideas.
The problem with method 2 is I was using:
destinationPresentation.CommandBars.ExecuteMso("PasteSourceFormatting")
Whereas this should have been:
destinationPresentation.Application.CommandBars.ExecuteMso("PasteSourceFormatting")
Using this code, I no longer get the null pointer exception.
I've submitted this as an answer to assist anyone who makes a similar error in future.
That being said, I still find that the performance of this approach (method 2) is not significantly better than the manual "copy and paste formats" method (method 1). In both cases, the performance with "paste with source formatting" functionality is many, many times slower than a normal "paste" - and takes around 2 minutes to do the paste of about 20 slides (each with its own design template). This is reduced to less than a second if I use "destination formatting", or don't have each slide with its an individual design template.
This may just be an issue with PowerPoint 2010 performance, however, so I will accept this answer unless someone has more information that would provide a better solution to the performance aspect of the original query.
Not sure it helps, but you can do this sort of thing with Apache POI:
#Grab( 'org.apache.poi:poi-ooxml:3.10-beta1' )
import org.apache.poi.xslf.usermodel.XMLSlideShow
new File( '/tmp/Presentation1.pptx' ).withInputStream { p1 ->
new File( '/tmp/Presentation2.pptx' ).withInputStream { p2 ->
// Load our 2 presentations
inpptx = new XMLSlideShow( p1 )
outpptx = new XMLSlideShow( p2 )
// Add slide 1 from inpptx to the end of outpptx
outpptx.createSlide().importContent( inpptx.slides[ 0 ] )
// Save it out again to a 3rd presentation
new File( '/tmp/Presentation3.pptx' ).withOutputStream { out ->
outpptx.write( out )
}
}
}

Side-Step Application.MsgBox in VBA (Excel)

In order to head off a storm of "comment it out" replies, here is my situation:
I have a process is normally run 1 iteration by 1 iteration. A user manually hits a button that calls a macro which, upon completion, pops up a message box that reports the total time length the macro ran for. It's pretty handy for diagnosing issues. This code is locked down and I cannot modify it.
I am trying to do this at scale. Because the code in the main spreadsheet and workbook are locked, I have a separate workbook open in the same instance of excel with a macro that operates the locked down workbook. Rather than 1 by 1, I've got a set of 300 I'm trying to run through. Right now I have to babysit the thing and hit space to get past the MsgBox. Does anyone know of any tricks to prevent me having to monitor the thing? Either disabling the pop-ups or some way to make them non-modal. Maybe a trick to make the mouse click?
You're right in knowing that the best way to fix the issue is to correct the code. In which case you would probably make the pop-ups toggle-able.
However, I wrote this for you which could be used as a potential work around. It utilizes VBScript to "sort-of" simulate multithreading so that you can send a key to the modal Msgbox. Assuming you can do what you want to do via code, simply call SendDelayedKeys before the action that will cause a Msgbox. You may have to tinker with the Delay based upon your circumstances as 100 milliseconds may not be enough. To change the Delay, just call like this: SendDelayedKeys 500 for 500 milliseconds.
Sub SendDelayedKeys(Optional Delay As Long = 100, Optional keys As String = """ """)
Dim oFSO As Object
Dim oFile As Object
Dim sFile As String
sFile = "C:\SendKeys.vbs" 'Make this a valid path to which you can write.
'Check for the .vbs file.
If Not Len(Dir$(sFile)) Then
'Create the vbs file.
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFile = oFSO.CreateTextFile(sFile)
oFile.WriteLine "Set WshShell = WScript.CreateObject(""WScript.Shell"")"
oFile.WriteLine "WScript.Sleep CLng(WScript.Arguments(0))"
oFile.WriteLine "WshShell.SendKeys WScript.Arguments(1)"
oFile.Close
End If
Shell "wscript C:\SendKeys.vbs " & Delay & " " & keys
End Sub
Sub ProofOfConcept()
'Using default parameters which sends a space after 100 milliseconds
SendDelayedKeys
MsgBox "I disappear on my own!"
End Sub
A word of warning: Any solution that utilizes SendKeys is a fragile solution and should be avoided when possible. However, when your options are limited and you need to avoid a manual process, sometimes it's your only option.
Since SiddhartRout rightly pointed out that this could be solved using API calls: here's a link with C# code that would close your msgbox every second.
The problem here really isn't strictly a problem more code can (or indeed should) solve.
There are a great many things to consider and any solution will be more complex AND less reliable than the problem it is initially trying to solve. But lets look at your options...
SendKeys is not reliable for that kind of use, what happens if the dialogue says "would you like me to save this workbook?" just after making a change that was meant to be temporary or "would you like to play global thermonuclear war?" Plus with a batch process like that you want to get on with something else while you wait, even if it's only to come here to downvote trolls. If nothing else you may not be in control of this code so what kind of mess will it cause when the maintainers realise msgbox is bad UX and kill it?
FindWindow API calls would let you check the content in the window to make sure it says what you're expecting but then you're potentially asking some bit of quick & dirty vbscript to go into a race condition until the right window comes up. Can you guarantee that the threads won't lock up?. What about platform issues - what happens if someone wants to run your code on their shiny new surface? What happens when your 64 bit modal Excel dialogue window can't be seen by the 32-bit api calls you were making? What about a new version of office that doesn't present modal dialogues in the same way? None of those problems are insurmountable but each adds complexity (aka opportunity for failure.)
The best solution is fix the actual problem you have identified from the outset which is that the original code throws up an unnecessary modal dialogue. Someone needs to fix that - it doesn't have to be you but if you estimate how much time that modal dialogue wastes in lost productivity that should get you a solid business case for getting it sorted.

How do I set up a COM Server interface with Excel?

Versions
Excel 2003Windows XP SimaPro 7.3.0 Developer Version Using a Work computer but was made administrator on this machine Libraries referenced in Excel/VBA: Visual Basic for Applications; Microsoft Excel 11.0 Object Library; OLE Automation; Microsoft Office 11.0 Object Library; Microsoft Forms 2.0 Object Library; COM+ 1.0 Admin Type Library; COM MakeCab 1.0 Type Library; COM+ Services Type Library; SimaPro Library Me: Beginner
What I'm trying to do
I am using a program called SimaPro that stores databases of "Life Cycle Analysis" information. The program has built in COM interface functionality. The program states that it does, indeed support Excel/VBA (but it doesn't specify versions).
I am trying to connect this program and/or COM server to excel so that I can interact with the information through excel.
What I've done
I've done the procedure they list:
-Open SimaPro
-Register COM Server
-Then I pasted the below code into VBA and tried to run it. This code is the sample code provided by the software company, I edited only the SP.Server, SP.Alias, SP.Login, and SP.OpenProject fields (below is as edited).
What Happened
Run-time Error: '-2147418113 (8000ffff)':
Automation Error
Catastrophic Failure
Question(s)
-Is the server name right? I've been reading a little on COM servers and I don't know if the way I put it in is in the right "form"
-Could it have something to do with certain registered/unregistered DLLs? I've worked with the company's IT people, and software programmers. None of them were very familiar with COM but one person suggested the DLLs might be the issue.
Thanks for your help!!
Here is the code that I'm inputting:
Sub CreateProcess()
Dim SP As SimaProServer
Dim PC As Process
Dim PC2 As Process
Dim PL As ProcessLine
Dim Param As ParamLine
Dim Subs As Substance
Set SP = New SimaProServer
SP.Server = "Local Server"
SP.Alias = "C:\Documents and Settings\All Users\Documents\SimaPro\Database\"
SP.Database = "Professional"
SP.OpenDatabase
SP.Login "", ""
SP.OpenProject "PROJECT", ""
' Not project's actual name, not allowed to state name of project
SP.CreateSubstance "Air", Subs
Subs.CASNumber = "4-5-13"
Subs.Name = "Some substance"
Subs.DefaultUnit = "kg"
Subs.Update
SP.CreateProcess ptMaterial, PC
Set PL = PC.AddLine(ppProduct, -1)
PL.ObjectName = "Steel 2"
PL.UnitName = "kg"
PL.Amount = "2"
PL.Comment.Add ("My new created process")
PL.CategoryPath = "Chemicals\inorganic"
PC.Update
' create second material process Case
SP.CreateProcess ptMaterial, PC2
Set PL = PC2.AddLine(ppProducts, 0)
PL.ObjectName = "Case 2"
PL.UnitName = "kg"
PL.Amount = "10"
Set Param = PC2.AddParamLine(ptInputParameter, -1)
Param.Name = "A"
Param.Value = "2,3"
' add input from Steel
Set PL = PC2.AddLine(ppMaterialsFuels, -1)
' input from steel
PL.SetProduct "Introduction to SimaPro 7", ptMaterial, "Steel 2"
PL.Amount = "8"
PL.UnitName = "kg"
Set PL = PC2.AddLine(ppAirborneEmissions, -1)
' input from steel
PL.SetSubstance "Some substance", ""
PL.Amount = "A+1"
PL.UnitName = "kg"
PC2.Update
SP.Logout
SP.CloseDatabase
Set SP = Nothing
End Sub
Given that this was more than one year ago. I'm assuming you got this working. If you haven't yet, I might know what the root cause might be.
I used to get the same error and from your changes for server, alias and login, I was able to make it run. One thing that is different is that you have changed the name of the project to "Project" from "Introduction to SimaPro 7". I honestly have zero (not being humble here) VBA knowledge. So, I'm speculating that there is no project names "Project" to open. I'm not sure if VBA would create a project automatically, if it can't find it. You can either try creating a project named "Project" or just rename it back. I am interested to see if that worked.
Automation Error usually means that there was a problem within the COM library you try to use. As it is a run-time error, it could be something very stupid, as missing parameter or wrong path or access rights. In my opinion, it also means that the library is not very well designed.
As you are not the author of the library you do not have many options. You can try to contact the vendor to get more documentation. You can also pray that the designer thought about logging - check the event log; if you are lucky you may find something interesting there.
Answering your first question, if you referenced the library and the code compiles - that means that you did everything right there.

Resources