How can i get an object from pointer-address - c#

i have written a user control for WinCC (Siemens) (SCADA). Now I want to pass a pointer to the control. The only way to do this is: write the pointer to a property.
WinCC has only this methods to set properties
SetPropBOOL
SetPropChar
SetPropDouble
SetPropWord
The property from control has UInt as datatype and i use the SetPropDouble method to set the address from an object.
WinCC Global Script (ANSI-C)
//autoDB is an ADODB.Connection object
//object* autoDB = __object_create("ADODB.Connection");
extern __object* autoDB;
//SetPropDouble( "PictureName", "ControlName", "PropertyName", (DWORD)(&autoDB) );
SetPropDouble( "PictureName", "ControlName", "PropertyName", (DWORD)autoDB );
I've debug my control (hook on WinCC-process) and i see the property-set becomes assigned an address-value e.g. 0x03041080.
Now the question: How can i get the object in c# (.Net) on the address?
My try throws an exception: ExecutionEngineException
private ADODB.Connection _database;
private IntPtr _ptr = IntPtr.Zero;
public uint DataBase{
get{
return (uint)_ptr;
}
set{
if( value != 0 ){
_ptr = (IntPtr)value;
GCHandle gH = GCHandle.FromIntPtr(_ptr); // THIS LINE THROW THE EXCEPTION
_database = gH.Target;
}
}
}
Ok: i've changed my code to use STRING
WinCC
extern __object* autoDB;
DWORD addr = (DWORD)autoDB;
char sAddr[11];
sprintf( sAddr, "%d\0", addr );
SetPropChar( "PictureName", "ControlName", "DataBaseAddr", sAddr );
And c# is now
private string _lpszDataBaseAddr = "";
public string DataBaseAddr{
get{
return _lpszDataBaseAddr;
}
set{
uint addr;
bool ret = uint.TryParse( value, out addr );
if( ! ret ){
return;
}
IntPtr ptr = (IntPtr)addr;
GCHandle gH = GCHandle.FromIntPtr( ptr ); // THE SAME ERROR!
}
}
Other findings!
The address from the ADO-Object is not in the process-memory who called my control (debug with ollydbg). WinCC has two programs: PDLRT.exe for visualisation (this is calling my control) and SCRIPT.exe for running GLOBAL-SCRIPT (Ansi-C).
From PDLRT, i've access to the pointer-address from ADO-Object. By call GCHandle in C# of the ADO-object-address, the exception is thrown. (ExecutionEngineException)

I have no idea if C# can access through a pointer into C++ like that.
Regardless, this: (DWORD)(&autoDB) is wrong, that puts the address of the pointer as the property's value, which is pointless.
You need the pointer's value, i.e. (DWORD) autoDB.
Also, SetPropDouble() accepts a value of type double, i.e. a floating-point number. That will not be a very nice way to share a pointer, which is a (large) integer. Try some different representation, string might work if you don't have access to a large enough integer.

Ok,
long time ago and I've ask the support from Siemens.
Siemens: The loaded Dlls, Controls and so on are load in an separated memory and not in the application (main) memory. Memory-Address-Share between Dlls, Controls .... is not working. All have a separated memory.
Super. Only way: Pipes or other communication implementations (TCP/IP, ...).

Related

Marshaling a pointer to a pointer

Problem:
I have a test application that is looking for a scanner attached to a PC. The European manufacturer supplies an SDK written in C++ and provides some header files and other information. Their C++ API populates a structure as shown below. My concern is de-referencing (Marshaling) the returned char** inside of that structure into a set of strings.
//
//The struct, as per the manufacturer's C++ header file:
//
typedef struct {
int count;
char** serialNumbers;
} ScannerList;
//
// My interpretation of the struct
//
struct ScannerList
{
public int count;
public IntPtr serialNumbers;
}
//
// C++ API Method Signature:
//
bool SCN_GetScannerList( ScannerList** scannerList );
//
// My DLLImport:
//
[DllImport(#"C:\ScannerSDK\Scanner.dll", CharSet = CharSet.Unicode)]
public static extern bool SCN_GetScannerList(out IntPtr scannerList);
Inside my C# wrapper class, I have several classes that wrap each specific set of Scanner API calls. For this method, I'm using the ScannerNative class that the DllImport resides in, along with the struct. This is the method charged with summoning the list:
private object GetScanlistFromDLL()
{
// our managed code return object - class instead of struct
ScannerNative.ScannerListSafe safeList = new ScannerNative.ScannerListSafe();
// IntPtr to give to the API
IntPtr ptr = new IntPtr();
// unmanaged code struct
ScannerNative.ScannerList scanList = new ScannerNative.ScannerList();
// Call the API with our IntPtr
bool success = ScannerNative.GetScannerList(out ptr);
if(success)
{
// Map the pointer to the type of structure from the API call
scanList = (ScannerNative.ScannerList)Marshal.PtrToStructure(ptr, typeof(ScannerNative.ScannerList));
// copy the unsafe struct members to our safe class
safeList.count = scanList.count;
//Problem - the marshaling returns a string full of garbage
//likely because char** is an array of char* ...
//Not sure how to Marshal a pointer to a pointer to a string :-\
safeList.serialNumbers = Marshal.PtrToStringUni(scanList.serialNumbers);
}
else
{
return null;
}
//
// API call to release unmanaged scanner list ..
//
return safeList;
}
Results:
I do get what appears to be a valid structure back from the API call
bool success = ScannerNative.GetScannerList(out ptr);
The subsequent casting in the if(success) block:
if(success)
{
// Map the pointer to the type of structure from the API call
scanList = (ScannerNative.ScannerList)Marshal.PtrToStructure(ptr, typeof(ScannerNative.ScannerList));
This works all the way until we get to our IntPtr stored in the scanList.serialNumbers member, which is holding a reference to the char** from the API struct.
scanList.count shows a correct value of '1' (when the scanner is attached) but I'm not able to de-ref the char** held in the scanList.serialNumbers member.
I've tried attacking this in a couple ways, but nothing is coming out and either I'm using the wrong search engines, or trying to find "c# DllImport Char** Marshal" and variants is asking the wrong question.

Access Violation while exporting an unmanaged function pointer

I have been trying for the past 4 hours to solve a very mysterious problem.
I am writing some plugin for Notepad++. To achieve syntax highlighting one has to export such a function:
//this function is exported via exports.def file
LexerFactoryFunction SCI_METHOD GetLexerFactory(unsigned int index)
{
return (index == 0) ? RTextLexer::LexerFactory : nullptr;
}
where,
LexerFactoryFunction is typedef ILexer *(*LexerFactoryFunction)();
#define SCI_METHOD __stdcall
I have managed to get this thing working perfectly with C++, however another part of the plugin is written in C#, so I tried to merge the two using Fody Costura NuGet package ( so that the CLI .dll is embedded into the main .dll ), however with no success.
What I've tried :
public ref class RTextLexerCliWrapper
{
public:
delegate ILexer * GetLexerFactoryDelegate();
IntPtr GetLexerFactory()
{
return System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(_lexerFactoryPtr);
}
RTextLexerCliWrapper();
private:
GetLexerFactoryDelegate ^ _lexerFactoryPtr;
GCHandle gch;
~RTextLexerCliWrapper();
};
RTextLexerCliWrapper::RTextLexerCliWrapper()
{
_lexerFactoryPtr = gcnew GetLexerFactoryDelegate(&RTextLexer::LexerFactory);
gch = GCHandle::Alloc(_lexerFactoryPtr);
}
RTextLexerCliWrapper::~RTextLexerCliWrapper()
{
gch.Free();
}
This CLI wrapper, is referenced in my main .dll like this :
static RTextLexerCliWrapper _lexerWrapper = new RTextLexerCliWrapper();
[DllExport(CallingConvention = CallingConvention.Cdecl)]
static IntPtr GetLexerFactory(uint index)
{
return (index == 0) ? _lexerWrapper.GetLexerFactory() : IntPtr.Zero;
}
So what happens is, my .net function gets indeed called and the cli wrapper function is also called, and a function pointer is indeed returned. However any attempts to call that function pointer results in an access violation. Which means that either the type of the pointer is wrong or something else which I am currently missing. I have tried countless variations of the .net exported function with void *, StdCall etc. All result in the same problem.
Is there any other way to return a function pointer of a C++ class? Or well am I doing something completely wrong?
Thanks in advance!
So I have finally managed to found the solution to my problem.
First step was exporting the functions with the correct calling convention:
static RTextLexerCliWrapper _lexerWrapper = new RTextLexerCliWrapper();
[DllExport(CallingConvention = CallingConvention.StdCall)]
static IntPtr GetLexerFactory(uint index)
{
return (index == 0) ? _lexerWrapper.GetLexerFactory() : IntPtr.Zero;
}
The convention in this case had to be StdCall. Otherwise the stack pointer is invalidated, hence the exceptions.
Now in order to return a function pointer of a C++ instance things were a bit more tricky.
I am statically storing an instance of the CLI wrapper class so that it doesn't get GCed. ( _lexerWrapper ).
This instance has a function called GetLexerFactory which return a function pointer of the C++ instance ( which is then used by some other .dll to get actual instances of some object ).
The CLI Wrapper class looks like this:
public ref class RTextLexerCliWrapper
{
public:
delegate ILexer * GetLexerFactoryDelegate();
IntPtr GetLexerFactory()
{
return System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(_lexerFactoryPtr);
}
RTextLexerCliWrapper();
private:
GetLexerFactoryDelegate ^ _lexerFactoryPtr;
GCHandle gch;
~RTextLexerCliWrapper();
};
Where ILexer * is the type of the object we shall be returning later on.
RTextLexerCliWrapper::RTextLexerCliWrapper()
{
_lexerFactoryPtr = gcnew GetLexerFactoryDelegate(&RTextLexer::LexerFactory);
gch = GCHandle::Alloc(_lexerFactoryPtr);
}
RTextLexerCliWrapper::~RTextLexerCliWrapper()
{
gch.Free();
}
So, what we have managed here is, exporting through .NET a function pointer which is able to return a pure C++ object.

How To P/Invoke char* [] in C#

How To P/Invoke char* [] in C#
Any 1 tell how to continue hereafter.
I want to send parameters to C++ DLL from C#. I had googled many sites but no solution.
C Function
public void* find(char*[] argv)
{
}
C# Function
i want invoke this function with following paramter arguments
char *Argv[] = { "Tool", "Sachin", "192.168.1.1", "3", "400"};
Thanks in Advance.
There are multiple ways to do it...here are just a couple...but I outline other information you need to be aware of.
Properly Exporting/Importing your "find" Function
First you need to make sure your "exported" C function and the DllImport are defined properly (if you have this working already, then ignore this section).
If your C function is compiled to use the cdecl calling convention (usually the default in C/C++ projects), then you need to use CallingConvention = CallingConvention.Cdecl on the DllImport e.g.
[DllImport("yourdll.dll", CharSet = Ansi, CallingConvention = CallingConvention.Cdecl)]
public IntPtr find([In] String[] args);
If your C function is compiled to use the 'stdcall' calling convention (by project options or by putting the WINAPI macro, or __stdcall decoration on the function definition), then you need to use CallingConvention = CallingConvention.Stdcall (which is the default on DllImport anyway) e.g.
[DllImport("yourdll.dll", CharSet = Ansi)]
public IntPtr find([In] String[] args);
In addition when defining a "C" function you can use extern "C" to stop the the C++ compiler mangling the name of the function.
You then either use __declspec(export), or use a .DEF file to specify the function as an exported entry.
What is your "find" Contract?
Important: you need to know the contract of your "find" function...i.e. what will it look for to signify the "end" of that list of arguments...it might use NULL, or it might use an empty string, etc.
Usually, functions like that, have another parameter that signifies the "count" of the number of items in the array so that a "marker" isn't needed.
For the example below, I will assume it uses NULL at the end...you will have to modify the code accordingly if it's not that way.
If it's possible for some of your parameters to be NULL, then you will have to change the "sentinel" used to signify the end.
You can do the "marshalling" between C# and native types (using the Marshalling attributes):
// Note: this uses a "NULL" to mark the end of the array...
// because your "find" function doesn't have a "count" parameter, and I'm
// assuming it uses a NULL parameter to detect the end.
IntPtr opaqueresult = find(new string[] { "Tool", "Sachin", "192.168.1.1", "3", "400", null}); // call the function in that DLL.
Or you can do the marshaling logic yourself (rearrange and expand the code as necessary to clean up memory etc):
// When need to use IntPtr as we are allocating the native memory.
[DllImport("yourdll.dll", CharSet = Ansi)]
public IntPtr find([In] IntPtr args);
IntPtr []allocatednativestrings;
IntPtr args = AllocateAnsiIntPtrArrayWithSentinel( new string[] { "Tool", "Sachin", "192.168.1.1", "3", "400"}, out allocatednativestrings);
IntPtr opaqueresult = find(args); // call the function in that DLL.
// If "find" DOESN'T hold onto the pointers passed into it, then you can "free"
// the memory just after you make the call....otherwise you can't...you then
// have to decide how/who has responsibility for freeing that memory and when.
// Free the "strings", and the memory containing the pointers
FreeAnsiIntPtrArrayWithSentinel(args, allocatednativestrings);
(Note: I am only using this bit of hacky code, as it was mentioned in a comment above...
http://www.codeproject.com/Articles/17450/Marshal-an-Array-of-Zero-Terminated-Strings-or-Str ....
the crucial thing is to make sure you "free" the memory properly, when you are finished with it....
this can be done neater...just giving you enough to get the idea)
public static IntPtr AllocateAnsiIntPtrArrayWithSentinel(string[] InputStrArray, out IntPtr[] InPointers)
{
int size = InputStrArray.Length + 1; // +1 for NULL sentinel
//build array of pointers to string
InPointers = new IntPtr[size];
int dim = IntPtr.Size * size;
IntPtr rRoot = Marshal.AllocCoTaskMem(dim);
int i = 0;
foreach(string arg in args)
{
if (arg == null)
{
System.Diagnostics.Debug.Assert(false, "this code needs changing to support NULL arguments");
}
InPointers[i++] = Marshal.StringToCoTaskMemAnsi(arg);
}
// The NULL sentinel...don't need to do this as already initialized to null...
// but just making it clearer for you.
InPointers[size-1] = IntPtr.Zero;
//copy the array of pointers
Marshal.Copy(InPointers, 0, rRoot, size);
return rRoot;
}
public static void FreeAnsiIntPtrArrayWithSentinel(IntPtr args, IntPtr[] intptrs)
{
foreach(IntPtr ptr in intptrs)
{
if (ptr != IntPtr.Zero) // we need to avoid the sentinel
Marshal.FreeCoTaskMem(ptr); // free the mem allocated for the string
}
// free the memory that contained the list of string pointers and sentinel
Marshal.FreeCoTaskMem(args);
}

How do I call this c function in c# (unmarshalling return struct)?

I want to use c# interop to call a function from a dll written in c. I have the header files.
Take a look at this:
enum CTMBeginTransactionError {
CTM_BEGIN_TRX_SUCCESS = 0,
CTM_BEGIN_TRX_ERROR_ALREADY_IN_PROGRESS,
CTM_BEGIN_TRX_ERROR_NOT_CONNECTED
};
#pragma pack(push)
#pragma pack(1)
struct CTMBeginTransactionResult {
char * szTransactionID;
enum CTMBeginTransactionError error;
};
struct CTMBeginTransactionResult ctm_begin_customer_transaction(const char * szTransactionID);
How do I call ctm_begin_customer_transaction from c#. The const char * mapps well to string, but despite various attempts (looking at stackoverflow and other sites), I fail to marshal the return structure. If I define the function to return IntPtr it works ok...
Edit
I changed the return type to IntPtr and use:
CTMBeginTransactionResult structure = (CTMBeginTransactionResult)Marshal.PtrToStructure(ptr, typeof(CTMBeginTransactionResult));
but it throws AccessViolationException
I also tried:
IntPtr ptr = Transactions.ctm_begin_customer_transaction("");
int size = 50;
byte[] byteArray = new byte[size];
Marshal.Copy(ptr, byteArray, 0, size);
string stringData = Encoding.ASCII.GetString(byteArray);
stringData == "70e3589b-2de0-4d1e-978d-55e22225be95\0\"\0\0\a\0\0\b\b?" at this point. The "70e3589b-2de0-4d1e-978d-55e22225be95" is the szTransactionID from the struct. Where is the Enum? Is it the next byte?
There's a memory management problem hidden in this struct. Who owns the C string pointer? The pinvoke marshaller will always assume that the caller owns it so it will try to release the string. And passes the pointer to CoTaskMemFree(), same function as the one called by Marshal.FreeCoTaskMem(). These functions use the COM memory allocator, the universal interop memory manager in Windows.
This rarely comes to a good end, C code does not typically use that allocator unless the programmer designed his code with interop in mind. In which case he'd never have used a struct as a return value, interop always works much less trouble-free when the caller supplies buffers.
So you cannot afford to let the marshaller do its normal duty. You must declare the return value type as IntPtr so it doesn't try to release the string. And you must marshal it yourself with Marshal.PtrToStructure().
That however still leaves the question unanswered, who owns the string? There is nothing you can do to release the string buffer, you don't have access to the allocator used in the C code. The only hope you have is that the string wasn't actually allocated on the heap. That's possible, the C program might be using string literals. You need to verify that guess. Call the function a billion times in a test program. If that doesn't explode the program then you're good. If not then only C++/CLI can solve your problem. Given the nature of the string, a "transaction ID" ought to change a lot, I'd say you do have a problem.
I hate to answer my own question, but I found the solution to marshal the resulting struct. The struct is 8 bytes long (4 bytes for the char * and 4 bytes for enum). Marshalling the string does not work automatically, but the following works:
// Native (unmanaged)
public enum CTMBeginTransactionError
{
CTM_BEGIN_TRX_SUCCESS = 0,
CTM_BEGIN_TRX_ERROR_ALREADY_IN_PROGRESS,
CTM_BEGIN_TRX_ERROR_NOT_CONNECTED
};
// Native (unmanaged)
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
internal struct CTMBeginTransactionResult
{
public IntPtr szTransactionID;
public CTMBeginTransactionError error;
};
// Managed wrapper around native struct
public class BeginTransactionResult
{
public string TransactionID;
public CTMBeginTransactionError Error;
internal BeginTransactionResult(CTMBeginTransactionResult nativeStruct)
{
// Manually marshal the string
if (nativeStruct.szTransactionID == IntPtr.Zero) this.TransactionID = "";
else this.TransactionID = Marshal.PtrToStringAnsi(nativeStruct.szTransactionID);
this.Error = nativeStruct.error;
}
}
[DllImport("libctmclient-0.dll")]
internal static extern CTMBeginTransactionResult ctm_begin_customer_transaction(string ptr);
public static BeginTransactionResult BeginCustomerTransaction(string transactionId)
{
CTMBeginTransactionResult nativeResult = Transactions.ctm_begin_customer_transaction(transactionId);
return new BeginTransactionResult(nativeResult);
}
The code works, but I still need to investigate, if calling the unmanaged code results in memory leaks.

Getting Text from SysListView32 in 64bit

here is my code :
public static string ReadListViewItem(IntPtr lstview, int item)
{
const int dwBufferSize = 1024;
int dwProcessID;
LV_ITEM lvItem;
string retval;
bool bSuccess;
IntPtr hProcess = IntPtr.Zero;
IntPtr lpRemoteBuffer = IntPtr.Zero;
IntPtr lpLocalBuffer = IntPtr.Zero;
IntPtr threadId = IntPtr.Zero;
try
{
lvItem = new LV_ITEM();
lpLocalBuffer = Marshal.AllocHGlobal(dwBufferSize);
// Get the process id owning the window
threadId = GetWindowThreadProcessId(lstview, out dwProcessID);
if ((threadId == IntPtr.Zero) || (dwProcessID == 0))
throw new ArgumentException("hWnd");
// Open the process with all access
hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
if (hProcess == IntPtr.Zero)
throw new ApplicationException("Failed to access process");
// Allocate a buffer in the remote process
lpRemoteBuffer = VirtualAllocEx(hProcess, IntPtr.Zero, dwBufferSize, MEM_COMMIT,
PAGE_READWRITE);
if (lpRemoteBuffer == IntPtr.Zero)
throw new SystemException("Failed to allocate memory in remote process");
// Fill in the LVITEM struct, this is in your own process
// Set the pszText member to somewhere in the remote buffer,
// For the example I used the address imediately following the LVITEM stuct
lvItem.mask = LVIF_TEXT;
lvItem.iItem = item;
lvItem.iSubItem = 2;
lvItem.pszText = (IntPtr)(lpRemoteBuffer.ToInt32() + Marshal.SizeOf(typeof(LV_ITEM)));
lvItem.cchTextMax = 50;
// Copy the local LVITEM to the remote buffer
bSuccess = WriteProcessMemory(hProcess, lpRemoteBuffer, ref lvItem,
Marshal.SizeOf(typeof(LV_ITEM)), IntPtr.Zero);
if (!bSuccess)
throw new SystemException("Failed to write to process memory");
// Send the message to the remote window with the address of the remote buffer
SendMessage(lstview, LVM_GETITEMText, 0, lpRemoteBuffer);
// Read the struct back from the remote process into local buffer
bSuccess = ReadProcessMemory(hProcess, lpRemoteBuffer, lpLocalBuffer, dwBufferSize,IntPtr.Zero);
if (!bSuccess)
throw new SystemException("Failed to read from process memory");
// At this point the lpLocalBuffer contains the returned LV_ITEM structure
// the next line extracts the text from the buffer into a managed string
retval = Marshal.PtrToStringAnsi((IntPtr)(lpLocalBuffer +
Marshal.SizeOf(typeof(LV_ITEM))));
}
finally
{
if (lpLocalBuffer != IntPtr.Zero)
Marshal.FreeHGlobal(lpLocalBuffer);
if (lpRemoteBuffer != IntPtr.Zero)
VirtualFreeEx(hProcess, lpRemoteBuffer, 0, MEM_RELEASE);
if (hProcess != IntPtr.Zero)
CloseHandle(hProcess);
}
return retval;
}
no matter what i do retval returns empty, although lpLocalBuffer doesnt .
here is the def of ListItem :
[StructLayout(LayoutKind.Sequential)]
private struct LV_ITEM
{
public int mask;
public int iItem;
public int iSubItem;
public int state;
public int stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
internal int lParam;
internal int iIndent;
}
i tried compiling for 86x , 64bit, any cpu , nothing seems to work at all !
any idea why this might be happening ?
C# + .net4 , windows 7 64bit.
Here's a different approach to doing this - use UI Automation. It does the cross-process, cross-bitness work for you, and will work against listviews, listboxes, or pretty much any other standard Windows UI. Here's a sample app that will get the HWND from the listview under the mouse pointer, and dump the items in it. It dumps just the name of each item; with Listviews, I think you can recurse into the fields in each item if you want.
// Compile using: csc ReadListView.cs /r:UIAutomationClient.dll
using System;
using System.Windows.Automation;
using System.Runtime.InteropServices;
class ReadListView
{
public static void Main()
{
Console.WriteLine("Place pointer over listview and hit return...");
Console.ReadLine();
// Get cursor position, then the window handle at that point...
POINT pt;
GetCursorPos(out pt);
IntPtr hwnd = WindowFromPoint(pt);
// Get the AutomationElement that represents the window handle...
AutomationElement el = AutomationElement.FromHandle(hwnd);
// Walk the automation element tree using content view, so we only see
// list items, not scrollbars and headers. (Use ControlViewWalker if you
// want to traverse those also.)
TreeWalker walker = TreeWalker.ContentViewWalker;
int i = 0;
for( AutomationElement child = walker.GetFirstChild(el) ;
child != null;
child = walker.GetNextSibling(child) )
{
// Print out the type of the item and its name
Console.WriteLine("item {0} is a \"{1}\" with name \"{2}\"", i++, child.Current.LocalizedControlType, child.Current.Name);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
};
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(POINT pt);
[DllImport("user32.dll")]
private static extern int GetCursorPos(out POINT pt);
}
I know this is old, but I found it while trying to solve my problem and hopefully this will help someone else.
I used the recommendation in this question, that was in C++, and slightly modified the LV_ITEM structure to make it work with 64bit in VB.NET (I haven't tested in C# but I imagine the solution is quite similar.)
Public Structure LV_ITEM64
Public mask As Integer
Public iItem As Integer
Public iSubItem As Integer
Public state As Integer
Public stateMask As Integer
Public placeholder1 As Integer
Public pszText As Integer
Public placeholder2 As Integer
Public cchTextMax As Integer
Public iImage As Integer
End Structure
Then, when declaring the instance of the structure, I used the following code to choose between 64 bit and 32 bit structures:
Dim lvi As Object
If IntPtr.Size = 4 Then
lvi = New LV_ITEM
Else
lvi = New LV_ITEM64
End If
You have clarified that you are trying to read items from a list view control in a 32 bit process into a different 64 bit process.
I have seen many questions on this topic in various forums and not one ever seemed to achieve a successful outcome.
I think your best option is to create a 32 bit executable which will be able to read out of the other program's list view.
There is at least one obstacle to overcome if your program is 32-bit and the target program is 64-bit. Or the other way around. The LVITEM declaration will be wrong, IntPtr has the wrong number of bits. Which makes Marshal.SizeOf() return the wrong value. Alignment is okay, I think, by accident. Changing the field to either int or long can fix the problem, depending on the bitness of the target program. Which you can find out by looking at the Taskmgr.exe, Processes tab. The process name is post-fixed with "*32" if it is a 32-bit process. Or simply stay out of trouble by setting your project's Target platform setting to match the target process (x86 or AnyCPU).
Debug this by using Debug + Windows + Memory + Memory1. Put "lpLocalBuffer" in the Address box and observe what you see vs what your code reads. You should definitely be able to tell from the hex view that you got the string properly. Note that if you see zeros between the string characters then the target process uses the Unicode version of the list view. Marshal.PtrToStringUnicode is then required to read it.
Sorry my response is so late but I just came across the same issue. Here is the structure I used for VB.NET which works on both 32 and 64 bit systems.
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure LV_ITEM
Public Mask As UInteger
Public Index As Integer
Public SubIndex As Integer
Public State As Integer
Public StateMask As IntPtr
Public Text As String
Public TextLength As Integer
Public ImageIndex As Integer
Public LParam As IntPtr
End Structure

Categories

Resources