2009-03-15 21 views
7

Sto ricevendo System.IO.FileNotFoundException: The specified module could not be found quando si esegue il codice C# che chiama un assembly C++/CLI che a sua volta chiama una DLL C pura. Accade non appena viene creato un oggetto che richiama le funzioni della DLL C pura.C# a C++/CLI a C DLL System.IO.FileNotFoundException

BackingStore è puro C. CPPDemoViewModel è C++/CLI che chiama BackingStore ha un riferimento a BackingStore.

Ho provato il caso più semplice possibile: aggiungere un nuovo progetto di test dell'unità C# che tenta solo di creare un oggetto definito in CPPDemoViewModel. Ho aggiunto un riferimento dal progetto C# a CPPDemoViewModel.

Un progetto di test C++/CLI funziona bene solo con l'aggiunta di CPPDemoViewModel quindi è qualcosa che va tra le lingue.

Utilizzo Visual Studio 2008 SP1 con .Net 3.5 SP1. Sto costruendo su Vista x64 ma sono stato attento ad assicurarmi che il mio target della piattaforma sia impostato su x86.

Mi sembra che qualcosa di stupido e ovvio mi manchi ma sarebbe ancora più stupido da parte mia perdere tempo a cercare di risolverlo in privato, quindi sono qui a disagio con me stesso!

Questo è un test per un progetto che esegue il porting di un'enorme quantità di codice C legacy che sto mantenendo in una DLL con un ViewModel implementato in C++/CLI.

modifica Dopo aver controllato le directory, posso confermare che il BackingStore.dll non è stato copiato.

Ho le cartelle di progetto univoche standard create con una soluzione multiprogetto tipica.

 
WPFViewModelInCPP 
    BackingStore 
    CPPViewModel 
    CPPViewModelTestInCS 
    bin 
     Debug 
    Debug 

Il più alto livello di debug sembra essere una cartella comune usata dai progetti C e C++/CLI, con mia grande sorpresa.

WPFViewModelInCPP \ Debug contiene BackingStore.dll, CPPDemoViewModel.dll, CPPViewModelTest.dll e la loro .ilk associati e file PDB

WPFViewModelInCPP \ CPPViewModelTestInCS \ bin \ Debug contiene CPPDemoViewModel e CPPViewModelTestInCS .dll e file PDB, ma non BackingStore. Tuttavia, la copia manuale di BackingStore nella directory non ha corretto l'errore.

CPPDemoViewModel ha la proprietà Copia locale set che presumo sia responsabile della copia della DLL quando viene fatto riferimento. Non riesco ad aggiungere un riferimento da un progetto C# a una DLL C pura. Dice solo Impossibile aggiungere un riferimento a Backing Store.

Non sono sicuro di avere solo un problema o due.

È possibile utilizzare una fase di copia di copia vecchio stile per copiare il BackingStore.dll in qualsiasi directory del progetto C#, anche se speravo che il nuovo modello .net non lo richiedesse.

DependencyWalker mi sta dicendo che il file mancante è GPSVC.dll che has been suggested indica problemi relativi alle impostazioni di sicurezza. Sospetto che questa sia un'aringa rossa.

edit2 Con una copia manuale di BackingStore.dll per essere adiacente all'eseguibile, la GUI ora funziona correttamente. Il progetto di test C# ha ancora problemi che sospetto sia dovuto all'ambiente di runtime di un progetto di test, ma per ora posso vivere senza di esso.

risposta

4

La risposta per la GUI, altro che cambiare le impostazioni di output, è stata l'aggiunta di un pre-compilazione passo

copy $(ProjectDir)..\Debug\BackingStore.* $(TargetDir) 

La risposta per i progetti di test è stato quello di aggiungere la DLL mancante alla scheda Distribuzione della testrunconfig. È possibile farlo modificando direttamente il valore predefinito LocalTestRun.testrunconfig (visualizzato in Soluzione in Elementi della soluzione) o fare clic con il pulsante destro del mouse sulla Soluzione e aggiungere una nuova configurazione di prova, che verrà quindi visualizzata nel menu Test principale.

Grazie per le risposte su this SO question sulle configurazioni di test per avermi portato alla risposta.

+0

Sì, lo so che è impertinente contrassegnare la propria risposta come accettata, ma nessun altro si è avvicinato alla vera risposta per i test, che ha richiesto una buona ora di ricerca. Sono ancora stordito che devo usare una tecnica così antiquata per copiare la DLL! –

+0

Andy, dovresti ancora controllare il punto che ho sollevato, ho la sensazione che tu abbia avuto fortuna con l'ordine/tempo di inizializzazione, e possa diventare sfortunato in futuro senza una salda presa su questo. – RandomNickName42

11

Le DLL C e C++ si trovano nella stessa directory dell'assembly C# in esecuzione?

Potrebbe essere necessario modificare le impostazioni di output del progetto in modo che l'assembly C# e le altre DLL finiscano tutti nella stessa cartella.

Ho usato spesso lo Dependency Walker in casi come questo; è un controllo di integrità che mostra che tutte le dipendenze possono effettivamente essere trovate.

Una volta che l'app è in esecuzione, si consiglia inoltre di provare Process Monitor sul codice che si sta eseguendo, per vedere quali DLL vengono referenziate e dove si trovano.

+0

+1 per Dipendenza Walker; ottimo strumento – Randolpho

+0

Non avevo pensato di usare il buon vecchio depends.exe su .Net (sono un vecchio Win32/MFC e un altro C++). Buon punto Ha confermato la DLL mancante. –

+0

@Andy: Bene, va bene;] Depends.exe ftw! –

1

Questo è un dilemma interessante. Non ho mai sentito parlare di un problema nel caricamento di .DLL nativi da C++/CLI dopo una chiamata da C# prima. Posso solo presumere che il problema sia come suggerito da @Daniel L e che il tuo .DLL semplicemente non si trovi in ​​un percorso che può trovare il loader di assiemi.

Se il suggerimento di Daniel non funziona, ti suggerisco di provare a collegare staticamente il codice C nativo al programma C++/CLI, se puoi. Ciò risolverebbe sicuramente il problema, in quanto la DLL sarà interamente assorbita nel file C++/CLI .DLL.

+0

è una buona idea, ma non è possibile - il codice deve rimanere in una DLL C pura per motivi di sicurezza e perché è K & R C antico che non può essere convertito in ANSI (cattive funzioni matematiche) –

+0

Ah, beh. Per fortuna Daniel aveva ragione, eh? – Randolpho

1

Assicurarsi che il sistema di destinazione abbia il runtime MS Visual C corretto e che non si sviluppi accidentalmente la DLL C con un runtime di debug.

+0

+1 punto buono, il materiale di debug CRT mi ha ottenuto prima ... –

+0

È detto che la DLL C++ nativa non dovrebbe essere costruita con il target Debug? Di cosa esattamente "mi sto accertando"? Ho una situazione molto simile (C# che chiama DLL C++/CLI che chiama DLL C++ nativa per incorporare il codice legacy) e vorrei evitare qualsiasi insidia venga identificata qui. –

+0

@richp: IIRC, la dipendenza è su msvcrtd.dll. Depends.exe dovrebbe confermare. – leppie

4

Il motivo per cui questo accade è perché si sta caricando DLLMAIN dal codice gestito, prima che il CRT abbia l'opportunità di essere inizializzato. Potresti non avere alcun codice gestito, essere eseguito DIRETTAMENTE o INDERETTAMENTE da un effetto delle notifiche DllMain. (Vedi: Expert C++/CLI: .Net per Visual C++ Programmatori, capitolo 11 ++).

O non si dispone di un punto di accesso nativo definito waht, tuttavia è stato effettuato il collegamento a MSVCRT. Il CLR viene automaticamente inizializzato per te con/clr, questo dettaglio causa molta confusione e deve essere preso in considerazione. Una DLL in modalità mista in realtà carica il ritardo il CLR tramite l'utilizzo di patch a caldo di tutti i vtables di punti di ingresso gestiti nelle classi.

Un numero di problemi di inizializzazione della classe circondano questo argomento, il blocco del caricatore e il caricamento ritardato del CLR sono talvolta un po 'trickey. Prova a dichiarare statico globale e non utilizzare #pragma gestito/non gestito, isola il tuo codice con/clr per-file.

Se non è possibile isolare il codice dal codice gestito e si verificano problemi, (dopo aver eseguito alcuni di questi passaggi), è possibile anche cercare di ospitare il CLR stesso e magari passare attraverso lo sforzo di creazione di un gestore dominio , questo assicurerebbe il tuo completo "in-the-loop" degli eventi di runtime e bootstrap.

Questo è esattamente il motivo, non ha nulla da fare con il percorso di ricerca, o l'inizializzazione. Sfortunatamente il visualizzatore di log di Fusion non è di grande aiuto (che è il solito posto dove cercare i problemi di binding dell'assembly CLR .NET e non il walker delle dipendenze).

Anche il collegamento statico non ha nulla da fare. È possibile NON collegare in modo statico un'applicazione C++/CLI che è in modalità mista.

  1. Inserire la funzione DLLMAIN in un file da sola.
  2. Assicurarsi che questo file fa NON hanno /CLR set nelle opzioni di generazione (file di opzioni di generazione)
  3. Assicurarsi che il collegamento con/MD o/MDd, e tutte le dipendenze che vi uniscono uso lo stesso CRT.
  4. Valutare le impostazioni del linker per/DEFAULTLIB e/INCLUDE per identificare eventuali problemi di riferimento possibili, è possibile dichiarare un prototipo nel codice e utilizzare/INCLUDE per sovrascrivere la risoluzione predefinita del collegamento della libreria.

Buona fortuna, controlla anche che il libro sia molto buono.

+0

grazie per aver sollevato questi problemi - sono stati presi sul serio e aggiunti ai nostri compiti per la revisione del codice –

+0

Punto 2 su non impostare/CLR mi ha aiutato. Grazie. – Patel

0

Ha avuto lo stesso problema passando a Vista a 64 bit. La nostra applicazione stava chiamando DLL Win32 che confondevano la build di destinazione per l'applicazione. Per risolverlo abbiamo fatto quanto segue:

  1. Vai alle proprietà del progetto;
  2. Selezionare la scheda Crea;
  3. Modifica opzione 'Piattaforma target:' su x86;
  4. Ricostruisci l'applicazione.

Quando ho ripetuto l'applicazione ha funzionato.