.NET w C++

0

Witam,

Mam problem taki : chcę korzystać z funkcji zawartych w DLL napisanych w .NET w VC++. Z którego to potem kodu chcę zrobić DLL. Znalazłem co nieco na ten temat, ale mam wątpliwości i pytania:

Zaznaczę, że nie potrzebuję dostępu do środa klas, czyli nie chcę pisać wrapperów.

Znalazłem taki przykład :

 #using <mscorlib.dll>
#using <System.dll>
#include <vcclr.h>
#include <stdio.h>
#include <tchar.h>

using namespace System;
using namespace System::Reflection;

void main ()
{
   try {
      // Load library dynamically:
      Assembly* a = Assembly::Load("MyLib");
      if (a) {
         Console::WriteLine("Assembly = {0}", a);
         Type* t = a->GetType("MyLib.MyClass");
         if (t) {
            Console::WriteLine("Type     = {0}", t);
            MethodInfo* m = t->GetMethod("SayHello");
            if (m) {
               Console::WriteLine("Method   = {0}\n", m);
               String* args[] = {"Test2"};
               m->Invoke(NULL, args);
            } else {
               printf("Can't find SayHello!\n");
            }
         } else {
            printf("Can't find MyLib.MyClass!\n");
         }
      } else {
         printf("Can't load MyLib!\n");
      }
   } catch (Exception* e) {
      Console::WriteLine("*** Oops: Exception occurred: {0}", e);
   }
}

Przypuśćmy, że mam dwie DLLki w .NET:

Assembly* a = Assembly::Load("DL1");
Assembly* b = Assembly::Load("DL2");

Ok, teraz chciałbym napisać coś takiego

//kod napisany w Matlabie (wcześniej jest NET.addAssembly('sciezki do DLL'))
//Meth - method

wfs = DL1.Meth1(true);
res = CL1.CL2.Meth2(wfs, 'sample.ssc'); //ta metoda siedzi w drugiej DLL - DL2

//Data and Img - parameters
res.Data = wfs.Img;

Więc zacząłem od włączenia /clr. Napisałem trochę kodu, ale gubię się strasznie jak mam metodę tak zagnieżdżoną jak w linicje z "res". Zapewne to są obiekty jakieś a w nich dopiero metoda. Jak to ugryźć ?

dzięki z góry : )

D.

0

Jaką masz wersję Visual Studio? Bo przykład który podałeś to Managed C++, od dawna zarzucony. W VS2005 pojawił się język C++/CLI, o innej składni.

0

Taka ciekawostka: w nowym Visual Studio pojawi się kolejna odmiana C++ umożliwiająca korzystanie i rozbudowywanie Windows Runtime. Składnia podobna do C++/CLI, żeby móc korzystać z COM-opodobnych obiektów WinRT, ale w pełni natywna.

0

Ojej, to niedobrze. Bo właśnie przykładów z All-in-One Framework tego z COM i CLI nie za bardzo obczajam. Być może dlatego, że jest tam mnóstwo kodu z wrapperami - których nie potrzebuję tak do końca, a mało przykładów jak callować metody - zresztą jak pisałem. Mam VS 2010 Pro : ) Lubię MSDNAA : P

0

Kurcze, byłem pewny, że byłem zalogowany ; ) Eniłej. Znalazłem to czego szukałem :

 /****************************** Module Header ******************************\
Module Name:  CppCallNETAssemblyWrapper.cpp
Project:      CppCallNETAssemblyWrapper
Copyright (c) Microsoft Corporation.

The code sample demonstrates calling the .NET classes defined in a managed 
assembly from a native C++ application through C++/CLI wrapper classes.

CppCallNETAssemblyWrapper (this native C++ application)
-->
CppCLINETAssemblyWrapper (the C++/CLI wrapper)
-->
CSClassLibrary (a .NET assembly)

This source is subject to the Microsoft Public License.
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
All other rights reserved.

THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/

#pragma region Includes
#include <stdio.h>
#include <windows.h>
#include "CppCLINETAssemblyWrapper.h"
#pragma endregion


//
//   FUNCTION: IsModuleLoaded(PCWSTR)
//
//   PURPOSE: Check whether or not the specified module is loaded in the 
//   current process.
//
//   PARAMETERS:
//   * pszModuleName - the module name
//
//   RETURN VALUE: The function returns TRUE if the specified module is 
//   loaded in the current process. If the module is not loaded, the function 
//   returns FALSE.
//
BOOL IsModuleLoaded(PCWSTR pszModuleName) 
{
    // Get the module in the process according to the module name.
    HMODULE hMod = GetModuleHandle(pszModuleName);
    return (hMod != NULL);
}


int wmain(int argc, wchar_t *argv[])
{
    BOOL fLoaded = FALSE;

    // The name of the .NET assembly to be 
    PCWSTR pszModuleName = L"CSClassLibrary";

    // Check whether or not the module is loaded.
    fLoaded = IsModuleLoaded(pszModuleName);
    wprintf(L"Module \"%s\" is %sloaded\n", pszModuleName, fLoaded ? L"" : L"not ");

    //
    // Invoke the public class defined in the .NET assembly.
    //

    // Use the .NET class CSSimpleObject defined in the C# class library 
    // through the C++/CLI wrapper class CSSimpleObjectWrapper.
    CSSimpleObjectWrapper obj;

    obj.set_FloatProperty(1.2F);
    float fProp = obj.get_FloatProperty();
    wprintf(L"Class: CSSimpleObject::FloatProperty = %.2f\n", fProp);

    wchar_t szStr[100];
    HRESULT hr = obj.ToString(szStr, ARRAYSIZE(szStr));
    if (SUCCEEDED(hr))
    {
        wprintf(L"Class: CSSimpleObject::ToString => \"%s\"\n", szStr);
    }
    else
    {
        wprintf(L"CSSimpleObject::ToString failed w/hr 0x%08lx\n", hr);
    }

    // You cannot unload the .NET assembly. 
    // Check whether or not the module is loaded.
    fLoaded = IsModuleLoaded(pszModuleName);
    wprintf(L"Module \"%s\" is %sloaded\n", pszModuleName, fLoaded ? L"" : L"not ");

    return 0;
}

Lecz problem właściwie jest ten sam. W definicji CS obiektu i w pliku nagłówkowym tam wrappują wszystko co się da. Czy to jest konieczne ? Nie potrzebuję dostępu do środka konstruktora, tylko go potrzebuję wywołać. Zacząłem coś czytać na temat regasm. Mam już te dllki w tlb. Nie wiem czy czasem nie czytam dwóch różnych rzeczy : )
Będę wdzięczny za podpowiedzi ! : )

pozdrawiam,
D.

P.S - jeszcze dodam, że ten "interfejs", który chciałbym zrobić, musiałby być w C - czyli muszę użyć jeszcze extern "C". Mam nadzieję, że to nie problem ? A potem to skompilować jako DLL.

0

Wbrew temu co piszesz, to co chcesz i musisz zrobić to właśnie wrapper w C++/CLI. Wygugluj jakiś podstawowy tutorial o tym języku, zobaczysz w jaki sposób możesz korzystać z .NETowych obiektów (te z daszkami).

Ja napisałem mały projekcik. Myślę, że się połapiesz o co biega.

0

O super : ) Już lookam. Wrapper wrapperem, ale przypuśćmy - że nie mam dostępu do plików .cs (jeśli dobrze pamiętam), tzn. tych z source codem z klasami, tylko do DLL. Co wtedy ? : >

Już zaczaiłem. Tylko muszę ogarnąć chyba od programisty informacje o tym, co jest namespace'm, jakie są nazwy klasy, bo nie jestem biegły w tej składni .NET'a.

Dziękuję za przykład !

0

W Visual Studio masz okno Object Browser. Tam możesz sobie wczytać zarządzaną DLL i wszystko ci się pokaże jak na tacy.

0

O : ) To przydatne.

Ok, czyli jak mam coś takiego :

public SHSensorComplete(bool bGenerateComponents)
    Member of FSLib.SHSensorComplete 

To co jest czym, żeby się upewnić ?

Jeśli odbieram to dobrze, to Assembly to będzie FSLib, type to będzie FSLib.SHSensorComplete a method to SHSensorComplete ?

A pytanie - jak dotrzeć do parametrów ? Jeśli są public, to zwyczajnie przez Type.parametr ?

D.

P.S A konstruktory też można w taki sposób wywoływać ?

Assembly^ asm_fslib = Assembly::LoadFrom("FSLib.dll");
	Assembly^ asm_fslib4serialize = Assembly::LoadFrom("FSLib4Serialize.dll");
	Type^  FSLib.SHSensorComplete = asm_fslib->GetType("FSLib.SHSensorComplete");
	MethodInfo^ SHSensorComplete = FSLib.SHSensorComplete->GetMethod("SHSensorComplete");
	FSLib.SHSensorComplete* wfs;
	array<Object^>^ params = { number1, number2 };
	SHSensorComplete->Invoke(wfs, true); 

P.S 2

A jeśli wiem, że jedna z metod zwraca boolean, to mogę pominąć linijkę GetType ? Czy jakoś trzeba pokazać, że to ten typ ?

public static bool LoadSensorConfiguration(ref FSLib.SHSensorComplete Sensor, string FileName)
    Member of OKOTech.Kryukov.Serializer 
0

Jeżeli masz coś takiego to lepiej użyć metody "na referencję w projekcie". Nie musisz się bawić w refleksję.

Namespace::Klasa obiekt^ = gcnew Namespace::Klasa(argument);
obiekt->parametr = 5;
obiekt->metoda(parametr);
0

Nie jestem pewny czy cały czas rozmawiamy o tym samym, więc musisz trochę więcej info mi dać ; D Ten zapis z ostatniego postu jest dla mnie bardzo prosty i zrozumiały - więc dobrze byłoby go użyć : D Ale czy wciąż potrzebuję

Assembly^ asm_fslib4serialize = Assembly::LoadFrom("FSLib4Serialize.dll"); 

getType itd ?

Dzięki za pomoc : )
D.

0

Jeżeli dołączysz referencję w ustawieniach projektu to nie musisz.

0

Hm, ale czy w referencjach projektu nie ustawia się statycznych bibliotek ? Tzn. .lib a nie .dll ? Czy można również ? : )

0

w referencjach projektu ustawia się zarządzane DLL-ki.

0

A czy jak nie tworzę obiektu danej klasy, tylko po prostu wołam metodę to mogę napisać tylko :

 res = FSLib4Serialize::OKOTech.Kryukov::Serializer::LoadSensorConfiguration(wfs,"sample.ssc");

FSLib4Serialize to Namespace. Reszta to tak jak pisałem :

 public static bool LoadSensorConfiguration(ref FSLib.SHSensorComplete Sensor, string FileName)
    Member of OKOTech.Kryukov.Serializer
0

Możesz, ponieważ jest to metoda statyczna.
Powinno być raczej L"sample.ssc", chociaż kompilator w większości przypadków przepuści też stringa bez prefiksu.

0

DLLki załadować w Additional Dependencies ? Bo jak ładuję, to mówi, że nie rozpoznaje Namespace'ów.

0

nie, DLL-ki załadować w „References” pod prawym klawiszem w Solution Explorerze. Albo we właściwościach projektu, zakładka Framework and References.

0

Tak zrobiłem - nie zauważyłem tego. Dzięki : )

Ale i tak, po małym kodzie nawet :

 
void main() {
FSLib::SHSensorComplete wfs^ = gcnew FSLib::SHSensorComplete(true);
}

Syntax mówi, że nie mogę. Pewnie coś w ustawieniach. A czy te .NETowe DLLki muszą być jakoś specjalnie skompilowane ?

Error 1 error C2653: 'FSLib' : is not a class or namespace name

Error 2 error C2065: 'SHSensorComplete' : undeclared identifier
Error 3 error C2146: syntax error : missing ';' before identifier 'wfs'

Ok, jeśli miało być tak :

 
void main() {
FSLib::SHSensorComplete^ wfs = gcnew FSLib::SHSensorComplete(true);
}

to działa : )

A mam problem. Jeśli mam :

public static System.Boolean LoadSensorConfiguration(FSLib.SHSensorComplete Sensor, System.String FileName)
    Member of O.Kry.Serializer 

Ale - O.Kry to jest nazwa. tzn kropka nie rozdziela klas. I tutaj się VS pluje, że nie ma czegoś takiego... Sugestie ? : >

0
dziech napisał(a)

A czy te .NETowe DLLki muszą być jakoś specjalnie skompilowane ?

Wystarczy, że klasa będzie publiczna.

Wrzuć może swój projekt, bo w teorii już wszystko powinno działać w porządku.

Projekt, a nie kod.

0

Jasne ; ) No tak. W sumie nie wiele się projekt różni ; P To tylko jeden .cpp. W ustawieniach dodałem tylko .NET + że ma zrobić DLL + __declspec(dllexport).

Jutro wrzucę, bo mam tutaj bardzo wolne łącze ; )

Do jutra ! : )

0

Rzuć jeszcze tymi dllkami, żeby dało się to skompilować.

0

Dobra, otworzyłem sobie projekt w Visual Studio 11 Developer Preview, żeby Intellisense działało. Dllki w referencjach są dobrze dodane, to w kodzie jest niesamowity bajzel.

res = OKOTech::Kryukov::Serializer::LoadSensorConfiguration(wfs, L"sample.ssc"); // było "OKOTech.Kryukov"
FSLib::DllCaptureDevice^ dev = gcnew FSLib::DllCaptureDevice(); // dlaczego chcesz rzutować DllCaptureDevice na SHSensorComplete?
																	// nie dziedziczy po nim, ani nie ma zdefiniowanego operatora rzutowania
if (dev->LoadDll("ueye_dll.dll") != 1) // było "if dev->LoadDll("ueye_dll.dll")!=1" ify w C++ jednak muszą mieć ( i ) ;)
for (int i = 0; i < 10; i++) // było int = 0
FSLib::GrayImage^ surf = gcnew FSLib::GrayImage(); // było FSLib::GrayImage surf^ daszek występuje po typie od razu
surf = wfs->m_WFSurface->GetManagedPixelData();

GetManagedPixelData zwraca tablicę doubli, a nie obiekt GrayImage.

Potem jeszcze korzystasz z jakiś niezadeklarowanych zmiennych.

Polecam spędzić trochę czasu nad C++/CLI i samą dokumentacją biblioteki (ew. object browser / ilspy).
Wiele by ci pomogło, gdybyś mógł korzystać z IntelliSense, w VS2010 niestety dla C++/CLI nie działa, trzeba użyć VS2008 albo VS11DP.

0

Pomijając błędy if() itd. - może to z pośpiechu. Jak pisałem :

res = OKOTech::Kryukov::Serializer::LoadSensorConfiguration(wfs, L"sample.ssc"); // było "OKOTech.Kryukov" 

Object Browser pokazuje OKOTech.Kryukov, bo to jest jako nazwa, a nie klasa OKOTech i w niej Kryukov. Chyba, że się mylę.

Mylę się; P Bo śmiga.

Okej - m_WFBasis->GetCoefficients(); w FSLib::SHSensorComplete... Znalazłem. Nie działało - bo w matlabowym skrypcie było bez (); . Dopisałem i działa. Hm, dostałem od programisty spis typów :

wfs : FSLib.SHSensorComplete
res : Int16
dev : FSLib.DllCaptureDevice
surf : FSLib.GrayImage
nx_surf, ny_surf : int
zern : array<double>^ (managed double vector)
zern_n : unsigned int
zer_vec : floating point vector created in Matlab

0

Nie ma nazw klas z kropkami. To namespace'y, oddzielane w C++ ::. Zawsze tak było.

0

Tak, właśnie edytowałem posta jak napisałeś : ) I dopisałem co nieco.

0

Dzięki za przybliżenie tematu ; ) Faktycznie byłoby prościej, gdyby VS, tak jak z C++ mówił gdzie jaki składnik leży. Bo faktycznie trochę się pogubiłem w tych klasach.

Teraz rzuca tylko przy tym :

int nx_surf, ny_surf;
	array<double>^ surf;
	array<double>^ wf;
    surf = wfs->m_WFSurface->GetManagedPixelData();
    nx_surf = wfs->m_WFSurface->GetWidth();
    ny_surf = wfs->m_WFSurface->GetHeight();

	

    for(int i=0;i<nx_surf;i++){
        for(int j=0;i<ny_surf;i++){
            wf[j][i] = surf[(ny_surf-j)*nx_surf+i];  // o tą linijkę
		}}
     

Error 1 error C2109: subscript requires array or pointer type

0

Do niezainicjowanej tablicy jednowymiarowej próbujesz przypisywać elementy w dwóch wymiarach ;).
http://www.functionx.com/cppcli/index.htm
Wykuj na blachę ten kurs i dopiero zabierz się za programowanie w języku, którego nie znasz.

0

To rozumiem, ale np. matlab nie ma z tym problemów ; ) Zapewne musiałbym rozłożyć tą tablicę wf na dwa razy. Pokminię : )

 for i=1:nx_surf
        for j=1:ny_surf
            wf(j,i) = surf((ny_surf-j)*nx_surf+i);
        end;
    end;

W każdym razie - dzięki za stronę. Na pewno zerknę : ) Nie natrafiłem na tą jeszcze w poszukiwaniach.

1 użytkowników online, w tym zalogowanych: 0, gości: 1