P / Invoke иногда вызывает Win32 1008 Ошибка с параметрами StringBuilder - PullRequest
2 голосов
/ 07 марта 2012

У меня есть DLL, из которой мне нужно P / Invoke следующий метод C:

int DAStart(
    HANDLE hOpen,
    char* IPAddress,
    int IPPort,
    int threadPriority,
    char* name,
    char* password,
    char* userName)

Используя P / Invoke Assistant и мои собственные исследования, я создал следующую сигнатуру C #:

[DllImportAttribute("<libName>", EntryPoint="DAStart")]  
static extern  int DAStart(  
    IntPtr hOpen,  
    [MarshalAs(Unmanaged.LPStr)] StringBuilder IPAddress,
    int IPPort,
    int threadPriority, 
    [MarshalAs(Unmanaged.LPStr)] StringBuilder name, 
    [MarshalAs(Unmanaged.LPStr)] StringBuilder password,   
    [MarshalAs(Unmanaged.LPStr)] StringBuilder userName);

Теперь я делаю вызов следующим образом:

int port = 3000;
int threadPriority = 20;

DAStart(
    this.nativeDllHandle, // an IntPtr class field  
    new StringBuilder("10.0.10.1"),
    int port,
    int threadPriority,
    new StringBuilder("admin"),
    new StringBuilder("admin"),
    new StringBuilder("admin"));

Теперь иногда это работает просто отлично, но я получаю сообщение об ошибке Win32 1008 - An attempt was made to reference a token that does not exist при следующих вызовах этой библиотеки.

Может ли быть так, что мои объекты StringBuilder будут собирать мусор, чтобы ссылка больше не существовала, если нативный код пытается ее использовать? Должен ли я сохранить ссылку на каждого из них?

Будет ли IntPtr лучшим решением для передачи моих строк в этом случае?

** ОБНОВЛЕНИЕ **

Это вся документация по API, которая у меня есть для DAStart:

Входы
РУЧКА hInfo Ручка, возвращаемая DAOpen
char * IPAdress_in IP-адрес сервера TMEE
int IPPort Console Порт сервера TMEE (по умолчанию используется порт 3000)
int threadPriority Приоритет потока для потока файла отправки.
char * name Не используется в Hardware DLL
char * пароль Не используется в аппаратной DLL
char * username Не используется в Hardware DLL

Возвращает
ERROR_SUCCESS 0
ERROR_BAD_HANDLE -1
ERROR_BIND_FAILED -10

Комментарии
API-интерфейс DAStart соединяет клиентскую DLL с активной службой сервера TMEE. Клиентский поток запускается с приоритетом, установленным для параметра threadPriority. Параметр IP-адреса должен быть установлен на адрес сервера TMEE. Параметр порта должен быть установлен на порт, который сервер TMEE прослушивает для подключений к консоли (порт по умолчанию, который используют консоли, - 3010). Консольный поток запускается с приоритетом, равным параметру threadPriority.

Ответы [ 2 ]

2 голосов
/ 07 марта 2012

В комментариях вы указываете, что DLL берет копию указателей char* (а не содержимое строк), а затем изменяет содержимое строк после возврата DAStart.

Перед лицом этого весьма нестандартного дизайна интерфейса вы можете взять на себя ответственность за маршалинг строковых параметров.Вы не можете использовать StringBuilder, поскольку char*, переданный в собственный код, действителен только во время вызова pinvoke.Нельзя полагаться, что он действителен после возврата вызова pinvoke.

Таким образом, ваше единственное решение - передать строковые параметры как IntPtr.Выделите память с помощью Marshal.StringToHGlobalAnsi.Передайте полученный IntPtr на DAStart.Когда вы уверены, что DLL выполнена с указателем, освободите ее с помощью Marshal.FreeHGlobal.

0 голосов
/ 07 марта 2012

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

...