Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
Топик располагается на нескольких страницах: Ctrl  назад   1 [2] 3 4   вперед  Ctrl      все
 Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 10998
makhaon
включи range check,
Мы сейчас говорим о корректности обращении к символу строки за пределами ее длины или о физическом состоянии памяти?
makhaon
подсказка: UStrToPWChar.
function _UStrToPWChar(const S: UnicodeString): PWideChar;
begin
  if Pointer(S) = nil then
    Result := @(PEmptyString(@EmptyStringW[1])^.Nul)
  else
    Result := Pointer(S);
end;
и что я должен был тут увидеть? То, что просто возвращается указатель на строку? Которая, внезапно, оказывается нуль-терминатной?
28 май 19, 19:48    [21896026]     Ответить | Цитировать Сообщить модератору
 Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
makhaon
Member

Откуда: A galaxy far far away
Сообщений: 3260
_Vasilisk_,

в документации описано про гарантированный ноль в конце строки?
28 май 19, 20:07    [21896039]     Ответить | Цитировать Сообщить модератору
 Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 10998
makhaon
в документации описано про гарантированный ноль в конце строки?
Например вот
http://docwiki.embarcadero.com/RADStudio/Tokyo/en/String_Types_(Delphi)
You can also cast a UnicodeString or AnsiString string as a null-terminated string. The following rules apply:

  • If S is a UnicodeString, PChar(S) casts S as a null-terminated string; it returns a pointer to the first character in S. Such casts are used for the Windows API. For example, if Str1 and Str2 are UnicodeString, you could call the Win32 API MessageBox function like this:
    MessageBox(0, PChar(Str1), PChar(Str2), MB_OK);
    

    Use PAnsiChar(S) if S is an AnsiString.
  • You can also use Pointer(S) to cast a string to an untyped pointer. But if S is empty, the typecast returns nil.
  • 28 май 19, 23:21    [21896123]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    cptngrb
    Member

    Откуда:
    Сообщений: 343
    а я видел строки без /0 и с /0 посередине ))
    29 май 19, 09:12    [21896306]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    RWolf
    Member

    Откуда: Казань
    Сообщений: 430
    cptngrb
    а я видел строки без /0 и с /0 посередине ))

    вот прямо string без завершающего нуля?
    это, наверно, ошибка в программе была. Delphi всегда выделяет памяти на символ больше длины строки и пишет 0 в лишний символ.
    В самой строке нулей может быть сколько угодно, конечно.
    29 май 19, 10:15    [21896382]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    cptngrb
    Member

    Откуда:
    Сообщений: 343
    RWolf, конечно ошибка, но я то думал, что такого не бывает
    29 май 19, 10:20    [21896392]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    Квейд
    Member

    Откуда: Kyiv, Ukraine
    Сообщений: 5225
    makhaon
    _Vasilisk_,

    автор
    А как, по твоему, работает привидение к PChar?

    чудом, не иначе
    включи range check, узнаешь много нового. никакого нуля сзади строки нет.

    Есть

    makhaon
    ну и посмотри как приведение работает. подсказка: UStrToPWChar.


    А ты сам смотрел как работает UStrToPWChar?

    function _UStrToPWChar(const S: UnicodeString): PWideChar;
    begin
      if Pointer(S) = nil then
        Result := @(PEmptyString(@EmptyStringW[1])^.Nul)
      else
        Result := Pointer(S);
    end;
    
    29 май 19, 10:22    [21896395]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    makhaon
    Member

    Откуда: A galaxy far far away
    Сообщений: 3260
    ок, уговорили, черти красноречивые
    29 май 19, 12:01    [21896544]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    _Vasilisk_
    Member

    Откуда: Украина, Харьков
    Сообщений: 10998
    cptngrb
    а я видел строки без /0 и с /0 посередине ))
    И что? Это не противоречит тому, что строка оканчивается \0

    Вот при касте к PChar такой строки она обрежется до серединного \0. Вернее, если принимающая сторона считает длину строки по завершающему \0, то она прочитает только часть строки
    29 май 19, 14:34    [21896743]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    cptngrb
    Member

    Откуда:
    Сообщений: 343
    _Vasilisk_, вы категорично заявляли, что заканчивается 0.
    29 май 19, 16:31    [21897000]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    cptngrb
    Member

    Откуда:
    Сообщений: 343
    _Vasilisk_
    Вот при касте к PChar такой строки она обрежется до серединного \0. Вернее, если принимающая сторона считает длину строки по завершающему \0, то она прочитает только часть строки


    вот и приходилось нолик игнорировать в середине
    29 май 19, 16:33    [21897004]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    kealon(Ruslan)
    Member

    Откуда: Нижневартовск
    Сообщений: 4805
    cptngrb
    _Vasilisk_, вы категорично заявляли, что заканчивается 0.
    да, заканчивается, но это никак не исключает, что нулей может быть больше одного, завершающего

    "Те кто говорят, что у Кутозова не было одного глаза, нагло врут. Один глаз у него был!!!"
    29 май 19, 17:06    [21897080]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    GunSmoker
    Member

    Откуда:
    Сообщений: 3110
    SQL-Talker, товарищ! Не слушай некоторых тут, они давно не чистили лук на подводной лодке. Читай сюда: Разработка API (контракта) для своей DLL или: не создавайте своих DLL, не прочитав эту статью!.
    7 июн 19, 22:37    [21904965]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    shonli95
    Member

    Откуда:
    Сообщений: 85
    makhaon
    никакого нуля сзади строки нет


    +
    program Project3;
    
    {$APPTYPE CONSOLE}
    {$R *.res}
    uses
      System.SysUtils;
    
    var
      Str: string = 'Test';
    
    begin
      try
        Str := Str + ' Hello';
        Writeln(Str);
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
      Readln;
    
    end.
    


    Включаем отладчик, попадаем в функцию
    procedure _UStrCat(var Dest: UnicodeString; const Source: UnicodeString);
    


    Из неё попадаем в
    +
    procedure _UStrSetLength(var Str: UnicodeString; NewLength: Integer);
    var
      P: PStrRec;
      Temp: Pointer;
      CopyCount: Integer;
    begin
      if NewLength <= 0 then
        _UStrClr(Str)
      else
      begin
        if Pointer(Str) <> nil then
        begin
          if __StringRefCnt(Str) = 1 then
          begin
            P := Pointer(PByte(Str) - Sizeof(StrRec));
            if Cardinal(NewLength) >= Cardinal(- SizeOf(StrRec) - SizeOf(WideChar)) div 2 then
              _IntOver;
            _ReallocMem(Pointer(P), (NewLength + 1) * SizeOf(WideChar) + SizeOf(StrRec));
            P.length := NewLength;
            Pointer(Str) := Pointer(PByte(P) + SizeOf(StrRec));
            PWideChar(Str)[NewLength] := #0;
            Exit;
          end;
        end;
        Temp := _NewUnicodeString(NewLength);
        if Pointer(Str) <> nil then
        begin
          CopyCount := __StringLength(Str);
          if CopyCount > NewLength then
            CopyCount := NewLength;
          Move(PWideChar(Str)^, PWideChar(Temp)^, CopyCount * SizeOf(WideChar));
          _UStrClr(Str);
        end;
        Pointer(Str) := Temp;
      end;
    end;
    


    От сюда видим
    PWideChar(Str)[NewLength] := #0;
    


    Идём дальше, и проверяем PAnsiChar И в конечном итоге попадаем на выделение строки
    +
    function _NewUnicodeString(CharLength: Integer): Pointer;
    var
      P: PStrRec;
    begin
      Result := nil;
      if CharLength > 0 then
      begin
        // Allocate a memory with record and extra wide-null terminator.
        if CharLength >= (MaxInt - SizeOf(StrRec)) div SizeOf(WideChar) then _IntOver;
        GetMem(P, SizeOf(StrRec) + (CharLength + 1) * SizeOf(WideChar));
        Result := Pointer(PByte(P) + SizeOf(StrRec));
        P.length := CharLength;
        P.refCnt := 1;
        P.elemSize := SizeOf(WideChar);
        P.codePage := Word(DefaultUnicodeCodePage);
        PWideChar(Result)[CharLength] := #0;
      end;
    end;
    


    Где опять же видим
    PWideChar(Result)[CharLength] := #0;
    


    Чудом, не иначе

    +
    AnsiString type

     _PAnsiChr(Str)[NewLength] := #0;
    


    procedure _LStrSetLength(var Str: _AnsiStr; NewLength: Integer; CodePage: Word);
    var
      P: PStrRec;
      Temp: Pointer;
      CopyCount: Integer;
    begin
      if newLength <= 0 then
      begin
        _LStrClr(Str);
        Exit;
      end
      else
      begin
        if Pointer(Str) <> nil then
        begin
          if __StringRefCnt(Str) = 1 then
          begin
            P := Pointer(PByte(Str) - Sizeof(StrRec));
            _ReallocMem(Pointer(P), NewLength + 1 + SizeOf(StrRec));
            P.length := NewLength;
            Pointer(Str) := Pointer(PByte(P) + SizeOf(StrRec));
            _PAnsiChr(Str)[NewLength] := #0;
            Exit;
          end;
        end;
        Temp := _NewAnsiString(NewLength, CodePage);
        if Pointer(Str) = nil then
        begin
          Pointer(Str) := Temp;
          Exit;
        end;
        CopyCount := __StringLength(Str);
        if CopyCount > NewLength then
          CopyCount := NewLength;
        Move(_PAnsiChr(str)^, _PAnsiChr(Temp)^, CopyCount);
        _LStrClr(Str);
        Pointer(Str) := Temp;
      end;
    end;
    

    [/SRC]
    8 июн 19, 00:38    [21905006]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    makhaon
    Member

    Откуда: A galaxy far far away
    Сообщений: 3260
    shonli95,

    Явного нуля в конце строки нет. Текущая частная реализация. Можно, конечно, заложиться. Но внезапно поменяется завтра что-то - и всё посыпется.
    8 июн 19, 15:15    [21905156]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    Kazantsev Alexey
    Member

    Откуда:
    Сообщений: 3543
    http://docwiki.embarcadero.com/RADStudio/Rio/en/Internal_Data_Formats_(Delphi)#Long_String_Types
    8 июн 19, 15:37    [21905163]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    shonli95
    Member

    Откуда:
    Сообщений: 85
    makhaon,

    есть, это легко проверить. Когда ты пытаешься вывести строку, она конвертируется не на стадии присвоения string к PWideChar а на выводе.


    +
    program Project3;
    
    {$APPTYPE CONSOLE}
    {$R *.res}
    uses
      System.SysUtils;
    
    var
      Str: string = 'Test';
      NStr: PWideChar;
    
    begin
      try
        Str := Str + ' Hello';
        NStr := PWideChar(Str);
        Writeln(NStr);
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
      Readln;
    
    end.
    



    При вызове Writeln приведёт к тому, что строка будет скопирована


    procedure _UStrFromPWCharLen(var Dest: UnicodeString; Source: PWideChar; CharLength: Integer);
    var
      Temp: Pointer;
    begin
      Temp := Pointer(Dest);
      if CharLength > 0 then
      begin
        Pointer(Dest) := _NewUnicodeString(CharLength);
        if Source <> nil then
          Move(Source^, Pointer(Dest)^, CharLength * SizeOf(WideChar));
      end
      else
        Pointer(Dest) := nil;
      _UStrClr(Temp);
    end;
    


    И при создании данной строки, будет создана новая строка _NewUnicodeString ->


    function _NewUnicodeString(CharLength: Integer): Pointer;
    var
      P: PStrRec;
    begin
      Result := nil;
      if CharLength > 0 then
      begin
        // Allocate a memory with record and extra wide-null terminator.
        if CharLength >= (MaxInt - SizeOf(StrRec)) div SizeOf(WideChar) then _IntOver;
        GetMem(P, SizeOf(StrRec) + (CharLength + 1) * SizeOf(WideChar));
        Result := Pointer(PByte(P) + SizeOf(StrRec));
        P.length := CharLength;
        P.refCnt := 1;
        P.elemSize := SizeOf(WideChar);
        P.codePage := Word(DefaultUnicodeCodePage);
        PWideChar(Result)[CharLength] := #0;
      end;
    end;
    



    Где на конце, располагается символ конца строки
    10 июн 19, 10:06    [21905714]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    alekcvp
    Member

    Откуда:
    Сообщений: 1417
    GunSmoker
    Читай сюда: Разработка API (контракта) для своей DLL или: не создавайте своих DLL, не прочитав эту статью!.


    Мне кажется, или там ошибка в разделе "Выделенные функции"?
    var
      P: array of Something;  
      Data: Pointer;
      DataSize: DWORD;
    begin
      GetDynData(0, Data, DataSize); <---
     
      SetLength(P, DataSize div SizeOf(Something));
      Move(Data^, Pointer(P)^, DataSize);
      CoTaskMemFree(Data); ??!!!
       
      // Работаем с P
    end;
    
    10 июн 19, 11:24    [21905785]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    GunSmoker
    Member

    Откуда:
    Сообщений: 3110
    alekcvp, может у меня глаз замылился, в чём там вопрос?
    10 июн 19, 12:49    [21905866]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    shonli95
    Member

    Откуда:
    Сообщений: 85
    alekcvp,

    Да там легко можно узнать выделенный размер блока, и функции для освобождения этим и пользуются по умолчанию.

    Это вам не delphi с его крутым менеджером

    К примеру
    +
      IMalloc = interface(IUnknown)
        ['{00000002-0000-0000-C000-000000000046}']
        function Alloc(cb: Longint): Pointer; stdcall;
        function Realloc(pv: Pointer; cb: Longint): Pointer; stdcall;
        procedure Free(pv: Pointer); stdcall;
        function GetSize(pv: Pointer): Longint; stdcall;
        function DidAlloc(pv: Pointer): Integer; stdcall;
        procedure HeapMinimize; stdcall;
      end;
    


    Имеет GetSize, но Free не принимает второго аргумента, что бы удалить определённое количество.

    Так же и там. Майки не дают делать программисту - то, что ему не нужно
    10 июн 19, 13:53    [21905916]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    alekcvp
    Member

    Откуда:
    Сообщений: 1417
    GunSmoker
    alekcvp, может у меня глаз замылился, в чём там вопрос?


    В том, что память выделяется через GetDynData(), а освобождается через CoTaskMemFree(), вместо FreeDynData() (или как там).
    10 июн 19, 14:37    [21905960]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    shonli95
    Member

    Откуда:
    Сообщений: 85
    alekcvp,


    function GetDynData(const AFlags: DWORD; out AData: Pointer; out ADataSize: DWORD): BOOL; stdcall;
    var
      P: array of Something;
    begin
      P := { ... готовим данные ... };
     
      ADataSize := Length(P) * SizeOf(Something);
      AData := CoTaskMemAlloc(ADataSize);
      Move(Pointer(P)^, AData^, ADataSize);
     
      Result := True;
    end;
    


    Идём на сайт
    https://docs.microsoft.com/en-us/windows/desktop/api/combaseapi/nf-combaseapi-cotaskmemalloc

    Видим, что это аналог IMalloc::Alloc видим, что освобождение идёт через CoTaskMemFree что аналогично IMalloc::Free


    Ну а ваще Ок

    procedure FreeDynData(var Data);
    begin 
     CoTaskMemFree(Data);
    end;
    
    10 июн 19, 15:27    [21906003]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    RWolf
    Member

    Откуда: Казань
    Сообщений: 430
    GunSmoker
    // Неправильно!
    procedure DoSomething(const AArg: ISomething); safecall;
     
    // Правильно
    procedure DoSomething(AArg: ISomething); safecall;
    


    Это ведь означает лишние _AddRef/_Release на каждый вызов функции, разве нет? в чём выгода?
    10 июн 19, 16:34    [21906048]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    _Vasilisk_
    Member

    Откуда: Украина, Харьков
    Сообщений: 10998
    автор
    Не нужно делать реализацию методов интерфейса виртуальными:
    Бред. Если методы будут в дальнейшем переопределяться, то они должны быть виртуальными
    автор
    Не помечайте интерфейсные параметры модификатором const:
    с какого перепугу? Наоборот, они должны быть всегда c const, чтобы не дергать счетчик ссылок
    автор
    // Правильно
    procedure GetSomething(const AIID: TGUID; out Intf); safecall;
    
    Идея красивая. Но импортер Delphi не умеет импортировать
    ([out] void ** AOut)
    
    в
    (out AOut)
    
    . А каждый раз руками править замучаешься
    10 июн 19, 17:51    [21906109]     Ответить | Цитировать Сообщить модератору
     Re: DLL - Написать DLL, в ней функцию, которая возвращает массив записей.  [new]
    Kazantsev Alexey
    Member

    Откуда:
    Сообщений: 3543
    _Vasilisk_
    с какого перепугу? Наоборот, они должны быть всегда c const, чтобы не дергать счетчик ссылок

    Вангую, это такая соломка от:
    DoSomething(TSomethingImplementor.Create);
    

    тут без явной (as) передачи интерфейса будет утечка, если интерфейсный параметр является константным.
    10 июн 19, 18:09    [21906123]     Ответить | Цитировать Сообщить модератору
    Топик располагается на нескольких страницах: Ctrl  назад   1 [2] 3 4   вперед  Ctrl      все
    Все форумы / Delphi Ответить