Почему COM не использует статический пустой BSTR? - PullRequest
3 голосов
/ 30 ноября 2009

При выделении пустого BSTR либо на SysAllocString(L""), либо на SysAllocStringLen(str, 0) вы всегда получаете новый BSTR (по крайней мере, по тесту, который я сделал). BSTR s, как правило, не являются общими (например, Java / .NET interment), так как они изменчивы, но пустая строка для всех намерений и целей неизменна.

Мой вопрос (наконец-то): почему COM не использует тривиальную оптимизацию всегда возвращать одну и ту же строку при создании пустого BSTR (и игнорировать его в SysFreeString)? Есть ли веская причина не делать этого (потому что мои рассуждения ошибочны) или это просто не считалось достаточно важным?

Ответы [ 3 ]

4 голосов
/ 30 ноября 2009

Я не могу говорить о том, что идиоматично в COM, но в C ++, Java и т. Д. Есть ожидание, что если вы new объект, он не будет сравниваться равным (что касается идентификации адреса / объекта ) к любому другому объекту. Это свойство полезно, когда вы используете отображение на основе идентичности, например, в качестве ключей в IdentityHashMap в Java. По этой причине я не думаю, что пустые строки / объекты должны быть исключением из этого правила.

Хорошо написанные COM-объекты позволят вам передать NULL параметру BSTR и обработать его как эквивалент пустой строки. (Это не будет работать с MSXML, хотя я научился трудному пути. :-P)

2 голосов
/ 30 ноября 2009

Это не COM, выделяющий BSTR, а подсистема Windows, предоставляющая его.

Пустые BSTR не могут совместно использовать статический экземпляр, поскольку существуют функции, которые могут перераспределять / изменять размеры BSTR. Смотрите SysReAllocString. Хотя не упоминается оптимистическое поведение при распределении, нельзя предположить, что вызывающий абонент никогда не получит исходный BSTR после вызова.

SysReAllocString @ MSDN

редактирование:

После некоторых размышлений я понимаю, что даже при учете SysReAllocString можно начать с пустого общего BSTR, вызвать SysReAllocString и получить новый BSTR без какого-либо прерывистого поведения. Так что это можно сбрасывать со счетов ради аргумента. Моя вина.

Однако я полагаю, что идея пустого BSTR несет в себе больше багажа, чем можно подумать. Я написал несколько тестовых программ, чтобы посмотреть, смогу ли я получить противоречивые или интересные результаты. После выполнения моих тестов и подведения итогов, я думаю, что лучший ответ на ваш вопрос заключается в том, что для всех участников просто безопаснее, если все запросы получают свои собственные BSTR. Есть много прикольных способов получить BSTR, которые сообщают о различных вариантах нулевой длины, как строковых, так и ориентированных на байты. Даже если бы была некоторая оптимизация, которая в некоторых местах возвращала общие экземпляры, есть много места для путаницы при словесном описании пустого BSTR по сравнению с фактическим BSTR, который имеет длину пустой строки и реальную длину выделения. Например, такой оператор, как « BSTR, который не имеет длины, выделенной для строки, может быть забыт », может привести к некоторым утечкам памяти (см. Тесты ниже относительно BSTR, выделенных для байтов).

Кроме того, несмотря на некоторые компоненты COM, которые допускают использование в качестве аргументов BSTR с нулевым указателем (0-значным), небезопасно предполагать, что все компоненты COM поддерживают его. Это может быть безопасно только в том случае, если и вызывающий абонент, и вызываемый абонент согласны разрешить это. Самое безопасное поведение для всех - это предположить, что при передаче BSTR он может иметь длину с нулевым определением, требовать обработки в случае длины с нулевым определением и требовать некоторого значения, которое не является NULL-указателем. По крайней мере, это значительно облегчает написание кода прокси / заглушки и других сложных задач.

Моя первая тестовая программа пыталась использовать несколько необычных методов размещения. Обратите внимание, что вы можете получить BSTR с указанными значениями SysStringLen-длины 0, но с реальным распределением байтов. Кроме того, я заранее признаю, что bstr5 и bstr6 не являются чистыми методами распределения.

Вот источник:

int _tmain(int argc, _TCHAR* argv[])
{
  BSTR bstr1 = SysAllocString(L"");
  BSTR bstr2 = SysAllocStringLen(NULL, 0);
  BSTR bstr3 = SysAllocStringLen(NULL, 1);
  *bstr3 = '\0';
  BSTR bstr4 = SysAllocStringLen(L"some string", 0);
  BSTR bstr5 = SysAllocStringByteLen((LPCSTR)L"", 1);
  BSTR bstr6 = SysAllocStringByteLen((LPCSTR)L"", 2);
  BSTR bstr7 = SysAllocStringByteLen("", 1);
  BSTR bstr8 = SysAllocStringByteLen("\0\0", 2);
  BSTR bstr9 = SysAllocStringByteLen(NULL, 0);
  BSTR bstr10 = SysAllocStringByteLen(NULL, 1);

  _tprintf(_T("L\"\"-sourced BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));
  _tprintf(_T("NULL BSTR with no alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr2, SysStringLen(bstr2), SysStringByteLen(bstr2));
  _tprintf(_T("NULL BSTR with 1 OLECHAR alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr3, SysStringLen(bstr3), SysStringByteLen(bstr3));
  _tprintf(_T("L\"some string\"-sourced BSTR with no alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr4, SysStringLen(bstr4), SysStringByteLen(bstr4));
  _tprintf(_T("L\"\"-sourced BSTR with 1 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr5, SysStringLen(bstr5), SysStringByteLen(bstr5));
  _tprintf(_T("L\"\"-sourced BSTR with 2 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr6, SysStringLen(bstr6), SysStringByteLen(bstr6));
  _tprintf(_T("\"\"-sourced BSTR with 1 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr7, SysStringLen(bstr7), SysStringByteLen(bstr7));
  _tprintf(_T("\"\\0\\0\"-sourced BSTR with 2 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr8, SysStringLen(bstr8), SysStringByteLen(bstr8));
  _tprintf(_T("NULL-sourced BSTR with 0 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr9, SysStringLen(bstr9), SysStringByteLen(bstr9));
  _tprintf(_T("NULL-sourced BSTR with 1 byte alloc length\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr10, SysStringLen(bstr10), SysStringByteLen(bstr10));

  SysFreeString(bstr1);
  SysFreeString(bstr2);
  SysFreeString(bstr3);
  SysFreeString(bstr4);
  SysFreeString(bstr5);
  SysFreeString(bstr6);
  SysFreeString(bstr7);
  SysFreeString(bstr8);
  SysFreeString(bstr9);
  SysFreeString(bstr10);

  return 0;
}

Вот результаты, которые я получил.

L""-sourced BSTR
        BSTR=0x00175bdc, length 0, 0 bytes
NULL BSTR with no alloc length
        BSTR=0x00175c04, length 0, 0 bytes
NULL BSTR with 1 OLECHAR alloc length
        BSTR=0x00175c2c, length 1, 2 bytes
L"some string"-sourced BSTR with no alloc length
        BSTR=0x00175c54, length 0, 0 bytes
L""-sourced BSTR with 1 byte alloc length
        BSTR=0x00175c7c, length 0, 1 bytes
L""-sourced BSTR with 2 byte alloc length
        BSTR=0x00175ca4, length 1, 2 bytes
""-sourced BSTR with 1 byte alloc length
        BSTR=0x00175ccc, length 0, 1 bytes
"\0\0"-sourced BSTR with 2 byte alloc length
        BSTR=0x00175cf4, length 1, 2 bytes
NULL-sourced BSTR with 0 byte alloc length
        BSTR=0x00175d1c, length 0, 0 bytes
NULL-sourced BSTR with 1 byte alloc length
        BSTR=0x00175d44, length 0, 1 bytes

Моя следующая тестовая программа показала, что изменения в размере могут вернуть тот же BSTR. Вот короткий фрагмент, который может продемонстрировать это для вас, а также вывод, который я получил. Я также увеличил его сверх его первоначальной длины и все еще получил тот же BSTR обратно. Это предполагает, по крайней мере, что нельзя предположить, что BSTR без длины не может быть увеличен в размерах.

int _tmain(int argc, _TCHAR* argv[])
{
  BSTR bstr1 = SysAllocString(L"hello world!");

  _tprintf(_T("L\"hello world!\"-sourced BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"\"\r\n"));
  SysReAllocString(&bstr1, L"");
  _tprintf(_T("L\"\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"hello!\"\r\n"));
  SysReAllocString(&bstr1, L"hello!");
  _tprintf(_T("L\"\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"hello world!+\"\r\n"));
  SysReAllocString(&bstr1, L"hello world!+");
  _tprintf(_T("L\"\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"),
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  SysFreeString(bstr1);

  return 0;
}

Запуск этой программы на моей рабочей станции (Windows XP) дал следующие результаты. Мне было бы интересно узнать, получит ли кто-нибудь новый BSTR где-нибудь по пути.

L"hello world!"-sourced BSTR
        BSTR=0x00175bdc, length 12, 24 bytes
resizing bstr1 to source L""
L""-sourced reallocated BSTR
        BSTR=0x00175bdc, length 0, 0 bytes
resizing bstr1 to source L"hello!"
L"hello!"-sourced reallocated BSTR
        BSTR=0x00175bdc, length 6, 12 bytes
resizing bstr1 to source L"hello world!+"
L"hello world!+"-sourced reallocated BSTR
        BSTR=0x00175bdc, length 13, 26 bytes

Я попробовал эту программу еще раз, но на этот раз, начиная с пустой строки широких символов (L ""). Это должно охватывать случай, начиная с BSTR без заданной длины строки, и видеть, имеет ли он на самом деле неявный размер. Когда я запустил его, я обнаружил, что я все еще получил тот же BSTR обратно. Я ожидаю, однако, что результаты могут отличаться здесь.

Вот источник:

int _tmain(int argc, _TCHAR* argv[])
{
  BSTR bstr1 = SysAllocString(L"");

  _tprintf(_T("L\"\"-sourced BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"hello world!\"\r\n"));
  SysReAllocString(&bstr1, L"hello world!");
  _tprintf(_T("L\"hello world!\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"hello!\"\r\n"));
  SysReAllocString(&bstr1, L"hello!");
  _tprintf(_T("L\"hello!\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"), 
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  _tprintf(_T("resizing bstr1 to source L\"hello world!+\"\r\n"));
  SysReAllocString(&bstr1, L"hello world!+");
  _tprintf(_T("L\"hello world!+\"-sourced reallocated BSTR\r\n")
    _T("\tBSTR=0x%8.8x, length %d, %d bytes\r\n"),
    bstr1, SysStringLen(bstr1), SysStringByteLen(bstr1));

  SysFreeString(bstr1);

  return 0;
}

Результаты:

L""-sourced BSTR
        BSTR=0x00175bdc, length 0, 0 bytes
resizing bstr1 to source L"hello world!"
L"hello world!"-sourced reallocated BSTR
        BSTR=0x00175bdc, length 12, 24 bytes
resizing bstr1 to source L"hello!"
L"hello!"-sourced reallocated BSTR
        BSTR=0x00175bdc, length 6, 12 bytes
resizing bstr1 to source L"hello world!+"
L"hello world!+"-sourced reallocated BSTR
        BSTR=0x00175bdc, length 13, 26 bytes
2 голосов
/ 30 ноября 2009

Я бы предположил (и да, это всего лишь предположение), что эта оптимизация не считалась достаточно важной для выполнения.

Хотя для многих вещей из прошлого использование Windows памяти было основным фактором в разработке API (см. Статьи Рэймонда Чена), в отличие от строк, заключенных в Java или .NET, преимущества довольно малы, поскольку они применяются только к одной строке, только шесть байтов в длину. И сколько пустых строк программа должна хранить в памяти в любой момент времени? Это число достаточно велико, чтобы оправдать эту оптимизацию, или оно на самом деле незначительное?

...