【问题标题】:Can Delphi 5 use a C# .Net Library?Delphi 5 可以使用 C# .Net 库吗?
【发布时间】:2021-04-16 16:45:19
【问题描述】:

我正在更新旧版 Delphi 5 应用程序(我们有代码),该应用程序通过使用用户提供的凭据绑定来针对 LDAP/AD 进行身份验证。但是,我们使用的库无法通过端口 636 或 3269 绑定 LDAPS (LDAP/SSL)。

我发现在 C# 中针对 LDAP/AD 进行身份验证可以很简单:

PrincipalContext pc = new PrincipalContext(ContextType.Domain, "testnet.testad.org:636", "dc=testnet,dc=testad,dc=org");
bool validated = pc.ValidateCredentials(username, password, ContextOptions.Negotiate);

我正在考虑将必要的调用包装在 .DLL 中,然后从 Delphi 应用程序中调用该调用。但是,我假设有一个“陷阱”的雷区会阻止它变得如此简单。

【问题讨论】:

  • 这可以工作,但前提是 C# DLL 公开了平面 C 接口或 COM 服务器。
  • @RemyLebeau,每当我看到你的回答时,我就知道我走在一条好路上(或者你会让我走上一条。)我将开始寻找如何创建 C# COM Visual Studio 2019 中的服务器。
  • 恕我直言,最简单的方法是使用UnmanagedExports 在 C# 中创建一个库,该库导出一个返回接口的函数。就像一个魅力,只需要最少的胶水代码。
  • @mghie,尝试使用 UnmanagedExports,但遇到无法直观解决的错误。我会把它放在我的口袋里,但会先尝试看看我是否可以按照 Remy 的想法创建一个 dll。我认为这将使我能够更好地了解幕后发生的事情,尤其是因为这应该既简单又棘手。

标签: c# delphi ssl dll ldap


【解决方案1】:

使用UnmanagedExports 这真的很简单。

您从功能接口开始,如下所示:

type
  IManagedInterface = interface
    ['{9F5A2431-5559-410C-BAB4-5144CA8C0B7B}']
    function CheckCredentials(AContextName, AContainerName, AUserName,
      APassword: WideString): integer; safecall;
  end;

这可能不是参数类型的最佳选择,因为您不能使用NULL 作为用户名和密码来调用它,而且您可能还想传递其他参数(如上下文类型),但是您明白了。

然后您在 VS 2019 中创建一个新的类库 (.NET Framework) 项目,将构建目标设置为 x86(使其与 Delphi 5 兼容)。为 UnmanagedExports 添加 NuGet 包。

在该项目中添加相同的界面:

[ComVisible(true)]
[Guid("9F5A2431-5559-410C-BAB4-5144CA8C0B7B")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IManagedInterface
{
    int CheckCredentials(
        [MarshalAs(UnmanagedType.BStr)] string contextname,
        [MarshalAs(UnmanagedType.BStr)] string container,
        [MarshalAs(UnmanagedType.BStr)] string username,
        [MarshalAs(UnmanagedType.BStr)] string password);
}

以及实现该接口的类:

public class Class1 : IManagedInterface
{
    public int CheckCredentials(string contextname, string container,
        string username, string password)
    {
        var pc = new PrincipalContext(ContextType.Domain, contextname, container);
        return Convert.ToInt32(pc.ValidateCredentials(username, password,
            ContextOptions.Negotiate));
    }
}

最后,您从 DLL 中导出一个函数,该函数创建并返回该类的一个实例:

static class Exports
{
    [DllExport]
    public static void CreateTheInterface(
        [MarshalAs(UnmanagedType.Interface)] out IManagedInterface instance)
    {
        instance = new Class1();
    }
}

现在您可以使用 Delphi 的 DLL(为了保持简短,库不会按需加载,我会在生产代码中这样做):

procedure CreateTheInterface(out AInstance: IManagedInterface); stdcall;
  external 'ClassLibrary1.dll';

var
  Imi: IManagedInterface;
  context, container, username, password: string;
begin
  CreateTheInterface(Imi);

  if Imi <> nil then try
    context := 'testnet.testad.org:636';
    container := 'dc=testnet,dc=testad,dc=org';
    username := 'someguy';
    password := 'password';

    Writeln('call returned: ',
      Imi.CheckCredentials(context, container, username, password));
  except
    on E: Exception do
      Writeln(E.Message);
  end;
  Readln;
end.

所以,没有“陷阱”的雷区,这真的很容易。

【讨论】:

  • 知道如果我在构建时遇到错误可能会出现什么问题:“找不到提供的密钥文件 'G:\C#\LDAPAuth\LDAPAuth\Mono.Cecil.CustomAttributeArgument'”?
  • @DanielBragg:也许github.com/3F/DllExport/issues/48 有帮助吗?上面的代码不需要任何 COM 的东西。
  • 看起来签署程序集是必需的。
  • @DanielBragg:也许是您的情况,但是我的回答中概述的步骤(从头开始使用新的库项目)对我有用。之后添加签名也对我有用。但我假设你可能已经修改了一个现有的库,因此我对 COM 的东西发表了评论。
  • 我一直在使用 Properties -> Build -> Register for COM interop,但是当我删除它时事情变得更简单了。代码现在可以编译,它几乎可以工作了。我应该为 PrincipalContext 和 ValidateCredentials 使用哪些设置来验证端口 636 或 3269?
猜你喜欢
  • 2015-03-31
  • 1970-01-01
  • 2022-07-20
  • 1970-01-01
  • 2012-04-18
  • 1970-01-01
  • 1970-01-01
  • 2011-06-04
  • 1970-01-01
相关资源
最近更新 更多