envdte doesn't fire some Debug events - c#

I'm trying to use envdte to communicate with visual studio debugger. Unfortunately I can't fire events OnEnterBreakMode/OnEnterRunMode/OnEnterDesignMode.
Event OnContextChanged works well, but the others are not fired.
Below Is my code to reproduce the problem C# Console Application (I was trying to simplify it as much as possible)
Most important is class Debugger. I test it in such way:
1. Compile code below
2. Open c++ project in Visual studio
3. Run compiled program from step 1
4. Connect to Visual Studio (type 0 and enter)
5. Start Debugging this c++ project - run debugging, add break points step, step in, step out, f5 ...
Finally the only output I can receive looks like below:
Select Debugger Number:
0 - !VisualStudio.DTE.15.0:13000
0
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
Context Changed
My test code:
using System;
using System.Collections.Generic;
using EnvDTE;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace DebugWatchTest
{
class DebuggerConnector
{
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
public static List<string> FindDebuggers()
{
List<string> result = new List<string>();
IBindCtx bindCtx = null;
IRunningObjectTable rot = null;
IEnumMoniker enumMonikers = null;
try
{
Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
bindCtx.GetRunningObjectTable(out rot);
rot.EnumRunning(out enumMonikers);
IMoniker[] moniker = new IMoniker[1];
IntPtr numberFetched = IntPtr.Zero;
while (enumMonikers.Next(1, moniker, numberFetched) == 0)
{
string name = null;
IMoniker runningObjectMoniker = moniker[0];
if (runningObjectMoniker != null)
{
try {
runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
}
catch (UnauthorizedAccessException)
{ } // Do nothing, there is something in the ROT that we do not have access to.
}
const string progName = "!VisualStudio.DTE";
if (!string.IsNullOrEmpty(name) && name.StartsWith(progName, StringComparison.Ordinal))
{
result.Add(name);
}
}
}
finally
{
if (enumMonikers != null) {
Marshal.ReleaseComObject(enumMonikers);
}
if (rot != null) {
Marshal.ReleaseComObject(rot);
}
if (bindCtx != null) {
Marshal.ReleaseComObject(bindCtx);
}
}
return result;
}
public static DTE Connect(string debuggerName)
{
object runningObject = null;
IBindCtx bindCtx = null;
IRunningObjectTable rot = null;
IEnumMoniker enumMonikers = null;
try
{
Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
bindCtx.GetRunningObjectTable(out rot);
rot.EnumRunning(out enumMonikers);
IMoniker[] moniker = new IMoniker[1];
IntPtr numberFetched = IntPtr.Zero;
while (enumMonikers.Next(1, moniker, numberFetched) == 0)
{
string name = null;
IMoniker runningObjectMoniker = moniker[0];
if (runningObjectMoniker != null)
{
try {
runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
}
catch (UnauthorizedAccessException)
{ } // Do nothing, there is something in the ROT that we do not have access to.
}
if (!string.IsNullOrEmpty(name) && name.Equals(debuggerName, StringComparison.Ordinal))
{
Marshal.ThrowExceptionForHR(rot.GetObject(runningObjectMoniker, out runningObject));
break;
}
}
}
finally
{
if (enumMonikers != null) {
Marshal.ReleaseComObject(enumMonikers);
}
if (rot != null) {
Marshal.ReleaseComObject(rot);
}
if (bindCtx != null) {
Marshal.ReleaseComObject(bindCtx);
}
}
if (runningObject is DTE)
{
return runningObject as DTE;
}
return null;
}
}
class Debugger
{
DTE instance;
Events e;
DebuggerEvents de;
public Debugger(DTE d)
{
instance = d;
e = instance.Events;
de = e.DebuggerEvents;
de.OnContextChanged += De_OnContextChanged;
de.OnEnterBreakMode += De_OnEnterBreakMode;
de.OnEnterRunMode += De_OnModeChanged;
de.OnEnterDesignMode += De_OnModeChanged;
}
private void De_OnModeChanged(dbgEventReason Reason) {
Console.WriteLine("Mode Changed");
}
private void De_OnEnterBreakMode(dbgEventReason Reason, ref dbgExecutionAction ExecutionAction)
{
Console.WriteLine("BreakMode");
}
private void De_OnContextChanged(Process NewProcess, EnvDTE.Program NewProgram, Thread NewThread, StackFrame NewStackFrame)
{
Console.WriteLine("Context Changed");
}
}
class Program
{
static void Main(string[] args)
{
List<string> debuggers = DebuggerConnector.FindDebuggers();
Console.WriteLine("Select Debugger Number:");
for(int i = 0; i < debuggers.Count;++i)
Console.WriteLine($"{i} - {debuggers[i]}");
string number = Console.ReadLine();
string processId = debuggers[int.Parse(number)];
DTE msvc = DebuggerConnector.Connect(processId);
Debugger d = new Debugger(msvc);
while (true)
{ }
}
}
}
Aby hints what is wrong in my code ?

Related

Programatically attach current debugger to started processes

One project I work on starts local processes during testing. Among other things it starts an instance of IISExpress running a .NET website.
I can debug the website code by manually attaching the debugger to the IISExpress process. However, I'd like to automate this manual step.
Below is the code I have so far. It does seem to find the process to attach to (i.e. Attach2 is called). However the break points in the web site code are still not getting hit even after Attach2 is called (they show up as a red circle with white fill).
What am I doing wrong?
public class DebuggerHelper
{
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
public static bool TryAttachProcessesToVisualStudioDebuggingCurrentProcess(params int[] processIds)
{
var notAttached = processIds.Length;
var currentProcessId = System.Diagnostics.Process.GetCurrentProcess().Id;
IBindCtx bindCtx = null;
IRunningObjectTable runningObjectTable = null;
IEnumMoniker enumMonikers = null;
try
{
Marshal.ThrowExceptionForHR(CreateBindCtx(0, out bindCtx));
bindCtx.GetRunningObjectTable(out runningObjectTable);
runningObjectTable.EnumRunning(out enumMonikers);
enumMonikers.Reset();
var numFetched = IntPtr.Zero;
var monikers = new IMoniker[1];
while (enumMonikers.Next(1, monikers, numFetched) == 0)
{
monikers[0].GetDisplayName(bindCtx, null, out var runningObjectName);
runningObjectTable.GetObject(monikers[0], out var runningObjectVal);
if (runningObjectVal is EnvDTE80.DTE2 dte
&& runningObjectName.StartsWith("!VisualStudio.DTE.15.0"))
{
foreach (EnvDTE80.Process2 debuggedProcess in dte.Debugger.DebuggedProcesses)
{
if (debuggedProcess.ProcessID == currentProcessId)
{
foreach (EnvDTE80.Process2 localProcess in dte.Debugger.LocalProcesses)
{
if (processIds.Contains(localProcess.ProcessID))
{
localProcess.Attach();
notAttached--;
}
}
}
}
}
}
return notAttached == 0;
}
finally
{
if (enumMonikers != null)
{
Marshal.ReleaseComObject(enumMonikers);
}
if (runningObjectTable != null)
{
Marshal.ReleaseComObject(runningObjectTable);
}
if (bindCtx != null)
{
Marshal.ReleaseComObject(bindCtx);
}
}
}
}
Edit: Seems like it somewhat attached but something is failing. I'm getting the following:
Turned out the debugger was attaching to IISExpress as a native process instead of a managed one. After changing the code to instead use Attach2 with managed specified it works as expected.
Updated working code (some of the other changes are probably unneeded and only a result of the troubleshooting process):
public class DebugHelper
{
[DllImport("ole32.dll")]
static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
[DllImport("ole32.dll")]
static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
public static bool AttachTo(params int[] processIds)
{
var notAttached = processIds.Length;
var maybeDebuggedProcessId = System.Diagnostics.Process.GetCurrentProcess().Id;
IRunningObjectTable runningObjectTable = null;
IEnumMoniker enumMoniker = null;
IBindCtx bindCtx = null;
try
{
CreateBindCtx(0, out bindCtx);
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out enumMoniker);
var monikers = new IMoniker[1];
var numFetched = IntPtr.Zero;
while (enumMoniker.Next(1, monikers, numFetched) == 0)
{
monikers[0].GetDisplayName(bindCtx, null, out var runningObjectName);
runningObjectTable.GetObject(monikers[0], out var runningObjectVal);
if (runningObjectVal is DTE2 dte
&& dte.Debugger is Debugger2 debugger
&& runningObjectName.StartsWith("!VisualStudio.DTE.15.0"))
{
foreach (Process2 debuggedProcess in dte.Debugger.DebuggedProcesses)
{
if (debuggedProcess.ProcessID == maybeDebuggedProcessId)
{
var def = debugger.Transports.Item("Default");
var engines = new List<Engine>();
foreach (Engine engine in def.Engines)
{
engines.Add(engine);
}
var debugEngine = new[]
{
engines.Single(x => x.Name == "Managed (v4.6, v4.5, v4.0)")
};
foreach (Process2 localProcess in dte.Debugger.LocalProcesses)
{
if (processIds.Contains(localProcess.ProcessID))
{
localProcess.Attach2(debugEngine);
notAttached--;
}
}
break;
}
}
}
}
return notAttached == 0;
}
finally
{
if (bindCtx != null) Marshal.ReleaseComObject(bindCtx);
if (runningObjectTable != null) Marshal.ReleaseComObject(runningObjectTable);
if (enumMoniker != null) Marshal.ReleaseComObject(enumMoniker);
}
}
}

How to resolve the Error " Unable to locate local.config.user file. Make sure you have run 'build.cmd local' '

i am trying to run this test on local host, but it keeps throwing the error " Unable to locate local.config.user file. Make sure you have run 'build.cmd local'"
So how do I locate local.config.user file? Or how to run build.cmd local ? Please try see Code below
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Microsoft.WindowsAzure.ServiceRuntime;
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Configurations
{
public class ConfigurationProvider : IConfigurationProvider, IDisposable
{
readonly Dictionary<string, string> configuration = new Dictionary<string, string>();
EnvironmentDescription environment = null;
const string ConfigToken = "config:";
bool _disposed = false;
public string GetConfigurationSettingValue(string configurationSettingName)
{
return this.GetConfigurationSettingValueOrDefault(configurationSettingName, string.Empty);
}
public string GetConfigurationSettingValueOrDefault(string configurationSettingName, string defaultValue)
{
try
{
if (!this.configuration.ContainsKey(configurationSettingName))
{
string configValue = string.Empty;
bool isEmulated = true;
bool isAvailable = false;
try
{
isAvailable = RoleEnvironment.IsAvailable;
}
catch (TypeInitializationException) { }
if (isAvailable)
{
configValue = RoleEnvironment.GetConfigurationSettingValue(configurationSettingName);
isEmulated = RoleEnvironment.IsEmulated;
}
else
{
configValue = ConfigurationManager.AppSettings[configurationSettingName];
isEmulated = Environment.CommandLine.Contains("iisexpress.exe") ||
Environment.CommandLine.Contains("WebJob.vshost.exe");
}
if (isEmulated && (configValue != null && configValue.StartsWith(ConfigToken, StringComparison.OrdinalIgnoreCase)))
{
if (environment == null)
{
LoadEnvironmentConfig();
}
configValue =
environment.GetSetting(configValue.Substring(configValue.IndexOf(ConfigToken, StringComparison.Ordinal) + ConfigToken.Length));
}
try
{
this.configuration.Add(configurationSettingName, configValue);
}
catch (ArgumentException)
{
// at this point, this key has already been added on a different
// thread, so we're fine to continue
}
}
}
catch (RoleEnvironmentException)
{
if (string.IsNullOrEmpty(defaultValue))
throw;
this.configuration.Add(configurationSettingName, defaultValue);
}
return this.configuration[configurationSettingName];
}
void LoadEnvironmentConfig()
{
var executingPath = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);
// Check for build_output
int buildLocation = executingPath.IndexOf("Build_Output", StringComparison.OrdinalIgnoreCase);
if (buildLocation >= 0)
{
string fileName = executingPath.Substring(0, buildLocation) + "local.config.user";
if (File.Exists(fileName))
{
this.environment = new EnvironmentDescription(fileName);
return;
}
}
// Web roles run in there app dir so look relative
int location = executingPath.IndexOf("Web\\bin", StringComparison.OrdinalIgnoreCase);
if (location == -1)
{
location = executingPath.IndexOf("WebJob\\bin", StringComparison.OrdinalIgnoreCase);
}
if (location >=0)
{
string fileName = executingPath.Substring(0, location) + "..\\local.config.user";
if (File.Exists(fileName))
{
this.environment = new EnvironmentDescription(fileName);
return;
}
}
throw new ArgumentException("Unable to locate local.config.user file. Make sure you have run 'build.cmd local'.");
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
if (environment != null)
{
environment.Dispose();
}
}
_disposed = true;
}
~ConfigurationProvider()
{
Dispose(false);
}
}
}
Hi sir, thank you for your help. I have tried "Build.cmd build" and "Build.cmd local" it gave me this error.
Normally in the root folder of your Visual Studio solution you should have a build.cmd file.
You need to run Developer Command Prompt for VS2015 (or with an another version of installed Visual Studio, use Windows Search in the Start menu) as an Administrator and change directory (cd [directory with solution]) to the root folder of mentioned solution and then type and run build.cmd local by clicking Enter.

Load serialized data in multiple applications C#

Scenario:
I have a program which uses a simple class to generate game data. Using the said program, I write the data out using serialization and BinaryFormatter to a file to be used by a second program. Reading the data from this initial program works without issue.
Problem:
It's probably down to my ignorance to how serialized files are handled, but I cannot then load this data into a second program, the actual game itself.
saveGame code (in program 1):
static List<GameData> gameData;
static GameData currentData;
private void saveGame(Sudoku sdk) {
BinaryFormatter bf = new BinaryFormatter();
FileStream file = null;
try {
if(!File.Exists(gameDataFile[currentDifficulty])) {
file = File.Open(gameDataFile[currentDifficulty], FileMode.CreateNew);
} else {
file = File.Open(gameDataFile[currentDifficulty], FileMode.Append);
}
currentData = setGameData(sdk);
bf.Serialize(file, currentData);
savePuzzleLog();
} catch(Exception e) {
Debug.LogException("saveGame", e);
}
if(file != null) {
file.Close();
}
}
loadGameData: (in program 2)
public static List<GameData> gameData;
public bool loadGameData() {
if(gameData == null) {
gameData = new List<GameData>();
} else {
gameData.Clear();
}
bool loadData = true;
bool OK = false;
if(File.Exists(gameDataFile[currentDifficulty])) {
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(gameDataFile[currentDifficulty], FileMode.Open);
while(loadData) {
try {
GameData gd = new GameData();
gd = (GameData)bf.Deserialize(file);
gameData.Add(gd);
OK = true;
if(file.Position == file.Length) {
loadData = false;
}
} catch(Exception e) {
Debug.LogException(e);
loadData = false;
OK = false;
}
}
if(file != null) {
file.Close();
}
} else {
Debug.LogWarning(gameDataFile[currentDifficulty] + " does not exist!");
}
return OK;
}
GameData Class: (1st program)
[Serializable]
class GameData {
private int gameID;
private List<int> contentArray;
private int difficultyValue;
public GameData(List<int> data = null) {
id = -1;
difficulty = -1;
if(content != null) {
content.Clear();
} else {
content = new List<int>();
}
if(data != null) {
foreach(int i in data) {
content.Add(i);
}
}
}
public int id {
get {
return this.gameID;
}
set {
this.gameID = value;
}
}
public int difficulty {
get {
return this.difficultyValue;
}
set {
this.difficultyValue = value;
}
}
public List<int> content {
get {
return this.contentArray;
}
set {
this.contentArray = value;
}
}
}
GameData Class: (2nd program) The only difference is declaring as public
[Serializable]
public class GameData {
private int gameID;
private List<int> contentArray;
private int difficultyValue;
public GameData(List<int> data = null) {
id = -1;
difficulty = -1;
if(content != null) {
content.Clear();
} else {
content = new List<int>();
}
if(data != null) {
foreach(int i in data) {
content.Add(i);
}
}
}
public int id {
get {
return this.gameID;
}
set {
this.gameID = value;
}
}
public int difficulty {
get {
return this.difficultyValue;
}
set {
this.difficultyValue = value;
}
}
public List<int> content {
get {
return this.contentArray;
}
set {
this.contentArray = value;
}
}
}
What my question is, is how do I save the data out in one program and be able to load it using a different program without getting serialization errors or do I need to use an alternate save/load method and/or class structure?
When I had to do it i made it this way :
An independant dll (assembly) containing the class holding the data (for you the GameData class), and utility methods to save/load from a file.
Your two other projects must then reference this dll (assembly) and you should be able to (de)serialize correctly.
What I think the issue is in your case is that the BinaryFormatter does not only save the data in the file, but also the complete Type of the serialized object.
When you try to deserialize in another similar object, even if the structure is the same, the full name of the class is not (because the assembly name is not).
OK, I've sorted it using the advice given. Instead of using BinaryFormatter I have used BinaryWriter and BinaryReader as follows...
In program 1 (the creator):
private byte[] setByteData(Sudoku sdk) {
List<int> clues = sdk.puzzleListData();
byte[] bd = new byte[puzzleSize];
SudokuSolver solver = new SudokuSolver();
List<int> solution = solver.Solve(sdk.Copy(), false, currentDifficulty).puzzleListData();
for(int i = 0; i < puzzleSize; i++) {
bd[i] = Convert.ToByte(solution[i] + (clues[i] == 0 ? 0xF0 : 0));
}
return bd;
}
private GameData setGameData(Sudoku sdk) {
List<int> clues = sdk.puzzleListData();
GameData gd = new GameData();
gd.id = puzzleList.Items.Count;
gd.difficulty = currentDifficulty;
SudokuSolver solver = new SudokuSolver();
List<int> solution = solver.Solve(sdk.Copy(), false, currentDifficulty).puzzleListData();
for(int i = 0; i < puzzleSize; i++) {
gd.content.Add(solution[i] + (clues[i] == 0 ? 0xF0 : 0));
}
return gd;
}
private List<int> getByteData(byte[] data) {
List<int> retVal = new List<int>();
foreach(byte i in data) {
if(i > 9) {
retVal.Add(i - 0xF0);
} else {
retVal.Add(0);
}
}
return retVal;
}
private string getGameData(List<int> data) {
string retVal = "";
foreach(int i in data) {
if(i > 9) {
retVal += (i - 0xF0).ToString();
} else {
retVal += i.ToString();
}
}
return retVal;
}
private void saveGame(Sudoku sdk) {
FileStream file = null;
BinaryWriter bw = null;
try {
if(!File.Exists(gameDataFile[currentDifficulty])) {
file = File.Open(gameDataFile[currentDifficulty], FileMode.CreateNew);
} else {
file = File.Open(gameDataFile[currentDifficulty], FileMode.Append);
}
bw = new BinaryWriter(file);
currentData = setGameData(sdk);
byte[] bd = setByteData(sdk);
bw.Write(currentData.id);
bw.Write(currentData.difficulty);
bw.Write(bd);
savePuzzleLog();
} catch(Exception e) {
Debug.LogException("saveGame", e);
}
if(file != null) {
if(bw != null) {
bw.Flush();
bw.Close();
}
file.Close();
}
}
In program 2: (the actual game)
public bool loadGameData() {
if(gameData == null) {
gameData = new List<GameData>();
} else {
gameData.Clear();
}
bool loadData = true;
bool OK = false;
if(File.Exists(gameDataFile[currentDifficulty])) {
FileStream file = File.Open(gameDataFile[currentDifficulty], FileMode.Open);
BinaryReader br = new BinaryReader(file);
while(loadData) {
try {
GameData gd = new GameData();
gd.id = br.ReadInt32();
gd.difficulty = br.ReadInt32();
gd.content = getByteData(br.ReadBytes(puzzleSize));
gameData.Add(gd);
OK = true;
if(file.Position == file.Length) {
loadData = false;
}
} catch(Exception e) {
Debug.LogException(e);
loadData = false;
OK = false;
}
}
if(file != null) {
file.Close();
}
} else {
Debug.LogWarning(gameDataFile[currentDifficulty] + " does not exist!");
}
return OK;
}
The class structure is the same as before but using this method has resolved my issue and I can now create the data files using my creator and load the data comfortably using the actual game.
Thanks all for your assistance and advice to help me get this sorted.

How to get list of projects in current Visual studio solution?

When we open Package Manager Console in any open solution, it shows all the projects of that solution. How it is loading all the projects of the same solution.
When I tried with below shown code it is fetching me projects of the first solution which I have opened.
private List<Project> GetProjects()
{
var dte = (DTE)Marshal.GetActiveObject(string.Format(CultureInfo.InvariantCulture, "VisualStudio.DTE.{0}.0", targetVsVersion));
var projects = dte.Solution.OfType<Project>().ToList();
return projects;
}
Here are a various set of functions that allow you to enumerate projects in a given solution. This is how you would use it with the current solution:
// get current solution
IVsSolution solution = (IVsSolution)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(IVsSolution));
foreach(Project project in GetProjects(solution))
{
....
}
....
public static IEnumerable<EnvDTE.Project> GetProjects(IVsSolution solution)
{
foreach (IVsHierarchy hier in GetProjectsInSolution(solution))
{
EnvDTE.Project project = GetDTEProject(hier);
if (project != null)
yield return project;
}
}
public static IEnumerable<IVsHierarchy> GetProjectsInSolution(IVsSolution solution)
{
return GetProjectsInSolution(solution, __VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION);
}
public static IEnumerable<IVsHierarchy> GetProjectsInSolution(IVsSolution solution, __VSENUMPROJFLAGS flags)
{
if (solution == null)
yield break;
IEnumHierarchies enumHierarchies;
Guid guid = Guid.Empty;
solution.GetProjectEnum((uint)flags, ref guid, out enumHierarchies);
if (enumHierarchies == null)
yield break;
IVsHierarchy[] hierarchy = new IVsHierarchy[1];
uint fetched;
while (enumHierarchies.Next(1, hierarchy, out fetched) == VSConstants.S_OK && fetched == 1)
{
if (hierarchy.Length > 0 && hierarchy[0] != null)
yield return hierarchy[0];
}
}
public static EnvDTE.Project GetDTEProject(IVsHierarchy hierarchy)
{
if (hierarchy == null)
throw new ArgumentNullException("hierarchy");
object obj;
hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out obj);
return obj as EnvDTE.Project;
}
There may be a nicer way but I had a quick go at this and found this to work (it assumes you have a way of knowing the solution name). According to this post, GetActiveObject does not guarantee the current instance of VS which is why you're getting results from another instance. Instead, you can use the GetDTE method shown there:
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
public static DTE GetDTE(int processId)
{
string progId = "!VisualStudio.DTE.10.0:" + processId.ToString();
object runningObject = null;
IBindCtx bindCtx = null;
IRunningObjectTable rot = null;
IEnumMoniker enumMonikers = null;
try
{
Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
bindCtx.GetRunningObjectTable(out rot);
rot.EnumRunning(out enumMonikers);
IMoniker[] moniker = new IMoniker[1];
IntPtr numberFetched = IntPtr.Zero;
while (enumMonikers.Next(1, moniker, numberFetched) == 0)
{
IMoniker runningObjectMoniker = moniker[0];
string name = null;
try
{
if (runningObjectMoniker != null)
{
runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
}
}
catch (UnauthorizedAccessException)
{
// Do nothing, there is something in the ROT that we do not have access to.
}
if (!string.IsNullOrEmpty(name) && string.Equals(name, progId, StringComparison.Ordinal))
{
Marshal.ThrowExceptionForHR(rot.GetObject(runningObjectMoniker, out runningObject));
break;
}
}
}
finally
{
if (enumMonikers != null)
{
Marshal.ReleaseComObject(enumMonikers);
}
if (rot != null)
{
Marshal.ReleaseComObject(rot);
}
if (bindCtx != null)
{
Marshal.ReleaseComObject(bindCtx);
}
}
return (DTE)runningObject;
}
If you know the solution name in advance, you can find it in the MainWindowTitle property of Process and pass the ProcessID to the method above.
var dte = GetDTE(System.Diagnostics.Process.GetProcesses().Where(x => x.MainWindowTitle.StartsWith("SolutionName") && x.ProcessName.Contains("devenv")).FirstOrDefault().Id);
Whilst the above code worked, I encountered a COM error which I fixed by using the MessageFilter class shown here.
From that post, this is what the MessageFilter class looks like
public class MessageFilter : IOleMessageFilter
{
// Class containing the IOleMessageFilter
// thread error-handling functions.
// Start the filter.
public static void Register()
{
IOleMessageFilter newFilter = new MessageFilter();
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
// Done with the filter, close it.
public static void Revoke()
{
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(null, out oldFilter);
}
//
// IOleMessageFilter functions.
// Handle incoming thread requests.
int IOleMessageFilter.HandleInComingCall(int dwCallType,
System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr lpInterfaceInfo)
{
//Return the flag SERVERCALL_ISHANDLED.
return 0;
}
// Thread call was rejected, so try again.
int IOleMessageFilter.RetryRejectedCall(System.IntPtr
hTaskCallee, int dwTickCount, int dwRejectType)
{
if (dwRejectType == 2)
// flag = SERVERCALL_RETRYLATER.
{
// Retry the thread call immediately if return >=0 &
// <100.
return 99;
}
// Too busy; cancel call.
return -1;
}
int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee,
int dwTickCount, int dwPendingType)
{
//Return the flag PENDINGMSG_WAITDEFPROCESS.
return 2;
}
// Implement the IOleMessageFilter interface.
[DllImport("Ole32.dll")]
private static extern int
CoRegisterMessageFilter(IOleMessageFilter newFilter, out
IOleMessageFilter oldFilter);
}
[ComImport(), Guid("00000016-0000-0000-C000-000000000046"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(
int dwCallType,
IntPtr hTaskCaller,
int dwTickCount,
IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(
IntPtr hTaskCallee,
int dwTickCount,
int dwRejectType);
[PreserveSig]
int MessagePending(
IntPtr hTaskCallee,
int dwTickCount,
int dwPendingType);
}
Then you can access the project names like this
var dte = GetDTE(System.Diagnostics.Process.GetProcesses().Where(x => x.MainWindowTitle.StartsWith("SolutionName") && x.ProcessName.Contains("devenv")).FirstOrDefault().Id);
MessageFilter.Register();
var projects = dte.Solution.OfType<Project>().ToList();
MessageFilter.Revoke();
foreach (var proj in projects)
{
Debug.WriteLine(proj.Name);
}
Marshal.ReleaseComObject(dte);
I believe you can use something like this:
var dte = (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE));
if (dte != null)
{
var solution = dte.Solution;
if (solution != null)
{
// get your projects here
}
}

Unit tests cannot provide situation when the class runs its methods in separate AppDomain

i have got some class that runs its method (WorkFunction) in another AppDomain. When i use it in Console Application, all right. But when i try run the same code in the unit test frameworks (MSTest, Nunit), I get follow error:
System.IO.FileNotFoundException was unhandled HResult = -2147024894
Message = Could not load file or assembly "FMEngine.Common.Tests,
Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null" or one of
its dependencies. Can not find the file specified. Source =
mscorlib FileName = FMEngine.Common.Tests, Version = 1.0.0.0, Culture
= neutral ...
Where is the error?
the code that work correctly in console but it's wrong in the tests
ThreadWorkerTemplateWrapper twt = new ThreadWorkerTemplateWrapper(-1, new TimeSpan(0, 0, 1));
twt.Start();
Console.ReadLine();
twt.Stop();
Source code:
[Serializable]
public class ThreadWorkerTemplate
{
#region non important code for question
private int _threadFlag;
[NonSerialized] private readonly Thread _thread;
[NonSerialized] private AppDomain _threadDomain;
public ThreadWorkerTemplate(int timeout, TimeSpan joinTimeout)
{
IterationTimespan = timeout;
JoinTimeout = joinTimeout;
_threadFlag = 0;
_thread = new Thread(ThreadHandler);
}
private bool IsDelaySpended(DateTime now, DateTime lastExecuted)
{
return (now > lastExecuted)
&&
(now - lastExecuted).TotalMilliseconds > IterationTimespan;
}
private bool IsNothingDelay()
{
return IterationTimespan == -1;
}
public void Start()
{
if (null == _threadDomain)
_threadDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString());
if (Interlocked.CompareExchange(ref _threadFlag, 1, 0) == 0)
_thread.Start();
}
public void Stop()
{
bool exitByTimeout = false;
// стопаем с одновременным измененем флага
if (Interlocked.CompareExchange(ref _threadFlag, 0, 1) == 1)
exitByTimeout = !_thread.Join(JoinTimeout);
if (exitByTimeout)
{
AppDomain.Unload(_threadDomain);
_threadDomain = null;
}
}
public bool IsStarted { get { return Interlocked.CompareExchange(ref _threadFlag, 0, 0) == 1; } }
public TimeSpan JoinTimeout { get; private set; }
public int IterationTimespan { get; private set; }
#endregion
private void ThreadHandler()
{
DateTime lastExecuted = DateTime.MinValue;
Func<bool> isAllowed =
() =>
{
DateTime now = DateTime.Now;
bool result = IsNothingDelay() || IsDelaySpended(now, lastExecuted);
if (result)
lastExecuted = now;
if (IterationTimespan != -1)
Thread.Sleep(1);
return result;
};
while (Interlocked.CompareExchange(ref _threadFlag, 0, 0) == 1)
{
if (isAllowed())
_threadDomain.DoCallBack(new CrossAppDomainDelegate(WorkFunction));
}
}
public void WorkFunction()
{
Thread.Sleep(60000);
}
}

Categories