Previous Excel instance not getting through IIS - vb.net

I am using VB.NET to open the Excel files but dont want to create excel object every time.
My code is working perfectly in debug mode, but after publish, it never gets the existing instances and always create new instances which we can see from Task Manager. Here is my code which always returns false in published mode.
My OS is Windows Server 2008. Please guide how to solve this.
Function IsExcelRunning() As Boolean
Dim xlApp As Excel.Application
On Error Resume Next
xlApp = GetObject(, "Excel.Application")
IsExcelRunning = (Err.Number = 0)
MyHelper.writeLog("Excel Instance found=" & IsExcelRunning)
xlApp = Nothing
Err.Clear()
End Function
Here is how I call.
If IsExcelRunning() Then
excelApp = GetObject(, "Excel.Application")
Else
excelApp = Server.CreateObject("Excel.Application")
End If

We used to use Excel Interop and I remember it always being difficult to work with (clunky.) Due to the Interop opening an Excel process and not closing it every time you use it, makes it difficult to work with.
The Interop opens Excel automatically, so all we needed to do was close it. This is what we used to use to kill the Process. Replace YourProcessName with Excel.exe.
Dim proc As System.Diagnostics.Process
Dim info As ManagementObject
Dim search As New ManagementObjectSearcher("SELECT ProcessId FROM Win32_process WHERE caption = 'YourProcessName'")
For Each info In search.Get()
Dim TheString As String = info.GetText(TextFormat.Mof).ToString
proc = System.Diagnostics.Process.GetProcessById(Mid$(TheString, _
(Len(TheString) - 8), 4))
proc.CloseMainWindow()
proc.Refresh()
If proc.HasExited Then GoTo NoKill
proc.Kill()
NoKill:
Next
You'll need to import
Imports System.Management
You'll also need to add the reference 'System.Management' to your project.
See: http://msdn.microsoft.com/en-us/library/vstudio/wkze6zky.aspx for adding a reference to a project.
If you can rather work with CSV, I would suggest you try to. If you are creating the Excel file yourself, try to find a report creator that let's you create / export to Excel. You'll save yourself a lot of time in the long run.

Related

VBA CreateObject can’t create ActiveX component on 64Bit Windows

I have to write a program in Visual Studio 2013, but it doesn't work, so I started testing all the possibilities with Microsoft Excel and its developement playground. I started with the following version (provided by the company supplying the program I need to use):
Sub Main()
Dim XServer As X2000.Server
Dim XApp As X2000.Application
Dim XSR As X2000.ScriptRoutines
Set XServer = New X2000.Server
Set XApp = XServer.AppConnection() '<- This actually fires the application
XApp.Visible = True
Set XSR = XApp.ScriptCommands
XSR.DATA_FILENAME = "C:\X2000\myFile.x" ' <- Code blocks here
End Sub
This basically starts the application X2000.exe, connects my code to it, and then executes the DATA_FILENAME assignation routine, by loading myFile.x in the program. I need to start the application, because at that point its license is checked, and I don't have any other option to work with.
Now, in this version, the code was blocking at the last line by saying "A dll is missing" without any other information. I tried late-binding the ScriptRoutines object by doing the following:
Dim XServer As X2000.Server
Dim XApp As X2000.Application
Dim XSR As Object
Set XServer = New X2000.Server
Set XApp = XServer.AppConnection()
XApp.Visible = True
XSR = CreateObject("XApp.ScriptCommands")
This doesn't work as well, but did give me a piece of information more: "ActiveX control couldn’t load the object". I did a little research, and found out, through "Dependancy Walker", that X2000.exe was depending on MSVBVM60.DLL, which was missing in the system32 folder. Curious though, because my system is 64bit, Excel is too, and I tried the code on VS2013 by using a 32bit compatibility and still the code didn't work.
I installed the service pack from Microsoft, where this file was contained, only to find out, that system32 was left untouched...
sysWoW64 instead had already a MSVBVM60.DLL, therefore I suspected that X2000.exe was written only to work with 32bit systems: I was wrong.
A colleague of mine could execute the same program written in MATLAB code:
function [Data,eng_or_met] = Read_X2000_File_BB_2(sFileName)
XServer = actxserver('X2000.server');
XApp = XServer.AppConnection;
XApp.Visible = 1;
objX2000 = XApp.ScriptCommands;
objX2000.data_filename = "C:\X2000\myFile.x";
delete(XServer);
This code works thanks to the actxserver function, which I believe to handle this compatibility problem.
How can I do the same with Excel/VS2013?
EDIT:
I tried with the code suggested by omegastripes:
Dim XSR As Object
Set XSR = CreateObjectx86("XApp.ScriptCommands") ' create ActiveX via x86 mshta host
being CreateObjectx86 the function linked here. I tried both the If / Else conditions and at the end the object is not created anyway, both with:
Set CreateObjectx86 = oWnd.CreateObjectx86(sProgID)
and
Set CreateObjectx86 = CreateObject(sProgID)
The last line is what gets executed if I don't change the code (therefore not #If Win64).
EDIT:
As per the first answer to this question, I also tried:
Dim XServer As Object
Dim XApp As X2000.Application
Dim XSR As X2000.Script
Set XServer = CreateObject("X2000.Server")
Set XApp = XServer.AppConnection()
XApp.Visible = True
Set XSR = X2000.ScriptCommands
Only to find out I get the same error.
I apparently solved the issue. Inspired by Hans Passant, I Dimed the XServer as the only object depending on the reference to X2000.exe:
Dim XServer As X2000.Server
Dim XApp As Object
Dim XSR As Object
Set XServer = New X2000.Server
Set XApp = XServer.AppConnection
XApp.Visible = True
Set XSR = XApp.ScriptCommands
XSR.DATA_FILENAME = "C:\foo.x"
DATA_FILENAME is actually a "ScriptCommand" that gets executed.
Simple as that. Yet I have no idea why this works.

How to Link a VBA code with opened ETABS OAPI?

a project on ETABS 2015 is open on my PC. I want to do some change and process on it by a VBA code automatically. but all the sample code that I found was like this:
Public Sub Example()
Dim SapModel As cSapModel
Dim EtabsObject As cOAPI
Dim FileName as String
Dim ret As Integer = -1
'create ETABS object
EtabsObject = CreateObject("CSI.ETABS.API.ETABSObject")
'start ETABS application
ret = EtabsObject.ApplicationStart()
'create SapModel object
SapModel = EtabsObject.SapModel
'initialize model
ret = SapModel.InitializeNewModel()
'open an existing file - If no file exists, run the Save example first.
FileName = "c:\CSI_API_temp\example.edb"
ret = SapModel.File.OpenFile(FileName)
This code just open a new ETABS in my PC. but my ETABS project is already running and I want to connect to it but I don't know how!
please help me.
Your sample code is basically and typically the one needed to open ETABS ITSELF and to initialize a new model,
ETABSObject = CreateObject("CSI.ETABS.API.ETABSObject") followed by the invocation of ApplicationStart method are the direct means for that.
If you refer to the ETABS API documentation file in the installation destination (e.g "C:\Program Files\Computers and Structures\ETABS 2015\CSi API ETABS 2015.chm" ) Under the section "Release Notes>Attaching to a manually started instance of ETABS", or if you see the same section from the link
http://docs.csiamerica.com/help-files/etabs-api-2015/html/3ceb8889-9028-4de3-9c87-69a12055ade7.htm
you'll find code (in VB.Net) close to what you aim to, but it will not work with vba, so, what you will simply do is this
'The ret variable, just for method invocation convenience
Dim ret As Integer
'Create an instance of the currently opened Program
Dim myETABSObject As ETABS2015.cOAPI
Set myETABSObject = GetObject(, "CSI.ETABS.API.ETABSObject")
'Create an instance to the model
Dim myETABSModel As ETABS2015.cSapModel
Set myETABSModel = myETABSObject.SapModel
'Now, after the "reference instances" are obtained
'Initialize the model (using the model's instance of course)
ret = myETABSModel.InitializeNewModel() 'default units in kip_in_F
'Set the model templete, which is "Blank"
ret = myETABSModel.File.NewBlank()
And You can normally proceed in your coding.
Note: 1- GetObject(CSI.ETABS.API.ETABSObject) method will raise an error if ETABS is not opened.
2- you still can use GetObject even if ETABS is not opened and if you want to open it from windows shell (like using command prompt to open programs instead of double clicking on their respective icons), but this of course needs some Error Handling and the use of "Windows Script Host Object Model".
3- The examples in the documentation (under the documented methods and properties) may not be complete in terms of initiating input data, in cases like this they are just to create an impression of how to use their respective methods and properties.
The code below does as you asked. It uses VBA to connect to a currently running instance of ETABS. Please note if you're running multiple instances of ETABS it may not attach to the one you want.
You'll need to add a reference to 'ETABSv1.tlb' to your VBA project. If you're using VBA Editor for Excel, on the Tools menu select References, then click Browse then add C:\Program Files\Computers and Structures\ETABS 19\ETABSv1.tlb
Sub Main()
'create API helper object
Dim myHelper As ETABSv1.cHelper
Set myHelper = New ETABSv1.Helper
'dimension the ETABS Object as cOAPI type
Dim myETABSObject As ETABSv1.cOAPI
Set myETABSObject = Nothing
'attach to a running instance of ETABS
'get the active ETABS object
Set myETABSObject = myHelper.GetObject("CSI.ETABS.API.ETABSObject")
'get a reference to cSapModel to access all API classes and functions
Dim mySapModel As ETABSv1.cSapModel
Set mySapModel = myETABSObject.SapModel
' DO YOUR WORK BELOW.
MsgBox "Do your work here."
' DO YOUR WORK ABOVE.
'clean up variables
Set mySapModel = Nothing
Set myETABSObject = Nothing
Exit Sub
ErrHandler:
MsgBox "Cannot run API script: " & Err.Description
End Sub
ETABS comes with API documentation that you can find in the installation folder. On my machine it is here: C:\Program Files\Computers and Structures\ETABS 19\CSA API ETABS v1.chm

Using SSIS to run an Excel macro

I have an SSIS package that runs a bunch of SSRS reports and saves them in a folder as .xlsx files. I also have an Excel file with a macro that goes through each .xlsx file saved, consolidates into one file, formats and saves in another folder.
Right now this is a 2 step process, so my goal is to make it just 1 step. To do that I'm trying to add a Script Task to the end of my SSIS package to open my Excel file with the macro and run it.
I have scoured the web looking for solutions for this and found many that seem to work for people, but aren't working for me. Specifically, I'm using the code from this site.
Now when I populate the code, add my reference to Microsoft Excel 15.0 Object Library and add the Imports Microsoft.Office.Interop to the top of my script, I'm getting errors on some of the code. Please see screenshot below:
The error I'm getting is:
Reference to class 'ApplicationClass' is not allowed when its assembly is linked using No-PIA mode.
I found this site where someone seemed to have a similar issue, but it didn't help me with mine.
Is there something I'm doing wrong in the script somewhere? See below for the script itself.
' Microsoft SQL Server Integration Services Script Task
' Write scripts using Microsoft Visual Basic 2008.
' The ScriptMain is the entry point class of the script.
Imports System
Imports System.Data
Imports System.Math
Imports Microsoft.SqlServer.Dts.Runtime
Imports Microsoft.Office.Interop
_
_
Partial Public Class ScriptMain
Inherits Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
Enum ScriptResults
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
End Enum
' The execution engine calls this method when the task executes.
' To access the object model, use the Dts property. Connections, variables, events,
' and logging features are available as members of the Dts property as shown in the following examples.
'
' To reference a variable, call Dts.Variables("MyCaseSensitiveVariableName").Value
' To post a log entry, call Dts.Log("This is my log text", 999, Nothing)
' To fire an event, call Dts.Events.FireInformation(99, "test", "hit the help message", "", 0, True)
'
' To use the connections collection use something like the following:
' ConnectionManager cm = Dts.Connections.Add("OLEDB")
' cm.ConnectionString = "Data Source=localhost;Initial Catalog=AdventureWorks;Provider=SQLNCLI10;Integrated Security=SSPI;Auto Translate=False;"
'
' Before returning from this method, set the value of Dts.TaskResult to indicate success or failure.
'
' To open Help, press F1.
Public Sub Main()
Dim oExcel As Excel.ApplicationClass = Nothing
Dim oBook As Excel.WorkbookClass = Nothing
Dim oBooks As Excel.Workbooks = Nothing
Try
'Start Excel and open the workbook.
oExcel = CreateObject("Excel.Application")
oExcel.Visible = False
oBooks = oExcel.Workbooks
oBook = oBooks.Open(Dts.Variables("StrFilePath").Value.ToString()) ' Change your variable name here.
'Run the macros.
oExcel.Run("Format") ' Change the name of your Macro here.
'Clean-up: Close the workbook and quit Excel.
oBook.Save()
oExcel.Quit()
Dts.TaskResult = ScriptResults.Success
Finally
If oBook IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(oBook)
If oBooks IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(oBooks)
If oExcel IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(oExcel)
oBook = Nothing
oBooks = Nothing
oExcel = Nothing
End Try
End Sub
End Class
Any help/advice is greatly appreciated!

Getting the count of sheets in debug mode - MS Excel PIA

I cannot access .Count property of Sheets. I'm using Excel Interop. I'm in debug mode and I'm trying this:
?xlSheets.Count
This results in:
(1) : error BC30456: 'Count' is not a member of 'Sheets'.
I have no clue on what's wrong, as I see in MSDN that there is such property!
This works well: ?xlSheets(1).Name. But Count fails... Is it possible to get the count of sheets?
These guys had a similar problem - they wanted to .Worksheets.Add(.Worksheets.Sheets.Count). Finally they did not get the count, they went for .Worksheets.Add(After:=.Worksheets(3))...
UPDATE:
To my great delight, after further trying / experimentations, it became clear that in debug modeSheets.Count does not work only when there is no such line in the code.
While debugging this code, I can access Sheets.Count, because this line exists in the code.
Imports Excel = Microsoft.Office.Interop.Excel
Public Class Form1
Dim xlApp As Excel.Application
Dim xlWorkBook As Excel.Workbook
Dim xlWorkbooks As Excel.Workbooks
Dim xlSheets As Excel.Sheets
Private Sub btnCreate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCreate.Click
xlApp = New Excel.Application
xlWorkbooks = xlApp.Workbooks
xlWorkBook = xlWorkbooks.Open("C:\Temp\Template.xlsm")
xlSheets = xlWorkBook.Sheets
MessageBox.Show(xlSheets.Count)
xlWorkBook.Close()
xlApp.Quit()
'Clean Up
releaseObject(xlSheets)
releaseObject(xlWorkBook)
releaseObject(xlWorkbooks)
releaseObject(xlApp)
End Sub
Private Sub releaseObject(ByVal obj As Object)
Try
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
obj = Nothing
Catch ex As Exception
obj = Nothing
Finally
GC.Collect()
GC.WaitForPendingFinalizers()
End Try
End Sub
End Class
But when I replace MessageBox.Show(xlSheets.Count) with MessageBox.Show(xlSheets.Creator), the error appears when trying to ?xlSheets.Count. I don't yet know the reason of such behaviour (I come from VBA environment where debug mode seems to be more flexible), but at least that works during run time...
If someone knows how to fix this, please let me know, as I feel restricted while testing small things in debug mode!
Use Project > Properties > References. Locate and select the "Microsoft Excel xx.x Object Library" entry. In the Properties window, set the Embed Interop Types property to False. Use Build > Rebuild to rebuild your app. It will now work the way you expected.
Briefly, this option is a strong optimization for COM interop libraries, like Microsoft.Office.Interop.Excel, you no longer have a runtime dependency on the library. The compiler copies the interop types from the library into your program's executable, only the ones you actually need to run your program. Explains your discovery, the Count property is in fact missing when you don't use it in your program.
You don't want to leave it this way, set the property back to True after you're done testing.
Your code works fine for me. I notice the file type is a macro-enabled workbook. Have you set your macro settings properly on your dev PC? By default Excel will disable macros.
Edit: I think I get your problem now. You are getting the error when trying to print the property in debug mode. Probably you have stopped the code at a point where the variable is not set (.Count is only available while the button code is actually running). Put a breakpoint on the message box line, click the button, and try again.

Kill Excel on Error

I am hoping you can help me here, in the past you all have been great. I have tried every variation of the kill script for killing excel from vb.net, to no avail.
First I can't post explicit code on here because it is my company's proprietary software, but I can tell you a few things. Also there are over 28,000 lines of code.
I am not using
Imports Excel = Microsoft.Office.Interop.Excel
due to the fact that we have to accommodate different variations of clients software. I am creating the new excel as an object as such
Dim XLObj As Object = CreateObject("Excel.Application")
I have seen this used on several other sites but the kill function they are using is when you save and then close it, which I'm not doing.
The error message I am getting says that "Com object that has been separated from its underlying RCW cannot be used". I'm not sure where this com object is because I have released the sheets, workbook and then the application.
Oh and I don't want to use the excel.kill() because if a client already has the excel open I don't want to kill it without saving it. I only want to kill the newly generated excel process that doesn't have a window open associated with it.
My questions are as follows
I need to be able to close the Excel application when/if the open fails. So say I am click a link and it opens the dialog box to select an Excel template to load but either the data from the database is corrupt or the sql statement is broken. The program throws and error and then Excel should close in the Task Manager. Unfortunately it doesn't close hence the problem.
is there a way to close only the newly created process id? I have tried to use the directions here but it doesn't work either. When I do that it gives me a different error "Value cannot be null Parameter name: o". The line that is throwing the error is on (from the link)
Marshal.FinalReleaseComObject(tempVar)
I only tried this because we are using the With on the XLObj. The With is in reference to the workbook itself so shouldn't it be released when I close the workbook? And being as I'm causing it to error on purpose at the moment it shouldn't reach the With statement anyway.
Is there a way to tell which com object is not closing?
Things I have tried:
This releaseObject that I found on the internet. (don't ask me where I've been through about 75 pages)
Private Sub releaseObject(ByRef obj As Object)
Try
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(obj)
If obj Is Nothing Then
Else
obj = Nothing
End If
Catch ex As Exception
If obj Is Nothing Then
Else
obj = Nothing
End If
Finally
GC.Collect()
GC.WaitForPendingFinalizers()
End Try
End Sub
This is used in conjunction with this function (which was pieced together from the many sites I have been on)
Public Sub CloseExcel(ByRef WorkBook As Object, ByRef Application As Object)
Dim xLSheet As Object = WorkBook.Sheets
For Each xLSheet In WorkBook.Sheets
If xLSheet IsNot Nothing Then
releaseObject(xLSheet)
End If
If xLSheet IsNot Nothing Then
Kill(xLSheet)
End If
Next
If WorkBook IsNot Nothing Then
WorkBook.Close(False)
End If
If WorkBook IsNot Nothing Then
Kill(WorkBook)
End If
releaseObject(WorkBook)
If Application IsNot Nothing Then
Application.Quit()
End If
If Application IsNot Nothing Then
Kill(Application)
End If
releaseObject(Application)
GC.Collect()
GC.WaitForPendingFinalizers()
Application.Quit()
End Sub
and because it is also referenced the Kill function
Public Sub Kill(ByRef obj As Object)
Try
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(obj)
Catch ex As Exception
MessageBox.Show("moduleExcel.Kill " & ex.Message)
Finally
obj = Nothing
End Try
End Sub
any help would be greatly appreciated.
Ok so for those of you having this exact same issue. I do have a solution for you. Yes the above code does work but for a few minor adjustments.
you need to take out all the code in the CloseExcel sub and place it EXACTLY where you want it to close. So if you want it to close if the program errors out, put after the catch statement. You cannot call a Sub and pass in your objects and expect it to kill the process.
you need a few bits above the opening of the new Excel process. and they are as follows.
'declare process for excel
Dim XLProc As Process
'loads the financials excel bookmarks
'this will be where you declare your new excel opbject
Dim XLObj As Object = CreateObject("Excel.Application")
'get window handle
Dim xlHWND As Integer = XLObj.hwnd
Dim ProcIDXL As Integer = 0
'get the process ID
GetWindowThreadProcessId(xlHWND, ProcIDXL)
XLProc = Process.GetProcessById(ProcIDXL)
and of course you will need the GetWindowThreadProcessId which I got from the link I included in the original question. I am posting it here so you don't have to search for it.
<System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Private Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
End Function
This code will only close the single process you have it associated with, it will not close other open Excel files. Our clients sometimes will have multiple files open and we don't want to close them without telling them. This KILLS the Excel process that was created at run time when the system Errors out.