Testing COM calls in WSH windows script - c#

I have a working C# COM component dll, with a class called MyComponent in the ap namespace, which is added to the GAC and registered successfully. I added a Add() call to it, tested it with a win32 c++ exe and called the Add() call successfully, so its all working.
However I want to test the call in WSF (windows script) I put the code below in a file called test.wsf, when I run the code I get an error:
Error: Could not connect object, on the line:
WScript.ConnectObject(appos,"ap_");
Why is it not connecting! Help!
<?xml version="1.0" encoding="utf-8" ?>
<package xmlns="http://schemas.microsoft.com/WindowsScriptHost">
<job>
<?job debug="true" ?>
<script language="JScript">
<![CDATA[
// Create action provider
var appos = WScript.CreateObject ("ap.MyComponent");
WScript.ConnectObject (appos, "ap_");
// Initialise voucher provider with store and terminal id strings
appos.Add(1,99);
// Release object
appos = null;
WScript.StdIn.Read(1);
]]>
</script>
</job>
</package>

From MSDN:
Connects the object's event sources to functions with a given prefix.
http://msdn.microsoft.com/en-us/library/ccxe1xe6(VS.85).aspx
This Methods registers for callbacks. If you do not have callbacks, you don't need to call this method.

Sorry, my answer was not precice enough.
WScript.ConnectObject (appos, "ap_");
This method is for attaching your script to the COM Objects events. You have no events, so you don't need to call that method.
From MSDN:
Connects the object's event sources to functions with a given prefix.
http://msdn.microsoft.com/en-us/library/ccxe1xe6%28VS.85%29.aspx
This should be enough:
// Create action provider
var appos = WScript.CreateObject ("ap.MyComponent");
// Initialise voucher provider with store and terminal id strings
appos.Add(1,99);

Related

Getting error when load the bing map version 8

when i run my code it show a script error dialog
"Error -Script error ,URL - http://www.bing.com/api/maps/mapcontrol?callback=OnLoad"
OnLoad is function which i call to initialize the map
Url i used to get map in my html file -
<script type='text/javascript' src='http://www.bing.com/api/maps/mapcontrol?callback=OnLoad' async defer></script>
Two things to try. First make sure your network can reach the http://bing.com website. Second, make sure you have a function call OnLoad that is accessible to the map control to call. Verify spelling and that it al control doesn't try and call this function before it is available. Try adding async defer to your script tag and this will allow your other code to load a bit faster.

Registration free (sxs) COM DirectShow filter

There are questions asking on how to get Registration free COM working, and this is not one of those. I have a DirectShow video source filter (catagory 860BB310-5D01-11d0-BD3B-00A0C911CE86) implemented in .Net with the help of an edited version of the code available here: Pure .Net DirectShow Filters by Maxim Kartavenkov.
I need to get ffmpeg to recognize my .Net DirectShow filter as a video source using Registration Free COM (Side by Side / sxs). Built into the .Net framework is support for COM component servers, so theoretically as long as the manifests are correct, ffmpeg should detect the filters.
Here is a snippet of the relevant sections of my manifest files currently.
<!-- FFMPEG MANIFEST -->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity name="ffmpeg.exe" version="1.0.0.0" type="win32" processorArchitecture="*"/>
<dependency>
<dependentAssembly asmv2:codebase="DShowVideoFilter.manifest">
<assemblyIdentity name="DShowVideoFilter" version="1.0.0.0" publicKeyToken="26A05D7C90FBA3E8"/>
</dependentAssembly>
</dependency>
</assembly>
<!-- DIRECTSHOW FILTER MANIFEST -->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="DShowVideoFilter" version="1.0.0.0" publicKeyToken="26A05D7C90FBA3E8" />
<clrClass
clsid="{65722BE6-3449-4628-ABD3-74B6864F9739}"
progid="DShowVideoFilter.VideoCaptureFilter"
threadingModel="Both"
runtimeVersion="v2.0.50727"/>
<file name="DShowVideoFilter.dll">
</file>
<file name="DShowVideoFilter.tlb">
<typelib
tlbid="{B618E67B-64C8-48E9-9F94-F13214B76808}"
version="1.0"
helpdir=""
flags="hasdiskimage"/>
</file>
</assembly>
So, I get no errors when running ffmpeg (like you would if there was a manifest error) - and I am confident that everything that is configured correctly (related to traditional sxs com loading), the problem I think (unconfirmed) is that ffmpeg loads DShow filters via DirectShow's intelligent connect system, which requires the filter and pins to be registered. Here are some documents that talk about how filters need to be registered that I've found:
#1 Registering DirectShow Filters
#2 Implementing DllRegisterServer
Now, in Maxim Kartavenkov's DShow base classes, he takes care of #2 automatically. Here is a significantly shortened version of the method that registers the filters implementing BaseFilter.
[ComRegisterFunction]
public static void RegisterFunction(Type _type)
{
AMovieSetup _setup = (AMovieSetup)Attribute.GetCustomAttribute(_type, typeof(AMovieSetup));
BaseFilter _filter = (BaseFilter)Activator.CreateInstance(_type);
string _name = _filter.Name;
DsGuid _category = new DsGuid(_setup.Category);
IFilterMapper2 _mapper2 = (IFilterMapper2)new FilterMapper2();
RegFilter2 _reg2 = new RegFilter2();
_reg2.dwVersion = (int)_setup.Version;
_reg2.dwMerit = _setup.FilterMerit;
_reg2.rgPins = IntPtr.Zero;
_reg2.cPins = 0;
IntPtr _register = Marshal.AllocCoTaskMem(Marshal.SizeOf(_reg2));
Marshal.StructureToPtr(_reg2, _register, true);
hr = _mapper2.RegisterFilter(_type.GUID, _name, IntPtr.Zero, _category, _instance, _register);
Marshal.FreeCoTaskMem(_register);
}
That is the method (particularly mapper2.RegisterFilter) that allows ffmpeg to find the DShow filter when it is registered traditionally (with RegAsm) into the registry, which creates registry keys for the filter and pins as described by #2 link.
tldr;
So the question is, how to emulate the function of RegisterFilter or the intelligent connect registry entries this within a manifest file as to allow the sxs context to find my DirectShow filter when ffmpeg searches for it.
This is almost one of those questions asking on how to get registration-free COM working.
As you correctly say, it's not a problem with getting it to work at the most basic level. However, the fact that the manifest doesn't generate SxS loading errors only means it's a valid manifest XML. To know if it's semantically correct, such as finding missing dependencies or typos in names, CLSIDs and/or versions, test with CLSIDFromProgID followed by CoCreateInstance natively, or Type.GetTypeFromCLSID/Type.GetTypeFromProgID followed by Activator.CreateInstance in .NET.
Unfortunately for cases like this, registration-free COM is only applicable for base COM functionality, like typical class, interface proxy/stub and type library registration, with a tiny bit of OLE (see miscStatus attributes). For categories or subkeys not used by COM itself, registration is necessary.
Why? Because no one else followed lead, not even COM+.

How is a mime player Activex control supposed to retrieve the path for the document?

I'm working on an ActiveX control, and the control is being correctly instantiated based on the Mime Type. I'm trying to understand how to retrieve the path to the file.
This very old (deprecated) KB article specifically says:
In all cases Internet Explorer passes the URL of the file to play to
the default player via the IPropertyBag interface. That is, when
Internet Explorer launches a player, it creates an instance of
IPropertyBag and loads the "SRC" property with the URL of the file to
play. Then it queries the player for IPersistPropertyBag and sends it
the IPropertyBag instance through IPersistPropertyBag::Load.
I implemented this, but the load method never gets called.
int IPersistPropertyBag.Load(IPropertyBag pIPropertyBag, object pErrorLog)
{
object var = null;
pIPropertyBag.Read("SRC", out var, (IErrorLog)pErrorLog);
return 0;
}
There's also a wealth of information here and this article talks extensively about using BindToStorage() in the IBindStatusCallback::OnDataAvailable event. I suspect that this is now the way to go about retrieving the source path for the document, but any confirmation or direction is much appreciated.
One last note: the control DOES implement IObjectSafety, and supports INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA.
I think this can be done by exposing a Data Path Property:
#define GUID_PathProperty 0002DE80-0000-0000-C000-000000000046
[id(1), bindable, displaybind, propput,
custom(GUID_PathProperty, "application/custom-mime-type")]
HRESULT put_Src(BSTR src);
Alternatively, I believe if your ActiveX MIME player control implements IPersistMoniker, then you could obtain the URL via IMoniker::GetDisplayName: How To Find the URL of an ActiveX Document from Inside the Server.
Finally, there may be cases when the data is embedded via Data URI scheme and the URL is not available. Your control should be able to handle this, e.g.:
<object src="data:application/custom-mime-type;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==">
</object>
Updated, the following is a simple test web page for Adobe PDF ActiveX Control (ProgID AcroPDF.PDF, CLSID {ca8a9780-280d-11cf-a24d-444553540000}), which is an established Mime Player for PDF.
<body>
<object
classid="clsid:ca8a9780-280d-11cf-a24d-444553540000" id="pdf1"
type="application/pdf"
data="test.pdf"
style="width: 100%; height: 100%">
<param name="src" "value"="test.pdf"></param>
</object>
</body>
To make it work with a local (file://) PDF document, I had to specify both the data attribute and the src param. When it was served from http://localhost, the data alone was just enough.
Here is the list of COM/OLE interfaces it implements, obtained with OleView:
The interesting ones: IMonikerProp (which may be the answer to your original question), IDataObject, IPersistFile, IPersistMoniker, IPersistStreamInit, IPersistPropertyBag. I'd implement all of them and see what gets called.
I can't answer the question on how to add a custom GUID_PathProperty typelib attribute to the type library generated by RegAsm for a C# project. I'm not even sure this is a hard requirement for a mime player. I'd be interested to know both answers if you managed to find them. Possibly, one option is to develop a wrapper ActiveX control in C++, which would just forward everything to the C# implementation.

Events raised in .net code is does not seem to occur in COM code when deployed with Side by side manifests

Here is a really simple .net <-> COM interop example using events.
This example works just fine as long as i either use regasm or the register for com interop option in Visual studio build options for the .net library.
But I need to deploy using registration free interop enabled side-by-side manifests.
The application runs just fine in side-by-side mode, it's just that the events seems to disappear. I suspect it's some thread marshalling issue, but I can't seem to find the correct solution.
This is of course an attempt to replicate an issue I have with a slightly more complicated interop integration. There is one difference between the issues I'm having here compared to the real issues:
Both solutions fail to properly sink events raised in the .net code while running on reg-free deployment, and both solutions works as expected when the .net dlls are registered in the registry.
However: on the "real" project I get a runtime error when it fails from System.Reflection.Target. On this simplified example it just fails silently.
I'm thoroughly stuck on this one, so any and all suggestions and solutions will be very much welcomed.
I've put the complete code on github if anyone needs to play around with it before answering: https://github.com/Vidarls/InteropEventTest
The .net part
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace InteropEventTest
{
[Guid("E1BC643E-0CCF-4A91-8499-71BC48CAC01D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
public interface ITheEvents
{
void OnHappened(string theMessage);
}
[Guid("77F1EEBA-A952-4995-9384-7228F6182C32")]
[ComVisible(true)]
public interface IInteropConnection
{
void DoEvent(string theMessage);
}
[Guid("2EE25BBD-1849-4CA8-8369-D65BF47886A5")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(ITheEvents))]
[ComVisible(true)]
public class InteropConnection : IInteropConnection
{
[ComVisible(false)]
public delegate void Happened(string theMessage);
public event Happened OnHappened;
public void DoEvent(string theMessage)
{
if (OnHappened != null)
{
Task.Factory.StartNew(() => OnHappened(theMessage));
}
}
}
}
The COM (VB6) part
Private WithEvents tester As InteropEventTest.InteropConnection
Private Sub Command1_Click()
Call tester.DoEvent(Text1.Text)
End Sub
Private Sub Form_Load()
Set tester = New InteropConnection
End Sub
Private Sub tester_OnHappened(ByVal theMessage As String)
Text2.Text = theMessage
End Sub
I currently have the following files / folder structure for deploy:
Root
|-> [D] Interop.Event.Tester
|-> Interop.Event.Tester.manifest
|-> [D] InteropEventTest
|-> InteropEventTest.dll
|-> InteropEventTest.manifest
|-> InteropEventTest.tlb
|-> tester.exe
|-> tester.exe.manifest
Content of manifest files:
Interop.Event.Test.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="Interop.Event.Tester" version="1.0.0.0" type="win32" processorArchitecture="x86"/>
</assembly>
InteropEventTest.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="InteropEventTest" version="1.0.0.0" type="win32"/>
<clrClass
name="InteropEventTest.InteropConnection"
clsid="{2EE25BBD-1849-4CA8-8369-D65BF47886A5}"
progid="InteropEventTest.InteropConnection"
runtimeVersion="v4.0.30319"
threadingModel="Both"/>
<file name="InteropEventTest.tlb">
<typelib
tlbid="{5CD6C635-503F-4103-93B0-3EBEFB91E500}"
version="1.0"
helpdir=""
flags="hasdiskimage"/>
</file>
<comInterfaceExternalProxyStub
name="ITheEvents"
iid="{E1BC643E-0CCF-4A91-8499-71BC48CAC01D}"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
baseInterface="{00000000-0000-0000-C000-000000000046}"
tlbid="{5CD6C635-503F-4103-93B0-3EBEFB91E500}" />
</assembly>
tester.exe.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="tester.exe" version="1.0.0.0" type="win32" processorArchitecture="x86"/>
<dependency>
<dependentAssembly>
<assemblyIdentity name="InteropEventTest" version="1.0.0.0" type="win32"/>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity name="Interop.Event.Tester" version="1.0.0.0" type="win32" processorArchitecture="x86"/>
</dependentAssembly>
</dependency>
</assembly>
After a long time (and several failed attempts) It turned out I could make this work by making one tiny change:
Make the VB6 code compile to P-Code instead of native code.
I'm pretty sure this somehow affects how marshalling between threads is handles, but I've been unable to find any information confirming that theory.
At least it works...
Or Not! (24. October 2013)
It turned out that in real life compiling to P-Code was not enough. In another implementation of this pattern we ended up with the event just disappearing into nowhere, with no exceptions (we thought) and no traces.
So more investigation was due:
1. The real issue
Wrapping the event triggering in a try-catch clause revealed that there was in fact an exception being thrown, it just never surfaced anywhere
if (OnHappened != null)
{
try
{
OnHappened(theMessage));
}
catch (Exception e)
{
Messagebox.Show(e.GetType().Name + " : " + e.message)
}
}
The exception was a TargetException (the object does not match the target type). Some research revealed that this was most probably a threading issue (as I had suspected earlier.)
2. The solution
Most of the stuff written about this seemed to solve it by using an Invoke method. It turned out that most other people trying to solve this was building winforms application, and thus had a handy Ìnvoke(Delegate) method available on all forms and controls.
As Winforms also does quite a bit of COM interop behind the scenes (according to now forgotten articles on the google result list) The invoke method is used to ensure that a method call is executed on the thread that created the given component and thus ensure that it happens on the message-pumped UI thread.
I figured this could be relevant for my case aswell, so I cheated.
I made my interop class inherit from the winforms control
public class InteropConnection : Control, IInteropConnection
Now I wrapped my call in the Invoke method
if (OnHappened != null)
{
try
{
Invoke(OnHappened, theMessage);
}
catch (Exception e)
{
Messagebox.Show(e.GetType().Name + " : " + e.message)
}
}
Now I got an exception because the Control had no WindowHandle assigned.
As it turned out the Control class has a handy CreateHandle() method that can be called and solves this particular issue. (I do not know what possible consequences this has, as the documentation does not recommend calling this method directly.
Now all seems to be working all the time, though I would not be surprised if something new jumps up and bites me now...
I have run into the same issue. COM can marshal the event/call to the correct thread but it needs to have a proxy-stub. These are added to the registry if you use the /tlb option with regasm, and the equivalent in the manifest file are the elements typelib and comInterfaceExternalProxyStub. The VB6 executable can be compiled to a native binary.
For more info see my SO topic: Regfree COM event fails from other thread
We had the same problem.
In our case we have to change the proxyStubClsid32 to {00020420-0000-0000-C000-000000000046}.
Attention: There is a change in one digit!
More information here: http://www.mazecomputer.com/sxs/help/proxy.htm

Parse XML function names and call within whole assembly

I have written an application that unit tests our hardware via a internet browser.
I have command classes in the assembly that are a wrapper around individual web browser actions such as ticking a checkbox, selecting from a dropdown box as such:
BasicConfigurationCommands
EventConfigurationCommands
StabilizationCommands
and a set of test classes, that use the command classes to perform scripted tests:
ConfigurationTests
StabilizationTests
These are then invoked via the GUI to run prescripted tests by our QA team. However, as the firmware is changed quite quickly between the releases it would be great if a developer could write an XML file that could invoke either the tests or the commands:
<?xml version="1.0" encoding="UTF-8" ?>
<testsuite>
<StabilizationTests>
<StressTest repetition="10" />
</StabilizationTests>
<BasicConfigurationCommands>
<SelectConfig number="2" />
<ChangeConfigProperties name="Weeeeee" timeOut="15000" delay="1000"/>
<ApplyConfig />
</BasicConfigurationCommands>
</testsuite>
I have been looking at the System.Reflection class and have seen examples using GetMethod and then Invoke. This requires me to create the class object at compile time and I would like to do all of this at runtime.
I would need to scan the whole assembly for the class name and then scan for the method within the class.
This seems a large solution, so any information pointing me (and future readers of this post) towards an answer would be great!
Thanks for reading,
Matt
To Find all classes in an assembly:
public Type FindClass(string name)
{
Assembly ass = null;
ass = Assembly.Load("System.My.Assembly"); // Load from the GAC
ass = Assembly.LoadFile(#"c:\System.My.Assembly.dll"); // Load from file
ass = Assembly.LoadFrom("System.My.Assembly"); // Load from GAC or File
foreach(Type t in ass.GetTypes())
{
if (t.Name == name)
return t;
}
return null;
}
In reality you should tag your class with an attribute, it makes them more discoverable.
To instantiate an instance of said class:
public T Instantiate<T>(Type typ, object[] arguments)
{
return (T)Activator.CreateInstance(typ, arguments, null);
}
Don't forget, you should probably wrap these with appropriate try/catch blocks
Then to find a method on a class
Type t = FindClass("MyType");
MethodInfo meth = t.GetMethod("TestSomething", typeof(string), typeof(int)); // finds public ??? TestSomething(string, int)
Just play around with the Object Browser in VStudio and learn the Reflection classes, there's lots you can do.

Categories