2016-01-21 1 views
0

Pourquoi cette erreur s'est produite? Le nombre de références est augmenté, les modèles de threads sont des appartements individuels. Coll-object et EmptyColl-function tous deux situés à l'intérieur d'une DLL. La conversion d'appel par défaut du projet ATL est __stdcall. La même erreur s'est produite avec d'autres objets dans cette DLL.Exception VariantClear lors de la transmission de l'objet COM à VB 6 avec la fenêtre de surveillance ouverte

VariantClear lève une exception lorsque la compensation VARIANT avec NULL objet:
Exception lancée à 0x75C14974 (oleaut32.dll) dans VB6.EXE: 0xC0000005:
emplacement lecture violation d'accès 0x00000008.

frmMain.frm (erreur, voir ci-dessous pourquoi):

Private Sub Form_Load() 
    Dim c As Coll 
    Set c = EmptyColl 
    'error when ends here with variable "c" in the watch window. 
End Sub 

frmMain.frm (pas d'erreur):

Private Sub Form_Load() 
    Dim c2 As Coll 'instead of Coll can be any object of same library 
    Set c2 = New Coll 'creation 
    Set c2 = Nothing 'destroying (optionaly) 
    Dim c As Coll 
    Set c = EmptyColl 
    'no error 
End Sub 

filyus.idl:

[ 
    object, 
    uuid(6FA7FAEB-5CE3-4A80-9288-2667EE5E7596), 
    dual, 
    nonextensible, 
    pointer_default(unique) 
] 
interface IColl : IDispatch{ 
    //some methods 
}; 

[ 
    uuid(157F3D2F-A427-4D5A-B908-87868297EA43), 
    version(1.0), 
] 
library Filyus 
{ 
    importlib("stdole2.tlb"); 
    [ 
    dllname("Filyus") 
    ] 
    module Filyus{ 
    [entry("EmptyColl")] 
    HRESULT EmptyColl([out, retval] IColl** Coll); 
    } 
}; 

filyus.def:

LIBRARY 

EXPORTS 
    DllCanUnloadNow  PRIVATE 
    DllGetClassObject PRIVATE 
    DllRegisterServer PRIVATE 
    DllUnregisterServer PRIVATE 
    DllInstall  PRIVATE 
    EmptyColl 

ole.h:

extern HRESULT EmptyColl(IColl** Coll); 

ole.cpp:

HRESULT EmptyColl(IColl** Coll) { 
    HRESULT hr; CComObject<CColl>* Object; 
    if (Coll != nullptr) { 
    hr = CComObject<CColl>::CreateInstance(&Object); 
    if (hr == S_OK) { 
     Object->AddRef(); 
     *Coll = Object; //same error with using QueryInterface 
    } 
    } 
    else hr = E_POINTER; 
    return hr; 
} 
+0

« * même erreur vient de se produire d'autres objets à l'intérieur de cette dll * » - il est clair que vous faites quelque chose de fondamentalement mauvais dans votre DLL. Veuillez fournir un [exemple minimal, complet et vérifiable] (http://stackoverflow.com/help/mcve) montrant plus de code de votre DLL. En particulier, comment il déclare et configure la classe 'CColl', et d'autres classes avec lesquelles vous rencontrez des problèmes. –

Répondre

0

EmptyColl() a besoin d'utiliser la __stdcall convention d'appel:

extern HRESULT __stdcall EmptyColl(IColl** Coll); 

HRESULT __stdcall EmptyColl(IColl** Coll) { 
    //... 
} 

Vous pouvez également utiliser la macro, qui STDMETHODCALLTYPE résout à __stdcall:Sans convention d'appel déclarée, un compilateur C/C++ utilisera par défaut __cdecl à la place, sauf s'il est configuré différemment. __cdecl et __stdcall gérer la pile d'appels différemment. Vous allez corrompre la pile d'appels si vous n'utilisez pas la bonne convention d'appel. Les normes COM exigent __stdcall, et c'est ce que VB attend.

+0

La conversion d'appel par défaut de mon projet est __stdcall. – Filyus

+0

Encore mieux d'être explicite sur la convention d'appel dans le code au lieu de s'appuyer sur les paramètres du projet. –

+0

Le décalage 8 à partir du début d'une interface COM est l'entrée vtable de la méthode 'Release()' de l'objet COM.Un AV à l'adresse '00000008' signifie' Release() 'est appelé sur un pointeur d'interface null. 'VariantClear()' appelle 'Release()' si le type 'VARIANT' est' VT_UNKNOWN' ou 'VT_DISPATCH', mais si le 'VARIANT' est géré correctement alors il ne devrait jamais avoir un pointeur d'interface null dedans . Un VARIANT vide doit avoir une valeur 'vt' de' VT_EMPTY' à la place. Cela dit, je vois que 'EmptyColl()' n'initialise pas le pointeur 'Coll' à null si' CreateInstance() 'échoue. –

0

L'erreur est survenue à cause d'un mauvais accès à l'objet.
CComPtr est pour le côté client, CComObject est pour le côté serveur (accès direct, l'obtenir uniquement lorsque vous avez déjà créé un objet de cette bibliothèque).

correcte ole.cpp:

HRESULT EmptyColl(IColl** Coll) { 
    HRESULT hr; CComPtr<IColl> Object; 
    if (Coll != nullptr) { 
    hr = Object.CoCreateInstance(CLSID_Coll); 
    if (hr == S_OK) { 
     Object.CopyTo(Coll); 
    } 
    } 
    else hr = E_POINTER; 
    return hr; 
} 
+0

L'implémentation 'EmptyColl()' est du code côté serveur, pas du code côté client. 'EmptyColl()' ne crée pas une instance d'un objet COM à partir d'une autre bibliothèque, il crée une nouvelle instance de la classe 'CColl' qui réside dans la même DLL que' EmptyColl() '. L'utilisation de 'CComObject' par OP est correcte dans cette situation (pourvu que' CColl' dérive de 'CComObjectRoot' ou' CComObjectRootEx', c'est-à-dire). L'utilisation de 'CComPtr' comme vous le suggérez, même si elle est correcte pour le code côté client, est incorrecte pour le code côté serveur' EmptyColl() '. –

+0

@ remy-lebeau Regardez le code "frmMain.frm (pas d'erreur)" ci-dessus, après la création de tout objet de cette bibliothèque (et éventuellement détruit) aucune erreur ne s'est produite. Par conséquent, pour utiliser CComObject, vous avez besoin de certains déjà créés à partir d'un objet côté client, une application possible - la création de l'enfant et des objets connexes. – Filyus

+0

Non, vous n'avez pas besoin d'un objet actif pour pouvoir utiliser 'CComObject' pour créer de nouveaux objets. J'ai écrit beaucoup de serveurs COM qui retournent de nouveaux objets lorsqu'ils sont appelés, et 'CComObject' fonctionne très bien pour cela quand il est utilisé correctement. Le problème ne figure pas dans le code affiché, comme le montre le commentaire "* Même erreur survenue avec d'autres objets à l'intérieur de ce dll *". La DLL doit faire quelque chose d'autre, mais ces détails ne sont pas encore disponibles. –