如何在 C# 和 C++ 之间共享常量代码?

发布于 2024-09-07 13:25:09 字数 225 浏览 8 评论 0原文

我正在编写两个进程,第一个进程使用 C# 和 WCF,第二个进程使用 C++ 和 WWSAPI。我希望能够在一个地方定义用于两者之间通信的地址,并让 C# 和 C++ 都使用它。这可能吗?

我最接近的是在 IDL 中定义常量,然后使用 MIDL 和 TLBIMP 将其放入可由 C# 使用的 DLL 中。然而,这似乎并没有暴露常量,或者至少我不知道如何让它这样做。也许它仅限于类型定义。

还有其他建议吗?

I'm writing two processes using C# and WCF for one and C++ and WWSAPI for the second. I want to be able to define the address being used for communication between the two in a single place and have both C# and C++ use it. Is this possible?

The closest I've come is defining the constant in an IDL, then using MIDL and TLBIMP to get it into a DLL that can be consumed by C#. However this doesn't seem to expose the constant, or at least I can't figure out how to make it do so. Maybe it is limited to type definitions only.

Any other suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

故事灯 2024-09-14 13:25:09

您可以创建一个单独的 C++/CLI 项目并在 .h 文件中定义所有常量。例如,创建名为“ConstantBridge”的 C++/CLI 类库项目和名为“CSharpProgram”的 C# 项目:

Constants.h

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.

ConstantBridge.h

#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;
};}

CSharpProgram.cs

Console.WriteLine(ConstantBridge.kMagicNumber); // "42"
Console.WriteLine(ConstantBridge.kMagicPhrase); // "Hello World"

现在,让“CSharpProgram”项目引用“ConstantBridge”项目。您的其他本机 C++ 项目只需 #include "Constants.h" 即可。

只要您仅引用 ConstantBridge 项目中的literal,就不会生成运行时依赖项。您可以使用 ILSpy 或 ILdasm 进行验证。 C# 中的 const 和 C++/CLI 中的 literal 在编译期间按字面意思复制到调用站点

You can create a separate C++/CLI project and define all your constants in a .h file. For example, create C++/CLI Class Library project called "ConstantBridge" and a C# project called "CSharpProgram":

Constants.h

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.

ConstantBridge.h

#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;
};}

CSharpProgram.cs

Console.WriteLine(ConstantBridge.kMagicNumber); // "42"
Console.WriteLine(ConstantBridge.kMagicPhrase); // "Hello World"

Now, have the "CSharpProgram" project reference the "ConstantBridge" project. Your other native C++ projects can simply #include "Constants.h".

As long as you reference only literals from the ConstantBridge project, a runtime dependency will not be generated. You can verify using ILSpy or ILdasm. const in C# and literal in C++/CLI are copied "literally" to the call site during compilation.

混吃等死 2024-09-14 13:25:09

对我的用例的其他解决方案不满意,因此编写了一个稍微有点hacky的解决方案,似乎更适合原始请求; 一个文件中的常量,可以同时构建到 C# 和 C++ 项目中...

  1. 版本信息位于 .cs 文件中的公共位置。

像这样:

// 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
}
  1. 使用“添加现有项目”包含在 C# 项目中... [添加为链接]
  2. 使用 #include 包含在 C++ 项目中(在 .cpp 文件中)

像这样:

//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
  1. 在 C# 中引用:< code>MyAppVersion.Number
  2. C++ 中的参考:MyAppVersion::Number

Wasn't happy with the other solutions for my use case so coded up a slightly hacky solution that seems to fit the original request better; a constant in one file that can be built into both a C# and a C++ project...

  1. Version information in a .cs file, in a common location.

Like this:

// 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
}
  1. Include in C# project using Add Existing Item... [Add As Link]
  2. Include in C++ project (in a .cpp file) with a #include

Like this:

//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
  1. Reference in C# with: MyAppVersion.Number
  2. Reference in C++ with: MyAppVersion::Number
逐鹿 2024-09-14 13:25:09

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# and C++ have differing models for constants. Typically, the constant won't even be emitted in the resulting C++ binary -- it's automatically replaced where it is needed most of the time.

Rather than using the constant, make a function which returns the constant, which you can P/Invoke from C#.

Thus,

#include <iostream>
const double ACCELERATION_DUE_TO_GRAVITY = 9.8;
int main()
{
     std::cout << "Acceleration due to gravity is: " << 
         ACCELERATION_DUE_TO_GRAVITY;
}

becomes

#include <iostream>
extern "C" double AccelerationDueToGravity()
{
    return 9.8;
}
int main()
{
     std::cout << "Acceleration due to gravity is: " << 
         AccelerationDueToGravity();
}

which you should be able to P/Invoke from C#.

流星番茄 2024-09-14 13:25:09

当我过去不得不做这些事情时,我只是在构建过程中添加了一个额外的预编译步骤,该步骤会自动从另一个文件创建一个文件。

由于您的常量可能位于 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++ 代码在启动时读取它。

这样,您就可以轻松地共享该值,并且它是可配置的,以防您将来想要更改它。

When I've had to do that stuff in the past, I've simply added an extra pre-compilation step to the build process which automagically creates one file from another.

Since your constants will probably be within a class in C#, you can use that as the source file:

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

This will give you:

MyClass.h:
    #define NUM_MONTHS 12
    #define YEAR_BASE 1900

MyClass.hpp:
    const int NUM_MONTHS = 12;
    const int YEAR_BASE = 1900;

Now, getting Visual Studio to perform that step is not something I know how to do. You'll have to investigate whether or not it's even possible. The UNIXy text processing tools are really worth downloading. I have CygWin installed on a few boxes but, for something this localised, you could get away with individual GnuWin32 packages.

You could probably do a similar job in PowerShell but I'm not really well versed in that.


Now that's a bit of a kludge so may I suggest a possibly better way for you particular question. Don't use a constant at all. Put the address into a configuration file and have your C# and C++ code read it at startup.

That way, you get to share the value painlessly and it's configurable in case you ever want to change it in future.

爱已欠费 2024-09-14 13:25:09

每个常量的方法的更简单替代方法可能是包含常量作为实例属性的类。您可以用 C# 创建它并通过 COM 接口将其公开给 C++。这比 P/Invoke 更容易且不易出错,因为您不必担心所有类型和名称是否正确 - 这一切都由编译器为您完成。

注意:我还没有尝试过这个,我只是推测它应该有效。

An easier alternative to a method for each constant may be a class containing the constants as instance properties. You could create it in C# and expose it via COM interfaces to C++. That's easier and less error-prone than P/Invoke, since you don't have to worry about getting all the types and names right - it's all done for you by the compiler.

Note: I have not tried this, I'm only speculating that it should work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文