0
votes

As part of a file storage migration project, I am trying to change some excel links in some excel workbooks to reflect the new file storage location.

I am using Winforms and C# in VS2017 RC to develop the solution that I intend to deploy.

At the top of my code, I have the following code so that alerts are turned off and auto updating of links is turned off.

excelApp.Visible = true;
excelApp.DisplayAlerts = false;
excelApp.AskToUpdateLinks = false;

In my solution; I am calling the ChangeLink method on the Excel Workbook object and passing in the old link, the new link and the Excel Link Type.

If I open a non password protected Workbook that contains links to other Workbooks that are not password protected, I don't get a problem and my solution goes on to successfully change the links as requested.

If I open a non password protected Workbook that contains links to other Workbooks that are password protected, Excel issues a prompt to enter a password for that linked Workbook.

Does anyone have any idea on suppressing this secondary prompt for the password of a linked Workbook? My code is below and I await your considered responses.

    if (MsOfficeHelper.IsPasswordProtected(fileName))
    {
        while ((excelApp.Workbooks.Count == 0) && (!allPasswordUsed))
        {
            // Open workbook - trying each password from password list in turn
            foreach (var excelPassword in excelPasswords)
            {
                try
                {
                    excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever, Password: excelPassword);
                    allPasswordUsed = true;
                    resultsOut = resultsOut.AppendLine(fileName + " - Opened");
                }
                catch (Exception WTF)
                {
                    //MessageBox.Show(WTF.Message);
                }
            }

            // Open workbook - trying each password from password list in turn
            foreach (var excelPassword in excelPasswords)
            {
                try
                {
                    excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever, Password: excelPassword.ToLower());
                    allPasswordUsed = true;
                    resultsOut = resultsOut.AppendLine(fileName + " - Opened");
                    //
                }
                catch (Exception WTF)
                {
                    //MessageBox.Show(WTF.Message);
                }
            }

            allPasswordUsed = true;
            resultsOut = resultsOut.AppendLine(fileName + " - All known passwords used - Unable to Open File");
        }
    }
    else
    {
        // Open Workbook - no password required
        excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever);
        resultsOut = resultsOut.AppendLine(fileName + " - Opened");
    }
1
This is a Q&A site, not a forum. Questions go in the question box. There's no need to mark them further by putting some kind of "marker" in the title.Damien_The_Unbeliever
@Damien_The_Unbeliever - I did ask a question perhaps you could re-read my post.Stephen Smith
Yes, but what I meant was that you, for some reason, felt the need to prefix the title with "ISSUE - ". What I did in my edit was to remove that marker, and then my comment was to help to explain why the marker was unnecessary.Damien_The_Unbeliever
@Damien_The_Unbeliever OK thank you for that. But I still have an ISSUE that I need to resolve.Stephen Smith

1 Answers

0
votes

OK. I've not been able to find any information regarding resolving this issue so I have been left with no option but to develop a workaround using calls to the Windows API.

This is the solution or workaround that I have developed.

On my winform, I have added the following declarations to the Windows API.

[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

I have also added the following to the top of my winform.

public bool fileOpenInProgress = false;

To my winform, I have added a BackgroundWorker control. On this BackgroundWorker control, I set the WorkerSupportsCancellation property to True.

In the DoWork event handler of the BackgroundWorker control, I have specified the following method be called.

private void workerXLPwdDialogCheck_DoWork(object sender, DoWorkEventArgs e)
{
    while (fileOpenInProgress)
    {

        IntPtr hwndExcel = FindWindow(lpClassName: "XLMain", lpWindowName: null);
        SetForegroundWindow(hwndExcel);

        try
        {
            IntPtr hwndPasswordDialog = FindWindow(lpClassName: null, lpWindowName: "Password");
            if (hwndPasswordDialog != IntPtr.Zero)
            {

                // Make the Password Dialog the active window
                SetForegroundWindow(hwndPasswordDialog);
                SendMessage(hwndPasswordDialog, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
            }

            IntPtr hwndSelectSheetDialog = FindWindow(lpClassName: null, lpWindowName: "Select Sheet");
            if (hwndSelectSheetDialog != IntPtr.Zero)
            {

                // Make the Password Dialog the active window
                SetForegroundWindow(hwndSelectSheetDialog);
                SendMessage(hwndSelectSheetDialog, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
            }


        }
        catch (Exception WTF)
        {
            MessageBox.Show(WTF.Message);
        } 
    }
}

In the rest of my code where I do the the opening of Excel files and changing links, I have the following code

            fileOpenInProgress = true;
            workerXLPwdDialogCheck.RunWorkerAsync();

            StringBuilder resultsOut = new StringBuilder();

            if (MsOfficeHelper.IsPasswordProtected(fileName))
            {
                while ((excelApp.Workbooks.Count == 0) && (!allPasswordUsed))
                {
                    // Open workbook - trying each password from password list in turn
                    foreach (var excelPassword in excelPasswords)
                    {
                        try
                        {

                            excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever, Password: excelPassword);
                            allPasswordUsed = true;
                            resultsOut = resultsOut.AppendLine(fileName + " - Opened");
                        }
                        catch (Exception WTF)
                        {
                            //MessageBox.Show(WTF.Message);
                        }
                    }

                    // Open workbook - trying each password from password list in turn
                    foreach (var excelPassword in excelPasswords)
                    {
                        try
                        {
                            excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever, Password: excelPassword.ToLower());
                            allPasswordUsed = true;
                            resultsOut = resultsOut.AppendLine(fileName + " - Opened");
                            //
                        }
                        catch (Exception WTF)
                        {
                            //MessageBox.Show(WTF.Message);
                        }
                    }

                    allPasswordUsed = true;
                    resultsOut = resultsOut.AppendLine(fileName + " - All known passwords used - Unable to Open File");
                }
            }
            else
            {
                // Open Workbook - no password required
                excelWorkbook = excelApp.Workbooks.Open(Filename: fileName, UpdateLinks: Excel.XlUpdateLinks.xlUpdateLinksNever);
                resultsOut = resultsOut.AppendLine(fileName + " - Opened");
            }

            // Assuming there is an openwork book object
            // check to see if it contains links and attempt to update them.
            if (excelApp.Workbooks.Count > 0)
            {
                excelWorkbook = excelApp.ActiveWorkbook;
#pragma warning disable IDE0019 // Use pattern matching
                Array olinks = excelWorkbook.LinkSources(Excel.XlLink.xlExcelLinks) as Array;
#pragma warning restore IDE0019 // Use pattern matching
                if (olinks != null)
                {
                    if (olinks.Length > 0)
                    {
                        resultsOut = resultsOut.AppendLine("  " + fileName + " - " + olinks.Length.ToString() + " links.");
                        foreach (var olink in olinks)
                        {
                            oldLink = olink.ToString();

                            // Search through list of linked files to find the oldLink
                            foreach (LinkedFile linkedFile in linkedFiles)
                            {
                                if (oldLink == linkedFile.OldLink)
                                {
                                    newLink = linkedFile.NewLink;
                                    break;
                                }
                            }

                            try
                            {

                                excelWorkbook.ChangeLink(Name: oldLink, NewName: newLink, Type: Excel.XlLinkType.xlLinkTypeExcelLinks);
                                resultsOut = resultsOut.AppendLine("  SUCCESS - ChangeLink from " + oldLink + " to " + newLink);
                                Application.DoEvents();
                            }
                            catch (Exception whoopsy)
                            {
                                resultsOut = resultsOut.AppendLine("  FAILURE - ChangeLink from " + oldLink + " to " + newLink);
                                Application.DoEvents();
                            }

                            //resultsOut = resultsOut.AppendLine("  " + oldLink);

                        }  // End For loop
                    }
                    else
                    {
                        resultsOut = resultsOut.AppendLine("  No links.");
                    }

                }

                excelWorkbook.Close(SaveChanges: true);
                resultsOut = resultsOut.AppendLine(fileName + " - Closed");
                resultsOut = resultsOut.AppendLine(" ");

            }

            // Stop the background worker that checks for the existence of a 
            // Excel Password Dialog
            fileOpenInProgress = false;
            workerXLPwdDialogCheck.CancelAsync();
            return resultsOut.ToString();

This has the effect of clicking the cancel button on any 'Password' or 'Select Sheet' dialogs that are displayed.

It might not be the prettiest workaround or solution but it is functional.