Esempio pratico di un ponte COM per chiamare una DLL a 64 bit da un'applicazione a 32 bit

· Aggiornato il: · · COM, Sviluppo Windows, 32 bit, 64 bit

Volere chiamare una DLL a 64 bit da un’applicazione a 32 bit è un’esigenza abbastanza comune su Windows. Soprattutto quando si vogliono mantenere in sede asset esistenti e usare solo la funzionalità del lato a 64 bit, un’architettura a ponte COM tende a essere la risposta pratica.

Indice

  1. Lo scenario
  2. La soluzione
  3. Flusso di elaborazione (diagramma di sequenza)
  4. Codice di esempio (concettuale)
  5. Codice di esempio completo
  6. Riferimenti

1. Lo scenario

Questo è il caso in cui si vuole mantenere un’applicazione a 32 bit così com’è, ma usare un’elaborazione che risiede in una DLL a 64 bit. Il problema è che un processo a 32 bit non può caricare una DLL a 64 bit. È un vincolo a livello di sistema operativo: non si può aggirare con trucchi.

La situazione tipica è questa.

  • L’applicazione a 32 bit esistente è un asset di grandi dimensioni e non può essere migrata a breve
  • La DLL a 64 bit ha nuove funzionalità, oppure le sue dipendenze sono solo a 64 bit
  • Si vuole chiamarla dal lato a 32 bit “con i tipi”

Con questa combinazione, la strada in-process è chiusa fin dall’inizio.

2. La soluzione

La soluzione di base è separare tramite COM out-of-process (un server EXE). La DLL a 64 bit viene chiamata da un server COM a 64 bit (EXE), e l’applicazione a 32 bit la utilizza tramite COM.

Il flusso è il seguente.

  1. Costruire un COM LocalServer a 64 bit (EXE) che chiama internamente la DLL a 64 bit
  2. Condividere l’interfaccia COM (IDL/TypeLib) per esporre i tipi
  3. L’applicazione a 32 bit chiama COM “con i tipi” (comunicando tramite proxy/marshaling)

Ci sono comunque delle avvertenze.

  • Le registrazioni a 32 bit e a 64 bit sono separate (incluso WOW6432Node)
  • Le strutture personalizzate richiedono una progettazione di marshaling
  • Esiste un overhead IPC, quindi fare attenzione alle chiamate ad alta frequenza

In sintesi, l’approccio collaudato è “spostare l’elaborazione a 64 bit in un processo separato e collegarla con COM”.

3. Flusso di elaborazione (diagramma di sequenza)

Di seguito il flusso quando l’applicazione a 32 bit invoca l’elaborazione nella DLL a 64 bit.

Gestito dall'infrastruttura di marshaling COM registrataDLL 64 bitServer COM 64 bit(EXE)Stub COM(lato 64 bit)RPC/IPC(comunicazione inter-processo)Proxy COM(lato 32 bit)App client 32 bitDLL 64 bitServer COM 64 bit(EXE)Stub COM(lato 64 bit)RPC/IPC(comunicazione inter-processo)Proxy COM(lato 32 bit)App client 32 bitEsegue il marshaling dei parametriEsegue l'unmarshaling dei parametriEsegue il marshaling del valore di ritornoEsegue l'unmarshaling del valore di ritornoICalcService.Add(1, 2)Dati serializzatiTrasferiti attraverso il confine di processoAdd(1, 2)Chiamata a funzione nativaRisultato: 3Risultato: 3Risultato serializzatoTrasferito attraverso il confine di processoRisultato: 3

Punti chiave:

  • L’applicazione a 32 bit può effettuare chiamate type-safe attraverso l’interfaccia ICalcService
  • Il runtime COM attraversa il confine di processo usando la DLL proxy/stub registrata, il marshaler della libreria dei tipi, il marshaler standard e così via
  • A causa dell’overhead della comunicazione inter-processo, è preferibile raggruppare il lavoro anziché fare molte chiamate a grana fine

4. Codice di esempio (concettuale)

Di seguito uno schizzo concettuale. In pratica servono anche registrazione, generazione della TypeLib e così via.

// Interfaccia condivisa (equivalente IDL)
[ComVisible(true)]
[Guid("7A4B5B23-0A2F-4D2B-9D4D-8A2A92B8B001")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICalcService
{
    int Add(int a, int b);
}

// COM LocalServer a 64 bit (lato EXE)
[ComVisible(true)]
[Guid("1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11")]
[ClassInterface(ClassInterfaceType.None)]
public class CalcService : ICalcService
{
    public int Add(int a, int b)
    {
        // Qui viene chiamata la DLL a 64 bit
        return a + b;
    }
}

// Lato app a 32 bit (client)
Type t = Type.GetTypeFromProgID("KomuraSoft.CalcService");
var calc = (ICalcService)Activator.CreateInstance(t);
int result = calc.Add(1, 2);

Con questa forma, il lato a 32 bit può lavorare “con i tipi”. COM usa proxy/stub internamente ed effettua la chiamata su IPC per noi.

5. Codice di esempio completo

Un’implementazione funzionante del concetto sopra è pubblicata su GitHub.

Call64bitDLLFrom32bitProc - GitHub

Il repository contiene quanto segue:

  • Call64bitDLLFrom32bitProc/ - COM LocalServer a 64 bit (EXE)
  • X64DLL/ - DLL a 64 bit (l’elaborazione effettiva)
  • X86App/ - Client a 32 bit (WinForms)
  • scripts/ - Script di registrazione/deregistrazione del server COM

Se lo si compila e registra seguendo i passaggi del README, si può vedere un processo a 32 bit che chiama effettivamente una DLL a 64 bit.

6. Riferimenti

  • Component Object Model (COM) overview https://learn.microsoft.com/en-us/windows/win32/com/component-object-model–com–portal
  • COM LocalServer32 registration https://learn.microsoft.com/en-us/windows/win32/com/localserver32
  • COM interface basics https://learn.microsoft.com/en-us/windows/win32/com/the-component-object-model
  • COM Interop (use from .NET) https://learn.microsoft.com/en-us/dotnet/standard/native-interop/cominterop

Articoli recenti con gli stessi tag per approfondire argomenti vicini.

Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.

L’articolo è direttamente collegato ai servizi seguenti.

Domande frequenti

Domande che ricorrono nelle consulenze sull’argomento dell’articolo.

Un'applicazione a 32 bit può caricare una DLL a 64 bit?
No. Un processo a 32 bit non può caricare una DLL a 64 bit: è un vincolo a livello di sistema operativo su Windows, non qualcosa che si possa aggirare con trucchi. La strada in-process è chiusa fin dall'inizio. Se serve la funzionalità che risiede in una DLL a 64 bit, l'elaborazione deve essere eseguita in un processo separato a 64 bit, e un ponte COM è un modo collaudato per collegare i due.
Come collega un ponte COM un'applicazione a 32 bit a una DLL a 64 bit?
Si costruisce un COM LocalServer a 64 bit (un EXE) che chiama internamente la DLL a 64 bit, si condivide l'interfaccia COM tramite IDL o una libreria dei tipi per esporre i tipi, e l'applicazione a 32 bit la utilizza attraverso COM. Il runtime COM attraversa il confine dei processi usando proxy, stub e marshaling, quindi il lato a 32 bit può effettuare chiamate type-safe attraverso l'interfaccia condivisa.
Quali sono i principali avvertenze quando si usa un ponte COM out-of-process?
Ce ne sono tre principali. Primo, le registrazioni COM a 32 bit e a 64 bit sono separate, inclusa l'area di registro WOW6432Node. Secondo, le strutture personalizzate richiedono una progettazione di marshaling anziché funzionare automaticamente. Terzo, la comunicazione inter-processo aggiunge overhead a ogni chiamata, quindi è preferibile raggruppare il lavoro anziché fare molte chiamate a grana fine in scenari ad alta frequenza.
Esiste un esempio funzionante di ponte COM da 32 a 64 bit?
Sì. Un'implementazione completa e funzionante è pubblicata su GitHub come repository Call64bitDLLFrom32bitProc. Contiene un COM LocalServer a 64 bit (EXE), una DLL a 64 bit con l'elaborazione effettiva, un client WinForms a 32 bit e script di registrazione del server COM. Se lo si compila e registra seguendo il README, si può vedere un processo a 32 bit che chiama effettivamente una DLL a 64 bit.

Profilo dell’autore

Pagina di presentazione dell’autore dell’articolo.

Go Komura

Rappresentante di KomuraSoft LLC

Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.

Torna al blog