Использование 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 и анализ выходных данных также является возможным путем интеграции, который менее подвержен критическим изменениям в будущем.обновления этой внешней библиотеки.