使用 Fortran DLL 进行 NUnit 测试

发布于 2024-10-16 14:16:49 字数 537 浏览 1 评论 0原文

我有一个来自 Fortran 的 ServerCom DLL。我使用 tlbimp 从 ServerCom.dll 自动生成 MyFortran.dll,可以直接从 C# 引用。

在 C# 类库中,我引用了 MyFortran.dll。

我创建了一个使用 MyFortran.dll 的控制台应用程序并生成了正确的清单(以便拥有自由互通的 COM 环境)。

它在控制台应用程序中完美运行。

现在,我编写了一个简单的 NUnit 测试,并收到了 COM 异常。

System.Runtime.InteropServices.COMException :检索 COM 类工厂 具有 CLSID 的组件 {0FB0F699-4EF8-4732-B98E-C088825E3912} 由于以下错误而失败: 80040154 类未注册 (HRESULT 异常:0x80040154 (REGDB_E_CLASSNOTREG))。

我该如何解决这个问题?

谢谢, 阿德里安.

I have a ServerCom DLL that comes from Fortran. I generate automatically using tlbimp a MyFortran.dll from the ServerCom.dll that can be referenced directly from C#.

In a C# Class Library I have referenced MyFortran.dll.

I created a console application that use the MyFortran.dll and generated the correct manifest (in order to have a free-interopt COM environment).

It works perfectly in the console application.

Now, I wrote a simple NUnit test and I got a COM Exception.

System.Runtime.InteropServices.COMException
: Retrieving the COM class factory for
component with CLSID
{0FB0F699-4EF8-4732-B98E-C088825E3912}
failed due to the following error:
80040154 Class not registered
(Exception from HRESULT: 0x80040154
(REGDB_E_CLASSNOTREG)).

How can I solve this?

Thanks,
Adrien.

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

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

发布评论

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

评论(3

回忆躺在深渊里 2024-10-23 14:16:49

您可以使用激活上下文 API 来实现此目的。此博客文章 给出了所有详细信息。

这是一个总结。

将以下代码粘贴到您的项目中(感谢 Spike McLarty):

/// <remarks>
/// Code from http://www.atalasoft.com/blogs/spikemclarty/february-2012/dynamically-testing-an-activex-control-from-c-and
/// </remarks>
class ActivationContext
{
    static public void UsingManifestDo(string manifest, Action action)
    {
        UnsafeNativeMethods.ACTCTX context = new UnsafeNativeMethods.ACTCTX();
        context.cbSize = Marshal.SizeOf(typeof(UnsafeNativeMethods.ACTCTX));
        if (context.cbSize != 0x20)
        {
            throw new Exception("ACTCTX.cbSize is wrong");
        }
        context.lpSource = manifest;

        IntPtr hActCtx = UnsafeNativeMethods.CreateActCtx(ref context);
        if (hActCtx == (IntPtr)(-1))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        try // with valid hActCtx
        {
            IntPtr cookie = IntPtr.Zero;
            if (!UnsafeNativeMethods.ActivateActCtx(hActCtx, out cookie))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            try // with activated context
            {
                action();
            }
            finally
            {
                UnsafeNativeMethods.DeactivateActCtx(0, cookie);
            }
        }
        finally
        {
            UnsafeNativeMethods.ReleaseActCtx(hActCtx);
        }
    }

    [SuppressUnmanagedCodeSecurity]
    internal static class UnsafeNativeMethods
    {
        // Activation Context API Functions
        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "CreateActCtxW")]
        internal extern static IntPtr CreateActCtx(ref ACTCTX actctx);

        [DllImport("Kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool DeactivateActCtx(int dwFlags, IntPtr lpCookie);

        [DllImport("Kernel32.dll", SetLastError = true)]
        internal static extern void ReleaseActCtx(IntPtr hActCtx);

        // Activation context structure
        [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)]
        internal struct ACTCTX
        {
            public Int32 cbSize;
            public UInt32 dwFlags;
            public string lpSource;
            public UInt16 wProcessorArchitecture;
            public UInt16 wLangId;
            public string lpAssemblyDirectory;
            public string lpResourceName;
            public string lpApplicationName;
            public IntPtr hModule;
        }

    }
}

每次您需要创建一个 COM 对象(本例中为 COMObject),将创建它的调用包装在 lambda 函数中,并将其传递给UsingManifestDo,如下所示:

object CreateManifestDependantCOMObject() 
{
   object myCOMObject = null;
   ActivationContext.UsingManifestDo(pathToManifestFile, () => myCOMObject = new COMObject());
   return myCOMObject;
}

You can use Activation Context API's to achieve this. This blog post gives all the details.

Here's a summary.

Paste the following code into your project (thanks to Spike McLarty for this):

/// <remarks>
/// Code from http://www.atalasoft.com/blogs/spikemclarty/february-2012/dynamically-testing-an-activex-control-from-c-and
/// </remarks>
class ActivationContext
{
    static public void UsingManifestDo(string manifest, Action action)
    {
        UnsafeNativeMethods.ACTCTX context = new UnsafeNativeMethods.ACTCTX();
        context.cbSize = Marshal.SizeOf(typeof(UnsafeNativeMethods.ACTCTX));
        if (context.cbSize != 0x20)
        {
            throw new Exception("ACTCTX.cbSize is wrong");
        }
        context.lpSource = manifest;

        IntPtr hActCtx = UnsafeNativeMethods.CreateActCtx(ref context);
        if (hActCtx == (IntPtr)(-1))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        try // with valid hActCtx
        {
            IntPtr cookie = IntPtr.Zero;
            if (!UnsafeNativeMethods.ActivateActCtx(hActCtx, out cookie))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            try // with activated context
            {
                action();
            }
            finally
            {
                UnsafeNativeMethods.DeactivateActCtx(0, cookie);
            }
        }
        finally
        {
            UnsafeNativeMethods.ReleaseActCtx(hActCtx);
        }
    }

    [SuppressUnmanagedCodeSecurity]
    internal static class UnsafeNativeMethods
    {
        // Activation Context API Functions
        [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "CreateActCtxW")]
        internal extern static IntPtr CreateActCtx(ref ACTCTX actctx);

        [DllImport("Kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool DeactivateActCtx(int dwFlags, IntPtr lpCookie);

        [DllImport("Kernel32.dll", SetLastError = true)]
        internal static extern void ReleaseActCtx(IntPtr hActCtx);

        // Activation context structure
        [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)]
        internal struct ACTCTX
        {
            public Int32 cbSize;
            public UInt32 dwFlags;
            public string lpSource;
            public UInt16 wProcessorArchitecture;
            public UInt16 wLangId;
            public string lpAssemblyDirectory;
            public string lpResourceName;
            public string lpApplicationName;
            public IntPtr hModule;
        }

    }
}

Every time you need to create a COM object (COMObject in this example), wrap the call that creates it in a lambda function, and pass that to UsingManifestDo, like this:

object CreateManifestDependantCOMObject() 
{
   object myCOMObject = null;
   ActivationContext.UsingManifestDo(pathToManifestFile, () => myCOMObject = new COMObject());
   return myCOMObject;
}
趴在窗边数星星i 2024-10-23 14:16:49

是的,这行不通。免注册表 COM 清单需要嵌入到使用 COM 服务器的 EXE 中。对于您的控制台应用程序来说足够简单。当您使用 NUnit 时并不容易,因为 EXE 现在是单元测试运行程序。你不能/不应该打扰它。无论如何,很难做到,因为它们有很多。

不用担心,这是与您想要执行的测试无关的部署细节。只需在执行测试的计算机上使用 regsvr32.exe 注册服务器即可完成。

Yes, this doesn't work. The registry-free COM manifest needs to be embedded in the EXE that uses the COM server. Easy enough for your console app. Not easy when you use NUnit because the EXE is now the unit test runner. You can't/shouldn't mess with it. Hard to do anyway because there are a bunch of them.

Just don't bother, this is a deployment detail that's not relevant for the testing you want to do. Just register the server with regsvr32.exe on the machine that executes the tests and be done with it.

呆萌少年 2024-10-23 14:16:49

看看这个答案:
如何在插件中实现免注册 COM -在架构中

尤金表示这可以通过以下两种方式之一实现:
1. 将清单嵌入到 dll 中并使用 ISOLATION_AWARE_ENABLED 进行编译
2.使用上下文激活API

Check out this answer:
How to do registration-free COM in a plug-in architecture

Eugene indicates that this is possible in one of two ways:
1. embed the manifest in the dll and compile with ISOLATION_AWARE_ENABLED
2. use context activation APIs

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