使用Fortran DLL进行NUnit测试

时间:2011-02-08 16:24:04

标签: c# com nunit fortran

我有一个来自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))。

我该如何解决这个问题?

谢谢, 阿德里安。

3 个答案:

答案 0 :(得分:4)

您可以使用Activation Context API来实现此目的。这个blog post提供了所有细节。

以下是摘要。

将以下代码粘贴到您的项目中(感谢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;
}

答案 1 :(得分:3)

是的,这不起作用。无需注册表的COM清单需要嵌入使用COM服务器的EXE中。足够简单的控制台应用程序。使用NUnit时不容易,因为EXE现在是单元测试运行器。你不能/不应该搞乱它。很难做到,因为有很多。

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

答案 2 :(得分:0)

看看这个答案:
How to do registration-free COM in a plug-in architecture

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