问题
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?
回答1:
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#.
回答2:
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 literal
s 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.
回答3:
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...
- 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
}
- Include in C# project using Add Existing Item... [Add As Link]
- 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
- Reference in C# with:
MyAppVersion.Number
- Reference in C++ with:
MyAppVersion::Number
回答4:
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.
回答5:
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.
来源:https://stackoverflow.com/questions/3146017/how-do-i-share-a-constant-between-c-sharp-and-c-code