Пишу ОРС (COM out of proc) сервер на С#. Возникла проблеммы с реализацией интерфейса IEnumString. Класс реализующий IEnumString выглядит следующим
образом:
class EnumString : IEnumString
{
public List<string> lStr;
private int currentpos;
public EnumString()
{
lStr=new List<string>();
currentpos = 0;
}
public int Next(int celt, [OutAttribute] string[] rgelt, IntPtr pceltFetched)
{
int i = 0;
List<string> ResultList = new List<string>();
while ((i < celt) && (currentpos < lStr.Count))
{
ResultList.Add(lStr[currentpos]);
i++;
currentpos++;
}
GC.KeepAlive(ResultList);
rgelt = ResultList.ToArray();
Marshal.StructureToPtr(i, pceltFetched, false);
return (i == celt) ? HRESULTS.S_OK : HRESULTS.S_FALSE;
}
public int Skip(int celt)
{
if (currentpos + celt <= lStr.Count)
{
currentpos += celt;
return HRESULTS.S_OK;
}
else
{
currentpos = lStr.Count;
return HRESULTS.S_FALSE;
}
}
public void Reset()
{
currentpos = 0;
}
public void Clone(out IEnumString ppenum)
{
throw new System.NotImplementedException();
}
public void Add(string newMember)
{
lStr.Add(newMember);
}
public bool Remove(string s)
{
return lStr.Remove(s);
}
}
На стороне клиента я работаю с ним следующим образом
В итоге,в методе Next в pszName содержиться указатеь 0x00000000. Уже голову себе сломал в чем тут дело,в дебагере видно что Next на сервере вызываеться,все проходит нормально,в count потом содержиться 1,то есть все нормально передаеться,а вот со строкой проблеммы. Помогите решить эту проблемму.
Здравствуйте, vmpire, Вы писали:
V>Здравствуйте, Phaust, Вы писали:
V>Может, дело в том, что в методе Next сервер отдаёт массив строк, а клиент ожидает только одну?
Сомневаюсь,с другими то серверами клиент работает прекрасно. И указатель то должен нормально передаваться,если бы было то, что вы предполагаете,была бы ошибка при работе с обьектом pszName,а тут он просто не передаеться.
Re[3]: Помогите с реализацией IEnumString для ОРС сервера на
Здравствуйте, Phaust, Вы писали:
P>Здравствуйте, vmpire, Вы писали:
V>>Здравствуйте, Phaust, Вы писали:
V>>Может, дело в том, что в методе Next сервер отдаёт массив строк, а клиент ожидает только одну?
P>Сомневаюсь,с другими то серверами клиент работает прекрасно. И указатель то должен нормально передаваться,если бы было то, что вы предполагаете,была бы ошибка при работе с обьектом pszName,а тут он просто не передаеться.
Да, Вы правы, скорее всего, дело не в этом.
Попробуйте, если это возможно, провести пару экспериментов:
1. Запустить всё это как InProc сервер
2. Поменять атрибут на [Out, MarshalAs( UnmanagedType.LPArray )], указав явно, как маршалить
Re[4]: Помогите с реализацией IEnumString для ОРС сервера на
V>Да, Вы правы, скорее всего, дело не в этом. V>Попробуйте, если это возможно, провести пару экспериментов: V>1. Запустить всё это как InProc сервер V>2. Поменять атрибут на [Out, MarshalAs( UnmanagedType.LPArray )], указав явно, как маршалить
1. Долго менять,да и нужен out of proc сервер.
2. Не помогло
Re: Помогите с реализацией IEnumString для ОРС сервера на С#
P> public int Next(int celt, [OutAttribute] string[] rgelt, IntPtr pceltFetched)
P> {
P> int i = 0;
P> List<string> ResultList = new List<string>();
P> while ((i < celt) && (currentpos < lStr.Count))
P> {
P> ResultList.Add(lStr[currentpos]);
P> i++;
P> currentpos++;
P> }
P> GC.KeepAlive(ResultList);//Нафига?
P> rgelt = ResultList.ToArray();// Ты создал новый массив и заменил им локальную ссылку... Массив не покинет приделы функции.
P> Marshal.StructureToPtr(i, pceltFetched, false);
P> return (i == celt) ? HRESULTS.S_OK : HRESULTS.S_FALSE;
P> }
P>
... << RSDN@Home 1.2.0 alpha rev. 745>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[2]: Помогите с реализацией IEnumString для ОРС сервера на
P>> GC.KeepAlive(ResultList);//Нафига?
Просто в сервер есть поток где принудительно вызываеться сборка мусора. P>> rgelt = ResultList.ToArray();// Ты создал новый массив и заменил им локальную ссылку... Массив не покинет приделы функции.
И как правильно сделать?
Re[5]: Помогите с реализацией IEnumString для ОРС сервера на
Здравствуйте, Phaust, Вы писали:
V>>Да, Вы правы, скорее всего, дело не в этом. V>>Попробуйте, если это возможно, провести пару экспериментов: V>>1. Запустить всё это как InProc сервер V>>2. Поменять атрибут на [Out, MarshalAs( UnmanagedType.LPArray )], указав явно, как маршалить P>1. Долго менять,да и нужен out of proc сервер. P>2. Не помогло
Понятно, что нужен out of proc, эксперимент был предложен чтобы понять в чём проблема. Например, если в InProc всё нормально, то проблема, скорее всего, связана с маршалингом COM. А если в InProc тоже плохо — то скорее всего проблема с интеропом.
Я, вроде, понял, в чём дело. Мне кажется, тут неправильно реализовано соглашение о вызовах.
Попробуйте так:
public int Next(int celt, string[] rgelt, IntPtr pceltFetched)
{
int i = 0;
while ((i < celt) && (currentpos < lStr.Count))
{
rgelt[i++] = lStr[currentpos];
currentpos++;
}
Marshal.StructureToPtr(i, pceltFetched, false);
return (i == celt) ? HRESULTS.S_OK : HRESULTS.S_FALSE;
}
Иными словами, кусок памяти уже выделен клиентом и в него надо просто записать значения.
Если Вы пишете данные в другой кусок памяти, то тот остаётся пустым.
Re[3]: Помогите с реализацией IEnumString для ОРС сервера на
Здравствуйте, Phaust, Вы писали:
P>>>GC.KeepAlive(ResultList);//Нафига? P>Просто в сервер есть поток где принудительно вызываеться сборка мусора.
1)Зачем? Ты уверен что оно тебе надо?
2)GC.KeepAlive тут совсем не к чему.
P>>>rgelt = ResultList.ToArray();// Ты создал новый массив и заменил им локальную ссылку... Массив не покинет приделы функции. P>И как правильно сделать?
Почитать соглащения о вызовах.
Скорей всего к тебе уже приходит выделенный массив в который нужно записать данные.
... << RSDN@Home 1.2.0 alpha rev. 745>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[8]: Помогите с реализацией IEnumString для ОРС сервера на
Здравствуйте, vmpire, Вы писали:
V>Я, вроде, понял, в чём дело. Мне кажется, тут неправильно реализовано соглашение о вызовах. V>Попробуйте так:
V>
V> public int Next(int celt, string[] rgelt, IntPtr pceltFetched)
V> {
V> int i = 0;
V> while ((i < celt) && (currentpos < lStr.Count))
V> {
V> rgelt[i++] = lStr[currentpos];
V> currentpos++;
V> }
V> Marshal.StructureToPtr(i, pceltFetched, false);
V> return (i == celt) ? HRESULTS.S_OK : HRESULTS.S_FALSE;
V> }
V>
V>Иными словами, кусок памяти уже выделен клиентом и в него надо просто записать значения. V>Если Вы пишете данные в другой кусок памяти, то тот остаётся пустым.
Заработало Спасибо Вам огромнейшее Единственное чего не могу понять,как была выделена память на стороне клиета
ведь явственно же
Здравствуйте, Phaust, Вы писали:
P>Заработало Спасибо Вам огромнейшее Единственное чего не могу понять,как была выделена память на стороне клиета P>ведь явственно же
P>
Под указатель Вы память выделили сами (переменная pszName). С памятью под саму строку — сложнее:
Для параметров, объявленных как Out по соглашению (http://msdn.microsoft.com/en-us/library/ms686638(VS.85).aspx) память выделяет com-сервер, а клиент обязан её освободить (кстати, добавьте в Ваш код вызов CoTaskMemFree(pszName), чтобы память не утекала).
В сервере память под строку, очевидно, выделяет CLI. Когда её нужно вернуть клиенту interop выделяет под неё память и копирует строку туда (возможно, там есть оптимизации, чтобы избежать лишнего копирования, но на суть это не влияет).
В случае InProc взаимодействия клиенту передаётся именно эта область памяти.
В случае OutOfProc данные в этой памяти маршалятся и память освобождается инфраструктурой маршалинга (это возможно как раз благодаря чётким правилам соглашения о вызовах). Отмаршаленные данные передаются в другой процесс (клиента).
В процессе клиента инфраструктура маршалинга выделяет память и разворачивает сериализованные данные туда, после чего отдаёт эти данные клиенту.
Клиент обязан эту память освободить по всё тому же соглашению.
Если бы сервер был написан на unmanageв языке, то память бы Вы выделяли там сами.
Как-то так.