I try to make Visual Studio Extension. It will be extension for debug. It will work at debug mode. I need get object instance from debugged application in my extension. And I do not know how to do it.
I have follow code:
var dte = (DTE)_serviceProvider.GetService(typeof(DTE));
var debugger = dte?.Debugger;
var currentMode = debugger?.CurrentMode;
if (currentMode != dbgDebugMode.dbgBreakMode) return;
var expression = debugger.GetExpression(myExpression);
myExpression contains expression for retrieve some object, for example IDbConnection.
How can I get IDbConnection in my extension-code? expression.Value has string-value.
Is that even possible?
Related
I have some very basic code:
NHibernate.ISession session = doSomeStuffWithNHibernateSession();
Using the object inspector in the Visual Studio debugger, I am able to open the ActionQueue of the session object and list all the pending NHibernate commands.
Is there a way to access it programmatically ? Being able to access properties like InsertionCount or HasAnyQueuedAction would be enough.
This should work (using explicit type names instead of var to simplify their finding in NH)
NHibernate.ISession session = ...
// here we get the access to underling implementation
NHibernate.Impl.SessionImpl sessionImpl = session
.GetSessionImplementation() as NHibernate.Impl.SessionImpl;
// and we can now work with action queue
var actionQueue = sessionImpl.ActionQueue;
// and check it
var count = actionQueue.InsertionsCount;
var hasAnyQueuedActions = actionQueue.HasAnyQueuedActions;
I have this code:
public ObjectModel.Path GetPath(int pathid)
{
var path = this.DbContext.Paths.Find(pathid);
var app = GetApplicationById(path.ApplicationId.GetValueOrDefault());
var p = new Path
{
ApplicationId = path.ApplicationId,
Id = path.Id, Password = path.Password,
UserName = path.UserName,
LocalUrl = string.IsNullOrEmpty(path.LocalUrl) ? null : System.IO.Path.Combine(path.LocalUrl, app.Name)
};
return p;
}
The variable p is always null. How is this posible?
Here is a print screen of what I am talking about:
I also try with immediate windows I got this:
p CANNOT BE NULL since im creating a new instance of it while all of its properties are not null. I tried clean the project, restart windows, open visual studio as administrator and nothing happened.
Any idea why?
The application is running on .net 4.0, visual studio 2017 v15.2(26430.13) relase.
Your variable is not null.
Debugger by default try to display content of ToString() method when it's overloaded. In this case it's displaying (propably) Uri or FtpUrl value which is null.
Try to click little triangle on your variable when you hover it then you will see full contents of the variable.
With Mono.Cecil it looks quite simple when we can just set the Body of the target MethodDefinition to the Body of the source MethodDefinition. For simple methods, that works OK. But for some methods whereas a custom type is used (such as to init a new object), it won't work (with an exception thrown at the time writing the assembly back).
Here is my code:
//in current app
public class Form1 {
public string Test(){
return "Modified Test";
}
}
//in another assembly
public class Target {
public string Test(){
return "Test";
}
}
//the copying code, this works for the above pair of methods
//the context here is of course in the current app
var targetAsm = AssemblyDefinition.ReadAssembly("target_path");
var mr1 = targetAsm.MainModule.Import(typeof(Form1).GetMethod("Test"));
var targetType = targetAsm.MainModule.Types.FirstOrDefault(e => e.Name == "Target");
var m2 = targetType.Methods.FirstOrDefault(e => e.Name == "Test");
var m1 = mr1.Resolve();
var m1IL = m1.Body.GetILProcessor();
foreach(var i in m1.Body.Instructions.ToList()){
var ci = i;
if(i.Operand is MethodReference){
var mref = i.Operand as MethodReference;
ci = m1IL.Create(i.OpCode, targetType.Module.Import(mref));
}
else if(i.Operand is TypeReference){
var tref = i.Operand as TypeReference;
ci = m1IL.Create(i.OpCode, targetType.Module.Import(tref));
}
if(ci != i){
m1IL.Replace(i, ci);
}
}
//here the source Body should have its Instructions set imported fine
//so we just need to set its Body to the target's Body
m2.Body = m1.Body;
//finally write to another output assembly
targetAsm.Write("modified_target_path");
The code above was not referenced from anywhere, I just tried it myself and found out it works for simple cases (such as for the 2 methods Test I posted above). But if the source method (defined in the current app) contains some Type reference (such as some constructor init ...), like this:
public class Form1 {
public string Test(){
var u = new Uri("SomeUri");
return u.AbsolutePath;
}
}
Then it will fail at the time writing the assembly back. The exception thrown is ArgumentException with the following message:
"Member 'System.Uri' is declared in another module and needs to be imported"
In fact I've encountered a similar message before but it's for method calls like (string.Concat). And that's why I've tried importing the MethodReference (you can see the if inside the foreach loop in the code I posted). And really that worked for that case.
But this case is different, I don't know how to import the used/referenced types (in this case it is System.Uri) correctly. As I know the result of Import should be used, for MethodReference you can see that the result is used to replace the Operand for each Instruction. But for Type reference in this case I totally have no idea on how.
All my code posted in my question is fine BUT not enough. Actually the exception message:
"Member 'System.Uri' is declared in another module and needs to be imported"
complains about the VariableDefinition's VariableType. I just import the instructions but not the Variables (which are just referenced exactly from the source MethodBody). So the solution is we need to import the variables in the same way as well (and maybe import the ExceptionHandlers as well because an ExceptionHandler has CatchType which should be imported).
Here is just the similar code to import VariableDefinition:
var vars = m1.Body.Variables.ToList();
m1.Body.Variables.Clear();
foreach(var v in vars){
var nv = new VariableDefinition(v.Name, targetType.Module.Import(v.VariableType));
m1.Body.Variables.Add(nv);
}
In new version of TeamFoundation 2013 default build templates, the Workspace variable is missing. It is needed as intput parameter for few key activities like ConvertWorkspaceItem. How do I get current workspace for TfvcTemplate.12.xaml templates? I've tried to use this msdn thread but it's not working for me (returns null workspace name). Any suggestions?
There's a new activity in 2013 called GetLocalPath that replaces ConvertWorkspaceItem.
The activity is under the Microsoft.TeamFoundation.Build.Activities.Core namespace in the Microsoft.TeamFoundation.Build.Activities assembly.
It uses the LocalPathProvider class that aggregates all workspaces used in the build and exposes path translation for all of them in one place. This basically removes the dependency of knowing the workspace in order to translate server paths to local paths and allows you to use as many workspaces as you want without worrying about breaking something down the line.
When MS takes something away, it's usually for a good reason. "hacking" is really not necessary.
I went with a hack using internal classes from Microsoft.TeamFoundation.Build.Activities.dll (used by microsoft to create workspace name). You need to create custom activity with following code:
public sealed class GetDefaultWorkspace : BaseActivity<Workspace>
{
public override Activity CreateBody()
{
var type = typeof(TfGetSources).Assembly.GetType("Microsoft.TeamFoundation.Build.Activities.TeamFoundation.TfGetSources+GetDefaultWorkspaceName");
var activity = (CodeActivity<string>)Activator.CreateInstance(type);
var sequence = new Sequence();
var workspaceName = new Variable<string>();
sequence.Variables.Add(workspaceName);
sequence.Activities.Add(activity);
activity.Result = (OutArgument<string>) workspaceName;
sequence.Activities.Add(new GetWorkspace
{
Name = workspaceName,
Result = new LambdaReference<Workspace>(ctx => Result.Get(ctx))
});
return sequence;
}
}
This answer might work better for some people. ghord's answer works well, and passes the Workspace back where it can be used in the XAML. However, for my purposes I only want the workspace in my custom TFS activities, so I ended up with this alternative...
public sealed class CustomActivity : CodeActivity
{
protected override void Execute(CodeActivityContext context)
{
// get workspace
var buildDetail = context.GetExtension<IBuildDetail>();
var buildAgent = context.GetExtension<IBuildAgent>();
var buildDirectory = buildAgent.GetExpandedBuildDirectory(buildDetail.BuildDefinition);
var workspacePath = Path.Combine(buildDirectory, "src");
var wsInfo = Workstation.Current.GetLocalWorkspaceInfo(workspacePath);
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(wsInfo.ServerUri);
tfs.Connect(ConnectOptions.None);
var vcs = tfs.GetService<VersionControlServer>();
// finally can get to the workspace here
var workspace = vcs.GetWorkspace(workspacePath);
}
}
Using this method, I don't have to have an activity that just returns the workspace, and then have to pass the workspace into other TFS activities. I just get the workspace from within my own activity while it runs.
I believe the method employed here will use the already downloaded workspace. Keep in mind, that this approach will only work within the scope of "Run on agent" sequence after "Initialize Environment" and before ResetEnvironment within the finally statement of Try Compile, Test, Publish. Else, the workflow will have no knowledge of a sources directory.
http://social.msdn.microsoft.com/Forums/vstudio/en-US/420ba073-bdf5-4ab4-88da-c84561d1a1ba/creating-dynamic-working-folder-in-tfs2013-defaulttemplate?forum=tfsbuild
I have written below code to queue a build via api. team project collection, workitem store and version control server works fine, but i am not getting buildserver object. its always returning null. am i missing any other configuration
using (TfsTeamProjectCollection tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://tfsserver:8080/tfs/DefaultCollection")))
{
tpc.EnsureAuthenticated();
// i am getting workitemstore object here
var wiStore = tpc.GetService<WorkItemStore>();
....
....
// i am getting version control server object here as well
var vcs = tpc.GetService<VersionControlServer>();
....
....
// but here i get a null object
var bs = tpc.GetService<IBuildServer>();
//this is what i want to do with buildserver object
var buildDefinition = bs.GetBuildDefinition("aaa", "bbb");
var buildRequest = buildDefinition.CreateBuildRequest();
bs.QueueBuild(buildRequest);
}
any idea?
This can happen if you reference the wrong version of Microsoft.TeamFoundation.Build.Client for the TFS that you're trying to connect to. Check that you're referencing the correct version for TFS2010 (10.0.0.0 I believe).
http://pmichaels.net/2015/03/20/unable-to-get-ibuildserver-always-returning-null/
Try var bs = (IBuildServer)tpc.GetService(typeof(IBuildServer)); Sometimes the GetService<>() didn't work for me, maybe because IBuildServer is an interface, while VersionControlServer for example is a class.