WPF; click once; Double Click file to launch; VS 2008 - wpf

My application is only for me and co-workers, so I don't care if it's Click-Once or copy-the-exe. I want to be able to click a file with given extension in windows explorer and have my program launch and open that file. I can't get it to capture the file name.
Ostensible solution:
Link
The code I'm trying is below and at this point all I'm trying to do is put the name of the clicked file in a text box. I suspect my relevant ignorance is about how to reference the click-once application from windows explorer. When I build I end up with a file called setup.exe, a file called Wis.application, and when I click on "setup" to install it, I end up with a shortcut of type "Click-once application reference" in "C:\Users\ptom\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Wis". I've tried associating files with that shortcut created by install, and associating files with setup.exe. When I click on the file, the application launches but indicates that
AppDomain.CurrentDomain.SetupInformation.ActivationArguments is null. (By "indicates" I mean the text box gets filled in with the text from where I test to see if it's null). If I run the app from debug, or just by running it from the start menu, it does what I'd expect, following the code path that indicates that ActivationArguments is not null, but that its ActivationData (string[]) is of length 0.
Here is the code from app.xaml.cs
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
namespace Wis
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// Check if this was launched by double-clicking a doc. If so, use that as the
// startup file name.
if (AppDomain.CurrentDomain.SetupInformation.ActivationArguments == null)
{
this.Properties["DoubleClickedFileToLoad"] = "There were no activation arguments AGAIN";
}
else
{
if (AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData != null
&& AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData.Length > 0)
{
this.Properties["DoubleClickedFileToLoad"] =
AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData[0];
}
else
{
this.Properties["DoubleClickedFileToLoad"] = "Type in a file name";
}
}
}
}
}
Thanks

First of all you should add file assoc for ClickOnce-publish (Select Project->Properties->Publish-Options->File Associations)
Then add Reference to "System.Deployment"
Then you could extract path in App.cs in such a manner, depending on type of startup (ClickOnce or Local)
protected override void OnStartup(System.Windows.StartupEventArgs e)
{
var path = GetPath (e);
}
private static string GetPath(StartupEventArgs e)
{
if (!ApplicationDeployment.IsNetworkDeployed)
return e.Args.Length != 0 ? e.Args[0] : null;
if (AppDomain.CurrentDomain.SetupInformation.ActivationArguments == null)
return null;
var args = AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData;
return args == null || args.Length == 0 ? null : new Uri(args[0]).LocalPath;
}

Have you checked Environment.GetCommandLineArgs()?
That shows the arguments with which your application was started.
If your application is associated with a file, it should contain the filename as one of the arguments.

Related

How should I run a exe on startup when it has app files

I made a windows forms app and exported it to a release folder.
I am trying to make it run on startup but the application needs to be in the same folder as it's application files.
I understand that you put it in shell:startup folder. But I cannot run just the exe because it depends on the other applications.
Is there a way that I can just run the application while keeping it in the folder. Or any other solutions?
You don't need to put the whole app in the startup folder. You just need to put the shortcut of your application there. Here's a Windows 10 help topic: Add an app to run automatically at startup in Windows which shows you how to do it manually:
Press Win + R keys to open Run window
Type shell:startup and press Enter
Right click and Add new shortcut to your application.
How to add the application to startup folder using C#
You can use either of the following options to add an application to the startup folder using C#:
Put application in startup folder using an installer
Put application is startup folder using code
Example 1 - Put application in startup folder using an installer
Follow these steps:
Download and install Microsoft Visual Studio Installer Projects extension. (VS2022, VS2017 & VS2019)
Add a new Windows Forms Project
Add a new Setup project to the solution
Right click on the Setup project → Add → Project Output, then select primary output from your windows forms project.
Right click on the Setup project → View → File System
Right click on the FileSystem → Add Special Folder → User's Startup Folder
Right click In the User's Startup Folder (in the list with two columns, name and type) → Create New Shortcut → Browse Application Folder and choose Primary output from the Windows Application and click OK.
Edit the name of the shortcut or set an icon for it.
Rebuild all the projects (including the setup).
Install the setup (approve if requested to modify system)
if you sign out and sign in again, you will see the application is in startup folder.
To verify and see if the app is inside the folder:
Press Win + R keys to open Run window
Type shell:startup and press Enter
See the shortcut of your application in the startup folder.
Example 2 - Put application is startup folder using code
Follow these steps:
Create Windows Forms Application
Drop a CheckBox on the form and change its text to "Run at startup of Windows"
Drop a Button on the form and change its text to "Save".
Add a the following code to the project (Thanks to the ref1, ref2):
using System;
using System.Runtime.InteropServices;
using System.Text;
[ComImport]
[Guid("00021401-0000-0000-C000-000000000046")]
class ShellLink
{
}
/// <summary>The IShellLink interface allows Shell links to be created, modified, and resolved</summary>
[ComImport()]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214F9-0000-0000-C000-000000000046")]
interface IShellLinkW
{
/// <summary>Retrieves the path and file name of a Shell link object</summary>
void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out IntPtr pfd, int fFlags);
/// <summary>Retrieves the list of item identifiers for a Shell link object</summary>
void GetIDList(out IntPtr ppidl);
/// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
void SetIDList(IntPtr pidl);
/// <summary>Retrieves the description string for a Shell link object</summary>
void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
/// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary>
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
/// <summary>Retrieves the name of the working directory for a Shell link object</summary>
void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
/// <summary>Sets the name of the working directory for a Shell link object</summary>
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
/// <summary>Retrieves the command-line arguments associated with a Shell link object</summary>
void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
/// <summary>Sets the command-line arguments for a Shell link object</summary>
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
/// <summary>Retrieves the hot key for a Shell link object</summary>
void GetHotkey(out short pwHotkey);
/// <summary>Sets a hot key for a Shell link object</summary>
void SetHotkey(short wHotkey);
/// <summary>Retrieves the show command for a Shell link object</summary>
void GetShowCmd(out int piShowCmd);
/// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
void SetShowCmd(int iShowCmd);
/// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary>
void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
int cchIconPath, out int piIcon);
/// <summary>Sets the location (path and index) of the icon for a Shell link object</summary>
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
/// <summary>Sets the relative path to the Shell link object</summary>
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
/// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary>
void Resolve(IntPtr hwnd, int fFlags);
/// <summary>Sets the path and file name of a Shell link object</summary>
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
Doubleclick on the Button and add the following code to the event handler
private void button1_Click(object sender, EventArgs e)
{
var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
var exeFilePath = Application.ExecutablePath;
var appName = Path.GetFileNameWithoutExtension(exeFilePath);
var lnkFilePath = Path.Combine(startupPath, $"{appName}.lnk");
if (checkBox1.Checked)
{
if (File.Exists(lnkFilePath))
return;
var lnk = (IShellLinkW)new ShellLink();
lnk.SetPath(exeFilePath);
lnk.SetDescription("My application!");
lnk.SetIconLocation(exeFilePath, 0);
var file = (IPersistFile)lnk;
file.Save(lnkFilePath, false);
}
else
{
if (File.Exists(lnkFilePath))
File.Delete(lnkFilePath);
}
}
Double click on the Form and add the following code:
private void Form2_Load(object sender, EventArgs e)
{
checkBox1.Checked = IsInStartup();
}
bool IsInStartup()
{
var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
var exeFilePath = Application.ExecutablePath;
var appName = Path.GetFileNameWithoutExtension(exeFilePath);
var lnkFilePath = Path.Combine(startupPath, $"{appName}.lnk");
if (File.Exists(lnkFilePath))
return true;
return false;
}
Run the application and you can put the checkmark or remove it to save/remove the application in/from startup:

WPF application command line arguments instead of start GUI

I have WPF application and i want to add the option to do my stuff in commend line instead of open the GUI.
Any way to send my Application exe arguments and in case the arguments length in > 0 continue with command line instead of open the GUI ?
You could edit the App.xaml.cs file and override the OnStartup method:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
string[] args = e.Args;
if(args.Length > 0 && args[0] == "cl")
{
//...
}
else
{
base.OnStartup(e);
Window2 mainWindow = new Window2();
mainWindow.Show();
}
}
}
You should also remove the StartupUri attribute from <Application> root element of the App.xaml file.
But if you want to be able to write to the console you need to create a console window manually:
No output to console from a WPF application?
Then you might as well create a console application in Visual Studio and instead start your WPF application based on the command line argument(s), e.g.:
public class Program
{
public static void Main(string[] args)
{
if (args.Length == 0 || args[0] != "cl")
{
System.Diagnostics.Process.Start(#"c:\yourWpfApp.exe");
}
else
{
//...
}
}
}
A console application is not a WPF application and vice versa. So create two different applications.
In your App.xaml.cs implement the OnStartup method. So you can access the arguments passed via command line.
protected override void OnStartup(StartupEventArgs e)
{
var args = e.Args;
// do anything with arguments
}

Combining multiple assemblies into a single EXE for a WPF application

I followed this tutorial to combine a few DLL's into my EXE.
http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application
The way I understand this works is:
- it starts by telling the compiler to embed (as embedded resources) each and every DLL that have their Local Copy set to True.
That part is working fine. It apparently doesn't "add" them as resources to my project (figure 1 in the tutorial kind of says otherwise), but I can tell that the size of my EXE is correct.
FYI, my program uses WPFtoolkit, in my case, that's 2 DLL's:
system.windows.controls.datavisualization.toolkit.dll
WPFToolkit.dll
Then, I set the Build Action of my App.xaml to Page, and made a program.cs file which I added to my project.
this is my project.cs:
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Media.TextFormatting;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Shell;
using System.IO;
using System.Reflection;
using System.Globalization;
namespace Swf_perium {
public class Program {
//[System.Diagnostics.DebuggerNonUserCodeAttribute()]
//[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
[STAThreadAttribute]
public static void Main() {
Swft_perium.App app = new Swf_perium.App();
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
app.InitializeComponent();
app.Run();
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
AssemblyName assemblyName = new AssemblyName(args.Name);
string path = assemblyName.Name + ".dll";
Console.WriteLine(path);
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
{
path = String.Format(#"{0}\{1}", assemblyName.CultureInfo, path);
}
using (Stream stream = executingAssembly.GetManifestResourceStream(path))
{
if (stream == null)
return null;
byte[] assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
}
}
After I build the project, I run it off VS2013, no problem, since both DLL's have their local copy set to true. If I go in my debug folder, take both DLL's out and run the EXE off windows explorer, then the program instantly crashes because it can find the DLL's.
What this tutorial should allow me to do is being able to run that EXE by itself without the DLL's, so yeah, it doesn't work.
I added a console writeline of the path that are being read by the OnResolveAssembly method of my program.cs. And here's what I get:
4 times the same path:
"Swf_perium.resources.dll"
Obviously, when it gets to the Stream, it's null and the method then returns null.
I am trying to understand where these paths are coming from? I don't understand why 4? And why this path?
Has anyone ever tried this technique? Comments on the blog show pretty good success rate.
Does anyone have an idea?
I made several mistakes to get to this stage, but at this point I don't see what I am doing wrong.
Thanks
Steve
EDIT: following HB's guidance, here's what I did:
I took the MSBuild target "mod" out.
Set both references' copy local to FALSE.
Added both DLL as embedded resources manually. They're both into the "Resources" directory at the root of the project.
I set App.xaml build action back to "ApplicationDefinition".
And I excluded my program.cs out of the project.
and added this code to App.xaml.cs:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Reflection;
namespace Swf_perium
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var execAssembly = Assembly.GetExecutingAssembly();
string resourceName = execAssembly.FullName.Split(',').First() + ".Resources." + new AssemblyName(args.Name).Name + ".dll";
Console.WriteLine(resourceName);
using (var stream = execAssembly.GetManifestResourceStream(resourceName))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
}
}
now, the console prints out of the 2 DLL's filename, but not the other.. I am guessing that's why it's still not working..
that's where I'm at.
edit:
The DLL that doesn't show is not called by my code directly. it's a dependence from the first DLL. I took that second DLL out of references and resources.. If I set copy local to true for the first DLL (which my program actually uses), building the project generates both DLL at the root - in this case with both dlls generated the program works, funny thing is if I delete that second DLL, the program still works. So the problem isn't that second DLL but the first one.
the error I have (which I've had all along no matter what technique I use) is that my XAML is calling that namespace and can't find it!
edit:
Ok, well it still doesn't work. I've brought my program.cs back into the solution, set it as the entry point. And added the code suggested by HB into it.
I made sure that the assemblyresolve is done on the first line of the main so that's it's done before any wpf is done. I even added a 5s sleep just to make sure that the dll was loaded before any wpf happens. Still no go.
Either the dependence to the second DLL is what's causing a problem (?) or maybe the way I import the namespace in my XAML is incorrect. Do I need to specify that this namespace is embedded? and where it's located - i.e. its path?.
thanks
Perhaps look at Costura where it will do all the hard work of embedding assemblies for you.
Don't know your project structure but i usually add a directory for the assemblies to the root of the project and then add the dlls to that directory as embedded resource. I also then turn off the local copy of the references to make sure that it works.
Here is the code i use in my App.xaml.cs:
static App()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var execAssembly = Assembly.GetExecutingAssembly();
string resourceName = execAssembly.FullName.Split(',').First() + ".ReferencedAssemblies." +
new AssemblyName(args.Name).Name + ".dll";
using (var stream = execAssembly.GetManifestResourceStream(resourceName))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
Simply replace the ".ReferencedAssemblies." string according to the directory you placed the dlls in.
(Using the static constructor of the class makes sure that the event is hooked up before any code that potentially accesses referenced assemblies is executed, in your code i would move the hook to the first line of Main, that may already solve your problem.)

silverlight 4, dynamically loading xap modules

I know that it is possible to load xap modules dynamically using Prism or MEF framework. However, I'd like not to use those frameworks; instead load my xap files manually. So, I created the following class (adapted from internet):
public class XapLoader
{
public event XapLoadedEventHandler Completed;
private string _xapName;
public XapLoader(string xapName)
{
if (string.IsNullOrWhiteSpace(xapName))
throw new ArgumentException("Invalid module name!");
else
_xapName = xapName;
}
public void Begin()
{
Uri uri = new Uri(_xapName, UriKind.Relative);
if (uri != null)
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += onXapLoadingResponse;
wc.OpenReadAsync(uri);
}
}
private void onXapLoadingResponse(object sender, OpenReadCompletedEventArgs e)
{
if ((e.Error == null) && (e.Cancelled == false))
initXap(e.Result);
if (Completed != null)
{
XapLoadedEventArgs args = new XapLoadedEventArgs();
args.Error = e.Error;
args.Cancelled = e.Cancelled;
Completed(this, args);
}
}
private void initXap(Stream stream)
{
string appManifest = new StreamReader(Application.GetResourceStream(
new StreamResourceInfo(stream, null), new Uri("AppManifest.xaml",
UriKind.Relative)).Stream).ReadToEnd();
XElement deploy = XDocument.Parse(appManifest).Root;
List<XElement> parts = (from assemblyParts in deploy.Elements().Elements()
select assemblyParts).ToList();
foreach (XElement xe in parts)
{
string source = xe.Attribute("Source").Value;
AssemblyPart asmPart = new AssemblyPart();
StreamResourceInfo streamInfo = Application.GetResourceStream(
new StreamResourceInfo(stream, "application/binary"),
new Uri(source, UriKind.Relative));
asmPart.Load(streamInfo.Stream);
}
}
}
public delegate void XapLoadedEventHandler(object sender, XapLoadedEventArgs e);
public class XapLoadedEventArgs : EventArgs
{
public Exception Error { get; set; }
public bool Cancelled { get; set; }
}
The above code works fine; I can load any xap the following way:
XapLoader xapLoader = new XapLoader("Sales.xap");
xapLoader.Completed += new XapLoadedEventHandler(xapLoader_Completed);
xapLoader.Begin();
Now, I have a UserControl called InvoiceView in the Sales.xap project, so I would like to instantiate the class. In the current project (Main.xap) I added reference to Sales.xap project, however, since I load it manually I set "Copy Local = False". But when executed, the following code throws TypeLoadException:
Sales.InvoiceView view = new Sales.InvoiceView();
It seems the code can't find InvoiceView class. But I checked that XapLoader's initXap() method was successfully executed. So why the code can't find InvoiceView class? Can someone help me with this problem?
This is based on the asker's self-answer below, rather than the question.
If you delete a project/module the output DLLs/XAP files do hang around. If you click the "show all files" button you will see some these left-over output files in your clientbin, bin and obj folders of related projects.
You can delete them individually from the project, or, when in doubt, search for all BIN and OBJ (e.g. using desktop explorer) and delete all those folders. The BIN/CLIENTBIN/OBJ folders will be recreated when needed (this the job that the "clean" option in Visual Studio should have done!)
Hope this helps.
Ok, I found the cause. The above code works. After creating a new silverlight project (Sales.xap) I happened to compile my solution once. Then I deleted App class in the Sales.xap and renamed default MainPage class to SalesView. However, no matter how many times I compile my solution, Visual Studio's development web server was loading the first version of Sales.xap (where from?), so my code couldn't find SalesView. In my host Asp.Net project I set development server's port to a different port number, and the problem gone. So the problem was with Visual Studio's development server. Apparently it is keeping compiled xap files in some temporary folder, and doesn't always update those xap files when source code changed.
What I do to avoid such problems when executing freshly compiled Silverlight is clear the browser cache, chrome even has a clear silverlight cache ;)
this XAP Cache phenomena is often due to the visual studio embedded web server (ASP.NET Development Server).
Just stop the occurence of this server and the cache will be cleared.
Start again your project and the latest build of your xap is called.

Debugging silverlight in a WPF app

I am developing a WPF app that contains a webbrowser control that loads a silverlight application. I would like to be able to launch the app from visual studio (F5) and have the debugger attach to the silverlight code. However, I've not had any luck with this.
The best I can currently do is to launch the app without attaching, then once it is up and running, attach to the process manually with silverlight as the specified type of code to debug, and this works. (When I cause the web browser control to load the silverlight app, it will hit breakpoints in my silverlight code). I've written some macros to automate this launching/attaching somewhat, but it still isn't the best.
I've tried specifying the WPF app as the external program to run when launching/debugging the silverlight app, but Visual Studio attaches to the process wanting to debug the managed .NET code.
Any ideas? Ideally, I would really like to attach to the process and debug both the managed .NET and the silverlight code, but I don't think this is possible. I'd really like to automatically be attached to the silverlight code at launch so that I can easily debug all issues with the silverlight app, including those that occur on load.
Thanks for your ideas Brandorf and fatty. Brandorf's almost gets me to where I wanted to go, but does require that my SL app be capable of running on its own. I really want to have only the one app, which is both wpf and silverlight, with the SL side being debugged.
A long time after I asked this question (I forgot I had asked it here), I actually pieced together a solution that I'm really happy with. I use visual studio automation within the WPF/.NET side of my app, to find all running instances of visual studio, figure out which one produced my exe (since it typically sits in a folder below the vcproj/sln folder), and then use visual studio automation to have that VS attach to the app, debugging silverlight code. After this is done, I then load my silverlight content.
It works really well. You end up with an app that goes and finds a debugger to attach to itself every time it runs (so you probably want this code only in a debug build, or somehow able to be turned off). So you just launch the app with ctrl-F5 (launch without debugging) from visual studio whenever you want to debug the silverlight side.
Here's my code:
#if DEBUG
using System;
using System.Collections.Generic;
using System.Collections;
using System.Runtime.InteropServices;
using System.IO;
namespace Launcher
{
//The core methods in this class to find all running instances of VS are
//taken/inspired from
//http://www.codeproject.com/KB/cs/automatingvisualstudio.aspx
class DebuggingAutomation
{
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(int reserved,
out UCOMIRunningObjectTable prot);
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(int reserved,
out UCOMIBindCtx ppbc);
///<summary>
///Get a snapshot of the running object table (ROT).
///</summary>
///<returns>
///A hashtable mapping the name of the object
///in the ROT to the corresponding object
///</returns>
private static Hashtable GetRunningObjectTable()
{
Hashtable result = new Hashtable();
int numFetched;
UCOMIRunningObjectTable runningObjectTable;
UCOMIEnumMoniker monikerEnumerator;
UCOMIMoniker[] monikers = new UCOMIMoniker[1];
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, out numFetched) == 0)
{
UCOMIBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
result[runningObjectName] = runningObjectVal;
}
return result;
}
/// <summary>
/// Get a table of the currently running instances of the Visual Studio .NET IDE.
/// </summary>
/// <param name="openSolutionsOnly">
/// Only return instances that have opened a solution
/// </param>
/// <returns>
/// A list of the ides (as DTE objects) present in
/// in the running object table to the corresponding DTE object
/// </returns>
private static List<EnvDTE.DTE> GetIDEInstances(bool openSolutionsOnly)
{
var runningIDEInstances = new List<EnvDTE.DTE>();
Hashtable runningObjects = GetRunningObjectTable();
IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator();
while (rotEnumerator.MoveNext())
{
string candidateName = (string)rotEnumerator.Key;
if (!candidateName.StartsWith("!VisualStudio.DTE"))
continue;
EnvDTE.DTE ide = rotEnumerator.Value as EnvDTE.DTE;
if (ide == null)
continue;
if (openSolutionsOnly)
{
try
{
string solutionFile = ide.Solution.FullName;
if (!String.IsNullOrEmpty(solutionFile))
{
runningIDEInstances.Add(ide);
}
}
catch { }
}
else
{
runningIDEInstances.Add(ide);
}
}
return runningIDEInstances;
}
internal static void AttachDebuggerIfPossible()
{
if (System.Diagnostics.Debugger.IsAttached)
{
//Probably debugging host (Desktop .NET side), so don't try to attach to silverlight side
return;
}
var ides = GetIDEInstances(true);
var fullPathToAssembly = System.Reflection.Assembly.GetExecutingAssembly().Location;
var potentials = new List<EnvDTE.DTE>();
foreach (var ide in ides)
{
var solutionPath = ide.Solution.FullName;
var topLevelSolutionDir = Path.GetDirectoryName(solutionPath);
var assemblyName = fullPathToAssembly;
if (assemblyName.StartsWith(topLevelSolutionDir, StringComparison.OrdinalIgnoreCase))
{
potentials.Add(ide);
}
}
EnvDTE.DTE chosenIde = null;
//If you have multiple ides open that can match your exe, you can come up with a scheme to pick a particular one
//(eg, put a file like solution.sln.pickme next to the solution whose ide you want to debug). If this is not a
//concern, just pick the first match.
if (potentials.Count > 0)
{
chosenIde = potentials[0];
}
var dbg = chosenIde != null ? (EnvDTE80.Debugger2)chosenIde.Debugger : null;
if (dbg != null)
{
var trans = dbg.Transports.Item("Default");
var proc = (EnvDTE80.Process2)dbg.GetProcesses(trans, System.Environment.MachineName).Item(Path.GetFileName(fullPathToAssembly));
var engines = new EnvDTE80.Engine[1];
engines[0] = trans.Engines.Item("Silverlight");
proc.Attach2(engines);
}
}
}
}
#endif
It's a bit of a shot in the dark, but assuming your silverlight app is capable of running on its own, you can, under your solution settings, set visual studio to start both apps together, and you should be attached to both of them.
If you can't add the Silverlight project to your solution (which will start debugging automatically), you might be able to make use of this tip. It will load both projects at the same time
http://saraford.net/2008/07/28/did-you-know-you-can-start-debugging-multiple-projects-268/

Resources