提前抱歉所有代码......
我正在尝试创建一个名为" CliParser"的Visual Studio自定义工具,并且有一段艰难时期。在我的VS解决方案中,我有一个名为CliParser
的项目依赖于名为Danware.COM
的项目。 Danware.COM处理将COM类注册为VS自定义工具的样板,其中包含一个名为VsSingleFileGenerator
的抽象类和一个名为VsCustomToolAttribute
的属性。然后,CliParser定义了一个名为CliParser
的类,它继承VsSingleFileGenerator
并具有[VsCustomTool]
属性。这两个项目都已经签署,并且已经注册了COM Interop"已检查属性,并且正在定位"任何CPU"。构建成功完成,没有任何COM注册错误。
然而,我无法在任何COM客户端找到CliParser! Visual Studio和MS Excel都不会在“添加引用”菜单中显示它。即使我手动运行regasm.exe / tlb(32- 或 64位版本),它也表示所有内容都已成功导出/注册,但引用不会出现在这些COM客户端中。如何能够毫无错误地构建/注册所有内容,但实际的COM类型是不可用的?我可以想象的是,我正在将这个继承层次结构拆分为汇编。所有相关代码如下所示。如果有人能发现我错过的东西,我将非常感激
以下是VsSingleFileGenerator
:
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Microsoft.VisualStudio.Shell.Interop;
namespace Danware.COM {
[ComVisible(true)]
[Guid("A298E59F-2B16-4E2F-A5CC-61FE4B7CA676")]
public abstract class VsSingleFileGenerator : IVsSingleFileGenerator {
public abstract int DefaultExtension(out string pbstrDefaultExtension);
public abstract int Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, IntPtr[] rgbOutputFileContents, out uint pcbOutput, IVsGeneratorProgress pGenerateProgress);
[ComRegisterFunction]
private static void RegisterClass(Type t) {
var ga = t.GetCustomAttribute<GuidAttribute>(true);
var cta = t.GetCustomAttribute<VsCustomToolAttribute>(true);
string keyName = getKeyName(cta.LanguageGuid, cta.Name);
using (RegistryKey key = Registry.LocalMachine.CreateSubKey(keyName)) {
key.SetValue("", cta.Description);
key.SetValue("CLSID", $"{{{ga.Value}}}");
key.SetValue("GeneratesDesignTimeSource", 1);
}
}
[ComUnregisterFunction]
private static void UnregisterClass(Type t) {
var cta = t.GetCustomAttribute<VsCustomToolAttribute>(true);
string keyName = getKeyName(cta.LanguageGuid, cta.Name);
Registry.LocalMachine.DeleteSubKey(keyName, false);
}
private static string getKeyName(string languageGuid, string customToolName) {
return $"SOFTWARE\\Microsoft\\VisualStudio\\14.0\\Generators\\{{{languageGuid}}}\\{customToolName}\\";
}
}
}
以下是VsCustomToolAttribute
:
using System;
using System.Collections.Generic;
namespace Danware.COM {
public enum VsLanguage {
CSharp,
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class VsCustomToolAttribute : Attribute {
private IDictionary<VsLanguage, string> _langGuids = new Dictionary<VsLanguage,string>() {
{ VsLanguage.CSharp, "FAE04EC1-301F-11D3-BF4B-00C04F79EFBC" },
};
public VsCustomToolAttribute(string name) :
this(name, VsLanguage.CSharp, "") { }
public VsCustomToolAttribute(string name, string description) :
this(name, VsLanguage.CSharp, description)
{
Name = name;
Description = description;
}
public VsCustomToolAttribute(string name, VsLanguage language, string description) {
Name = name;
LanguageGuid = _langGuids[language];
Description = description;
}
public string Name { get; }
public string LanguageGuid { get; }
public string Description { get; }
}
}
这是CliParser
:
using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Danware.COM;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
namespace CliParser {
[ComVisible(true)]
[Guid("F4BA90DA-E459-4889-8E14-AF27FC7437DC")]
[VsCustomTool("CliParser", "Generates a strongly typed class to faciliate implementating a command line interface")]
public class CliParser : VsSingleFileGenerator {
public override int DefaultExtension(out string pbstrDefaultExtension) {
pbstrDefaultExtension = ".txt";
return pbstrDefaultExtension.Length;
}
public override int Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, IntPtr[] rgbOutputFileContents, out uint pcbOutput, IVsGeneratorProgress pGenerateProgress) {
try {
byte[] bytes = Encoding.UTF8.GetBytes("OMG did we actually make it??");
int length = bytes.Length;
rgbOutputFileContents[0] = Marshal.AllocCoTaskMem(length);
Marshal.Copy(bytes, 0, rgbOutputFileContents[0], length);
pcbOutput = (uint)length;
}
catch (Exception ex) {
MessageBox.Show(ex.ToString());
pcbOutput = 0;
}
return VSConstants.S_OK;
}
}
}
编辑:以下是VS构建过程创建的CliParser.tlb的内容:
// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: CliParser.tlb
[
uuid(6C2440B0-A869-4983-A8CA-4FF470C64F18),
version(1.0),
helpstring("Generates a strongly typed class to faciliate implementating a command line interface"),
custom(90883F05-3D28-11D2-8F17-00A0C9A6186D, "CliParser, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5e32040c76b2825f")
]
library CliParser
{
// TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
importlib("mscorlib.tlb");
// TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
importlib("stdole2.tlb");
// Forward declare all types defined in this typelib
interface _CliParser;
[
uuid(F4BA90DA-E459-4889-8E14-AF27FC7437DC),
version(1.0),
custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "CliParser.CliParser")
]
coclass CliParser {
[default] interface _CliParser;
interface _Object;
};
[
odl,
uuid(0AEBD8C3-E835-3921-8830-CCB69042366B),
hidden,
dual,
oleautomation,
custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "CliParser.CliParser")
]
interface _CliParser : IDispatch {
};
};