Newsgroup: it.comp.dotnet
Titolo: Chiamata a c# da codice nativo

Ciao a tutti,
per evitare di utilizzare GSOAP all'interno
di un'applicazione nativa vorrei chiamare codice .net per
poter effettuare chiamate ad un web services.
Fino ad ora sono riuscito nell'intento di chiamare una
semplice class library c# da codice nativo usando un wrapper
c++/cli usando il seguente schema :
codice unmanaged > dllWrapper unmanaged/managed > ClassLibrary managed
appena aggiungo all'interno della classe c# il riferimento al
WebService service usando WCF
             ServizioProva.Service1Client ser = new ServizioProva.Service1Client();
il programma esce con un'eccezione.
Stessa cosa se tento di usare LINQ mentre se uso un dataset per
accedere a SQL funziona tutto.
Dove sbaglio?
Grazie Luca

 il programma esce con un'eccezione.
Quale?
-- 
Raffaele Rialdi  
http://www.iamraf.net
Weblog: 
http://blogs.ugidotnet.org/raffaele
Microsoft MVP profile 
https://mvp.support.microsoft.com/profile/raffaele
UGIdotNET - 
http://www.ugidotnet.org/


Eccezione non gestita a 0x75d0b727 in StandardC.exe: 0xE0434352: 0xe0434352.
Non è disponibile il sorgente e lo stack è il seguente :
 >	KernelBase.dll!75d0b727() 	
  	[I frame indicati di seguito possono essere errati e/o mancanti, non sono stati caricati simboli per KernelBase.dll]	
  	KernelBase.dll!75d0b727() 	
  	System.ServiceModel.ni.dll!06c7be75() 	
  	System.ServiceModel.ni.dll!0610c369() 	
  	System.ServiceModel.ni.dll!0699d4e1() 	
  	clr.dll!5fbf2142() 	
  	00de0882()	
  	005e9090()	
Luca


Purtroppo non è molto, bisognerebbe guardare più a fondo con windbg.
Visto così sembra però un problema sul marshalling del tuo wrapper.
Assicurati che i parametri che passi a C# vengano effettivamente 
copiati correttamente nella memoria managed e che non siano più 
toccati.
Quello che temo è che stai vedendo gli effetti di corruzione della 
memoria perché il codice nativo va a scrivere zone di memoria 'managed' 
che il GC sta spostando. Se da codice nativo vuoi scrivere nella 
memoria managed, dovrai fare il pinning della zona di memoria e poi 
rilasciarlo una volta che il codice nativo ha finito il suo lavoro.
Dire di più è difficile vista l'esigua informazione ...



ho controllato ma essendo veramente un giocattolo per
fare delle prove di fattibilità non credo dipenda da quello.
Grazie per l'interessamento se può aiutare i
sorgenti sono i seguenti :
Console application :
// StandardC.cpp : definisce il punto di ingresso dell'applicazione console.
//
#include "stdafx.h"
#include "..\DllWrapper\DllWrapper.h"
#include <conio.h>
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    char Date[200];
    int i;
    GetData ( Date, &i );
    printf ( "\nNumero <%d>", i );
    printf ( "\nData : <%s>", Date );
    printf ( "\nPremere un tasto per terminare..." );
    getch();
	return 0;
}
Dll wrapper :
// DllWrapper.cpp: definisce le funzioni esportate per l'applicazione DLL.
//
#include "stdafx.h"
#include "DllWrapper.h"
// Utilities for Native<>CLI conversions
#include <vcclr.h>
using namespace System;
using namespace Runtime::InteropServices;
using namespace SampleClass;
#pragma unmanaged
int NATIVEDLL_API GetData ( char *D, int *i )
{
    tstring Data;
    int Numero;
    CClass1 classe;
    Numero = classe.Get_Number();
    *i = Numero;
    Data = classe.GetDateTime();
    WideCharToMultiByte(CP_UTF8, 0, Data.c_str(), -1, (char *)D, 100, NULL, NULL);
    classe.~CClass1();
    return ( 0 );
}
#pragma managed
CClass1::CClass1()
{
    Class1^ class1 = gcnew Class1();
    // Managed type conversion into unmanaged pointer is not allowed unless
    // we use "gcroot<>" wrapper.
    gcroot<Class1^> *pp = new gcroot<Class1^>(class1);
    this->m_pClass1CLR = (void*)pp;
}
CClass1::~CClass1()
{
    if (this->m_pClass1CLR)
    {
       // Get the CLR handle wrapper
       gcroot<Class1^> *pp = reinterpret_cast<gcroot<Class1^>*>(this->m_pClass1CLR);
       this->m_pClass1CLR = 0;
       // Delete the wrapper; this will release the underlying CLR instance
       delete pp;
    }
}
tstring CClass1::GetDateTime() const
{
    tstring strDate;
    if (this->m_pClass1CLR != 0)
    {
       // Get the CLR handle wrapper
       gcroot<Class1^> *pp = reinterpret_cast<gcroot<Class1^>*>(this->m_pClass1CLR);
       // Convert to std::string
       // Note:
       // - Marshaling is mandatory
       // - Do not forget to get the string pointer...
       strDate = (const TCHAR*)Marshal::StringToHGlobalAuto( ( (Class1^)*pp)->GetDateTime()).ToPointer();
    }
    return strDate;
}
int CClass1::Get_Number ( ) const
{
    if (this->m_pClass1CLR != 0)
    {
       // Get the CLR handle wrapper
       gcroot<Class1^> *pp = reinterpret_cast<gcroot<Class1^>*>(this->m_pClass1CLR);
       // Convert to std::string
       // Note:
       // - Marshaling is mandatory
       // - Do not forget to get the string pointer...
       return ((Class1^)*pp)->GetNumero;
    }
    return ( 0 );
}
e classe c# :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Data;
using System.Data.SqlClient;
namespace SampleClass
{
     public class Class1
     {
         public int Numero;
         public string data;
         ~Class1()
         {
             Console.WriteLine("\nEsco");
         }
         public Class1 ()
         {
             Numero = 10;
         }
         public string GetDateTime ( )
         {
             // Anche solo se definisco la classe per l'accesso al
             // servizio il problema si presenta.
--->>>      ServizioProva.Service1Client ser = new ServizioProva.Service1Client();
             //servizio.Open();
             //data = servizio.DoWork();
             //servizio.Close();
             data = DateTime.Now.ToString();
             return (data);
         }
         public int GetNumero
         {
             get
             {
                 return (Numero);
             }
         }
     }
}
Luca

 fare delle prove di fattibilità non credo dipenda da quello.
1. Usa solo progetti VC++ di tipo Unicode.
Lavorare in ansi con VC++ non ha senso da più di 15 anni e aumenta 
inutilmente la complessità, soprattutto quando hai del marshalling 
verso il framework che è solo Unicode.
2. Non devi chiamare il distruttore in modo esplicito. Il distruttore 
viene chiamato dal runtime di vc++ non appena la variable esce dallo 
scope (o se usi un ptr quando nei fai la delete).
3. Stai scrivendo codice C e non C++. Non usare i puntatori se non 
quando è strettamente indispensabile. Usa smart pointer, classi, etc.
Leggi i libri di Stroustrup, Sutter e Alexandrescu per avere dei punti 
di riferimento.
4. Il distruttore in C# è in realtà la Finalize e non è deterministico 
perciò devi evitarlo come la peste (non serve per scaricare memoria o 
oggetti ma solo risorse native come gli handle). Auto_GCRoot ti evita 
le rogne di chiamare la Dispose se la tua classe Managed implementa 
IDisposable.
5. Se referenzi un WS devi creare il tuo Main.Exe.config in modo che 
contenga la configurazione di wcf, altrimenti va in eccezione
6. Per debuggare devi specificare nelle property del progetto C++ 
nativo che il debugger è mixed, altrimenti non puoi andare step by 
step.
7. se vuoi invocare un web service wcf da codice nativo esiste un 
percorso più diretto.
Ci sono infatti i Windows Web Services (WWS) nativi a partire da 
Windows 7 che permettono di invocare servizi WS-* da C/C++.
Dovrebbe esserci ancora un mio webcast su msdn che mostra rapidamente 
come fare.
Il codice del wrapper è piuttosto ingarbugliato, per cui faccio prima a 
scriverti qualche riga di codice.
=== Console nativa pura C++ ===
#include "stdafx.h"
#include "..\Wrapper\Wrapper.h"
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    wstring Date;
    int i;
    GetData( Date, i );
    wcout << endl << L"Numero " << i;
    wcout << endl << L"Data " << Date;
    wcout << endl << L"\nPremere un tasto per terminare...";
	cin.get();
	return 0;
}
=== DLL mixed C++ compilata con /clr ===
// Wrapper.h
#pragma once
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int GetData ( wstring &date, int &i );
// Wrapper.Cpp
#include "stdafx.h"
#include <msclr\gcroot.h>
#include <msclr\marshal_cppstd.h>
using namespace msclr;
using namespace msclr::interop;
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
using namespace System;
using namespace Runtime::InteropServices;
using namespace CSLibrary;
class SmallWrapper
{
private:
	gcroot<ManagedClass^> mc;
public:
	SmallWrapper()
	{
		mc = gcnew ManagedClass();
	}
	wstring GetDateTime()
	{
		System::String ^temp = mc->GetDateTime().ToString();
		wstring result = marshal_as<wstring>(temp);
		return result;
	}
	int GetNumero()
	{
		return mc->Numero;
	}
};
int GetData ( wstring &date, int &i )
{
	SmallWrapper wrapper;
	date = wrapper.GetDateTime();
	i = wrapper.GetNumero();
	return 0;
}
=== DLL C# che invoca un web service ===
using System;
using System.Diagnostics;
namespace CSLibrary
{
	public class ManagedClass
	{
		Random rnd = new Random((int)DateTime.Now.Ticks);
		public DateTime GetDateTime()
		{
			var client = new ServiceReference1.Service1Client();
			Debug.WriteLine(client.GetData(10));
			Numero = rnd.Next();
			return DateTime.Now;
		}
		public int Numero {get; set;}
	}
}
Diciamo che questo è uno strappo alla regola perché non è che mi metto 
a scrivere codice per ogni post sul ng, altrimenti non faccio altro :)


Ciao,
ti ringrazio per l'interessamento studierò con
attenzione le risorse da te indicate e farò sapere al ng
i risultati ottenuti.
Il problema è che devo costruire una dll "bridge" fra il mondo
unmanaged c ansi e il mondo "nuovo" in quanto si tratta di
un'applicazione, si vecchia ma con un numero enorme di
installazioni, per cui dobbiamo utilizzare questa base ed
aumentare le possibilità lavorando ai "fianchi" della
"business logic" principale.
Almeno fino alla sostituzione della parte principale.
 > 7. se vuoi invocare un web service wcf da codice nativo
 > esiste un percorso più diretto.
 > Ci sono infatti i Windows Web Services (WWS) nativi a partire da Windows 7
 > che permettono di invocare servizi WS-* da C/C++.
 > Dovrebbe esserci ancora un mio webcast su msdn che mostra rapidamente come fare.
Al momento ho quasi tutte le installazioni che sono con windows
xp embedded :( e questo mi limita nella scelta delle
alternative ma non mancherò di studiare questa possibilità.
Grazie mille delle dritte :)
Luca
P.S. Mi ritengo per il momento un programmatore C, anche se
sto studiando, molto, per l'utilizzo di .NET.


 sto studiando, molto, per l'utilizzo di .NET.
Ottimo, buono studio allora
