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.
Related
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.
I'm trying to find an example where a parameter that could be controlled through the filter property page has an exposed getter/setter so that, without loading the property page, the filter property can be changed by the parent program that generated the graph.
Ezrgb24 from the windows sdk has a working property page, but I don't see how to expose the functions used in the property page so that my program can access them directly without initalizing the property page itself. I looked through the Programming Microsoft Directshow book and saw that it goes through the YUVGray example filter and mentions that the colors used could be exposed so that the parent program of the graph could change them, but does not give an example how.
Meanwhile, samples from directshow.net, like the windows sdk samples, appear to include only the filter or only the program source, and I didn't see any example filter that has such properties directly exposed. But examples like the BitmapMixer call IVMRMixerBitmap9.SetAlphaBitmap, an interface for VMR9. I'd like a sample that gives me the code for a similar interface and the filter so I can see how they are related, and the program so I can see how my environment should be set up to utilize the interface.
I'm guessing this is a basic exercise in utilizing COM, but I really would like a complete example with all of the source so I can fully understand how everything is connected. Even if the exposed property is trivially used it would be enough of a skeleton to go on. Is there such an example somewhere that I missed? I'm in C# for the program but have been using directshow.net and can get any graph set up and running, so a C++ program would be fine.
Alright here we go:
We'll be using the ezrgb24 filter example that comes out of the samples that ship with the windows sdk because it's free and open source. It also already has the interface on this side defined. See the iez.h file for the interface, specifically we'll note two things from this file:
1) Our GUID is fd5010a3-8ebe-11ce-8183-00aa00577da1 - we'll need that for the code in the C# side
2) we're exposing the get_IPEffect and put_IPEffect functions which are defined in the ezrgb24.cpp file
So all the work on that side has already been done for us, and it gives us a good idea of how to make our own functions to expose.
Now in our C# program, we're going to make an interface that adapts to this:
using System;
using System.Runtime.InteropServices;
...
[ComImport,
System.Security.SuppressUnmanagedCodeSecurity,
Guid("fd5010a3-8ebe-11ce-8183-00aa00577da1"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IIPEffect
{
[PreserveSig]
void get_IPEffect([Out] out int effectNum, [Out] out double StartTime, [Out] out double Length);
[PreserveSig]
void put_IPEffect([In] int effectNum, [In] double StartTime, [In] double Length);
}
Notice that the Guid is the same and our exposed functions match what is defined inside the filter's code (you can just use [Out] when the C++ function asks for a pointer, in this specific case remember that REFTIME in ezrgb24 is just another name for the double class).
That is all you need to start using the interface.
For example, say I create a graph with directshow.net and make an instance of the ezrgb24 filter (quick and dirty - after registering my compiled dll of ezrgb24 with regsvr32.exe, I just looked up the moniker in GraphEdit and added it that way). I called the instance of the ezrgb24 filter that I was using in my graph IEfilter.
I add it to my graph and connect the pins as usual.
Then I can use the interface class I defined as follows at any point to change the properties of the filter, without any need to restart the graph or bring up the property page:
IIPEffect myIIPEffect = IEfilter as IIPEffect;
int myInt;
double myDouble1, myDouble2;
if (myIIPEffect != null) //the cast will break if you didn't use the right GUID in your interface
{
myIIPEffect.put_IPEffect(1002, 6, 7); //for this filter, look at resource.h for what the int should be, in this case 1002 is the emboss effect
myIIPEffect.get_IPEffect(out myInt, out myDouble1, out myDouble2);
}
And that's it. I hope this helps anyone looking for a complete picture of exposing and accessing filter properties!
I heard people talking about changing the interface of a dll.
What is a change in the interface of the dll, and how would you do that?
Changing a dll's interface would mean to change how the dll and the calling code interacts. This could mean changing the signatures of the dll's exporting functions, or changing to a different set of functions entirely, or it could mean passing different data from the calling code. A dll's interface is generally all it's exported and imported items (both functions and data), or in other words, the parts of the dll that you have access to when you use it.
Often you will want to change the behaviour of your dll without changing its interface. This is because changing the interface often will break code that uses it.
Imagine my dll exporting function foo:
void foo(int i)
{
// Does thing with integer
}
Changing the interface could mean changing foo's signature into
void foo(int, float);
Now, all the code that used foo previously has to be rewritten to use the new signature, which could be a bad thing.
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)
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.