65.9K
CodeProject 正在变化。 阅读更多。
Home

Java 程序中的 C# 方法调用

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.57/5 (25投票s)

2006 年 2 月 16 日

CPOL

4分钟阅读

viewsIcon

299465

downloadIcon

7770

通过 JNI 在 Java 中使用 C# 变得更容易理解了。

Sample Image - javacsharp.jpg

引言

您是否曾经(由于某种原因)需要在 Java 程序中使用 C#?是否曾经想过如何在 Java 中调用 C#?好吧,我将尝试解释这个过程需要什么。

背景

我正在处理一些键盘钩取问题,并且发现使用 C# 开发它非常容易。问题在于我最初试图为基于 Java 的程序开发解决方案。经过大量 Internet 搜索后,我终于偶然发现了一篇非常好的文章,开始将这些部分拼凑在一起。由 Judith Bishop、R. Nigel Horspool 和 Basil Worrall 撰写的文章“整合 Java 与 C# 和 .NET 的经验”是我启动这个项目所需的见解。

该文章确实展示了一种在 Java 程序中使用 C# 的方法;但是,我发现很难理解整个过程。在我实际让 C# 在 Java 程序中运行之后,我意识到这个过程可以成为 CodeProject 的一篇文章(我的第一篇文章)。

Using the Code

这张图取自 Judith Bishop、R. Nigel Horspool 和 Basil Worrall 撰写的文章“整合 Java 与 C# 和 .NET 的经验”。

我想使用此代码的最佳方法是进行实验,并将其用作项目的模板。显然,我没有做任何非常复杂的事情,只是提出了一个“Hello World”示例,但我希望人们首先看到最简单的方法。示例代码只是一个基于控制台的 Java 程序,它将显示“Hello World,来自 C#”消息。我还包括所有四种语言的源代码和项目。

您可能会问,为什么需要这么多包装器。好吧,需要几层包装,因为通过将 C# 程序编译成库生成的 DLL 与 Java 的 JNI 不兼容。上面需要两层 C++,因为 JNI 子系统与之交互的 DLL 必须是静态的、程序代码(不是面向对象的)。因此,第一层本质上是 C 代码。幸运的是,静态 C 代码将容纳(指向)C++ 对象的静态指针。静态 C 代码是否可以直接与 C# 对象交互,这值得怀疑,因为需要考虑垃圾回收等问题。

Java 部分

当时我使用 Netbeans 4.0 作为我的主要 Java IDE,因此使用原生代码的过程有点复杂。原因在于 Netbeans 项目希望将代码放在包中,而不是仅使用默认的命名空间。然而,javah 控制台程序并不查看包,因此无法在正确的命名空间中正确创建头文件。因此,正如我发现的那样,有一个简单的两秒钟修复,只需编辑生成的头文件,即可通过 JNI 正确连接到 C++ 中的原生方法。

这是唯一的 Java 代码,它位于 helloworld 包中

public class HelloWorld {
    public native void displayHelloWorld();
    static {
        System.loadLibrary("HelloWorld");
    }
    
    public static void main (String[] args) {
        new HelloWorld().displayHelloWorld();
    }
}

然后,要将其变成 C++ 库所需的头文件,需要运行此命令

javah -jni helloworld.java

该步骤生成以下头文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorlds
#define _Included_HelloWorld
#ifdef _cplusplus
extern "C" {
#endif
/*
 * Class:        HelloWorld
 * Method:        displayHelloWorld
 * Signature:    ()V
 */
JNIEXPORT void JNICALL 
  Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

但是,如果我们不更改生成的头文件,那么 JNI 调用将失败,因为由于 NetBeans 和包,该方法具有不同的签名。因此,JNIEXPORT 行更改为

JNIEXPORT void JNICALL Java_helloworld_HelloWorld_displayHelloWorld (JNIEnv *, jobject);

C++ 库

C++ 库的目的是成为调用(通过托管 C++ 包装器)C# 端点代码的 JNI 包装器。

#include <jni.h>

// This is the java header created using the javah -jni command.
#include "Java\HelloWorld.h"

// This is the Managed C++ header that contains the call to the C#
#include "MCPP\HelloWorld.h"

// This is the JNI call to the Managed C++ Class
// NOTE: When the java header was created, the package name was not included
//       in the JNI call. This naming convention was corrected by adding the 
//         "helloworld" name following the following syntax: 
//       Java_<package name>_<class name>_<method name>
JNIEXPORT void JNICALL 
  Java_helloworld_HelloWorld_displayHelloWorld(JNIEnv *jn, jobject jobj) {

    // Instantiate the MC++ class.
    HelloWorldC* t = new HelloWorldC();

    // The actual call is made. 
    t->callCSharpHelloWorld();
}

托管 C++ 库

#using <mscorlib.dll>
#using "CSharpHelloWorld.netmodule"

using namespace System;

public __gc class HelloWorldC
{
    public:
        // Provide .NET interop and garbage collecting to the pointer.
        CSharpHelloWorld __gc *t;
        HelloWorldC() {
            t = new CSharpHelloWorld();
            // Assign the reference a new instance of the object
        }
        
     // This inline function is called from the C++ Code
        void callCSharpHelloWorld() {
            t->displayHelloWorld();
        }
};

C# 库

Managed C++ 和 C# 交互所需的步骤是,将 CSharpHelloWorld 库编译为 .netmodule。为此,必须从控制台或作为 C# 项目中的生成后事件运行以下命令(我通过使用生成后进行了简化)

csc /debug /t:module "$(OutDir)\$(ProjectName).dll"

此项目中几乎没有任何代码,因为我只想展示最简单的示例。而我的朋友们,还有什么比“HelloWorld”更简单的呢?

using System;

public class CSharpHelloWorld
{
    public CSharpHelloWorld() {}

    /// <summary>
    /// displayHelloWorld will be the method called from within java.
    /// </summary>
    public void displayHelloWorld() 
    {
        Console.WriteLine("Hello World, from C#!");
    }
}

关注点

使用 Java JNI 确实很痛苦,尤其是在使用 Netbeans 作为 IDE 的情况下。希望我已经为您提供了一些关于此过程的不同步骤的期望的见解。我一定在这个项目中遇到了每一个障碍,并花时间查找了所需的内容。

历史

  • 版本 1.0 - 首次发布!
© . All rights reserved.