How do I find the open folder in a VSIX extension - visual-studio-extensions

I'd like to write a VSIX LSP extension. I'd like this to work in the simplest possible way - that seems to be using the "Open Folder" feature to open a folder of code, and do my thing.
To start the LSP server, I need to know the directory of the opened folder. How do I know whether Visual Studio is in "open folder" mode (if it's not, the LSP should just not be started), and how do I know the path to that folder (so I can start the LSP server)?
I found https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.shell.interop.ivssolutionevents7?view=visualstudiosdk-2017 which seems promising in that I can register for when some some specific folder is opened - an event that tells me the "open folder" functionality has been used would probably be perfect - if folder is opened, start the LSP for that folder.

The following code will get you 3 information:
// get solution reference from a service provider (package, etc.)
var solution = (IVsSolution)ServiceProvider.GetService(typeof(SVsSolution));
solution.GetSolutionInfo(out string dir, out string file, out string ops);
// dir will contain the solution's directory path (folder in the open folder case)
solution.GetProperty((int)__VSPROPID.VSPROPID_IsSolutionOpen, out object open);
bool isOpen = (bool)open; // is the solution open?
// __VSPROPID7 needs Microsoft.VisualStudio.Shell.Interop.15.0.DesignTime.dll
solution.GetProperty((int)__VSPROPID7.VSPROPID_IsInOpenFolderMode, out object folderMode);
bool isInFolderMode = (bool)folderMode; // is the solution in folder mode?

Related

IIS Shadow Copied DLL accessing dependency DLLs from bin location

I am trying to resolve an issue with ASP.Net Framework 4.8 site using EFCore 3.1.16 in IIS. Microsoft.Data.SqlClient has a process lock on SNI.dll which causes issues with xcopy deployment in IIS.
I have tried a strategy of copying the SNI.dll to the same shadow copy location as Microsoft.Data.SqlClient so it doesn't have to try and access the DLL in the bin folder as outlined in https://github.com/lscorcia/sqlclient.snishadowcopy.
// Look for the main Microsoft.Data.SqlClient assembly in the
// shadow copy path
var sqlClientShadowAssembly = Directory.GetFiles(
currentShadowPath, "Microsoft.Data.SqlClient.dll",
SearchOption.AllDirectories).FirstOrDefault();
// Extract the directory information from the shadow assembly path
var sqlClientShadowPath =
Path.GetDirectoryName(sqlClientShadowAssembly);
// Find out the process bitness and choose the appropriate native
// assembly
var moduleName = Environment.Is64BitProcess ? "x86\\SNI.dll"
: "x64\\SNI.dll";
// Compute the source and target paths for the native assembly
var sourceFile = Path.Combine(currentPrivatePath, moduleName);
var targetFile = Path.Combine(sqlClientShadowPath, "SNI.dll");
File.Copy(sourceFile, targetFile);
However, it still tries to access the bin location first instead of the sni.dll that is in the same folder location.
I have checked that the Microsoft.Data.SqlClient in the shadow location is being used correctly by deleting the DLL and confirming that a FileNotFound exception is thrown.I have also tried copying directly into the same folder and also copying into an x64 sub folder in the shadow location.
In my case, the error occured only when my IIS application is located on an UNC path (e.g. "\\myserver\myshare\myapplication"). Here is a workaround that worked in my scenario.
Use P/Invoke to SetDllDirectory:
[DllImport(#"kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetDllDirectory(string lpPathName);
(See also this MSDN article about DLL load order)
Then, early in my Global.asax.cs Application_Start method (before any DB calls), call it like this:
SetDllDirectory(Server.MapPath(#"~/bin"))
After that, everything works as expected.
I still to consider this to be kind of a hack/workaround, but at least, it is working now in my scenario.
As I do understand, you can call SetDllDirectory multiple times to add additional directories (i.e. not overwrite the existing one).
So in case someone reading this might have other assemblies that refer to native DLLs in "x86" or "x64" folders, one might do something like this:
var bitness = Environment.Is64BitProcess ? #"x64" : #"x86";
SetDllDirectory(Server.MapPath($#"~/bin/{bitness}"));
I've also tried serving my test application from a local path (like "C:\Inetpub\wwwroot") and here, the error does not occur, even when not calling SetDllDirectory.
I'm still not sure why the error occurs for UNC paths only, and not for local paths, as I would expect that the shadow copied managed assemblies to fail the DllImports, too.
(I've also posted the above in this GitHub issue)

Qt get path to application for file type

I want to use open parameters for a file but QDesktopServices doesn't work that way so I'll be using QProcess but for that I need to have the path to the application. I've used QFileIconProvider to get the icon and QFileInfo to get the type but how can I get the path to the application that the OS will use for the desired file?
I would like to have a filename, get the application path, depending on the application (different open parameter syntax given the application) use the application path with the filename and the open file parameters
QCoreApplication::applicationDirPath();
Returns the directory that contains the application executable.
For example, if you have installed Qt in the C:\Qt directory, and you run the regexp example, this function will return "C:/Qt/examples/tools/regexp".

File Not found in exe of wpf application

When publishing the WPF application and generate an exe, I am unable to get the files which are placed in the templates-folder. When I copy my folder and files to bin it works, or if use
string StartUpPath = AppDomain.CurrentDomain.BaseDirectory.ToString();
// var gparent = Directory.GetParent(Directory.GetParent(Directory.GetParent(StartUpPath).ToString()).ToString()).ToString();
ReportDocument reportDocument = new ReportDocument();
StreamReader reader = new StreamReader(new FileStream(StartUpPath + #"\Templates\Invoice.xaml", FileMode.Open));
This code works fine for my local, but when I generate an exe, these files are not found.
That's because your files aren't in the exe. I had the same problem recently.
It sounds like you need to handle your resources - there are several ways to go about this, and Microsoft has a full explanation here about resources, including the difference between using Linked and Embedded Resources, and links through to other great guides.
I'm assuming you're using Visual Studio, in which case this should work;
right click the file you can't access
select properties (or press ALT + ENTER)
set Build Action to be Resource
set Copy to Output Directory to be Copy if newer
save
... and you should be good to go.
I think it throws an exception because the file doesn't exist on that machine. You can set Build Action to Content and Copy to Output Directory to Copy Always or Copy If Newer on property window.
These settings will make sure that you have the file to your output.

c# get path for directory in project

I'm trying get the path of a folder in my project called EmailAttachments. I tried
File.Exists("~/EmailAttachments/TestReport.pdf")
but that returns false. How can I get the path to a directory in the program so I can write files to it and retrieve them later?
This is in asp.net, not winforms
If you're trying to get the ASP.NET local path, use Server.MapPath("~/EmailAttachments/TestReport.pdf") to get its fully qualified path.

CruiseControl.net , SVN, web site and dependencies

I have an Asp.Net web site (there is no .csproj file).
This web site's source code is controlled by SVN.
I excluded the bin folder from the source control.
All the external assemblies are referenced from the DLL folder which is at the root of my SVN.
I try to deploy this website with cruisecontrol.net.
The problem :
Cruise control load all the files from subversion, it runs msbuild.exe against the .sln file. This results in an error : can't find the external assemblies (because the bin folder is excluded).
The solution I found so far :
Before my msbuild task , do a robocopy of the dll from my source control to the /bin folder.
Is there any other solution ? (I don't want to edit my configuration every time I add an external assembly to my project).
EDIT :
I finally used the "refresh file" technique here is the program I used to create them
class Program
{
private const string PATH_BIN = #"F:\WebSite\bin\";
private const string PATH_REFERENCE = #"C:\DLL\";
static void Main(string[] args)
{
foreach (string aFile in Directory.GetFiles(PATH_BIN))
{
if(!aFile.EndsWith(".dll"))
continue;
string pathRefreshFile = aFile + ".refresh";
if(File.Exists(pathRefreshFile))
continue;
string referenceFilePath = PATH_REFERENCE+Path.GetFileName(aFile);
if (!File.Exists(referenceFilePath))
continue;
using (StreamWriter sw = new StreamWriter(pathRefreshFile))
{
sw.Write(referenceFilePath);
}
}
}
}
I'm assuming that you have .dll files that are third party, rather than ones created by class library projects that you could just include in your solution and add as a project reference. We have a similar scenario with items like the Microsoft Anti-Xss library and other common third party .dlls that we reference in almost all our web apps.
Instead of excluding the \bin directory, exclude *.dll. This way, when you add a reference to the .dll, Visual Studio will add the .dll to the \bin directory AND a .dll.refresh file to the \bin directory.
That.Refresh file tells Visual Studio where to grab the .dll file from. As long as the original .dll is in that location then you should be able to build on the build server OR on a brand new developer's PC.
For example, we have a \shared\commonDlls directory in source control, where the .dlls are checked into source control.
All of our other apps reference these when needed. As long as these are checked in, when the build server builds your project, it will know to go tot he \shared\commonDlls directory to copy that dll in.
This enables us to have one master source for the .dll files so that we don't have to copy it into a few dozen different web apps when it's time to upgrade. We put it into our shared\commonDlls directory (overwriting the original), check it in, and all of our web apps are now using the latest version.
edit - added - links to answers on related topics that have bearing on this answer:
SVN and binaries
How manage references in SVN/Visual Studio?
Best Practice: Collaborative Environment, Bin Directory, SVN
Edit - added - section from our ccnet.config file pointing to a .sln file
This is probably not needed, but since I mentioned ointing ot the .sln file in my comment below I thought I'd add it.
<msbuild>
<description>DonationRequests</description>
<workingDirectory>D:\dev\svn\trunk\Intranet\DonationRequests</workingDirectory>
<executable>C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe</executable>
<projectFile>DonationRequests.sln</projectFile>
<buildArgs>/p:Configuration=Debug /v:diag</buildArgs>
<timeout>900</timeout>
<logger>C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>

Resources