Info:
.Net 4.5
Tested on:
Win7 64 bit
Win10 64 bit (Virtual Box)
I am trying to get a list of handles of an external process and return their names as string so I can close a specific one afterwards. Therefore i wrote this function using the Win32API which will check if the handle is the handle i want to close: `
const int CNST_SYSTEM_HANDLE_INFORMATION = 16;
const uint STATUS_INFO_LENGTH_MISMATCH = 0xc0000004;
public static string getObjectTypeName(Win32API.SYSTEM_HANDLE_INFORMATION shHandle, Process process)
{
IntPtr m_ipProcessHwnd = Win32API.OpenProcess(Win32API.ProcessAccessFlags.All, false, process.Id);
IntPtr ipHandle = IntPtr.Zero;
var objBasic = new Win32API.OBJECT_BASIC_INFORMATION();
IntPtr ipBasic = IntPtr.Zero;
var objObjectType = new Win32API.OBJECT_TYPE_INFORMATION();
IntPtr ipObjectType = IntPtr.Zero;
IntPtr ipObjectName = IntPtr.Zero;
string strObjectTypeName = "";
int nLength = 0;
int nReturn = 0;
IntPtr ipTemp = IntPtr.Zero;
if (!Win32API.DuplicateHandle(m_ipProcessHwnd, shHandle.Handle,
Win32API.GetCurrentProcess(), out ipHandle,
0, false, Win32API.DUPLICATE_SAME_ACCESS))
return null;
ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic));
Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectBasicInformation,
ipBasic, Marshal.SizeOf(objBasic), ref nLength);
objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType());
Marshal.FreeHGlobal(ipBasic);
ipObjectType = Marshal.AllocHGlobal(objBasic.TypeInformationLength);
nLength = objBasic.TypeInformationLength;
while ((uint)(nReturn = Win32API.NtQueryObject(
ipHandle, (int)Win32API.ObjectInformationClass.ObjectTypeInformation, ipObjectType,
nLength, ref nLength)) ==
Win32API.STATUS_INFO_LENGTH_MISMATCH)
{
Marshal.FreeHGlobal(ipObjectType);
ipObjectType = Marshal.AllocHGlobal(nLength);
}
objObjectType = (Win32API.OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectType, objObjectType.GetType());
if (Is64Bits())
{
ipTemp = new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32);
}
else
{
ipTemp = objObjectType.Name.Buffer;
}
strObjectTypeName = Marshal.PtrToStringUni(ipTemp, objObjectType.Name.Length >> 1);
Marshal.FreeHGlobal(ipObjectType);
Win32API.CloseHandle(ipHandle);
return strObjectTypeName;
}`
The problem however is that this code works in Win7 64bit, not in Win10! --> In Win 10 strObjectTypeName = Marshal.PtrToStringUni(); throws a AcessViolationException (Last few lines in the code)
System.AccessViolationException Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Am I missing something here about how unmanaged memory has to be accessed in win10?
I have just come across the same issue. I haven't tried Win7, but when you run the code on Win10(x64) as 32bit (e.g. set the "Prefer 32-bit flag" of your application) it should work.
When the exception happens, drag&drop the variable "ipTemp" to the "memory window" of Visual Studio, if it displays only question marks or an error message you don't have a valid pointer.
As far as I figured out, there are (more) padding bytes in the 64bit versions of the structs that are used by this API:
OBJECT_TYPE_INFORMATION contains a UNICODE_STRING and UNICODE_STRING has 4 padding bytes before the Buffer-field in 64bit Mode.
My workaraound was this:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UNICODE_STRING
{
private IntPtr _dummy; // the two ushorts seem to be padded with 4 bytes in 64bit mode only
/// <summary>
/// The length, in bytes, of the string stored in Buffer. If the string is null-terminated, Length does not include the trailing null character.
/// </summary>
public ushort Length
{
get { return (ushort)Marshal.ReadInt16(this, 0); }
}
/// <summary>
/// The length, in bytes, of Buffer.
/// </summary>
public ushort MaximumLength
{
get { return (ushort)Marshal.ReadInt16(this, 2); }
}
public IntPtr Buffer;
}
During my research I found so many questions regarding this topic and basically two kinds of sample code
that have been copied all over the Internet that I am considering to create an open-source library named WinKernelObjectsDotNet.
Update: The library is now available here. It supports finding the process that is locking a file or a serial-port (COM) with a single line of code.
I suggest to change UNICODE_STRING structure in a such way.
public struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
[MarshalAs(UnmanagedType.LPWStr)] public string Buffer;
}
So getObjectTypeName method will be something like this and will work for both 32/64:
public static string getObjectTypeName(SYSTEM_HANDLE_INFORMATION shHandle, Process process) {
IntPtr ipProcessHwnd = OpenProcess(ProcessAccessFlags.All, false, process.Id);
if (!DuplicateHandle(ipProcessHwnd, shHandle.Handle, GetCurrentProcess(), out IntPtr ipHandle, 0, false, DUPLICATE_SAME_ACCESS)) {
return null;
}
OBJECT_BASIC_INFORMATION objBasicInformation = new OBJECT_BASIC_INFORMATION();
IntPtr ipBasicInformation = Marshal.AllocHGlobal(Marshal.SizeOf(objBasicInformation));
int iBasicInformationLength = 0;
NtQueryObject(ipHandle, (int) ObjectInformationClass.ObjectBasicInformation, ipBasicInformation, Marshal.SizeOf(objBasicInformation), ref iBasicInformationLength);
objBasicInformation = (OBJECT_BASIC_INFORMATION) Marshal.PtrToStructure(ipBasicInformation, typeof(OBJECT_BASIC_INFORMATION));
Marshal.FreeHGlobal(ipBasicInformation);
int iObjectTypeInformationLength = objBasicInformation.TypeInformationLength;
IntPtr ipObjectTypeInformation = Marshal.AllocHGlobal(iObjectTypeInformationLength);
while (Win32API.STATUS_INFO_LENGTH_MISMATCH == (uint) (NtQueryObject(ipHandle, (int) ObjectInformationClass.ObjectTypeInformation, ipObjectTypeInformation, iObjectTypeInformationLength, ref iObjectTypeInformationLength))) {
Marshal.FreeHGlobal(ipObjectTypeInformation);
ipObjectTypeInformation = Marshal.AllocHGlobal(iObjectTypeInformationLength);
}
CloseHandle(ipHandle);
OBJECT_TYPE_INFORMATION objObjectType = (OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectTypeInformation, typeof(OBJECT_TYPE_INFORMATION));
Marshal.FreeHGlobal(ipObjectTypeInformation);
return objObjectType.Name.Buffer;
}
Related
We suddenly have problems with the smart card api on some windows installations.
There seem to be a memory leak while calling the SCardEstablishContext function.
The problem can be reproduced in a console application with the code sample available at
http://www.pinvoke.net/default.aspx/winscard.scardestablishcontext
class Program
{
#region Win32
// WinSCard APIs to be imported.
[DllImport("WinScard.dll")]
static extern int SCardEstablishContext(uint dwScope,
IntPtr notUsed1,
IntPtr notUsed2,
out IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardReleaseContext(IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardConnect(IntPtr hContext,
string cReaderName,
uint dwShareMode,
uint dwPrefProtocol,
ref IntPtr phCard,
ref IntPtr ActiveProtocol);
[DllImport("WinScard.dll")]
static extern int SCardDisconnect(IntPtr hCard, int Disposition);
[DllImport("WinScard.dll", EntryPoint = "SCardListReadersA", CharSet = CharSet.Ansi)]
static extern int SCardListReaders(
IntPtr hContext,
byte[] mszGroups,
byte[] mszReaders,
ref UInt32 pcchReaders);
#endregion
static void Main(string[] args)
{
while (true)
{
SmartCardInserted();
System.Threading.Thread.Sleep(10);
}
}
internal static bool SmartCardInserted()
{
bool cardInserted = false;
IntPtr hContext = IntPtr.Zero;
try
{
List<string> readersList = new List<string>();
int ret = 0;
uint pcchReaders = 0;
int nullindex = -1;
char nullchar = (char)0;
// Establish context.
ret = SCardEstablishContext(2, IntPtr.Zero, IntPtr.Zero, out hContext);
// First call with 3rd parameter set to null gets readers buffer length.
ret = SCardListReaders(hContext, null, null, ref pcchReaders);
byte[] mszReaders = new byte[pcchReaders];
// Fill readers buffer with second call.
ret = SCardListReaders(hContext, null, mszReaders, ref pcchReaders);
// Populate List with readers.
ASCIIEncoding ascii = new ASCIIEncoding();
string currbuff = ascii.GetString(mszReaders);
int len = (int)pcchReaders;
if (len > 0)
{
while (currbuff[0] != nullchar)
{
nullindex = currbuff.IndexOf(nullchar); // Get null end character.
string reader = currbuff.Substring(0, nullindex);
readersList.Add(reader);
len = len - (reader.Length + 1);
currbuff = currbuff.Substring(nullindex + 1, len);
}
}
// We have list of readers, check for cards.
IntPtr phCard = IntPtr.Zero;
IntPtr ActiveProtocol = IntPtr.Zero;
int result = 0;
foreach (string readerName in readersList)
{
try
{
result = SCardConnect(hContext, readerName, 2, 3, ref phCard, ref ActiveProtocol);
if (result == 0)
{
cardInserted = true;
break;
}
}
finally
{
SCardDisconnect(phCard, 0);
}
}
}
finally
{
SCardReleaseContext(hContext);
}
return cardInserted;
}
}
To test, we call the method SmartCardInserted() in an infinite loop with a small delay => the memory grows constantly and new hadles are allocated.
We see this problem on systems runing Windows 10 or Windows Server 2012, but not on Windows Server 2008.
Any ideas are greatly appreciated!
The problem seems to have been released with v1709 of Windows 10. The shortest amount of code to reproduce the bug is
while(true) {
ret = SCardEstablishContext(2, IntPtr.Zero, IntPtr.Zero, out hContext);
SCardReleaseContext(hContext);
}
It leaks ~264 bytes of memory each time a context is established and released.
If you maintain hContext outside of the loop and only create a context if it's IntPtr.Zero you should be able to avoid the leak. Then when you call SCardListReaders, check to see if you get SCARD_E_INVALID_HANDLE back and invalidate your hContext.
class Program
{
#region Win32
// WinSCard APIs to be imported.
[DllImport("WinScard.dll")]
static extern int SCardEstablishContext(uint dwScope,
IntPtr notUsed1,
IntPtr notUsed2,
out IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardReleaseContext(IntPtr phContext);
[DllImport("WinScard.dll")]
static extern int SCardConnect(IntPtr hContext,
string cReaderName,
uint dwShareMode,
uint dwPrefProtocol,
ref IntPtr phCard,
ref IntPtr ActiveProtocol);
[DllImport("WinScard.dll")]
static extern int SCardDisconnect(IntPtr hCard, int Disposition);
[DllImport("WinScard.dll", EntryPoint = "SCardListReadersA", CharSet = CharSet.Ansi)]
static extern int SCardListReaders(
IntPtr hContext,
byte[] mszGroups,
byte[] mszReaders,
ref UInt32 pcchReaders);
#endregion
static void Main(string[] args)
{
IntPtr hContext = IntPtr.Zero;
while (true)
{
SmartCardInserted(hContext);
System.Threading.Thread.Sleep(10);
}
SCardReleaseContext(hContext);
}
internal static bool SmartCardInserted(IntPtr hContext)
{
bool cardInserted = false;
try
{
List<string> readersList = new List<string>();
int ret = 0;
uint pcchReaders = 0;
int nullindex = -1;
char nullchar = (char)0;
// Establish context.
if(hContext == IntPtr.Zero)
ret = SCardEstablishContext(2, IntPtr.Zero, IntPtr.Zero, out hContext);
// First call with 3rd parameter set to null gets readers buffer length.
ret = SCardListReaders(hContext, null, null, ref pcchReaders);
if(ret == 0x80100003) // SCARD_E_INVALID_HANDLE = 0x80100003, // The supplied handle was invalid
{
try
{
SCardReleaseContext(hContext);
}
catch {}
finally
{
hContext = IntPtr.Zero;
}
return false;
}
byte[] mszReaders = new byte[pcchReaders];
// Fill readers buffer with second call.
ret = SCardListReaders(hContext, null, mszReaders, ref pcchReaders);
// Populate List with readers.
ASCIIEncoding ascii = new ASCIIEncoding();
string currbuff = ascii.GetString(mszReaders);
int len = (int)pcchReaders;
if (len > 0)
{
while (currbuff[0] != nullchar)
{
nullindex = currbuff.IndexOf(nullchar); // Get null end character.
string reader = currbuff.Substring(0, nullindex);
readersList.Add(reader);
len = len - (reader.Length + 1);
currbuff = currbuff.Substring(nullindex + 1, len);
}
}
// We have list of readers, check for cards.
IntPtr phCard = IntPtr.Zero;
IntPtr ActiveProtocol = IntPtr.Zero;
int result = 0;
foreach (string readerName in readersList)
{
try
{
result = SCardConnect(hContext, readerName, 2, 3, ref phCard, ref ActiveProtocol);
if (result == 0)
{
cardInserted = true;
break;
}
}
finally
{
SCardDisconnect(phCard, 0);
}
}
}
return cardInserted;
}
}
It's a workaround until the Winscard.dll API is fixed.
I have this code for connect a process to proxy, but it returns false:
The code works if I do not enter username and password in SetConnectionProxy() and send only the two options PROXY flags and PROXY name. When I add the INTERNET_PER_CONN_OPTION set username and set password, it returns false.
I do not know anything about wininet.
Please, help me. Thanks.
{
private static string progressName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; //extrae el nombre de la aplicación
[DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr InternetOpen(
string lpszAgent, int dwAccessType, string lpszProxyName,
string lpszProxyBypass, int dwFlags);
[DllImport("wininet.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InternetCloseHandle(IntPtr hInternet);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct INTERNET_PER_CONN_OPTION_LIST
{
public int Size;
// The connection to be set. NULL means LAN.
public System.IntPtr Connection;
public int OptionCount;
public int OptionError;
// List of INTERNET_PER_CONN_OPTIONs.
public System.IntPtr pOptions;
}
private enum INTERNET_OPTION
{
// Sets or retrieves an INTERNET_PER_CONN_OPTION_LIST structure that specifies
// a list of options for a particular connection.
INTERNET_OPTION_PER_CONNECTION_OPTION = 75,
// Notify the system that the registry settings have been changed so that
// it verifies the settings on the next call to InternetConnect.
INTERNET_OPTION_SETTINGS_CHANGED = 39,
// Causes the proxy data to be reread from the registry for a handle.
INTERNET_OPTION_REFRESH = 37
}
private enum INTERNET_PER_CONN_OptionEnum
{
INTERNET_PER_CONN_FLAGS = 1,
INTERNET_PER_CONN_PROXY_SERVER = 2,
INTERNET_PER_CONN_PROXY_BYPASS = 3,
INTERNET_PER_CONN_AUTOCONFIG_URL = 4,
INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5,
INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL = 6,
INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS = 7,
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME = 8,
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL = 9,
INTERNET_PER_CONN_FLAGS_UI = 10,
INTERNET_OPTION_PROXY_USERNAME = 43,
INTERNET_OPTION_PROXY_PASSWORD = 44
}
private const int INTERNET_OPEN_TYPE_DIRECT = 1; // direct to net
private const int INTERNET_OPEN_TYPE_PRECONFIG = 0; // read registry
/// <summary>
/// Constants used in INTERNET_PER_CONN_OPTON struct.
/// </summary>
private enum INTERNET_OPTION_PER_CONN_FLAGS
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
/// <summary>
/// Used in INTERNET_PER_CONN_OPTION.
/// When create a instance of OptionUnion, only one filed will be used.
/// The StructLayout and FieldOffset attributes could help to decrease the struct size.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct INTERNET_PER_CONN_OPTION_OptionUnion
{
// A value in INTERNET_OPTION_PER_CONN_FLAGS.
[FieldOffset(0)]
public int dwValue;
[FieldOffset(0)]
public System.IntPtr pszValue;
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME ftValue;
}
[StructLayout(LayoutKind.Sequential)]
private struct INTERNET_PER_CONN_OPTION
{
// A value in INTERNET_PER_CONN_OptionEnum.
public int dwOption;
public INTERNET_PER_CONN_OPTION_OptionUnion Value;
}
/// <summary>
/// Sets an Internet option.
/// </summary>
[DllImport("wininet.dll", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern bool InternetSetOption(
IntPtr hInternet,
INTERNET_OPTION dwOption,
IntPtr lpBuffer,
int lpdwBufferLength);
/// <summary>
/// Queries an Internet option on the specified handle. The Handle will be always 0.
/// </summary>
[DllImport("wininet.dll", CharSet = CharSet.Ansi, SetLastError = true,
EntryPoint = "InternetQueryOption")]
private extern static bool InternetQueryOptionList(
IntPtr Handle,
INTERNET_OPTION OptionFlag,
ref INTERNET_PER_CONN_OPTION_LIST OptionList,
ref int size);
/// <summary>
/// Set the proxy server for LAN connection.
/// </summary>
public static bool SetConnectionProxy(string proxyServer, string username, string password)
{
IntPtr hInternet = InternetOpen(progressName, INTERNET_OPEN_TYPE_DIRECT, null, null, 0);
// Create 4 options.
int opciones = 4;
INTERNET_PER_CONN_OPTION[] Options = new INTERNET_PER_CONN_OPTION[opciones];
// Set PROXY flags.
Options[0] = new INTERNET_PER_CONN_OPTION();
Options[0].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS;
Options[0].Value.dwValue = (int)INTERNET_OPTION_PER_CONN_FLAGS.PROXY_TYPE_PROXY;
// Set proxy name.
Options[1] = new INTERNET_PER_CONN_OPTION();
Options[1].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER;
Options[1].Value.pszValue = Marshal.StringToHGlobalAnsi(proxyServer);
//Set username.
Options[2] = new INTERNET_PER_CONN_OPTION();
Options[2].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_OPTION_PROXY_USERNAME;
Options[2].Value.pszValue = Marshal.StringToCoTaskMemAuto(username);
// Set password.
Options[3] = new INTERNET_PER_CONN_OPTION();
Options[3].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_OPTION_PROXY_PASSWORD;
Options[3].Value.pszValue = Marshal.StringToCoTaskMemAuto(password);
// Allocate a block of memory of the options.
System.IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(Options[0]) + Marshal.SizeOf(Options[1]) + Marshal.SizeOf(Options[2]) + Marshal.SizeOf(Options[3]));
System.IntPtr current = buffer;
// Marshal data from a managed object to an unmanaged block of memory.
for (int i = 0; i < Options.Length; i++)
{
Marshal.StructureToPtr(Options[i], current, false);
current = (System.IntPtr)((int)current + Marshal.SizeOf(Options[i]));
}
// Initialize a INTERNET_PER_CONN_OPTION_LIST instance.
INTERNET_PER_CONN_OPTION_LIST option_list = new INTERNET_PER_CONN_OPTION_LIST();
// Point to the allocated memory.
option_list.pOptions = buffer;
// Return the unmanaged size of an object in bytes.
option_list.Size = Marshal.SizeOf(option_list);
// IntPtr.Zero means LAN connection.
option_list.Connection = IntPtr.Zero;
option_list.OptionCount = Options.Length;
option_list.OptionError = 0;
int size = Marshal.SizeOf(option_list);
// Allocate memory for the INTERNET_PER_CONN_OPTION_LIST instance.
IntPtr intptrStruct = Marshal.AllocCoTaskMem(size);
// Marshal data from a managed object to an unmanaged block of memory.
Marshal.StructureToPtr(option_list, intptrStruct, true);
// Set internet settings.
bool bReturn = InternetSetOption(hInternet, INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION, intptrStruct, size);
// Free the allocated memory.
Marshal.FreeCoTaskMem(buffer);
Marshal.FreeCoTaskMem(intptrStruct);
InternetCloseHandle(hInternet); //
// Throw an exception if this operation failed.
if (!bReturn)
{
throw new ApplicationException(" Set Internet Option Failed!");
}
return bReturn;
}
}
After a long time I found the answer and it was much simpler than it seemed. It was as simple as putting the original function, without username or password, of SetConnectionProxy () and then, in the first navigation, insert the user and password of the proxy in the url: http: // user: password # url, and I have done like this:
The connection to the proxy first:
public static bool SetConnectionProxy(string proxyServer)
{
IntPtr hInternet = InternetOpen(miniYo, INTERNET_OPEN_TYPE_DIRECT, null, null, 0);
INTERNET_PER_CONN_OPTION[] Options = new INTERNET_PER_CONN_OPTION[2];
// Set PROXY flags.
Options[0] = new INTERNET_PER_CONN_OPTION();
Options[0].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS;
Options[0].Value.dwValue = (int)INTERNET_OPTION_PER_CONN_FLAGS.PROXY_TYPE_PROXY;
// Set proxy name.
Options[1] = new INTERNET_PER_CONN_OPTION();
Options[1].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER;
Options[1].Value.pszValue = Marshal.StringToHGlobalAnsi(proxyServer);
// Allocate a block of memory of the options.
System.IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(Options[0]) + Marshal.SizeOf(Options[1]));
System.IntPtr current = buffer;
// Marshal data from a managed object to an unmanaged block of memory.
for (int i = 0; i < Options.Length; i++)
{
Marshal.StructureToPtr(Options[i], current, false);
current = (System.IntPtr)((int)current + Marshal.SizeOf(Options[i]));
}
// Initialize a INTERNET_PER_CONN_OPTION_LIST instance.
INTERNET_PER_CONN_OPTION_LIST option_list = new INTERNET_PER_CONN_OPTION_LIST();
// Point to the allocated memory.
option_list.pOptions = buffer;
// Return the unmanaged size of an object in bytes.
option_list.Size = Marshal.SizeOf(option_list);
// IntPtr.Zero means LAN connection.
option_list.Connection = IntPtr.Zero;
option_list.OptionCount = Options.Length;
option_list.OptionError = 0;
int size = Marshal.SizeOf(option_list);
// Allocate memory for the INTERNET_PER_CONN_OPTION_LIST instance.
IntPtr intptrStruct = Marshal.AllocCoTaskMem(size);
// Marshal data from a managed object to an unmanaged block of memory.
Marshal.StructureToPtr(option_list, intptrStruct, true);
// Set internet settings.
bool bReturn = InternetSetOption(hInternet, INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION, intptrStruct, size);
// Free the allocated memory.
Marshal.FreeCoTaskMem(buffer);
Marshal.FreeCoTaskMem(intptrStruct);
InternetCloseHandle(hInternet); //¿Esto lo cierra?
// Throw an exception if this operation failed.
if (!bReturn)
{
throw new ApplicationException(" Set Internet Option Failed!");
}
return bReturn;
}
}
The first navigation with the user and the password, after:
UriBuilder uriSite = new UriBuilder(string);
uriSite.UserName = usuario;
uriSite.Password = contraseña;
webBrowser.Navigate(uriSite.Uri);
I regret to say that I did not feel helped by the community
I am trying to do login in the phone. I am developing in c# and the library is in C++. The function "lineDevSpecific" returns the value "-2147483595", but it must to be positive.
[DllImport("Tapi32.dll", SetLastError = true)]
unsafe private static extern int lineDevSpecific(IntPtr hLine, IntPtr lpParams);
[StructLayout(LayoutKind.Sequential)]
public struct UserRec
{
//[MarshalAs(UnmanagedType.I4)]
public int dwMode=8;
public int dwParam1=201;
public int dwParam2=0;
}
unsafe static void Iniciar()
{
string vline = "Ip Office Phone: 201";
IntPtr hline = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(vline);
var sizeUserRec = Marshal.SizeOf(typeof(UserRec));
var userRec = Marshal.AllocHGlobal(sizeUserRec);
int result=lineDevSpecific(hline, userRec);
var x = (UserRec)Marshal.PtrToStructure(userRec, typeof(UserRec));
Marshal.FreeHGlobal(userRec);
}
Version 2:
I have modified the initial post adding the lineInitializeEx method and lineOpen.
These methods returns a positive value, after this lineDevSpecific continues returning the same value.
[DllImport("Tapi32.dll", CharSet = CharSet.Auto)]
unsafe private static extern long lineInitializeEx(ref uint lphLineApp, uint hInstance, uint lpfnCallback, uint lpszFriendlyAppName, ref uint lpdwNumDevs, ref uint lpdwAPIVersion, ref uint lpLineInitializeExParams);
[DllImport("Tapi32.dll", SetLastError = true)]
internal static extern long lineOpen(ref uint hLineApp, uint dwDeviceID, uint lphLine, uint dwAPIVersion, uint dwExtVersion, uint dwCallbackInstance, uint dwPrivileges, uint dwMediaModes, ref uint lpCallParams);
[DllImport("Tapi32.dll", CharSet = CharSet.Auto)]
unsafe private static extern int lineDevSpecific(uint hLine, IntPtr lpParams);
uint lineApp = 0;
uint numdevs = 0;
uint apiversion = 0;
uint exparams = 0;
uint lpcallparams = 0;
string sParams = "";
long lSize = 0;
long inicio = lineInitializeEx(ref lineApp, 0, 0, 0, ref numdevs, ref apiversion, ref exparams);
if (inicio > 0)
{
long open = lineOpen(ref lineApp, 0, 0, 0, 0, 0, 0, 0, ref lpcallparams);
//string vline = "Ip Office Phone: 201";
//IntPtr hline = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(vline);
var sizeUserRec = Marshal.SizeOf(typeof(UserRec));
var userRec = Marshal.AllocHGlobal(sizeUserRec);
int result = lineDevSpecific(lineApp, userRec);
var x = (UserRec)Marshal.PtrToStructure(userRec, typeof(UserRec));
Marshal.FreeHGlobal(userRec);
}
Solution: I have called to Julmar Atapi.
string extension = "201";
char[] c = new char[extension.Length + 2];
c[0] = (char)0x08; //login character
int i = 1;
foreach (char s in extension)
{
c[i] = s;
i++;
}
c[i] = (char)0x00; //null terminator
CurrentAddress.DeviceSpecific(Encoding.ASCII.GetBytes(c));
That is a LINEERR_ constant, see MSDN LINEERR_ Constants page
These use a hexadecimal unsigned "0x8000 00xx" style, turning them negative if you look at them as a signed int.
So this one is 0x8000 0035 LINEERR_INVALPOINTER
Your hline is wrong, this is a handle to a line not a text in a pointer. You need to get it from a lineOpen
Update for version 2
You are mixing up hLineApp and hLine. From lineOpen MSDN:
hLineApp: Handle to the application's registration with TAPI.
lphLine: Pointer to an HLINE handle that is then loaded with the handle representing the opened line device. Use this handle to identify the device when invoking other functions on the open line device.
My project needs to inspect .c and .dll files. it combines those informations to determine what it is supposed to call and then calls that.
I need to inspect the dlls to find which dll has which function. I have gotten so far as to map the dll to memory without initialising it. now i need to map the header to something so i can read out the section that has the callable names in it.
how can i do that? this is the code so far:
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
public static string[] GetFKTNames(string dll)
{
IntPtr lib = LoadLibraryEx(dll, IntPtr.Zero, LoadLibraryFlags.DONT_RESOLVE_DLL_REFERENCES);
//INDICATES WHAT I WANT TO DO, BUT DOES NOT WORk
//Header header = GetHeader(lib);
//Unload(lib);
//return header.Names;
}
EDIT #2:
i made a little bit of progress and quit it for today... there are 4 free days coming up here in germany...
i am not entirely sure if that marshalling is correct - i had no way to test it. I would love to read a book on that topic - so please comment if you know a good book that explains how that headerstuff works and what different headers are out there.
private static List<string> ListDLLFunctions(string sADllName)
{
List<string> names = new List<string>();
IntPtr LoadedImage = LoadLibraryEx(sADllName, IntPtr.Zero, LoadLibraryFlags.DONT_RESOLVE_DLL_REFERENCES);
IMAGE_NT_HEADERS header = (IMAGE_NT_HEADERS) Marshal.PtrToStructure(libPtr, typeof(IMAGE_NT_HEADERS));
// ImageExportDirectory = (_IMAGE_EXPORT_DIRECTORY*)
// ImageDirectoryEntryToData(LoadedImage.MappedAddress,
// false, IMAGE_DIRECTORY_ENTRY_EXPORT, &cDirSize);
// if (ImageExportDirectory != NULL)
// {
// dNameRVAs = (DWORD *)ImageRvaToVa(LoadedImage.FileHeader,
// LoadedImage.MappedAddress,
// ImageExportDirectory->AddressOfNames, NULL);
// for(size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++)
// {
// sName = (char *)ImageRvaToVa(LoadedImage.FileHeader,
// LoadedImage.MappedAddress,
// dNameRVAs[i], NULL);
// slListOfDllFunctions.push_back(sName);
// }
// }
FreeLibrary(LoadedImage);
return names;
}
static void Main(string[] args)
{
List<string> names = ListDLLFunctions("KERNEL32.DLL");
}
There isn't a "one call" function that can do it for you.
You have to load the dll and look for the names in the export table.
There is a post with some code to do it in c++ - maybe you can try to port it to c#
Non from the code, but from console I usually use
DumpBin utility to see Dll's exports.
I think you can use that tool with Process.Start(..) and parse its output in order to gain an information you need.
Hope this helps.
I solved the problem for my usecase:
I load the lib into momory, copy it into a bytearray and then investigate it using PE information.
There are a couple of useful ressources for this topic that helped me a lot:
c# import examples for relevant structures
msdn article on PE format
zotteljedi article on PE format
nunu brito article on PE format
simon cooper article on PE format
joachim bauch article on loading dll
In retrospec i get how i could have used the other approaches to get my information. The Class that i have written is great to learn PE and explore some dlls, i recommand rewriting it if you want to get the grip of how the PE works.
Here is the Class:
public class DLLHelper
{
private byte[] dllInMemory;
private UInt32 PESizeOfImage;
private UInt32 VA_PE;
[DllImport("kernel32.dll")]
static public extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
[DllImport("kernel32.dll")]
static public extern bool FreeLibrary(IntPtr hModule);
public enum LoadLibraryFlags : uint
{
DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008
}
public DLLHelper(string dllpath)
{
PESizeOfImage = GetDllSizeInMemory(dllpath);
dllInMemory = GetDLLCopy(dllpath, PESizeOfImage);
UInt32 VA_p_PE = 0x3C;
VA_PE = Get4ByteFromLocation(VA_p_PE, dllInMemory);
}
private byte[] GetDLLCopy(string dllpath, uint PESizeOfImage)
{
IntPtr libPtr = LoadLibraryEx(dllpath, IntPtr.Zero, LoadLibraryFlags.DONT_RESOLVE_DLL_REFERENCES);
byte[] dllInMemory = new byte[PESizeOfImage];
Marshal.Copy(libPtr, dllInMemory, 0, (int)PESizeOfImage);
FreeLibrary(libPtr);
return dllInMemory;
}
private UInt32 GetDllSizeInMemory(string dllpath)
{
byte[] dllpreload = File.ReadAllBytes(dllpath);
UInt32 pp_PE = 0x3C;
UInt32 p_PE = Get4ByteFromLocation(pp_PE, dllpreload);
UInt32 p_PEOPTIONALHEADER = p_PE + 0x18;
UInt32 p_PESizeOfImage = p_PEOPTIONALHEADER + 0x38;
return Get4ByteFromLocation(p_PESizeOfImage, dllpreload);
}
public void DumpToFile(String filename)
{
File.WriteAllBytes(filename, dllInMemory);
}
public string GetDLLName()
{
UInt32 VAExport = GetVAExport(VA_PE, dllInMemory);
UInt32 VAName = GetVAName(VAExport, dllInMemory);
String Name = GetString(VAName, dllInMemory);
return Name;
}
public List<String> GetFunctionNames()
{
List<String> fkts = new List<String>();
UInt32 VAExport = GetVAExport(VA_PE, dllInMemory);
UInt32 VA_p_firstFKT = GetVA_p_firstFKT(VAExport, dllInMemory);
UInt32 VA_p_lastFKT = GetVA_p_lastFKT(VAExport, dllInMemory);
for (UInt32 VA_p_fkt = VA_p_firstFKT; VA_p_fkt <= VA_p_lastFKT; VA_p_fkt += sizeof(UInt32))
{
UInt32 VA_fkt = Get4ByteFromLocation(VA_p_fkt, dllInMemory);
fkts.Add(GetString(VA_fkt, dllInMemory));
}
return fkts;
}
private UInt32 GetVA_p_lastFKT(UInt32 VAExport, byte[] dllInMemory)
{
UInt32 first = GetVA_p_firstFKT(VAExport, dllInMemory);
UInt32 count = GetfktCount(VAExport, dllInMemory);
UInt32 last = first + (count - 1) * sizeof(UInt32);
return last;
}
private UInt32 GetfktCount(UInt32 VAExport, byte[] dllInMemory)
{
UInt32 RVA_Count = 0x14;
UInt32 VA_Count = VAExport + RVA_Count;
return Get4ByteFromLocation(VA_Count, dllInMemory);
}
private UInt32 GetVA_p_firstFKT(UInt32 VAExport, byte[] dllInMemory)
{
UInt32 RVA_p_FIRST = 0x20;
UInt32 VA_p_FIRST = VAExport + RVA_p_FIRST;
return Get4ByteFromLocation(VA_p_FIRST, dllInMemory);
}
private UInt32 GetVAName(UInt32 VAExport, byte[] dllInMemory)
{
UInt32 RVA_p_NAME = 0x0C;
UInt32 VA_p_NAME = VAExport + RVA_p_NAME;
return Get4ByteFromLocation(VA_p_NAME, dllInMemory);
}
private UInt32 GetVAExport(UInt32 VAPE, byte[] dllInMemory)
{
UInt32 RVA_p_EXPORT = 0x78;
UInt32 VA_p_EXPORT = VAPE + RVA_p_EXPORT;
return Get4ByteFromLocation(VA_p_EXPORT, dllInMemory);
}
string GetString(UInt32 location, byte[] dll)
{
int length = 0;
while (dll[location + length] != 0x00)
{
length++;
}
if (location > int.MaxValue) throw new Exception("uncastable");
return Encoding.UTF8.GetString(dll, (int)location, length);
}
private UInt32 Get4ByteFromLocation(UInt32 location, byte[] dll)
{
if (!(BitConverter.IsLittleEndian))
{
byte[] partial = GetByteSubset(4, location, dll);
Array.Reverse(partial);
return BitConverter.ToUInt32(partial, 0);
}
return BitConverter.ToUInt32(dll, (int)location);
}
private byte[] GetByteSubset(int size, UInt32 location, byte[] dll)
{
byte[] val = new byte[size];
for (int i = 0; i < size; i++)
{
val[i] = dll[location + i];
}
return val;
}
}
I am trying to digitally sign files using the CryptUIWizDigitalSign function from a .NET 2.0 application compiled to AnyCPU. The call works fine when running on x86 but fails on x64, it also works on an x64 OS when compiled to x86. Any idea on how to better marshall or call from x64?
The Win32exception returned is "Error encountered during digital signing of the file ..." with a native error code of -2146762749.
The relevant portion of the code are:
[StructLayout(LayoutKind.Sequential)]
public struct CRYPTUI_WIZ_DIGITAL_SIGN_INFO {
public Int32 dwSize;
public Int32 dwSubjectChoice;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszFileName;
public Int32 dwSigningCertChoice;
public IntPtr pSigningCertContext;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszTimestampURL;
public Int32 dwAdditionalCertChoice;
public IntPtr pSignExtInfo;
}
[DllImport("Cryptui.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern bool CryptUIWizDigitalSign(int dwFlags, IntPtr hwndParent, string pwszWizardTitle, ref CRYPTUI_WIZ_DIGITAL_SIGN_INFO pDigitalSignInfo, ref IntPtr ppSignContext);
CRYPTUI_WIZ_DIGITAL_SIGN_INFO digitalSignInfo = new CRYPTUI_WIZ_DIGITAL_SIGN_INFO();
digitalSignInfo = new CRYPTUI_WIZ_DIGITAL_SIGN_INFO();
digitalSignInfo.dwSize = Marshal.SizeOf(digitalSignInfo);
digitalSignInfo.dwSubjectChoice = 1;
digitalSignInfo.dwSigningCertChoice = 1;
digitalSignInfo.pSigningCertContext = pSigningCertContext;
digitalSignInfo.pwszTimestampURL = timestampUrl;
digitalSignInfo.dwAdditionalCertChoice = 0;
digitalSignInfo.pSignExtInfo = IntPtr.Zero;
digitalSignInfo.pwszFileName = filepath;
CryptUIWizDigitalSign(1, IntPtr.Zero, null, ref digitalSignInfo, ref pSignContext));
And here is how the SigningCertContext is retrieved (minus various error handling)
public IntPtr GetCertContext(String pfxfilename, String pswd)
IntPtr hMemStore = IntPtr.Zero;
IntPtr hCertCntxt = IntPtr.Zero;
IntPtr pProvInfo = IntPtr.Zero;
uint provinfosize = 0;
try {
byte[] pfxdata = PfxUtility.GetFileBytes(pfxfilename);
CRYPT_DATA_BLOB ppfx = new CRYPT_DATA_BLOB();
ppfx.cbData = pfxdata.Length;
ppfx.pbData = Marshal.AllocHGlobal(pfxdata.Length);
Marshal.Copy(pfxdata, 0, ppfx.pbData, pfxdata.Length);
hMemStore = Win32.PFXImportCertStore(ref ppfx, pswd, CRYPT_USER_KEYSET);
pswd = null;
if (hMemStore != IntPtr.Zero) {
Marshal.FreeHGlobal(ppfx.pbData);
while ((hCertCntxt = Win32.CertEnumCertificatesInStore(hMemStore, hCertCntxt)) != IntPtr.Zero) {
if (Win32.CertGetCertificateContextProperty(hCertCntxt, CERT_KEY_PROV_INFO_PROP_ID, IntPtr.Zero, ref provinfosize))
pProvInfo = Marshal.AllocHGlobal((int)provinfosize);
else
continue;
if (Win32.CertGetCertificateContextProperty(hCertCntxt, CERT_KEY_PROV_INFO_PROP_ID, pProvInfo, ref provinfosize))
break;
}
}
finally {
if (pProvInfo != IntPtr.Zero)
Marshal.FreeHGlobal(pProvInfo);
if (hMemStore != IntPtr.Zero)
Win32.CertCloseStore(hMemStore, 0);
}
return hCertCntxt;
}
When applications are compiled with AnyCPU target, they will load as 32-bit on 32 bit OS, and 64-bit on 64 bit OS. You cannot load a 32-bit DLL from a 64-bit process.
You have said that it "works" when compiling to x86. Can you just do this? This leads me to believe that the cryptui.dll in your search path is a 32-bit DLL.