Continue to Site

Eng-Tips is the largest engineering community on the Internet

Intelligent Work Forums for Engineering Professionals

  • Congratulations cowski on being selected by the Eng-Tips community for having the most helpful posts in the forums last week. Way to Go!

VBA & FEMAP waiting for another application to complete an OLE action

Status
Not open for further replies.

jashizawa

Aerospace
Sep 2, 2005
7
I developed a little tool in Excel to do some load combining in FEMAP via its OLE/COM functionality. The tool operates fine, but there is one problem that I seem unable to catch. In the VBA code I create an object (FEMAP application) and thereafter I call a function femap.feFileWriteNastran which generates the Nastran DAT file for analysis. However, during this file export process an error is encountered as a result of a material which is purposely left as is. The result of the error is a message box that basically informs the user that an error was encountered in the writing process (this upon completion of writing the DAT file). The problem is that the message box essentially terminates or stops the execution of the rest of the code until the user clicks "Ok" on the message box. Otherwise, another message box in Excel appears stating that the applications is "waiting for another application to complete an OLE action".

I've tried several approaches to remedy this problem, but I have been unable to catch this error or resolve it any way. If anyone has any solutions or ideas to resolve this, it would be much appreciated
 
Replies continue below

Recommended for you

Hi jashizawa,

Have you set your Error Trapping options within VBA to Break on Unhandled Errors? If you have done this, you may need to try and use the DoEvents command, or switch to an API call with the WAITINDEFINITE flag set.

HTH
Todd
 
I have set error trapping options within VBA to break on unhandled errors and I have tried the DoEvents command as you suggested. Both seemed to be unable to resolve the problem. I am not quite sure how to implement the WAITINDEFINITE flag.

Basically, the error (which is more like a warning) occurs in FEMAP. This warning creates a dialog box with the following message "Errors have Occurred. Check Message Window". Unless the user clicks "Ok" the "Waiting for another application to complete an OLE action" dialog box in Excel pops up. It's quite troublesome, and often freezes up the computer if left idle.

I have tried to hook FEMAP window in an attempt to process the messages before having them sent to the window and do a EndDialog(hwnd, 0) sort of thing, but I have been unable to get this to work.



 
Hi jashizawa,

The best way to handle this then is probably with an API call to WaitForInputIdle. But, to use input idle, you need to make another API call to CreateProcess to launch your FEMAP program. Here is what I use to tell my application to wait indefinitely for a process to finish before executing another line of code:

In the General section of a module:
Code:
Option Explicit

Public Const STARTF_USEPOSITION = &H4
Public Const STARTF_USESIZE = &H2
Public Const STARTF_USESHOWWINDOW = &H1
Public Const SW_RESTORE = 9

Public Type PROCESS_INFORMATION
  hProcess As Long
  hThread As Long
  dwProcessId As Long
  dwThreadId As Long
End Type

Public Type STARTUPINFO
  cb As Long
  lpReserved As String
  lpDesktop As String
  lpTitle As String
  dwX As Long
  dwY As Long
  dwXSize As Long
  dwYSize As Long
  dwXCountChars As Long
  dwYCountChars As Long
  dwFillAttribute As Long
  dwFlags As Long
  wShowWindow As Integer
  cbReserved2 As Integer
  lpReserved2 As Long
  hStdInput As Long
  hStdOutput As Long
  hStdError As Long
End Type

Public Declare Function CreateProcess Lib "kernel32" _
  Alias "CreateProcessA" _
  (ByVal lpApplicationName As String, _
   ByVal lpCommandLine As String, _
         lpProcessAttributes As Any, _
         lpThreadAttributes As Any, _
   ByVal bInheritHandles As Long, _
   ByVal dwCreationFlags As Long, _
         lpEnvironment As Any, _
   ByVal lpCurrentDirectory As String, _
         lpStartupInfo As STARTUPINFO, _
         lpProcessInformation As PROCESS_INFORMATION) As Long

Public Declare Function OpenProcess Lib "kernel32.dll" _
  (ByVal dwAccess As Long, _
  ByVal fInherit As Integer, _
  ByVal hObject As Long) As Long

Public Declare Function TerminateProcess Lib "kernel32" _
  (ByVal hProcess As Long, _
  ByVal uExitCode As Long) As Long

Public Declare Function CloseHandle Lib "kernel32" _
  (ByVal hObject As Long) As Long

Public Declare Function WaitForInputIdle Lib "user32" _
  (ByVal hProcess As Long, ByVal dwMilliseconds As Long) As Long

Public Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
  (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long

Public Const SYNCHRONIZE = 1048576
Public Const NORMAL_PRIORITY_CLASS = &H20&
Public Const INFINITE = -1&
Public Const MAX_PATH = 260
Public Const BM_CLICK = &HF5

Public pInfo As PROCESS_INFORMATION
Public sInfo As STARTUPINFO
Public sNull As String
Public lSuccess As Long
Public lRetValue As Long
Public hAcadWin As Long
Public strApptoRun As String
Public strStartDir As String
Public strAcadDrive As String
Public strAcadDir As String

Public pAE As PROCESS_INFORMATION

Then in the same module, create a routine to launch FEMAP which will define the complete path and filename of the executable, in the chunk below, strApptoRun = "C:\Program Files\AutoCAD\Acad.exe":

Code:
...
..
  With sInfo
    .dwFlags = STARTF_USESIZE
    .dwX = (Screen.Width / Screen.TwipsPerPixelX) / 2
    .dwY = ((Screen.Width / Screen.TwipsPerPixelX) / 2) - 8
    .dwXSize = (Screen.Width / Screen.TwipsPerPixelX) / 2
    .dwYSize = (Screen.Height / Screen.TwipsPerPixelY) / 2
  End With

lSuccess = CreateProcess(lpApplicationName:=sNull, _
                         lpCommandLine:=strApptoRun, _
                         lpProcessAttributes:=ByVal 0&, _
                         lpThreadAttributes:=ByVal 0&, _
                         bInheritHandles:=1&, _
                         dwCreationFlags:=NORMAL_PRIORITY_CLASS, _
                         lpEnvironment:=ByVal 0&, _
                         lpCurrentDirectory:=strStartDir, _
                         lpStartupInfo:=sInfo, _
                         lpProcessInformation:=pInfo)

(Then I still use the GetObject call to set a reference to my now running session of AutoCAD).

The really important part of making this work, is the call to the API WaitForInputIdle. The way this works, is in your code whenever you make a call to your FEMAP program, such as a file open right below that line, you use this call:

Code:
objFEMAP.FileOpen "MyFile"
WaitForInputIdle pInfo.hProcess, INFINITE

The WaitForInputIdle stops all further processing until FEMAP is idle.

HTH
Todd
 
Todd,

First of all thank you so much for your suggestions. This bug is really unusual. I just implemented the code you posted but unfortunately it still hangs. However, I believe I found the root of the problem. Everything works with Excel (native app) after it creates the FEMAP process. I suspect the problem lies in the modal dialog that FEMAP displays, modal in that the dialog.show doesn't return until the user hit's OK. This is problematic since this will also halt the native app. In fact, the code never even reaches the line WaitForInputIdle:

rc = femap.feFileWriteNastran(0, path) 'hangs here
WaitForInputIdle pInfo.hProcess, INFINITE

This is getting rather interesting. I'm guessing there must be a way to preprocess the create modal dialog event before it is displayed. Perhaps subclass the WinProc of FEMAP in VBA and try to filter out the #32770 class type?

 
Actually, this bug seems very similar to what I'm seeing in Microsoft Article Q242017
 
Hi jashizawa,

Bummer, could you move your code to FEMAP and just read from Excel... Just a thought.

Todd
 
Yeah, this is just mediocre COM/OLE implementation on the part of FEMAP developers. However, I am trying another possible work around by calling FEMAP in a separate thread. I don't know how well this will work. I am getting mixed signals from posts that VBA is multithread capable and simultaneously that VBA is inherently single threaded. I'm a bit confused about this... but apparently they have the "AddressOf" capability after the introduction of VB6 which may prove to be useful here.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor