jeudi 16 juin 2016

Detect when password is used for opened or closed Excel workbook

I need to modify Excel workbooks from within VBA itself, where the user will point out which workbook to modify, after which a copy of the workbook will be modified and saved under a new name. This can be either an already open workbook, or a closed one on disk, and I need to support all workbook types from 2000 onward (2000-2016, binary, add-ins, templates, etc.). I need to modify the workbook's content as well as any custom UI (ribbon xml) in it. This all has to be done from random Excel installations not under my control.

The problem I'm facing is dealing with password protected workbooks - encrypted ones, i.e. a password for opening. My code needs to be able to handle those, and ideally apply any used password to the saved, updated copy as well.

The code flow is as follows:

  1. let the user select the workbook to modify (via a form)
  2. if the workbook is open:
    1. .SaveCopyAs it to a temp folder
    2. point to the saved copy for further processing
  3. open the user-selected file in a 2nd instance of Excel (invisible)
  4. update the opened temp copy's content
  5. .SaveAs the temp copy to a temp folder without a password and close it
  6. update the closed temp copy's custom UI
  7. re-open the updated temp copy and .SaveAs it again with any password that was on the original workbook

With step 2.1 above, the .SaveCopyAs will save the open workbook and apply any password to the copy as well, which results in step 3 asking for the password in all cases. I cannot remove the pass in step 2.1 by using a .SaveAs, since that will result in the open workbook not being open anymore in the end. It would also only be a half-measure since it doesn't stop the same thing happening with closed files.

In this case, when Excel asks the user for the password (in Excel 2010 at least), the password prompt only shows the filename of the file with an edit box for the pass, all with a popped-up empty 2nd Excel window beneath it open, which is an ugly sight to behold. And it doesn't allow me to capture the entered password as well for step 7.

The best I can do I think is to detect when a closed workbook on disk is encrypted, and show my own password prompt instead before trying to open it. But how to do this? These are the options I can come up with;

  1. When I use Workbooks.Open(Filename:=...), then Excel shows the password prompt, which I like to evade by asking for any pass beforehand myself.
  2. When I use Workbooks.Open(Filename:=..., Password:="notthepassword"), at least Excel won't show a password prompt anymore, workbooks without a password open fine, and those with a pass now generate error 1004. However, I can't act on that to infer that a password is needed, since 1004 is Excel's catch-all error number, and I cannot inspect the Err.Description either for "wrong password" or such, since I do not know the Excel GUI language running on the client. And then there's also Check whether Excel file is Password protected ; when encrypted files have workbook structure protection on as well, apparently Excel won't open them anymore this way - I tested this and it does work with my 2010 Excel, but it's not very encouraging to hear.
  3. Ignoring the ugliness of Excel asking for the pass, reading any Workbook.PasswordXxx property afterwards doesn't reveal anything; they always return the same values in all cases (with or without a password on the workbook).
  4. For OOXML files (.xlsm / .xlsx etc.) I can inspect the file's zip content beforehand for the presence of the two files "EncryptionInfo" and "EncryptedPackage", indicating the file is encrypted, but what about 2000-2003 (.xls) files? There is Microsoft's documentation on the BIFF file structure used in those files telling to check for a FilePass record in the workbook stream; while I know I could implement that logic (there's e.g. the now unsupported Koogra project), I'd rather just not :) (side-question: since when did Microsoft publish these details without first signing NDA's and jumping through their legal hoops?!)

Does anyone have any insight as to how I can make code step 7. above work, short of just living with Excel's prompt and adding a key logger to my app? :)

Aucun commentaire:

Enregistrer un commentaire