2009-09-09 16 views
7

Próbuję użyć dll C++ z natywnego programu. Śledzę wirtualnego scenariusza metoda jak wyjaśniono hereDelphi PChar do C++ const char *

Powiedzmy moja C podpis funkcja ++ ma postać

int Setup(const char* szIp, const char* szPort); 

i odpowiadająca podpis Delphi

function Setup(ip, port: PChar):Integer: virtual; cdecl; abstract; 

A gdzieś z programu delphi mogę zadzwonić pod numer

pObj.Setup('192.168.1.100', '97777'); 

C ontrol wchodzi do biblioteki dll, ale parametry formalne szIp i szPort otrzymują tylko pierwszy znak adresu IP i portu, który przekazałem z programu delphi.

Rozumiem, że ma to związek z zerowym kończeniem łańcucha znaków w delphi. Tak więc próbowałem również następujących.

var 
    pzIp, pzPort: PChar; 
    szIp, szPort: string; 

begin 
    szIp := '192.168.1.2'; 
    szPort := '9777'; 
    //initilize memory for pchar vars 
    GetMem(pzIp, Length(szIp)+1); 
    GetMem(pzPort, Length(szPort)+1); 
    //null terminate the strings 
    pzIp[Length(szIp)+1] := #0; 
    pzPort[Length(szPort)+1] := #0; 
    //copy strings to pchar 
    StrPCopy(pzIp, szIp); 
    StrPCopy(pzPort, szPort); 
end. 

To też nie działa. Kiedy i WritelnpzIp i pzPort dostaję dziwne wyniki.

Zapomniałem powiedzieć, wszystkie funkcje członka z biblioteki dll C++ są kompilowane z __stdcall i eksportowane prawidłowo

Odpowiedz

17

W Delphi 2010 (i Delphi 2009) typ "char" jest w rzeczywistości WIDEChar - czyli 16-bitowy. Więc kiedy wywołasz swoją funkcję C++, jeśli spodziewasz się, że CHAR ma szerokość 8 bitów (tak zwane "ANSI", a nie UNICODE), to źle interpretuje parametr wejściowy.

np. jeśli przełączysz ciąg znaków 'ABC' # 0 (Pokazuję jawnie terminator zerowy, ale jest to tylko niejawna część ciągu znaków w Delphi i nie ma potrzeby dodawania go) przekazuje wskaźnik do 8 bajt sekwencja, NOT 4 bajty!

Ale ponieważ 3 znaków twojej ciąg mieć tylko wartości kodu punktu 8-bitowe (w kategoriach Unicode, oznacza to, że co kodu C++ „widzi” to ciąg znaków, który wygląda następująco:

'A'#0'B'#0'C'#0#0#0 

Które wyjaśniałoby, dlaczego twój kod C++ wydaje się być otrzymujący tylko pierwszy znak ciągu - jest to pierwszy bajt # 0 tego pierwszego znaku i zakładając, że jest on terminatorem zerowym dla całego łańcucha.

albo trzeba modyfikować kodu C++ poprawnie odbierać wskaźniki do WideChar strun lub zmodyfikować podpisu funkcji w Delphi i konwertowanie ciągi do AnsiString w kodzie Delphi przed przejściu tych z C funkcji ++ :

poprawiona funkcja podpis:

function Setup(ip, port: PANSIChar):Integer: virtual; stdcall; abstract; 

i odpowiednie „Długa ręka” pokazujący przemianę ciągów NSIString przed wywołaniem funkcji - kompilator może dbać o to dla ciebie, ale może okazać się pomocne, aby było jasne, w kodzie, zamiast polegania na „kompilator magii”:

var 
    sIPAddress: ANSIString; 
    sPort: ANSIString; 
    begin 
    sIPAddress := '192.168.1.100'; 
    sPort  := '97777'; 

    pObj.Setup(sIPAddress, sPort); 

    // etc... 
+0

Dzięki za bardziej szczegółowe wyjaśnienie: moja odpowiedź była szybką myślą, która okazała się słuszna, ale zalecam to jako odpowiedź do zaakceptowania. – IanH

+0

Dzięki @Deltics, to zadziałało. Jednak niewielka zmiana wymagała rzucenia, aby program delphi kompilował się (pObj.Setup) (PAnsiChar (sIPAddress), PAnsiChar (sPort)); Specjalne podziękowania dla @IanH też – rptony

3

Jeśli dobrze rozumiem Twój prototyp funkcja powinna być stdcall również.

function Setup(ip, port: PChar):Integer: virtual; stdcall; abstract; 

ps. Ciągi Delphi są już zakończone znakiem NUL.

+0

hmm, to nie pracował. Ten sam wynik. Ale mogłem pozbyć się awarii naruszenia dostępu po powrocie funkcji C++. Tak, myślę, że robię postępy :) – rptony

+0

czas na zrzut do widoku montażowego :) –

+0

sprawił, że stdcall rozwiązał kilka innych problemów, +1 za wskazówkę – rptony

4

Czy char ma ten sam rozmiar w obu kompilatorach? Jeśli używasz D2009/D2010, char jest teraz 16-bitowy.

+0

tak, używam D2010. A mój kod C++ jest 32-bitowy. Więc rozmiar postaci powinien pasować? – rptony

+2

Niekoniecznie: BCB4 jest 32-bitowym kompilatorem C++, ale znak to bajt. Możesz przedwcześnie zakończyć swój łańcuch za pomocą zerowego znaku high-byte dla pierwszego znaku. Spróbuj zmienić powyższy kod testowy, aby zadeklarować szIP i szPort jako AnsiString zamiast łańcucha. – IanH

+0

do głosowania za wprowadzenie do odpowiedzi – rptony

Powiązane problemy