Using ClickOnce exe as a Visual Studio external tool - visual-studio

I have a command line executable I built which is published on the network via ClickOnce. The main use of this tool is through Visual Studio as an external tool. When I set this up in Visual Studio I am able to set the command path to the shortcut under roaming data for my profile.
However, Visual Studio resolves this to a path such as:
C:\Users\ME\AppData\Local\Apps\2.0\CGR50YPV.W5E\RXBXM176.HH8\crea..tion_f423fce0316e1dfa_0001.0000_adecafbe6c6acba3\MyAppp.exe
So what happens is if I launch the exe and grab a new version, Visual Studio is still pointing at the old version (as indicated above). I can fix this by re-pointing the command value of my external tool to the shortcut of my exe, but this is a bit frustrating to deal with.
How can I make this work without having to update my command path every time?

You shouldn't access a ClickOnce application via the exe file. If you're going to do that, just xcopy the \bin folder of the application to the other machine. If you want to use the update features, you should always invoke the ClickOnce application by using the shortcut or by invoking the link to the deployment manifest on the webserver. (The deployment manifest is the application file). You can do a process.start on that link.
[edit -- add new info]
Ohhhhh, so you're accessing the shortcut in the folder under the user's profile? Am I getting that? Instead of looking for that one, can you point to the shortcut on the start menu? It will add one automatically when the user installs the application, if the application is online/offline. The shortcut is added to the start menu to the location of the Publishing Company / Product Name using those fields from the Options dialog.
I do this by setting the assembly information to the same values, and retrieving the assembly information programmatically. I always set the assembly description to be identical to the product name, and the assembly company to be the same as the publishing company. Then I can do this:
Assembly code = Assembly.GetExecutingAssembly();
string company = string.Empty;
string description = string.Empty;
if (Attribute.IsDefined(code, typeof(AssemblyCompanyAttribute)))
{
AssemblyCompanyAttribute ascompany =
(AssemblyCompanyAttribute)Attribute.GetCustomAttribute(code,
typeof(AssemblyCompanyAttribute));
company = ascompany.Company;
}
if (Attribute.IsDefined(code, typeof(AssemblyDescriptionAttribute)))
{
AssemblyDescriptionAttribute asdescription =
(AssemblyDescriptionAttribute)Attribute.GetCustomAttribute(code,
typeof(AssemblyDescriptionAttribute));
description = asdescription.Description;
}
if (company != string.Empty && description != string.Empty)
{
string shortcutName =
string.Concat(Environment.GetFolderPath(Environment.SpecialFolder.Programs),
\", company, "\\", description, ".appref-ms");
}
(Sorry, I can't figure out how to make the code format prettier and show the indents properly, but you get the idea.)

Related

Issues while converting a windows app to windows service

I created a monitoring utility that checks cpu, ram, drive space stats and emails if the usage goes above set threshold. It works great in the system tray but I realized that the exe will stop when I log out of windows server. That led me to believe that I needed to create a windows service. I would like to use the existing GUI Form to save data to application settings and use those settings in windows service. Here are the steps I took so far,
Added a Windows Service class.
Modified the original code to get rid of any interactive items that were related to GUI Form.
Added the code to this class.
Added a Service installer.
Added this code to it-->
public ProjectInstaller()
{
InitializeComponent();
ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
ServiceInstaller serviceInstaller = new ServiceInstaller();
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Password = null;
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.ServiceName = "Server Monitoring";
this.Installers.Add(serviceProcessInstaller);
this.Installers.Add(serviceInstaller);
}
Change Start up object to Utility.Program.
When I try installing this through installUtil I get this error
System.IO.FileNotFoundException: Could not load file or assembly 'file:///C:\Use
rs\AdminUser\Desktop\Temp\Server' or one of its dependencies. The system cannot
find the file specified..
Thanks!
If you are saving these application settings into a file that is in the same directory as the Windows service, that is going to be your problem. All Windows Services are run in the C:/Windows directory (or a sub-directory in there) so when you access files you will need to do one of two things:
Change the executing directory
You can change the 'current directory' for the executing app back to the folder that contains the exe with the following line of code:
System.IO.Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);
This will make all relative files become relative to the executable location once again.
Make all file request full paths
This one is easier for some files than others. System files are the hardest. So if you are trying to get to a .config file, that's going to be a nightmare.

Embedded Resource missing in Visual Studio 2010 when name ends with "No.xxx"

I've come across a strange behaviour in Visual Studio 2010.
When using embedded resources (files which are added to my C# project and for which the Build Action property is set to Embedded Resource), the files are included in the output assembly as binary data. Listing the resources is straightforward:
class Program
{
static void Main(string[] args)
{
string[] names = typeof (Program).Assembly.GetManifestResourceNames ();
foreach (var name in names)
{
System.Console.Out.WriteLine (name);
}
}
}
However, if the embedded resource file name ends with No.xxx (the extension is irrelevant), the file does not show up in the list. I cannot figure out any reason why Visual Studio 2010 would not include such a file. What did I miss?
Note: if I rename the embedded resource file in the solution explorer to something else, then everything works as expected.
Dan from the Microsoft Connect team has finally provided a valid explanation for this behaviour:
Hello, thanks for the report, this is actually working normally. The reason is that any resx files whose names match the pattern .VALIDCULTURE.resx are assumed to be specific to that culture. (This is how it has worked since VS2002, for better or worse)
In your case "no" is a valid culture (Norwegian, I guess) so the build process builds it into a satellite assembly. Here's what I got when I tried this. Note the "no" subfolder. If in your app you change your current culture to Norwegian, the resource load will load this set of resources.
So the problem has nothing to do with the word No itself, but rather with the fact that it is a valid, two-letter, culture name (in my case Norwegian). I checked, and indeed, there was a sub-folder in bin\Debug named No, containing a satellite assembly named Project.resources.dll in it.
Renaming the resource to end with .EN.xxx or .FR.xxx does, of course, exhibit the same behaviour.
As of MSBuild 16.9 you can include files like these by setting the WithCulture property to "false", as pointed out by #reduckted :-)
<EmbeddedResource Include="Resources.en.xml" WithCulture="false" />

How to attach a debugger dynamically to a specific process

I am building an internal development tool to manage different processes commonly used in our development environment. The tool shows the list of the monitored processes, indicating their running state and allows to start or stop each process.
I'd like to add the functionality of attaching a debugger to a monitored process from my tool instead of going in Debug -> Attach to process in Visual Studio and finding the process.
My goal is to have something like Debugger.Launch() that would show a list of the available Visual Studio. I can't use Debugger.Launch(), because it launches the debugger on the process that makes the call. I would need something like Debugger.Launch(processId).
How do I achieve this functionality?
A solution could be to implement a command in each monitored process to call Debugger.Launch() when the command is received from the monitoring tool, but I would prefer something that does not require to modify the code of the monitored processes.
Side question:
When using Debugger.Launch(), instances of Visual Studio that already have a debugger attached are not listed. Visual Studio is not limited to one attached debugger, you can attach on multiple process when using Debug → Attach to process.
How do I bypass this limitation when using Debugger.Launch() or an alternative?
A coworker ended up with a solution using DTE, and I posted the code on PasteBin.
The methods of interest are AttachVisualStudioToProcess and TryGetVsInstance
Source Code
public static void AttachVisualStudioToProcess(Process visualStudioProcess, Process applicationProcess)
{
_DTE visualStudioInstance;
if (TryGetVsInstance(visualStudioProcess.Id, out visualStudioInstance))
{
//Find the process you want the Visual Studio instance to attach to...
DTEProcess processToAttachTo = visualStudioInstance.Debugger.LocalProcesses.Cast<DTEProcess>().FirstOrDefault(process => process.ProcessID == applicationProcess.Id);
// Attach to the process.
if (processToAttachTo != null)
{
processToAttachTo.Attach();
ShowWindow((int)visualStudioProcess.MainWindowHandle, 3);
SetForegroundWindow(visualStudioProcess.MainWindowHandle);
}
else
{
throw new InvalidOperationException("Visual Studio process cannot find specified application '" + applicationProcess.Id + "'");
}
}
}
private static bool TryGetVsInstance(int processId, out _DTE instance)
{
IntPtr numFetched = IntPtr.Zero;
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
IMoniker[] monikers = new IMoniker[1];
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
{
IBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
if (runningObjectVal is _DTE && runningObjectName.StartsWith("!VisualStudio"))
{
int currentProcessId = int.Parse(runningObjectName.Split(':')[1]);
if (currentProcessId == processId)
{
instance = (_DTE)runningObjectVal;
return true;
}
}
}
instance = null;
return false;
}
WinDbg does the chain debugging for native code by default. If you want to launch another instance of Visual Studio, check Launch the Debugger Automatically on MSDN:
To automate the existing debugger, use Marshal.GetActiveObject to get the current EnvDTE.Debugger then let it attach to the process you just created.
Sometimes, you may need to debug the startup code for an application that is launched by another process. Examples include services and custom setup actions. In these scenarios, you can have the debugger launch and automatically attach when your application starts.
To setup an application to launch the debugger automatically
Start the Registry Editor (regedit).
In the Registry Editor, open the HKEY_LOCAL_MACHINE folder.
Navigate to HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\currentversion\image file execution options.
In the Image File Execution Options folder, locate the name of the application you want to debug, such as myapp.exe. If you cannot find the application you want to debug:
a. Right-click the Image File Execution Options folder, and on the shortcut menu, click New Key.
b. Right-click the new key, and on the shortcut menu, click Rename.
c. Edit the key name to the name of your application; myapp.exe, in this example.
Right-click the myapp.exe folder, and on the shortcut menu, click New String Value.
Right-click the new string value, and on the shortcut menu, click Rename.
Change the name to debugger.
Right-click the new string value, and on the shortcut menu, click Modify.
The Edit String dialog box appears.
In the Value data box, type vsjitdebugger.exe.
Click OK.
From the Registry menu, click Exit.
The directory containing vsjitdebugger.exe must be in your system path. To add it to the system path, follow these steps:
a. Open the Control Panel in Classic view, and double-click System.
b. Click Advanced System Settings.
c. In System Properties, click the Advanced tab.
d. On the Advanced tab, click Environment Variables.
e. In the Environment Variables dialog box, under System variables, select Path, then click the Edit button.
f. In the Edit System Variable dialog box, add the directory to the Variable value box. Use a semicolon to separate it from other entries in the list.
g. Click OK to close the Edit System Variable dialog box.
h. Click OK to close the Environment Variables dialog box.
i. Click OK to close the System Properties dialog box.
Now, use any method to start your application. Visual Studio will start and load the application.
Here is some information about how you can programmatically attach the debugger to multiple processes:
Attach to locally running processes
Attach to remotely running processes

InstallShield - Get version of a file

I am using InstallShield 2010 and was wondering if anyone knows how to do the following:
I want to get the version of my main exe within my installer and make it the name of my setup.exe InstallShield is generating. Any idea how to do that?
There are several ways you could do this....depending on your IS project type (MSI, installscript, etc.)
1) create a variable such as Product_Name in the Property Manager, set it in your installscript and retrieve it to modify your *.exe name
2) Using SQL, you can programatically get the product_name and set the *.exe name. Search the Direct Editor (Installation Designer -> Additional Tools -> Direct Editor) for the exact location of the table/value you need. For example (below), using VBScript, I modify the path to the installation root of the files I pull into the IS project. Similarly this can be done for any table in the IS Direct Editor. Using a tool such as Visual build Pro I believe would help you out as well. It's well worth the ~$100!
Set oMSI = CreateObject("WindowsInstaller.Installer")
On Error Resume Next
' open ISM file in transacted mode
Set oDB = oMSI.OpenDatabase("C:\Path\to\myProject.ism", 1)
strQuery = "Select * FROM `ISPathVariable` WHERE `ISPathVariable`.`ISPathVariable` = 'InstallTreeFolder'"
'////////////////////////////////////////////////////////////
'// Update Path Variable
' fetch the one and only samplesource record
Set oView = oDB.OpenView(strQuery)
oView.Execute
Set oRec = oView.Fetch
' change field 2, the Value field
oRec.StringData(2) = "%INSTALL_TREE_ROOT%"
' update the changed record
oView.Modify 2, oRec
' close the view, commit changes, clean up
oView.Close: oDB.Commit: Set oMSI = Nothing
I was able to use the Automation services to accomplish this, which is the programming interface for InstallShield. Whenever I build my project in Visual Studio for my exe I run an exe in the Post-build that sets the InstallSheild project to be the same version.

How do I embed a File Version in an MSI file with Visual Studio?

I have a setup project for my C# program, and this setup project has a Version in its properties. I'd like for the MSI file that is generated to have this Version embedded in it, so I can mouse over it in explorer and see what version the file is.
I'm using VS2008. How can I do this?
If you simply add the "Version: 1.5.0" text into the Description property of the Setup Project, the version number also shows on the MSI file like so:
http://screencast.com/t/A499i6jS
As far as I know MSI file will never show version. Simple reason is that MSI files are not PE files, they are sort-of database. Msiexec.exe then interprets this database to do the actual installation. The version property you mention is used by MSI engine internally for upgrades, uninstalls etc and is never displayed.
That's a good question but I don't know any setup tool that could do that. Moreover I never encountered an MSI file with file version resource embedded in it, so it's not a common practice. Usually if I want to find out version of an MSI file I have to open it in Orca and check ProductVersion property there (in Property table).
Open up the associated .vdproj file in a text editor. Look for the "Product" section, then modify the "ProductVersion", and the "Manufacturer" fields.
"Product"
{
"Name" = "8:Microsoft Visual Studio"
"ProductName" = "8:tidAxCleanupScript"
"ProductCode" = "8:{0949AAAD-2C29-415E-851C-825C74C9CA81}"
"PackageCode" = "8:{8F012EF1-D5D0-43DC-BBFD-761A639DDB07}"
"UpgradeCode" = "8:{38DE1949-0782-4EF3-BDC2-080EB5B73EF8}"
"RestartWWWService" = "11:FALSE"
"RemovePreviousVersions" = "11:TRUE"
"DetectNewerInstalledVersion" = "11:TRUE"
"InstallAllUsers" = "11:FALSE"
"ProductVersion" = "8:**1.5.0**"
"Manufacturer" = "8:**Default Company Name**"
"ARPHELPTELEPHONE" = "8:"
I might be wrong, but doesn't the msi version follow the version in the AssemblyInfo file of your startup project?

Resources