Конференция ".Net" » Подключение функций из внешних DLL [C#, WinXP]
 
  • Desdechado © (01.06.06 21:02) [0]
    Есть DLL, которая писана на delphi 7, и ее исходники.
    Там идет работа со строками, с памятью.
    Если функции из DLL возвращают PСhar на выделенную (кстати, чем надо выделять для совместимости с NET) память, то где и как надо (и надо ли) освобождать эту память?
    Какие соглашения о вызовах прописывать в экспортируемых функциях (stdcall, cdecl, др.)?
  • Evgeny V © (02.06.06 08:50) [1]
    В MSDN по С# cм. CallingConvention для DllImportAttribute
    Пример из MSDN
    [DllImport("msvcrt.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)] - поддерживается и соглашение StdCall,ThisCall,Winapi

    Память выделить и удалить можно в вашей длл, одна функция выделяет память и возвращает указатель. Другая освобождает память. Ее конечно надо будет вызвать, как отпадет необходимость в в блоке памяти.
    Второй вариант - функции передается указатель на уже выделенную в NET коде память и размер, если памяти не хватает, возвращается соответствующий код ошибки.
  • Desdechado © (02.06.06 13:34) [2]
    спасибо
    2-й вариант меня больше устраивает

    Только вот чем выделять память в C#, чтоб она была совместима с менеджером памяти в DLL, передаваясь как PChar?
  • Evgeny V © (02.06.06 14:28) [3]
    Просто массив например, правда при преобразовании в указатель надо использовать команду fixed что бы массив не перемещался в памяти,или явно используя стек, в этом случае gc не убирает блок памяти, а он освобождается при выходе из области видимости блока, в котором распределялась память. Запросить память из стека например
    char *name=stacalloc char [256]

    В MSDN есть примеры по словам stacalloc  и слову fixed, написано коротко и как раз для обоих вариантов:-)
  • Desdechado © (20.06.06 18:26) [4]
    Попробовал со stackalloc. С char не получилось, выкрутился с byte.
    Передается в DLL все нормально. Назад вроде тоже должно возвращать в выделенную память, но что-то не получается.
    Перед выходом из DLL поставил отладочную печать - показывает верно записанные (в выделенную извне память) данные. А в самом C# уже показывает, что начальный байт равен 0, хотя таким быть не должен.
    Вот пример кода вызова:
       [DllImport( "f32.dll", EntryPoint="s2lp", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall )]
       unsafe private static extern Int32 s2lp( Byte* l1, Byte* l2, Byte* p2 );

       unsafe private void button1_Click( object sender, EventArgs e )
       {
         const Int32 nBufLen = 80 + 1;
         Byte* cL1  = stackalloc Byte[ nBufLen ];
         Byte* cL2n = stackalloc Byte[ nBufLen ];
         Byte* cP2 = stackalloc Byte[ nBufLen ];
    ...
         if( s2lp( cL1, cL2, cP2 ) == 1 )
         {
           Int32 i = 0;
           while( *( cL2 + i ) != 0 )
           {
             textBox2.Text = textBox2.Text + ( *( cL2 + i ) ).ToString();
             i++;
           }


    И вот здесь в цикл не входит :(
    В дельфи функция описана как
    function s2lp( l1, l2, p2: PChar ): Cardinal; stdcall;



    В чем может быть затык?
  • Evgeny V © (21.06.06 10:26) [5]

    > С char не получилось, выкрутился с byte.


    Я просто не совсем правильно сказал, тип char в дельфи и  тип сhar  в с# - разные типы. Так что тип байт более близкий к char или sbyte.

    По коду - ( *( cL2 + i ) ).ToString(); - не даст вам букву А например, а даст ее код 61. Почему не входит в цикл -сказать трудно, если вы пишете, что в память действительно написано, что надо.... ???

    Вот еще варианты безопасного кода, в массив b возвращается строка из реестра, которая потом преобразуется в строку C#
    объявление всех переменных тут не привожу

    [DllImport("Advapi32.dll",SetLastError=true)]
     
    public  static extern IntPtr RegQueryValueEx(IntPtr key,string valueName,IntPtr Reserv,out uint lptype,ref byte lpdata,ref uint lpsize);

    byte [] b=new byte[1024];

    int l=b.Length;

    Decoder d=Encoding.Unicode.GetDecoder();

    int k1=RegQueryValueEx(k,s1,ref t,ref b[0],ref l);
    char[] chars=new char[l+1];
    d.GetChars(b,0,(int)l,chars,0);
    string s=new string(chars);
    //**********************
  • Desdechado © (21.06.06 13:14) [6]
    Нашел ошибку. В DLL просто присваивал pIn := PAnsiChar( str )
    а надо было копировать память.

    > По коду - ( *( cL2 + i ) ).ToString(); - не даст вам букву А например, а даст ее код 61
    Но как из байтов получить символы?
  • Evgeny V © (21.06.06 13:50) [7]
    См. класс Decoder, возможно может выглядеть так

    byte [] b - массив куда получили байты

    Decoder d=Encoding.ASCII.GetDecoder();

    char[] chars=new char[размер строки];
    d.GetChars(b,0,размер строки-1,chars,0);
    string s=new string(chars);

    или еще упростить

    string s=new string(d.GetChars(b));
  • Evgeny V © (21.06.06 14:17) [8]
    Вообще написал неверно [7], извиняюсь

    char [] chars;
    byte [] b1=new byte[3] {0x41,0x42,0};
    Encoding e1=Encoding.Unicode;

    // из кодовой страницы 1251 в юникод

    byte [] unicbyte=Encoding.Convert(Encoding.GetEncoding(1251),e1,b1);

    chars=new char[unicbyte.Length];
    e1.GetChars(unicbyte,0,unicbyte.Length,chars,0);
    MessageBox.Show(new string(chars),"Test");



    Вот пример написал, думаю переделать будет не сложно
  • Desdechado © (23.06.06 11:38) [9]
    спасибо, все получилось
  • _Ламер_ (13.10.06 23:19) [10]
    stdcall в DllImport по умолчанию, явно можно не вызывать.

    А вот как dll загружается в С# - статически или динамически? Было бы не плохо и так и этак.
  • Evgeny V © (16.10.06 13:58) [11]
    Судя по литературе (насколько я понял) - динамически. Загрузка происходит при первом обращении к длл. Да и из поведения программы это следует. Можно указать несуществующую длл, но ошибка возникнет только при обращении к функции длл.
  • _Ламер_ (16.10.06 22:32) [12]
    А в юникод обязательно переводить? А то у меня база в win1251, dll туда же.
  • _Ламер_ (21.10.06 21:31) [13]
    А как передать в dll число???
    В делфях

    MyProc (var LLength : byte);

    в С#

    byte PBuffer = 20;

    ....

    MyProc (PBuffer);




    При трассировке dll значение PBuffer не передаётся. Делфя пишет IV.
  • _Ламер_ (22.10.06 20:57) [14]
    Что-то я так и не понял, как из byte* получить string без всяких перекодировок.  Encoding.Convert последним параметром просит byte[], а у нас byte*. ToString выдаёт byte. Опять не совместимость.
  • _Ламер_ (30.10.06 19:31) [15]
    чёрт, не выходит

    На делфях



    var
    edLogin, edPassword   : WideString;

    .....

    procedure PostInfo (aLogin, aPassword : PWideChar; var LLength, PLength : byte); stdcall;
    begin
    if (aLogin = nil) or (aPassword = nil) then exit;
    LLength := Length (edLogin);
    Move (edLogin[1], aLogin[0], LLength);
    PLength := Length (edPassword);
    Move (edPassword[1], aPassword[0], PLength);
    MessageBoxW (0, PWideChar (edLogin), PWideChar (edPassword), MB_OK);
    MessageBoxW (0, aLogin, aPassword, MB_OK);
    end;



    на C#


           [DllImport("bin/OJRes", CallingConvention = CallingConvention.StdCall)]
           
           unsafe private static extern void PostInfo(    
    char* Login,    
    char* Password,    
               ref byte LLength,    
               ref byte PLength);    

     unsafe public static void postinfo(string Login, string Password)
           {
               byte LLength = 20;
               byte PLength = 20;

               const Int32 b = 100;

               char* aLogin = stackalloc char[ b ];
               char* aPassword = stackalloc char[ b ];

               try
               {
                   PostInfo(aLogin, aPassword, ref LLength, ref PLength);
               }

               catch
               {
                   //MessageBox.Show("Error", "Test");
               }
               

               Main_Form.Login = new string(aLogin, 0, LLength);
               Main_Form.Password = new string(aPassword, 0, PLength);
           }



    Первый MessageBox показывает всё правильно, второй - ровно половину. Т.е. если ввести 1234567890, то будет 12345 (остальное \0). Где теряется? Может память не так выделяется?
  • _Ламер_ (30.10.06 20:13) [16]
    Дико извиняюсь, но проблема не в net.

    Если не лень, удалите [13]-[16].
 
Конференция ".Net" » Подключение функций из внешних DLL [C#, WinXP]
Есть новые Нет новых   [134430   +1][b:0][p:0.005]