c # p / вызвать трудности с указанием указателей - PullRequest
4 голосов
/ 02 июня 2009

Я пытаюсь вызвать нативный .dll из c # с помощью p / invoke. Я могу сделать вызов (без сбоев, функция возвращает значение), но код возврата указывает «Параметр указателя не указывает на доступную память». Я решил использовать метод проб и ошибок, чтобы решить эту проблему, но пока не достиг никакого прогресса.

Вот подпись нативной функции, которую я вызываю:

LONG extern WINAPI MyFunction ( LPSTR lpszLogicalName, //input
                                   HANDLE hApp,           //input  
                                   LPSTR lpszAppID,       //input 
                                   DWORD dwTraceLevel,    //input
                                   DWORD dwTimeOut,       //input
                                   DWORD dwSrvcVersionsRequired, //input
                                   LPWFSVERSION lpSrvcVersion, //WFSVERSION*, output
                                   LPWFSVERSION lpSPIVersion,  //WFSVERSION*, output
                                   LPHSERVICE lphService       //unsigned short*, output
                                 );

Вот импортированная подпись в C #:

 [DllImport("my.dll")]
 public static extern int MyFunction( [MarshalAs(UnmanagedType.LPStr)] 
                                      string logicalName, 
                                      IntPtr hApp, 
                                      [MarshalAs(UnmanagedType.LPStr)] 
                                      string appID, 
                                      int traceLevel, 
                                      int timeout, 
                                      int srvcVersionsRequired, 
                                      [Out] WFSVersion srvcVersion, 
                                      [Out] WFSVersion spiVersion,
                                      [Out] UInt16 hService
                                    );

Вот определение C WFSVERSION:

typedef struct _wfsversion
{
    WORD            wVersion;
    WORD            wLowVersion;
    WORD            wHighVersion;
    CHAR            szDescription[257];
    CHAR            szSystemStatus[257];
} WFSVERSION, * LPWFSVERSION;

Вот определение CFS WFSVersion:

[StructLayout(LayoutKind.Sequential)]
public class WFSVersion
{
    public Int16 wVersion;
    public Int16 wLowVersion;
    public Int16 wHighVersion;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
    public char[] szDescription;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
    public char[] szSystemStatus;
}

Вот вызов MyFunction из C #:

WFSVersion srvcVersionInfo = new WFSVersion();
WFSVersion spiVersionInfo = new WFSVersion();


 UInt16 hService = 0;
 IntPtr hApp = IntPtr.Zero;
 string logicalServiceName = tbServiceName.Text;
 int openResult = MyFunction(logicalServiceName, hApp, null, 0,  
                              XFSConstants.WFS_INDEFINITE_WAIT,
                              0x00000004, srvcVersionInfo, spiVersionInfo, 
                              hService);

Как я уже сказал, этот вызов возвращается, но возвращаемым значением является код ошибки, указывающий: «Параметр указателя не указывает на доступную память». Я должен делать что-то не так с параметрами 1,3,7,8 или 9. Однако я сделал успешные вызовы других функций в этом .dll, которые требовали WFSVERSION * в качестве параметров, поэтому я не думаю, что параметры 7 или 8 проблема здесь.

Буду признателен за любые мысли о причине этой проблемы или за конструктивную критику моего кода. Это мой первый опыт работы с P / Invoke, поэтому я не уверен, с чего начать. Можно ли как-то сузить проблему или пробная версия - это единственный вариант?

Ответы [ 6 ]

2 голосов
/ 03 июня 2009

У вас есть две очевидные ошибки здесь. В вашем определении структуры вы должны использовать byte [] вместо char [] для szDescription и szSystemStatus.

Также последний параметр в вашем вызове pInvoke не является указателем. Когда вы делаете свой вызов MyFunction, hService равен нулю и, следовательно, недействительный указатель, если речь идет о функции. [Out] - это директива Marshaling, сообщающая среде выполнения, когда и где копировать данные, а не показатель того, что параметр является указателем. Вам нужно изменить значение [Out] на out или ref, чтобы среда выполнения указала, что hService является указателем:

[DllImport("my.dll")]
public static extern int MyFunction( [MarshalAs(UnmanagedType.LPStr)] 
                                  string logicalName, 
                                  IntPtr hApp, 
                                  [MarshalAs(UnmanagedType.LPStr)] 
                                  string appID, 
                                  int traceLevel, 
                                  int timeout, 
                                  int srvcVersionsRequired, 
                                  [Out] WFSVersion srvcVersion, 
                                  [Out] WFSVersion spiVersion,
                                  out UInt16 hService);
1 голос
/ 03 июня 2009

Некоторые идеи:

  • Вероятно, класс C # WFSVersion должен быть struct. Я не знаю, заботится ли маршаллер P / Invoke, но я всегда видел используемые конструкции.

  • Размер символа может быть проблемой.

    CAR CH имеет ширину 8 бит (ANSI), а System.Char .Net - 16 бит (Unicode). Чтобы дать маршаллеру как можно больше информации, чтобы он выполнял правильное преобразование, попробуйте добавить «CharSet = CharSet.Ansi» в атрибуты DllImport и StructLayout и измените объявления строк в WFSVersion:

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
    public string szDescription;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
    public string szSystemStatus;
    
  • Другой проблемой может быть выравнивание данных в структурах. Если выравнивание не было указано при компиляции структуры C, элементы данных в структуре, вероятно, были выровнены на границе одного или двух байтов. Попробуйте использовать Pack в атрибуте WFSVersion StructLayout:

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
    // Try pack values of 2, 4 and 8 if 1 doesn't work.
    

И несколько вопросов:

  • Была ли MyFunction предназначена для вызова из не-C кода? Первоначальный автор, возможно, написал код, который предполагает, что передаваемые данные выделяются с помощью менеджера памяти времени выполнения C.

  • Использует ли код в C DLL указатели, переданные ему для последующей обработки после возврата MyFunction? Если это так - и при условии, что в такой ситуации можно / мудро / разумно идти вперед - может потребоваться «закрепить» структуры, переданные MyFunction, с помощью ключевого слова fixed. Плюс, возможно, есть проблемы с безопасностью.

0 голосов
/ 03 июня 2009

Вы уверены, что это не hApp? Это похоже на входной параметр, дескриптор запрашивающего приложения. Быстрый гугл ... да, есть функция для создания дескриптора приложения и параметр по умолчанию, который вы можете использовать.

0 голосов
/ 03 июня 2009

Я полагаю, что один из ваших указателей (lpSrvcVersion, lpSPIVersion или lphService) недоступен из вашего приложения .NET. Можете ли вы попробовать изменить DLL (если она ваша) и посмотреть, сможете ли вы заставить ее работать без указателей? (Я знаю, что вам придется добавить их позже, но, по крайней мере, вы можете сузить местоположение проблемы.)

0 голосов
/ 03 июня 2009

Я нашел AppVerifier , полезный для отслеживания P / Invoke проблем с маршалом раньше. Это бесплатно и от Microsoft.

0 голосов
/ 02 июня 2009

Я точно не знаю, в чем проблема с этим, но, надеюсь, это станет отправной точкой.

Попробуйте поискать опции в атрибуте DllImport, это может быть связано с сортировкой строк.

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]

Возможно, вам понадобится опция CharSet.

...