C # DLLImport преобразует "const char *" в строку - PullRequest
0 голосов
/ 12 марта 2019

Мне нужно реализовать этот DLLImport в C #

const char* PegaSolicitacao(const char* CNPJ,
                            const char* CPF,
                            const char* CRM,
                            const char* UF_CRM,
                            const char* DT_EMISSAO );

Dll можно найти по этой ссылке https://farmaciapopular -portal-homologacao.saude.gov.br / farmaciapopular-portal / gbas/GBASMSB_2-Client.rar

Внутри .RAR \ GBASMSB_2-Client \ Ofd SDK 0.2 Windows.zip -> gbasmsb_library.dll

Единственный способчто я получил возврат был с этим кодом:

        [DllImport(@"gbasmsb_library.dll")]
    public static extern char PegaSolicitacao(string CNPJ,
                                              string CPF,
                                              string CRM,
                                              string UF_CRM,
                                              string DT_Emissao);

            var Teste = PegaSolicitacao("31617905000139",
                                    "99999999484",
                                    "30828",
                                    "SP",
                                    DateTime.Today.ToString("d"));

Но возвращение будет строкой, а не символом.Когда я попытался вернуть строку в DLLImport, система перестала работать, если я попытался вернуть char [], я получил исключение, сообщающее мне о Marshaling.

Я новичок в C # и никогда не работал с MarshalAs, но смотрюна форуме я попробовал некоторые варианты, такие как:

                [DllImport(@"gbasmsb_library.dll", CharSet = CharSet.Ansi)]
    [return: MarshalAs(UnmanagedType.LPTStr)]
    public static extern char[] PegaSolicitacao([MarshalAs(UnmanagedType.LPArray)]char[] CNPJ,
                                                [MarshalAs(UnmanagedType.LPArray)]char[] CPF,
                                                [MarshalAs(UnmanagedType.LPArray)]char[] CRM,
                                                [MarshalAs(UnmanagedType.LPArray)]char[] UF_CRM,
                                                [MarshalAs(UnmanagedType.LPArray)]char[] DT_Emissao);

и некоторые другие варианты, но я не могу найти правильный вариант.

1 Ответ

2 голосов
/ 12 марта 2019

Использование DLL

Я использовал DLL Export Viewer, чтобы увидеть экспортированные функции.Быстрый поиск в Google привел к следующим определениям экспорта C:

const char* IdentificaEstacao();
const char* PegaSolicitacao( const char* CNPJ, const char* CPF, const char* CRM, const char* UF_CRM, const char* DT_EMISSAO );
const char* PegaConfirmacao( const char* CNPJ, const char* NU_AUTORIZACAO, const char* NU_CUPOM_FISCAL );

Из-за его простоты я решил начать с IdentificaEstacao, который должен возвращать идентификатор, идентифицирующий станцию.

Iпробовал все виды возвращаемых значений MarshalAs, CharSet и CallingConvention, но не смог заставить его работать с импортом, который возвращает тип string.Итак, давайте вместо этого изменим тип возвращаемого значения на IntPtr:

[DllImport("gbasmsb_library.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr IdentificaEstacao();

Теперь, при вызове этой функции, вы получите обратно IntPtr, который указывает на адрес памяти.При проверке содержимого этой области памяти ( Отладка> Windows> Память> Память1 во время паузы на точке останова) вы можете увидеть однобайтовую строку с нулевым символом в конце (похожую на данные Base64).

Я попытался освободить его одним из Marshal.Free... методов, но это не сработало.Я вызывал один и тот же метод несколько раз, и каждый раз мы возвращаем один и тот же адрес памяти в IntPtr, что заставляет меня догадываться, что они используют глобальную распределенную строку, которая не должна быть освобождена вызывающей стороной (которая можеттакже причина того, что тип возврата string не работает).

С помощью приведенного ниже кода мы можем получить идентификатор станции:

var ptr = IdentificaEstacao();
var stationIdentifier = Marshal.PtrToStringAnsi(ptr);

Давайте изменимподпись другого импорта таким же образом:

[DllImport("gbasmsb_library.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr PegaSolicitacao(
    [MarshalAs(UnmanagedType.LPStr)] string CNPJ,
    [MarshalAs(UnmanagedType.LPStr)] string CPF,
    [MarshalAs(UnmanagedType.LPStr)] string CRM,
    [MarshalAs(UnmanagedType.LPStr)] string UF_CRM,
    [MarshalAs(UnmanagedType.LPStr)] string DT_Emissao);

И сделать этот тестовый вызов:

var ptr = PegaSolicitacao("31617905000139",
                           "99999999484",
                           "30828",
                           "SP",
                           DateTime.Today.ToString("d"));

Это снова возвращает указатель на статическую строку (вызов ее несколько раз возвращаеттот же адрес памяти), так что вы можете просто получить результат, снова вызвав Marshal.PtrToStringAnsi(ptr);.

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

Обратите внимание, что я изменил CallingConvention с StdCall на Cdecl, поэтому мы можем использовать string в качестве входных параметров без получения несбалансированныйстек исключение.

Использование EXE

Я также заметил, что архив содержит исполняемый файл gbasmsb_gbas.exe, который может выполнять те же функции.

gbasmsb_gbas.exe --i дает вам идентификатор станции, в то время как gbasmsb_gbas.exe --solicitacao 99999999484 31617905000139 30828 SP 12/03/2019 возвращает информацию о запросе.

Вызов EXE и анализ выходных данных также является возможным путем интеграции, который менее подвержен критическим изменениям в будущем.обновления этой внешней библиотеки.

...