webForumDet fria alternativet

Omvandla System::String till char[].

15 svar · 660 visningar · startad av Robban

RobbanMedlem sedan dec. 19992 555 inlägg
#1

Håller fortfarande på med att skriva C++ i .NET mot ett API utanför .NET, och nu har jag råkat på nästa problem. Även här så tror jag att jag har en lösning, men den är långtifrån snygg. Måste gå att göra det bättre.

Försöker omvandla en System::String till char[256] (det är APIet som skall matas med den senare). Lyckas inte "casta" hur jag än försöker, så jag har fått ta till en nödlösning där jag loopar igenom strängen bokstav för bokstav och skriver den till min char[].

Såhär kunde jag göra med C++ MFC:

mniiFile.fileName : char[256]
mstrFileName : CString

strcpy(mniiFile.fileName, mstrFileName);

Men i managed C++ har jag inte hittat någon bra lösning.

mniiFile.fileName : char[256]
mstrFileName : System::String

char* pChars = (char*)Marshal::StringToHGlobalAnsi(mstrFileName).ToPointer();
int i = 0;
while (((*(pChars + i)) != 0) && (i < 255))
{
    mniiFile.fileName[i] = *(pChars + i);
    i++;
}
mniiFile.fileName[i] = 0;
Marshal::FreeHGlobal(pChars);

(Har inte testkört den än, så den kan mycket väl vara helt uppåt väggarna).

PeWMedlem sedan juni 200010 432 inlägg
#2

Den sista koden ser iaf korrekt ut.

Lyckas inte "casta" hur jag än försöker

Hur har du försökt?

developerMedlem sedan aug. 2001458 inlägg
#3

Med "ett API utanför .NET" menar du väl en unmanaged DLL? I den DLL:en finns en funktion som du vill anropa. Hur ser den funktionen ut?

Jag är inte säker på att jag förstår ditt problem, men jag tror du gör mer jobb än vad du behöver. Ta exempelvis MessageBoxA i user32.dll som exempel. Den tar ju char* för strängarna. Motsvarande Unicode-varian, MessageBoxW, tar en wchar_t*. Dessa kan du använda såhär:

namespace User32
{
	[DllImport("User32.dll", EntryPoint="MessageBoxW", ExactSpelling=true, CharSet=Charset::Unicode)]
	int MsgW(IntPtr hWnd, String* lpText, String* lpCaption, UInt32 uType);

	[DllImport("User32.dll", EntryPoint="MessageBoxA", ExactSpelling=true, CharSet=Charset::Ansi)]
	int MsgA(IntPtr hWnd, String* lpText, String* lpCaption, UInt32 uType);
}

int _tmain(void)
{
	System::String* s = "text";
	User32::MsgA(IntPtr::Zero, s, "Ansi", 0);
	User32::MsgW(IntPtr::Zero, s, "Unicode", 0);
}

Kan du göra på liknande sätt, eller har jag missuppfattat något?

RobbanMedlem sedan dec. 19992 555 inlägg
#4

PeW:

Hur har du försökt?

Har dels provat att omvandla System::String till CString.

strcpy(mniiFile.fileName, (CString)mstrFileName);

Omvandlingen till CString verkar fungera, men det skiter sig ändå med följande felmeddelande:

error C2664: 'strcpy' : cannot convert parameter 1 from 'char [256]' to 'char *'

Tror det är jag som inte riktigt förstår det här med pekare, och hur en char[256] förhåller sig till en char*. Går det att omvandla en char* till en char[256] ö.h.t.?

En annan metod jag försökte var att använda System::String::CopyTo

mstrFileName->CopyTo(0,mniiFile.fileName,0,256);

Problemet är att CopyTo vill ha en System::Char [] och jag vet inte hur jag skall göra där.

developer:

Med "ett API utanför .NET" menar du väl en unmanaged DLL? I den DLL:en finns en funktion som du vill anropa. Hur ser den funktionen ut?

Jag jobbar mot NeroAPI (CD-brännarprogrammet). mniiFile är en struct som ser ut som följer:

typedef struct tag_NERO_ISO_ITEM
{
char fileName[256];
BOOL isDirectory;
BOOL isReference;
char sourceFilePath[256];
struct tag_NERO_ISO_ITEM *subDirFirstItem;
struct tag_NERO_ISO_ITEM *nextItem;
void *userData;
long dataStartSec;
__int64 dataLength;
struct tm entryTime;
int itemSize;
struct CImportInfo *importinfo;
} NERO_ISO_ITEM;

Det jag försöker göra är att fylla fileName från en System::String.

NeroAPI har inget COM interface, utan det är bara ett rent API.

DLLimport har jag missat. Skall ta en titt på det, men jag tror inte det löser just detta problem. Jag tror som sagt att det grunläggande problemet är min ovana att jobba med pekare. Hade mniiFile.fileName varit en char* så hade jag inte haft några problem, utan problemet ligger i hur jag får in en char* i en char[256]. :)

developerMedlem sedan aug. 2001458 inlägg
#5

Du behöver inte sköta den kopieringen själv, låt interop hantera det åt dig istället. Det har säker smygit sig in några småfel, men ungefär såhär borde du kunna göra:

[StructLayout(LayoutKind::Sequential, CharSet=Ansi)]
__gc class NeroIsoItem
{
public:
	[MarshalAs(UnmanagedType::ByValTStr, SizeConst=256)]
	String* m_fileName;

	BOOL m_isDirectory;
	BOOL m_isReference;

	[MarshalAs(UnmanagedType::ByValTStr, SizeConst=256)]
	String* m_sourceFilePath;

	NeroIsoItem *_subDirFirstItem;
	NeroIsoItem *_nextItem;

	void *_userData;
	long m_dataStartSec;
	__int64 m_dataLength;
	struct tm m_entryTime;
	int m_itemSize;
//	ImportInfo *importinfo;
};
developerMedlem sedan aug. 2001458 inlägg
#6

just det... Jag började döpa om variablerna till m_ för att jag tänkte göra dessa private och lägga till en konstructor där man tvingas sätta allt istället och properties för det man behöver läsa/skriva, men orkade inte göra klart det.

RobbanMedlem sedan dec. 19992 555 inlägg
#7

Nu börjar det bli överkurs för mig märker jag. :)

Så jag definierar mniiFile som ett NeroIsoItem-objekt då istället? Men kommer NeroAPI att godta den (NeroAPI vill ju ha en NERO_ISO_ITEM-struktur)?

PeWMedlem sedan juni 200010 432 inlägg
#8

Robban skrev:

Omvandlingen till CString verkar fungera, men det skiter sig ändå med följande felmeddelande:

error C2664: 'strcpy' : cannot convert parameter 1 from 'char [256]' to 'char *'

Tror det är jag som inte riktigt förstår det här med pekare, och hur en char[256] förhåller sig till en char*. Går det att omvandla en char* till en char[256] ö.h.t.?

Från en char[] får du en adress. Från en *ptr får du det som finns på adressen som ptr har, efter en tilldelning:

char *ptr;
char string[];    
ptr=string;         //är detsamma som ptr=&string;
...
...

Och *ptr ger då det första elementet i string[].

En sak som ibland strular till liknande operationer är om man deklarerar en char-array i en funktion och sen returnerar en pekare till arrayen. Det som strular då är att arrayen dör i och med att funktionen gått ur sitt kodblock och pekaren som man trodde innehöll en array pekar på odefinierat minne.

Din error kod tolkar jag att du tagit det som pekaren pekar på för att kopiera in med strcpy, när du skulle istället tagit adressen. Typ:

char *ptr;
char string[13] = "kalleskaviar";
strcpy(ptr, string);

Det man även bör tänka på med ovan exempel är att ptr bör vara allokerad för samma storlek som strängen man vill kopiera in.

I övrigt verkar det som att developer löst ditt problem. ;)

RobbanMedlem sedan dec. 19992 555 inlägg
#9

(NeroAPI vill ju ha en NERO_ISO_ITEM-struktur)

... och det är det jag fixar med attributet DllIport antar jag? :)

Skall testa. Tack så länge.

RobbanMedlem sedan dec. 19992 555 inlägg
#10

PeW:

Det skumma är ju att det fungerade att lägga in en CString i en char[] i unmanaged C++. Men försökte jag med samma sak (genom att först "casta" System::String till CString) i managed C++ så gick det inte.

Det där med pekare dröjer det nog innan man får kläm på, käns det som. :)

developerMedlem sedan aug. 2001458 inlägg
#11

Robban skrev:

(NeroAPI vill ju ha en NERO_ISO_ITEM-struktur)

... och det är det jag fixar med attributet DllIport antar jag? :)

Ungefär. Lite off-topic, men det kan vara värt att tänka på ändå... När du anropar en funktion i nero-api skickar du med en pekare till en NERO_ISO_ITEM-struct. I och med att detta är unmanaged kod finns ingen form av typ-information i datat som överför i funktionsanropet, utan det är bara ett antal Bytes i minnet. Funktionen i nero-api:t förväntar sig att strukturen du skickar är av typen NERO_ISO_ITEM och behandlar minnesytan som en sådan struktur. (Det är så att säga en konvention mellan den som anropar och den som tar emot som gör att det fungerar.)

Robban skrev:

Det skumma är ju att det fungerade att lägga in en CString i en char[] i unmanaged C++. Men försökte jag med samma sak (genom att först "casta" System::String till CString) i managed C++ så gick det inte.

Att du kan flytta enkelt mellan en char[] och en CString i unmanaged kod är vad man förväntar sig. Men när du försöker casta System::String till CString finns ett par problem inblandade. För det första är System::String managed, och du vet inget om hur den fungerar när det gäller minnesutnyttjande. Det finns ingen garanti på var i minnet den ligger placerad. Till exempel kan garbage collectorn flytta var i minnet en System::String ligger när som helst när den defragmenterar minnet (vilket händer hela tiden). Du kan alltså inte förvänta dig att komma åt den så enkelt som en CString. Vidare är System::String representerad i Unicode (varje tecken är 1 Byte), medan din CString ligger i ANSI (varje tecken är 2 Bytes). Man måste således göra en konvertering från unicode till ansi.

Det finns flera sätt att lösa detta på. Det jag föreslog är att låta .Net runtimen sköta allt åt dig. Du säger att structen har en System::String* som är 256 tecken stor, och att CharSet är ANSI. Sedan får .net interop lösa alla problem åt dig.

Du kan även göra som du börjat. Isåfall skulle jag nog göra typ såhär:

WCHAR unicodeBuf[256] = {0};
for (int i=0; i < s->Length; ++i)
  unicodeBuf[i] = s[i];
WideCharToMultiByte(...) // från unicodeBuf till din struct
RobbanMedlem sedan dec. 19992 555 inlägg
#12

Mmm, finns nog en del att lära när det gäller managed C++ märker jag. Du har inte några länkar där man kan läsa mer (specifikationen för "managed C++ extensions" har jag hittat)? Eller någon bra bok att rekommendera. :)

developerMedlem sedan aug. 2001458 inlägg
#13

En bra bok om .Net framework är: http://www.wintellect.com/about/instructors/richter/app_net.aspx som dock handlar om .Net framework och inte C#, managed C++ eller något annat språk. Dock är förståelse för frameworket såväl som vanlig C++ nödvändig för att förstå managed c++.

Flera av artiklar om managed C++ här: http://www.codeproject.com/managedcpp/

http://msdn.microsoft.com/msdnmag/issues/02/02/ManagedC/default.aspx
http://msdn.microsoft.com/msdnmag/issues/01/07/vsnet/default.aspx

MSDN referens: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netstart/html/cpcsvbvcjscript_managed_c__.asp

RobbanMedlem sedan dec. 19992 555 inlägg
#14

Tackar! :)

Överväger f.ö. att släppa managed C++ för nu. Dina tips om interop fick mig att förstå att det går att komma åt NeroAPI direkt från C# (det handlade om ett C#-projekt från början, men jag trodde att jag var tvungen att gå via managed C++ för att komma åt NeroAPI -- men det går ju lika bra att använda interop från C# visar det sig).

Tack för all hjälp! Jag är mycket klokare nu än innan. :)

developerMedlem sedan aug. 2001458 inlägg
#15

Bara kul att kunna hjälpa till. Jag tror det är en bra idé att använda C# och interop istället.

RobbanMedlem sedan dec. 19992 555 inlägg
#16

Fungerade utmärkt med C# och interop tills jag stötte på en C++ klass med medlemsfunktioner i NeroAPI. Man kan bara använda ImportDll på statiska funktioner (vad jag förstår). Inte på en medlemsfunktion. Back to the drawingboard, alltså. Och tillbaka till managed C++. :)

145 ms totalt · 3 externa anrop · v20260731065814-full.30151723
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
143 ms — hämta tråd, inlägg och bilagor (db)