0

I have a WinForm that manipulates Excel files. Right now it's an .exe that has a hardcoded file load or creation method (just for testing). I need to be able to open the .exe via an Excel file, and then add that file (maybe as a sender through some event?) to the form for manipulation.

Below is an example of how I load the Excel file into the form right now.

public Excel.Application EXCEL_FILE;

public frmMain()
{
  InitializeComponent();
  this.EXCEL_FILE = new Excel.Application();
  this.EXCEL_FILE.SheetSelectionChange += new Excel.AppEvents_SheetSelectionChangeEventHandler(activeCellChanged);
  ExcelHandling.LoadExcelFile();
}

public static void LoadExcelFile()
{
    frmMain._frmMain.EXCEL_FILE.Workbooks.Open(@"F:\dsa.xlsx", 0, false, 5, "", "", true, Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
    frmMain._frmMain.EXCEL_FILE.Visible = true;
}

To summerize the workflow I need to establish is: Open some Excel file->call the WinForm (via add-in or something?)->Load the Excel file that called the WinForm into it.

Schadenfreude
  • 1,522
  • 1
  • 20
  • 41
  • Right so what have you tried so far? You can open an *.exe file from VBA using `Shell` command. The harder part is to get handle of the processed file via that .exe you opened, but you can grab the handle if the Excel file is open easily using winAPI –  Feb 26 '14 at 11:24
  • So far I've stumbled upon this command `=EMBED("WinForms.Control.Host","")`, and I'm looking into that as a way, but I'm not certain it will help, that's why I was asking if someone knew a way to do what I've asked. – Schadenfreude Feb 26 '14 at 11:27
  • @mehow My 1st thought was to make a dialog box to load the file into the form, but the thing is the client wants to be able to call the form via the excel file, because it was previously an add-in/userform written on VBA and they want to keep the previous workflow. – Schadenfreude Feb 26 '14 at 11:28
  • right, so does it neccessarily have to be an exe? why not use it as a userform then? If it was just an Excel's userform than it would eliminate the complexity of calling an external exe just to achieve a very simple task from what it looks like –  Feb 26 '14 at 11:33
  • The idea was that the userform functions needed to grow and the VBA code was getting very hard to maintain and expand, so I've rewritten the userform into a winform, which is more scalable. Can I use a WinForm as a userform? – Schadenfreude Feb 26 '14 at 11:37
  • AFAIK no, you can't. What I would do rather have a separate executable, I would make it a COM addin. It would allow you to still develop functions in (C# for example) and expose them to Excel via creating a new Object of your type. If you want to get an idea have a look at [**my blog**](http://vba4all.wordpress.com/category/c-and-vba/) –  Feb 26 '14 at 11:45

2 Answers2

3

You can start your C#-Exe from an Excel-File and pass the file-name of the excel-file via VBA like this:

Sub StartCSharpExe()
    Shell "<full path to your exe-file> " + ActiveWorkbook.FullName, 1
    Application.Quit //optional to close Excel
End Sub

Then you can read the file-name of the excel file in your C#-Program as a command parameter like this:

   private void GetCommands()
    {
        String[] arrCommands = Environment.GetCommandLineArgs();
        foreach (String command in arrCommands)
        {
            MessageBox.Show(command); // just for debugging purpose / if you'd like to see all parameters
            // get the excel-file-name and open it...
        }
    }

Perhaps the excel-file is read-only as it may be still open, I have not tested it,
but this would be the way to go.

user1567896
  • 2,398
  • 2
  • 26
  • 43
  • It works fine, but as you mentioned, when I use `Workbooks.Open`, to load the invoking excel file into the form, it loads it again as read-only. Is there any other command to just load the file into a variable or do I have to use a `StreamReader` or something to get the file? – Schadenfreude Feb 27 '14 at 12:39
  • The problem is that you open the file in excel first. For every other programm that trys to access the file now it is read-only as it is already open. You could try to close Excel via VBA and then use a timer in your C#-Programm to wait a few milliseconds or seconds before you open the Excel-file. Then it should not be read-only anymore. – user1567896 Feb 27 '14 at 13:27
  • I found a way to do it without closing the file, but I can't be sure that I'll get the right file that way, which is `EXCEL_FILE = (Excel.Application)Marshal.GetActiveObject("Excel.Application");`. If I can somehow get all the active `Excel.Applications` and then choose the one which has called the Winform by comparing filepaths or something, it would solve all my problems. – Schadenfreude Feb 27 '14 at 14:27
  • 1
    Perhaps this link helps you: http://stackoverflow.com/questions/7646498/how-to-iterate-through-instance-of-excel-c-sharp – user1567896 Feb 27 '14 at 14:29
  • http://social.msdn.microsoft.com/Forums/en-US/30b4f345-cc1e-4548-b249-30d0b3c08691/how-to-getobject-from-a-process-id?forum=vblanguage Sadly what I was trying to achieve turned out to be a dead end :/ – Schadenfreude Feb 27 '14 at 15:06
  • I'm sorry. You can try what I mentioned earlier. Close Excel with VBA and delay the opening of the file in your C#-App. This should make it possible to open the file without it beeing read-only. – user1567896 Feb 27 '14 at 15:10
  • 1
    Scratch that, it turns out that `EXCEL_FILE = (Excel.Application)Marshal.GetActiveObject("Excel.Application");` works perfectly in my case, I gave it a test with 5 excel windows open and it always got the one that invoked the winform. – Schadenfreude Feb 27 '14 at 15:16
0

I'm gonna post my full solution here, cause I think it might be useful to people.
You're gonna invoke the form in Excel as user1567896 suggested, by using this code:

Sub StartCSharpExe()
    Shell "<full path to your exe-file> " + ActiveWorkbook.FullName, 1
    Application.Quit //optional to close Excel
End Sub

Then you save the Excel file as an Excel Add-in file(.xla).
After that (in Excel), go to the Developer Tab->Add-ins->[browse to your .xla file]->OK
Then you can add that add-in to your Quick Access Toolbar the following way:
File->Options->Quick Access Toolbar->[Choose commands from:]Macros->YourAddin.xla

The c# method should look something like this and be invoked in the Form Initialization:

using Excel = Microsoft.Office.Interop.Excel;
public static void LoadExcelFile()
{
    Excel.Application EXCEL_FILE = new Excel.Application();
    EXCEL_FILE = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
}

NOTE: I've tested this with a few windows open, and GetActiveObject("Excel.Application") always returned the one that was invoking the WinForm via Excel.

Hope this helps.

Schadenfreude
  • 1,522
  • 1
  • 20
  • 41