Sign in to follow this  
Followers 0
Spiff59

ObjGet failing?

26 posts in this topic

#1 ·  Posted (edited)

ObjGet doesn't seem to be doing what I thought it was supposed to do.

I parked a test.doc, test.txt and test.xls file in my root directory and started banging around with the following code:

While 1     
    $file = "C:\" & InputBox("","ObjGet target file in C:\ folder? ")
    If $file = "C:\" Then Exitloop
    $oFile = ObjGet($file)
    If @error Then
        msgbox(1,"", "Not found: " & $file)
    Else
        msgbox(1,"", "Found : " & $file)
    EndIf
WEnd
Exit

For me, ObjGet responds like this:

1. If the filename references an existing .txt file, autoit3.exe abends with 0xC0000005 at offset 42A376.

2. If a file is currently open by some process ObjGet returns quickly with a 0 (file found)

3. If a file is NOT CURRENTLY open by some process, but WAS accessed previously, ObjGet chews on my processor for way too long then errantly returns a 0 (file found)

4. If a file either does not exist, is not a .txt file (abend), or was not previously open (false positive), then ObjGet properly returns an error condition (not found)

Where have I gone astray?

Thanks.

Edited by Spiff59

Share this post


Link to post
Share on other sites



ObjGet is for COM objects.

For files, use the File... functions (FileOpen, FileRead, etc.)

If I wanted to directly access the files in that manner, I'd surely use one of those functions.

I want a COM object pointing to a file with an open handle, so think I'm on the right track.

If ObjGet doesn't work this way, then the guys using it in _WordDocAttach and _ExcelBookAttach are magicians!

Per the ObjGet Helpfile:

Retrieves a reference to a COM object from an existing process or filename

Per the ObjCreate Helpfile:

If you want to connect to an existing process, use ObjGet() instead.

Per the ObjGet example:

; An Excel file with filename Worksheet.xls must be created in the root directory

; of the C:\ drive in order for this example to work.

$FileName="C:\Worksheet.xls"

$oExcelDoc = ObjGet($FileName) ; Get an Excel Object from an existing filename

Am thinking this is an actaul AutoIT bug, but am not sure why others haven't encountered it.

Is everyone else up to SP3 of XP Pro yet (am clutching at straws)?

Any other ideas?

Thanks.

Share this post


Link to post
Share on other sites

A file (.txt, .xls, etc.) is NOT an object so you can't "Get" it with ObjGet(). There is a table in Windows of Running Objects called, mysteriously enough, the Running Objects Table (ROT). You can only ObjGet() an object that exists in the ROT. To create a new object, where one does not yet exist, requires ObjCreate(), and in that case you still wouldn't pass it a string file path, but rather an object name that can be looked up in the registry, i.e. "InternetExplorer.Application" or "Excel.Application".

:P


Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites

You *can* use ObjGet() on files - files that support it, that is. Office files do. It's perfectly fine to ObjGet() a Word or Excel file. Text files? No, they do not support it.

The only bug you found was the crash on text files. AutoIt should never hard-crash. Everything else seems to be working fine.

Share this post


Link to post
Share on other sites

Is everyone else up to SP3 of XP Pro yet (am clutching at straws)?

Your clutch is broken. Replace the clutch. :P

My UDFs: ExitCodes

Share this post


Link to post
Share on other sites

A file (.txt, .xls, etc.) is NOT an object so you can't "Get" it with ObjGet().

I understand that.

I'm expecting ObjGet to tell if if the file is open in Excel, or Word, or Notepad, etc, and return a structure allowing me to manipulate the process (not the file directly).

That's all I want to do. The docs/examples indicate ObjGet does that, but it does not work for me.

_ExcelBookAttach no longer functions for me, and the fault is in the ObjGet call.

Also, abending autoit3.exe can't be acceptable bahavior for ObjGet?

Share this post


Link to post
Share on other sites

You *can* use ObjGet() on files - files that support it, that is. Office files do. It's perfectly fine to ObjGet() a Word or Excel file. Text files? No, they do not support it.

The only bug you found was the crash on text files. AutoIt should never hard-crash. Everything else seems to be working fine.

Thanks, Valik.

Ok, that explains #1 in my original post: ObjGet is not intended to handle some file types, but has insufficient error handling to avert abending autoit3.exe when an unsupported file type is specified.

As to #3?

I can close out of Excel or Word, and ObjGet goes into "be back in 3 seconds with the wrong answer" mode.

If an Office file has previously been opened, but is now closed, ObjGet "thinks" way too long and then continues to report the file as in use.?

I've run SysInternal's Process Explorer and ensured there are no processes/handles referring to either the Word/Excel application, or the data file directly, even though ObjGet keeps reporting a "found" condition.

Thanks! I appreciate the input!

Share this post


Link to post
Share on other sites

This is why I bitched at you on the issue tracker and why you're about to get more. You have never explicitly stated your expectations, but I've just discerned them, I think. But your wording and your test script are stupid. If I'm understanding you correctly, everything is working just fine but your expectations are all wrong.

When you call ObjGet() on an Office file you're going to get an object back. If the file is open in another instance of Office, I believe that you get a reference to the object there. If the file is not already open, a new instance is created. This seems to be in-line with your shittily worded description:

2. If a file is currently open by some process ObjGet returns quickly with a 0 (file found)

3. If a file is NOT CURRENTLY open by some process, but WAS accessed previously, ObjGet chews on my processor for way too long then errantly returns a 0 (file found)

You state that in both cases a 0 is returned which I assume mearns @error is set to 0 since you claim the file is "found" in both cases. That is correct. In the first case, ObjGet() gets an existing object from the ROT. In the second case it has to instantiate the object which involves starting up the Office program - hence the delay.

As I've said a couple times now, the only bug you found was the crash. Everything else is working exactly as designed. You could have saved a lot of us a bunch of trouble had you just stated what your expecatations were from the beginning.

Share this post


Link to post
Share on other sites

I'm expecting ObjGet to tell if if the file is open in Excel, or Word, or Notepad, etc, and return a structure allowing me to manipulate the process (not the file directly).

There we go, there's your expectation. And as expected, it's totally wrong. The process is what is providing the COM interface for interacting with the file (and itself). Office applications have a COM interface, Notepad does not. You can't just blindly assume you can use ObjGet() with anything and it will magically work. There has to be appropriate COM interface.

_ExcelBookAttach no longer functions for me, and the fault is in the ObjGet call.

You're probably using it wrong. You clearly do not have a firm understanding of what you are doing so I suspect you're just calling it wrong.

Share this post


Link to post
Share on other sites

This is why I bitched at you on the issue tracker and why you're about to get more. You have never explicitly stated your expectations, but I've just discerned them, I think. But your wording and your test script are stupid. If I'm understanding you correctly, everything is working just fine but your expectations are all wrong.

When you call ObjGet() on an Office file you're going to get an object back. If the file is open in another instance of Office, I believe that you get a reference to the object there. If the file is not already open, a new instance is created. This seems to be in-line with your shittily worded description:

You state that in both cases a 0 is returned which I assume mearns @error is set to 0 since you claim the file is "found" in both cases. That is correct. In the first case, ObjGet() gets an existing object from the ROT. In the second case it has to instantiate the object which involves starting up the Office program - hence the delay.

As I've said a couple times now, the only bug you found was the crash. Everything else is working exactly as designed. You could have saved a lot of us a bunch of trouble had you just stated what your expecatations were from the beginning.

Thanks, Valik!

I haven't read your issue tracker reply yet, but regardless, I've reported a scenario that causes autoit to crash, which ought to be a valid reason for opening a ticket.

Yes, I assumed you knew my expectations, and then I did spell them out in my second post, they are that ObjGet: "Retrieves a reference to a COM object from an existing process or filename". Now, I've learned that the "filename" version of ObjGet behaves differently than the "application" mode. The documentation ought to be expanded to reflect the differences between the two types of objGet calls, and that if the "filename" type of ObjGet call fails to find an existing object, it will create a new object and process.

My test script is taken directly from the helpfile's example.

There we go, there's your expectation. And as expected, it's totally wrong. The process is what is providing the COM interface for interacting with the file (and itself). Office applications have a COM interface, Notepad does not. You can't just blindly assume you can use ObjGet() with anything and it will magically work. There has to be appropriate COM interface.

You'd explained that a few posts back. And I replied "Thanks, I understand now!". I get it: Not all applications place entries into the ROT, therefore ObjGet can not be used universally. And now, I understand that the two forms of the objGet call function differently. That's why I ask questions.

Thanks again.

Share this post


Link to post
Share on other sites

Well, the crash is now fixed at any rate.

Share this post


Link to post
Share on other sites

You *can* use ObjGet() on files - files that support it, that is. Office files do. It's perfectly fine to ObjGet() a Word or Excel file. Text files? No, they do not support it.

The only bug you found was the crash on text files. AutoIt should never hard-crash. Everything else seems to be working fine.

Thanks for the correction. I did in fact get an object back from an .xls file:
$sFile = "C:\Temp\Test\Test1.xls"
$oXLS = ObjGet($sFile)
ConsoleWrite("Debug: $oXLS Type = " & VarGetType($oXLS) & @LF)
ConsoleWrite("Debug: $oXLS Name = " & ObjName($oXLS) & @LF)
$iSheets = $oXLS.Sheets.Count
ConsoleWrite("Debug: $iSheets = " & $iSheets & @LF)
$oXLS.Sheets(2).Select()

Output:

Debug: $oXLS Type = Object
Debug: $oXLS Name = _Workbook
Debug: $iSheets = 3
+>16:02:46 AutoIT3.exe ended.rc:0

Educational, as always!

:P


Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites

I think I'm screwed...

My goal sounds pretty simple:

Open a password-protected Excel wooksheet from an AutoIt application, but if the worksheet is already being edited, simply make that window Active/Visible.

Achieving that goal is proving troublesome.

If anyone can bear reading the long version, here it is:

End-users kept modifying our Excel worksheets by changing column widths, colors, adding cells, etc. This trashed our wish to maintain standardized worksheets. We've now converted these spreadsheets to be "protected", certain formatting is locked and accessing the spreadsheet requires a password. This password must not be known to the end-user.

We've been using an AutoIt front-end to access these spreadsheets, which also stops the users from running around our folders in Windows Explorer where they often accidentally move and delete files.

So, the existing AutoIt front-end is kicked off directly from an icon in some software called Medisoft. I hijacked one of thier external links that also contains parameters, including our file key: patient chart number. I use that chart number to bang on certain Advantage Database Server tables, retrieve necessary fields, determine filepaths and then bring up a pretty GIU that lets users see demographic info, patient flags, account balances, etc (via SQL), they can edit "Progress Notes" (executes Word), view X-rays (I kick off a .tff image viewer), check appointment data (MS Access via OLEDB), AND... edit the Patient Ledger!. These are the protected Excel spreadsheets that are causing all the trouble. The Autoit front-end also scans folders on multiple servers to build/maintain index files, it maintains preferences/screen location info for each user in .ini files, and has an auto-update mechanism built-in that works very nicely.

(Note: This may overly-ambitious for an Autoit script, but I've been away from C++ for 8 years and the COBOL2/DB2/CICS stuff from 15 years ago wouldn't be of much use. I've kept it to 1600 lines of code (plus 140 more for the auto-updater which displays a "what's new", copies in the new executable, then restarts the program). To date, it has worked quite nicely, except that now I'm stumped because we've protected the Patient Ledger worksheets).

I could call _ExcelBookOpen (which uses ObjGet in the "application" mode) with the filename and password, but I need to know if the worksheet is already being edited, or else a second read-only copy will be created.

If I use _ExcelBookAttach in "application" mode, it will only search the first instance of Excel, which if the worksheet is open in another instance, results again in a duplicate read-only worksheet being created.

Now that I know how the "filename" version of _ExcelBookAttach (and ObjGet) works, I could use it except, if the workbook is not already open, ObjGet does not allow for a password to be passed to it's built-in ObjCreate function. Without passing the password as a parameter, Excel will prompt the user for a password they aren't allowed to know.

I'd either need:

1. an ObjGet "filename" call with a flag to toggle the auto-create portion on and off. It would behave like the "application" mode of ObjGet. When the requested object was not found, it would just set the @error condition and return without initiating a new object and process. Then I could check the @error state and call _ExcelBookOpen, if necessary.

2. an ObjGet "filename" call that allowed for passwords to be passed as parameters, so that it could supply them to it's Excel ObjCreate process when the requested object was not found. It would behave like the "application" mode of ObjGet

3. Or maybe (hopefully not), I need to try to figure out how to use the Windows GetActiveObject() API function call directly from my script.

Sorry for the novelette (no one can say I didn't fully explain what I'm trying to do!).

Thanks for your time.

Share this post


Link to post
Share on other sites

Well, the crash is now fixed at any rate.

[troll] Somebody gets a cookie! :P [/troll]

My UDFs: ExitCodes

Share this post


Link to post
Share on other sites

Any Excel/Office experts wish to chime in on how to enumerate all the workbooks in all the running Excel processes? I see no way via the Excel object model of obtaining an object to another running instance of Excel. And if ObjGet() always returns the first instance of Excel, that means a lot of stuff is inaccessible...

Share this post


Link to post
Share on other sites

ObjGet with the filename parameter does search all instances of Excel, and will correctly return the proper object when found.

It's just when it doesn't find an object that I get in a mess. I'd either need to be able to supply a password for when ObjGet automatically creates an object/process (ObjCreate allows for a password parameter), or I'd need a parameter flag to be able to toggle off the object/process create portion of ObjGet and allow it to simply return an error code if no matching object were found. That would allow me to test the @error level and call ObjCreate with a passord when necessary.

PS - It appears the GetActiveObject() API function is what is used in the ".application" call to ObjGet. GetActiveObject() can only return the first instance of Word or Excel. I found this on Microsoft, but it's beyond my expertise : How to get IDispatch of an Excel or Word document from an OCX, even when multiple instances are running. Am not sure how pertinent that article is.

Thanks again, all.

Share this post


Link to post
Share on other sites

#17 ·  Posted (edited)

ObjGet with the filename parameter does search all instances of Excel, and will correctly return the proper object when found.

It's just when it doesn't find an object that I get in a mess. I'd either need to be able to supply a password for when ObjGet automatically creates an object/process (ObjCreate allows for a password parameter), or I'd need a parameter flag to be able to toggle off the object/process create portion of ObjGet and allow it to simply return an error code if no matching object were found. That would allow me to test the @error level and call ObjCreate with a passord when necessary.

The password of ObjCreate() is not what you want. That's for DCOM, not for secured objects. The Open method on the Excel.Application object is where you specify the password for the Spreadsheet.

PS - It appears the GetActiveObject() API function is what is used in the ".application" call to ObjGet. GetActiveObject() can only return the first instance of Word or Excel. I found this on Microsoft, but it's beyond my expertise : How to get IDispatch of an Excel or Word document from an OCX, even when multiple instances are running. Am not sure how pertinent that article is.

Thanks again, all.

Basically, that confirms something I was going to test out without me having to write the test to prove my theory (though I will anyway simply because I need to understand how this works). The short of it is, multiple objects can be in the ROT. Clearly GetActiveObject() walks the ROT until it finds the first matching item and then it stops ignoring all the objects after it which also match the same criteria. However, by manually walking the ROT one can gain access to those items. There seems to be no other way than manually walking the ROT.

I understand what the code at the link is doing. It makes sense, and now that I have a better understanding of how the ROT works, I can (eventually) offer a solution with AutoIt. We need to allow user's to specify an instance (defaulting to 1). So for example you could write code like this:

Local $nCount = 0
 While True
     Local $obj = ObjGet("", "Excel.Application", $nCount + 1)
     If @error Then ExitLoop
     $nCount += 1
 WEnd
 MsgBox(4096, "", $nCount " Excel objects running.")

That code keeps asking for Excel objects until it doesn't get one at which point it prints how many objects it did get. This will require some re-writing on the AutoIt end to provide the funcitonality. However, that code is going to be my first-round test script and once I have that working I will move on to more complicated tests to confirm the behavior is working.

The implications of this are fairly obvious. It would suddenly be possible to enumerate all work-books for all running Excel instances so that the correct one could be found even if it's not the same one GetActiveObject() would return. It basically turns ObjGet() into a function capable of enumerating all running objects of a specific CLSID. I am not sure how relevant an instance parameter is as it pertains to file-based access.

Something will be up on the issue tracker soon for this.

Edit: In an attempt to crash the universe with recursive linking: Issue #488.

Edited by Valik

Share this post


Link to post
Share on other sites

Something will be up on the issue tracker soon for this.

Wonderful! Thank you kindly, Sir. That would make the .application version of ObjGet much more useful to me (although I agree, I'm not sure I'd ever use a parameter to limit the search to a certain number of occurances).

The password of ObjCreate() is not what you want. That's for DCOM, not for secured objects. The Open method on the Excel.Application object is where you specify the password for the Spreadsheet.

I guessed poorly as to how the password gets to Excel, and I stand corrected. Are there possibilities to add to the functionality of the "filename" version of ObjGet as well? If it utilizes the Excel Open method, could a password be passed to that? Or would it be (fairly) simple to provide a flag to tell it to not automatically create an object if the target object does not already exist?

I am finished here. I do appreciate your time, and I'll patiently (ok, maybe anxiously) await your next incarnation of ObjGet.

Have a nice day :P

Share this post


Link to post
Share on other sites

#19 ·  Posted (edited)

Any Excel/Office experts wish to chime in on how to enumerate all the workbooks in all the running Excel processes? I see no way via the Excel object model of obtaining an object to another running instance of Excel. And if ObjGet() always returns the first instance of Excel, that means a lot of stuff is inaccessible...

Hmm... thought I found a trick, but can't make it work yet...

NOTE: If there are multiple instances of an automation server running at the same time, the GetActiveObject() API function returns the IDispatch pointer to the instance that was first running.

Theoretically, you can iterate the ROT for each individual instance, but Office applications do not register themselves if another instance is already in the ROT because the moniker for itself is always the same, and cannot be distinguished. This means that you cannot attach to any instance except for the first. However, because Office applications also register their documents in the ROT, you can successfully attach to other instances by iterating the ROT looking for a specific document, attaching to this document, and then getting the Application object from this document. For a code example of iterating the ROT and looking for a document name, click the article number below to view the article in the Microsoft Knowledge Base:

190985 (http://support.microsoft.com/kb/190985/) How To Get IDispatch of an Excel or Word Document from an OCX

Any hints on how to iterate the ROT in AutoIt? The examples all seem to be VB or VB.Net, not VBScript.

:P

P.S. NVM, looks like everybody got there before me.

Edited by PsaltyDS

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites

Are there possibilities to add to the functionality of the "filename" version of ObjGet as well?

No. There is no need for a password, the ROT is for the local machine only.

If it utilizes the Excel Open method, could a password be passed to that?

It does not use the Open method.

Or would it be (fairly) simple to provide a flag to tell it to not automatically create an object if the target object does not already exist?

I don't know. I don't think it's something I will pursue, however. It doesn't sound that useful because your situation is rather unique.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0