[ vujkev @ 30.05.2010. 22:46 ] @
potrebno je da napravim export jedne funkcije koja izgleda ovako u C++-u
Code (cpp):

extern "C" __declspec(dllexport) BOOL UserInstruction(
HWND hWnd,
HINSTANCE hInst,
double *nArray,
LPSTR lpData1,
LPSTR lpData2 )
{
...
}
 


našao sam na http://www.codeproject.com/KB/dotnet/DllExport.aspx kako da eksportujem funkciju pravljenu u .NET-u, ali ovo rešenje radi delimično.
Funkcija koju sam napravio u .net-u izgleda ovako
Code (vbnet):

Public Class Class1
    <ExportDllAttribute.ExportDll("UserInstruction", Runtime.InteropServices.CallingConvention.StdCall)> _
    Public Shared Function UserInstruction() As Boolean
        MsgBox("fs")
        Return True
    End Function
End Class
 

i radi kako treba, tj. program koji poziva ovu funkciju prikaže msgbox. Problem nastaje kad ubacim parametere u ovu funkciju jer tad program koji je poziva puca. Da li neko može da mi kaže kako da definišem parametre ove funkcije kako bi sve proradilo i sa paramterima. Da li je ovo uopšte moguće sa .net bibliotekama ili ne?

primer jedne prodecure u C++ (uzeto iz uputstva kako napraviti funkciju)
Code (cpp):
LibMain() , WEP(),
#include "windows.h"
#include "math.h"
#define _WINDLL
//***********************************************************************
// USER DEFINED INSTRUCTION
BOOL far PASCAL UserInstruction( hWnd, hInst, lpNumeric, lpAlpha1,
lpAlpha2 )
HWND hWnd;
HINSTANCE hInst;
double far *lpNumeric;
char far *lpAlpha1;
char far *lpAlpha2;
{
double fZweiPi = 6.283185307;
*lpNumeric = sin( *lpNumeric * fZweiPi / 360. );
return(TRUE);
}


Pozdrav

[Ovu poruku je menjao mmix dana 31.05.2010. u 09:13 GMT+1]
[ mmix @ 31.05.2010. 08:12 ] @
Ni ne cudi me da ne radi, ovo je neka teska haxor budzevina na nivou asemblija, definitivno ti ne bih preoprcio da se sa ovim bakces.

Najbolji nacin da ovo uradis je da napravis unmanaged to managed C++/CLI wrapper. Dakle napravis DLL u C++ koji ka tebi exportuje native funkcije a interno poziva managed code iz referenciranog asemblija. C++/CLI ima podrsku i za konverziju tipova iz unmanaged u managed space i za tranziciju izmedju jednog i drugog. Druga opcija ti je da napravis i registrujes asembly kao COM i da posle koristis CoXXX iz C++a kad kad koristis bilo koji drugi COM.
[ Shadowed @ 31.05.2010. 10:38 ] @
Citat:
mmix: Najbolji nacin da ovo uradis je da napravis unmanaged to managed C++/CLI wrapper.

Ti si zapravo napravio ovo, tj. primer, samo ne mogu da se setim koja tema bese.
[ vujkev @ 31.05.2010. 10:49 ] @
inače eksperimentisanjem sam uspeo da napravim da ovo delimično radi

Code (vbnet):

 <ExportDllAttribute.ExportDll("UserInstruction", Runtime.InteropServices.CallingConvention.StdCall)> _
    Public Shared Function UserInstruction(ByRef hWnd As IntPtr, ByVal hInst As IntPtr,
                                                         ByVal num() As Double, ByVal str1 As String,
                                                         ByVal str2 As String) As Boolean
        MsgBox(String.Format("{0}:{1}:{2}:{3}:{4}:{5}", num.Length, num(0), str1, str2, hWnd, hInst))

 


problem je samo što dobijam da niz num() ima samo jedan član i što program koji poziva ovu prodecuru nema nikakvu povratnu informaciju šta je procedura uradila (procedura uvek mora da vrati true tako da ni taj podatak ne mogu da koristim).



[Ovu poruku je menjao vujkev dana 31.05.2010. u 12:08 GMT+1]
[ mmix @ 31.05.2010. 11:35 ] @
@vujkev, ne verujem da ces uspeti koliko god da eksperimentises, num ti je System.Array managed tip i posto .NET ne podrzava direktne exporte onda ne postoji ni implicitni wrapper/thunk oko metode koji bi marsalovao neki void* (ili double*) u System.Array. Mozes eventualno da se provuces sa nekim value tipovima i to je to. Mislim da gubis vreme, bolje pogledaj npr ovaj blog ili pogledaj moje projekte na donjem linku za primer.

@Shadowed,
http://www.elitesecurity.org/t312821-Koristenje-openCV

pa pri dnu.
[ vujkev @ 31.05.2010. 18:50 ] @
tvoj projekat deluje prilično jednostavno i nadam se da ću uspeti tako da napravim, ali imam mali problem sa definisanjem export funnkcije u wrapperu. Ne znam kako da prevedem parametre koje funkcija zahteva :(

u originalu to izgleda ovako
Code (cpp):
extern "C" __declspec(dllexport) BOOL UserInstruction(
HWND hWnd,
HINSTANCE hInst,
double *nArray,
LPSTR lpData1,
LPSTR lpData2 )
 

a ja sam došao do ovog dela
Code (cpp):

extern "C" __declspec(dllexport) bool UserInstruction()
 

i sad ne znam šta dalje :( Program javlja da funkcija UserInstruction nepostoji ukoliko postoji i "__stdcall", a bez toga puca verovatno zato što nisam definisao parametere. Pretpotavljam da parametri treba da budu iz System.Runtime.InteropServices.UnmanagedType, ali ne znam kako to da napišem.
[ mmix @ 31.05.2010. 21:23 ] @
A kako izgleda potpis managed metode koju ce wrapper da pozove? Tvoj wrapper jednostavno sluzi da marshaluje parametre iz jednog u drugi (ako implicitno ili deklarativno ne pomaze)

[ vujkev @ 31.05.2010. 21:40 ] @
to još nisam napravio, ali da kažemo da treba da pošalje samo tri parametra: niz num() as double i dva stringa. nadam se da mi to neće biti ovoliki problem
[ mmix @ 31.05.2010. 22:00 ] @
Pa jedno bez drugog ti ne vredi :) Nema poente pisati wrapper za nesto sto nemas (wrapper po definicji wrappuje nesto). Tek kad imas managed metod i njegovpotpis mozes da odredis koji native tipovi ce ti pokriti koje managed tipove i na osnovu toga napraviti native export funkciju.
[ vujkev @ 31.05.2010. 22:10 ] @
možda se nismo razumeli, moja native funkcija mora imati onakav potpis bez obzira šta imam u managed kodu. Da kažemo da mi iz native funkcije za sad treba samo niz i dva stringa koja ta funkcija dobija i to bi trebalo proslediti.
[ vujkev @ 02.06.2010. 21:21 ] @
Nikako da napravim da program ispravno poziva DLL pravljen u VS2010 pa sam zato sve izbacio i kompajlirao samo funkciju u Borland C++ kompajleru i proradilo je kako treba :( Šta to pogrešno radim kad program ne može da pozove funkciju koja se kompajlira u VS2010?

kod koji je kompajliram sa Borland C++ kompajlerom
Code (cpp):

// This is the main DLL file.
#include "../../include/windows.h"


extern "C" __declspec(dllexport) __stdcall BOOL UserInstruction(
    HWND hWnd,
    HINSTANCE hInst,
    double *nArray,
    LPSTR lpData1,
    LPSTR lpData2 )
{
   
    return (true);
}
 

i spisak exportovanih funkcija
Section contains the following exports for test.dll

00000000 characteristics
0 time date stamp Thu Jan 01 01:00:00 1970
0.00 version
1 ordinal base
2 number of functions
2 number of names

ordinal hint RVA name

1 0 00001238 UserInstruction
2 1 00009158 ___CPPdebugHook

Summary

3000 .data
1000 .edata
1000 .idata
1000 .reloc
1000 .rsrc
8000 .text
1000 .tls


kod koji kompajliram u VS2010
Code (cpp):
#include "stdafx.h"
#include <windows.h>

extern "C" __declspec(dllexport)  BOOL __stdcall UserInstruction(
    HWND hWnd,
    HINSTANCE hInst,
    double *nArray,
    LPSTR lpData1,
    LPSTR lpData2 )
{
    return (true);
}

i njegova lista exportovanih funkcija

Section contains the following exports for JobInfoWrapper.dll

00000000 characteristics
4C065568 time date stamp Wed Jun 02 14:58:16 2010
0.00 version
1 ordinal base
1 number of functions
1 number of names

ordinal hint RVA name

1 0 00001010 _UserInstruction@20 = _UserInstruction@20

Summary

1000 .data
A000 .rdata
1000 .reloc
1000 .rsrc
3000 .text


koliko vidim razlika je u "_" i "@20" koji se pojavljuje u nazivu exportovane funkcije iz VS-a. Mogu li nekako ovo ispraviti?
[ mmix @ 03.06.2010. 09:09 ] @
Tek mi sad nije bas najjasnije sta ti pokusavas.

Ko poziva koga? U cpp projektu kao build dobijas i dll a i lib fajl koji ti resava probleme, kako uopste ucitavas i koristis DLL?


OK, za pocetak da ti objasnim za ime, to nije decorated cpp ime funkcije to je modifikovano C ime zato sto koristis _stdcall u extern "C", samim tim VS menja ime na sebi standardan nacin da razlikuje ovo od default (cdecl) metode pozivanja. Bez ovog bi ti mogao iz C jezika da se linkujes direktno na UserInstruction i da napravis problem (jer pozivac podrzava samo cdecl a tebi je export stdcall), borland C++ ti doozvoljava da se zeznes, MS VC++ ne. Ako hoces da ti bas stoji "UserInstruction" moras da izbacis __stdcall.

Medjutim, ne moras ti uopste da ulazis u pricu sa extern "C" ako je tebi pozivac C++ aplikacija. Bez extern "C" export ce biti potpuno dekorisan na MS nacin:


ordinal hint RVA name

1 0 0001110E ?UserInstruction@@YGHPAUHWND__@@PAUHINSTANCE__@@PANPAD3@Z = @ILT+265(?UserInstruction@@YGHPAUHWND__@@PAUHINSTANCE__@@PANPAD3@Z)


ali uz DLL ide standardizovani LIB fajl koji svakom linkeru objasni o cemu se radu

Dump of file SampleLib.lib

File Type: LIBRARY

Exports

ordinal name

?UserInstruction@@YGHPAUHWND__@@PAUHINSTANCE__@@PANPAD3@Z (int __stdcall UserInstruction(struct HWND__ *,struct HINSTANCE__ *,double *,char *,char *))


[ vujkev @ 03.06.2010. 10:38 ] @
Aplikacija koja koristi ovaj DLL je pisana u ... ne znam kom jeziku i ne znam kako se povezuje na ovaj DLL jer je to gotov program koji se koristi već godinama :(
sve što ja treba da uradim u toj aplikaciji je da napišem
Citat:

userhook "putanja/nazivfajla.dll", <niz>, <string1>, <tring2>

i to treba da radi.
Prvi cpp kod kod koji sam dao (definicija funkcije) je uzeta iz HELP-a tog programa pa pretpostavljam da "C" treba. Sve jedno, probao sam sve moguće varijante (__stdcall, _cdecl, bez __stdcall i __cdecl, sa i bez "C") i iz VS-a ništa ne radi, a iz Borlanda prolazi samo onaj jedan slučaj.

[ mmix @ 03.06.2010. 10:53 ] @
Ok, znaci to je neki plugin sistem. Znaci ocekuje definitivno da imas stdcall i cdecl export ime. Tako reci

Ostavi u kodu extern c, i kreiraj .DEF fajl u C++ projektu (npr SampleLib.def) i ubaci ovo u njega

Code:

EXPORTS
    UserInstruction = _UserInstruction@20



u project properties/linker/input/modeule definition ubaci ime def fajla (ili ako rucno pozivas linker ubaci /DEF:"SampleLib.def")

To ce dodati jos jedan export entry u DLL koji ce mapirati na ovu funkciju.



ordinal hint RVA name

1 0 000110B4 UserInstruction = @ILT+175(_UserInstruction@20)
2 1 000110B4 _UserInstruction@20 = @ILT+175(_UserInstruction@20)

[ vujkev @ 06.06.2010. 13:41 ] @
Posle par dana eto slobodnog vremena i opet mene sa ovim problemom.
Odmah da kažem da ništa od predhodnog nije uspelo i caller ili prijavljuje da ne može da nađe funkciju ili prijavljuje fatalnu grešku sa porukom da se obratim podršci.
Međutim uspeo sam da ga nateram da radi i sa VS kompajlerom tako što sam kao projekat izabrao ne "CLR Class library" nego "WIN32 Project". Sa Win32 tipom projekta sve radi bez greške (barem grešku ne prijavljuje), ali ukoliko u koniguraciji Win32 projekta uključim CLR support caller počinje da prijavljuje grešku (samo uključim CLR supprot bez izmene koda)
Da li ovo znači da caller jednostavno ne može da radi sa dll-om kome je ukljčen CLR? Postoji li neki drugi način da povežem .NET aplikaciju sa programom?
[ mmix @ 06.06.2010. 14:19 ] @
Kad je ukljucen CLR switch jel imas native export odgovarajuceg imena kao u mojoj poruci?
[ vujkev @ 06.06.2010. 19:03 ] @
imena su odgovarajuća u oba slučaja. Evo ga export za obe verzije (prvi je sa CLR, a drui bez)
D:\My Documents\Visual Studio 2010\Projects\test\Debug>dumpbin test8.dll /export
s
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.


Dump of file test8.dll

File Type: DLL

Section contains the following exports for test8.dll

00000000 characteristics
4C0BE11B time date stamp Sun Jun 06 19:55:39 2010
0.00 version
1 ordinal base
2 number of functions
2 number of names

ordinal hint RVA name

1 0 00001030 UserInstruction = _UserInstruction@20
2 1 00001030 _UserInstruction@20 = _UserInstruction@20

Summary

1000 .data
9000 .rdata
1000 .reloc
1000 .rsrc
3000 .text


D:\My Documents\Visual Studio 2010\Projects\test\Debug>dumpbin test8.dll /export
s
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.


Dump of file test8.dll

File Type: DLL

Section contains the following exports for test8.dll

00000000 characteristics
4C0BE1B2 time date stamp Sun Jun 06 19:58:10 2010
0.00 version
1 ordinal base
2 number of functions
2 number of names

ordinal hint RVA name

1 0 000110B4 UserInstruction = @ILT+175(_UserInstruction@20)
2 1 000110B4 _UserInstruction@20 = @ILT+175(_UserInstruction@20)

Summary

1000 .data
1000 .idata
2000 .rdata
1000 .reloc
1000 .rsrc
4000 .text
10000 .textbss


program prijavljuje fatalnu grešku, a ne da ne može da nađe UserInstruction

[Ovu poruku je menjao vujkev dana 06.06.2010. u 20:32 GMT+1]
[ mmix @ 06.06.2010. 19:54 ] @
Jedina razlika je izmedju pointera export elementa, sa CLR to je relativni pointer, bez CLR je apsolutni, ali to je nesto sto GetProcAddress mora da uradi.

btw, vidim da su i na codeguru-u u zbunu sa tvojim pitanjem ;) Bar si dobio kod za marshalovanje paramtera od alexa.

Mislim da ovo ide dublje od export tabele, sve mi nekako izgleda da tvoja server aplikacija (koja ucitava DLL) zapravo ne koristi GetProcAddress, vec ucitava export tabelu manuelno i ne ume da parsira relativne pointere. Ili to ili ima problema u uspostavljanju managed contexta.

Cisto da eliminisemo potencijalni uzrok, na masini na kojoj testiras DLL imas instaliran framework? Da ne bude da puca na LoadLibrary (kad ucitas DLL koji je CLR, on podize mscorlib.dll i dize application domain oko cele aplikacije ako ga vec nema, ako nema .neta nema ni mscorlib.dll i puca LoadLibrary). Ako DLL pokreces na nekoj drugoj masini proveri.

[ vujkev @ 06.06.2010. 20:13 ] @
da, ima instaliran VS2010 RC. Da nije možda problem što je projekat pravljen za .NET 4.0? Probao sam da smanjim verziju na 3.5, ali mi traži da imam instaliran VS2008?!?!?!
[ mmix @ 06.06.2010. 20:41 ] @
Kad ti izbaci fatal error, jel ti bar da neki numericki kod greske?
[ mmix @ 07.06.2010. 15:08 ] @
Hmm, sto vise pratim razvoj situacije ovde, sve vise mi se cini da je greska u dizanju managed contexta.

Ajd malo vise podataka o toj aplikaciji koja poziva DLL, problem definitivno potice odatle? Iz nekog razloga CLR puca kad se podigne unutar njegovog procesa. Da nije neka java aplikacija koja poziva metod preko JNI?
Ako aplikacija uek puca kad se digne managed context, onda nam to namece jedino preostalo resenje.

Caller -> native CPP COM Client ----> COM object -> CCW (Interop) -> .net classw, ti pises bold stvari (ostalo resava .net/windows)

Za COM Interop imas informacije ovde (http://msdn.microsoft.com/en-us/library/6bw51z5z.aspx). U principu napravis managed DLL i exportujes klasu/interrfejs kao COM objekat, onda type library tog COM objekta koristis u pure unmanaged C++ aplikaciji. Sa tim sto pazi, ako ti ovo pukne sa in-process COM objektom, moraces da napravis out-of-process COM. sucks.


[ vujkev @ 07.06.2010. 16:38 ] @
Aplikacija je klasična Win, ako se dobro sećam nekog razgovora od pre par godina pisana je u Delphi-u.
Greška koju dobijam je
Citat:

99-0-1-04-0002:
A fatal error has occured. The application will terminate. Please contact PrintSoft Technical Support with the following exception information: Exception code=e0434352, Address = 7C812AFB.


Probaću da napišem ovo isto koristeći COM, a ako ne uspem znam kome da se obratim :)
[ vujkev @ 07.06.2010. 18:29 ] @
USPEO SAM, ali opet delimično :)

ovo je kod za .NET projekat. Ništa posebno, ali je uključena COM podrška
Code (vbnet):

Imports System.Runtime.InteropServices

<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface test
    <DispId(1)> _
    Sub Test(ByVal str1 As String, ByVal str2 As String)
End Interface

<ClassInterface(ClassInterfaceType.None)> _
Public Class Class1
    Implements test
    Public Sub test(ByVal str1 As String, ByVal str2 As String) Implements netlib.test.Test
        MsgBox(String.Format("str1:{0} - str2:{1}", str1, str2))
    End Sub
    Sub New()
    End Sub
End Class
 


CPP kod u običnom Win32 projektu
Code (cpp):

#include "stdafx.h"
#include "windows.h"
#import "../debug/netlib.tlb"   //tlb koji dobijem prilikom kreiranja .NET assembly-a
using namespace netlib;

extern "C" __declspec(dllexport)  bool  __stdcall UserInstruction(HWND hWnd,HINSTANCE hInst,double *nArray,LPSTR lpData1,LPSTR lpData2)
{    
    netlib::test *com_ptr;
    CoInitialize(NULL);
    netlib::testPtr p(__uuidof(Class1));
   
    p->_test( lpData1, lpData2 );
    return (true);
}
 


stringovi prolaze bez problema, ali dobijam grešku
Citat:

cannot convert parameter from 'double' to 'SAFEARRAY *'

ukoliko pokušam da pošaljem nArray. Na net-u ne nalazim kako da napravim ovu konverziju.

inače, gore napisani kod sam našao na netu i malo modifikovao :)

[Ovu poruku je menjao vujkev dana 07.06.2010. u 19:50 GMT+1]

[Ovu poruku je menjao mmix dana 07.06.2010. u 21:05 GMT+1]
[ deerbeer @ 07.06.2010. 18:49 ] @
Za LPSTR tip koristi StringBuilder ,
a za double* pokusaj sa MarshalAsAttribute (UnmanagedType.LPArray )
pa da double pointer bude prosledjen kao niz .

[ vujkev @ 07.06.2010. 19:33 ] @
Stringovi prolaze bez ikakve konverzije - samao prosledim parametar

MarshalAsAttribute (UnmanagedType.LPArray ) - zar ovo nije komanda iz managed koda? meni treba da se array iz native koda da se prebaci u managed kod
[ mmix @ 07.06.2010. 20:54 ] @
Nema ovde marshalovanja, ovo je unmanaged code. Ovo je konverzija iz native C++ parametara u COM-compatible parametre.

Znaci, treba ti kod da pretvori double* u safearray. Implicitna konverzija nije moguca, moras da uradis sledece:

1. ODredis koliko elemenata ima u nArray (pretpostavljam da je null terminated niz, pa ces morati da prebrojis "rucno")
2. Kreiras SAFEARRAY sa alociranim bufferom za podatke
3. Kopiras podatke iz tvog niza u buffer safearray-a


otprilike:

Code (cpp):

     // prebroj niz
     int i = 0; while(nArray[i]) i++;

     // kreiraj safe array sa alociranim blobom, VT_R8 je double (real sa 8 bajtova)
     SAFEARRAY* snArray = SafeArrayCreateVector(VT_R8, 0, i);

     // kopiraj niz u array buffer (prvo ga lockuj)
     void *pData;
     HRESULT hr = SafeArrayAccessData(snArray, (void **)&pData);
     memcpy(pData, nArray, i*8);
     SafeArrayUnaccessData(snArray);

     // u poziv saljes SAFEARRAY*
     p->_test( lpData1, lpData2, snArray );

     // OBAVEZNO: ti si kreirao safe array, kad ti vise ne treba ti moras da ga unistis inace mem leak.
     SafeArrayDestroy(snArray);
 
[ mmix @ 07.06.2010. 21:09 ] @
Btw, kod e0434352 je genericki kod za exception u CLRu, sto znaci da ti je managed context podignut ali da je managed metod bacio exception koji je unhandled unutar managed contexta i pobegao napolje (a posto je native kod nesvestan CLR exceptiona puca kao ovaj LastErrorCode). Ako hoces mozemo da uradimo par debug trikova da vidimo sta je izazvalo exception (pretpostalvjam da je neki los marshaling u pitanju). Mislim da ti je to mozda bolje nego da za svaki plugin pises poseban COM i poseban interop (plus sto je sporije).
[ vujkev @ 10.06.2010. 07:26 ] @
Sastavio sam kompletan kod i radi.
HVALA svima, ali imam još jedno pitanje :(
Ukoliko promenim vrednost nekog elementa nArray-a (nArray[1] = 54), ta vrednost se vraća pozivaocu. Da li to isto mogu da uradim sa stringovima i kako pošto lpData1 = "test" ne radi.

Što se tiče eksperimentisanja sa C++/CLI naravno da sam raspoložen. Reci samo šta da uradim i probaću

[ mmix @ 10.06.2010. 10:15 ] @
Tesko ces to. Morao bi da ga menjas na isti nacin na koji menjas nArray, tako sto menjas postojece memorijske lokacije koje zauzima string pozivaca. LPSTR je u osnovi char*, pointer na niz karaktera terminisan nulom. Tako da mozda bi mogao da ubacis "Perica\0" umesto "Slavomir\0" jer je krace, ali ne bi smeo da stavis "Slavomir\0" umesto "Perica\0" jer ces verovatno pregaziti neki drugi buffer, mozda cak i izazvati segfault. Kad se stringovi vracaju pozivacu u c/C++u to se radi preko char** i slicnih tako da je vrlo verovatno da ni pozivao nece registrovati da si mu promenio string jer to ne ocekuje.


Ajd sad da probamo sledece.

1. Napravi solution gde imas c++/CLI wrapper i drugi managed projekat koji se poziva iz c++-a, kao onaj projekat u kojem dobijas fatal error. Sve izbilduj u debug modu.
2. Prebaci VS debager da hvata exceptione pre procesiranja. TO radis tako sto u Debug meniju izaberes "Exceptions" i "Common Language Runtime Exceptions" prebacis na Thrown. (posle ti to vrati na off da te ne smara za druge projekte). Ovo znaci da ce VS da breakuje program na prvoj debugable liniji u kojoj nastane exception, u nasem slucaju pre nego sto ga manage/unmanaged thunk pretvori u fatal error.
3a. Treba da pokrenes userhook u debug modu, a kako to uraditi zavisi od toga sta je userhook? Ako je exe koji pozivas iz komandne linije onda ga stavi u debug folder C++ projekta gde je c++ DLL (tu prebaci managed dll i pdb, nek ti pdb uvek ide u pratnji). Nek ti C++ projekat bude startup projekat. Udji u properties od C++ projekta i lociraj Configuration Properties/Debugger. Promeni Command iz $(TargetPath) u $(TargetDir)\userhook.exe a u command arguments stavi ono sto bi stavio iza userhook komande. Debugger Type prebaci u "Managed Only" ili "Mixed". Pokreni projekat u debug modu (F5). VS ce pozvati userhook i zakaciti se za njega, on ce ucatati c++ dll koji ce ucitati maanged DLL koji ce puknuti.

Ako je to komanda koju ukucavas u konzolu neke aplikacije (te delphi) onda 3b:
3b. (ako nije komandna linija) Iz debug menija izaberi Attach to process i lociraj aplikaciju. Promeni attach to: sa "automatic" na "Managed" (i eventualno native ako hoces da single-stepujes kroz C++ deo). Attach. i posle iz aplikacije pokreni user hook da izazoves gresku

Ako je sve ok trebalo bi da ti breakuje u VSu kad nastupi managed exception.
[ vujkev @ 11.06.2010. 19:25 ] @
Samo Managed attach ne stopira izvršenje koda već sam morao da uključim i native attach. Evo spiska grešaka
Citat:

First-chance exception at 0x7c812afb in prsAFPDP.exe: Microsoft C++ exception: EEFileLoadException at memory location 0x0012d46c..
First-chance exception at 0x7c812afb in prsAFPDP.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000..
First-chance exception at 0x7c812afb in prsAFPDP.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000..
First-chance exception at 0x7c812afb in prsAFPDP.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000..
First-chance exception at 0x7c812afb in prsAFPDP.exe: Microsoft C++ exception: MalformedURLException at memory location 0x0012b7b4..
First-chance exception at 0x7c812afb in prsAFPDP.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000..
First-chance exception at 0x7c812afb in prsAFPDP.exe: Microsoft C++ exception: MalformedURLException at memory location 0x0012b5a0..
First-chance exception at 0x7c812afb in prsAFPDP.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000..
First-chance exception at 0x7c812afb in prsAFPDP.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000..
First-chance exception at 0x7c812afb in prsAFPDP.exe: Microsoft C++ exception: EndOfEntityException at memory location 0x0012b648..


prsafpdp.exe je aplikacija koja poziva userinstruction funkciju (nije komandna linija)
[ mmix @ 11.06.2010. 20:08 ] @
Hmm, kako izgleda struktura foldera. gde je prsafpdp.exe a gde su c++ i managed DLL?

Ako nisu u istom folderu probaj da managed dll prebacis tamo gde je EXE. Postoji fora sa lociranjem managed DLLa, CLR ga nece potraziti tamo gde je unmanaged DLL vec tamo gde je glavni EXE fajl (plus u GACu)
[ vujkev @ 13.06.2010. 00:10 ] @

fajlovi su bili na odvojenoj lokaciji, prebacio ih tamo gde je exe - ne radi; prebacio u system32 - ne radi; dodao .NET managed assembly u GAC i sad radi :)

kako sad da konvertujem double *nArray u array<double>^ d?? Ako je bitno možemo da kažemo da će array uvek imati 10 elemenata
[ vujkev @ 13.06.2010. 08:21 ] @
uspeo :)
Code (cpp):

extern "C" __declspec(dllexport)  bool  __stdcall UserInstruction(HWND hWnd,HINSTANCE hInst,double *nArray,LPSTR lpData1,LPSTR lpData2) {
     IntPtr hwnd(hWnd);
    IntPtr hinst(hInst);
     IntPtr arr(nArray);
     array<double>^ d = gcnew array<double>(5);
     Marshal::Copy(arr, d, 0, 5);
    String^ data1 = Marshal::PtrToStringAnsi(IntPtr(lpData1));
    String^ data2 = Marshal::PtrToStringAnsi(IntPtr(lpData2));

     NetTest::Class1 ^ t = gcnew NetTest::Class1();
     t->Test(hwnd, hinst, d, data1, data2);

     return (true);
}


sad mi ostaje samo da string parametri koje caller šalje budu ByRef tj. da se promene na stringovima u ovoj funkciji vide i u programu koji je poziva. Stringovi se šalju ovoj proceduri tako što se u pozivaocu prvo definiše buffer od npr 2000 karaktera i onda se u njega ubaci tekst terminisan nulom. Prema tome slobodno mogu da menjam string to veličine tog buffera, pitanje je samo kako. Da li u ovoj proceduri mogu da znam kolika je veličina buffera?
[ mmix @ 13.06.2010. 14:27 ] @
Prilicno prosto, postoji metod u marshal koji konvertuje CLR string u ansi char* string, samo ga treba posle iskopirati u tvoj buffer i osloboditi marshalovani string (managed delovi ce se sami ocistiti kroz GC). Evo primer (narano bez iakavih buffer overrun provera, koristi na svoju odgovornost

Code (vbnet):


Public Class Class1
    Public Sub Test(ByVal hWnd As IntPtr, ByVal hInst As IntPtr, ByVal nArray As Double(), ByRef lpData1 As String, ByRef lpData2 As String)
        MsgBox(lpData1 + "" + lpData2)
        lpData1 = "Milisav"
        lpData2 = "Zivorad"
        nArray(1) = 123
    End Sub

End Class


Code (cpp):

#include "stdafx.h"
#include "windows.h"

using namespace System;
using namespace System::Runtime::InteropServices;

extern "C" __declspec(dllexport)  bool  __stdcall UserInstruction(HWND hWnd,HINSTANCE hInst, double *nArray,LPSTR lpData1,LPSTR lpData2)
{    
     array<double>^ d = gcnew array<double>(5);
     Marshal::Copy(IntPtr(nArray), d, 0, 5);
     String^ data1 = Marshal::PtrToStringAnsi(IntPtr(lpData1));
     String^ data2 = Marshal::PtrToStringAnsi(IntPtr(lpData2));

     NetTest::Class1 ^ t = gcnew NetTest::Class1();
     t->Test(IntPtr(hWnd), IntPtr(hInst), d, data1, data2);

     // kopiraj rezultat, ovaj deo tebi treba
     Marshal::Copy(d, 0, IntPtr(nArray), 5);
     IntPtr d1 = Marshal::StringToHGlobalAnsi(data1);
     strcpy(lpData1, d1.ToPointer());
     IntPtr d2 = Marshal::StringToHGlobalAnsi(data2);
     strcpy(lpData2, d2.ToPointer());
     Marshal::FreeHGlobal(d1);
     Marshal::FreeHGlobal(d2);

     return (true);
}


int main(array<System::String ^> ^args)
{
     // imitiramo unmanaged pozivaoca
     char buff1[2000], buff2[2000];
     strcpy(buff1, "Pera");
     strcpy(buff2, "Mika");
     double nArr[] = {1,2,3,4,5};

     UserInstruction(NULL, NULL, nArr, buff1, buff2);

     Console::WriteLine(Marshal::PtrToStringAnsi(IntPtr(buff1)));
     Console::WriteLine(Marshal::PtrToStringAnsi(IntPtr(buff2)));
     for(int i=0; i<5; i++) Console::WriteLine(nArr[i]);
     Console::ReadLine();

     return 0;
}


Rezultat:
Milisav
Zivorad
1
123
3
4
5


EDIT: strcpy je primereniji za kopiranje char* stringova, isti trip samo dinamicki otkriva duzinu

[Ovu poruku je menjao mmix dana 13.06.2010. u 15:39 GMT+1]
[ mmix @ 13.06.2010. 15:54 ] @
I da, neodgovoreno pitanje, ne mozes dinamicki da otkrijes velicinu buffera pozivaoca, ili dokumentacija ili da kontaktiras autore. Zato npr COM ne dozvoljava type* konstrukcije vec radi samo sa SAFEARRAY koji u sebi vuce sve podatke neophodne za opis niza, slicna prica je i za System::Array u CLRu. Iz istih razloga kad pozivas unmanaged API iz C++a uvek ide i dodatni parametar kojim pozvanoj API funkciji kazujes koliki buffer si joj prosledio u type*.
[ vujkev @ 13.06.2010. 16:05 ] @
Hvala, takav kod sam manje više i ja sastavio koristeće odgovore sa codeguru foruma. Pošto mogu da kontrolišem sve parametre koji se šalju ovoj funkciji staviću uslov da u array-u koji prosleđujem prva dva parametra moraju da budu veličine buffera i problem rešen. Ako neko baš hoće da laže ... pa njihov problem pošto ovo neće raditi :)

HVALA još jednom