Send sms using Mbn Api C# - c#

I'm looking for a way to send Sms throught MbnApi using :
http://msdn.microsoft.com/en-us/library/windows/desktop/dd323167(v=vs.85).aspx
IMbnSms interface. But it's not working at all.
I could not have a sample project to see how to do it. I'm coding on C# .Net
Thx
public class NaErrorHandling
{
public Dictionary<UInt32, string> Hresults { get;private set; }
public NaErrorHandling()
{
Hresults = new Dictionary<UInt32, string>();
Init();
}
private void Init()
{
Hresults.Add(0, "S_OK");
Hresults.Add(0x8054820a, "E_MBN_SIM_NOT_INSERTED");
Hresults.Add(0x80070057, "E_INVALIDARG");
Hresults.Add(0x80548202, "E_MBN_BAD_SIM");
Hresults.Add(0x80548210, "E_MBN_PIN_REQUIRED");
Hresults.Add(0x80548224, "E_MBN_SMS_MEMORY_FAILURE");
Hresults.Add(0x80548226, "E_MBN_SMS_UNKNOWN_SMSC_ADDRESS");
Hresults.Add(0x80548209, "E_MBN_SERVICE_NOT_ACTIVATED");
Hresults.Add(0x80548225, "E_MBN_SMS_NETWORK_TIMEOUT");
Hresults.Add(0x8054820d, "E_MBN_NOT_REGISTERED");
Hresults.Add(0x80548223, "E_MBN_SMS_LANG_NOT_SUPPORTED");
Hresults.Add(0x80548220, "E_MBN_SMS_ENCODING_NOT_SUPPORTED");
Hresults.Add(0x80548228, "E_MBN_SMS_OPERATION_NOT_ALLOWED");
Hresults.Add(0x80548229, "E_MBN_SMS_MEMORY_FULL");
Hresults.Add(0X80070015, "ERROR_NOT_READY");
Hresults.Add(1062, "ERROR_SERVICE_NOT_ACTIVE");
}
public string GetError(int error)
{
UInt32 uerror = (UInt32)error;
if (Hresults.ContainsKey(uerror))
return Hresults[uerror];
return "Error Not Know";
}
}
public class SmsResult : MbnApi.IMbnSmsEvents
{
public void OnSetSmsConfigurationComplete(MbnApi.IMbnSms sms, uint requestID, int status)
{
}
public void OnSmsConfigurationChange(MbnApi.IMbnSms sms)
{
}
public void OnSmsDeleteComplete(MbnApi.IMbnSms sms, uint requestID, int status)
{
}
public void OnSmsNewClass0Message(MbnApi.IMbnSms sms, MbnApi.MBN_SMS_FORMAT SmsFormat, object[] readMsgs)
{
}
public void OnSmsReadComplete(MbnApi.IMbnSms sms, MbnApi.MBN_SMS_FORMAT SmsFormat, object[] readMsgs, bool moreMsgs, uint requestID, int status)
{
string messageShow = String.Empty;
SMSPDULib.SMS rms= new SMSPDULib.SMS();
foreach (var msgReceived in readMsgs)
{
var msg = msgReceived as IMbnSmsReadMsgPdu;
messageShow += GetSms(msg.PduData) + "\n";
}
System.Windows.MessageBox.Show( messageShow);
}
private string GetSms(string pduSource)
{
SMSType smsType = SMSBase.GetSMSType(pduSource);
string message = string.Empty;
switch (smsType)
{
case SMSType.SMS:
SMS sms = new SMS();
SMS.Fetch(sms, ref pduSource);
message = sms.Message;
//Use instance of SMS class here
break;
case SMSType.StatusReport:
SMSStatusReport statusReport = new SMSStatusReport();
SMSStatusReport.Fetch(statusReport, ref pduSource);
//Use instance of SMSStatusReport class here
break;
}
return message;
}
public void OnSmsSendComplete(MbnApi.IMbnSms sms, uint requestID, int status)
{
NaErrorHandling handling = new NaErrorHandling();
var res = handling.GetError(status);
UInt32 uStatus = (UInt32)status;
}
public void OnSmsStatusChange(MbnApi.IMbnSms sms)
{
throw new NotImplementedException();
}
}
public void SendSms()
{
MbnConnectionManager mbnConnectionMgr = new MbnConnectionManager();
IMbnConnectionManager connMgr = (IMbnConnectionManager)mbnConnectionMgr;
try
{
IMbnConnection[] arrConn = connMgr.GetConnections();
if (arrConn != null)
{
String res = String.Empty;
foreach (var connection in arrConn)
{
res += String.Format("Connection ID : {0} | Interface Id : {1}\n", connection.ConnectionID, connection.InterfaceID);
}
//MessageBox.Show(res);
var conn = arrConn[0];
uint prId = 0;
//conn.Connect(MBN_CONNECTION_MODE.MBN_CONNECTION_MODE_PROFILE, "Bouygues Telecom", out prId);
MbnInterfaceManager mbnInterfaceMgr = new MbnInterfaceManager();
var mbnInterface = mbnInterfaceMgr as IMbnInterfaceManager;
var selectInterface = mbnInterface.GetInterface(conn.InterfaceID);
MBN_INTERFACE_CAPS caps = selectInterface.GetInterfaceCapability();
var theCap =caps.smsCaps;// (uint)MBN_SMS_CAPS.MBN_SMS_CAPS_PDU_SEND;
IConnectionPointContainer icpc;
icpc = (IConnectionPointContainer)mbnInterfaceMgr;
Guid IID_IMbnConnectionEvents = typeof(IMbnSmsEvents).GUID;
IConnectionPoint icp;
icpc.FindConnectionPoint(ref IID_IMbnConnectionEvents, out icp);
SmsResult connEvtsSink = new SmsResult();
uint cookie;
icp.Advise(connEvtsSink, out cookie);
var sms = selectInterface as IMbnSms;
string phoneDest = "";
string pdu = String.Empty;
byte[] size = new byte[5];
uint requestId = 456;
MBN_SMS_STATUS_INFO smsStatusInfo = new MBN_SMS_STATUS_INFO();
smsStatusInfo.flag = 10;
try
{
IMbnSmsConfiguration smsConfig = sms.GetSmsConfiguration();
sms.GetSmsStatus(out smsStatusInfo);
var wtf = smsStatusInfo;
string serviceDatacenter = smsConfig.ServiceCenterAddress;
CreatePduSms(null, phoneDest, "Hello", out pdu, out size);
//var res1 = SMSPDULib.SMS.GetBytes(pdu, 16);
var total = SMSPDULib.SMS.GetBytes(pdu);
byte wantedSize =(byte) total.Length;
CreatePduSms(serviceDatacenter, phoneDest, "Hello", out pdu, out size);
//var byteArray = StringToByteArray(pdu);
//var str = System.Text.Encoding.UTF7.GetString(byteArray);
sms.SmsSendPdu(pdu, wantedSize, out requestId);
//sms.SmsSendCdma(phoneDest, MBN_SMS_CDMA_ENCODING.MBN_SMS_CDMA_ENCODING_7BIT_ASCII, MBN_SMS_CDMA_LANG.MBN_SMS_CDMA_LANG_FRENCH, 2, GetBytes("Hi"), out requestId);
}
catch (Exception exp)
{
//MessageBox.Show("Cannot send Message : " + exp.Message);
}
}
}
catch (Exception e)
{
//MessageBox.Show(e.Message + e.StackTrace);
}
}
private void CreatePduSms(string servAddress, string phoneDest, string msg, out string pdu, out byte[] size)
{
SMSPDULib.SMS sms = new SMSPDULib.SMS();
sms.Direction = SMSPDULib.SMSDirection.Submited;
sms.PhoneNumber = "";
if (servAddress != null)
sms.ServiceCenterNumber = servAddress;
sms.ValidityPeriod = new TimeSpan(4, 0, 0, 0);
sms.Message = msg + DateTime.Now.ToString();
pdu = sms.Compose(SMSPDULib.SMS.SMSEncoding._7bit);
size = 0;
}

Related

C# (NET6) - creating keypair (public cert/private key) FROM PFX

I'd like to export PFX (public + private part) to separate files (*.cer, *.key). PFX may (or may not) be password protected.
I've tried to achieve my goal, which is fine with certificate (public), but causes a problem with private key.
My bootastrap class is as below:
public class CertBootstrap
{
private readonly FileInfo CertificateFile;
private readonly SecureString CertificatePassword;
public bool HasPasswordBeenSet { get; private set; } = false;
public X509Certificate2 Certificate { get; private set; }
public CertBootstrap(FileInfo certificationFile, string password)
{
if(!certificationFile.Exists)
{
throw new FileNotFoundException(certificationFile.FullName);
}
CertificateFile = certificationFile;
HasPasswordBeenSet = true;
CertificatePassword = ConvertPassword(password);
}
public CertBootstrap(FileInfo certificationFile)
{
if(!certificationFile.Exists)
{
throw new FileNotFoundException(certificationFile.FullName);
}
CertificateFile = certificationFile;
HasPasswordBeenSet = false;
}
public CertBootstrap(string certificationFullFileName, string password)
{
var certificateFile = new FileInfo(certificationFullFileName);
if(certificateFile == null || !certificateFile.Exists)
{
throw new FileNotFoundException(certificationFullFileName);
}
CertificateFile = certificateFile;
HasPasswordBeenSet = true;
CertificatePassword = ConvertPassword(password);
}
public CertBootstrap(string certificationFullFileName)
{
var certificateFile = new FileInfo(certificationFullFileName);
if(certificateFile == null || !certificateFile.Exists)
{
throw new FileNotFoundException(certificationFullFileName);
}
CertificateFile = certificateFile;
HasPasswordBeenSet = false;
}
public bool VerifyPassword(string password)
{
try
{
byte[] fileContent = File.ReadAllBytes(CertificateFile.FullName);
var certificate = new X509Certificate2(fileContent, password);
}
catch(CryptographicException ex)
{
if((ex.HResult & 0xFFFF) == 0x56)
{
return false;
}
;
throw;
}
return true;
}
private static SecureString ConvertPassword(string password)
{
var secure = new SecureString();
foreach(char c in password)
{
secure.AppendChar(c);
}
return secure;
}
public void LoadBootstrap()
{
LoadBootstrap(X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
}
public void LoadBootstrap(X509KeyStorageFlags keyStorageFlags)
{
this.Certificate = this.HasPasswordBeenSet ? new X509Certificate2(this.CertificateFile.FullName, this.CertificatePassword, keyStorageFlags) : new X509Certificate2(this.CertificateFile.FullName);
}
private static RSA ExportPrivateKeyCoreNet5Net6(RSA privateKey)
{
var password = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString());
PbeParameters pPar = new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 100_000);
try
{
byte[] tempG = privateKey.ExportEncryptedPkcs8PrivateKey(password, pPar);
using(RSA exportRewriter = RSA.Create())
{
exportRewriter.ExportParameters(true);
exportRewriter.ImportEncryptedPkcs8PrivateKey(password, tempG, out _);
return exportRewriter;
}
}
catch(Exception e)
{
//catch this error
string em = e.Message;
throw;
}
}
private static byte[] ExportPrivateKey(RSA privateKey)
{
try
{
return privateKey.ExportPkcs8PrivateKey();
}
catch(CryptographicException)
{
}
return ExportPrivateKeyCoreNet5Net6(privateKey).ExportRSAPublicKey();
}
public byte[] GeneratePrivateKeyPem()
{
byte[] privateCertKeyBytes = ExportPrivateKey(this.Certificate.GetRSAPrivateKey());
char[] newPemData = PemEncoding.Write("PRIVATE KEY", privateCertKeyBytes);
return newPemData.Select(c => (byte)c).ToArray();
}
public byte[] GenerateCertificatePem()
{
var certData = Certificate.RawData;
var newPemData = PemEncoding.Write("CERTIFICATE", certData);
return newPemData.Select(c => (byte)c).ToArray();
}
public FileInfo SaveCertificate()
{
var newData = GenerateCertificatePem();
var oldFile = Path.GetFileNameWithoutExtension(CertificateFile.FullName);
var newCertPemFile = new FileInfo($#"{CertificateFile.DirectoryName}\{oldFile}.cer");
return SaveNewCertFile(newCertPemFile, newData);
}
public FileInfo SavePrivateKey()
{
var newData = GeneratePrivateKeyPem();
var oldFile = Path.GetFileNameWithoutExtension(CertificateFile.FullName);
var newPrivateKeyPemFile = new FileInfo($#"{CertificateFile.DirectoryName}\{oldFile}.key");
return SaveNewCertFile(newPrivateKeyPemFile, newData);
}
public FileInfo SaveNewCertFile(FileInfo newFile, byte[] data)
{
File.WriteAllBytes(newFile.FullName, data);
return newFile;
}
}
With it, I'm trying to save private part:
// (...)
CertBootstrap certBootstrap = new CertBootstrap("simple.pfx");
certBootstrap.LoadBootstrap();
// (...)
certBootstrap.SavePrivateKey();
and it all ends up with exception: "The requested operation it not supported".
I found similar thread: Cannot export RSA private key parameters, the requested operation is not supported and tried to do the same (Export -> Import with password). But did not help.
Can You point me propper direction? Most probably I'm missing something, but am out of ideas for now.
Thank You in advance.

Try to call Clients.Caller but it's not working

Hello I am trying to catch messages from database and transfer to caller but when I used Clients.Caller method then it pass me error like
Using a Hub instance not created by the HubPipeline is unsupported
If I am using
var context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
context.Clients.All.receiver(respose);
then it replying nothing. So please help me to solve out this question.
and I am using below code:
public async Task GetAllMessages(int roomid, int fromuserId, int touserId, int index, int pageSize, int broadcastid = 0)
{
ResponseModel respose = new ResponseModel();
try
{
var model = new MessageDetailModel
{
Index = index,
PageSize = pageSize,
FromUserId = fromuserId,
ToUserId = touserId,
ChatRoomId = roomid,
BroadCastId = broadcastid
};
MesaageModel totalmessages = new MesaageModel();
List<RecentMessage> messages = new List<RecentMessage>();
var usermessages = await _userService.GetAllMessages(model);
messages.AddRange(usermessages);
foreach (var message in messages)
{
model.BroadCastMessageId = model.BroadCastId == 0 ? 0 : message.BroadCastMessageId;
model.ChatRoomMessageId = model.BroadCastId == 0 ? message.ChatRoomMessageId : 0;
List<UserDetail> userofseenby = new List<UserDetail>();
var users = await _userService.GetMessageSeenBy(model);
userofseenby.AddRange(users);
message.SeenBy = userofseenby;
}
//totalmessages.Messages.AddRange(messages);
totalmessages.Messages = messages;
totalmessages.UnseenMessageCount = await _userService.GetUnSeenCount(messages.FirstOrDefault().ChatRoomId, fromuserId);
if (messages.FirstOrDefault().IsBroadCast == true)
totalmessages.Users = (List<UserDetail>)await _userService.GetUsersByBroadCastId(messages.FirstOrDefault().BroadCastId);
else if (messages.FirstOrDefault().IsGroup == true)
totalmessages.Users = (List<UserDetail>)await _userService.GetUsersByChatRoomId((int)messages.FirstOrDefault().ChatRoomId);
respose = ResponseHelper.SuccessResponse("onAllMessagesReceived", totalmessages);
Clients.Caller.receiver(respose); // Error catch here
}
catch (Exception ex)
{
respose.ActionCommand = "onAllMessagesReceived";
respose = ResponseHelper.ErrorResponse(respose.ActionCommand, ex.ToString());
Clients.Caller.receiver(respose);
}
}
And Constructors of class is below :
public ChatHub()
{
_userService = new UserBusinessLogic();
_chatterService = new ChatterBusinessLogic();
}
public ChatHub(IChatterBusinessLogic chatterService, IUserBusinessLogic userService)
{
_chatterService = chatterService;
_userService = userService;
}
And Controller constructor are like this :
public ChatterController()
{
_userService = new UserBusinessLogic();
_chatterService = new ChatterBusinessLogic();
}
public ChatterController(IChatterBusinessLogic chatterService, IUserBusinessLogic userService)
{
_chatterService = chatterService;
_userService = userService;
}

How to declare a var inside a class inside in c#?

I want to declare the following things:
var simlBot = new SimlBot();
var botUser = simlBot.CreateUser();
var packageString = File.ReadAllText("SIMLPackage.simlpk");
simlBot.PackageManager.LoadFromString(packageString);
If I declare it directly in the class I get: "The contextual keyword 'var' may only appear within a local variable declaration or in script code"
I can't declare it inside an if statement because I can't access it from outside.
if (hasBotBeenCreated == false)
{
var simlBot = new SimlBot();
var botUser = simlBot.CreateUser();
var packageString = File.ReadAllText("SIMLPackage.simlpk");
simlBot.PackageManager.LoadFromString(packageString);
}
I need to use the following form inside a function:
var chatRequest = new ChatRequest(textReceived, botUser);
var chatResult = simlBot.Chat(chatRequest);
What can I do? The problem is that the last snippet of code can't access botUser and simlBot. The way I have it working now is recreating the object every time the function is called, but that takes a lot of time.
My complete code is
public partial class Form1 : Form
{
string textEntered = "";
string response = "";
SpeechSynthesizer synth = new SpeechSynthesizer();
bool hasBotBeenCreated = false;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
textEntered = textBox1.Text;
textBox1.Text = "";
if (textEntered != "")
{
sendToAI(textEntered);
}
}
void sendToAI(string textReceived)
{
listBox1.Items.Add(textEntered);
response = getResponse(textEntered);
listBox1.Items.Add(response);
synth.Speak(response);
}
string getResponse(string textReceived)
{
if (hasBotBeenCreated == false)
{
var simlBot = new SimlBot();
var botUser = simlBot.CreateUser();
var packageString = File.ReadAllText("SIMLPackage.simlpk");
simlBot.PackageManager.LoadFromString(packageString);
}
var chatRequest = new ChatRequest(textReceived, botUser);//These two can't access the objects created above
var chatResult = simlBot.Chat(chatRequest);
if (chatResult.Success)
{
var botMessage = chatResult.BotMessage;
return botMessage;
}
else
{
return "I don't have a response for that";
}
}
}
Revision 2:
string getResponse(string textReceived)
{
SimlBot simlBot;
BotUser botUser;
if (hasBotBeenCreated == false)
{
simlBot = new SimlBot();
botUser = simlBot.CreateUser();
var packageString = File.ReadAllText("SIMLPackage.simlpk");
simlBot.PackageManager.LoadFromString(packageString);
}
var chatRequest = new ChatRequest(textReceived, botUser); //These get the error
var chatResult = simlBot.Chat(chatRequest);
if (chatResult.Success)
{
var botMessage = chatResult.BotMessage;
return botMessage;
}
else
{
return "I don't have a response for that";
}
}
Add the members:
SimlBot simlBot;
WhatEverTypeOfCreateUserIs botUser;
Then initialize them:
if (hasBotBeenCreated == false)
{
simlBot = new SimlBot();
botUser = simlBot.CreateUser();
....
}
---------- Full Code ----------
public partial class Form1 : Form
{
string textEntered = "";
string response = "";
SpeechSynthesizer synth = new SpeechSynthesizer();
bool hasBotBeenCreated = false;
SimlBot simlBot;
BotUser botUser;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
textEntered = textBox1.Text;
textBox1.Text = "";
if (textEntered != "")
{
sendToAI(textEntered);
}
}
void sendToAI(string textReceived)
{
listBox1.Items.Add(textEntered);
response = getResponse(textEntered);
listBox1.Items.Add(response);
synth.Speak(response);
}
string getResponse(string textReceived)
{
if (hasBotBeenCreated == false)
{
simlBot = new SimlBot();
botUser = simlBot.CreateUser();
var packageString = File.ReadAllText("SIMLPackage.simlpk");
simlBot.PackageManager.LoadFromString(packageString);
}
var chatRequest = new ChatRequest(textReceived, botUser);//These two can't access the objects created above
var chatResult = simlBot.Chat(chatRequest);
if (chatResult.Success)
{
var botMessage = chatResult.BotMessage;
return botMessage;
}
else
{
return "I don't have a response for that";
}
}
}

Programmatically add route

I write a simple utility that adds a route for specific interface. Code is very simple:
using System;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Text;
class Program
{
static void Main(string[] args)
{
const string firstConnection = "xxxx.yyyyy";
const string routeAddMask = "route add XX.XX.XX.XX mask 255.255.255.255 XXX.XXX.XXX.XXX METRIC 99 IF {0}";
StartProcess("rasdial", firstConnection);
var interfaceId = GetInterfaceId(firstConnection);
string routeAdd = string.Format(routeAddMask, interfaceId);
StartProcess("route", routeAdd);
}
private static int GetInterfaceId(string name)
{
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
var adapter = Array.Find(adapters, ni => ni.Name == name);
var props = adapter.GetIPProperties().GetIPv4Properties();
return props.Index;
}
private static void StartProcess(string name, string args)
{
var process = new Process
{
StartInfo = new ProcessStartInfo(name, args)
{
UseShellExecute = false,
RedirectStandardOutput = true,
StandardOutputEncoding = Encoding.ASCII
}
};
process.Start();
while (!process.HasExited)
{
Console.WriteLine(process.StandardOutput.ReadToEnd());
}
process.WaitForExit();
Console.WriteLine("Process {0} finished, exit code = {1}", name, process.ExitCode);
}
}
rasdial works fine and I get interface number, but when I'm running route I get an error:
Invalid MASK generates an error, that is when (DEST & MASK) != DEST.
I think problem is that process start args are not passed because I get the same error while sending null instead of routeAdd parameter meanwhile this command works fine when i'm prompting it in cmd.
Executable is running as Administrator.
I'm trying to create it with winapi, but it fails too
const string firstConnection = "someconnect";
StartProcess("rasdial", firstConnection);
var interfaceIndex = GetInterfaceIndex(firstConnection); //Everything is fine
var route = new MIB_IPFORWARDROW
{
dwForwardDest = BitConverter.ToUInt32(IPAddress.Parse("XX.XX.XX.XX").GetAddressBytes(), 0),
dwForwardMask = BitConverter.ToUInt32(IPAddress.Parse("255.255.255.255").GetAddressBytes(), 0),
dwForwardNextHop = BitConverter.ToUInt32(IPAddress.Parse("XXX.XXX.XXX.XXX").GetAddressBytes(), 0),
dwForwardMetric1 = 99,
dwForwardIfIndex = interfaceIndex
};
var ipForwardEntry = RouteInterop.CreateIpForwardEntry(ref route);
Console.WriteLine(ipForwardEntry);
returns ERROR_INVALID_PARAMETER
I know that question is not very complex, but I didn't encounter any explanation in the internet, this is why I think this answer would be helpful and this is why I'm writing it instead of deleting my initial question.
You just should specify more parameters to make it work
var route = new MIB_IPFORWARDROW
{
dwForwardDest = BitConverter.ToUInt32(IPAddress.Parse("XX.XX.XX.XX").GetAddressBytes(), 0),
dwForwardMask = BitConverter.ToUInt32(IPAddress.Parse("255.255.255.255").GetAddressBytes(), 0),
dwForwardNextHop = BitConverter.ToUInt32(IPAddress.Parse("XXX.XXX.XXX.XXX").GetAddressBytes(), 0),
dwForwardMetric1 = 99,
dwForwardType = ForwardType.Indirect,
dwForwardProto = ForwardProtocol.NetMGMT,
dwForwardAge = 0,
dwForwardIfIndex = interfaceIndex
};
var ipForwardEntry = RouteInterop.CreateIpForwardEntry(ref route);
Elaborating on Alex Zhukovskiy answer as I've combined his answer with a bunch of MSDN articles. The code below uses the Win32 API's, specifically the "iphlpapi.h" module, the comprehensive documentation can be found in this link on managing routing. The PMIB_IPFORWARDTABLE structure and enums are documented here.
The primary methods for mimicking the "route print", "route add", and "route delete" command are
GetIpForwardTable - Gets the network routing table, "route PRINT"
CreateIpForwardEntry - Add Route, like "route ADD"
DeleteIpFowardEntry - Delete Route, like "route DELETE"
SetIpForwardEntry - Modify existing route, like "route CHANGE"
The comprehensive code using C# can be found in this github repository (Ip4RouteTable) - https://github.com/CodeCowboyOrg/Ip4RouteTable
Setting up the Win32 API Calls in C#
internal static class NativeMethods
{
[DllImport("iphlpapi", CharSet = CharSet.Auto)]
public extern static int GetIpForwardTable(IntPtr /*PMIB_IPFORWARDTABLE*/ pIpForwardTable, ref int /*PULONG*/ pdwSize, bool bOrder);
[DllImport("iphlpapi", CharSet = CharSet.Auto)]
//public extern static int CreateIpForwardEntry(ref /*PMIB_IPFORWARDROW*/ Ip4RouteTable.PMIB_IPFORWARDROW pRoute); Can do by reference or by Pointer
public extern static int CreateIpForwardEntry(IntPtr /*PMIB_IPFORWARDROW*/ pRoute);
[DllImport("iphlpapi", CharSet = CharSet.Auto)]
public extern static int DeleteIpForwardEntry(IntPtr /*PMIB_IPFORWARDROW*/ pRoute);
}
public class Ip4RouteTable
{
[ComVisible(false), StructLayout(LayoutKind.Sequential)]
internal struct IPForwardTable
{
public uint Size;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public PMIB_IPFORWARDROW[] Table;
};
[ComVisible(false), StructLayout(LayoutKind.Sequential)]
internal struct PMIB_IPFORWARDROW
{
internal uint /*DWORD*/ dwForwardDest;
internal uint /*DWORD*/ dwForwardMask;
internal uint /*DWORD*/ dwForwardPolicy;
internal uint /*DWORD*/ dwForwardNextHop;
internal uint /*DWORD*/ dwForwardIfIndex;
internal uint /*DWORD*/ dwForwardType;
internal uint /*DWORD*/ dwForwardProto;
internal uint /*DWORD*/ dwForwardAge;
internal uint /*DWORD*/ dwForwardNextHopAS;
internal uint /*DWORD*/ dwForwardMetric1;
internal uint /*DWORD*/ dwForwardMetric2;
internal uint /*DWORD*/ dwForwardMetric3;
internal uint /*DWORD*/ dwForwardMetric4;
internal uint /*DWORD*/ dwForwardMetric5;
};
static IPForwardTable ReadIPForwardTable(IntPtr tablePtr)
{
var result = (IPForwardTable)Marshal.PtrToStructure(tablePtr, typeof(IPForwardTable));
PMIB_IPFORWARDROW[] table = new PMIB_IPFORWARDROW[result.Size];
IntPtr p = new IntPtr(tablePtr.ToInt64() + Marshal.SizeOf(result.Size));
for (int i = 0; i < result.Size; ++i)
{
table[i] = (PMIB_IPFORWARDROW)Marshal.PtrToStructure(p, typeof(PMIB_IPFORWARDROW));
p = new IntPtr(p.ToInt64() + Marshal.SizeOf(typeof(PMIB_IPFORWARDROW)));
}
result.Table = table;
return result;
}
public static void RoutePrint(bool testing)
{
var fwdTable = IntPtr.Zero;
int size = 0;
var result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
fwdTable = Marshal.AllocHGlobal(size);
result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
var forwardTable = ReadIPForwardTable(fwdTable);
Marshal.FreeHGlobal(fwdTable);
Console.Write("\tNumber of entries: {0}\n", forwardTable.Size);
for (int i = 0; i < forwardTable.Table.Length; ++i)
{
Console.Write("\n\tRoute[{0}] Dest IP: {1}\n", i, new IPAddress((long)forwardTable.Table[i].dwForwardDest).ToString());
Console.Write("\tRoute[{0}] Subnet Mask: {1}\n", i, new IPAddress((long)forwardTable.Table[i].dwForwardMask).ToString());
Console.Write("\tRoute[{0}] Next Hop: {1}\n", i, new IPAddress((long)forwardTable.Table[i].dwForwardNextHop).ToString());
Console.Write("\tRoute[{0}] If Index: {1}\n", i, forwardTable.Table[i].dwForwardIfIndex);
Console.Write("\tRoute[{0}] Type: {1}\n", i, forwardTable.Table[i].dwForwardType);
Console.Write("\tRoute[{0}] Proto: {1}\n", i, forwardTable.Table[i].dwForwardProto);
Console.Write("\tRoute[{0}] Age: {1}\n", i, forwardTable.Table[i].dwForwardAge);
Console.Write("\tRoute[{0}] Metric1: {1}\n", i, forwardTable.Table[i].dwForwardMetric1);
}
}
public static void RoutePrint()
{
List<Ip4RouteEntry> routeTable = GetRouteTable();
RoutePrint(routeTable);
}
public static void RoutePrint(List<Ip4RouteEntry> routeTable)
{
Console.WriteLine("Route Count: {0}", routeTable.Count);
Console.WriteLine("{0,18} {1,18} {2,18} {3,5} {4,8} ", "DestinationIP", "NetMask", "Gateway", "IF", "Metric");
foreach (Ip4RouteEntry entry in routeTable)
{
Console.WriteLine("{0,18} {1,18} {2,18} {3,5} {4,8} ", entry.DestinationIP, entry.SubnetMask, entry.GatewayIP, entry.InterfaceIndex, entry.Metric);
}
}
public static List<Ip4RouteEntry> GetRouteTable()
{
var fwdTable = IntPtr.Zero;
int size = 0;
var result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
fwdTable = Marshal.AllocHGlobal(size);
result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
var forwardTable = ReadIPForwardTable(fwdTable);
Marshal.FreeHGlobal(fwdTable);
List<Ip4RouteEntry> routeTable = new List<Ip4RouteEntry>();
for (int i = 0; i < forwardTable.Table.Length; ++i)
{
Ip4RouteEntry entry = new Ip4RouteEntry();
entry.DestinationIP = new IPAddress((long)forwardTable.Table[i].dwForwardDest);
entry.SubnetMask = new IPAddress((long)forwardTable.Table[i].dwForwardMask);
entry.GatewayIP = new IPAddress((long)forwardTable.Table[i].dwForwardNextHop);
entry.InterfaceIndex = Convert.ToInt32(forwardTable.Table[i].dwForwardIfIndex);
entry.ForwardType = Convert.ToInt32(forwardTable.Table[i].dwForwardType);
entry.ForwardProtocol = Convert.ToInt32(forwardTable.Table[i].dwForwardProto);
entry.ForwardAge = Convert.ToInt32(forwardTable.Table[i].dwForwardAge);
entry.Metric = Convert.ToInt32(forwardTable.Table[i].dwForwardMetric1);
routeTable.Add(entry);
}
return routeTable;
}
The C# wrapper functions
public class Ip4RouteEntry
{
public IPAddress DestinationIP { get; set; }
public IPAddress SubnetMask { get; set; }
public IPAddress GatewayIP { get; set; }
public int InterfaceIndex { get; set; }
public int ForwardType { get; set; }
public int ForwardProtocol { get; set; }
public int ForwardAge { get; set; }
public int Metric { get; set; }
}
public static void RoutePrint()
{
List<Ip4RouteEntry> routeTable = GetRouteTable();
RoutePrint(routeTable);
}
public static void RoutePrint(List<Ip4RouteEntry> routeTable)
{
Console.WriteLine("Route Count: {0}", routeTable.Count);
Console.WriteLine("{0,18} {1,18} {2,18} {3,5} {4,8} ", "DestinationIP", "NetMask", "Gateway", "IF", "Metric");
foreach (Ip4RouteEntry entry in routeTable)
{
Console.WriteLine("{0,18} {1,18} {2,18} {3,5} {4,8} ", entry.DestinationIP, entry.SubnetMask, entry.GatewayIP, entry.InterfaceIndex, entry.Metric);
}
}
public static List<Ip4RouteEntry> GetRouteTable()
{
var fwdTable = IntPtr.Zero;
int size = 0;
var result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
fwdTable = Marshal.AllocHGlobal(size);
result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
var forwardTable = ReadIPForwardTable(fwdTable);
Marshal.FreeHGlobal(fwdTable);
List<Ip4RouteEntry> routeTable = new List<Ip4RouteEntry>();
for (int i = 0; i < forwardTable.Table.Length; ++i)
{
Ip4RouteEntry entry = new Ip4RouteEntry();
entry.DestinationIP = new IPAddress((long)forwardTable.Table[i].dwForwardDest);
entry.SubnetMask = new IPAddress((long)forwardTable.Table[i].dwForwardMask);
entry.GatewayIP = new IPAddress((long)forwardTable.Table[i].dwForwardNextHop);
entry.InterfaceIndex = Convert.ToInt32(forwardTable.Table[i].dwForwardIfIndex);
entry.ForwardType = Convert.ToInt32(forwardTable.Table[i].dwForwardType);
entry.ForwardProtocol = Convert.ToInt32(forwardTable.Table[i].dwForwardProto);
entry.ForwardAge = Convert.ToInt32(forwardTable.Table[i].dwForwardAge);
entry.Metric = Convert.ToInt32(forwardTable.Table[i].dwForwardMetric1);
routeTable.Add(entry);
}
return routeTable;
}
public static bool RouteExists(string destinationIP)
{
List<Ip4RouteEntry> routeTable = Ip4RouteTable.GetRouteTable();
Ip4RouteEntry routeEntry = routeTable.Find(i => i.DestinationIP.ToString().Equals(destinationIP));
return (routeEntry != null);
}
public static List<Ip4RouteEntry> GetRouteEntry(string destinationIP)
{
List<Ip4RouteEntry> routeTable = Ip4RouteTable.GetRouteTable();
List<Ip4RouteEntry> routeMatches = routeTable.FindAll(i => i.DestinationIP.ToString().Equals(destinationIP));
return routeMatches;
}
public static List<Ip4RouteEntry> GetRouteEntry(string destinationIP, string mask)
{
List<Ip4RouteEntry> routeTable = Ip4RouteTable.GetRouteTable();
List<Ip4RouteEntry> routeMatches = routeTable.FindAll(i => i.DestinationIP.ToString().Equals(destinationIP) && i.SubnetMask.ToString().Equals(mask));
return routeMatches;
}
public static void CreateRoute(Ip4RouteEntry routeEntry)
{
var route = new PMIB_IPFORWARDROW
{
dwForwardDest = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.DestinationIP.ToString()).GetAddressBytes(), 0),
dwForwardMask = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.SubnetMask.ToString()).GetAddressBytes(), 0),
dwForwardNextHop = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.GatewayIP.ToString()).GetAddressBytes(), 0),
dwForwardMetric1 = 99,
dwForwardType = Convert.ToUInt32(3), //Default to 3
dwForwardProto = Convert.ToUInt32(3), //Default to 3
dwForwardAge = 0,
dwForwardIfIndex = Convert.ToUInt32(routeEntry.InterfaceIndex)
};
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PMIB_IPFORWARDROW)));
try
{
Marshal.StructureToPtr(route, ptr, false);
var status = NativeMethods.CreateIpForwardEntry(ptr);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
public static void CreateRoute(string destination, string mask, int interfaceIndex, int metric)
{
NetworkAdaptor adaptor = NicInterface.GetNetworkAdaptor(interfaceIndex);
var route = new PMIB_IPFORWARDROW
{
dwForwardDest = BitConverter.ToUInt32(IPAddress.Parse(destination).GetAddressBytes(), 0),
dwForwardMask = BitConverter.ToUInt32(IPAddress.Parse(mask).GetAddressBytes(), 0),
dwForwardNextHop = BitConverter.ToUInt32(IPAddress.Parse(adaptor.PrimaryGateway.ToString()).GetAddressBytes(), 0),
dwForwardMetric1 = Convert.ToUInt32(metric),
dwForwardType = Convert.ToUInt32(3), //Default to 3
dwForwardProto = Convert.ToUInt32(3), //Default to 3
dwForwardAge = 0,
dwForwardIfIndex = Convert.ToUInt32(interfaceIndex)
};
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PMIB_IPFORWARDROW)));
try
{
Marshal.StructureToPtr(route, ptr, false);
var status = NativeMethods.CreateIpForwardEntry(ptr);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
public static void DeleteRoute(Ip4RouteEntry routeEntry)
{
var route = new PMIB_IPFORWARDROW
{
dwForwardDest = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.DestinationIP.ToString()).GetAddressBytes(), 0),
dwForwardMask = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.SubnetMask.ToString()).GetAddressBytes(), 0),
dwForwardNextHop = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.GatewayIP.ToString()).GetAddressBytes(), 0),
dwForwardMetric1 = 99,
dwForwardType = Convert.ToUInt32(3), //Default to 3
dwForwardProto = Convert.ToUInt32(3), //Default to 3
dwForwardAge = 0,
dwForwardIfIndex = Convert.ToUInt32(routeEntry.InterfaceIndex)
};
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PMIB_IPFORWARDROW)));
try
{
Marshal.StructureToPtr(route, ptr, false);
var status = NativeMethods.DeleteIpForwardEntry(ptr);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
public static void DeleteRoute(string destinationIP)
{
List<Ip4RouteEntry> routeMatches = Ip4RouteTable.GetRouteEntry(destinationIP);
if (routeMatches == null) return;
foreach (Ip4RouteEntry routeEntry in routeMatches)
{
DeleteRoute(routeEntry);
}
}
public static void DeleteRoute(string destinationIP, string mask)
{
List<Ip4RouteEntry> routeMatches = Ip4RouteTable.GetRouteEntry(destinationIP, mask);
if (routeMatches == null) return;
foreach (Ip4RouteEntry routeEntry in routeMatches)
{
DeleteRoute(routeEntry);
}
}
public static void DeleteRoute(int interfaceIndex)
{
var fwdTable = IntPtr.Zero;
int size = 0;
var result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
fwdTable = Marshal.AllocHGlobal(size);
result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
var forwardTable = ReadIPForwardTable(fwdTable);
Marshal.FreeHGlobal(fwdTable);
List<PMIB_IPFORWARDROW> filtered = new List<PMIB_IPFORWARDROW>();
for (int i = 0; i < forwardTable.Table.Length; ++i)
{
if (Convert.ToInt32(forwardTable.Table[i].dwForwardIfIndex).Equals(interfaceIndex))
{
filtered.Add(forwardTable.Table[i]);
}
}
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PMIB_IPFORWARDROW)));
try
{
foreach (PMIB_IPFORWARDROW routeEntry in filtered)
{
Marshal.StructureToPtr(routeEntry, ptr, false);
var status = NativeMethods.DeleteIpForwardEntry(ptr);
}
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}

C# Inconsistent accessibility

I get an inconsistent accessibility error when trying to compile my C# code. I have googled it extensively, but all the cases and answers I find deal with some private-public inconstancy. I have not written private ANYWHERE in my code at all, I have checked and double checked. I read something somewhere that I could try "lowering the scope", but I have no idea what that means. Help.
Sorry for missing code, here it is on github:
https://gist.github.com/jyggorath/7589742
using System;
public class oblig5 {
// // // BEGIN GLOBAL 1 // // //
public const int STRLEN = 40;
public const int MAXPERSONER = 500;
public const int MAXLAG = 50;
public enum funksjon {trener, oppman, ingen};
// // // END GLOBAL 1 // // //
// // // BEGIN PERSON CLASS // // //
public class Person {
// // // privates:
public string navn;
public string adr;
public string mail;
public string klasse;
public int tlf;
public funksjon verv;
public int lagnr;
// // // publics:
// constructor
public Person() {
navn = ""; adr = ""; mail = ""; klasse = "";
tlf = 0; lagnr = 0;
verv = (funksjon)2;
}
// skriv:
public void skriv() {
Console.WriteLine("Navn: {0}\nAdresse: {1}\nMail: {2}\nKlasse: {3}\nTlf: {4}\nLagnr: {5}", navn, adr, mail, klasse, tlf, lagnr);
if (verv == (funksjon)0)
Console.WriteLine("Er Trener\n");
else if (verv == (funksjon)1)
Console.WriteLine("Er Oppmann\n");
else
Console.WriteLine("Har ikkeno verv\n");
lag[lagnr].skriv();
}
public void skriv2() {
Console.WriteLine("Navn: {0}\nAdresse: {1}\nMail: {2}\nKlasse: {3}\nTlf: {4}\n", navn, adr, mail, klasse, tlf);
}
// les:
public void les_data(int lnr) {
lagnr = lnr;
navn = les_str("Personens navn");
adr = les_str("Personens adresse");
mail = les_str("Personens mail");
klasse = les_str("Hvilke klasse går personen i?");
tlf = les_int("Personens telefonnr", 10000000, 99999999);
verv = les_enum();
}
// fil:
public void skriv_til_fil(System.IO.StreamWriter file) {
file.WriteLine(navn);
file.WriteLine(adr);
file.WriteLine(mail);
file.WriteLine(klasse);
file.WriteLine(tlf);
file.WriteLine((int)verv);
file.WriteLine(lagnr);
}
public void les_fra_fil(System.IO.StreamReader file, string nvn) {
navn = nvn;
adr = file.ReadLine();
mail = file.ReadLine();
klasse = file.ReadLine();
tlf = int.Parse(file.ReadLine());
verv = (funksjon)int.Parse(file.ReadLine());
lagnr = int.Parse(file.ReadLine());
}
// sammenligninger:
public bool har_navn(string t) {
return (navn == t);
}
public bool har_verv(funksjon v) {
return (verv == v);
}
public bool tilhorer_lag(int n) {
return (lagnr == n);
}
}
// // // END PERSON CLASS // // //
// // // BEGIN LAG CLASS // // //
public class Lag {
// // // privates:
public string navn;
public string adr;
public string mail;
public string hjemmeside;
// // // publics:
public Lag() {
navn = ""; adr = ""; mail = ""; hjemmeside = "";
}
public void skriv() {
Console.WriteLine("Lagnavn: {0}\nLagadresse: {1}\nLagmail: {2}\nLaghjemmeside: {3}\n", navn, adr, mail, hjemmeside);
}
public void les_data(string t) {
navn = t;
adr = les_str("Lagets adresse");
mail = les_str("Lagets mail");
hjemmeside = les_str("Lagets hjemmeside");
}
public bool har_navn(string t) {
return (navn == t);
}
}
// // // END LAG CLASS // // //
// // // BEGIN GLOBAL 2 // // //
// things:
public static Person[] personer = new Person[MAXPERSONER + 1];
public static Lag[] lag = new Lag[MAXLAG + 1];
public static int siste_person = 0, siste_lag = 0;
// funskjoner:
public static void skriv_meny() {
Console.WriteLine("\n\nFØLGENDE KOMMANDOER ER LOVLIG:");
Console.WriteLine("\tA = skriv ALT om En person");
Console.WriteLine("\tB = skriv ALLE trenere ELLER oppmenn");
Console.WriteLine("\tC = skriv ALT om et gitt lag");
Console.WriteLine("\tL = nytt Lag legges inn");
Console.WriteLine("\tP = ny Person legges inn");
Console.WriteLine("\tQ = Quit/Avslutt");
}
public static char les_char(string t) {
Console.Write(string.Concat("\n", t, ": "));
char retur = Console.ReadLine().ToUpper()[0];
return retur;
}
public static int les_int(string t, int min, int max) {
int retur = min - 1;
do {
Console.Write(string.Concat("\t", t, " (", min.ToString(), "-", max.ToString(), "): "));
retur = int.Parse(Console.ReadLine());
} while (retur < min || retur > max);
return retur;
}
public static string les_str(string t) {
return Console.ReadLine();
}
public static funksjon les_enum() {
char ch = '\0';
do {
Console.Write("\tT(rener) eller O(ppmann): ");
ch = Console.ReadLine().ToUpper()[0];
} while (ch != 'T' && ch != 'O');
return ((ch == 'T') ? (funksjon)0 : (funksjon)1);
}
public static int finn_person(string t) {
for (int i = 1; i <= siste_person; i++)
if (personer[i].har_navn(t))
return i;
return 0;
}
public static int finn_lag(string t) {
for (int i = 1; i <= siste_lag; i++)
if (lag[i].har_navn(t))
return i;
return 0;
}
public static void skriv_person() {
string nvn = les_str("Navn på person");
int person_nr = finn_person(nvn);
if (person_nr == 0)
Console.WriteLine("Personen fins ikke");
else
personer[person_nr].skriv();
}
public static void skriv_verv() {
funksjon v = les_enum();
for (int i = 1; i < siste_person; i++) {
if (personer[i].har_verv(v))
personer[i].skriv();
}
}
public static void skriv_lag() {
string lagnvn = les_str("Navn på lag");
int lagnr = finn_lag(lagnvn);
if (lagnr == 0)
Console.WriteLine("Laget fins ikke");
else {
lag[lagnr].skriv();
for (int i = 1; i <= siste_person; i++) {
if (personer[i].tilhorer_lag(lagnr))
personer[i].skriv2();
}
}
}
public static void nytt_lag() {
if (siste_lag < MAXLAG) {
string lagnvn = les_str("Lagets navn");
if (finn_lag(lagnvn) == 0) {
siste_lag++;
lag[siste_lag] = new Lag();
lag[siste_lag].les_data(lagnvn);
}
else
Console.WriteLine("Laget fins fra før!");
}
else
Console.WriteLine("Lag er full!");
}
public static void ny_person() {
if (siste_person < MAXPERSONER) {
string lagnvn = les_str("Hvilke lag tilhører personen?");
int lagnr = finn_lag(lagnvn);
if (lagnr != 0) {
siste_person++;
personer[siste_person] = new Person();
personer[siste_person].les_data(lagnr);
}
else
Console.WriteLine("Laget fins ikke!");
}
else
Console.WriteLine("Personer er fulle!");
}
public static void skriv_til_fil() {
System.IO.StreamWriter file = new System.IO.StreamWriter("personer.dta");
for (int i = 1; i <= siste_person; i++) {
personer[i].skriv_til_fil(file);
}
}
public static void les_fra_fil() {
string buffer;
try {
System.IO.StreamReader file = new System.IO.StreamReader("personer.dta");
while ((buffer = file.ReadLine()) != null) {
siste_person++;
personer[siste_person] = new Person();
personer[siste_person].les_fra_fil(file, buffer);
}
}
catch {
Console.WriteLine("Finner ikke filen!");
}
}
// // // END GLOBAL 2 // // //
// // // BEGIN MAIN // // //
public static void Main() {
char kommando;
les_fra_fil();
skriv_meny();
kommando = les_char("Ønske");
while (kommando != 'Q') {
switch (kommando) {
case 'A':
skriv_person(); break;
case 'B':
skriv_verv(); break;
case 'C':
skriv_lag(); break;
case 'L':
nytt_lag(); break;
case 'P':
ny_person(); break;
default:
skriv_meny();
break;
}
kommando = les_char("Ønske");
}
skriv_til_fil();
Console.WriteLine("\n");
}
// // // END MAIN // // //
}
Sorry for strange variable names. This is code ported from C++ assignments from my Norwegian teacher (I didn't change them 'cause I'm Norwegian as well, and I never thought I would be getting these problems)
As you can see, ALL classes defined in my code is public. So that is not my problem. The errors are generated on lines 126 and 127, as well as 160. The errors deal with the assignments of variables with my custom classes as type, and with my function returning a custom enum type. Help?
My psychic debugging sense tells me that you're not declaring a class as public
public class MyType
IF you want to make a public field of MyType, it's not enough to just type class MyType you have to make it public explicitly.

Categories

Resources