Urgent. Excel automation error

G

Guest

Hi,

Sorry to trouble you again. I have a module that takes recordsets and shoves them into an excel file one at a time (a different sheet for each recordset). The code should work fine logically, and when only sending the recordsets one at a time does great. The problem comes when looping to different recordsets and going back to the workbook to select the next sheet where we can store the next loaded data set. I have put a * next to where it fails. The error is this:

Run-time error '-2147417851 (80010105)':
Automation error
The server threw an exception

Here is the relevant part of code. GetPatReprst is the sub to get the recordset, stagecounter is an integer. This is looped further up by increasing stagecounter. Always falls the second time through on the worksheet activate bit (I've tried setting stagecounter to show the name of the worksheet and everything)...

GetPatReprst
With XLObject
.Application.Visible = True
.Parent.Windows(1).Visible = True
.Worksheets(stagecounter + 1).Activate '* This is where the error occurs the second time round.
.ActiveSheet.Range("A9").CopyFromRecordset rstdata
.ActiveSheet.Range("N1:p1").EntireColumn.Delete
End With
rstdata.Close

I hope you might have an idea on how to get round this problem.

Many many thanks,

Basil
 
K

Ken Snell

What is XLObject? Unless it's a workbook object (which I doubt, after
looking at your code), your reference to the .Worksheets(stagecounter +
1).Activate in the With block seems inappropriate. My guess is that
(assuming that stagecounter + 1 is never greater than the count of
worksheets) the code doesn't know what to do with this step after the first
time through the loop, as it's not properly referenced.

Can you tell those of us that weren't involved in your apparently earlier
thread how you open the workbook, etc.?

--
Ken Snell
<MS ACCESS MVP>

Basil said:
Hi,

Sorry to trouble you again. I have a module that takes recordsets and
shoves them into an excel file one at a time (a different sheet for each
recordset). The code should work fine logically, and when only sending the
recordsets one at a time does great. The problem comes when looping to
different recordsets and going back to the workbook to select the next sheet
where we can store the next loaded data set. I have put a * next to where
it fails. The error is this:
Run-time error '-2147417851 (80010105)':
Automation error
The server threw an exception

Here is the relevant part of code. GetPatReprst is the sub to get the
recordset, stagecounter is an integer. This is looped further up by
increasing stagecounter. Always falls the second time through on the
worksheet activate bit (I've tried setting stagecounter to show the name of
the worksheet and everything)...
GetPatReprst
With XLObject
.Application.Visible = True
.Parent.Windows(1).Visible = True
.Worksheets(stagecounter + 1).Activate '* This is
where the error occurs the second time round.
 
K

Ken Snell

I obviously cannot test the entire code here, as I don't have the
appropriate data and such all set up for a test. I've gone through the code
and can't find any obvious errors.

However, it's possible that the error is occurring because you're using the
default "activesheet" type references, and this may be caused by what the
code does in the other functions.

Why not try this and see if it helps. Let's replace these three lines of
code in the SendPatReptoExcel subroutine

.Sheets(patientreport).Activate
.ActiveSheet.Range("A9").CopyFromRecordset rstdata
.ActiveSheet.Range("N1:p1").EntireColumn.Delete

to these lines:

.Sheets(patientreport).Range("A9").CopyFromRecordset rstdata
.Sheets(patientreport).Range("N1:p1").EntireColumn.Delete

This uses specific references and there is less likelihood of the wrong
spreadsheet being accessed.

Post back and let us know.
--
Ken Snell
<MS ACCESS MVP>


Basil said:
Thanks for getting back to me Ken, it is really appreciated. Here is the detail that you asked.

Firstly, I don't think my previous messages went through. Secondly, I
thought it would be easier to show you the full code. I am running MS
Access 97. I have also tried to put the activate worksheet line outside of
the With Statement - it made no difference. One thing I have noticed is
that it seems to also error the first time round if the first sheet
referenced is not the default on workbook open. Stepping through the stages
it does seem to recognise (only on the first loop) what I am asking it to do
as in Excel it will activate the appropriate sheet and even successfully
complete any worksheet_onActivate procedures (when they exist) - but then
moving back to Access it will produce the error. Here is the full code (the
simplest version - I've tried loads of alternative methods), thanks for you
thoughts:
Option Compare Database
Option Explicit

'Declare API routines:
Declare Function FindWindow Lib "user32" Alias _
"FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As Long) As Long

Declare Function SendMessage Lib "user32" Alias _
"SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long

Private dbs As Database
Private rstdata As Recordset
Public XLObject As Object 'Variable to hold MS Excel objects
Public XLRunning As Boolean 'Flag to hold initial state of MS Excel
Public reporttype As String
Public patientreport As String
Public ReferringTrust As String
Private patrepemail As String
Private rstdatacount As Long
Private nodata As Boolean



Function HasOutlook() As Boolean 'Returns 'True' if MS Outlook can be activated

On Error Resume Next

Dim OLObject As Object

Set OLObject = CreateObject("Outlook.Application") 'Attempts to open MS Outlook object
HasOutlook = (Err.Number = 0) 'Returns 'True' if
there is no error, Else 'False'
End Function

Function XLStatus() 'Enters the status
of Excel into XLRunning variable
'and registers it in Running Object Table
Const WM_USER = 1024
Dim hWnd As Long

hWnd = FindWindow("XLMAIN", 0) 'This API call returns Excel's handle
If hWnd = 0 Then '0 means Excel not running
XLRunning = False
Exit Function

Else
SendMessage hWnd, WM_USER + 18, 0, 0 'Excel is running so use the SendMessage API
XLRunning = True 'function to enter it in the Running Object Table

End If

End Function

Private Sub EmailPatXLRep()

Dim tabrecipient As TableDef
Dim rstrecipient As Recordset
Dim counter

Set dbs = CurrentDb
Set tabrecipient = dbs.TableDefs("Referring Trusts")
Set rstrecipient = tabrecipient.OpenRecordset

rstrecipient.MoveFirst

Do Until rstrecipient.EOF

ReferringTrust = rstrecipient("National Site/Trust Name")
patrepemail = rstrecipient("Patient Level Contact Email")
Forms![external reporting]!cborecipient = ReferringTrust
CreatePatXLRep

If nodata = True Then
XLObject.Close

Else
XLObject.SendMail patrepemail, "RPH Patient Report"
XLObject.Close

End If

rstrecipient.MoveNext

Loop

If XLRunning = False Then XLObject.Application.Quit

End Sub

Private Sub CreatePatXLRep()

Dim counter

counter = 0
rstdatacount = 0
nodata = True

Do
counter = counter + 1

Select Case counter

Case 1
patientreport = "Referral"

Case 2
patientreport = "Pre-Operative Clinic"

Case 3
patientreport = "Booked List"

Case 4
patientreport = "Inpatient"

Case 5
patientreport = "Post-Operative Clinic"

End Select

SendPatReptoExcel

Loop Until counter = 5

If rstdatacount > 0 Then nodata = False

End Sub

Private Sub SendPatReptoExcel()

Dim Master As String, TargetFile As String 'Variables for referencing external files

If patientreport = "Referral" Then
Master = "M:\RPH - Reporting\Reporting Components\Master External Patient Report.xls"
TargetFile = "M:\Patient Flow Team\External Reports\Patient
Reports\" & ReferringTrust & ".xls"
XLStatus 'Check if
Microsoft Excel is already running
On Error GoTo err_mastertarget
If Dir(TargetFile) <> "" Then
Kill TargetFile
End If

FileCopy Master, TargetFile

Set XLObject = GetObject(TargetFile) 'Set the
object variable to reference the target file
End If

GetPatReprst

With XLObject
.Application.Visible = True
.Parent.Windows(1).Visible = True
.Sheets(patientreport).Activate
.ActiveSheet.Range("A9").CopyFromRecordset rstdata
.ActiveSheet.Range("N1:p1").EntireColumn.Delete
End With

rstdata.Close

Exit Sub


err_mastertarget:

Select Case Err.Number

Case 53
MsgBox "The Master Excel file has been moved or it's directory has changed@Please contact the " _
& "administrator to report the problem@", vbOKOnly +
vbExclamation, "Error " & Err.Number
Case 70
MsgBox "The Master file is currently open@Please ensure it is
closed and re-run this procedure@", , _
"Error " & Err.Number

Case 75
MsgBox "The target file is currently open@Please close the file and re-run this procedure@", _
vbOKOnly + vbExclamation, "Error " & Err.Number

Case Else
MsgBox Err.Description, , Err.Number

End Select

Exit Sub

End Sub

Private Sub GetPatReprst()

Dim qry As QueryDef 'Variables for referencing the Access Objects
Set dbs = CurrentDb

Set qry = dbs.QueryDefs("qryExternal " & patientreport & " Report")

If patientreport = "Referral" Then
qry.Parameters(0) = Forms![external reporting]!cborecipient
qry.Parameters(1) = Forms![external reporting]!txtstdate

Else
qry.Parameters(0) = Forms![external reporting]!txtstdate

End If

Set rstdata = qry.OpenRecordset
rstdatacount = rstdatacount + rstdata.RecordCount

End Sub
 
G

Guest

Hi Ken,

Thanks for this suggestion. Unfortunately it makes no difference. I really urgently need to find a way around this... 30 hospitals are waiting on me! They insist on getting the data in an Excel workbook.

Is there some alternative methodology that I can use to simply shove the data from 5 different queries into 5 different sheets of the same workbook?

How would you have done it?

Thanks loads,

Basil

----- Ken Snell wrote: -----

I obviously cannot test the entire code here, as I don't have the
appropriate data and such all set up for a test. I've gone through the code
and can't find any obvious errors.

However, it's possible that the error is occurring because you're using the
default "activesheet" type references, and this may be caused by what the
code does in the other functions.

Why not try this and see if it helps. Let's replace these three lines of
code in the SendPatReptoExcel subroutine

.Sheets(patientreport).Activate
.ActiveSheet.Range("A9").CopyFromRecordset rstdata
.ActiveSheet.Range("N1:p1").EntireColumn.Delete

to these lines:

.Sheets(patientreport).Range("A9").CopyFromRecordset rstdata
.Sheets(patientreport).Range("N1:p1").EntireColumn.Delete

This uses specific references and there is less likelihood of the wrong
spreadsheet being accessed.

Post back and let us know.
 
T

Tim Ferguson

Is there some alternative methodology that I can use to simply shove
the data from 5 different queries into 5 different sheets of the same
workbook?

Do it in the workbook...


Tim F
 
K

Ken Snell

If all you want to do is to write five queries into separate worksheets in
the same workbook, use the TransferSpreadsheet command five times, once for
each stored query, and use the same file name as the target file for all
five. ACCESS will write each query onto its own spreadsheet.

See Help for more details about TransferSpreadsheet.

--
Ken Snell
<MS ACCESS MVP>

Basil said:
Hi Ken,

Thanks for this suggestion. Unfortunately it makes no difference. I
really urgently need to find a way around this... 30 hospitals are waiting
on me! They insist on getting the data in an Excel workbook.
Is there some alternative methodology that I can use to simply shove the
data from 5 different queries into 5 different sheets of the same workbook?
 
G

Guest

Cheers for the suggestions... sorry because I didn't explain it's complexity enough

The worksheets all have controls and stuff on them - the recordsets need to be pasted into row A9 of the sheets (A8 on 1 of them)

I have thought of 2 workable methods that might help
1. Allow 5 different variables to hold 5 different recordsets - open them all up, go to Excel and paste them all in - hopefully it won't crash when changing worksheet then
2. Close the workbook after pasting the data into each sheet and then re-open for the next sheet

I tried number 2 first and got a bit stuck on reopening the file for the 2nd recordset - there is another post up from me

Thanks so much
PS. Tim I did think of doing it all through Excel, but it is driven through an Access form and the looping, variable recordsets and file copying stuff seemed too complicated for me to tackle from a single click of a button on an Access Form.
 
K

Ken Snell

Your best bet will be using Automation to open the EXCEL file and then to
write into the specific cells on the sheets.

It's not overly difficult to do; here's some generic code for doing that
part (looping through the recordsets isn't difficult either):

Dim xlsApp As Object, xlsWB As Object, xlsWS As Object, xlsRng As Object
Set xlsApp = CreateObject("Excel.Application")
Set xlsWB = xlsApp.Workbooks.Open("C:\FolderName\FileName.xls", , True)
Set xlsWS = xlsWB.Worksheets("WorkSheetName")
Set xlsRng = xlsWS.Range("A1")
xlsRng.Value = VariableName
Set xlsRng = Nothing
Set xlsWS = Nothing
xlsWB.Close True
Set xlsWB = Nothing
xlsApp.Quit
Set xlsApp = Nothing



--
Ken Snell
<MS ACCESS MVP>

Basil said:
Cheers for the suggestions... sorry because I didn't explain it's complexity enough.

The worksheets all have controls and stuff on them - the recordsets need
to be pasted into row A9 of the sheets (A8 on 1 of them).
I have thought of 2 workable methods that might help:
1. Allow 5 different variables to hold 5 different recordsets - open them
all up, go to Excel and paste them all in - hopefully it won't crash when
changing worksheet then.
2. Close the workbook after pasting the data into each sheet and then re-open for the next sheet.

I tried number 2 first and got a bit stuck on reopening the file for the
2nd recordset - there is another post up from me.
Thanks so much.
PS. Tim I did think of doing it all through Excel, but it is driven
through an Access form and the looping, variable recordsets and file copying
stuff seemed too complicated for me to tackle from a single click of a
button on an Access Form.
 
G

Guest

Ken - you're a star. Thanks for all your time and help.

I actually sorted out the problem... in a very weird way. I discovered the automation error was occuring when moving on to a specific sheet - God knows what was special about it? Anyway, I created 2 new sheets in the workbook and it now seems to work fine - even my original looping code! And bizarrly - the weird sheet also now works. I'm lost to be honest, but Microsoft has a magic way of doing weird things. It's now giving me another load of weird goings on in Excel, but I need a break!

I'll keep hold of your suggestions in case this current fix ends up unreliable. Thanks so much.

Just quickly as well, you wouldn't have any idea how to avoid the prompt to enable macros when opening the workbook would you? I've put a post in the Excel community, so don't take any time if not. Also, when the email is actioned it always prompts for me to add signature or not - do you know how I can bypass this without changing the email settings or using automation? - once again, if you don't know, don't worry about it - the latter is a minor problem.

Basil
 
K

Ken Snell

I previously have searched without success for a way to bypass that "enable
macros" message box in EXCEL. Only way to do it that I've found is to lower
the security setting of the EXCEL program so that it won't ask the question.

I have not worked with email messages via code yet, so I am completely
useless to you for that question. Sorry!

--
Ken Snell
<MS ACCESS MVP>

Basil said:
Ken - you're a star. Thanks for all your time and help.

I actually sorted out the problem... in a very weird way. I discovered
the automation error was occuring when moving on to a specific sheet - God
knows what was special about it? Anyway, I created 2 new sheets in the
workbook and it now seems to work fine - even my original looping code! And
bizarrly - the weird sheet also now works. I'm lost to be honest, but
Microsoft has a magic way of doing weird things. It's now giving me another
load of weird goings on in Excel, but I need a break!
I'll keep hold of your suggestions in case this current fix ends up unreliable. Thanks so much.

Just quickly as well, you wouldn't have any idea how to avoid the prompt
to enable macros when opening the workbook would you? I've put a post in the
Excel community, so don't take any time if not. Also, when the email is
actioned it always prompts for me to add signature or not - do you know how
I can bypass this without changing the email settings or using automation? -
once again, if you don't know, don't worry about it - the latter is a minor
problem.
 
O

onedaywhen

If you could bypass the security, it wouldn't be very secure, would
it?!

Your workaround (i.e. asking the user to lower their macro security
setting) is not recommended (neither by MS nor myself) because it
potentially exposes users to harmful macros.

The preferred approach is to sign the VBA project using a digital
certificate from a recognized authority (i.e. not one created with
selfcert.exe) so the user can accept the author as a trusted source.
This way, the high macro security setting can be kept and still have
the macros enabled (and see no "enable macros" message box either).

--
 
K

Ken Snell

Your comment is fine....determining what security settings to use are
personal. My comment is based on my experience; if I know that my code is
going to open an EXCEL file with macros, then I can reduce my security
settings on EXCEL before I run my code; and then can reset it when done.

Obtaining certifications are not necessarily easy nor cheap. Likely this
won't be an attractive option for a person who's running the code on his
machine when he is in control of it.
--
Ken Snell
<MS ACCESS MVP>


..
 
O

onedaywhen

I was pointing out that your personal approach is considered to be a
security risk and an unnecessary one considering you can create "your
own certificate for personal use or testing purposes with the
SelfCert.exe tool provided in Office". Self certification *is* easy
and costs nothing. See the following article:

OFF2000: Overview of Digital Certificates
http://support.microsoft.com/default.aspx?scid=kb;en-us;206637

--
 
G

Guest

Thanks for the ideas.

Only one person will be running this part of the system.

I appreciate the recommendations from Microsoft, but I think I am going to have to work with the security stuff. It will always be the same workbook that will be referenced, and only I have access to it.

I don't know how to change the security setting - can someone advise please?

Also, what would be ideal would be to have some code in the Access module that would lower the excel security setting before opening the workbook, and then up the security setting again after closing.

Thanks,

Basil
 
T

Tim Ferguson

Also, what would be ideal would be to have some code in the Access
module that would lower the excel security setting before opening the
workbook, and then up the security setting again after closing.

No it wouldn't: this would give free rein to any wannabee Outlook worm-
writer to have unrestricted access to the victims' machines. For GS, can we
persuade Microsoft to continue plugging security gaps in the system, not
open up new ones?

In any case, you can use the MakeCert applet that comes with Office to
create a code-signing certificate for personal and restricted use.

B Wishes


Tim F
 
K

Ken Snell

Security setting is set in EXCEL by Tools | Macro | Security menu.

--
Ken Snell
<MS ACCESS MVP>

Basil said:
Thanks for the ideas.

Only one person will be running this part of the system.

I appreciate the recommendations from Microsoft, but I think I am going to
have to work with the security stuff. It will always be the same workbook
that will be referenced, and only I have access to it.
I don't know how to change the security setting - can someone advise please?

Also, what would be ideal would be to have some code in the Access module
that would lower the excel security setting before opening the workbook, and
then up the security setting again after closing.
 
G

Guest

Alright Tim, thanks - I'll try and go down the certification route

Is certification specific to individual PCs though? If I run selfcert will any PC that tries to look at the workbook know that it has been certified

I'm still a bit confused though - outlook will be connected to a mail account through pop3 - if the security is lowered, workbook changed, security raised again and then workbook sent, how could anyone mess with it? As long as the certification is not specific to 1 PC, then it will be great (there are other issues) - otherwise.... damn, I'm in trouble

Baz
 
T

Tim Ferguson

Is certification specific to individual PCs though? If I run selfcert
will any PC that tries to look at the workbook know that it has been
certified?

I make a code-signing certificate on each PC that I develop on - because
signing only works on the machine it was created with. I then import the
certificates into each of the PCs I run the code on so that they will
recognise them.

If I did a lot of distributing, I would use one particular certificate for
final signing and only install that on the target machines.
As long as the certification is not specific to 1 PC, then
it will be great (there are other issues) - otherwise.... damn, I'm in
trouble.

Code signing is specific to the PC that created the certificate. The
certificate will be recognised on the target PC as long as (a) it has been
imported/ installed onto that PC and (b) the VBA code has not been altered.

I know that entire documents can be signed too, and these will be broken as
soon as the file is resaved, so presumably to do that right each of the
users would need some kind of document-signing certificate.

I don't have a lot of experience in this area; so if someone else comes
along and assures you that the above is all rubbish, beleive them not me!

All the best


Tim F
 
T

TC

From your experience, could an installation program do the selfcert on the
target PC, so the whole singing thing was invisible to the end-user, & did
not require specific action from the end user (when he installed your
product on his PC)?

TC
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top