消息值,托管字符串,扰乱代码及其它
2006-07-20 11:39:33 来源:WEB开发网Figure 6 十六进制程序显示的信息
我遇到了一个在即将出品的 C# 应用软件中保护我们的知识产权的问题。我知道所有的代码都可以被 ILDASM 反汇编,这将使得别人可以很 轻松地获得我们的数学公式。为了解决这个问题,我可以写一个 C++ DLL并使用 DllImport 在 C# 中导入函数,我更喜欢使用托管 C++ 写一个托管 __gc 类,这样我便能同时暴露属性和方法。当我 这样做并编译 DLL 时,我仍然可以使用 ILDASM 对之进行反汇编,这样并没有实现我试图做的事情。我认为 C++ 编译器会生成原生机器代码,而不是 MSIL。是 不是这样呢?
Matt Hess
当你用/clr编译 C++ 程序时,编译器将所有函数视作托管的,并默认将它们编译成 MSIL(少数情况除外,这我会在稍后讨论)。结构和类可以是托管的,也可以是非托管的,这取决于你的是否使用 __gc。如果你想将一个特殊函数编译成为原生代码(可能为了隐藏它的实现,或出于性能的原因),你可以使用 #pragma unmanaged:#pragma unmanaged
// this fn and all subsequent ones are compiled
// to native code
void func(/* args */)
{
...
}
#pragma managed
#pragma managed 指示开关返回到托管模式。Figure 7 展示了我写的一个简单的 C++程序,它有两个函数,PrintFive1 和 PrintFive2,每个 函数打印字符串 5 次。第一个函数是原生的,第二个是托管的。如果你用/FAs编译并查看所生成的 .asm 文件,你将看到在 PrintFive1 中的 for 循环编译成如下的东东:
; for (int i=0; i<5; i++) {
而 PrintFive2 中相同的 for 循环编译却是下面这样的:
mov DWORD PTR _i$1662[ebp], 0
jmp SHORT $L1663
...
; etc... (more native assembly); for (int i=0; i<5; i++) {
ldc.i.0 0 ; i32 0x0
stloc.0 ; _i$1669
...
; etc... (more MSIL)
我说过当你使用/clr时,所有函数默认是托管的,但是这并不完全正确。虽然 MSIL 通常足以用来表示大多数 C/C++ 构造,但它不能表示全部 的东西。少数情况下,它强制某个函数被编译成原生代码,即便没有 #pragma unmanaged 也一样。这种情况很容易看出来:比如,如果你的函数有__asm 块,setjmp/longjmp 指令,或一个可变参数列表(varargs)。 因为 MSIL 无法处理这些结构,编译器便自动将这些函数编译为原生代码。
你可以只用 #pragma unmanaged 来编译纯 C++ 函数——也就是说,你不使用托管类型。你不能用 #pragma unmanaged 来编译托管 __gc 类的方法(那样做没有意义)。因此,如果你想用 C++ 来隐藏你的算法,你必须用原生的 C++ 类型将他们实现成纯粹的 C++ 函数。你的 __gc 类方法可以调用这些函数,在调用你的实现之前,你必须将任何托管参数转换成非托管类型:
__gc class Widget {
public:
MyMethod(String *s)
{
// convert managed types to C++ types
const WCHAR __pin* pstr = PtrToStringChars(s);
// call unmanaged C++ function (in #pragma unmanaged block)
DoSecretAlgorithm(pstr);
}
};
托管 C++ 是微软唯一允许你在相文件中组合使用原生代码和 MSIL 代码的语言,这就是它如此强大的原因之一!另一种使用托管 C++ 混合代码的方法是通过链接。比如,无论何时你调用如 printf 或 memset 这样的 C 库函数,它们从相应的库中获得静态链接。当你使用/clr,它自动 暗示使用/MT来确保你的程序与 C 运行时库的多线程版本进行链接。CLR 需要它们,因为垃圾收集器在一个独立的线程里运行终结例程(finalizers)。
更多精彩
赞助商链接