我有一个来自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))。
我该如何解决这个问题?
谢谢, 阿德里安。
答案 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