我的问题是关于软件包提供的Win32-Ada绑定的:
我想使用软件包Win32.Mgmtapi
中定义的以下功能:
function SnmpMgrOidToStr
(oid : access Win32.Snmp.AsnObjectIdentifier;
string : access Win32.LPSTR)
return Win32.BOOL;
我可以访问类型为Variable_Binding
的变量Win32.Snmp.a_RFC1157VarBind_t
。软件包Win32.Snmp
中定义了以下四种类型:
type AsnObjectIdentifier is record
idLength : Win32.UINT;
ids : Win32.PUINT;
end record;
subtype AsnObjectName is AsnObjectIdentifier;
type RFC1157VarBind is record
name : AsnObjectName;
value : AsnObjectSyntax;
end record;
type a_RFC1157VarBind_t is access all RFC1157VarBind;
缩短的示例代码:
with Win32;
with Win32.Mgmtapi;
with Win32.Snmp;
procedure Test is
begin
-- [...]
declare
Variable_Binding : aliased Win32.Snmp.a_RFC1157VarBind_t := (...);
OID_String : access Win32.LPSTR;
Return_Value : Win32.BOOL;
begin
Return_Value := Win32.Mgmtapi.SnmpMgrOidToStr
(oid => Variable_Binding.name, -- type is not compatible
string => OID_String);
end;
end Test;
我的三个问题:
SnmpMgrOidToStr
需要参数name
的访问类型。类型为name
定义的组件RFC1157VarBind
与该访问类型不兼容。将变量name
的组件Variable_Binding
转换为访问类型有哪些选择?SnmpMgrOidToStr
为类型OID_String
的变量Win32.LPSTR
分配了所需的内存。如何将类型Win32.LPSTR
转换为Ada字符串(String
/ Unbounded_String
)?更新:
我添加了完整的SNMP示例。这需要启用并配置的Windows SNMP服务。程序输出应为:
Opened session.
Converted string to object identifier.
Copied object identifier.
Sent request.
Type is: 4
WORKSTATION
system.sysName.0
Closed session.
函数SnmpMgrRequest
的文档指出:
请注意,必须使用SnmpUtilMemAlloc函数分配由SnmpVarBindList结构指向的SnmpVarBind数组。
根据函数SnmpUtilMemAlloc
的文档,应使用函数SnmpUtilMemFree
释放分配的内存。
我认为我正在使用以下过程调用正确分配内存:
-- Allocate memory
-- Allocated memory is not freed properly
SnmpUtilMemAlloc (Win32.UINT (Win32.Snmp.RFC1157VarBind'Size * System.Storage_Unit));
新问题:
SnmpUtilMemFree
释放分配的内存?目前,根据工具 Dr无法正确释放内存。内存(版本2.2.0-1):
Error #1: LEAK 1536 direct bytes 0x0418b850-0x0418be50 + 0 indirect bytes
# 0 replace_RtlAllocateHeap [d:\drmemory_package\common\alloc_replace.c:3771]
# 1 KERNELBASE.dll!GlobalAlloc +0x6d (0x75404015 <KERNELBASE.dll+0x14015>)
# 2 snmpapi.dll!SnmpUtilMemAlloc +0xf (0x73db1ee8 <snmpapi.dll+0x1ee8>)
# 3 _ada_snmp_example [C:/Users/username/Desktop/SNMPExample/src/snmp_example.adb:123]
# 4 main [C:\Users\username\Desktop\SNMPExample\obj/b__snmp_example.adb:259]
完整源代码:
with Ada.Text_IO;
with Ada.Unchecked_Conversion;
with Interfaces.C.Strings;
with System;
with Win32;
with Win32.Mgmtapi;
with Win32.Snmp;
procedure SNMP_Example
is
-- Imported functions and procedures
procedure SnmpUtilMemAlloc (nBytes : Win32.UINT);
pragma Import (Stdcall, SnmpUtilMemAlloc, "SnmpUtilMemAlloc");
procedure SnmpUtilMemFree (pMem : Win32.LPVOID);
pragma Import (Stdcall, SnmpUtilMemFree, "SnmpUtilMemFree");
function SnmpUtilOidCpy
(DestObjId : access Win32.Snmp.AsnObjectIdentifier;
SrcObjId : access Win32.Snmp.AsnObjectIdentifier)
return Win32.INT;
pragma Import (Stdcall, SnmpUtilOidCpy, "SnmpUtilOidCpy");
procedure SnmpUtilOidFree (Obj : access Win32.Snmp.AsnObjectIdentifier);
pragma Import (Stdcall, SnmpUtilOidFree, "SnmpUtilOidFree");
procedure SnmpUtilVarBindFree (VarBind : access Win32.Snmp.RFC1157VarBind);
pragma Import (Stdcall, SnmpUtilVarBindFree, "SnmpUtilVarBindFree");
procedure SnmpUtilVarBindListFree (VarBindList : access Win32.Snmp.RFC1157VarBindList);
pragma Import (Stdcall, SnmpUtilVarBindListFree, "SnmpUtilVarBindListFree");
-- Conversion related
function To_LPVOID is new Ada.Unchecked_Conversion (Win32.LPSTR, Win32.LPVOID);
function To_PCSTR is new Ada.Unchecked_Conversion (Win32.PBYTE, Win32.PCSTR);
-- Linker options
pragma Linker_Options ("-lmgmtapi");
pragma Linker_Options ("-lsnmpapi");
-- Connection related (Ada)
IP_Address : constant String := "127.0.0.1";
Community_String : constant String := "public";
Timeout_MS : constant Integer := 500;
Number_Of_Retries : constant Integer := 1;
-- Connection related (C)
IP_Address_C : Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_String (IP_Address);
Community_String_C : Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_String (Community_String);
-- Connection related (Win32)
IP_Address_Win32 : constant Win32.LPSTR := Win32.To_PSTR (IP_Address_C);
Community_String_Win32 : constant Win32.LPSTR := Win32.To_PSTR (Community_String_C);
Timeout_MS_Win32 : constant Win32.INT := Win32.INT (Timeout_MS);
Number_Of_Retries_Win32 : constant Win32.INT := Win32.INT (Number_Of_Retries);
-- Comparison of types related
use type Interfaces.C.int;
use type Win32.BOOL;
use type Win32.Mgmtapi.LPSNMP_MGR_SESSION;
-- Session
SNMP_Session : Win32.Mgmtapi.LPSNMP_MGR_SESSION;
-- Custom types
type Counter_Type is mod 2**32 - 1;
type Gauge_Type is mod 2**32 - 1;
begin
-- Open session
SNMP_Session := Win32.Mgmtapi.SnmpMgrOpen
(lpAgentAddress => IP_Address_Win32,
lpAgentCommunity => Community_String_Win32,
nTimeOut => Timeout_MS_Win32,
nRetries => Number_Of_Retries_Win32);
if SNMP_Session = null then
Ada.Text_IO.Put_Line ("Failed to open session.");
else
Ada.Text_IO.Put_Line ("Opened session.");
Send_Request : declare
-- Request related (Ada)
SNMP_Object_Identifier_Ada : constant String := ".1.3.6.1.2.1.1.5.0";
-- Request related (C)
SNMP_Object_Identifier_C : Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_String (SNMP_Object_Identifier_Ada);
-- Request related (Win32)
SNMP_Object_Identifier_Win32 : constant Win32.LPSTR := Win32.To_PSTR (SNMP_Object_Identifier_C);
SNMP_Object_Identifier : aliased Win32.Snmp.AsnObjectIdentifier;
Variable_Bindings : aliased Win32.Snmp.RFC1157VarBindList;
Variable_Bindings_Entry : aliased Win32.Snmp.RFC1157VarBind;
Error_Status : aliased Win32.Snmp.AsnInteger;
Error_Index : aliased Win32.Snmp.AsnInteger;
Return_Value_String_To_OID_Conversion : Win32.BOOL;
Return_Value_OID_Copy : Win32.INT;
Return_Value_Request : Win32.INT;
Return_Value_OID_To_String_Conversion : Win32.BOOL;
begin
Return_Value_String_To_OID_Conversion := Win32.Mgmtapi.SnmpMgrStrToOid
(string => SNMP_Object_Identifier_Win32,
oid => SNMP_Object_Identifier'Access);
if Return_Value_String_To_OID_Conversion = 0 then
Ada.Text_IO.Put_Line ("Failed to convert string to object identifier.");
else
Ada.Text_IO.Put_Line ("Converted string to object identifier.");
-- Allocate memory
-- Allocated memory is not freed properly
SnmpUtilMemAlloc (Win32.UINT (Win32.Snmp.RFC1157VarBind'Size * System.Storage_Unit));
Return_Value_OID_Copy := SnmpUtilOidCpy
(DestObjId => Variable_Bindings_Entry.name'Unrestricted_Access,
SrcObjId => SNMP_Object_Identifier'Access);
if Return_Value_OID_Copy = 0 then
Ada.Text_IO.Put_Line ("Failed to copy object identifier.");
else
Ada.Text_IO.Put_Line ("Copied object identifier.");
-- Construct variable bindings entry
Variable_Bindings_Entry.value.asnType := Win32.Snmp.ASN_NULL;
-- Construct variable bindings
Variable_Bindings.len := 1;
Variable_Bindings.list := Variable_Bindings_Entry'Unchecked_Access;
Return_Value_Request := Win32.Mgmtapi.SnmpMgrRequest
(session => SNMP_Session,
requestType => Win32.Snmp.ASN_RFC1157_GETREQUEST,
variableBindings => Variable_Bindings'Access,
errorStatus => Error_Status'Access,
errorIndex => Error_Index'Access);
if Return_Value_Request = 0 then
Ada.Text_IO.Put_Line ("Failed to send request.");
else
Ada.Text_IO.Put_Line ("Sent request.");
Ada.Text_IO.Put_Line ("Type is:" & Win32.BYTE'Image (Variable_Bindings.list.value.asnType));
case Variable_Bindings.list.value.asnType is
when Win32.Snmp.ASN_INTEGER =>
Ada.Text_IO.Put_Line (Integer'Image (Integer (Variable_Bindings.list.value.asnValue.number)));
when Win32.Snmp.ASN_OCTETSTRING =>
Ada.Text_IO.Put_Line (Interfaces.C.Strings.Value (Win32.To_Chars_Ptr (To_PCSTR (Variable_Bindings.list.value.asnValue.string.stream))));
when Win32.Snmp.ASN_RFC1155_COUNTER =>
Ada.Text_IO.Put_Line (Counter_Type'Image (Counter_Type (Variable_Bindings.list.value.asnValue.counter)));
when Win32.Snmp.ASN_RFC1155_GAUGE =>
Ada.Text_IO.Put_Line (Gauge_Type'Image (Gauge_Type (Variable_Bindings.list.value.asnValue.counter)));
when Win32.Snmp.ASN_RFC1155_TIMETICKS =>
Ada.Text_IO.Put_Line (Duration'Image (Duration (Variable_Bindings.list.value.asnValue.ticks) / 100));
when others =>
Ada.Text_IO.Put_Line ("Unsupported type.");
end case;
-- Convert object identifier to string
Convert_OID_To_String : declare
OID_String_Win32 : aliased Win32.LPSTR;
begin
Return_Value_OID_To_String_Conversion := Win32.Mgmtapi.SnmpMgrOidToStr
(oid => Variable_Bindings.list.name'Unrestricted_Access,
string => OID_String_Win32'Unchecked_Access);
if Return_Value_OID_To_String_Conversion = 0 then
Ada.Text_IO.Put_Line ("Failed to convert object identifier to string.");
else
Ada.Text_IO.Put_Line (Interfaces.C.Strings.Value (Win32.To_Chars_Ptr (OID_String_Win32)));
end if;
SnmpUtilMemFree (To_LPVOID (OID_String_Win32));
end Convert_OID_To_String;
-- Free memory
SnmpUtilOidFree (SNMP_Object_Identifier'Access);
SnmpUtilVarBindFree (Variable_Bindings_Entry'Access);
SnmpUtilVarBindListFree (Variable_Bindings'Access);
Interfaces.C.Strings.Free (IP_Address_C);
Interfaces.C.Strings.Free (Community_String_C);
Interfaces.C.Strings.Free (SNMP_Object_Identifier_C);
end if;
end if;
end if;
end Send_Request;
Close_Session : declare
Return_Value : constant Win32.BOOL := Win32.Mgmtapi.SnmpMgrClose (SNMP_Session);
begin
if Return_Value = 0 then
Ada.Text_IO.Put_Line ("Failed to close session.");
else
Ada.Text_IO.Put_Line ("Closed session.");
end if;
end Close_Session;
end if;
end SNMP_Example;
答案 0 :(得分:2)
不幸的是,我无法创建一个最小的工作示例来测试代码,因此我不能保证以下代码会真正起作用:
test.adb
with Ada.Text_IO;
with Ada.Unchecked_Conversion;
with Interfaces.C.Strings;
with Win32;
with Win32.Mgmtapi;
with Win32.Snmp;
procedure Test is
pragma Linker_Options ("-lMgmtapi"); -- for SnmpMgrOidToStr
pragma Linker_Options ("-lSnmpapi"); -- for SnmpUtilMemFree
begin
-- [...]
declare
-- Field "name" in type "RFC1157VarBind" is not aliased so you cannot
-- use 'Unchecked_Access. I see two options here:
--
-- (1) Use 'Unrestricted_Access on the field "name".
-- (2) Copy "VarBind_Ptr.name" into a new aliased variable and use 'Unchecked_Access.
--
-- I *assume* that the library behind "SnmpMgrOidToStr" will not "store"
-- our pointer and dereferece it after we left this block of code (resulting
-- in an invalid dereference). If this assumption holds, then we
-- can use 'Unchecked_Access.
--
-- As "VarBind_Ptr" is already an access type, its field must also have a
-- memory address (depite it not being defined as aliased). Hence, I
-- think it's safe to use the very permissive 'Unrestricted_Access
-- attribute here to obtain a reference.
VarBind_Ptr : Win32.Snmp.a_RFC1157VarBind_t;
OID : aliased Win32.Snmp.AsnObjectIdentifier := VarBind_Ptr.name;
OID_String_Ptr : aliased Win32.LPSTR;
Return_Value : Win32.BOOL;
begin
-- Option 1
Return_Value := Win32.Mgmtapi.SnmpMgrOidToStr
(oid => VarBind_Ptr.name'Unrestricted_Access,
string => OID_String_Ptr'Unchecked_Access);
-- Option 2
Return_Value := Win32.Mgmtapi.SnmpMgrOidToStr
(oid => OID'Unchecked_Access,
string => OID_String_Ptr'Unchecked_Access);
declare
-- Win32.ads contains a convenience function "To_Chars_Ptr" to
-- convert "Win32.LPSTR" to "Interfaces.C.Strings.chars_ptr". Hence,
-- we can convert "OID_String_Ptr" to an Ada string via "chars_ptr":
--
-- Win32.LPSTR ---> chars_ptr ---> String
--
-- Note that the character array to which "Win32.LPSTR" points is, in
-- this particular case, null terminated (see [1]). This might not
-- always be the case [3].
use Ada.Text_IO;
use Interfaces.C.Strings;
begin
Put_Line (Value (Win32.To_Chars_Ptr (OID_String_Ptr)));
end;
declare
-- The documentation [1] states that the string returned by
-- "SnmpMgrOidToStr" should be freed using "SnmpUtilMemFree" [2]. This
-- function seems to be missing in the win32ada library (?), so we
-- need to import it ourselves.
procedure SnmpUtilMemFree (pMem : Win32.LPVOID)
with Import, Convention => Stdcall, Link_Name => "SnmpUtilMemFree";
function To_LPVOID is
new Ada.Unchecked_Conversion (Win32.LPSTR, Win32.LPVOID);
begin
SnmpUtilMemFree (To_LPVOID (OID_String_Ptr));
end;
end;
end Test;
参考文献: