Daniel Lutz
Posts: 5
Joined: 2023-02-27
|
Hello AddinExpress team,
do you have information about a 16èr linup of the MS Office interop libaries which ships with you setup? The current one comes from the 15er branch but it seems to be that there are differences between the com provided interface model and the vba model for certain office applications.
Best regards
Daniel |
|
Andrei Smolin
Add-in Express team
Posts: 19011
Joined: 2006-05-11
|
|
Daniel Lutz
Posts: 5
Joined: 2023-02-27
|
Thanks for the link. I cannot answer there so here followed my current knowledge about the problem, mybe your guys have some more ides to resolve it:
I run in the same problem for the powerpoint object model. After some test here a my currently knowledge about it. Since years microsoft don`t update the interop the libraries. They ship with the installation the .olb definitions which will be also used in the vba code editor of office. So in vba you can use the latest definitions instead.
I test it out VS19 and VS22 to add a com reference to the 16. object libraries. The problem is in both version that the generating of libraries depends of your solution type. If you use a .net Framework based solution (as example the Console application) adding a Com Reference to the 16 version of object libraries will resolve to the gac registered 15 versions and use it.
If you use a newer .net solution (as example console) it will generade a new <ComReference> node in the new sdk styled csproj and call directly tlbimp . For example it will uyse the shared dll or the olb files as example
- C:\Program Files\Microsoft Office\Root\VFS\ProgramFilesCommonX86\Microsoft Shared\OFFICE16\MSO.DLL
- C:\Program Files\Microsoft Office\Root\VFS\ProgramFilesCommonX86\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB
- C:\Program Files\Microsoft Office\Root\Office16\MSPPT.OLB
The problem here is (or maybe it could be resolved and i dont know it right know) the a direct call generates a warning because in the gac is a version always registred. Another case is, that VS change the identity from Microsoft.Office.Interop.* to Interop.Microsoft.Office.Interop . and generate the libraries also with reference to new named dlls. In this case it doesn`t helps to rename it after generation because the internal reference the new naed versions.
If i understand it right we cannot load these libaries with late binding because for addin express use the older liberies and load theme to the app asembly context. The newer one cannot load in this case alos because of version conflicts.
After some research with the COM interface (as example the the presentation class) i can confirm that office load the newer c++ definitions in the case they will be also available. You can define the IDispatch interface and convert a office com object to this. After this it is possible to research for as example newer method/property and will find this in the object.
My current problem is that i cannot call the method because the IDispatch.Invoke mechanisme failed with some errors like convert managed valued to the unmanaged area.
Maybe some other have an idea here more. |
|
Daniel Lutz
Posts: 5
Joined: 2023-02-27
|
[ComImport()]
[Guid("00020400-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IDispatch
{
[PreserveSig]
int GetTypeInfoCount(out int Count);
[PreserveSig]
int GetTypeInfo
(
[MarshalAs(UnmanagedType.U4)] int iTInfo,
[MarshalAs(UnmanagedType.U4)] int lcid,
out ITypeInfo typeInfo
);
[PreserveSig]
int GetIDsOfNames
(
ref Guid riid,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)]
string[] rgsNames,
int cNames,
int lcid,
[MarshalAs(UnmanagedType.LPArray)] int[] rgDispId
);
[PreserveSig]
int Invoke(
int dispIdMember,
ref Guid riid,
int lcid,
uint dwFlags,
ref ComTypes.DISPPARAMS pDispParams,
out object pVarResult,
ref ComTypes.EXCEPINFO pExcepInfo,
ref uint puArgErr);
}
internal abstract class DispatchedObject
{
private const int S_OK = 0;
private const int LCID_DEFAULT = 2048;
private DispatchedObject(IDispatch dispatch)
=> Dispatch = dispatch;
protected DispatchedObject(object dispatchedObject) : this((IDispatch)dispatchedObject) { }
public IDispatch Dispatch { get; }
protected bool TryGetDispatchID(string name, out int dispID)
{
var rgDispId = new int[1] { 0 };
var IID_NULL = new Guid("00000000-0000-0000-0000-000000000000");
var hrRet = Dispatch.GetIDsOfNames
(
ref IID_NULL,
new string[1] { name },
1,
LCID_DEFAULT,
rgDispId
);
if (hrRet == S_OK)
{
dispID = rgDispId[0];
return true;
}
else
{
dispID = int.MinValue;
return false;
}
}
protected bool TryGetDispatchedMembers(out List<DispatchedMember> dispatchedMembers)
{
if (Dispatch.GetTypeInfo(0, 0, out ComTypes.ITypeInfo pTypeInfo) != S_OK)
{
dispatchedMembers = new List<DispatchedMember>();
return false;
}
pTypeInfo.GetTypeAttr(out IntPtr pTypeAttr);
var typeAttr = (ComTypes.TYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(ComTypes.TYPEATTR));
pTypeInfo.ReleaseTypeAttr(pTypeAttr);
var members = new DispatchedMember[typeAttr.cFuncs];
for (int memberIndex = 0; memberIndex < typeAttr.cFuncs; memberIndex++)
{
pTypeInfo.GetFuncDesc(memberIndex, out IntPtr pFuncDesc);
var funcdesc = (ComTypes.FUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(ComTypes.FUNCDESC));
pTypeInfo.GetDocumentation(funcdesc.memid, out string strName, out string strDocumentation, out _, out _);
members[memberIndex].Name = strName;
members[memberIndex].Documentation = strDocumentation;
members[memberIndex].ParamsCount = funcdesc.cParams;
if (funcdesc.invkind == ComTypes.INVOKEKIND.INVOKE_FUNC)
{
members[memberIndex].Type = DispatchedMemberType.Method;
members[memberIndex].Params = new DispatchedMemberParameter[funcdesc.cParams];
for (int paramIndex = 0; paramIndex < funcdesc.cParams; paramIndex++)
{
var elemDesc = Marshal.PtrToStructure<ComTypes.ELEMDESC>(funcdesc.lprgelemdescParam + paramIndex * Marshal.SizeOf(typeof(ComTypes.ELEMDESC)));
var vt = elemDesc.tdesc.vt; // Typ des Parameters
members[memberIndex].Params[paramIndex].Index = paramIndex;
members[memberIndex].Params[paramIndex].Type = (VarEnum)vt;
}
}
else if (funcdesc.invkind == ComTypes.INVOKEKIND.INVOKE_PROPERTYGET)
{
members[memberIndex].Type = DispatchedMemberType.Property_Getter;
}
else if (funcdesc.invkind == ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT)
{
members[memberIndex].Type = DispatchedMemberType.Property_Setter;
}
else if (funcdesc.invkind == ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF)
{
members[memberIndex].Type = DispatchedMemberType.Property_Reference;
}
pTypeInfo.ReleaseFuncDesc(pFuncDesc);
}
dispatchedMembers = new List<DispatchedMember>(members);
return true;
}
}
internal struct DispatchedMember
{
public string Name;
public string Documentation;
public DispatchedMemberType Type;
public int ParamsCount;
public DispatchedMemberParameter[] Params;
}
internal enum DispatchedMemberType
{
Method = 0,
Property_Getter = 1,
Property_Setter = 2,
Property_Reference = 3
}
internal struct DispatchedMemberParameter
{
public int Index;
public VarEnum Type;
}
internal class DispatchedObject<TDispatchedObject> : DispatchedObject
{
public DispatchedObject(TDispatchedObject dispatchedObject) : base(dispatchedObject)
=> Object = dispatchedObject;
public TDispatchedObject Object { get; }
public bool HasMember(string memberName)
=> TryGetDispatchID(memberName, out _);
public ReadOnlyCollection<DispatchedMember> Members
{
get
{
if (TryGetDispatchedMembers(out var members))
{
return new ReadOnlyCollection<DispatchedMember>(members);
}
return new ReadOnlyCollection<DispatchedMember>(new List<DispatchedMember>());
}
}
}
This is my current code for my wrapper.
var obj = new DispatchedObject<Microsoft.Office.Interop.Presentation>(Application.ActivePresentation);
obj.HasMember("IsFullyDownloaded");
The members include the type informations for parameters. But maybe some other have an ide for invokingproperties or methods. |
|
Andrei Smolin
Add-in Express team
Posts: 19011
Joined: 2006-05-11
|
Hello Daniel,
An interop lets your add-in use early binding with Office versions higher or equal to the interop's version. It doesn't work with previous Office versions - this makes developer decide what minimal Office version should be supported - and it makes your add-in use late binding to access features introduced in newer Office versions. That's the case with Office: interops for Office 2016 don't cover functionality introduced after that.
Daniel Lutz writes:
there are differences between the com provided interface model and the vba model for certain office applications.
These are differences between the stable (or, maybe, outdated?) interops and the functionality given COM servers (Office DLLs) provide.
Daniel Lutz writes:
VS change the identity from Microsoft.Office.Interop.* to Interop.Microsoft.Office.Interop
TLBIMP.EXE allows setting the namespace of the assembly to be produced; see the /namespace:Namespace parameter.
Daniel Lutz writes:
If i understand it right we cannot load these libaries with late binding because for addin express use the older liberies and load theme to the app asembly context. The newer one cannot load in this case alos because of version conflicts.
An interop is sort of a table providing 1) names of public properties and method, 2) their signatures, 3) their offsets in the binary code of the COM server (e.g. MSO.DLL). You load any interop - custom or primary - by specifying the interop DLL in the references section of your project; no need to load it via late binding. If you have several interops for different versions of the same COM server, you must decide what version to use. Typically, you have an IF operator that checks the version of the COM server and decide whether to call version-specific code or not: for instance, "if Office version is greater than XXXXXX, call the YYYYYY functionality, which isn't available in previous Office versions".
Regards from Poland (GMT+2),
Andrei Smolin
Add-in Express Team Leader |
|