I have a COM automation server hosted by a VB6 exe.
The COM API is used from Excel VBA:
Dim o as MyCOMAPI.MyCOMType
Set o = new MyCOMAPI.MyCOMType
o.DoSomething
When I create objects in VBA the exe is started along with COM automation and VBA can use the API.
But the exe is closed quickly and "randomly" by Excel, I guess when Excel decides it doesn't need the COM API anymore.
This behaviour is causing random errors.
The simple solution is to start the exe before running the VBA code ; in this case all is working fine as the exe won't stop running until closed by the user.
Have you some information/documentation about the way Excel VBA manages calls to hosted APIs?
Is there a way to avoid this behaviour and have the exe kept open until the VBA code decides to stop it?
This would be the default behavior for a COM automation server when the last object is dereferenced, meaning that the variable that points to the server is set to nothing.
Now, if your code looks something like this today:
Sub MyFunction()
...
Dim o as MyCOMAPI.MyCOMType
Set o = new MyCOMAPI.MyCOMType
o.DoSomething
End Sub
Then, the server life is connected to the life of the o variable. That variable gets set to nothing when the function is finished, and then the server will be shut down (unless there are other variables keeping it alive).
To make sure that your COM server is kept alive for a longer time, simply define the variable as a Public variable as in the sample below.
This sample will start and show Excel and keep it open until the ShutdownExcel function is called.
Public o As Excel.Application
Sub MakeSureExcelIsRunning()
If o Is Nothing Then
Set o = New Excel.Application
o.Visible = True
End If
End Sub
Sub ShutdownExcel()
Set o = Nothing
End Sub
From COM docs.
**Component Automation**
Shutting Down Objects
ActiveX objects must shut down in the following way:
If the object's application is visible, the object should shut down only in response to an explicit user command (for example, clicking Exit on the File menu) or the equivalent command from an ActiveX client.
If the object's application is not visible, the object should shut down only when the last external reference is gone.
If the object's application is visible and is controlled by an ActiveX client, it should become invisible when the user shuts it down (for example, clicking Exit on the File menu). This behavior allows the controller to continue to control the object. The controller should shut down only when the last external reference to the object has disappeared.
© Microsoft Corporation. All rights reserved.
When you write an COM server exe the first thing you do it take a reference to yourself when starting as a normal exe else the exe shuts down as soon as initialisation is over.
Related
Building an automated logging process where the log calls are automatically inserted into any applications VBA code. For example, log the name of each sub or function when it is called. If sub SUB_A calls function FUNCTION_Q then the log file will read:
SUB_A
FUNCTION_Q
I need to add in log points to seperate processes so that I can see the start and end. An example output could be:
New Process started
SUB_A
FUNCTION_Q
New Process started
SUB_C
FUNCTION_D
FUNCTION_T
Issue is how to determine the start / end of a process. The Excel application may have multiple processes triggered by the user without closing Excel to reset anything. I'd thought of declaring a boolean variable (which initialises as False) that gets set to True if it is False but the value of True persists after the code has ended.
Any ideas?
Background
I have two databases that I need to connect to. One is in Hyperion and the other one is in ESS. I have imported the smartview.bas as stated by the documentation and I am attempting to use the functions within it. I have dummy sheets (SavedLogHyperion and SavedLogESS) for each enviroment to make sure the users logs in before running all the code. I want to retrieve the proper error code if the user closes the window without logging or other things that may prevent the successful login.
Problem
The HypRetrieve only acknowledge for the first result: if the user was able to log to Hyperion environment, but if ESS login window is cancelled or provided with non-valid credentials and then closed, it detects the code as 0 ("Ok"), thus detecting a successful login for the second environment when it was not.
Code
I wrote a function to retrieve the number, I thought that it could be a time thing and that is why I made it (so for the main code could resolve on time), but it seems like it is not.
Function Return_NumCodeSVHypRetrieve(VarTxtSheetToLogin As Variant) As Long
Dim NumCodeHypRetrieve As Long
NumCodeHypRetrieve = HypRetrieve(VarTxtSheetToLogin)
Return_NumCodeSVHypRetrieve = NumCodeHypRetrieve
End Function
This function is called in my main sub
Sub Main()
Dim NumCodeConnectionSheet1 As Long
Dim NumCodeConnectionSheet2 As Long
NumCodeConnectionSheet1 = Return_NumCodeSVHypRetrieve("SavedLogHyperion")
NumCodeConnectionSheet2 = Return_NumCodeSVHypRetrieve("SavedLogESS") 'If I log in "SavedLogHyperion", this variable becomes 0 too, or any other error code that variable had
End Sub
Question
How can I make the correct code according to the sheet attempted to log be correctly saved? I am clueless on what may be the approach
Solution:
The problem seems to be on how the function works; I noticed that when the function is applied, it activates the sheet, which lead me to believe that there was a problem on timing events, I came with the following solution, which has been basically to provide the scenario that I saw the function is expecting to, also I noticed that if I set the NumCode to retrieve as long as the direct result, it does not behave as expected, my approach was to declare it a variant and then cast it to a long instead.
Function Return_NumCodeSVHypRetrieve(VarTxtSheetToLogin As Variant) As Long
Dim VarNumCode As Variant
'It seems the function relies on the sheet being activated and if the Retrives does it, it takes miliseconds to do, which are not sync with excel life cycle, thus causing missreadings
Sheets(VarTxtSheetToLogin).Visible = True: Sheets(VarTxtSheetToLogin).Select: DoEvents
VarNumCode = HypRetrieve(VarTxtSheetToLogin)
Sheets(VarTxtSheetToLogin).Visible = False
Return_NumCodeSVHypRetrieve = CLng(VarNumCode)
End Function
I am trying to automate a workbook so that when it is opened it will automatically update the TFS data and send an email. I can update the TFS data successfully using a macro (found code on this blog) however when I run the code from the Workbook_Open event it errors out with "Run-time error '-2147467259 (80004005)' Method 'execute' of object '_CommandBarButton' failed". I looked at How do I resolve a VBA 'Method Execute of a [TFS Excel Add-in CommandBarButton] failed' Run-time error, but that solution doesn't work for me. This actually makes sense to me because the workbook hasn't actually connected to TFS yet - that happens AFTER the Workbook_Open completes.
My question is, is there an event after the TFS Database connection is made that I can call my code from or is there a way to force the TFS connection first? I have tried doing Application.Wait and Sleep, but that just delays the TFS Database connection.
Thanks!
I have tried doing Application.Wait and Sleep, but that just delays the TFS Database connection.
That's because these methods essentially pause the current (one and only) thread; they won't let any other (non-VBA) code run in the meantime.
the workbook hasn't actually connected to TFS yet - that happens AFTER the Workbook_Open completes.
Sounds like you need to defer execution then. Move the CommandButton.Execute code to another procedure, and then use Application.OnTime to schedule the execution of that procedure, say, 5 seconds later - assuming you pulled the offending code to some UpdateTFS procedure:
Application.OnTime DateTime.DateAdd("s", 5, DateTime.Now), "UpdateTFS"
I'm trying to invoke browser control in a Window Form using ChromiumFX framework in the Excel VSTO Addin.
The excel application throws the following alerts and another excel instance gets started.
Sorry, we couldn't find Files\Microsoft.xlsx. Is is possible it was removed, renamed of deleted ?
Sorry, we couldn't find Office\root\Office16\debug.log. Is is possible it was removed, renamed of deleted ?
Although I’m able to load the URL in the Web Browser control but i get the above errors multiple time and every time another instance of excel gets initiated.
I’m invoking the following piece of code after which the alerts start appearing.
var assemblyDir = System.IO.Path.GetDirectoryName(new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath);
Environment.CurrentDirectory = System.IO.Path.Combine(assemblyDir, #".");
if (CfxRuntime.PlatformArch == CfxPlatformArch.x64)
CfxRuntime.LibCefDirPath = #"cef\Release64";
else
CfxRuntime.LibCefDirPath = #"cef\Release";
Chromium.WebBrowser.ChromiumWebBrowser.OnBeforeCfxInitialize += ChromiumWebBrowser_OnBeforeCfxInitialize;
ChromiumWebBrowser.OnBeforeCommandLineProcessing += ChromiumWebBrowser_OnBeforeCommandLineProcessing;
Chromium.WebBrowser.ChromiumWebBrowser.Initialize();
Any suggestions on what i might be doing wrong ? Or what piece of code i’m missing ?
I am delivering external data using the dispatch interface via a COM dll to an Excel plugin. In the VBA sink method, I check that Application.Ready = True before trying to write the data to the appropriate cell like this,
If Application.Ready = True Then
With Workbooks(bookName).Sheets(sheetName).Range(rangeName)
.Value = thisData
End With
Else
Debug.Print "Dropped Payload"
End If
I don't want to just drop the data, so I've tried to do a few things to get it right.
Call Application.OnTime instead of setting the value. Unfortunately, Excel would not let me call Application.OnTime when Application.Ready=False.
Use the technique here to start a Windows timer that then calls OnTime. The problem here was that multiple events were coming in on top of each other and they were all trying to access the AfterUDFRoutine1 simultaneously, causing a crash.
So my latest thought is to put the data into a queue and then drain the queue when Application.Ready=True. To implement this, I need an event to fire when Application.Ready changes state, but I can't find the event in the list. Does anybody know if this exists and how to expose it? Alternatively, if somebody has a better idea of how to make this work I'm very open to it. The only suggestion I can't do is to use RTD instead of the dispatch I have already - I can't change that part.