strings breaking coming back from unmanaged code C++ - c#

I'm creating a wrapper for an unmanaged C++ function to be called using C#.
The function returns a vector of structs.
When returning from the function, the strings are all ok, but after the 'return' to the wrapper, the strings break returning weird characters.
The weird thing is, if I call it a second time, without closing Visual Studio, it works!
I have saved all the files as Unicode UTF-8, set Unicode as the character set on the Visual Studio project, defined UNICODE and _UNICODE, but still the problem persists.
This is the unmanaged struct:
typedef struct SessionEnumOutput {
SessionEnumOutput() {};
wchar_t *UserName;
wchar_t *SessionName;
WtsSessionState SessionState;
}SessionEnumOutput, *PSessionEnumOutput;
It being built on the unmanaged function:
for (DWORD i = 0; i < pCount; i++)
{
WTS_SESSION_INFO_1 innerSes = sessionInfo[i];
if (innerSes.State == WTSActive)
{
wchar_t *sessionName;
wchar_t *sessUserName;
SessionEnumOutput outObj;
if (innerSes.pUserName == NULL) { sessUserName = L"System"; }
else { sessUserName = innerSes.pUserName; }
if (innerSes.pSessionName == NULL) { sessionName = L""; }
else { sessionName = innerSes.pSessionName; }
Unmanaged::SessionEnumOutput inner;
inner.UserName = sessUserName;
inner.SessionName = sessionName;
inner.SessionState = (WtsSessionState)innerSes.State;
output.push_back(inner);
}
}
The managed wrapper:
ref class wSessionEnumOutput
{
public:
String^ UserName;
String^ SessionName;
wWtsSessionState SessionState;
};
List<wSessionEnumOutput^>^ GetEnumeratedSession(String^ computerName, bool onlyActive, bool excludeSystemSessions)
{
pin_ptr<const wchar_t> wName = PtrToStringChars(computerName);
List<wSessionEnumOutput^>^ output = gcnew List<wSessionEnumOutput^>();
vector<Unmanaged::SessionEnumOutput> *result = new vector<Unmanaged::SessionEnumOutput>;
*result = ptr->GetEnumeratedSession((LPWSTR)wName, onlyActive, excludeSystemSessions);
for (size_t it = 0; it < result->size(); it++)
{
Unmanaged::SessionEnumOutput single = result->at(it);
wSessionEnumOutput^ inner = gcnew wSessionEnumOutput();
inner->UserName = Marshal::PtrToStringUni((IntPtr)single.UserName);
inner->SessionName = Marshal::PtrToStringUni((IntPtr)single.SessionName);
inner->SessionState = (wWtsSessionState)single.SessionState;
output->Add(inner);
}
I can see the strings broken at *vectorUnmanaged::SessionEnumOutput result = new vectorUnmanaged::SessionEnumOutput;
I have created a test console to call the C# function twice to analyze the heap:
List<Managed.wSessionEnumOutput> thing = Utilities.GetComputerSession();
Console.WriteLine("###################################################");
for (int i = 0; i < thing.Count; i++)
{
Console.WriteLine("User name: " + thing[i].UserName);
Console.WriteLine("Session name: " + thing[i].SessionName);
Console.WriteLine("Session state: " + thing[i].SessionState);
Console.WriteLine("###################################################");
}
Console.WriteLine("\n");
thing = Utilities.GetComputerSession();
Console.WriteLine("###################################################");
for (int i = 0; i < thing.Count; i++)
{
Console.WriteLine("User name: " + thing[i].UserName);
Console.WriteLine("Session name: " + thing[i].SessionName);
Console.WriteLine("Session state: " + thing[i].SessionState);
Console.WriteLine("###################################################");
}
The difference is, on the second call, I can see Unicode and UTF-8 decoders loaded on the heap.
On the first call, they are not there.
here are both call's results:
I'm not a developer, just a curious system administrator, so pardon my coding habilities.
What am I missing?
EDIT:
Function definition:
vector<Unmanaged::SessionEnumOutput> Unmanaged::GetEnumeratedSession(
LPWSTR computerName = NULL,
BOOL onlyActive = 0,
BOOL excludeSystemSessions = 0
)
{
HANDLE session;
BOOL enumResult;
DWORD pCount = 0;
DWORD pLevel = 1;
vector<SessionEnumOutput> output;
PWTS_SESSION_INFO_1 sessionInfo = (PWTS_SESSION_INFO_1)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WTS_SESSION_INFO_1));
if (computerName != NULL)
{
session = WTSOpenServer(computerName);
if (session == NULL) { goto END; }
}
else { session = WTS_CURRENT_SERVER_HANDLE; }
enumResult = WTSEnumerateSessionsEx(session, &pLevel, 0, &sessionInfo, &pCount);
if (enumResult == 0) { goto END; }
switch (onlyActive)
{
case 1:
for (DWORD i = 0; i < pCount; i++)
{
WTS_SESSION_INFO_1 innerSes = sessionInfo[i];
if (innerSes.State == WTSActive)
{
wchar_t *sessionName;
wchar_t *sessUserName;
SessionEnumOutput outObj;
if (innerSes.pUserName == NULL) { sessUserName = L"System"; }
else { sessUserName = innerSes.pUserName; }
if (innerSes.pSessionName == NULL) { sessionName = L""; }
else { sessionName = innerSes.pSessionName; }
Unmanaged::SessionEnumOutput inner;
inner.UserName = sessUserName;
inner.SessionName = sessionName;
inner.SessionState = (WtsSessionState)innerSes.State;
output.push_back(inner);
}
}
break;
default:
if (excludeSystemSessions == 0)
{
for (DWORD i = 0; i < pCount; i++)
{
WTS_SESSION_INFO_1 innerSes = sessionInfo[i];
wchar_t* sessionName;
wchar_t* sessUserName;
SessionEnumOutput outObj;
if (innerSes.pUserName == NULL) { sessUserName = L"System"; }
else { sessUserName = innerSes.pUserName; }
if (innerSes.pSessionName == NULL) { sessionName = L""; }
else { sessionName = innerSes.pSessionName; }
Unmanaged::SessionEnumOutput inner;
inner.UserName = sessUserName;
inner.SessionName = sessionName;
inner.SessionState = (WtsSessionState)innerSes.State;
output.push_back(inner);
}
}
else
{
for (DWORD i = 0; i < pCount; i++)
{
WTS_SESSION_INFO_1 innerSes = sessionInfo[i];
wstring sessUserName;
if (innerSes.pUserName == NULL) { sessUserName = L""; }
else { sessUserName = innerSes.pUserName; }
if (sessUserName.length() > 0)
{
wchar_t *innerUser = (wchar_t*)sessUserName.c_str();
wchar_t *sessionName;
SessionEnumOutput outObj;
WTS_SESSION_INFO_1 innerSes = sessionInfo[i];
if (innerSes.pSessionName == NULL) { sessionName = L""; }
else { sessionName = innerSes.pSessionName; }
Unmanaged::SessionEnumOutput inner;
inner.UserName = innerUser;
inner.SessionName = sessionName;
inner.SessionState = (WtsSessionState)innerSes.State;
output.push_back(inner);
}
}
}
break;
}
END:
if (session != NULL) { WTSCloseServer(session); }
if (pCount > 0) { WTSFreeMemoryEx(WTSTypeSessionInfoLevel1, sessionInfo, pCount); }
return output;
}

Solved the mystery!
Mr. CharlieFace on the comments posted another question where they are discussing the unespected behavior of the function WTSEnumerateSessionsEx.
Unfortunately, this is an issue happening on Windows for some time now.
I've followed the approach of calling WTSEnumerateSessions and then WTSQuerySessionInformation to get the user name.
Fragment:
PWTS_SESSION_INFO sessionInfo = (PWTS_SESSION_INFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WTS_SESSION_INFO));
if (computerName != NULL)
{
session = WTSOpenServer(computerName);
if (session == NULL) { goto END; }
}
else { session = WTS_CURRENT_SERVER_HANDLE; }
enumResult = WTSEnumerateSessions(session, 0, 1, &sessionInfo, &pCount);
if (enumResult == 0) { goto END; }
switch (onlyActive)
{
case 1:
for (DWORD i = 0; i < pCount; i++)
{
WTS_SESSION_INFO innerSes = sessionInfo[i];
if (innerSes.State == WTSActive)
{
wchar_t *sessionName;
wchar_t *sessUserName;
LPWSTR ppBuffer;
DWORD pBytesReturned;
BOOL thisResult;
thisResult = WTSQuerySessionInformation(session, innerSes.SessionId, WTSUserName, &ppBuffer, &pBytesReturned);
if (thisResult == 0) { goto END; }
if (ppBuffer == NULL) { sessUserName = L"System"; }
else { sessUserName = ppBuffer; }
if (innerSes.pWinStationName == NULL) { sessionName = L""; }
else { sessionName = innerSes.pWinStationName; }
Unmanaged::SessionEnumOutput inner;
inner.UserName = sessUserName;
inner.SessionName = sessionName;
inner.SessionState = (WtsSessionState)innerSes.State;
output.push_back(inner);
WTSFreeMemory(&ppBuffer);
}
}
break;
Look how pretty that is!
Sources:
Why does WTSFreeMemoryExA always return ERROR_INVALID_PARAMETER when passed a WTSTypeClass of WTSTypeSessionInfoLevel1?
Memory leak issues with Windows API call - Delphi
Thank you very much for the help!

As I mentioned, there is an outstanding bug involving the WTS functions when used with the ANSI versions. Instead you should use the Unicode versions.
See Why does WTSFreeMemoryExA always return ERROR_INVALID_PARAMETER when passed a WTSTypeClass of WTSTypeSessionInfoLevel1?
I think using a C++/CLI wrapper for this to be able to use in C# is overkill. You should be able to do this using standard PInvoke marshalling in C#.
It's best not to rely on BestFitMapping, and instead specify the function names explicitly.
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)]
static extern IntPtr WTSOpenServerExW (string pServerName);
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)]
static extern void WTSCloseServer(IntPtr hServer);
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)]
static extern bool WTSEnumerateSessionsExW(
IntPtr hServer,
ref int pLevel,
int Filter,
out IntPtr ppSessionInfo,
out int pCount
);
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)]
static extern bool WTSFreeMemoryExW(
WTS_TYPE_CLASS WTSTypeClass,
IntPtr pMemory,
int NumberOfEntries
);
enum WTS_TYPE_CLASS
{
WTSTypeProcessInfoLevel0,
WTSTypeProcessInfoLevel1,
WTSTypeSessionInfoLevel1
}
enum WtsSessionState
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct WTS_SESSION_INFO
{
public int ExecEnvId;
public WtsSessionState State;
public int SessionId;
[MarshalAs(UnmanagedType.LPTStr)]
public string pSessionName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pHostName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pUserName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pDomainName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pFarmName;
}
List<SessionEnumOutput> GetEnumeratedSession(
string computerName = null,
bool onlyActive = false,
bool excludeSystemSessions = false
)
{
IntPtr server = default;
IntPtr sessionInfo = default;
int pCount = default;
List<SessionEnumOutput> output = new List<SessionEnumOutput>();
if (computerName != null)
{
server = WTSOpenServerExW(computerName);
if (server == IntPtr.Zero || server == new IntPtr(-1))
throw new Exception("Invalid computer name");
}
try
{
int pLevel = 1;
if (!WTSEnumerateSessionsExW(server, ref pLevel, 0, out sessionInfo, out pCount))
throw new Win32Exception(Marshal.GetLastWin32Error());
for (var i = 0; i < pCount; i++)
{
WTS_SESSION_INFO innerSes = Marshal.PtrToStructure<WTS_SESSION_INFO>(sessionInfo + i * Marshal.SizeOf<WTS_SESSION_INFO>());
if (onlyActive && innerSes.State != WtsSessionState.WTSActive
|| excludeSystemSessions && innerSes.pSessionName == null)
continue;
SessionEnumOutput inner = new SessionEnumOutput
{
UserName = innerSes.pUserName ?? "System",
SessionName = innerSes.pSessionName ?? "",
SessionState = innerSes.State,
};
output.Add(inner);
};
}
finally
{
if (sessionInfo != default)
WTSFreeMemoryExW(WTS_TYPE_CLASS.WTSTypeSessionInfoLevel1, sessionInfo, pCount);
if (server != default)
WTSCloseServer(server);
}
return output;
}
Note the use of a finally to enure memory is freed correctly.
There is also a huge amount of duplicated code. I have cleaned up the rest of the function significantly.

Related

In multithreading, C# calls C++DLL function error

My C# program needs to call C++ functions located in an external DLL.
I found that it works fine under single thread, but error under multithread.
C++ code:
enum ProgramExitType:int {
ET_ERROR,
ET_SUCCEED,
ET_TLE,
ET_MLE
};
enum ProgramErrorType :int {
PE_UNKNOWN,
PE_INPUT,
PE_OUTPUT,
PE_ERRPUT,
PE_CREATEPROCESS,
PE_WAIT,
};
struct ProgramExitInfo {
ProgramExitType type;
ProgramErrorType err_type;
long long memory_usage;
int time;
unsigned int exit_code;
};
#define ___RR_COMBINE_(x,y) x##y
#define __RR_COMBINE(x,y) ___RR_COMBINE_(x,y)
#define UNIQUESYM __RR_COMBINE(__RRLibrary__unique_symbol_,__COUNTER__)
#define AUTOCLOSE(h) AutoClose UNIQUESYM(h)
#define AUTOCLOSEREF(h) AutoCloseRef UNIQUESYM(h)
class AutoClose {
HANDLE h;
public:
AutoClose(HANDLE h) :h(h) {}
~AutoClose() { CloseHandle(h);/* LOGW(L"AutoClose\n"); */ }
};
class AutoCloseRef {
HANDLE* h;
public:
AutoCloseRef(HANDLE* h) :h(h) {}
~AutoCloseRef() { CloseHandle(*h); /* LOGW(L"AutoCloseRef\n");*/ }
};
ProgramExitInfo RunProgram(PCWSTR path, PCWSTR param, PCWSTR input, PCWSTR output, PCWSTR errput, int time_limit, long long memory_limit) {
//MessageBox(0, L"RunProgram Start", L"", MB_ICONINFORMATION);
ProgramExitInfo ret;
ZeroMemory(&ret, sizeof(ProgramExitInfo));
HANDLE hin = INVALID_HANDLE_VALUE, hout = INVALID_HANDLE_VALUE, herr = INVALID_HANDLE_VALUE;
AUTOCLOSEREF(&hin); AUTOCLOSEREF(&hout); AUTOCLOSEREF(&herr);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if (input != NULL && *input != 0) {
//MessageBox(0, input, L"Load Input", MB_ICONINFORMATION);
hin = CreateFile(input, GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hin == 0 || hin == INVALID_HANDLE_VALUE) {
ret.type = ET_ERROR;
ret.err_type = PE_INPUT;
return ret;
}
}
if (output != NULL && *output != 0) {
//MessageBox(0, input, L"Load Output", MB_ICONINFORMATION);
hout = CreateFile(output, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hout == 0 || hout == INVALID_HANDLE_VALUE) {
ret.type = ET_ERROR;
ret.err_type = PE_OUTPUT;
return ret;
}
}
if (errput != NULL && *errput != 0) {
//MessageBox(0, input, L"Load Errput", MB_ICONINFORMATION);
herr = CreateFile(errput, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (herr == 0 || herr == INVALID_HANDLE_VALUE) {
ret.type = ET_ERROR;
ret.err_type = PE_ERRPUT;
return ret;
}
}
STARTUPINFO si = { sizeof(si) };
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = hout;
si.hStdError = herr;
si.hStdInput = hin;
PROCESS_INFORMATION pi = { 0 };
wchar_t* param_buff = 0;
if (param != NULL) {
param_buff = new wchar_t[lstrlenW(param) + 1];
lstrcpyW(param_buff, param);
}
BOOL b = CreateProcess(path, NULL,
NULL,
NULL,
TRUE,
NULL,
NULL,
NULL,
&si, &pi);
if (param_buff != 0)
delete[] param_buff;
AUTOCLOSE(pi.hProcess); AUTOCLOSE(pi.hThread);
if (b == FALSE) {
ret.type = ET_ERROR;
ret.err_type = PE_CREATEPROCESS;
return ret;
}
int ti = GetTickCount();
DWORD dw = WaitForSingleObject(pi.hProcess, time_limit);
ret.time = GetTickCount() - ti;
if (dw != WAIT_OBJECT_0)
TerminateProcess(pi.hProcess, -1);
GetExitCodeProcess(pi.hProcess, (LPDWORD)&ret.exit_code);
switch (dw) {
case WAIT_OBJECT_0: {
break;
}
case WAIT_TIMEOUT: {
ret.type = ET_TLE;
break;
}
default: {
ret.type = ET_ERROR;
ret.err_type = PE_WAIT;
return ret;
}
}
PROCESS_MEMORY_COUNTERS pmc = { 0 };
GetProcessMemoryInfo(pi.hProcess, &pmc, sizeof(pmc));
ret.memory_usage = (long long)pmc.PeakWorkingSetSize + pmc.PeakPagefileUsage;
if (ret.memory_usage > memory_limit) {
ret.type = ET_MLE;
return ret;
}
ret.type = ET_SUCCEED;
return ret;
}
C# code:
[DllImport("ProgramRunningHelper.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ProgramExitInfo RunProgram([MarshalAs(UnmanagedType.LPWStr)]string path, [MarshalAs(UnmanagedType.LPWStr)]string param, [MarshalAs(UnmanagedType.LPWStr)]string input, [MarshalAs(UnmanagedType.LPWStr)]string output, [MarshalAs(UnmanagedType.LPWStr)]string errput, int time_limit, long memory_limit);
I use RunProgram runs different .exe file with different in/output redirections in different thread.
But the function returns PE_INPUTorPE_OUTPUT which means it cannot create an in/output file.
I called FindLastError() and get 32 "Another program is using this file and the process cannot access it."
But if there is only one thread, no any errors.
So I think this may be because the parameters passed by different threads are confused( For example, there are two threads passing parameters 'a' and 'b' respectively, but CLR considers that both of them passed 'a'. ). Is it possible?
How can I solve it?
Any help would be much appreciated !

Visual Studio - Get the startup project programmatically

I'm building a .Net application that should import a VS2010/2008 solution and find the start up project by reading the .suo file. I looked at How do I programmatically find out the Action of each StartUp Project in a solution? but the solution mentioned works only if I select Multiple Startup Projects in the Solution Properties window. My solution does not have multiple startup project. Is there a way to find the startup project when the solution has only 1 startup proj?
Here's the code
public static class StartUpProjectHelper
{
public static FileInfo GetStartUpProject(FileInfo solutionFile)
{
FileInfo startUpProject = null;
string suoFile = solutionFile.FullName.Substring(0, solutionFile.FullName.Length - 4) + ".suo";
string guid = null;
bool found = false;
foreach (var kvp in ReadStartupOptions(suoFile))
{
if (((kvp.Value & 1) != 0 || (kvp.Value & 2) != 0) && !found)
{
guid = kvp.Key.ToString();
found = true;
}
}
if (!string.IsNullOrEmpty(guid))
{
string projectname = GetProjectNameFromGuid(solutionFile, guid).Trim().TrimStart('\"').TrimEnd('\"');
startUpProject = new FileInfo(Path.Combine(solutionFile.DirectoryName, projectname));
}
return startUpProject;
}
public static string GetProjectNameFromGuid(FileInfo solutionFile, string guid)
{
string projectName = null;
using (var reader = new StreamReader(solutionFile.FullName))
{
string line;
bool found = false;
while ((line = reader.ReadLine()) != null && !found)
{
// sample format
//Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Saltus.digiTICKET.laptop", "digiTICKET.laptop\Saltus.digiTICKET.laptop.csproj",
//"{236D51A1-DEB7-41C3-A4C1-1D16D0A85382}" EndProject
if ((line.IndexOf(guid.ToUpper()) > -1) && line.Contains(",") && line.Contains("="))
{
projectName = line.Split(',')[1].Split(',')[0];
found = true;
}
}
}
return projectName;
}
//// from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut
public static IDictionary<Guid, int> ReadStartupOptions(string filePath)
{
if (filePath == null)
{
throw new InvalidOperationException("No file selected");
}
// look for this token in the file
const string token = "dwStartupOpt\0=";
byte[] tokenBytes = Encoding.Unicode.GetBytes(token);
var dic = new Dictionary<Guid, int>();
byte[] bytes;
using (var stream = new MemoryStream())
{
ExtractStream(filePath, "SolutionConfiguration", stream);
bytes = stream.ToArray();
}
int i = 0;
do
{
bool found = true;
for (int j = 0; j < tokenBytes.Length; j++)
{
if (bytes[i + j] != tokenBytes[j])
{
found = false;
break;
}
}
if (found)
{
// back read the corresponding project guid
// guid is formatted as {guid}
// len to read is Guid length* 2 and there are two offset bytes between guid and startup options token
var guidBytes = new byte[38*2];
Array.Copy(bytes, i - guidBytes.Length - 2, guidBytes, 0, guidBytes.Length);
var guid = new Guid(Encoding.Unicode.GetString(guidBytes));
// skip VT_I4
int options = BitConverter.ToInt32(bytes, i + tokenBytes.Length + 2);
dic[guid] = options;
}
i++;
} while (i < bytes.Length);
return dic;
}
public static void ExtractStream(string filePath, string streamName, Stream output)
{
if (filePath == null)
throw new ArgumentNullException("filePath");
if (streamName == null)
throw new ArgumentNullException("streamName");
if (output == null)
throw new ArgumentNullException("output");
IStorage storage;
int hr = StgOpenStorage(filePath, null, STGM.READ | STGM.SHARE_DENY_WRITE, IntPtr.Zero, 0, out storage);
if (hr != 0)
throw new Win32Exception(hr);
try
{
IStream stream;
hr = storage.OpenStream(streamName, IntPtr.Zero, STGM.READ | STGM.SHARE_EXCLUSIVE, 0, out stream);
if (hr != 0)
throw new Win32Exception(hr);
int read = 0;
IntPtr readPtr = Marshal.AllocHGlobal(Marshal.SizeOf(read));
try
{
var bytes = new byte[0x1000];
do
{
stream.Read(bytes, bytes.Length, readPtr);
read = Marshal.ReadInt32(readPtr);
if (read == 0)
break;
output.Write(bytes, 0, read);
} while (true);
}
finally
{
Marshal.FreeHGlobal(readPtr);
Marshal.ReleaseComObject(stream);
}
}
finally
{
Marshal.ReleaseComObject(storage);
}
}
[DllImport("ole32.dll")]
private static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
IStorage pstgPriority, STGM grfMode, IntPtr snbExclude, uint reserved,
out IStorage ppstgOpen);
#region Nested type: IStorage
[ComImport, Guid("0000000b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IStorage
{
void Unimplemented0();
[PreserveSig]
int OpenStream([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, IntPtr reserved1, STGM grfMode,
uint reserved2, out IStream ppstm);
// other methods not declared for simplicity
}
#endregion
#region Nested type: STGM
[Flags]
private enum STGM
{
READ = 0x00000000,
SHARE_DENY_WRITE = 0x00000020,
SHARE_EXCLUSIVE = 0x00000010,
// other values not declared for simplicity
}
#endregion
}
//// from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut``
Here's the solution. It is mostly based on #SimonMourier post, but instead of searching for "dwStartupOpt\0=", I'm looking for "StartupProject\0=&\0". This token occurs only once in the .suo file and is followed by the startup project's GUID.
public static FileInfo GetStartUpProject(FileInfo solutionFile)
{
FileInfo startUpProject = null;
string projectName = Path.GetFileNameWithoutExtension(solutionFile.FullName);
FileInfo suoFileInfo = new FileInfo(Path.Combine(solutionFile.Directory.FullName, string.Format(projectName + "{0}", ".suo")));
string guid = ReadStartupOptions(suoFileInfo.FullName).ToString();
if (!string.IsNullOrEmpty(guid))
{
string projectname = GetProjectNameFromGuid(solutionFile, guid).Trim().TrimStart('\"').TrimEnd('\"');
startUpProject = new FileInfo(Path.Combine(solutionFile.DirectoryName, projectname));
}
return startUpProject;
}
public static string GetProjectNameFromGuid(FileInfo solutionFile, string guid)
{
string projectName = null;
using (var reader = new StreamReader(solutionFile.FullName))
{
string line;
bool found = false;
while ((line = reader.ReadLine()) != null && !found)
{
if ((line.IndexOf(guid.ToUpper()) > -1) && line.Contains(",") && line.Contains("="))
{
projectName = line.Split(',')[1].Split(',')[0];
found = true;
}
}
}
return projectName;
}
// from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut
public static Guid ReadStartupOptions(string filePath)
{
Guid guid = new Guid();
if (filePath == null)
{
throw new InvalidOperationException("No file selected");
}
const string token = "StartupProject\0=&\0";
byte[] tokenBytes = Encoding.Unicode.GetBytes(token);
byte[] bytes;
using (var stream = new MemoryStream())
{
ExtractStream(filePath, "SolutionConfiguration", stream);
bytes = stream.ToArray();
}
var guidBytes = new byte[36 * 2];
for (int i2 = 0; i2 < bytes.Length; i2++)
{
if (bytes.Skip(i2).Take(tokenBytes.Length).SequenceEqual(tokenBytes))
{
Array.Copy(bytes, i2 + tokenBytes.Length + 2, guidBytes, 0, guidBytes.Length);
guid = new Guid(Encoding.Unicode.GetString(guidBytes));
break;
}
}
return guid;
}
public static void ExtractStream(string filePath, string streamName, Stream output)
{
if (filePath == null)
throw new ArgumentNullException("filePath");
if (streamName == null)
throw new ArgumentNullException("streamName");
if (output == null)
throw new ArgumentNullException("output");
IStorage storage;
int hr = StgOpenStorage(filePath, null, STGM.READ | STGM.SHARE_DENY_WRITE, IntPtr.Zero, 0, out storage);
if (hr != 0)
throw new Win32Exception(hr);
try
{
IStream stream;
hr = storage.OpenStream(streamName, IntPtr.Zero, STGM.READ | STGM.SHARE_EXCLUSIVE, 0, out stream);
if (hr != 0)
throw new Win32Exception(hr);
int read = 0;
IntPtr readPtr = Marshal.AllocHGlobal(Marshal.SizeOf(read));
try
{
var bytes = new byte[0x1000];
do
{
stream.Read(bytes, bytes.Length, readPtr);
read = Marshal.ReadInt32(readPtr);
if (read == 0)
break;
output.Write(bytes, 0, read);
} while (true);
}
finally
{
Marshal.FreeHGlobal(readPtr);
Marshal.ReleaseComObject(stream);
}
}
finally
{
Marshal.ReleaseComObject(storage);
}
}
[DllImport("ole32.dll")]
private static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
IStorage pstgPriority, STGM grfMode, IntPtr snbExclude, uint reserved,
out IStorage ppstgOpen);
#region Nested type: IStorage
[ComImport, Guid("0000000b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IStorage
{
void Unimplemented0();
[PreserveSig]
int OpenStream([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, IntPtr reserved1, STGM grfMode,
uint reserved2, out IStream ppstm);
// other methods not declared for simplicity
}
#endregion
#region Nested type: STGM
[Flags]
private enum STGM
{
READ = 0x00000000,
SHARE_DENY_WRITE = 0x00000020,
SHARE_EXCLUSIVE = 0x00000010,
// other values not declared for simplicity
}
#endregion

C# Easyhook Winsock WS2_32.dll,connect hook Socks5

I'm trying to hook the winsock connect function and route the TCP connection through socks5 proxy /w auth.
This works if the socket is a blocking socket, but while using firefox ( nonblocking sockets ) I get a lot of 10035, 10022 winsock Errors.
How can i determine if it's a nonblocking / blocking socket?
I would really appreciate any hints or ideas to achieve the functionality to hook the wsock connect function and route tcp traffic through a socks5 server.
I can put the demo application on github if anybody wants to test it. ( Works with any version of firefox )
Edit1: https://github.com/duketwo/WinsockConnectHookSocks5/
( You have to edit the proxy information in WSockConnectHook/HookManager.cs and the path of firefox in Injector/MainForm.cs )
Edit2: It's easyhook which is causing the trouble, anything after the original function call doesn't work properly.
Edit3: Seems like i got it working with many flaws, in fact it is required differentiate between nonblocking sockets and blocking sockets. Any ideas how to achieve this?
Edit4: Windows doesn't offer any method to retrieve the blocking-attribute of a socket, so I might have to hook the ioctlsocket function to keep track of the blocking status of the sockets.
Thanks
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using EasyHook;
using System.IO;
using System.Windows.Forms;
namespace WSockConnectHook
{
public class WinSockConnectController : IDisposable, IHook
{
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi, SetLastError = true)]
private delegate int WinsockConnectDelegate(IntPtr s, IntPtr addr, int addrsize);
[DllImport("WS2_32.dll", SetLastError = true)]
public static extern int connect(IntPtr s, IntPtr addr, int addrsize);
[StructLayout(LayoutKind.Sequential, Size = 16)]
public struct sockaddr_in
{
public const int Size = 16;
public short sin_family;
public ushort sin_port;
public struct in_addr
{
public uint S_addr;
public struct _S_un_b
{
public byte s_b1, s_b2, s_b3, s_b4;
}
public _S_un_b S_un_b;
public struct _S_un_w
{
public ushort s_w1, s_w2;
}
public _S_un_w S_un_w;
}
public in_addr sin_addr;
}
[DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int WSAGetLastError();
[DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern void WSASetLastError(int set);
[DllImport("Ws2_32.dll", CharSet = CharSet.Ansi)]
public static extern uint inet_addr(string cp);
[DllImport("Ws2_32.dll")]
public static extern ushort htons(ushort hostshort);
[DllImport("ws2_32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr socket(short af, short socket_type, int protocol);
[DllImport("Ws2_32.dll")]
public static extern int send(IntPtr s, IntPtr buf, int len, int flags);
[DllImport("Ws2_32.dll")]
public static extern int recv(IntPtr s, IntPtr buf, int len, int flags);
[DllImport("ws2_32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int closesocket(IntPtr s);
[DllImport("Ws2_32.dll")]
public static extern ushort ntohs(ushort netshort);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern void SetLastError(int errorCode);
private string _name;
private LocalHook _hook;
public bool Error { get; set; }
public string Name { get; set; }
private string proxyIp, proxyPort, proxyUser, proxyPass;
public WinSockConnectController(IntPtr address, string proxyIp, string proxyPort, string proxyUser, string proxyPass)
{
this.Name = typeof(WinSockConnectController).Name;
this.proxyIp = proxyIp;
this.proxyPort = proxyPort;
this.proxyUser = proxyUser;
this.proxyPass = proxyPass;
try
{
_name = string.Format("WinsockHook_{0:X}", address.ToInt32());
_hook = LocalHook.Create(address, new WinsockConnectDelegate(WinsockConnectDetour), this);
_hook.ThreadACL.SetExclusiveACL(new Int32[] { 1 });
}
catch (Exception)
{
this.Error = true;
}
}
private object wSockLock = new object();
private int WinsockConnectDetour(IntPtr s, IntPtr addr, int addrsize)
{
lock (wSockLock)
{
// retrieve remote ip
sockaddr_in structure = (sockaddr_in)Marshal.PtrToStructure(addr, typeof(sockaddr_in));
string remoteIp = new System.Net.IPAddress(structure.sin_addr.S_addr).ToString();
ushort remotePort = ntohs(structure.sin_port);
HookManager.Log("Ip: " + remoteIp + " Port: " + remotePort.ToString() + " Addrsize: " + addrsize);
if (!proxyIp.Equals(""))
//if (!proxyIp.Equals(""))
{
// connect to socks5 server
SetAddr(s, addr, proxyIp, proxyPort);
var result = Connect(s, addr, addrsize);
if (result == -1)
return -1;
// send socks 5 request
IntPtr socksProtocolRequest = SetUpSocks5Request();
result = send(s, socksProtocolRequest, 4, 0);
if (result == -1)
return -1;
// retrieve server repsonse
var response = Recieve(s, 2);
if (response == IntPtr.Zero)
return -1;
byte[] recvBytes = new byte[2] { Marshal.ReadByte(response), Marshal.ReadByte(response, 1) };
if (recvBytes[1] == 255)
{
HookManager.Log("No authentication method was accepted by the proxy server");
return -1;
}
if (recvBytes[0] != 5)
{
HookManager.Log("No SOCKS5 proxy");
return -1;
}
// if auth request response, send authenicate request
if (recvBytes[1] == 2)
{
int length = 0;
var authenticateRequest = SetUpAuthenticateRequest(proxyUser, proxyPass, out length);
result = Send(s, authenticateRequest, length);
response = Recieve(s, 2);
if (response == IntPtr.Zero)
return -1;
recvBytes = new byte[2] { Marshal.ReadByte(response), Marshal.ReadByte(response, 1) };
if (recvBytes[1] != 0)
{
HookManager.Log("Proxy: incorrect username/password");
return -1;
}
}
// request bind with server
var bindRequest = SetUpBindWithRemoteHost(remoteIp, remotePort);
result = Send(s, bindRequest, 10);
if (result == -1)
return -1;
// response
response = Recieve(s, 10);
if (response == IntPtr.Zero)
return -1;
if (!VerifyBindResponse(response))
return -1;
// success
WSASetLastError(0);
SetLastError(0);
// clean memory
foreach (var ptr in allocatedMemory)
Marshal.FreeHGlobal(ptr);
allocatedMemory.Clear();
return 0;
}
else
{
var result = connect(s, addr, addrsize);
return result;
}
}
}
private int Connect(IntPtr socket, IntPtr addr, int addrsize)
{
var result = connect(socket, addr, addrsize);
while (result == -1)
{
var errorcode = WSAGetLastError();
HookManager.Log("Error: " + errorcode);
if (errorcode == 10056)
break;
if (errorcode == 10037)
break;
if (errorcode != 10035 && errorcode != 10037)
return -1;
//flag = 1;
result = connect(socket, addr, addrsize);
}
return result;
}
private int Send(IntPtr socket, IntPtr buf, int len)
{
var result = send(socket, buf, len, 0);
while (result == -1)
{
var errorcode = WSAGetLastError();
HookManager.Log("Error: " + errorcode);
if (errorcode == 10056)
break;
if (errorcode == 10037)
break;
if (errorcode != 10035 && errorcode != 10037)
return -1;
result = send(socket, buf, 4, 0);
}
return result;
}
private List<IntPtr> allocatedMemory = new List<IntPtr>();
private IntPtr Recieve(IntPtr socket, int len)
{
var buffer = Marshal.AllocHGlobal(len);
allocatedMemory.Add(buffer);
var result = recv(socket, buffer, len, 0);
if (result == -1)
{
HookManager.Log("Error2: " + WSAGetLastError());
return IntPtr.Zero;
}
return buffer;
}
private IntPtr RecieveAuth(IntPtr socket, int len)
{
var buffer = Marshal.AllocHGlobal(len);
allocatedMemory.Add(buffer);
var result = recv(socket, buffer, len, 0);
if (result == -1)
{
HookManager.Log("Error3: " + WSAGetLastError());
return IntPtr.Zero; ;
}
if (result == 0)
return buffer;
if (result != 2)
{
HookManager.Log("Proxy: Bad response from server");
return IntPtr.Zero;
}
return buffer;
}
private IntPtr RecieveBind(IntPtr socket, int len)
{
var buffer = Marshal.AllocHGlobal(len);
allocatedMemory.Add(buffer);
var result = recv(socket, buffer, len, 0);
if (result == -1)
{
HookManager.Log("Error3: " + WSAGetLastError());
return IntPtr.Zero; ;
}
if (result == 0)
return buffer;
if (result != 10)
{
HookManager.Log("Proxy: Bad response from server");
return IntPtr.Zero;
}
return buffer;
}
private void SetAddr(IntPtr socket, IntPtr addr, string ip, string port)
{
sockaddr_in structure = (sockaddr_in)Marshal.PtrToStructure(addr, typeof(sockaddr_in));
string originalip = new System.Net.IPAddress(structure.sin_addr.S_addr).ToString();
ushort originalport = ntohs(structure.sin_port);
structure.sin_addr.S_addr = inet_addr(ip);
structure.sin_port = htons(Convert.ToUInt16(port));
Marshal.StructureToPtr(structure, addr, true);
structure = (sockaddr_in)Marshal.PtrToStructure(addr, typeof(sockaddr_in));
}
private IntPtr SetUpSocks5Request()
{
var initialRequest = Marshal.AllocHGlobal(4);
Marshal.WriteByte(initialRequest, Convert.ToByte(5));
Marshal.WriteByte(initialRequest + 1, Convert.ToByte(2));
Marshal.WriteByte(initialRequest + 2, Convert.ToByte(0));
Marshal.WriteByte(initialRequest + 3, Convert.ToByte(2));
return initialRequest;
}
private IntPtr SetUpAuthenticateRequest(string username, string password, out int index)
{
index = 0;
var size = 3 + Encoding.Default.GetBytes(username).Length + Encoding.Default.GetBytes(password).Length;
var authenticateBuffer = Marshal.AllocHGlobal(size);
Marshal.WriteByte(authenticateBuffer + index++, Convert.ToByte(1));
Marshal.WriteByte(authenticateBuffer + index++, Convert.ToByte(username.Length));
byte[] rawBytes;
if (username.Length > 0)
{
rawBytes = Encoding.Default.GetBytes(username);
for (int i = 0; i < rawBytes.Length; i++)
{
Marshal.WriteByte(authenticateBuffer + index++, rawBytes[i]);
}
}
Marshal.WriteByte(authenticateBuffer + index++, Convert.ToByte(password.Length));
if (password.Length > 0)
{
rawBytes = Encoding.Default.GetBytes(password);
for (int i = 0; i < rawBytes.Length; i++)
{
Marshal.WriteByte(authenticateBuffer + index++, rawBytes[i]);
}
}
return authenticateBuffer;
}
private IntPtr SetUpBindWithRemoteHost(string eveIP, ushort evePort)
{
var bindWithEveBuffer = Marshal.AllocHGlobal(10);
var iplist = eveIP.Split('.').ToList();
byte[] portbyte = BitConverter.GetBytes(evePort).Reverse().ToArray();
byte[] newbyte = new byte[2];
int indexy = 0;
foreach (var byty in portbyte)
{
newbyte[indexy] = byty;
indexy++;
}
// bind with remote server
Marshal.WriteByte(bindWithEveBuffer, Convert.ToByte(5));
Marshal.WriteByte(bindWithEveBuffer + 1, Convert.ToByte(1));
Marshal.WriteByte(bindWithEveBuffer + 2, Convert.ToByte(0));
Marshal.WriteByte(bindWithEveBuffer + 3, Convert.ToByte(1));
Marshal.WriteByte(bindWithEveBuffer + 4, Convert.ToByte(iplist[0]));
Marshal.WriteByte(bindWithEveBuffer + 5, Convert.ToByte(iplist[1]));
Marshal.WriteByte(bindWithEveBuffer + 6, Convert.ToByte(iplist[2]));
Marshal.WriteByte(bindWithEveBuffer + 7, Convert.ToByte(iplist[3]));
Marshal.WriteByte(bindWithEveBuffer + 8, newbyte[0]);
Marshal.WriteByte(bindWithEveBuffer + 9, newbyte[1]);
return bindWithEveBuffer;
}
private bool VerifyBindResponse(IntPtr buffer)
{
var recvBytes = new byte[10] { Marshal.ReadByte(buffer), Marshal.ReadByte(buffer, 1), Marshal.ReadByte(buffer, 2), Marshal.ReadByte(buffer, 3), Marshal.ReadByte(buffer, 4), Marshal.ReadByte(buffer, 5), Marshal.ReadByte(buffer, 6), Marshal.ReadByte(buffer, 7), Marshal.ReadByte(buffer, 8), Marshal.ReadByte(buffer, 9) };
if (recvBytes[1] != 0)
{
if (recvBytes[1] == 1)
HookManager.Log("General failure");
if (recvBytes[1] == 2)
HookManager.Log("connection not allowed by ruleset");
if (recvBytes[1] == 3)
HookManager.Log("network unreachable");
if (recvBytes[1] == 4)
HookManager.Log("host unreachable");
if (recvBytes[1] == 5)
HookManager.Log("connection refused by destination host");
if (recvBytes[1] == 6)
HookManager.Log("TTL expired");
if (recvBytes[1] == 7)
HookManager.Log("command not supported / protocol error");
if (recvBytes[1] == 8)
HookManager.Log("address type not supported");
HookManager.Log("Proxy: Connection error binding eve server");
return false;
}
return true;
}
public void Dispose()
{
if (_hook == null)
return;
_hook.Dispose();
_hook = null;
}
}
}

System.AccessViolationException:Attempted to read or write protected memory

I have a problem about production of a C# wrapper starting from C++ unmanaged library. Everytime I fall into a System.AccessViolationException:Attempted to read or write protected memory. I know probably it depends on the way I translate structures and their elements from C++ to C#, but Idon't know how to solve.
Could someone help me?
C++ code:
typedef struct VBLEnvironmentVariable_t
{
VBLObjectHeader mHeader;
DWORD mNameLength;
DWORD mDataLength;
LPSTR mName;
LPBYTE mData;
} VBLEnvironmentVariable;
typedef struct VBLAppTrigger_t
{
VBLObjectHeader mHeader;
ULONGLONG mPreTriggerTime;
ULONGLONG mPostTriggerTime;
WORD mChannel;
WORD mFlags;
DWORD mAppSecific2;
} VBLAppTrigger;
//signature
BLAPI( BOOL) BLWriteObject( HANDLE hFile, VBLObjectHeaderBase* pBase);
//how function is called
int write_test( LPCTSTR pFileName, LPDWORD pWritten)
{
if ( NULL == pWritten)
{
return -1;
}
*pWritten = 0;
/* open file */
hFile = BLCreateFile( pFileName, GENERIC_WRITE);
if ( INVALID_HANDLE_VALUE == hFile)
{
return -1;
}
/* set applicaton information */
/* bSuccess = BLSetApplication( hFile, BL_APPID_UNKNOWN, 1, 0, 0); */
bSuccess = BLSetApplication( hFile, BL_APPID_CANCASEXLLOG, 1, 0, 1);
GetSystemTime( &systemTime);
bSuccess = bSuccess && BLSetMeasurementStartTime( hFile, &systemTime);
/* set write options */
bSuccess = bSuccess && BLSetWriteOptions( hFile, 6, 0);
if ( bSuccess)
{
// setup object headers
appTrigger.mHeader.mBase.mSignature = BL_OBJ_SIGNATURE;
appTrigger.mHeader.mBase.mHeaderSize = sizeof( appTrigger.mHeader);
appTrigger.mHeader.mBase.mHeaderVersion = 1;
appTrigger.mHeader.mBase.mObjectSize = sizeof( VBLAppTrigger);
appTrigger.mHeader.mBase.mObjectType = BL_OBJ_TYPE_APP_TRIGGER;
appTrigger.mHeader.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
message.mHeader.mBase.mSignature = BL_OBJ_SIGNATURE;
message.mHeader.mBase.mHeaderSize = sizeof( message.mHeader);
message.mHeader.mBase.mHeaderVersion = 1;
message.mHeader.mBase.mObjectSize = sizeof( VBLCANMessage);
message.mHeader.mBase.mObjectType = BL_OBJ_TYPE_CAN_MESSAGE;
message.mHeader.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
variable_s.mHeader.mBase.mSignature = BL_OBJ_SIGNATURE;
variable_s.mHeader.mBase.mHeaderSize = sizeof( variable_s.mHeader);
variable_s.mHeader.mBase.mHeaderVersion = 1;
variable_s.mHeader.mBase.mObjectType = BL_OBJ_TYPE_ENV_STRING;
variable_s.mHeader.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
variable_i.mHeader.mBase.mSignature = BL_OBJ_SIGNATURE;
variable_i.mHeader.mBase.mHeaderSize = sizeof( variable_i.mHeader);
variable_i.mHeader.mBase.mHeaderVersion = 1;
variable_i.mHeader.mBase.mObjectType = BL_OBJ_TYPE_ENV_INTEGER;
variable_i.mHeader.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
ethframe.mHeader.mBase.mSignature = BL_OBJ_SIGNATURE;
ethframe.mHeader.mBase.mHeaderSize = sizeof( ethframe.mHeader);
ethframe.mHeader.mBase.mHeaderVersion = 1;
ethframe.mHeader.mBase.mObjectType = BL_OBJ_TYPE_ETHERNET_FRAME;
ethframe.mHeader.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
appText.mHeader.mBase.mSignature = BL_OBJ_SIGNATURE;
appText.mHeader.mBase.mHeaderSize = sizeof( appText.mHeader);
appText.mHeader.mBase.mHeaderVersion = 1;
appText.mHeader.mBase.mObjectType = BL_OBJ_TYPE_APP_TEXT;
appText.mHeader.mObjectFlags = BL_OBJ_FLAG_TIME_ONE_NANS;
for ( i = 0; i < 1000; ++i)
{
ethbuffer[i] = ( BYTE)i;
}
for ( i = 0; i < 1000 && bSuccess; ++i)
{
// increment in milliseconds
time = i * 10000000;
// setup app trigger object header
appTrigger.mHeader.mObjectTimeStamp = time;
// write app trigger object
bSuccess = BLWriteObject( hFile, &appTrigger.mHeader.mBase);
*pWritten += bSuccess ? 1 : 0;
// setup CAN object header
message.mHeader.mObjectTimeStamp = time;
// setup CAN message
message.mChannel = 1;
message.mFlags = CAN_MSG_FLAGS( 0, 0);
message.mDLC = 8;
message.mID = 0x100;
memcpy( message.mData, ( i % 2) ? _T( "01234567") : _T( "76543210"), message.mDLC);
// write CAN message
bSuccess = BLWriteObject( hFile, &message.mHeader.mBase);
*pWritten += bSuccess ? 1 : 0;
if ( 0 == ( i % 3) && bSuccess)
{
// setup environment variable object headers
variable_s.mHeader.mObjectTimeStamp = time;
variable_i.mHeader.mObjectTimeStamp = time;
// setup environment variables
variable_s.mNameLength = strlen( ENV_NAME1);
variable_s.mDataLength = strlen( ( i % 2) ? ENV_DATA1 : ENV_DATA2);
variable_s.mName = ENV_NAME1;
variable_s.mData = ( i % 2) ? ENV_DATA1 : ENV_DATA2;
variable_s.mHeader.mBase.mObjectSize = sizeof( VBLEnvironmentVariable) + variable_s.mNameLength + variable_s.mDataLength;
variable_i.mNameLength = strlen( ENV_NAME2);
variable_i.mDataLength = sizeof( int);
variable_i.mName = ENV_NAME2;
variable_i.mData = ( LPBYTE)&i;
variable_i.mHeader.mBase.mObjectSize = sizeof( VBLEnvironmentVariable) + variable_i.mNameLength + variable_i.mDataLength;
// write environment variables
bSuccess = BLWriteObject( hFile, &variable_s.mHeader.mBase);
*pWritten += bSuccess ? 1 : 0;
bSuccess = bSuccess && BLWriteObject( hFile, &variable_i.mHeader.mBase);
*pWritten += bSuccess ? 1 : 0;
// write ethernet frame
memcpy( ethframe.mSourceAddress, src, sizeof( ethframe.mSourceAddress));
ethframe.mReserved1 = 0;
memcpy( ethframe.mDestinationAddress, dst, sizeof( ethframe.mDestinationAddress));
ethframe.mReserved2 = 0;
ethframe.mType = 0x0800;
ethframe.mTPID = 0;
ethframe.mTCI = 0;
ethframe.mPayLoadLength = ( WORD)i;
ethframe.mPayLoad = ethbuffer;
ethframe.mHeader.mBase.mObjectSize = sizeof( VBLEthernetFrame) + ethframe.mPayLoadLength;
bSuccess = bSuccess && BLWriteObject( hFile, &ethframe.mHeader.mBase);
*pWritten += bSuccess ? 1 : 0;
// write text
if ( ( i % 100) == 0)
{
char text[128];
sprintf( text, "%d objects written...", *pWritten);
appText.mText = text;
appText.mTextLength = strlen( appText.mText);
appText.mHeader.mBase.mObjectSize = sizeof( VBLAppText) + appText.mTextLength;
bSuccess = bSuccess && BLWriteObject( hFile, &appText.mHeader.mBase);
*pWritten += bSuccess ? 1 : 0;
}
}
}
appText.mText = "All objects written...";
appText.mTextLength = strlen( appText.mText);
appText.mHeader.mBase.mObjectSize = sizeof( VBLAppText) + appText.mTextLength;
bSuccess = bSuccess && BLWriteObject( hFile, &appText.mHeader.mBase);
*pWritten += bSuccess ? 1 : 0;
}
/* close file */
if ( !BLCloseHandle( hFile))
{
return -1;
}
return bSuccess ? 0 : -1;
}`
C# code:
//translation of C++ struct into C# class
public class VBLEnvVar
{
public VBLEnvVarStruct variable_s;
public VBLEnvVar()
{
variable_s = new BLF_Function.VBLEnvVar.VBLEnvVarStruct();
}
public struct VBLEnvVarStruct
{
public VBLObjectHeader.VBLObjectHeaderStruct mHeader;
public uint NameLength;
public uint DataLength;
public string Name;
[MarshalAsAttribute(UnmanagedType.LPArray)]
public byte[] Data;
}
}
public class VBLAppTrigger
{
public VBLAppTriggerStruct apptrigger;
public VBLAppTrigger()
{
apptrigger = new BLF_Function.VBLAppTrigger.VBLAppTriggerStruct(null);
}
public struct VBLAppTriggerStruct
{
public VBLObjectHeader.VBLObjectHeaderStruct mHeader;
public UInt64 mPreTriggerTime;
public UInt64 mPostTriggerTime;
public ushort mFlags;
public ushort mChannel;
public uint mAppSpecific2;
}
}
[DllImport("binlog.dll")]
public static extern bool BLWriteObject( int Handle,ref BLF_Function.ObjectHeader.ObjHeader pBase);
//how function is called into C# code
public static void Main(string[] args)
{
int written=0;
BLF_Function b = new BLF_Function();
UInt64 time=0;
byte[] ethbuffer = new byte[1500];
bool success=false;
string filename = "provamia.blf";
int Handle = MyBLF.BLCreateFile(filename,b.GENERIC_WRITE);
if (Handle != -1)
{
success = MyBLF.BLSetApplication( Handle, (byte)BLF_Function.FileStatistics.APPID.BL_APPID_UNKNOWN, 1, 0, 1);
//***********
MyBLF.SYSTEMTIME d = new MyBLF.SYSTEMTIME();
MyBLF.GetLocalTime(out d);
MyBLF.SYSTEMTIME* s = &d;
success = MyBLF.BLSetMeasurementStartTime( Handle,ref s);
//*************
if (success)
{
success = MyBLF.BLSetWriteOptions( Handle, 6,0);
if (success)
{
BLF_Function.VBLObjectHeader vblobjectheaderclass = new BLF_Function.VBLObjectHeader();
BLF_Function.ObjectHeader objectheaderclass = new BLF_Function.ObjectHeader();
vblobjectheaderclass.mHeader.baseheader=objectheaderclass.baseheader;
BLF_Function.VBLAppTrigger apptriggerclass = new BLF_Function.VBLAppTrigger();
apptriggerclass.apptrigger.mFlags=(ushort)vblobjectheaderclass.BL_OBJ_FLAG_TIME_ONE_NANS;
apptriggerclass.apptrigger.mHeader=vblobjectheaderclass.mHeader;
apptriggerclass.apptrigger.mHeader.baseheader.HeaderSize = apptriggerclass.apptrigger.mHeader.GetSize();
apptriggerclass.apptrigger.mHeader.baseheader.HeaderVersion = 1;
apptriggerclass.apptrigger.mHeader.baseheader.signature = objectheaderclass.BL_OBJ_SIGNATURE;
apptriggerclass.apptrigger.mHeader.baseheader.ObjectSize = (ushort)apptriggerclass.GetSize();
apptriggerclass.apptrigger.mHeader.baseheader.ObjectType = (ushort)BLF_Function.ObjectHeader.OBJ_TYPR.BL_OBJ_TYPE_APP_TRIGGER;
apptriggerclass.apptrigger.mHeader.mObjectFlags = (uint)vblobjectheaderclass.BL_OBJ_FLAG_TIME_ONE_NANS;
BLF_Function.VBLCANMessage messageclass = new BLF_Function.VBLCANMessage();
messageclass.message.mHeader=vblobjectheaderclass.mHeader;
messageclass.message.mHeader.baseheader.HeaderSize = messageclass.message.mHeader.GetSize();
messageclass.message.mHeader.baseheader.HeaderVersion = 1;
messageclass.message.mHeader.baseheader.signature = objectheaderclass.BL_OBJ_SIGNATURE;
messageclass.message.mHeader.baseheader.ObjectSize = (ushort)messageclass.GetSize();
messageclass.message.mHeader.baseheader.ObjectType = (ushort)BLF_Function.ObjectHeader.OBJ_TYPR.BL_OBJ_TYPE_CAN_MESSAGE;
messageclass.message.mHeader.mObjectFlags = (uint)vblobjectheaderclass.BL_OBJ_FLAG_TIME_ONE_NANS;
BLF_Function.VBLEnvVar variable_sclass = new BLF_Function.VBLEnvVar();
variable_sclass.variable_s.mHeader=vblobjectheaderclass.mHeader;
variable_sclass.variable_s.mHeader.baseheader.HeaderSize = variable_sclass.variable_s.mHeader.GetSize();
variable_sclass.variable_s.mHeader.baseheader.HeaderVersion = 1;
variable_sclass.variable_s.mHeader.baseheader.signature = objectheaderclass.BL_OBJ_SIGNATURE;
variable_sclass.variable_s.mHeader.baseheader.ObjectType = (ushort)BLF_Function.ObjectHeader.OBJ_TYPR.BL_OBJ_TYPE_ENV_STRING;
variable_sclass.variable_s.mHeader.mObjectFlags = (uint)vblobjectheaderclass.BL_OBJ_FLAG_TIME_ONE_NANS;
BLF_Function.VBLEnvVar variable_iclass = new BLF_Function.VBLEnvVar();
variable_iclass.variable_s.mHeader=vblobjectheaderclass.mHeader;
variable_iclass.variable_s.mHeader.baseheader.HeaderSize = variable_iclass.variable_s.mHeader.GetSize();
variable_iclass.variable_s.mHeader.baseheader.HeaderVersion = 1;
variable_iclass.variable_s.mHeader.baseheader.signature = objectheaderclass.BL_OBJ_SIGNATURE;
variable_iclass.variable_s.mHeader.baseheader.ObjectType = (ushort)BLF_Function.ObjectHeader.OBJ_TYPR.BL_OBJ_TYPE_ENV_INTEGER;
variable_iclass.variable_s.mHeader.mObjectFlags = (uint)vblobjectheaderclass.BL_OBJ_FLAG_TIME_ONE_NANS;
BLF_Function.VBLEthernetFrame ethframeclass = new BLF_Function.VBLEthernetFrame();
ethframeclass.ethframe.mHeader=vblobjectheaderclass.mHeader;
ethframeclass.ethframe.mHeader.baseheader.HeaderSize = ethframeclass.ethframe.mHeader.GetSize();
ethframeclass.ethframe.mHeader.baseheader.HeaderVersion = 1;
ethframeclass.ethframe.mHeader.baseheader.signature = objectheaderclass.BL_OBJ_SIGNATURE;
ethframeclass.ethframe.mHeader.baseheader.ObjectType = (ushort)BLF_Function.ObjectHeader.OBJ_TYPR.BL_OBJ_TYPE_ETHERNET_FRAME;
ethframeclass.ethframe.mHeader.mObjectFlags = (uint)vblobjectheaderclass.BL_OBJ_FLAG_TIME_ONE_NANS;
BLF_Function.VBLAppText appTextclass = new BLF_Function.VBLAppText();
appTextclass.appText.mHeader=vblobjectheaderclass.mHeader;
appTextclass.appText.mHeader.baseheader.HeaderSize = appTextclass.appText.mHeader.GetSize();
appTextclass.appText.mHeader.baseheader.HeaderVersion = 1;
appTextclass.appText.mHeader.baseheader.signature = objectheaderclass.BL_OBJ_SIGNATURE;
appTextclass.appText.mHeader.baseheader.ObjectSize = (ushort)appTextclass.GetSize();
appTextclass.appText.mHeader.baseheader.ObjectType = (ushort)BLF_Function.ObjectHeader.OBJ_TYPR.BL_OBJ_TYPE_APP_TEXT;
appTextclass.appText.mHeader.mObjectFlags = (uint)vblobjectheaderclass.BL_OBJ_FLAG_TIME_ONE_NANS;
for (int h=0;h<ethbuffer.Length;h++)
ethbuffer[h]=Convert.ToByte(h & 0xFF);
for (int i = 0; i < 1000 ; ++i)
{
/* increment in milliseconds */
time = (uint)(i * 100000);
/* setup app trigger object header */
apptriggerclass.apptrigger.mHeader.mObjectTimestamp = time;
/* write app trigger object */
success = Scrivi(Handle, apptriggerclass.apptrigger.mHeader.baseheader);////NO ERROR
written += success ? 1 : 0;
if (success)
{
string envdata1="01234567";
string envdata2="76543210";
/* setup CAN object header */
messageclass.message.mHeader.mObjectTimestamp = time;
/* setup CAN message */
messageclass.message.mChannel=1;
messageclass.message.mFlags=(byte)vblobjectheaderclass.BL_OBJ_FLAG_TIME_ONE_NANS;
messageclass.message.mDLC = 8;
messageclass.message.mID = 0x100;
char[] supp = envdata1.ToCharArray();
char[] supp2 = envdata2.ToCharArray();
messageclass.message.Data = new byte[messageclass.message.mDLC];
if ((i%2)==0)
{
for (int g=0;g<supp.Length;g++)
messageclass.message.Data[g] = (byte)supp[g];
}
else
{
for (int g=0;g<supp2.Length;g++)
messageclass.message.Data[g] = (byte)supp2[g];
}
/* write CAN message */
success = Scrivi(Handle, messageclass.message.mHeader.baseheader);////NO ERROR
written += success ? 1 : 0;
if (success)
{
if ((i%3)==0)
{
/* setup environment variable object headers */
variable_sclass.variable_s.mHeader.mObjectTimestamp= time;
/* setup environment variables */
string envname1="EnvString";
string envname2="EnvInt";
char[] suppstring1 = envname1.ToCharArray();
char[] suppstring2 = envname2.ToCharArray();
variable_sclass.variable_s.NameLength = (uint)envname1.Length;
variable_sclass.variable_s.DataLength = (uint)(((i%2)==0)?envdata1.Length:envdata2.Length);
variable_sclass.variable_s.Name = envname1;
variable_sclass.variable_s.Data = new byte[variable_sclass.variable_s.DataLength];
if ((i%2)==0)
{
for (int g=0;g<supp.Length;g++)
variable_sclass.variable_s.Data[g] = Convert.ToByte(supp[g]);
}
else
{
for (int g=0;g<supp2.Length;g++)
variable_sclass.variable_s.Data[g] = Convert.ToByte(supp2[g]);
}
variable_sclass.variable_s.mHeader.baseheader.ObjectSize = 65;
success = Scrivi(Handle, variable_sclass.variable_s.mHeader.baseheader);////ERROR
...........
}
public static bool Scrivi(int a, BLF_Function.ObjectHeader.ObjHeader b)
{
return MyBLF.BLWriteObject( a, ref b);
}
CB
Firstly, the type HANDLE should translate to IntPtr, not int.
Also the sizes of the structs may differ to the original due to structure padding. Use the [Structlayout()]-Attribute in your C#-code to control this.
Change "ref" to "out" everywhere in C# and see that there is no memory allocation issues in c++ code.

C# PInvoke VerQueryValue returns back OutOfMemoryException?

Below is the code sample which I got from online resource but it's suppose to work with fullframework, but when I try to build it using C# smart device, it throws exception saying it's out of memory. Does anybody know how can I fix it to use on compact? the out of memory exception when I make the second call to VerQueryValue which is the last one.
thanks,
[DllImport("coredll.dll")]
public static extern bool VerQueryValue(byte[] buffer, string subblock, out IntPtr blockbuffer, out uint len);
[DllImport("coredll.dll")]
public static extern bool VerQueryValue(byte[] pBlock, string pSubBlock, out string pValue, out uint len);
//
private static void GetAssemblyVersion()
{
string filename = #"\Windows\MyLibrary.dll";
if (File.Exists(filename))
{
try {
int handle = 0;
Int32 size = 0;
size = GetFileVersionInfoSize(filename, out handle);
if (size > 0)
{
bool retValue;
byte[] buffer = new byte[size];
retValue = GetFileVersionInfo(filename, handle, size, buffer);
if (retValue == true)
{
bool success = false;
IntPtr blockbuffer = IntPtr.Zero;
uint len = 0;
//success = VerQueryValue(buffer, "\\", out blockbuffer, out len);
success = VerQueryValue(buffer, #"\VarFileInfo\Translation", out blockbuffer, out len);
if(success)
{
int p = (int)blockbuffer;
//Reads a 16-bit signed integer from unmanaged memory
int j = Marshal.ReadInt16((IntPtr)p);
p += 2;
//Reads a 16-bit signed integer from unmanaged memory
int k = Marshal.ReadInt16((IntPtr)p);
string sb = string.Format("{0:X4}{1:X4}", j, k);
string spv = #"\StringFileInfo\" + sb + #"\ProductVersion";
string versionInfo;
VerQueryValue(buffer, spv, out versionInfo, out len);
}
}
}
}
catch (Exception err)
{
string error = err.Message;
}
}
}
After adding these two statements:
Int32 dwVerMinor = j & 0xffff;
Int32 dwVerBuild = k & 0xffff;
it's able to retrieve the DLL version.
Here's an implementation:
using DWORD = System.UInt32;
public static class NativeFile
{
public struct NativeFileInfo
{
public Version Version;
public NameValueCollection StringTable;
}
public unsafe static NativeFileInfo GetFileInfo(string path)
{
if (!File.Exists(path))
{
throw new FileNotFoundException();
}
IntPtr handle;
var size = GetFileVersionInfoSize(path, out handle);
var buffer = Marshal.AllocHGlobal(size);
try
{
if (!GetFileVersionInfo(path, handle, size, buffer))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
IntPtr pVersion;
int versionLength;
VerQueryValue(buffer, “\”, out pVersion, out versionLength);
var versionInfo = (VS_FIXEDFILEINFO)Marshal.PtrToStructure(pVersion, typeof(VS_FIXEDFILEINFO));
var version = new Version((int)versionInfo.dwFileVersionMS >> 16,
(int)versionInfo.dwFileVersionMS & 0xFFFF,
(int)versionInfo.dwFileVersionLS >> 16,
(int)versionInfo.dwFileVersionLS & 0xFFFF);
// move to the string table and parse
var pStringTable = ((byte*)pVersion.ToPointer()) + versionLength;
var strings = ParseStringTable(pStringTable, size – versionLength);
return new NativeFileInfo
{
Version = version,
StringTable = strings
};
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
private unsafe static NameValueCollection ParseStringTable(byte* pStringTable, int length)
{
NameValueCollection nvc = new NameValueCollection();
byte* p = pStringTable;
short stringFileInfoLength = (short)*p;
byte* end = pStringTable + length;
p += (2 + 2 + 2); // length + valuelength + type
// verify key
var key = Marshal.PtrToStringUni(new IntPtr(p), 14);
if (key != "StringFileInfo") throw new ArgumentException();
// move past the key to the first string table
p += 30;
short stringTableLength = (short)*p;
p += (2 + 2 + 2); // length + valuelength + type
// get locale info
key = Marshal.PtrToStringUni(new IntPtr(p), 8);
// move to the first string
p += 18;
while (p < end)
{
short stringLength = (short)*p;
p += 2;
short valueChars = (short)*p;
p += 2;
short type = (short)*p;
p += 2;
if (stringLength == 0) break;
if ((valueChars == 0) || (type != 1))
{
p += stringLength;
continue;
}
var keyLength = stringLength – (valueChars * 2) – 6;
key = Marshal.PtrToStringUni(new IntPtr(p), keyLength / 2).TrimEnd(”);
p += keyLength;
var value = Marshal.PtrToStringUni(new IntPtr(p), valueChars).TrimEnd(”);
p += valueChars * 2;
if ((int)p % 4 != 0) p += 2;
nvc.Add(key, value);
}
return nvc;
}
private const string COREDLL = "coredll.dll";
[DllImport(COREDLL, SetLastError = true)]
private static extern int GetFileVersionInfoSize(string lptstrFilename, out IntPtr lpdwHandle);
[DllImport(COREDLL, SetLastError = true)]
private static extern bool GetFileVersionInfo(string lptstrFilename, IntPtr dwHandle, int dwLen, IntPtr lpData);
[DllImport(COREDLL, SetLastError = true)]
private static extern bool VerQueryValue(IntPtr pBlock, string lpSubBlock, out IntPtr lplpBuffer, out int puLen);
[StructLayout(LayoutKind.Sequential)]
private struct VS_FIXEDFILEINFO
{
public DWORD dwSignature;
public DWORD dwStrucVersion;
public DWORD dwFileVersionMS;
public DWORD dwFileVersionLS;
public DWORD dwProductVersionMS;
public DWORD dwProductVersionLS;
public DWORD dwFileFlagsMask;
public DWORD dwFileFlags;
public FileOS dwFileOS;
public FileType dwFileType;
public DWORD dwFileSubtype;
public DWORD dwFileDateMS;
public DWORD dwFileDateLS;
};
public enum FileOS : uint
{
Unknown = 0x00000000,
DOS = 0x00010000,
OS2_16 = 0x00020000,
OS2_32 = 0x00030000,
NT = 0x00040000,
WindowsCE = 0x00050000,
}
public enum FileType : uint
{
Unknown = 0x00,
Application = 0x01,
DLL = 0x02,
Driver = 0x03,
Font = 0x04,
VXD = 0x05,
StaticLib = 0x07
}
}
And an example of usage:
class Program
{
static void Main(string[] args)
{
string target = “\FlashFX Disk\ARMv4i\conmanclient2.exe”;
var version = NativeFile.GetFileInfo(target);
Console.WriteLine(string.Format(“File: { 0}”, Path.GetFileName(target)));
Console.WriteLine(string.Format(“Version: { 0}”, version.Version.ToString(4)));
foreach (var key in version.StringTable.AllKeys)
{
Console.WriteLine(string.Format(“{ 0}: { 1}”, key, version.StringTable[key]));
}
Console.ReadLine();
}

Categories