Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working with pointer offsets in C#

I've been working on an application that use API to get events from the Windows event log. I'm stuck on pointer offsets at the moment. The specific struct I'm using is EVENTLOGRECORD (see: http://msdn.microsoft.com/en-us/library/aa363646(v=vs.85).aspx). My C# struct is defined as:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
internal struct EVENTLOGRECORD
{
    internal UInt32 Length;
    internal UInt32 Reserved;
    internal UInt32 RecordNumber;
    internal UInt32 TimeGenerated;
    internal UInt32 TimeWritten;
    internal UInt32 EventID;
    internal UInt16 EventType;
    internal UInt16 NumStrings;
    internal UInt16 EventCategory;
    internal UInt16 ReservedFlags;
    internal UInt32 ClosingRecordNumber;
    internal UInt32 StringOffset;
    internal UInt32 UserSidLength;
    internal UInt32 UserSidOffset;
    internal UInt32 DataLength;
    internal UInt32 DataOffset;
}

My ReadEventLog function is declared as:

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "ReadEventLog")]
internal static extern Boolean ReadEventLog(IntPtr hEventLog, EVT_READ_FLAGS dwReadFlags, UInt32 dwRecordOffset, IntPtr lpBuffer, UInt32 nNumberOfBytesToRead, out UInt32 pnBytesRead, out UInt32 pnMinNumberOfBytesNeeded);

I'm able to get the struct filled with data and I can get to SourceName and ComputerName sections by using IntPtr.Add. Example:

IntPtr pSrc = IntPtr.Add(pRecord, Marshal.SizeOf(typeof(EVENTLOGRECORD)));
string sSrc = Marshal.PtrToStringAuto(pSrc);
Console.WriteLine("source: {0}\n", sSrc);

IntPtr pComp = IntPtr.Add(pSrc, (sSrc.Length * 2) + 2);
string sComp = Marshal.PtrToStringAuto(pComp);
Console.WriteLine("computer: {0}\n", sComp);

My issue is trying to get the Strings portion from the struct. I can't seem to figure out what the correct offsets will be. I can do it in C++, but I can't seem to make it work in C#. Here's a snippet of what I use in C++ (elr is (EVENTLOGRECORD*)pRecord):

char* strings = (LPSTR)((LPBYTE) elr + elr->StringOffset);
while (elr->NumStrings)
{
    wprintf(L"String: %s\n", strings);
    strings += (wcslen((wchar_t*)strings) * sizeof(wchar_t)) + sizeof(wchar_t);
    elr->NumStrings--;
}

Hopefully someone can help explain what I'm missing. I'd also be curious if there are any alternatives to IntPtr.Add since that require .NET 4.0. I'm not an expert with p/invoke by any means. Thanks.

like image 377
Deviation Avatar asked Feb 12 '26 19:02

Deviation


2 Answers

It you use Marshal.PtrToStructure() to copy the first part of your block of data to a EVENTLOGRECORD, then you should be able to just do something like:

EVENTLOGRECORD record;
... Copy the ptr into record ...
IntPtr pStrings = IntPtr.Add(pRecord, (record.StringOffset * 2));

I'd be happy to get this going for you, but I'm too lazy to do all the other p/invoke bit which get as far as being able to make the ReadEventLog call.

like image 198
Will Dean Avatar answered Feb 21 '26 11:02

Will Dean


After some time away from coding to clear the brain, I finally have a solution. I was incorrectly making the offset too large since I was taking the size of the struct (EVENTLOGRECORD), adding it to StringOffset, then adding that to the existing object. All I really needed to do was add the offset to the existing IntPtr (pRecord). Not sure why that didn't click before.

So simply doing:

int offset = ((int)(((EVENTLOGRECORD)o).StringOffset));
IntPtr pStrings = IntPtr.Add(pRecord, offset);
string sString = Marshal.PtrToStringAuto(pStrings);
Console.WriteLine("string : {0}\n", sString);

..is enough to get the string. Thanks for the assists though. I apparently lack rep to mark suggestions as helpful. Guess I just needed time away from this.

like image 42
Deviation Avatar answered Feb 21 '26 13:02

Deviation



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!