How would one go about Accessing Variable Addresses in VB.NET - vb.net

I have to upgrade a legacy VB6 app to VB.NET; this app uses a function call from a .dll that takes a memory address as one of it's parameters. The VB6 app does this with the VarPtr() function, but this function does not exist in .NET. How do I retrieve the memory location of a variable in .NET?
-Edit1
For example
aVariable1 = aFunctionCall(VarPtr(aVariable2))
-Edit2
The exact function call is in a DLL called FTD2XX.DLL and the exact call is
FT_STATUS = FT_ListDevices(arg1, arg2, _
FT_LIST_BY_INDEX or FT_OPEN_BY_SERIAL_NUMBER)

Trying to pass addresses of something in managed code (.NET) to an unmanaged DLL might not be the best plan. VB6 and VB.NET don't have a lot in common beyond similar syntax and a similar sounding name. You may need to to pin the memory before passing an address. You will need to look into platform invoke: http://msdn.microsoft.com/en-us/library/aa288468(VS.71).aspx

It is automatic when you declare the external function with the Declare keyword. All you have to do is declare the argument with ByRef. That forces the P/Invoke marshaller to pass a pointer to the native code. Same thing as VarPtr. Only if you pass ByVal do you have to explicitly convert the passed argument to a pointer.

Related

How to pass a Object^ to a native function in C++ CLI

I'm new to C++ CLI and I still don't get the new pointers and handles.
I have a native function which opens a window. It requires a handle to a parent window:
void open(void* parentHwnd);
How am I supposed to pass a parent window from managed code to this function? I was trying to do something like this:
void managedOpen(Object^ parent)
{
interior_ptr<void> ptr = &*parent);
open(ptr);
}
but the & operator "cannot be used to take the address of an object with a ref class type".
Also should I use pin_ptr instead of interior_ptr?
Picking proper types in an interop scenario is 99% of the battle. You didn't get any help from the existing code, void* is not an appropriate type to use for a window handle. It should be HWND. That ship probably sailed a long time ago.
But on top of the list of types never to use is System::Object. That only ever interops correctly by sheer accident, unless you interop with COM code that uses variants. The appropriate type to store an operating system handle in managed code is IntPtr or SafeHandle. Heavily biased to IntPtr for window handles since there isn't anything safe about them, they'll die beyond your control when the user closes a window.
So this needs to look like this:
void managedOpen(IntPtr parent)
{
open(parent.ToPointer());
}
With the burden on the client code to produce a valid IntPtr. Could be Control.Handle in Winforms or WindowInteropHelper.Handle in WPF, etcetera.
Stuff like System::Object is only passed from managed to unmanaged with intention of passing it back to managed code, such as a managed function calling EnumWindows. But in this case:
In C++/CLI, you can simply pass a pointer to an unmanaged object containing a gcroot<> to the managed object you want to access.
In C#, you use the GCHandle class to obtain an IntPtr and back.

How to receive bytes in Managed C++ project from COM plus project

I have a module A in Managed C++, it depends on module B in native C++ which wrapped as COM plus.
In module B, I read bytes from a file. Now I am trying to call the file reading functionality from A. But failed.
Dependency detail: I used tlbimp.exe and generated the interop according to Module B. A referrs to the interop.
I tried to pass an "array^" but only one char was received, which is understandable because marshaling doesn't know the array length and could NOT handle the whole array.
I searched out some recommendation about safe array, but could NOT use it successfully in my projects.
Could somebody help me on this?
Thanks a lot.
If you are going to be talking to your native object via COM, you're going to have to pass the array the COM way.
SAFEARRAY would definitely work, but you don't have to use it. It is a fair amount of work to set up anyway. If neither component is a scripting language or VB6, there is little value to using a SAFEARAY.
COM can marshal the array just fine, you just have to tell it how big it is. The two most common mechanisms in COM to pass (native) arrays are "fixed-sized arrays" and "conformant arrays".
Fixed-size array:
If you know at compile time the size of the array, this is the way to go. Declare your COM method as follows in your IDL:
...
const long ARRAY_SIZE = 1024;
...
HRESULTS MethodAbc(MyClass array[ARRAY_SIZE]);
Marshalling will take care of passing the whole array.
Conformant Arrays:
You declare them as follows in IDL:
HRESULT MethodAbc([size_is(arraySize)] MyClass array[], long arraySize);
This tells COM that the arraySize parameter holds the count of elements.
My experience with CLI is minimal, but I don't think you can just pass a CLI handle. Among other things, I believe you need to pin the pointer so that GC doesn't move the array during the COM call. Others please correct me here if I'm wrong.

Automating an Application using COM

I am building an automation interface for an existing application. After implementing a DLL server and an EXE server (mainly for getting familiar with the basics of COM) I am now at the point where I generate a type library from an IDL file and can, for example, basically automate my application from VBScript:
Set oApp = CreateObject("MyApp.1")
oApp.ShowAboutBox()
This simple call to a function that takes no parameters works. The next step I want to take is call a function that takes a parameter.
The signature of the function in the IDL file is
HRESULT CreateSomeChildWindow([out, retval] MyChildWindow** ppChildWindow);
and in VBScript I assume it would be
Dim oWnd As MyChildWindow
oWnd = oApp.CreateSomeChildWindow()
This call already works in C++ although MyChildWindow is not currently registered as a COM object in the registry. The reason MyChildWindow doesn't need to be registered is that CreateSomeChildWindow simply returns the interface pointer to the created MyChildWindow object in a parameter. And the reason it isn't registered is that I want to avoid redundancy, and also I don't want MyChildWindow to be instantiated directly (e.g. by calling CreateObject in VBScript).
Question:
Now I'm trying to find out whether it will be necessary to register MyChildWindow after all. Is my assumption correct that in order to call CreateSomeChildWindow in VBScript
I need to write Dim oWnd As MyChildWindow
For this to work, MyChildWindow must be registered
And if the answer to that is yes, hopefully clients still can't MyChildWindow directly, since I don't implement a class object for it? Or will I have to implement a class object?
Your out/retval is not an object (on the scripting side), it is an interface pointer. And since the method CreateSomeChildWindow is on IDL, in type library, in registered type library - scripting/automation is aware of interface definition, such as methods etc, because the whole type library is already registered. You are already well set, no additional registration required.
When caller receives an interface pointer, it does not care what object the pointer belongs to. Interface pointer alone is good enough, and scripting/automation environment known how to deal with it.
On the callee side however, you need to return an interface pointer, and you are dealing with objects. So you need some class which implements this interface and you return this object's interface.

Problems calling a DLL from .NET

I have a VB6 DLL that wraps a call to a 3rd party component. When I call my DLL from VB6, everything works fine, but when I call it from vb.net (2.0 framework targeted - VS2010) I get this error:
AccessViolationException occurred
Attempted to read or write protected memory. This is often an
indication that other memory is corrupt.
This error only occurs on Windows 7 (Windows XP clients work fine).
I've looked this up and all the articles I found talked about the declaration not being correct. I am not declaring any APIs calls though, the 3rd party component is early bound in my VB6 DLL. I can run the DLL, set a breakpoint, and it goes into my VB6 function, but errors calling a function in the 3rd party component.
My VB6 DLL takes 3 string and one 32bit numeric (long in VB6) parameters. The 3rd party's DLL function that I am calling is taking a string (bstrDNSID as string is what Intellisense shows in VB6). This is where it errors.
Does anyone know how this might be resolved?
Update:
None of the marshalling has helped, so I tried creating a test sub in my VB6 DLL. I hardcoded all the values within the DLL's test sub. It works fine when called from VB6, but gives the same error as above when running from .NET. Also of interest, when I have the VB6 DLL running from the VB6 IDE, I do not get the error when calling the DLL from .NET.
This doesn't really answer your question, but I couldn't fit these examples in the comments.
When marshaling strings to unmanaged DLL calls from .NET, I would receive an AccessViolationException on occasion because I wasn't specifying the right charset. I fixed it by explicitly converting an IntPtr to a string in the charset I needed.
[DllImport("MyDLL.dll", CharSet = CharSet.Ansi)]
static extern void do_something(IntPtr str);
void DoSomethingWrapper(string str) {
var ptr = Marshal.StringToHGlobalAnsi(str);
do_something(ptr);
}
You might need Marshal.StringToBSTR. I don't know if you can call the VB6 function with a pointer or if you'll have to create the pointer in the VB6 DLL.
Here's a question you might find useful: How can I get the charset VB6 is using?
Workaround:
I have found one solution that works. I simply created an ActiveX EXE in VB6 that called the 3rd party component. Works like a charm from .NET.
Also of note, I'd never created an ActiveX EXE and didn't know that regsvr32 will not work to register it. Here is the proper way to register ActiveX EXEs.
VB6 probably declares integer as 16 bit. This is a problem with calling DLLs from VBA too. The solution should be to change your integer to long

VB6 DLL takes callback as Integer, VB.NET requires delegate reference type

I have an issue with a third-party COM+ DLL meant to be used from VB6, where it has a function to set a Callback for a hardware event. However, I'm using VB.NET, and AddressOf now returns a reference type instead of an integral type, which means that the setCallback function on the COM+ DLL apparently can't be used.
Is there a way around this problem (I don't have VB6 available to develop some sort of wrapper with), or will I have to find a different 3rd-party DLL in order to get this to work? For reference, I'm trying to access the LCD on a Logitech G15(v2) keyboard.
For Reference, here is the setCallback function and the callback's prototype itself:
Public Sub setCallback(funcAddr As Integer)
Public Sub LCDbuttonPress(ByVal butStates As Integer)
I think if you declare your setCallback function like this:
Public Sub setCallback([MarshalAs(UnmanagedType.FunctionPtr)]funcAddr as function)
I'm not a VB guy, so I don't know the exact syntax. But the basic idea here is that in VB.Net you declare the prototype as taking a delegate, even a delegate of the correct type. But then you use the MarshalAs attribute to tell the marshalling code to treat it as a function pointer (actually a Integer) on the other side. I do this in C# to pass callbacks to C++ code and it works just fine.
For instance this in C#
public delegate int MyProgress(double dPercentComplete);
...
int WaitWithProgress([MarshalAs(UnmanagedType.FunctionPtr)]MyProgress pfn);
Shows up on the C++ side of the house as this
HRESULT __stdcall WaitWithProgress (long pfn, int * pRetVal);
Note the comments. The actual solution to this problem turned out to be Marshal.GetFunctionPointerForDelegate() And be careful to read the docs on this. If you use this method, it becomes your responsibility to make sure that the delegate isn't garbage collected before you setCallback(NULL)