PDA

Voir la version complète : ManagedSxDL


Lenolian
15/04/2005, 13h03
J'essaye de porter la lib SxDL (j'ai la 2.4) sur la plateforme .Net et après quelques longues heures, j'arrive enfin à créer une application SxDL. C'est fou le nombre de SDK que j'ai du installer pour la faire tourner.

Après çà j'ai juste recompiler avec l'option /clr pour passer en environement managée. Bien évidemment ça marche pas correctement, j'ai une foultitude d'erreur dans des headers obscurs (ObjBase.h & Unknw.h) du type :

C:\Program Files\Microsoft Visual Studio 8\VC\include\ObjBase.h(398) : error C2146: syntax error : missing ';' before identifier 'IRpcStubBuffer'
C:\Program Files\Microsoft Visual Studio 8\VC\include\ObjBase.h(398) : error C2079: 'IRpcStubBuffer' uses undefined struct 'IRpcStubBuffer'
C:\Program Files\Microsoft Visual Studio 8\VC\include\ObjBase.h(399) : error C2146: syntax error : missing ';' before identifier 'IRpcChannelBuffer'
C:\Program Files\Microsoft Visual Studio 8\VC\include\ObjBase.h(399) : error C2371: 'IRpcChannelBuffer' : redefinition; different basic types
c:\Program Files\Microsoft Visual Studio 8\VC\include\RpcNdr.h(675) : see declaration of 'IRpcChannelBuffer'

J'ai remarquer qu'à chaque fois une ligne du type "typedef interface IXXX IXXX" était en cause. J'ai cru comprendre qu' interface n'était pas un mot-clé du C++ alors qu'il en est un en C++ managée, je pense que l'erreur vient de là, mais je vois pas trop comment résoudre le problème.

Seconde tentative avec l'option /clr:oldsyntax, cela oblige à utiliser les anciens mots-clé du C++ managée (ils ont complètement changé maintenant). Avec cette option de compilation je n'ai plus de problème avec les headers mais plutôt avec le linker :

------ Build started: Project: TestSxDL, Configuration: Debug Win32 ------
Compiling...
Test.cpp
Linking...
LibSxDL.lib(d3dapp.obj) : warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/INCREMENTAL:NO' specification
LIBCMTD.lib(dbgheap.obj) : error LNK2005: _malloc already defined in MSVCRT.lib(MSVCR80.dll)
LIBCMTD.lib(dbgheap.obj) : error LNK2005: _realloc already defined in MSVCRT.lib(MSVCR80.dll)
LIBCMTD.lib(dbgheap.obj) : error LNK2005: _free already defined in MSVCRT.lib(MSVCR80.dll)
LIBCMTD.lib(crt0dat.obj) : error LNK2005: _exit already defined in MSVCRT.lib(MSVCR80.dll)
LIBCMTD.lib(crt0dat.obj) : error LNK2005: __exit already defined in MSVCRT.lib(MSVCR80.dll)
LIBCMTD.lib(crt0dat.obj) : error LNK2005: __cexit already defined in MSVCRT.lib(MSVCR80.dll)
LIBCMTD.lib(crt0dat.obj) : error LNK2005: __amsg_exit already defined in MSVCRT.lib(MSVCR80.dll)
LIBCMTD.lib(crt0dat.obj) : error LNK2005: __initterm_e already defined in MSVCRT.lib(MSVCR80.dll)
LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xc_z already defined in MSVCRT.lib(cinitexe.obj)
LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xc_a already defined in MSVCRT.lib(cinitexe.obj)
LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xi_z already defined in MSVCRT.lib(cinitexe.obj)
LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xi_a already defined in MSVCRT.lib(cinitexe.obj)
LIBCMTD.lib(winxfltr.obj) : error LNK2005: __XcptFilter already defined in MSVCRT.lib(MSVCR80.dll)
LIBCMTD.lib(errmode.obj) : error LNK2005: ___set_app_type already defined in MSVCRT.lib(MSVCR80.dll)
LIBCMTD.lib(crt0.obj) : error LNK2005: ___app_type already defined in MSVCMRT.lib(mstartup.obj)
LIBCMTD.lib(_file.obj) : error LNK2005: ___iob_func already defined in MSVCRT.lib(MSVCR80.dll)
LIBCMTD.lib(fflush.obj) : error LNK2005: _fflush already defined in MSVCRT.lib(MSVCR80.dll)
Creating library Debug/TestSxDL.lib and object Debug/TestSxDL.exp
LINK : warning LNK4098: defaultlib 'MSVCRT' conflicts with use of other libs; use /NODEFAULTLIB:library
LINK : warning LNK4098: defaultlib 'LIBCMTD' conflicts with use of other libs; use /NODEFAULTLIB:library
LIBCMTD.lib(crt0.obj) : error LNK2019: unresolved external symbol _main referenced in function _mainCRTStartup
Debug/TestSxDL.exe : fatal error LNK1120: 1 unresolved externals
Build log was saved at "file://d:\ManagedSxDL\TestSxDL\Debug\BuildLog.htm"
TestSxDL - 19 error(s), 3 warning(s)
========== Build: 0 succeeded, 1 failed, 1 up-to-date, 1 skipped ==========

Je ne connais pas trop les options de compilateurs donc je ne sais pas trop comment résoudre ce nouveau problème.

Sinon pour la méthode, je vais créer une dll.Net en C++ managée qui fera la liaison entre la lib SxDL et les codes C#,VB et autres. C'est je pense la méthode la plus adapté, P/Invoke est trop lent pour avoir de bonnes perfs, et utiliser l'IJW n'autorise que le c++ managée comme langage.

En gros on utilise un pointeur sur la classe que l'on veut wrapper, dans une classe managée et on marshal les données si nécessaire. Ca donne un truc comme ça (en utilisant l'ancienne syntaxe) :

public __gc class ManagedSxDL
{
private :
SxDL __nogc *m_Ptr;

public :
ManagedSxDL() { m_Ptr = new SxDL(); }
virtual ~ManagedSxDL() { delete m_Ptr; }
}

Voilà pour la présentation. A force de coder en C#, j'en avais complètement oublier que le C++ est si "chiant". Faut toujours linker des nouvelles libs qui sont parfois dur à trouver (j'ai du installer le SDK internet de windows pour compiler SxDL !!)... Bref vive C# .

LXS
15/04/2005, 17h54
TIens oui pas bête, je vais essayer de faire pareil sous Mono pour OpenSxDL...

LaurentUSA
15/04/2005, 18h13
Pour la deuxieme serie d'erreurs, il semblerait qu'il y ait une librairie en trop.

Cela fait maintenant 3 ans que je fais du C# tous les jours, mais c'est quasi exclusivement en managed. Je n'ai jamais fait autre chose que du p/invoke... Je ne peux guere t'aider pour l'instant

Est-ce que les methodes virtuelles "OnMachinChose" fonctionnent bien avec ton systeme ? Tres important dans SxDL!

Il va probablement y avoir des histoires pour les chaines de caracteres entre les strings Unicode du managed et SxDL 2.X. SxDL 3.0 est completement Unicode et en principe devrait mieux s'interfacer.

Lenolian
15/04/2005, 18h54
J'ai modifié les options de compilations en /clr:oldsyntax /MDd et les options de link avec /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:libcmt.lib, et il n'y a finalement plus d'erreur. ouf :)

En ce qui concerne les méthodes virtuelles, c'est ce qui me préocupe le plus. Je suis en train de me renseigner sur le net. J'ai bien une petite idée mis faut que je teste d'abord.

Bon je retourne dans les docs :)

Lenolian
16/04/2005, 12h44
Les premiers pas de Managed SxDL...

J'ai enfin réussi à lancer une appli SxDL à partir de C++ managed et de C#... pour l'instant on peut pas faire grand chose, mis a part FrameworkRun(). Enfin SxDL fait déjà plein de chose avec çà :)

ManagedSxDL.h
#pragma once

#include "sxdl.h"

namespace ManagedSxDL
{
public __gc class MSxDL
{
private:
SxDL __nogc *m_Ptr;
public:
MSxDL() { m_Ptr = new SxDL(); }
virtual ~MSxDL() { delete m_Ptr; }

int FrameworkRun();
};
}

ManagedSxDL.cpp
#include "stdafx.h"

#include "ManagedSxDL.h"

int ManagedSxDL::MSxDL::FrameworkRun()
{
System::Reflection::Module __gc *module = System::Reflection::Assembly::GetExecutingAssembly()->GetModule(S"ManagedSxDL.dll");
return m_Ptr->FrameworkRun((HINSTANCE)(System::Runtime::InteropServices::Marshal::GetHINSTANCE(module).ToPointer()));
}


SxDL.cs (pour le C#)
using ManagedSxDL;

namespace SxDL.Net
{
public class SXDL : MSxDL
{
public static int Main()
{
SXDL test = new SXDL();
return test.FrameworkRun();
}
}
}

Bon maintenant reste à trouver un moyen simple et fiable d'utiliser les fonctions virtuelles. A+.

LaurentUSA
16/04/2005, 21h19
Effectivement... Bien joué. C'est une premiere victoire importante.

Reste a gagner la guerre des méthodes virtuelles... :)

Lenolian
17/04/2005, 17h14
La "guerre des méthodes virtuelles" est en passe d'être gagné mais d'une façon légèrement tordu mais j'ai pas trouvé autre chose qui marche.

Comme on peut pas hériter directement entre natif et managée (heureusement :) ), j'ai pensé utiliser des CALLBACKs à la place des méthodes virtuelles dans un premier temps. Problème : les callbacks natifs et les délégués managed sont incompatibles , les premiers sont appelés grâce à __stdcall alors que les seconds grâce à __clrcall. J'ai résolu le problème un créant une classe native à l'intérieur de la classe managée.

typedef void (CALLBACK* SxDLSimpleEvent)(void);

__nogc class MixedSxDL : public SxDL
{
public :
MixedSxDL() {};
virtual ~MixedSxDL() {};

SxDLSimpleEvent OnStartUpEvent;

virtual void OnStartUp()
{
OnStartUpEvent();
}
};

public __gc class MSxDL
{
private:
MixedSxDL __nogc *m_Ptr;
static MSxDL *s_Instance =NULL;

__nogc class EventsProxy
{
static void CALLBACK OnStartUpEvent()
{
MSxDL::GetClass()->OnStartUp(); // Nécessaire car une classe native ne peut pas avoir un pointeur sur une classe managée en donnée membre
}
};

static MSxDL* GetClass() { return s_Instance; }

public :
MSxDL()
{
s_Instance = this;
m_Ptr = new MixedSxDL();
m_Ptr->OnStartUpEvent = MSxDL::EventsProxy::OnStartUpEvent;
}

virtual ~MSxDL() { delete m_Ptr; }

virtual void OnStartUp() { return; }
};

C'est une méthode qui ne me plait pas trop, ça ressemble plus à un hack qu'autre chose mais en attendant et après 2 jours de reflexion je trouve pas mieux. Je pourrais toujours changer après si je trouve mieux mais en attendant ça me permet d'avancer...

Lenolian
17/04/2005, 17h57
Ouh la, je dois vraiment être crevé pour avoir pondu une connerie comme ça :)

Y'a juste à faire ça, à la place de tout le bazar la haut :

__nogc MixedSxDL : public SxDL
{
public:
virtual void OnStartUp()
{
MSxDL::GetClass()->OnStartUp();
}
}

Ca oblige la classe MSxDL à être un singleton, mais ça pose pas trop de problème vu qu'il n'y a qu'une seule instance de SxDL à la fois.

Bon j'ai besoin de repos moi :)

LaurentUSA
17/04/2005, 23h38
J'ai poste sur Usenet a propos de ce sujet et il y a quelques reponses ici :

http://groups-beta.google.com/group/microsoft.public.fr.dotnet.csharp/browse_thread/thread/ecabad0c974745bf/cef4eef26c14a253#cef4eef26c14a253

Lenolian
18/04/2005, 07h31
Interressant le problème soulevé sur les variables globales pas correctement initialisé si on lie en statique la lib. Ca pourrait expliquer certains "problèmes" de texte mal rendu (en fait il n'y a que la fin de chaque ligne qui est affiché à l'écran et pas au bon endroit). Je vais essayer de linker en dynamique voir ce que ca donne. Merci.

LaurentUSA
19/04/2005, 04h39
Rémi Thomas a posté sur Usenet :


Salut Laurent,

C'est possible à condition de ne pas utiliser l'héritage multiple dans ton
code C++ que tu désires wrapper.
La bonne question à se poser est : comment appeler une méthode de base d'une
classe dérivée si je n'ai jamais instancié cette classe de base?
Tu peux t'en sortir avec une macro pour mettre en place un mécanisme de
pointeur sur la classe instancié.

#define NOGCCLASSWRAPPER(ContainerClassName,NestedClassType,Init,Id)\
private:\
NestedClassType *m_ptr;\
void ConstructorTrace() { /* Console::WriteLine("Constructor
"#NestedClassType); */ }\
void New() { if (GetId()==Id) { ConstructorTrace(); m_ptr = __nogc new
NestedClassType;} else m_ptr=0; } \
public:\
ContainerClassName() { New(); Init(); }\
virtual ~ContainerClassName() { if (m_ptr!=0) { /*
Console::WriteLine("Destructor "#NestedClassType); */ delete m_ptr;} }\
virtual void*_GetNestedPtr() { return m_ptr; }\
virtual int GetId() { return Id; }\
NestedClassType *GetNestedPtr() { return static_cast<NestedClassType
*>(_GetNestedPtr()); }

/*
exemple

C++

class baseType
{
int m_type;
public:
baseType() { SetType(5); }
int GetType() { return m_type; }
void SetType(int type) { m_type=type; }
};

class derivedType : public baseType
{
public:
derivedType() { SetType(6); }
int Add(int a, int b) { return a+b; }
};

C++/CLI (ou managed C++)
namespace LNNetWrapper2
{
class baseType
{
NOGCCLASSWRAPPER(baseType, ::baseType, __noop);
public:
int GetType() { return GetNestedPtr()->GetType(); }
};

class derivedType : public baseType
{
NOGCCLASSWRAPPER(derivedType, ::derivedType, __noop);
public:
int Add(int a, int b) {return GetNestedPtr()->Add(a,b);}
};
}
*/

J'avais dans l'idée d'écrire un article sur le sujet mais pour le moment je
n'ai pas trop le temps.
Bien entendu les méthodes virtuelles et le polymorphisme fonctionnent avec
ce mécanisme.
Il parait qu'un White Paper existe sur ce point mais je ne l'ai jamais
trouvé.

Rémi

-- Rémi Thomas - MVP Visual Studio .NET Développeur Windows indépendant

Lenolian
19/04/2005, 08h30
Oui mais c'est l'inverse qu'il faut faire. Ici c'est la méthode managée qui appelle la méthode native, alors que pour la SxDL on veut que ce soit la méthode managée qui soit appelée lorsque la méthode virtuelle de la SxDL est appelé. Les méthodes OnXXXX() sont définies dans la partie managée mais ne sont jamais directement appelé par le code managé.

Et si on peut avoir une classe native aggrégé dans une classe managée, l'inverse n'est pas possible.

Enfin je reverrais ça ce soir après le stage.

Lenolian
20/04/2005, 17h09
J'ai un problème de compilation avec SxDL 2.4, la classe CMesh est définie deux fois une dans Mesh.h et l'autre dans Mesh_old.h . Bon je suppose au vu du nom que Mesh_old.h est obsolète, mais je ne veut pas faire de connerie en supprimant un fichier qui s'avérerait nécessaire plus tard. Donc est-ce que je peut enlever l'un des deux fichiers sans avoir de problème plus tard ?

Sinon au stade de l'avancement, j'ai quelques problèmes avec l'affichage du texte (y'a que la fin de chaque ligne de visible dans les crédits par exemple) et je viens de remarquer un problème à l'initialisation : CMusic et CVidéo ne sont pas initialisé à cause d'une erreur avec une interface COM. Donc j'essaye de lier SxDL en dynamique, en espèrant que le problème venait de là.

LaurentUSA
20/04/2005, 20h29
Effectivement Mesh_Old n'est plus utile. J'ai conserve la version precedante en cas de GROS BUG. :)

Pour DirectShow ( Music et Video ), j'ai des problemes avec le SDK Avril. Il semblerait qu'il y ait probleme de compatibilite. Je n'ai pas encore tout compris le probleme.

Avec le SDK de Fevrier, il faut installer DirectShow "a la main" en commencant par DL les "SDK extras"

Lenolian
20/04/2005, 21h55
Pour CVideo et CMusic l'erreur se situe là :

// Initialize COM
HRESULT hr ;
if ( FAILED ( hr = CoInitializeEx ( NULL, COINIT_APARTMENTTHREADED ) ) )
{
TCHAR Msg [ ] = TEXT ( "ERROR - CVideo::Startup : Cant initialize COM.\n" ) ;
CTools::Log ( Msg ) ;
Game->OnCrash ( Msg ) ;
return E_FAIL ;
} ;


Le HRESULT retourné vaut RPC_E_CHANGED_MODE, d'après ce que j'en ai compris c'est parce que CoInitializeEx a déjà été appelé dans le même thread mais avec un paramètre différent de COINIT_APARTMENTTHREADED.

A noté aussi que cela m'arrive seulement en managée, y'a pas de problème en natif et que j'ai le SDK d'avril avec les extras de février d'installé.

LaurentUSA
20/04/2005, 22h37
Bon, la on a un bug dans SxDL. On doit effectivement ne pas melanger les modes d'initialisation dans le meme thread. Je vais regler ca ce soir probablement et te faire passer une Maj des que possible.

Lenolian
20/04/2005, 22h44
J'ai modifié en mettant la valeur par défaut selon msdn et ça fonctionne bien mais je suis incapable de dire si ça n'aura pas d'effet dans le futur.


// if ( FAILED ( hr = CoInitializeEx ( NULL, COINIT_APARTMENTTHREADED ) ) )
if ( FAILED ( hr = CoInitializeEx ( NULL, COINIT_MULTITHREADED ) ) )

Edit : mais ca ne marche plus en natif....

Lenolian
01/05/2005, 13h29
La je suis arrivé à un cul de sac et je vois pas trop comment m'en sortir.

Pour gérer les méthodes virtuelles je suis obligé de passer par un pointeur sur la classe managée :


class Natif
{
virtual void OnVirtualMethod(void)
{
Managed::GetInstance()->OnVirtualMethod();
}
}

class Managed
{
static Managed __gc *s_Instance = NULL;
static Managed* GetInstance() { return s_Instance; }
virtual void OnVirtualMethod(void) { return; }
}


Pour les méthodes virtuelles de la classe SxDL, il n'y a pas de problème puisque'il ne peut y avoir qu'une seule instance de cette classe. Le problème se pose pour les méthodes virtuelles des classe CRenderObject et CEntity puisque par définition de multiples instances de celles-ci doivent exister et donc la méthode utiliser plus haut ne marche plus...

Pour l'instant les portes de sortie que j'aperçois ne me plaisent guère :
- passer par COM : j'y connais rien, je ne sais même pas si c'est faisable.
- réécrire le code directement en managée : là y a beaucoup de boulot :)

Si quelqu'un a une autre idée, je l'attends avec impatience.

Lenolian
01/05/2005, 20h59
J'ai trouvé une autre solution, je passe par une classe managée qui va servir de proxy entre les classe natives et les classes managées.


#pragma once

using namespace System::Collections::Generic;
using namespace System::Collections;

namespace Proxy
{
public interface class IManagedWrapper
{
virtual void SetProxyIndex(int index) abstract;
};

generic<class ManagedClass>
where ManagedClass : IManagedWrapper
public ref class WrapperProxy
{
private:
static ArrayList^ ObjectList = gcnew ArrayList(10);
public:
inline static ManagedClass GetManagedInstance(int id)
{
return (ManagedClass)ObjectList[id];
}
inline static void AddManagedInstance(ManagedClass obj)
{
int index = ObjectList->Add(obj);
obj->SetProxyIndex(index);
};
};
}


A la création de l'objet managée il suffit de faire :


ManagedSxDL::SxDL::SxDL()
{
m_NestedPtr = __nogc new NativeSxDL::SxDL();
WrapperProxy<SxDL*>::AddManagedInstance(this);
}


et le code des méthodes virtuelles deviennent :


void NativeSxDL::SxDL::OnStartup(void)
{ WrapperProxy<ManagedSxDL::SxDL*>::GetManagedInstance(m_ProxyIndex)->OnStartup();
}