在C#代码中重用.h文件中的define语句

我有C ++项目(VS2005),其中包含#define指令中版本号的头文件。 现在我需要在双C#项目中包含完全相同的数字。 最好的方法是什么?

我正在考虑将此文件包含为资源,然后在运行时使用正则表达式解析它以恢复版本号,但也许有更好的方法,您如何看待?

我不能在.h文件之外移动版本,也建立系统依赖于它而C#项目是应该适应的。

只需几个步骤即可实现您的目标:

  1. 创建MSBuild任务 – http://msdn.microsoft.com/en-us/library/t9883dzc.aspx
  2. 更新项目文件以包含对构建之前创建的任务的调用

该任务接收一个参数,其中包含您引用的标题.h文件的位置。 然后,它会提取版本并将该版本放入您之前创建的C#占位符文件中。 或者您可以考虑使用通常包含版本的AssemblyInfo.cs,如果可以的话。

如果您需要额外信息,请随时发表评论。

我会考虑使用.tt文件来处理.h并将其转换为.cs文件。 它非常简单,源文件将成为您的C#解决方案的一部分(意味着它们将在.h文件更改时刷新),可以单击以在编辑器中打开等。

如果你只有1 #define它可能有点矫枉过正,但是如果你有一个完整的文件(例如mfc resource.h文件),那么这个解决方案将成为一个巨大的胜利。

例如:创建一个文件,DefineConverter.tt并将其添加到您的项目中,更改标记的行以引用您的.h文件,您将在项目中获得一个充满静态const条目的新类。 (注意输入文件是相对于项目文件的,如果你想要绝对路径,则设置hostspecific = false)。

<#@ template language="C#v3.5" hostspecific="True" debug="True" #> <#@ output extension="cs" #> <#@ assembly name="System.Core.dll" #> <#@ import namespace="System" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.IO" #> <# string input_file = this.Host.ResolvePath("resource.h"); <---- change this StreamReader defines = new StreamReader(input_file); #> //------------------------------------------------------------------------------ // This code was generated by template for T4 // Generated at <#=DateTime.Now#> //------------------------------------------------------------------------------ namespace Constants { public class <#=System.IO.Path.GetFileNameWithoutExtension(input_file)#> { <# // constants definitions while (defines.Peek() >= 0) { string def = defines.ReadLine(); string[] parts; if (def.Length > 3 && def.StartsWith("#define")) { parts = def.Split(null as char[], StringSplitOptions.RemoveEmptyEntries); try { Int32 numval = Convert.ToInt32(parts[2]); #> public static const int <#=parts[1]#> = <#=parts[2]#>; <# } catch (FormatException e) { #> public static const string <#=parts[1]#> = "<#=parts[2]#>"; <# } } } #> } } 

MSDN告诉我们:

#define指令不能用于声明常量值,这通常在C和C ++中完成。 C#中的常量最好定义为类或结构的静态成员。 如果你有几个这样的常量,可以考虑创建一个单独的“常量”类来保存它们。

您可以使用托管C ++创建库,其中包括围绕常量的类 – 包装器。 然后你可以从C#项目中引用这个类。 只是不要忘记使用readonly 而不是const 作为常量声明:)

您始终可以使用预构建事件在.cs文件和后构建事件上运行C预处理器以撤消预构建步骤。 预处理器只是一个文本替换系统,所以这是可能的:

 // version header file #define Version "1.01" // C# code #include "version.h" // somewhere in a class string version = Version; 

并且预处理器将生成:

 // C# code // somewhere in a class string version = "1.01"; 

您可以编写包含此.h文件的简单C ++ / C实用程序,并动态创建可在C#中使用的文件。
该实用程序可以作为预构建阶段作为C#项目的一部分运行。
这样,您始终可以与原始文件同步。

我编写了一个python脚本,将#define FOO“bar”转换为C#中可用的东西,我在C#项目的预构建步骤中使用它。 有用。

 # translate the #defines in messages.h file into consts in MessagesDotH.cs import re import os import stat def convert_h_to_cs(fin, fout): for line in fin: m = re.match(r"^#define (.*) \"(.*)\"", line) if m != None: if m.group() != None: fout.write( "public const string " \ + m.group(1) \ + " = \"" \ + m.group(2) \ + "\";\n" ) if re.match(r"^//", line) != None: fout.write(line) fin = open ('..\common_cpp\messages.h') fout = open ('..\user_setup\MessagesDotH.cs.tmp','w') fout.write( 'using System;\n' ) fout.write( 'namespace xrisk { class MessagesDotH {\n' ) convert_h_to_cs(fin, fout) fout.write( '}}' ) fout.close() s1 = open('..\user_setup\MessagesDotH.cs.tmp').read() s2 = open('..\user_setup\MessagesDotH.cs').read() if s1 != s2: os.chmod('..\user_setup\MessagesDotH.cs', stat.S_IWRITE) print 'deleting old MessagesDotH.cs' os.remove('..\user_setup\MessagesDotH.cs') print 'remaming tmp to MessagesDotH.cs' os.rename('..\user_setup\MessagesDotH.cs.tmp','..\user_setup\MessagesDotH.cs') else: print 'no differences. using same MessagesDotH.cs' 

在gbjbaanb的解决方案的基础上,我创建了一个.tt文件,该文件找到特定目录中的所有.h文件,并将它们转换为具有多个类的.cs文件。

差异

  • 我增加了对双打的支持
  • 从try-catch切换到TryParse
  • 读取多个.h文件
  • 使用’readonly’而不是’const’
  • 修剪以此结尾的#define行;
  • 命名空间基于项目中的.tt位置设置

 <#@ template language="C#" hostspecific="True" debug="True" #> <#@ output extension="cs" #> <#@ assembly name="System.Core.dll" #> <#@ import namespace="System" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.IO" #> <# string hPath = Host.ResolveAssemblyReference("$(ProjectDir)") + "ProgramData\\DeltaTau\\"; string[] hFiles = System.IO.Directory.GetFiles(hPath, "*.h", System.IO.SearchOption.AllDirectories); var namespaceName = System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint"); #> //------------------------------------------------------------------------------ // This code was generated by template for T4 // Generated at <#=DateTime.Now#> //------------------------------------------------------------------------------ namespace <#=namespaceName#> { <#foreach (string input_file in hFiles) { StreamReader defines = new StreamReader(input_file); #> public class <#=System.IO.Path.GetFileNameWithoutExtension(input_file)#> { <# // constants definitions while (defines.Peek() >= 0) { string def = defines.ReadLine(); string[] parts; if (def.Length > 3 && def.StartsWith("#define")) { def = def.TrimEnd(';'); parts = def.Split(null as char[], StringSplitOptions.RemoveEmptyEntries); Int32 intVal; double dblVal; if (Int32.TryParse(parts[2], out intVal)) { #> public static readonly int <#=parts[1]#> = <#=parts[2]#>; <# } else if (Double.TryParse(parts[2], out dblVal)) { #> public static readonly double <#=parts[1]#> = <#=parts[2]#>; <# } else { #> public static readonly string <#=parts[1]#> = "<#=parts[2]#>"; <# } } } #> } <#}#> }