5

我创建了一个项目模板,其中包含一个 csproj,其中包含一个指向项目文件的 Import,在该文件中我列出了所有第三方项目位置。我总是使用这个项目模板在同一个相对目录中创建项目。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="../../../3rdParty/ThirdParty.targets" />
  ...
  <ItemGroup>
    <Reference Include="Library, Version=$(LibraryVersion), Culture=neutral, PublicKeyToken=$(LibraryPublicKeyToken), processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>$(LibraryDir)LibraryDll.dll</HintPath>
    </Reference>
  </ItemGroup>
  ...
</Project>

csproj 文件在 Visual Studio 中以及从命令提示符运行 msbuild 时可以正常工作。当我尝试使用项目模板创建项目时,出现以下错误:

C:\Users...\AppData\Local\Temp\534cylld.o0p\Temp\MyModule.csproj(3,3):导入的项目“C:\Users...\AppData\Local\3rdParty\ThirdParty.targets “ 没找到。确认声明中的路径正确,并且该文件存在于磁盘上。

似乎 Visual Studio 正在尝试首先在临时位置打开项目。我尝试将 $(MSBuildProjectDirectory) 添加到导入位置,希望它可能会强制它使用我想要的位置,但这也没有用。

有什么建议么?

4

4 回答 4

9

您应该在 vstemplate 中将CreateInPlace属性设置为。文件true_

指定是在指定位置创建项目并进行参数替换,还是在临时位置进行参数替换,然后将项目保存到指定位置。

如果您希望相对路径起作用,您需要在创建项目的地方进行参数替换,而不是在临时位置

于 2012-01-05T12:45:32.130 回答
1

我选择了使用带有项目模板的向导的解决方案,主要是因为我的一些模板已经需要向导。

我创建了一个基类,我的所有其他向导都应该扩展它,或者可以单独使用它来实现基本功能:

public class AddTargetsWizard : IWizard
{
    private const string RELATIVE_PATH_TO_TARGETS = @"..\..\..\..\PATH\TO\Custom.Tasks.Targets";

    private const string TASK_NOT_FOUND_MESSAGE = @"A project of this type should be created under a specific path in order for the custom build task to be properly executed.

The build task could not be found at the following location:
    {0}

Including the build task would result in unexpected behavior in Visual Studio.

The project was created, but the build task WILL NOT BE INCLUDED.
This project's builds WILL NOT benefit from the custom build task.";

    private string _newProjectFileName;

    private bool _addTaskToProject;

    private Window _mainWindow;

    public AddTargetsWizard()
    {
        this._addTaskToProject = true;
    }

    protected Window MainWindow
    {
        get
        {
            return this._mainWindow;
        }
    }

    public virtual void BeforeOpeningFile(EnvDTE.ProjectItem projectItem)
    {
    }

    public virtual void ProjectFinishedGenerating(EnvDTE.Project project)
    {
        this._newProjectFileName = project.FullName;

        var projectDirectory = Path.GetDirectoryName(this._newProjectFileName);

        var taskPath = Path.GetFullPath(Path.Combine(projectDirectory, RELATIVE_PATH_TO_TARGETS));

        if (!File.Exists(taskPath))
        {
            MessageBox.Show(
                this.MainWindow,
                string.Format(TASK_NOT_FOUND_MESSAGE, taskPath),
                "Project Creation Error",
                MessageBoxButton.OK,
                MessageBoxImage.Error,
                MessageBoxResult.OK,
                MessageBoxOptions.None);

                this._addTaskToProject = false;
        }
    }

    public virtual void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem)
    {
    }

    public virtual void RunFinished()
    {
        if (this._addTaskToProject)
        {
            var project = new Microsoft.Build.Evaluation.Project(this._newProjectFileName);

            project.Xml.AddImport(RELATIVE_PATH_TO_TARGETS);

            project.Save();
        }
    }

    public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
    {
        var dte = (EnvDTE80.DTE2)automationObject;

        var mainWindow = dte.MainWindow;

        foreach (var proc in System.Diagnostics.Process.GetProcesses())
        {
            if (proc.MainWindowTitle.Equals(mainWindow.Caption))
            {
                var source = HwndSource.FromHwnd(proc.MainWindowHandle);
                this._mainWindow = source.RootVisual as System.Windows.Window;
                break;
            }
        }

        this.OnRunStarted(automationObject, replacementsDictionary, runKind, customParams);
    }

    protected virtual void OnRunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
    {
    }

    public virtual bool ShouldAddProjectItem(string filePath)
    {
        return true;
    }
}

本演练很好地解释了如何将向导与项目(或项)模板相关联。

您会注意到我正在为OnRunStarted需要提供附加功能的子向导提供虚拟方法,例如显示向导窗口、填充替换字典等。

我不喜欢这种方法和/或我的实现的事情:

  • 它比普通的项目模板要复杂得多。
  • 为了使我的向导窗口(所有 WPF)成为以 Visual Studio 作为其所有者的真正模式窗口,我发现没有比使用当前实例的标题来确定 HWND 和关联的Window.
  • 在预期的文件夹层次结构中创建项目时,一切都很好,但 Visual Studio 的行为很奇怪(即弹出无用的对话框)。这就是为什么我选择显示错误消息并避免插入Import如果当前项目的位置不适用于我的相对路径。

如果有人有其他想法,我仍然全神贯注。

于 2011-08-12T10:16:08.660 回答
0

默认行为是将模板解压缩到临时文件夹中。然后,在那里执行参数替换。不知何故,测试了相对路径,并且在临时位置,文件不存在。

您是否尝试在导入之前添加以下行?

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
于 2011-08-17T12:08:52.707 回答
0

我想我可能会改用环境变量......这对你的情况有用吗?如果您必须在开发人员之间共享项目模板,您可以在 powershell 脚本中做一些花哨的事情,它会自动设置环境变量,或者通过询问开发人员他的模板目录在哪里。

[Environment]::SetEnvironmentVariable("3rdPartyTargets", "%ProgramFiles%/3rdParty/ThirdParty.targets", "User")

然后在 csproj 宏中:

<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(3rdPartyTargets)" />
  ...

等一下。这适用于 C++,但您可能必须将 msbuild用于 ac#/vb.net proj。

于 2011-08-18T15:38:49.337 回答