我正在编写两个进程,其中一个使用 C# 和 WCF,第二个使用 C++ 和 WWSAPI。我希望能够在一个地方定义用于两者之间通信的地址,并让 C# 和 C++ 都使用它。这可能吗?
我最接近的是在 IDL 中定义常量,然后使用 MIDL 和 TLBIMP 将其放入 C# 可以使用的 DLL 中。然而,这似乎并没有暴露常量,或者至少我不知道如何让它这样做。也许它仅限于类型定义。
还有其他建议吗?
我正在编写两个进程,其中一个使用 C# 和 WCF,第二个使用 C++ 和 WWSAPI。我希望能够在一个地方定义用于两者之间通信的地址,并让 C# 和 C++ 都使用它。这可能吗?
我最接近的是在 IDL 中定义常量,然后使用 MIDL 和 TLBIMP 将其放入 C# 可以使用的 DLL 中。然而,这似乎并没有暴露常量,或者至少我不知道如何让它这样做。也许它仅限于类型定义。
还有其他建议吗?
您可以创建一个单独的 C++/CLI 项目并在一个.h
文件中定义所有常量。例如,创建名为“ConstantBridge”的 C++/CLI 类库项目和名为“CSharpProgram”的 C# 项目:
namespace Constants
{
const int MAGIC_NUMBER = 42;
}
// String literals must be defined as macros
#define MAGIC_PHRASE "Hello World"
// Since stirngs must be macros it's arguably more consistent
// to use `define` throughout. This is for demonstration purpose.
#include "Constants.h"
namespace ConstantBridge { public ref class ConstantBridge {
public:
// The use of the `literal` keyword is important
// `static const` will not work
literal int kMagicNumber = Constants::MAGIC_NUMBER;
literal String ^ kMagicPhrase = MAGIC_PHRASE;
};}
Console.WriteLine(ConstantBridge.kMagicNumber); // "42"
Console.WriteLine(ConstantBridge.kMagicPhrase); // "Hello World"
现在,让“CSharpProgram”项目引用“ConstantBridge”项目。您的其他本机 C++ 项目可以简单地#include "Constants.h"
.
只要你从项目中只 引用 s ,就不会产生运行时依赖。您可以使用ILSpy或 ILdasm 进行验证。在 C# 和C++/CLI 中,在编译期间将“字面”复制到调用站点。literal
ConstantBridge
const
literal
C# 和 C++ 具有不同的常量模型。通常,该常量甚至不会在生成的 C++ 二进制文件中发出——它会在大多数情况下自动替换它。
不要使用常量,而是创建一个返回常量的函数,您可以从 C# 中 P/Invoke。
因此,
#include <iostream>
const double ACCELERATION_DUE_TO_GRAVITY = 9.8;
int main()
{
std::cout << "Acceleration due to gravity is: " <<
ACCELERATION_DUE_TO_GRAVITY;
}
变成
#include <iostream>
extern "C" double AccelerationDueToGravity()
{
return 9.8;
}
int main()
{
std::cout << "Acceleration due to gravity is: " <<
AccelerationDueToGravity();
}
您应该能够从 C# 中 P/Invoke。
对我的用例的其他解决方案不满意,因此编写了一个略显老套的解决方案,似乎更适合原始请求;一个文件中的常量,可以内置到C # 和 C++ 项目中...
像这样:
// Version.cs
public static class MyAppVersion
{
//build
public static string Number = "1.0";
public static string Phase = "Alpha";
//configuration (these are the build constants I use, substitute your own)
#if BUILD_SHIPPING
public static string Configuration = "Shipping";
#elif BUILD_DEVELOPMENT
public static string Configuration = "Development";
#elif BUILD_DEBUG
public static string Configuration = "Debug";
#else
"build type not defined"
#endif
}
#include
像这样:
//include version information into a .cpp
#define class namespace
#define public
#define static
#define string const char*
#include "..\..\Version.cs" //or to where-ever your file is
;
#undef class
#undef public
#undef static
#undef string
MyAppVersion.Number
MyAppVersion::Number
当我过去不得不做这些事情时,我只是在构建过程中添加了一个额外的预编译步骤,它会自动从另一个文件创建一个文件。
由于您的常量可能在 C# 中的一个类中,您可以将其用作源文件:
MyClass.cs:
class MyClass {
public const int NUM_MONTHS = 12; //COMMON
public const int YEAR_BASE = 1900; //COMMON
}
grep '//COMMON' MyClass.cs
| sed -e 's/^ *public const [a-z][a-z]*/#define/'
-e 's/ *= */ /'
-e 's/;.*$//'
>MyClass.h
grep '//COMMON' MyClass.cs | sed -e 's/ *public //' -e 's/;.*$/;/' >MyClass.hpp
这会给你:
MyClass.h:
#define NUM_MONTHS 12
#define YEAR_BASE 1900
MyClass.hpp:
const int NUM_MONTHS = 12;
const int YEAR_BASE = 1900;
现在,我不知道如何让 Visual Studio 执行该步骤。你必须调查它是否可能。UNIXy 文本处理工具确实值得下载。我在几个盒子上安装了CygWin,但是对于这个本地化的东西,你可以使用单独的 GnuWin32 包。
您可能可以在 PowerShell 中完成类似的工作,但我对此并不十分精通。
现在这有点混乱,所以我可以为您的特定问题提出一个可能更好的方法。根本不要使用常量。将地址放入配置文件,并让您的 C# 和 C++ 代码在启动时读取它。
这样,您就可以轻松地共享价值,并且可以配置它,以防您将来想要更改它。
每个常量的方法更简单的替代方法可能是包含常量作为实例属性的类。您可以在 C# 中创建它并通过 COM 接口将其公开给 C++。这比 P/Invoke 更容易且更不容易出错,因为您不必担心正确获取所有类型和名称 - 这一切都由编译器为您完成。
注意:我没有尝试过,我只是推测它应该可以工作。