Delphi 以无需注册的方式从可执行文件中使用COM对象

Delphi 以无需注册的方式从可执行文件中使用COM对象,delphi,com,delphi-xe5,Delphi,Com,Delphi Xe5,我正在努力修改使用COM对象实例进行通信的现有程序集 COM服务器实际上是主应用程序的(可选)扩展。两者都通过简单的拷贝部署到目标系统 目前,这两个应用程序仅在本地部署到我们的办公室(尽管主要应用程序COM客户端在我们客户的办公室部署得更广泛),我们手动注册服务器。我们需要重新设计,以便在云服务上部署,因此,我正在研究免注册COM 到目前为止,我已经尝试: 正在编写客户端和服务器的清单。不幸的是,我们无法部署此解决方案,因为客户端与服务器应用程序紧密链接,无法单独部署(我们的客户就是这样) 创

我正在努力修改使用COM对象实例进行通信的现有程序集

COM服务器实际上是主应用程序的(可选)扩展。两者都通过简单的拷贝部署到目标系统

目前,这两个应用程序仅在本地部署到我们的办公室(尽管主要应用程序COM客户端在我们客户的办公室部署得更广泛),我们手动注册服务器。我们需要重新设计,以便在云服务上部署,因此,我正在研究免注册COM

到目前为止,我已经尝试:

  • 正在编写客户端和服务器的清单。不幸的是,我们无法部署此解决方案,因为客户端与服务器应用程序紧密链接,无法单独部署(我们的客户就是这样)
  • 创建新的激活上下文并从资源中读取服务器的清单。这可以工作,但尝试实例化对象会导致Dll中出现“
    错误”
    “OLE异常。一些谷歌用户告诉我这是因为缺少导出
  • 从可执行文件导出
    DllGetClassObject
    (使用
    System.Win.ComServ
    中的内置实现,只需将export子句添加到项目源代码中即可。调用时(直接或通过激活上下文)会导致访问冲突.我还没有弄清楚AV发生在哪里
以下是我使用的清单(我在代码中以注释的形式留下了各种尝试):

服务器应用程序清单

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
name="wgaticket.exe"
type="Win32"
version="1.0.0.0"
/>

<file name = "wgaticket.exe">

<comClass
    clsid="{E33A1F59-CEA2-463E-97B2-1CCDA66DA984}"
    />
<!-- comClass
    clsid="{E33A1F59-CEA2-463E-97B2-1CCDA66DA984}"
    threadingModel = "Apartment"
    /-->

<typelib tlbid="{414AE7FB-3025-40D8-B14C-2A29B6E42C29}"
       version="1.0" helpdir=""/>

</file>

<!--comInterfaceExternalProxyStub
    name="INewTicket"
    iid="{740BF585-3246-483E-9146-B6A8E49400B5}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{414AE7FB-3025-40D8-B14C-2A29B6E42C29}" /-->

  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        publicKeyToken="6595b64144ccf1df"
        language="*"
        processorArchitecture="*"/>
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="asInvoker"
          uiAccess="false"/>
        </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>
class function CoNewTicket.CreateAsClient: INewTicket;
begin
  // GActContext is a global, lazy interface variable. It will be auto-created the first time GActContext.Value is referenced
  GActContext.Value.Activate;
  result := CreateComObject(CLASS_NewTicket) as INewTicket;
end;
客户端激活上下文激活

function getActivationContext: IActivationContext;
var
  actCtx: TActCtx;
begin
  result := TActivationContext.Create;
  zeroMemory(@actCtx, SizeOf(actCtx));
  actCtx.cbSize := SizeOf(actCtx);
  actCtx.lpSource := 'wgaticket.exe';

  actCtx.lpResourceName := MakeIntResource(1);
  actCtx.lpAssemblyDirectory := PChar(ModulePath);
  actCtx.dwFlags := ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID or ACTCTX_FLAG_RESOURCE_NAME_VALID;
  result.Handle := CreateActCtx(actCtx);
  if result.Handle = INVALID_HANDLE_VALUE then
  begin
    RaiseLastOSError;
  end;
  result.Cookie := 0;
end;
procedure TActivationContext.Activate;
begin
  CriticalSection.Enter;
  try
    if (FHandle <> INVALID_HANDLE_VALUE) and (Cookie = 0) then
    begin
      if not ActivateActCtx(FHandle, FCookie) then
        RaiseLastOSError;
    end;
  finally
    CriticalSection.Leave;
  end;

end;
您可以创建COM扩展的“虚拟”版本,该版本与实际扩展具有相同的公共接口,但没有实际功能。向该接口添加一个属性,您可以检查该属性是否为真实的属性。始终部署虚拟。在需要时,用真实扩展替换虚拟


这似乎使您的部署策略与在运行时如何建立激活上下文脱钩。例如,单个固定清单文件(或嵌入式资源,以两者为准)将始终有效。

两个基本问题。清单条目是帮助客户端启动服务器所必需的。因此,它们必须嵌入到客户端exe中,而不是服务器中。看起来您已经解决了这个问题,否则就不会遇到下一个问题,即显示阻止器。它仅适用于进程内服务器。DLL。这是这就是为什么您必须导出DllGetClassObject()的原因但是EXE不能像DLL一样加载。如果是进程外服务器,一切都要困难十倍,你必须使用注册表。谢谢你的评论。正如你所注意到的,我不能将注册放在客户端,因为如果服务器不在同一个文件夹中,它将不再运行。尽管如此,即使放弃创建activation竞赛并仅使用清单,问题仍然是:SxS尝试调用(不存在)dllgetClassobject导出在我的模块中。嗯,上次我尝试的时候,我不需要激活上下文。我写过它。不久前我尝试使用link让它工作,不幸的是没有用。如果真的有可能从未注册的进程外COM服务器创建实例,我也会对解决方案感兴趣。@Sti恐怕您误解了我的问题。使用进程内COM,这很容易(尽管根据线程模型,您所做的可能不安全)。进程外服务器不公开DllgetClassObject入口点,正如我在问题中所解释的。如果问题是我无法创建激活上下文,那么这将是一个解决方案。不幸的是,即使我使用清单,我也无法找到任何方法在不注册的情况下创建进程外COM对象的实例。在问题中u表示您尝试了清单,但无法使用它们,因为它“强链接”了客户端和服务器。我的建议是采用这种(相对简单/非编程)方法。如果在这种方法中清单不起作用,您应该调试清单。请注意接受的答案,例如。